turbollm 1.9.6 → 1.9.7

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 (99) hide show
  1. package/dist/cli.js +284 -113
  2. package/dist/webdist/assets/{AgentEditPage-DtVYUbnL.js → AgentEditPage-C8RGdWJF.js} +1 -1
  3. package/dist/webdist/assets/{ChatScreen-CFqvsPyx.js → ChatScreen-N3uMBH5o.js} +1 -1
  4. package/dist/webdist/assets/{CodeComposer-CEjCtZkU.js → CodeComposer-CiCzwc9F.js} +1 -1
  5. package/dist/webdist/assets/{CodeHomeScreen-DMVcD76o.js → CodeHomeScreen-CL0EstEl.js} +1 -1
  6. package/dist/webdist/assets/{CodeSessionScreen-DAtsPyto.js → CodeSessionScreen-Cmpd5nk-.js} +1 -1
  7. package/dist/webdist/assets/{CustomizeScreen-Cbki-6V9.js → CustomizeScreen-BIdHpMx-.js} +1 -1
  8. package/dist/webdist/assets/{DeveloperScreen-WD9BR3c2.js → DeveloperScreen-Du9T6j_f.js} +1 -1
  9. package/dist/webdist/assets/{EnginesScreen-Dn2h0ZJu.js → EnginesScreen-CFhygpHp.js} +1 -1
  10. package/dist/webdist/assets/{FsBrowser-B3FpjXmq.js → FsBrowser-yCDndpa8.js} +1 -1
  11. package/dist/webdist/assets/{ModelLoadMenu-gr5_lfaC.js → ModelLoadMenu-B6OgZ7AZ.js} +1 -1
  12. package/dist/webdist/assets/{ModelsScreen-CB62SK43.js → ModelsScreen-DAcHEngS.js} +1 -1
  13. package/dist/webdist/assets/{SettingsScreen-YEjZw2_s.js → SettingsScreen-CgwmrEYG.js} +1 -1
  14. package/dist/webdist/assets/{SkillEditPage-CwUXP2-z.js → SkillEditPage-swxEgg6z.js} +1 -1
  15. package/dist/webdist/assets/{TokensScreen-Dr8A9OL9.js → TokensScreen-1gAoLbZq.js} +1 -1
  16. package/dist/webdist/assets/{ToolApprovalBar-bXZAEk7U.js → ToolApprovalBar-D-VPdhAA.js} +3 -3
  17. package/dist/webdist/assets/WorkspaceScreen-DA4l7OyH.js +1 -0
  18. package/dist/webdist/assets/{agent-api-DZH-y2xg.js → agent-api-C4rG-Jhm.js} +1 -1
  19. package/dist/webdist/assets/{arc-D5l_g5is.js → arc-DN-erBAi.js} +1 -1
  20. package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-CguxoWho.js → architectureDiagram-3BPJPVTR-C_Y3sT8y.js} +1 -1
  21. package/dist/webdist/assets/{arrow-left-wlX0VpDE.js → arrow-left-Dv89StCL.js} +1 -1
  22. package/dist/webdist/assets/{badge-BRepTaJr.js → badge-C367M9ui.js} +1 -1
  23. package/dist/webdist/assets/{blockDiagram-GPEHLZMM-B9xsnW_4.js → blockDiagram-GPEHLZMM-DIhPpxoy.js} +1 -1
  24. package/dist/webdist/assets/{bot-D2tkiUsh.js → bot-CkS7SpAq.js} +1 -1
  25. package/dist/webdist/assets/{c4Diagram-AAUBKEIU-CYTOwpJg.js → c4Diagram-AAUBKEIU-DhxTzCHS.js} +1 -1
  26. package/dist/webdist/assets/channel-B5rf-0pa.js +1 -0
  27. package/dist/webdist/assets/{chat-api-FD0R9dNF.js → chat-api-BjLw__7G.js} +1 -1
  28. package/dist/webdist/assets/{chat-queries-BKXFq3YC.js → chat-queries-DkW-crMK.js} +1 -1
  29. package/dist/webdist/assets/{chevron-left-BKw66_4_.js → chevron-left-CvC9QlMY.js} +1 -1
  30. package/dist/webdist/assets/{chevron-right-CFMIMfHC.js → chevron-right-dxKQD1jQ.js} +1 -1
  31. package/dist/webdist/assets/{chunk-2J33WTMH-DpsCpZbI.js → chunk-2J33WTMH-BStbX98A.js} +1 -1
  32. package/dist/webdist/assets/{chunk-4BX2VUAB-C0i72mr2.js → chunk-4BX2VUAB-Dt_NYZlI.js} +1 -1
  33. package/dist/webdist/assets/{chunk-55IACEB6-DmbNCy4Z.js → chunk-55IACEB6-Bhp_kq7k.js} +1 -1
  34. package/dist/webdist/assets/{chunk-727SXJPM-CiKdPdL2.js → chunk-727SXJPM-DSlmyTXu.js} +1 -1
  35. package/dist/webdist/assets/{chunk-AQP2D5EJ-Bc6RXr7f.js → chunk-AQP2D5EJ-DTjPEmPv.js} +1 -1
  36. package/dist/webdist/assets/{chunk-FMBD7UC4-CVnhYSu6.js → chunk-FMBD7UC4-CJFy7xKH.js} +1 -1
  37. package/dist/webdist/assets/{chunk-ND2GUHAM-CW8vQLlh.js → chunk-ND2GUHAM-U5KxKYoG.js} +1 -1
  38. package/dist/webdist/assets/{chunk-QZHKN3VN-x4JaoE5f.js → chunk-QZHKN3VN-DqU8C1o_.js} +1 -1
  39. package/dist/webdist/assets/{circle-check-8hmejhyG.js → circle-check-X2gW5Lao.js} +1 -1
  40. package/dist/webdist/assets/{circle-x-C9EQG8zH.js → circle-x-EHdG_-2l.js} +1 -1
  41. package/dist/webdist/assets/classDiagram-4FO5ZUOK-B6iRj6y1.js +1 -0
  42. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-B6iRj6y1.js +1 -0
  43. package/dist/webdist/assets/{common-CqVKpWIL.js → common-CR_Lyg91.js} +1 -1
  44. package/dist/webdist/assets/{cose-bilkent-S5V4N54A-Cv-MBe40.js → cose-bilkent-S5V4N54A-BYKK4Cf1.js} +1 -1
  45. package/dist/webdist/assets/{dagre-BM42HDAG-CZWDfsjb.js → dagre-BM42HDAG-g4hhdP8F.js} +1 -1
  46. package/dist/webdist/assets/{diagram-2AECGRRQ-CsDxXM77.js → diagram-2AECGRRQ-Dr_agpgo.js} +1 -1
  47. package/dist/webdist/assets/{diagram-5GNKFQAL-t3BVl_nH.js → diagram-5GNKFQAL-HhGT-1mW.js} +1 -1
  48. package/dist/webdist/assets/{diagram-KO2AKTUF-Ck_gIw7B.js → diagram-KO2AKTUF-DAbDCiEN.js} +1 -1
  49. package/dist/webdist/assets/{diagram-LMA3HP47-w5BRZa6G.js → diagram-LMA3HP47-69Vaqnrj.js} +1 -1
  50. package/dist/webdist/assets/{diagram-OG6HWLK6-DLKPbcMA.js → diagram-OG6HWLK6-BfoIa3zL.js} +1 -1
  51. package/dist/webdist/assets/{dialog-6Wb-Xczv.js → dialog-J9fwiM_6.js} +1 -1
  52. package/dist/webdist/assets/{dropdown-menu-BAw7TO38.js → dropdown-menu-CzGm9m23.js} +1 -1
  53. package/dist/webdist/assets/{erDiagram-TEJ5UH35-3L9k9gBp.js → erDiagram-TEJ5UH35-_kvCnb2f.js} +1 -1
  54. package/dist/webdist/assets/{flowDiagram-I6XJVG4X-l8IUdo3a.js → flowDiagram-I6XJVG4X-CyoD5NFD.js} +1 -1
  55. package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-Dj69Fodg.js → ganttDiagram-6RSMTGT7-BHbeuBsb.js} +1 -1
  56. package/dist/webdist/assets/{git-branch-BQTTe9uX.js → git-branch-DwkQBy50.js} +1 -1
  57. package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-Du_5XKj1.js → gitGraphDiagram-PVQCEYII-CLiLu2eD.js} +1 -1
  58. package/dist/webdist/assets/{index-HEbqFffw.js → index-sdlWNoLv.js} +2 -2
  59. package/dist/webdist/assets/{infoDiagram-5YYISTIA-Cna43URi.js → infoDiagram-5YYISTIA-DUsHPouL.js} +1 -1
  60. package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-hNRD3nFQ.js → ishikawaDiagram-YF4QCWOH-dmOwTeXN.js} +1 -1
  61. package/dist/webdist/assets/{journeyDiagram-JHISSGLW-DDZl1KbG.js → journeyDiagram-JHISSGLW-B-ROjE-9.js} +1 -1
  62. package/dist/webdist/assets/{kanban-definition-UN3LZRKU-Dj3MHmQh.js → kanban-definition-UN3LZRKU-BcmeWTJv.js} +1 -1
  63. package/dist/webdist/assets/{linear-BIBN6AEG.js → linear-D_7tH0LK.js} +1 -1
  64. package/dist/webdist/assets/{lock-D9JNrPro.js → lock-B6DBc8H2.js} +1 -1
  65. package/dist/webdist/assets/{mermaid.core-Cx_BdpgL.js → mermaid.core-COPgjG_G.js} +4 -4
  66. package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-AghKUeZq.js → mindmap-definition-RKZ34NQL-DYa-6SsO.js} +1 -1
  67. package/dist/webdist/assets/{pencil-BKwhK8ii.js → pencil-dcCQp7PP.js} +1 -1
  68. package/dist/webdist/assets/{personas-DpoGW3xD.js → personas-sJAzYoeu.js} +2 -1
  69. package/dist/webdist/assets/{pieDiagram-4H26LBE5-BNVbKhn5.js → pieDiagram-4H26LBE5-Btte96Dk.js} +1 -1
  70. package/dist/webdist/assets/{plus-BwuuyzoK.js → plus-BvjpqMip.js} +1 -1
  71. package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-Br5J-7iq.js → quadrantDiagram-W4KKPZXB-NS-IUaYv.js} +1 -1
  72. package/dist/webdist/assets/{refresh-cw-WDCjpgA4.js → refresh-cw-CJO18sMX.js} +1 -1
  73. package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-BRepNBqi.js → requirementDiagram-4Y6WPE33-6S8LhS5B.js} +1 -1
  74. package/dist/webdist/assets/{rotate-ccw-CsT9aYGg.js → rotate-ccw-Dg4hTNG6.js} +1 -1
  75. package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-CT1B03d0.js → sankeyDiagram-5OEKKPKP-DzP_aBJ0.js} +1 -1
  76. package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-B0h8Ocb4.js → sequenceDiagram-3UESZ5HK-DbM6WtvX.js} +1 -1
  77. package/dist/webdist/assets/{skeleton-C_8cUA8S.js → skeleton-BWg_uQbt.js} +1 -1
  78. package/dist/webdist/assets/{sliders-horizontal-DnZxStk1.js → sliders-horizontal-BEN8YNq8.js} +1 -1
  79. package/dist/webdist/assets/{sparkles-CgOPZTJx.js → sparkles-BXKX67cv.js} +1 -1
  80. package/dist/webdist/assets/{star-DRDhG9iP.js → star-UYX7iaVC.js} +1 -1
  81. package/dist/webdist/assets/{stateDiagram-AJRCARHV-C6W3BUAK.js → stateDiagram-AJRCARHV-DQd2hln9.js} +1 -1
  82. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-DnzMvaiS.js +1 -0
  83. package/dist/webdist/assets/{switch-Qya-6vsg.js → switch-CbjIB99k.js} +1 -1
  84. package/dist/webdist/assets/{terminal-CR5oCvI7.js → terminal-DVNTdtpQ.js} +1 -1
  85. package/dist/webdist/assets/{timeline-definition-PNZ67QCA-Dtggo9rb.js → timeline-definition-PNZ67QCA-BnfK2zzg.js} +1 -1
  86. package/dist/webdist/assets/{trash-2-Ejk7so01.js → trash-2-DkWlMuY9.js} +1 -1
  87. package/dist/webdist/assets/{useIsDesktop-CmQXgI2O.js → useIsDesktop-DvCa8GTi.js} +1 -1
  88. package/dist/webdist/assets/{vennDiagram-CIIHVFJN-mYXtI-Zx.js → vennDiagram-CIIHVFJN-tPTfe0OZ.js} +1 -1
  89. package/dist/webdist/assets/{wardley-L42UT6IY-D4Qq_cbT.js → wardley-L42UT6IY-D7FeB03v.js} +1 -1
  90. package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-dGqgsAhD.js → wardleyDiagram-YWT4CUSO-BDRCgpY_.js} +1 -1
  91. package/dist/webdist/assets/{wrench-C42l7xRT.js → wrench-BoYGiR8D.js} +1 -1
  92. package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-brgk11Hq.js → xychartDiagram-2RQKCTM6-CVQgje-B.js} +1 -1
  93. package/dist/webdist/index.html +1 -1
  94. package/package.json +1 -1
  95. package/dist/webdist/assets/WorkspaceScreen-DoG03BQz.js +0 -1
  96. package/dist/webdist/assets/channel-BCf5gRqW.js +0 -1
  97. package/dist/webdist/assets/classDiagram-4FO5ZUOK-Bg74ry_y.js +0 -1
  98. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-Bg74ry_y.js +0 -1
  99. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-CdjOVW2f.js +0 -1
package/dist/cli.js CHANGED
@@ -30,10 +30,10 @@ function hideChildConsoleWindows() {
30
30
  }
31
31
 
32
32
  // src/cli.ts
33
- import { spawn as spawn8 } from "child_process";
33
+ import { spawn as spawn9 } from "child_process";
34
34
  import { openSync as openSync3, readFileSync as readFileSync19 } from "fs";
35
35
  import { serve } from "@hono/node-server";
36
- import { dirname as dirname12, join as join35 } from "path";
36
+ import { dirname as dirname12, join as join36 } from "path";
37
37
  import { fileURLToPath as fileURLToPath2 } from "url";
38
38
 
39
39
  // src/config/config.ts
@@ -5435,6 +5435,7 @@ function rowToAgentRun(r) {
5435
5435
  useWorktree: r.use_worktree === null ? void 0 : r.use_worktree === 1,
5436
5436
  worktreeBranch: r.worktree_branch ?? void 0,
5437
5437
  worktreeBase: r.worktree_base ?? void 0,
5438
+ worktreePath: r.worktree_path ?? void 0,
5438
5439
  linesAdded: r.lines_added ?? void 0,
5439
5440
  linesRemoved: r.lines_removed ?? void 0,
5440
5441
  compactionSummary: r.compaction_summary ?? void 0,
@@ -5752,6 +5753,10 @@ var ConversationStore = class {
5752
5753
  if (!this.hasColumn("api_usage", "gen_tps")) this.db.exec(`ALTER TABLE api_usage ADD COLUMN gen_tps REAL;`);
5753
5754
  this.db.exec(`PRAGMA user_version = 39;`);
5754
5755
  }
5756
+ if (v < 40) {
5757
+ if (!this.hasColumn("agent_runs", "worktree_path")) this.db.exec(`ALTER TABLE agent_runs ADD COLUMN worktree_path TEXT;`);
5758
+ this.db.exec(`PRAGMA user_version = 40;`);
5759
+ }
5755
5760
  }
5756
5761
  listConversations(q, kind = "all") {
5757
5762
  if (q) {
@@ -6404,7 +6409,7 @@ var ConversationStore = class {
6404
6409
  const id = randomUUID3();
6405
6410
  const now = (/* @__PURE__ */ new Date()).toISOString();
6406
6411
  const agentId = params.agentId ?? null;
6407
- this.db.prepare(`INSERT INTO agent_runs (id,conv_id,title,status,allowed_tools,agent_id,repo_root,repo_branch,use_worktree,worktree_branch,worktree_base,lines_added,lines_removed,code_agent,created_at,updated_at) VALUES ($id,$cid,$title,'queued',$at,$aid,$rr,$rb,$uw,$wb,$wbase,$la,$lr,$ca,$now,$now)`).run({
6412
+ this.db.prepare(`INSERT INTO agent_runs (id,conv_id,title,status,allowed_tools,agent_id,repo_root,repo_branch,use_worktree,worktree_branch,worktree_base,worktree_path,lines_added,lines_removed,code_agent,created_at,updated_at) VALUES ($id,$cid,$title,'queued',$at,$aid,$rr,$rb,$uw,$wb,$wbase,$wpath,$la,$lr,$ca,$now,$now)`).run({
6408
6413
  $id: id,
6409
6414
  $cid: params.convId,
6410
6415
  $title: params.title,
@@ -6415,6 +6420,7 @@ var ConversationStore = class {
6415
6420
  $uw: params.useWorktree === void 0 ? null : params.useWorktree ? 1 : 0,
6416
6421
  $wb: params.worktreeBranch ?? null,
6417
6422
  $wbase: params.worktreeBase ?? null,
6423
+ $wpath: params.worktreePath ?? null,
6418
6424
  // Phase 1: diff stats are not computed yet — seed a code run's counters at 0 so the
6419
6425
  // sidebar shows +0/-0 rather than a blank; a background 'agent' run leaves them NULL.
6420
6426
  $la: params.repoRoot !== void 0 ? 0 : null,
@@ -11610,14 +11616,14 @@ import { Hono } from "hono";
11610
11616
  import { cors } from "hono/cors";
11611
11617
  import { existsSync as existsSync24, readFileSync as readFileSync17, statSync as statSync6 } from "fs";
11612
11618
  import { fileURLToPath } from "url";
11613
- import { dirname as dirname11, join as join32, normalize as normalize2 } from "path";
11619
+ import { dirname as dirname11, join as join33, normalize as normalize2 } from "path";
11614
11620
  import { Agent, setGlobalDispatcher } from "undici";
11615
11621
 
11616
11622
  // src/api/routes.ts
11617
11623
  import { streamSSE as streamSSE2 } from "hono/streaming";
11618
11624
  import { existsSync as existsSync22, mkdirSync as mkdirSync16, readFileSync as readFileSync14, readdirSync as readdirSync10, realpathSync, rmSync as rmSync14, statSync as statSync5, writeFileSync as writeFileSync14 } from "fs";
11619
11625
  import { execFileSync as execFileSync6 } from "child_process";
11620
- import { basename as basename3, dirname as dirname9, join as join28, resolve } from "path";
11626
+ import { basename as basename3, dirname as dirname9, join as join29, resolve } from "path";
11621
11627
 
11622
11628
  // src/comfyui/gate-template.ts
11623
11629
  var GATE_VERSION = 2;
@@ -13701,7 +13707,7 @@ function resolveClaudePermissionMode(mode, choices) {
13701
13707
  return prefs.find((p) => choices.includes(p)) ?? null;
13702
13708
  }
13703
13709
  var realHelpRunner = async () => {
13704
- const { spawn: spawn9 } = await import("child_process");
13710
+ const { spawn: spawn10 } = await import("child_process");
13705
13711
  const { buildShellCommand: buildShellCommand2 } = await import("./shell-command-QI6VPNNL.js");
13706
13712
  const { requiresShell: requiresShell2, resolveExecutable: resolveExecutable2 } = await import("./resolve-executable-ZWQDUVY3.js");
13707
13713
  return await new Promise((resolve5) => {
@@ -13716,7 +13722,7 @@ var realHelpRunner = async () => {
13716
13722
  try {
13717
13723
  const resolved = resolveExecutable2("claude");
13718
13724
  const stdio = ["ignore", "pipe", "ignore"];
13719
- const child = requiresShell2(resolved) ? spawn9(buildShellCommand2("claude", ["--help"]), { shell: true, stdio }) : spawn9(resolved ?? "claude", ["--help"], { stdio });
13725
+ const child = requiresShell2(resolved) ? spawn10(buildShellCommand2("claude", ["--help"]), { shell: true, stdio }) : spawn10(resolved ?? "claude", ["--help"], { stdio });
13720
13726
  const timer = setTimeout(() => {
13721
13727
  try {
13722
13728
  child.kill();
@@ -14158,6 +14164,143 @@ var TerminalManager = class {
14158
14164
  }
14159
14165
  };
14160
14166
 
14167
+ // src/code/worktree.ts
14168
+ import { spawn as spawn5 } from "child_process";
14169
+ import { appendFile, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
14170
+ import { join as join25 } from "path";
14171
+ var WORKTREES_SUBDIR = join25(".turbollm", "worktrees");
14172
+ var EXCLUDE_LINE = "/.turbollm/";
14173
+ var MAX_BRANCH_SUFFIX = 20;
14174
+ function runGit(cwd, args, timeoutMs = 3e4) {
14175
+ return new Promise((resolve5) => {
14176
+ let stdout = "";
14177
+ let stderr = "";
14178
+ let settled = false;
14179
+ const done = (r) => {
14180
+ if (!settled) {
14181
+ settled = true;
14182
+ resolve5(r);
14183
+ }
14184
+ };
14185
+ try {
14186
+ const child = spawn5("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
14187
+ const timer = setTimeout(() => {
14188
+ try {
14189
+ child.kill();
14190
+ } catch {
14191
+ }
14192
+ done({ ok: false, stdout, stderr: stderr || `git ${args[0]} timed out after ${timeoutMs}ms` });
14193
+ }, timeoutMs);
14194
+ child.stdout?.on("data", (b) => {
14195
+ stdout += b.toString("utf8");
14196
+ });
14197
+ child.stderr?.on("data", (b) => {
14198
+ stderr += b.toString("utf8");
14199
+ });
14200
+ child.on("error", (e) => {
14201
+ clearTimeout(timer);
14202
+ done({ ok: false, stdout, stderr: e.message });
14203
+ });
14204
+ child.on("close", (code) => {
14205
+ clearTimeout(timer);
14206
+ done({ ok: code === 0, stdout, stderr });
14207
+ });
14208
+ } catch (e) {
14209
+ done({ ok: false, stdout: "", stderr: e.message });
14210
+ }
14211
+ });
14212
+ }
14213
+ async function isGitRepo(root) {
14214
+ const r = await runGit(root, ["rev-parse", "--is-inside-work-tree"]);
14215
+ return r.ok && r.stdout.trim() === "true";
14216
+ }
14217
+ function sanitizeBranchName(raw) {
14218
+ const cleaned = raw.trim().toLowerCase().replace(/\s+/g, "-").replace(/[~^:?*[\]\\]/g, "").replace(/\.\.+/g, "-").replace(/\/+/g, "/").replace(/^[/.]+/, "").replace(/\.lock$/, "").replace(/-+/g, "-").slice(0, 80).replace(/[-./]+$/, "");
14219
+ return cleaned || "turbollm-session";
14220
+ }
14221
+ async function refExists(repoRoot, name) {
14222
+ const r = await runGit(repoRoot, ["rev-parse", "--verify", "--quiet", `refs/heads/${name}`]);
14223
+ return r.ok && r.stdout.trim().length > 0;
14224
+ }
14225
+ async function findFreeBranchName(repoRoot, desired) {
14226
+ if (!await refExists(repoRoot, desired)) return desired;
14227
+ for (let n = 2; n <= MAX_BRANCH_SUFFIX; n++) {
14228
+ const candidate = `${desired}-${n}`;
14229
+ if (!await refExists(repoRoot, candidate)) return candidate;
14230
+ }
14231
+ return null;
14232
+ }
14233
+ async function ensureLocalExclude(repoRoot) {
14234
+ try {
14235
+ const gitDir = await resolveGitDir(repoRoot);
14236
+ if (!gitDir) return;
14237
+ const infoDir = join25(gitDir, "info");
14238
+ const excludePath = join25(infoDir, "exclude");
14239
+ let current = "";
14240
+ try {
14241
+ current = await readFile2(excludePath, "utf8");
14242
+ } catch {
14243
+ }
14244
+ if (current.split(/\r?\n/).some((l) => l.trim() === EXCLUDE_LINE)) return;
14245
+ await mkdir2(infoDir, { recursive: true });
14246
+ await appendFile(excludePath, `${current.endsWith("\n") || current === "" ? "" : "\n"}${EXCLUDE_LINE}
14247
+ `, "utf8");
14248
+ } catch {
14249
+ }
14250
+ }
14251
+ async function resolveGitDir(repoRoot) {
14252
+ const r = await runGit(repoRoot, ["rev-parse", "--absolute-git-dir"]);
14253
+ const p = r.stdout.trim();
14254
+ return r.ok && p ? p : null;
14255
+ }
14256
+ function agentCwd(run) {
14257
+ return run.worktreePath ?? run.repoRoot ?? "";
14258
+ }
14259
+ async function createSessionWorktree(params) {
14260
+ const { repoRoot, base: base2 } = params;
14261
+ if (!await isGitRepo(repoRoot)) {
14262
+ return {
14263
+ ok: false,
14264
+ code: "not_a_repo",
14265
+ message: `Cannot create a worktree: ${repoRoot} is not a git repository. Untick "Use worktree" to work in the folder directly, or run \`git init\` there first.`
14266
+ };
14267
+ }
14268
+ const desired = sanitizeBranchName(params.branch);
14269
+ const branch = await findFreeBranchName(repoRoot, desired);
14270
+ if (!branch) {
14271
+ return {
14272
+ ok: false,
14273
+ code: "branch_unavailable",
14274
+ message: `Cannot create a worktree: branches "${desired}" through "${desired}-${MAX_BRANCH_SUFFIX}" all already exist. Choose a different branch name.`
14275
+ };
14276
+ }
14277
+ await ensureLocalExclude(repoRoot);
14278
+ const path = join25(repoRoot, WORKTREES_SUBDIR, branch);
14279
+ const baseArgs = base2 && await refExists(repoRoot, base2) ? [base2] : [];
14280
+ const r = await runGit(repoRoot, ["worktree", "add", "-b", branch, path, ...baseArgs], 12e4);
14281
+ if (!r.ok) {
14282
+ return {
14283
+ ok: false,
14284
+ code: "git_failed",
14285
+ message: `Cannot create a worktree: ${(r.stderr || r.stdout).trim().slice(0, 400)}`
14286
+ };
14287
+ }
14288
+ return { ok: true, path, branch };
14289
+ }
14290
+ async function removeSessionWorktree(repoRoot, worktreePath) {
14291
+ const r = await runGit(repoRoot, ["worktree", "remove", worktreePath], 6e4);
14292
+ if (r.ok) return { ok: true };
14293
+ const err7 = (r.stderr || r.stdout).trim();
14294
+ if (/contains modified or untracked files|use --force/i.test(err7)) {
14295
+ return {
14296
+ ok: false,
14297
+ code: "dirty",
14298
+ message: `The worktree at ${worktreePath} still has uncommitted changes, so it was left in place. Commit or discard them, then remove it with: git worktree remove "${worktreePath}"`
14299
+ };
14300
+ }
14301
+ return { ok: false, code: "git_failed", message: err7.slice(0, 400) };
14302
+ }
14303
+
14161
14304
  // src/terminal/terminal-routes.ts
14162
14305
  async function body2(c) {
14163
14306
  try {
@@ -14269,7 +14412,7 @@ function registerTerminalRoutes(app2, d) {
14269
14412
  mode,
14270
14413
  firstMessage
14271
14414
  );
14272
- const terminalId = m.create(run.repoRoot, run.id, cols, rows, launchCommand);
14415
+ const terminalId = m.create(agentCwd(run), run.id, cols, rows, launchCommand);
14273
14416
  if (!run.terminalLaunchedOnce) d.db.updateAgentRun(run.id, { terminalLaunchedOnce: true });
14274
14417
  return c.json({ terminalId }, 201);
14275
14418
  } catch (e) {
@@ -14797,7 +14940,7 @@ function catalogEngine(id) {
14797
14940
  import { execFile as execFile11 } from "child_process";
14798
14941
  import { cpSync as cpSync2, existsSync as existsSync20, mkdirSync as mkdirSync14, readdirSync as readdirSync9, rmSync as rmSync13 } from "fs";
14799
14942
  import { tmpdir } from "os";
14800
- import { join as join25 } from "path";
14943
+ import { join as join26 } from "path";
14801
14944
  import { promisify as promisify8 } from "util";
14802
14945
  var execFileP8 = promisify8(execFile11);
14803
14946
  var REDIST_BASE = "https://developer.download.nvidia.com/compute/cuda/redist/";
@@ -14852,9 +14995,9 @@ async function provisionCuda(dataDir, onProgress, signal) {
14852
14995
  phase: "resolving",
14853
14996
  message: `Using CUDA ${version2}${driverMax ? ` (driver supports up to ${driverMax})` : ""}.`
14854
14997
  });
14855
- const root = join25(dataDir, "cuda", version2);
14856
- const binDir = join25(root, "bin");
14857
- if (existsSync20(join25(binDir, "nvcc.exe"))) {
14998
+ const root = join26(dataDir, "cuda", version2);
14999
+ const binDir = join26(root, "bin");
15000
+ if (existsSync20(join26(binDir, "nvcc.exe"))) {
14858
15001
  onProgress({ phase: "assembling", message: `CUDA ${version2} already downloaded \u2014 reusing it.` });
14859
15002
  return { root, binDir, version: version2 };
14860
15003
  }
@@ -14869,7 +15012,7 @@ async function provisionCuda(dataDir, onProgress, signal) {
14869
15012
  const rel = relOf(id);
14870
15013
  if (rel) components.push({ id, url: REDIST_BASE + rel });
14871
15014
  }
14872
- const work = join25(tmpdir(), `tllm-cuda-${version2}`);
15015
+ const work = join26(tmpdir(), `tllm-cuda-${version2}`);
14873
15016
  rmSync13(work, { recursive: true, force: true });
14874
15017
  mkdirSync14(work, { recursive: true });
14875
15018
  const stageRoot = `${root}.partial`;
@@ -14878,23 +15021,23 @@ async function provisionCuda(dataDir, onProgress, signal) {
14878
15021
  try {
14879
15022
  for (let i = 0; i < components.length; i++) {
14880
15023
  const { id, url } = components[i];
14881
- const zip = join25(work, `${id}.zip`);
15024
+ const zip = join26(work, `${id}.zip`);
14882
15025
  onProgress({ phase: "downloading", message: `Downloading ${id} (${i + 1}/${components.length})\u2026`, pct: 0 });
14883
15026
  await downloadFile(url, zip, (p) => onProgress({ phase: "downloading", message: `Downloading ${id} (${i + 1}/${components.length})\u2026`, pct: p.pct }), signal);
14884
- const exDir = join25(work, id);
15027
+ const exDir = join26(work, id);
14885
15028
  mkdirSync14(exDir, { recursive: true });
14886
15029
  onProgress({ phase: "extracting", message: `Extracting ${id}\u2026` });
14887
15030
  await extractArchive(zip, exDir);
14888
- const inner = readdirSync9(exDir).map((n) => join25(exDir, n)).find((p) => existsSync20(join25(p, "include")) || existsSync20(join25(p, "bin")) || existsSync20(join25(p, "lib")));
14889
- const srcRoot = inner ?? join25(exDir, readdirSync9(exDir)[0] ?? "");
15031
+ const inner = readdirSync9(exDir).map((n) => join26(exDir, n)).find((p) => existsSync20(join26(p, "include")) || existsSync20(join26(p, "bin")) || existsSync20(join26(p, "lib")));
15032
+ const srcRoot = inner ?? join26(exDir, readdirSync9(exDir)[0] ?? "");
14890
15033
  cpSync2(srcRoot, stageRoot, { recursive: true, force: true });
14891
15034
  rmSync13(zip, { force: true });
14892
15035
  }
14893
- if (!existsSync20(join25(stageRoot, "bin", "nvcc.exe"))) {
15036
+ if (!existsSync20(join26(stageRoot, "bin", "nvcc.exe"))) {
14894
15037
  throw new Error("Assembled CUDA toolkit is missing bin/nvcc.exe \u2014 the component set may have changed.");
14895
15038
  }
14896
15039
  rmSync13(root, { recursive: true, force: true });
14897
- mkdirSync14(join25(dataDir, "cuda"), { recursive: true });
15040
+ mkdirSync14(join26(dataDir, "cuda"), { recursive: true });
14898
15041
  cpSync2(stageRoot, root, { recursive: true, force: true });
14899
15042
  onProgress({ phase: "assembling", message: `CUDA ${version2} ready.` });
14900
15043
  return { root, binDir, version: version2 };
@@ -14977,7 +15120,7 @@ function recommendEngines(p, engines) {
14977
15120
  // src/artifacts/screenshot.ts
14978
15121
  import { existsSync as existsSync21 } from "fs";
14979
15122
  import { execFileSync as execFileSync5 } from "child_process";
14980
- import { join as join26 } from "path";
15123
+ import { join as join27 } from "path";
14981
15124
  function chromeCandidates() {
14982
15125
  const env = process.env.PUPPETEER_EXECUTABLE_PATH || process.env.CHROME_PATH;
14983
15126
  const out = env ? [env] : [];
@@ -14986,10 +15129,10 @@ function chromeCandidates() {
14986
15129
  const pfx86 = process.env["PROGRAMFILES(X86)"] || "C:\\Program Files (x86)";
14987
15130
  const local = process.env["LOCALAPPDATA"] || "";
14988
15131
  for (const base2 of [pf, pfx86, local].filter(Boolean)) {
14989
- out.push(join26(base2, "Google", "Chrome", "Application", "chrome.exe"));
15132
+ out.push(join27(base2, "Google", "Chrome", "Application", "chrome.exe"));
14990
15133
  }
14991
15134
  for (const base2 of [pfx86, pf].filter(Boolean)) {
14992
- out.push(join26(base2, "Microsoft", "Edge", "Application", "msedge.exe"));
15135
+ out.push(join27(base2, "Microsoft", "Edge", "Application", "msedge.exe"));
14993
15136
  }
14994
15137
  } else if (process.platform === "darwin") {
14995
15138
  out.push(
@@ -15072,10 +15215,10 @@ async function screenshotArtifact(html, opts) {
15072
15215
 
15073
15216
  // src/telemetry/log.ts
15074
15217
  import { readFileSync as readFileSync13, writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "fs";
15075
- import { join as join27 } from "path";
15218
+ import { join as join28 } from "path";
15076
15219
  var MAX_LOGGED = 200;
15077
15220
  function logPath(dataDir) {
15078
- return join27(dataDir, "telemetry", "sent.json");
15221
+ return join28(dataDir, "telemetry", "sent.json");
15079
15222
  }
15080
15223
  function readSentLog(dataDir) {
15081
15224
  try {
@@ -15090,7 +15233,7 @@ function recordSent(dataDir, events) {
15090
15233
  const sentAt = (/* @__PURE__ */ new Date()).toISOString();
15091
15234
  const fresh = events.map((event) => ({ sentAt, event })).reverse();
15092
15235
  const next = [...fresh, ...readSentLog(dataDir)].slice(0, MAX_LOGGED);
15093
- mkdirSync15(join27(dataDir, "telemetry"), { recursive: true });
15236
+ mkdirSync15(join28(dataDir, "telemetry"), { recursive: true });
15094
15237
  writeFileSync13(logPath(dataDir), JSON.stringify(next));
15095
15238
  } catch {
15096
15239
  }
@@ -15213,7 +15356,7 @@ function registerApi(app2, d) {
15213
15356
  let installedVersion = null;
15214
15357
  if (gatePath) {
15215
15358
  try {
15216
- const src = readFileSync14(join28(gatePath, "__init__.py"), "utf-8");
15359
+ const src = readFileSync14(join29(gatePath, "__init__.py"), "utf-8");
15217
15360
  const m = src.match(/^GATE_VERSION\s*=\s*(\d+)/m);
15218
15361
  if (m) installedVersion = Number(m[1]);
15219
15362
  } catch {
@@ -15253,7 +15396,7 @@ function registerApi(app2, d) {
15253
15396
  const sys = getSysInfo();
15254
15397
  const vendor = primaryVendor(sys);
15255
15398
  const recommended = recommendBackendId(vendor, sys.gpus.length > 0);
15256
- const root = join28(d.store.dir(), "engines");
15399
+ const root = join29(d.store.dir(), "engines");
15257
15400
  const active = d.registry.active();
15258
15401
  const regEngines = d.registry.list().engines;
15259
15402
  const backendOf = (p) => p.match(/llama\.cpp-[^/\\]+-(cuda|rocm|sycl|vulkan|metal|cpu)/i)?.[1]?.toLowerCase();
@@ -15274,7 +15417,7 @@ function registerApi(app2, d) {
15274
15417
  engineId: eng?.id ?? ""
15275
15418
  };
15276
15419
  });
15277
- const mlxVenvPy = join28(root, "mlx", "venv", process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
15420
+ const mlxVenvPy = join29(root, "mlx", "venv", process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
15278
15421
  const mlxInstalledOnDisk = existsSync22(mlxVenvPy);
15279
15422
  const mlxEngine = regEngines.find((e) => e.kind === "mlx");
15280
15423
  const mlx = {
@@ -15290,7 +15433,7 @@ function registerApi(app2, d) {
15290
15433
  const b = await body3(c);
15291
15434
  const def = availableBackends().find((x) => x.id === b.backend);
15292
15435
  if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
15293
- const root = join28(d.store.dir(), "engines");
15436
+ const root = join29(d.store.dir(), "engines");
15294
15437
  const existing = installedBackendBuild(root, def.id);
15295
15438
  if (existing) {
15296
15439
  return c.json({ accepted: false, alreadyInstalled: true, build: existing.tag });
@@ -15334,7 +15477,7 @@ function registerApi(app2, d) {
15334
15477
  app2.post("/api/v1/engines/backends/:id/update", async (c) => {
15335
15478
  const def = availableBackends().find((x) => x.id === c.req.param("id"));
15336
15479
  if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
15337
- const root = join28(d.store.dir(), "engines");
15480
+ const root = join29(d.store.dir(), "engines");
15338
15481
  const oldBin = installedBackendBuild(root, def.id)?.bin ?? null;
15339
15482
  if (!oldBin) return err3(c, 409, "not_installed", "Backend is not installed \u2014 download it first.");
15340
15483
  {
@@ -15393,7 +15536,7 @@ function registerApi(app2, d) {
15393
15536
  app2.post("/api/v1/engines/backends/:id/enable", async (c) => {
15394
15537
  const def = availableBackends().find((x) => x.id === c.req.param("id"));
15395
15538
  if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
15396
- const root = join28(d.store.dir(), "engines");
15539
+ const root = join29(d.store.dir(), "engines");
15397
15540
  const build2 = installedBackendBuild(root, def.id);
15398
15541
  if (!build2) return err3(c, 409, "not_installed", "Backend is not installed on disk \u2014 download it first.");
15399
15542
  let eng = d.registry.list().engines.find((e) => e.binPath === build2.bin);
@@ -15404,7 +15547,7 @@ function registerApi(app2, d) {
15404
15547
  app2.delete("/api/v1/engines/backends/:id", async (c) => {
15405
15548
  const def = availableBackends().find((x) => x.id === c.req.param("id"));
15406
15549
  if (!def) return err3(c, 400, "invalid_config_value", "Unknown backend for this platform.");
15407
- const root = join28(d.store.dir(), "engines");
15550
+ const root = join29(d.store.dir(), "engines");
15408
15551
  const backendDirRe = new RegExp(`[\\\\/]engines[\\\\/]llama\\.cpp-[^\\\\/]+-${def.id}[\\\\/]`);
15409
15552
  const engs = d.registry.list().engines.filter((e) => backendDirRe.test(e.binPath));
15410
15553
  const activeId = d.registry.active()?.id;
@@ -15426,7 +15569,7 @@ function registerApi(app2, d) {
15426
15569
  const busy = engineWorkBusy(d);
15427
15570
  if (busy) return err3(c, 409, "engine_already_running", busy);
15428
15571
  }
15429
- const root = join28(d.store.dir(), "engines");
15572
+ const root = join29(d.store.dir(), "engines");
15430
15573
  const upgrade = c.req.query("update") === "1";
15431
15574
  void (async () => {
15432
15575
  try {
@@ -15449,7 +15592,7 @@ function registerApi(app2, d) {
15449
15592
  const busy = engineWorkBusy(d);
15450
15593
  if (busy) return err3(c, 409, "engine_already_running", busy);
15451
15594
  }
15452
- const root = join28(d.store.dir(), "engines");
15595
+ const root = join29(d.store.dir(), "engines");
15453
15596
  const upgrade = c.req.query("update") === "1";
15454
15597
  void (async () => {
15455
15598
  try {
@@ -15466,12 +15609,12 @@ function registerApi(app2, d) {
15466
15609
  });
15467
15610
  app2.get("/api/v1/engines/catalog", (c) => {
15468
15611
  const regEngines = d.registry.list().engines;
15469
- const enginesRoot = join28(d.store.dir(), "engines");
15612
+ const enginesRoot = join29(d.store.dir(), "engines");
15470
15613
  const items = catalogForPlatform().map((e) => {
15471
15614
  let installed2;
15472
15615
  let enabled;
15473
15616
  if (e.provision === "pip") {
15474
- const pyPath = join28(
15617
+ const pyPath = join29(
15475
15618
  enginesRoot,
15476
15619
  e.id,
15477
15620
  "venv",
@@ -15480,7 +15623,7 @@ function registerApi(app2, d) {
15480
15623
  installed2 = existsSync22(pyPath);
15481
15624
  enabled = regEngines.some((x) => x.kind === e.kind);
15482
15625
  } else if (e.id === "turboquant") {
15483
- const tqDir = join28(enginesRoot, "turboquant");
15626
+ const tqDir = join29(enginesRoot, "turboquant");
15484
15627
  installed2 = existsSync22(tqDir);
15485
15628
  enabled = regEngines.some((x) => /[\\/]engines[\\/]turboquant[\\/]/.test(x.binPath));
15486
15629
  } else if (e.id === "koboldcpp") {
@@ -15550,9 +15693,9 @@ function registerApi(app2, d) {
15550
15693
  const busy = engineWorkBusy(d);
15551
15694
  if (busy) return err3(c, 409, "engine_already_running", busy);
15552
15695
  const name = (b.name ?? "").trim() || void 0;
15553
- const enginesRoot = join28(d.store.dir(), "engines");
15696
+ const enginesRoot = join29(d.store.dir(), "engines");
15554
15697
  const toolchainDirs = d.store.snapshot().build.toolchainDirs;
15555
- const buildRoot = join28(enginesRoot, "build", buildDirName(repoUrl, branch, commit));
15698
+ const buildRoot = join29(enginesRoot, "build", buildDirName(repoUrl, branch, commit));
15556
15699
  const ac = new AbortController();
15557
15700
  buildAbort = ac;
15558
15701
  d.build.start(name ?? repoUrl);
@@ -15662,7 +15805,7 @@ function registerApi(app2, d) {
15662
15805
  const busy = engineWorkBusy(d);
15663
15806
  if (busy) return err3(c, 409, "engine_already_running", busy);
15664
15807
  }
15665
- const root = join28(d.store.dir(), "engines");
15808
+ const root = join29(d.store.dir(), "engines");
15666
15809
  const upgrade = c.req.query("update") === "1";
15667
15810
  void (async () => {
15668
15811
  try {
@@ -15682,7 +15825,7 @@ function registerApi(app2, d) {
15682
15825
  const busy = engineWorkBusy(d);
15683
15826
  if (busy) return err3(c, 409, "engine_already_running", busy);
15684
15827
  }
15685
- const root = join28(d.store.dir(), "engines");
15828
+ const root = join29(d.store.dir(), "engines");
15686
15829
  const upgrade = c.req.query("update") === "1";
15687
15830
  void (async () => {
15688
15831
  try {
@@ -15707,13 +15850,13 @@ function registerApi(app2, d) {
15707
15850
  const busy = engineWorkBusy(d);
15708
15851
  if (busy) return err3(c, 409, "engine_already_running", busy);
15709
15852
  }
15710
- const root = join28(d.store.dir(), "engines");
15853
+ const root = join29(d.store.dir(), "engines");
15711
15854
  const upgrade = c.req.query("update") === "1";
15712
15855
  void (async () => {
15713
15856
  try {
15714
15857
  d.provision.start("turboquant");
15715
15858
  if (upgrade) {
15716
- const tqDir = join28(root, "turboquant");
15859
+ const tqDir = join29(root, "turboquant");
15717
15860
  if (existsSync22(tqDir)) rmSync14(tqDir, { recursive: true, force: true });
15718
15861
  }
15719
15862
  const bin = await provisionTurboquant(
@@ -15742,7 +15885,7 @@ function registerApi(app2, d) {
15742
15885
  const busy = engineWorkBusy(d);
15743
15886
  if (busy) return err3(c, 409, "engine_already_running", busy);
15744
15887
  }
15745
- const root = join28(d.store.dir(), "engines");
15888
+ const root = join29(d.store.dir(), "engines");
15746
15889
  const upgrade = c.req.query("update") === "1";
15747
15890
  const hasNvidia = primaryVendor(getSysInfo()) === "nvidia";
15748
15891
  void (async () => {
@@ -15773,7 +15916,7 @@ function registerApi(app2, d) {
15773
15916
  const busy = engineWorkBusy(d);
15774
15917
  if (busy) return err3(c, 409, "engine_already_running", busy);
15775
15918
  }
15776
- const root = join28(d.store.dir(), "engines");
15919
+ const root = join29(d.store.dir(), "engines");
15777
15920
  const upgrade = c.req.query("update") === "1";
15778
15921
  void (async () => {
15779
15922
  try {
@@ -15880,7 +16023,7 @@ function registerApi(app2, d) {
15880
16023
  }
15881
16024
  d.registry.remove(id);
15882
16025
  if (purge && eng) {
15883
- const enginesRoot = join28(d.store.dir(), "engines");
16026
+ const enginesRoot = join29(d.store.dir(), "engines");
15884
16027
  const purgeDir = engineInstallDir(eng, enginesRoot);
15885
16028
  if (purgeDir && existsSync22(purgeDir)) {
15886
16029
  rmSync14(purgeDir, { recursive: true, force: true });
@@ -15969,12 +16112,12 @@ function registerApi(app2, d) {
15969
16112
  let isDir = d2.isDirectory();
15970
16113
  if (d2.isSymbolicLink()) {
15971
16114
  try {
15972
- isDir = statSync5(join28(real, d2.name)).isDirectory();
16115
+ isDir = statSync5(join29(real, d2.name)).isDirectory();
15973
16116
  } catch {
15974
16117
  isDir = false;
15975
16118
  }
15976
16119
  }
15977
- return { name: d2.name, path: join28(real, d2.name), isDir };
16120
+ return { name: d2.name, path: join29(real, d2.name), isDir };
15978
16121
  }).sort((a, b) => a.isDir === b.isDir ? a.name.localeCompare(b.name) : a.isDir ? -1 : 1);
15979
16122
  } catch {
15980
16123
  return err3(c, 400, "fs_read_failed", "Could not read that folder (permission denied or not a directory).");
@@ -16117,17 +16260,17 @@ function registerApi(app2, d) {
16117
16260
  }
16118
16261
  let customNodes;
16119
16262
  if (basename3(root).toLowerCase() === "custom_nodes") customNodes = root;
16120
- else if (existsSync22(join28(root, "custom_nodes"))) customNodes = join28(root, "custom_nodes");
16263
+ else if (existsSync22(join29(root, "custom_nodes"))) customNodes = join29(root, "custom_nodes");
16121
16264
  else {
16122
16265
  return err3(c, 400, "invalid_config_value", "No 'custom_nodes' folder here. Point me at your ComfyUI folder or its custom_nodes folder.");
16123
16266
  }
16124
16267
  const reqUrl = new URL(c.req.url);
16125
16268
  const port2 = reqUrl.port || (reqUrl.protocol === "https:" ? "443" : "80");
16126
16269
  const base2 = `http://127.0.0.1:${port2}`;
16127
- const gateDir = join28(customNodes, "turbollm_gate");
16270
+ const gateDir = join29(customNodes, "turbollm_gate");
16128
16271
  try {
16129
16272
  mkdirSync16(gateDir, { recursive: true });
16130
- writeFileSync14(join28(gateDir, "__init__.py"), gateNodeSource(base2));
16273
+ writeFileSync14(join29(gateDir, "__init__.py"), gateNodeSource(base2));
16131
16274
  } catch (e) {
16132
16275
  return err3(c, 500, "fs_write_failed", `Could not write the gate node: ${e instanceof Error ? e.message : e}`);
16133
16276
  }
@@ -16494,7 +16637,7 @@ function registerApi(app2, d) {
16494
16637
  };
16495
16638
  const isMemoryServer = server2.transport === "stdio" && (server2.command?.includes("server-memory") || server2.args?.some((a) => a.includes("server-memory")));
16496
16639
  if (isMemoryServer && !server2.env?.MEMORY_FILE_PATH) {
16497
- server2.env = { ...server2.env, MEMORY_FILE_PATH: join28(homedir4(), ".turbollm", "mcp-memory.jsonl") };
16640
+ server2.env = { ...server2.env, MEMORY_FILE_PATH: join29(homedir4(), ".turbollm", "mcp-memory.jsonl") };
16498
16641
  }
16499
16642
  d.store.update((cfg2) => {
16500
16643
  cfg2.mcp.servers.push(server2);
@@ -16953,19 +17096,19 @@ function engineInstallDir(eng, enginesRoot) {
16953
17096
  return n.startsWith(norm + "/") || n.startsWith(norm + "\\");
16954
17097
  };
16955
17098
  if (eng.kind === "mlx") {
16956
- const d = join28(enginesRoot, "mlx", "venv");
17099
+ const d = join29(enginesRoot, "mlx", "venv");
16957
17100
  return inside(d) ? d : null;
16958
17101
  }
16959
17102
  if (eng.kind === "rapid-mlx") {
16960
- const d = join28(enginesRoot, "rapid-mlx", "venv");
17103
+ const d = join29(enginesRoot, "rapid-mlx", "venv");
16961
17104
  return inside(d) ? d : null;
16962
17105
  }
16963
17106
  if (eng.kind === "vllm") {
16964
- const d = join28(enginesRoot, "vllm", "venv");
17107
+ const d = join29(enginesRoot, "vllm", "venv");
16965
17108
  return inside(d) ? d : null;
16966
17109
  }
16967
17110
  if (/[\\/]engines[\\/]turboquant[\\/]/.test(eng.binPath)) {
16968
- const d = join28(enginesRoot, "turboquant");
17111
+ const d = join29(enginesRoot, "turboquant");
16969
17112
  return inside(d) ? d : null;
16970
17113
  }
16971
17114
  if (eng.kind === "koboldcpp") {
@@ -16980,7 +17123,7 @@ function engineInstallDir(eng, enginesRoot) {
16980
17123
  if (srcDir) return inside(srcDir) ? srcDir : null;
16981
17124
  const llamaMatch = eng.binPath.replace(/\\/g, "/").match(/\/(llama\.cpp-[^/]+)\//i);
16982
17125
  if (llamaMatch) {
16983
- const d = join28(enginesRoot, llamaMatch[1]);
17126
+ const d = join29(enginesRoot, llamaMatch[1]);
16984
17127
  return inside(d) ? d : null;
16985
17128
  }
16986
17129
  return null;
@@ -17402,8 +17545,8 @@ import { streamSSE as streamSSE3 } from "hono/streaming";
17402
17545
  import { EventEmitter as EventEmitter2 } from "events";
17403
17546
 
17404
17547
  // src/code/code-session.ts
17405
- import { join as join31, resolve as resolve3 } from "path";
17406
- import { readFile as readFile2 } from "fs/promises";
17548
+ import { join as join32, resolve as resolve3 } from "path";
17549
+ import { readFile as readFile3 } from "fs/promises";
17407
17550
  import { Type, Unsafe } from "typebox";
17408
17551
  import {
17409
17552
  createAgentSession,
@@ -17419,7 +17562,7 @@ import {
17419
17562
  // src/code/robust-bash.ts
17420
17563
  import { access as fsAccess } from "fs/promises";
17421
17564
  import { constants } from "fs";
17422
- import { spawn as spawn5 } from "child_process";
17565
+ import { spawn as spawn6 } from "child_process";
17423
17566
  import { getShellConfig } from "@earendil-works/pi-coding-agent";
17424
17567
  var EXIT_STDIO_GRACE_MS = 100;
17425
17568
  function waitForChildProcess(child) {
@@ -17506,7 +17649,7 @@ function sleep3(ms) {
17506
17649
  async function killDescendantsViaWmi(rootPid) {
17507
17650
  const psScript = `$all = Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId; $ids = @(${rootPid}); $frontier = @(${rootPid}); for ($i = 0; $i -lt 8; $i++) { $children = $all | Where-Object { $frontier -contains $_.ParentProcessId } | Select-Object -ExpandProperty ProcessId; if (-not $children) { break }; $ids += $children; $frontier = $children }; $ids | Select-Object -Unique | ForEach-Object { Write-Output $_ }`;
17508
17651
  const pids = await new Promise((resolve5) => {
17509
- const proc = spawn5("powershell", ["-NoProfile", "-NonInteractive", "-Command", psScript], {
17652
+ const proc = spawn6("powershell", ["-NoProfile", "-NonInteractive", "-Command", psScript], {
17510
17653
  stdio: ["ignore", "pipe", "ignore"],
17511
17654
  windowsHide: true
17512
17655
  });
@@ -17522,7 +17665,7 @@ async function killDescendantsViaWmi(rootPid) {
17522
17665
  });
17523
17666
  for (const pid of pids) {
17524
17667
  try {
17525
- spawn5("taskkill", ["/F", "/PID", String(pid)], { stdio: "ignore", detached: true, windowsHide: true });
17668
+ spawn6("taskkill", ["/F", "/PID", String(pid)], { stdio: "ignore", detached: true, windowsHide: true });
17526
17669
  } catch {
17527
17670
  }
17528
17671
  }
@@ -17531,7 +17674,7 @@ async function killDescendantsViaWmi(rootPid) {
17531
17674
  function killProcessTreeFast(pid) {
17532
17675
  if (process.platform === "win32") {
17533
17676
  try {
17534
- spawn5("taskkill", ["/F", "/T", "/PID", String(pid)], { stdio: "ignore", detached: true, windowsHide: true });
17677
+ spawn6("taskkill", ["/F", "/T", "/PID", String(pid)], { stdio: "ignore", detached: true, windowsHide: true });
17535
17678
  } catch {
17536
17679
  }
17537
17680
  } else {
@@ -17583,7 +17726,7 @@ function createRobustBashOperations(options) {
17583
17726
  Cannot execute bash commands.`);
17584
17727
  }
17585
17728
  const commandFromStdin = shellConfig.commandTransport === "stdin";
17586
- const child = spawn5(
17729
+ const child = spawn6(
17587
17730
  shellConfig.shell,
17588
17731
  commandFromStdin ? shellConfig.args : [...shellConfig.args, command],
17589
17732
  {
@@ -17631,7 +17774,7 @@ Cannot execute bash commands.`);
17631
17774
 
17632
17775
  // src/code/containment.ts
17633
17776
  import { realpathSync as realpathSync2 } from "fs";
17634
- import { resolve as resolve2, dirname as dirname10, basename as basename4, join as join29, sep } from "path";
17777
+ import { resolve as resolve2, dirname as dirname10, basename as basename4, join as join30, sep } from "path";
17635
17778
  var isWin = process.platform === "win32";
17636
17779
  function canonicalize(input) {
17637
17780
  if (typeof input !== "string" || input.length === 0) return null;
@@ -17647,7 +17790,7 @@ function canonicalize(input) {
17647
17790
  for (let i = 0; i < 4096; i++) {
17648
17791
  try {
17649
17792
  const real = realpathSync2(cur);
17650
- const full = tail.length ? join29(real, ...[...tail].reverse()) : real;
17793
+ const full = tail.length ? join30(real, ...[...tail].reverse()) : real;
17651
17794
  return normForCompare(full);
17652
17795
  } catch {
17653
17796
  const parent = dirname10(cur);
@@ -17691,7 +17834,7 @@ function isContainedFromRoot(input, root) {
17691
17834
 
17692
17835
  // src/code/persona.ts
17693
17836
  import { readFileSync as readFileSync15 } from "fs";
17694
- import { join as join30 } from "path";
17837
+ import { join as join31 } from "path";
17695
17838
  var MAX_SKILL_CATALOG_CHARS = 16e3;
17696
17839
  var MAX_AGENTS_MD_CHARS = 2e4;
17697
17840
  var MAX_AGENTS_MD_CANDIDATES = 8;
@@ -17819,7 +17962,7 @@ function readAgentsFile(path) {
17819
17962
  function resolveAgentsFile(root, candidates) {
17820
17963
  for (const entry of candidates.slice(0, MAX_AGENTS_MD_CANDIDATES)) {
17821
17964
  if (!isContainedFromRoot(entry, root)) continue;
17822
- const text = readAgentsFile(join30(root, entry));
17965
+ const text = readAgentsFile(join31(root, entry));
17823
17966
  if (text !== null) return { name: entry, text };
17824
17967
  }
17825
17968
  return null;
@@ -17914,7 +18057,7 @@ function isDependencyAddCommand(command) {
17914
18057
  }
17915
18058
 
17916
18059
  // src/code/lsp-client.ts
17917
- import { spawn as spawn6 } from "child_process";
18060
+ import { spawn as spawn7 } from "child_process";
17918
18061
  import { createMessageConnection, StreamMessageReader, StreamMessageWriter } from "vscode-jsonrpc/node";
17919
18062
  import { URI } from "vscode-uri";
17920
18063
  var toFileUri = (path) => URI.file(path).toString();
@@ -17931,7 +18074,7 @@ function resolveFallbackTsserverPath() {
17931
18074
  cachedTsserverPath = new Promise((resolvePromise) => {
17932
18075
  const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
17933
18076
  const script = "process.stdout.write(require.resolve('typescript/lib/tsserver.js'))";
17934
- const proc = spawn6(npxCmd, ["-y", "-p", "typescript", "node", "-e", script], npxSpawnOptions());
18077
+ const proc = spawn7(npxCmd, ["-y", "-p", "typescript", "node", "-e", script], npxSpawnOptions());
17935
18078
  let out = "";
17936
18079
  proc.stdout?.on("data", (d) => {
17937
18080
  out += String(d);
@@ -17977,7 +18120,7 @@ var LspClient = class {
17977
18120
  try {
17978
18121
  const fallbackTsserverPath = this.spec.language === "typescript" ? await resolveFallbackTsserverPath() : null;
17979
18122
  const npxCmd = process.platform === "win32" ? "npx.cmd" : "npx";
17980
- const proc = spawn6(npxCmd, this.spec.npxArgs, npxSpawnOptions(this.rootDir));
18123
+ const proc = spawn7(npxCmd, this.spec.npxArgs, npxSpawnOptions(this.rootDir));
17981
18124
  this.proc = proc;
17982
18125
  const spawnFailure = new Promise((resolve5) => {
17983
18126
  proc.once("error", (e) => resolve5(e instanceof Error ? e.message : String(e)));
@@ -18353,7 +18496,7 @@ async function runCodeSession(params) {
18353
18496
  console.warn(`[code-session] history seed for session ${sessionId} produced ${sessionManager.getEntries().length}/${expectedEntries} expected entries \u2014 falling back to a plain-text history prefix on this turn's prompt`);
18354
18497
  task = `${HISTORY_FALLBACK_PREFIX}${renderHistoryFallbackText(summaryText, priorMessages)}${HISTORY_FALLBACK_SUFFIX}${rawTask}`;
18355
18498
  }
18356
- const agentDir = join31(d.store.dir(), "pi-agent");
18499
+ const agentDir = join32(d.store.dir(), "pi-agent");
18357
18500
  modelRegistry.registerProvider("local", {
18358
18501
  baseUrl: `${target}/v1`,
18359
18502
  apiKey: "agent-key",
@@ -18484,7 +18627,7 @@ async function runCodeSession(params) {
18484
18627
  await sink({ event: "tool_call", data });
18485
18628
  });
18486
18629
  };
18487
- const skillAgentDir = join31(d.store.dir(), "pi-agent");
18630
+ const skillAgentDir = join32(d.store.dir(), "pi-agent");
18488
18631
  const skillResourceLoader = new DefaultResourceLoader({
18489
18632
  cwd: repoRoot,
18490
18633
  agentDir: skillAgentDir,
@@ -18627,7 +18770,7 @@ async function runCodeSession(params) {
18627
18770
  await sink({ event: "tool_call", data });
18628
18771
  });
18629
18772
  };
18630
- const subAgentDir = join31(d.store.dir(), "pi-agent");
18773
+ const subAgentDir = join32(d.store.dir(), "pi-agent");
18631
18774
  const subResourceLoader = new DefaultResourceLoader({
18632
18775
  cwd: repoRoot,
18633
18776
  agentDir: subAgentDir,
@@ -18829,7 +18972,7 @@ async function runCodeSession(params) {
18829
18972
  if (spec) {
18830
18973
  try {
18831
18974
  const absPath = resolve3(repoRoot, relPath);
18832
- const content = await readFile2(absPath, "utf8");
18975
+ const content = await readFile3(absPath, "utf8");
18833
18976
  const diagnostics = await getLspClient(spec).getDiagnostics(absPath, content);
18834
18977
  if (diagnostics.length > 0) {
18835
18978
  const shown = diagnostics.slice(0, 20);
@@ -19185,7 +19328,7 @@ async function compactCodeSession(params) {
19185
19328
  return payload;
19186
19329
  });
19187
19330
  };
19188
- const compactAgentDir = join31(d.store.dir(), "pi-agent");
19331
+ const compactAgentDir = join32(d.store.dir(), "pi-agent");
19189
19332
  const compactResourceLoader = new DefaultResourceLoader({
19190
19333
  cwd: repoRoot,
19191
19334
  agentDir: compactAgentDir,
@@ -19287,7 +19430,7 @@ async function lookbackPreCompactionHistory(params) {
19287
19430
  return payload;
19288
19431
  });
19289
19432
  };
19290
- const lookbackAgentDir = join31(d.store.dir(), "pi-agent");
19433
+ const lookbackAgentDir = join32(d.store.dir(), "pi-agent");
19291
19434
  const lookbackResourceLoader = new DefaultResourceLoader({
19292
19435
  cwd: repoRoot,
19293
19436
  agentDir: lookbackAgentDir,
@@ -20150,7 +20293,10 @@ function toSidebarRow(run) {
20150
20293
  convId: run.convId,
20151
20294
  title: run.title,
20152
20295
  status: toSessionStatus(run.status),
20153
- branch: run.repoBranch ?? "",
20296
+ // A worktree session's real branch is the one git created for it — `repoBranch` is only ever
20297
+ // set for a session working directly in the repo, so without this the sidebar showed a blank
20298
+ // branch for exactly the sessions that have a dedicated one (caught in end-to-end testing).
20299
+ branch: run.worktreeBranch ?? run.repoBranch ?? "",
20154
20300
  when: relativeTime(run.createdAt),
20155
20301
  add: run.linesAdded ?? 0,
20156
20302
  del: run.linesRemoved ?? 0,
@@ -20159,7 +20305,11 @@ function toSidebarRow(run) {
20159
20305
  running: void 0,
20160
20306
  // filled from CodeRunManager below when available
20161
20307
  createdAt: run.createdAt,
20308
+ // `repoRoot` stays the PROJECT — that's what the user picked and what identifies the session.
20309
+ // `worktreePath` is where the agent actually works; surfaced alongside rather than replacing
20310
+ // it, so the UI can show both without either being a lie.
20162
20311
  repoRoot: run.repoRoot ?? "",
20312
+ worktreePath: run.worktreePath ?? "",
20163
20313
  codeAgent: run.codeAgent ?? "turbollm",
20164
20314
  error: run.error,
20165
20315
  archivedAt: run.archivedAt,
@@ -20179,6 +20329,18 @@ function registerCodeRoutes(app2, d) {
20179
20329
  if (!repoRoot) return err6(c, 400, "invalid_input", "repoRoot is required.");
20180
20330
  if (!task) return err6(c, 400, "invalid_input", "A task description is required.");
20181
20331
  if (!VALID_MODES.has(mode)) return err6(c, 400, "invalid_input", "mode must be one of: auto, plan, ask.");
20332
+ let worktreePath;
20333
+ let worktreeBranch = b.worktreeBranch;
20334
+ if (b.useWorktree) {
20335
+ const created = await createSessionWorktree({
20336
+ repoRoot,
20337
+ branch: b.worktreeBranch || task,
20338
+ base: b.worktreeBase
20339
+ });
20340
+ if (!created.ok) return err6(c, 400, created.code, created.message);
20341
+ worktreePath = created.path;
20342
+ worktreeBranch = created.branch;
20343
+ }
20182
20344
  const conv = db2.createConversation({ kind: "code", modelKey: b.modelKey });
20183
20345
  db2.setConversationMode(conv.id, mode);
20184
20346
  const codeAgent = d.store.snapshot().code.defaultAgent;
@@ -20189,11 +20351,13 @@ function registerCodeRoutes(app2, d) {
20189
20351
  repoRoot,
20190
20352
  repoBranch: b.repoBranch,
20191
20353
  useWorktree: b.useWorktree,
20192
- // captured; NOT acted on in Phase 1 (fast-follow)
20193
- worktreeBranch: b.worktreeBranch,
20194
- // captured; NOT acted on in Phase 1
20354
+ worktreeBranch,
20355
+ // the branch git actually created, not the one requested
20195
20356
  worktreeBase: b.worktreeBase,
20196
- // captured; NOT acted on in Phase 1
20357
+ // Absent unless a worktree was really created above. `repoRoot` deliberately stays the BASE
20358
+ // repository: it is what `git worktree remove` has to run from, and what the session should
20359
+ // still show as its project. `agentCwd()` is the single place that picks between them.
20360
+ worktreePath,
20197
20361
  codeAgent
20198
20362
  });
20199
20363
  db2.addMessage(conv.id, "user", task, { textAttachments: b.contextFiles });
@@ -20245,7 +20409,9 @@ function registerCodeRoutes(app2, d) {
20245
20409
  // with the SAME lookup persona.ts injects into the prompt, so "shown as loaded" == "actually
20246
20410
  // fed to the model". Both false when the session has no repoRoot.
20247
20411
  hasAgentsMd: run.repoRoot ? agentsMdPresence(
20248
- run.repoRoot,
20412
+ // The worktree when there is one — that is the tree the model's prompt is built from,
20413
+ // so "shown as loaded" must be resolved against the same directory persona.ts reads.
20414
+ agentCwd(run),
20249
20415
  d.store.dir(),
20250
20416
  d.store.snapshot().code.agentsMdProjectCandidates,
20251
20417
  d.store.snapshot().code.agentsMdGlobalCandidates
@@ -20311,14 +20477,19 @@ function registerCodeRoutes(app2, d) {
20311
20477
  db2.setAgentRunArchived(id, archived);
20312
20478
  return c.json({ ok: true, archived });
20313
20479
  });
20314
- app2.delete("/api/v1/code/sessions/:id", (c) => {
20480
+ app2.delete("/api/v1/code/sessions/:id", async (c) => {
20315
20481
  const id = c.req.param("id");
20316
20482
  const run = db2.getAgentRun(id);
20317
20483
  if (!run) return err6(c, 404, "not_found", "Session not found.");
20318
20484
  if (runs.isActive(id)) return err6(c, 409, "run_active", "Stop the current run before deleting this session.");
20319
20485
  disposeLspClientsForConv(run.convId);
20486
+ let worktreeNote;
20487
+ if (run.worktreePath && run.repoRoot) {
20488
+ const removed = await removeSessionWorktree(run.repoRoot, run.worktreePath);
20489
+ if (!removed.ok) worktreeNote = removed.message;
20490
+ }
20320
20491
  db2.deleteCodeSession(id);
20321
- return c.json({ ok: true });
20492
+ return c.json(worktreeNote ? { ok: true, worktreeNote } : { ok: true });
20322
20493
  });
20323
20494
  app2.post("/api/v1/code/sessions/:id/compact", async (c) => {
20324
20495
  const id = c.req.param("id");
@@ -20335,7 +20506,7 @@ function registerCodeRoutes(app2, d) {
20335
20506
  d,
20336
20507
  convId: run.convId,
20337
20508
  sessionId: id,
20338
- repoRoot: run.repoRoot,
20509
+ repoRoot: agentCwd(run),
20339
20510
  customInstructions: b.instructions?.trim() || void 0
20340
20511
  });
20341
20512
  return c.json({ ok: true, ...result });
@@ -20398,7 +20569,7 @@ function registerCodeRoutes(app2, d) {
20398
20569
  let failedFiles = [];
20399
20570
  if (b.revertFiles) {
20400
20571
  const rangeMessages = db2.getMessagesFromIncludingInactive(run.convId, messageId);
20401
- const result = revertFileEdits(rangeMessages, run.repoRoot);
20572
+ const result = revertFileEdits(rangeMessages, agentCwd(run));
20402
20573
  revertedFiles = result.reverted;
20403
20574
  failedFiles = result.failed;
20404
20575
  }
@@ -20410,7 +20581,7 @@ function registerCodeRoutes(app2, d) {
20410
20581
  const run = db2.getAgentRun(c.req.param("id"));
20411
20582
  if (!run) return err6(c, 404, "not_found", "Session not found.");
20412
20583
  if (!run.repoRoot) return err6(c, 400, "invalid_input", "Session has no repo root.");
20413
- const status = await getGitStatus(run.repoRoot);
20584
+ const status = await getGitStatus(agentCwd(run));
20414
20585
  return c.json({ ok: true, status });
20415
20586
  });
20416
20587
  app2.post("/api/v1/code/sessions/:id/git/commit", async (c) => {
@@ -20421,7 +20592,7 @@ function registerCodeRoutes(app2, d) {
20421
20592
  if (runs.isActive(id)) return err6(c, 409, "run_active", "Stop or wait for the current run before committing.");
20422
20593
  const b = await body6(c);
20423
20594
  try {
20424
- const result = await commitGitChanges(run.repoRoot, b.message ?? "", b.files);
20595
+ const result = await commitGitChanges(agentCwd(run), b.message ?? "", b.files);
20425
20596
  return c.json({ ok: true, ...result });
20426
20597
  } catch (e) {
20427
20598
  const message = e instanceof Error ? e.message : "Commit failed.";
@@ -20438,21 +20609,21 @@ function registerCodeRoutes(app2, d) {
20438
20609
  if (!run) return err6(c, 404, "not_found", "Session not found.");
20439
20610
  if (!run.repoRoot) return err6(c, 400, "invalid_input", "Session has no repo root.");
20440
20611
  if (runs.isActive(id)) return err6(c, 409, "run_active", "Stop or wait for the current run before pushing.");
20441
- const result = await pushGitBranch(run.repoRoot);
20612
+ const result = await pushGitBranch(agentCwd(run));
20442
20613
  if (!result.ok) {
20443
20614
  const status = result.reason === "diverged" ? 409 : 400;
20444
20615
  return err6(c, status, result.reason, result.message);
20445
20616
  }
20446
- const compareUrl = await getGithubCompareUrl(run.repoRoot, result.branch);
20617
+ const compareUrl = await getGithubCompareUrl(agentCwd(run), result.branch);
20447
20618
  return c.json({ ok: true, remote: result.remote, branch: result.branch, compareUrl });
20448
20619
  });
20449
20620
  app2.get("/api/v1/code/sessions/:id/git/compare-url", async (c) => {
20450
20621
  const run = db2.getAgentRun(c.req.param("id"));
20451
20622
  if (!run) return err6(c, 404, "not_found", "Session not found.");
20452
20623
  if (!run.repoRoot) return err6(c, 400, "invalid_input", "Session has no repo root.");
20453
- const status = await getGitStatus(run.repoRoot);
20624
+ const status = await getGitStatus(agentCwd(run));
20454
20625
  if (!status.isRepo || status.detached || !status.branch) return c.json({ ok: true, compareUrl: null });
20455
- const compareUrl = await getGithubCompareUrl(run.repoRoot, status.branch);
20626
+ const compareUrl = await getGithubCompareUrl(agentCwd(run), status.branch);
20456
20627
  return c.json({ ok: true, compareUrl });
20457
20628
  });
20458
20629
  app2.post("/api/v1/code/sessions/:id/exec", async (c) => {
@@ -20467,7 +20638,7 @@ function registerCodeRoutes(app2, d) {
20467
20638
  const feedToModel = b.feedToModel !== false;
20468
20639
  let result;
20469
20640
  try {
20470
- result = await runShellCommand(command, run.repoRoot, id);
20641
+ result = await runShellCommand(command, agentCwd(run), id);
20471
20642
  } catch (e) {
20472
20643
  return err6(c, 500, "exec_failed", e instanceof Error ? e.message : "Command failed to run.");
20473
20644
  }
@@ -20528,7 +20699,7 @@ function registerCodeRoutes(app2, d) {
20528
20699
  userMsgId = lastUser.id;
20529
20700
  task = contextFilesBlock(lastUser.textAttachments) + lastUser.content;
20530
20701
  }
20531
- const enqueueParams = { convId: run.convId, repoRoot: run.repoRoot, task, userMsgId, thinkingBudget: b.thinkingBudget };
20702
+ const enqueueParams = { convId: run.convId, repoRoot: agentCwd(run), task, userMsgId, thinkingBudget: b.thinkingBudget };
20532
20703
  if (kind === "steer") {
20533
20704
  const { steered, queued: queued2 } = await runs.steer(id, enqueueParams);
20534
20705
  return c.json({ ok: true, queued: queued2, steered, userMessageId: userMsgId }, 202);
@@ -21709,7 +21880,7 @@ function featureForPath(path) {
21709
21880
 
21710
21881
  // src/server.ts
21711
21882
  setGlobalDispatcher(new Agent({ keepAliveMaxTimeout: 6e4, connections: 10 }));
21712
- var WEB_ROOT = join32(dirname11(fileURLToPath(import.meta.url)), "webdist");
21883
+ var WEB_ROOT = join33(dirname11(fileURLToPath(import.meta.url)), "webdist");
21713
21884
  function createApp(d) {
21714
21885
  const app2 = new Hono();
21715
21886
  app2.use(
@@ -21752,9 +21923,9 @@ function createApp(d) {
21752
21923
  if (path.startsWith("api/") || path.startsWith("v1/")) {
21753
21924
  return c.json({ error: { code: "not_found", message: "Unknown endpoint." } }, 404);
21754
21925
  }
21755
- let file = normalize2(join32(WEB_ROOT, path || "index.html"));
21926
+ let file = normalize2(join33(WEB_ROOT, path || "index.html"));
21756
21927
  if (!file.startsWith(WEB_ROOT) || !existsSync24(file) || statSync6(file).isDirectory()) {
21757
- file = join32(WEB_ROOT, "index.html");
21928
+ file = join33(WEB_ROOT, "index.html");
21758
21929
  }
21759
21930
  if (!existsSync24(file)) return c.text("web ui not built \u2014 run `npm run build:web`", 500);
21760
21931
  return new Response(readFileSync17(file), { status: 200, headers: { "Content-Type": contentType(file) } });
@@ -21782,22 +21953,22 @@ function contentType(file) {
21782
21953
  }
21783
21954
 
21784
21955
  // src/tunnel/manager.ts
21785
- import { execFile as execFile13, spawn as spawn7, spawnSync as spawnSync3 } from "child_process";
21956
+ import { execFile as execFile13, spawn as spawn8, spawnSync as spawnSync3 } from "child_process";
21786
21957
  import { mkdirSync as mkdirSync18, readFileSync as readFileSync18, readdirSync as readdirSync11, rmSync as rmSync16, writeFileSync as writeFileSync16 } from "fs";
21787
- import { join as join34 } from "path";
21958
+ import { join as join35 } from "path";
21788
21959
 
21789
21960
  // src/tunnel/provision.ts
21790
21961
  import { chmodSync as chmodSync3, existsSync as existsSync25, mkdirSync as mkdirSync17, rmSync as rmSync15 } from "fs";
21791
- import { join as join33 } from "path";
21962
+ import { join as join34 } from "path";
21792
21963
  var CLOUDFLARED_REPO = "cloudflare/cloudflared";
21793
21964
  function cloudflaredDir(dataDir) {
21794
- return join33(dataDir, "cloudflared");
21965
+ return join34(dataDir, "cloudflared");
21795
21966
  }
21796
21967
  function cloudflaredBinName(platform = process.platform) {
21797
21968
  return platform === "win32" ? "cloudflared.exe" : "cloudflared";
21798
21969
  }
21799
21970
  function cloudflaredBinPath(dataDir, platform = process.platform) {
21800
- return join33(cloudflaredDir(dataDir), cloudflaredBinName(platform));
21971
+ return join34(cloudflaredDir(dataDir), cloudflaredBinName(platform));
21801
21972
  }
21802
21973
  function cloudflaredAssetName(platform = process.platform, archStr = process.arch) {
21803
21974
  if (platform === "win32") {
@@ -21829,7 +22000,7 @@ async function ensureCloudflared(dataDir, onProgress, signal) {
21829
22000
  mkdirSync17(dir, { recursive: true });
21830
22001
  onProgress?.({ phase: "downloading", pct: 0 });
21831
22002
  if (asset.name.endsWith(".tgz")) {
21832
- const tmp = join33(dir, asset.name);
22003
+ const tmp = join34(dir, asset.name);
21833
22004
  try {
21834
22005
  await downloadFile(asset.browser_download_url, tmp, onProgress, signal);
21835
22006
  onProgress?.({ phase: "extracting", pct: -1 });
@@ -21873,10 +22044,10 @@ function forceKill2(child) {
21873
22044
  else child.kill("SIGKILL");
21874
22045
  }
21875
22046
  function tunnelPidDir(dataDir) {
21876
- return join34(dataDir, "run");
22047
+ return join35(dataDir, "run");
21877
22048
  }
21878
22049
  function tunnelPidFile(dataDir, pid) {
21879
- return join34(tunnelPidDir(dataDir), `tunnel-${pid}.pid`);
22050
+ return join35(tunnelPidDir(dataDir), `tunnel-${pid}.pid`);
21880
22051
  }
21881
22052
  function writeTunnelPid(dataDir, pid) {
21882
22053
  try {
@@ -21910,7 +22081,7 @@ function readTunnelPidFiles(dataDir) {
21910
22081
  }
21911
22082
  const out = [];
21912
22083
  for (const name of names) {
21913
- const file = join34(dir, name);
22084
+ const file = join35(dir, name);
21914
22085
  try {
21915
22086
  const { pid, owner } = JSON.parse(readFileSync18(file, "utf8"));
21916
22087
  if (typeof pid === "number" && pid > 0) out.push({ pid, owner: typeof owner === "number" ? owner : 0, file });
@@ -21982,7 +22153,7 @@ var TunnelManager = class {
21982
22153
  this._error = null;
21983
22154
  const { binPath } = await ensureCloudflared(this.dataDir);
21984
22155
  return new Promise((resolve5, reject) => {
21985
- const child = spawn7(binPath, ["tunnel", "--url", `http://127.0.0.1:${localPort}`], { windowsHide: true });
22156
+ const child = spawn8(binPath, ["tunnel", "--url", `http://127.0.0.1:${localPort}`], { windowsHide: true });
21986
22157
  this.child = child;
21987
22158
  writeTunnelPid(this.dataDir, child.pid ?? 0);
21988
22159
  let settled = false;
@@ -22125,7 +22296,7 @@ var Emitter = class {
22125
22296
  hideChildConsoleWindows();
22126
22297
  var version = "0.1.1";
22127
22298
  try {
22128
- const pkgPath = join35(dirname12(fileURLToPath2(import.meta.url)), "..", "package.json");
22299
+ const pkgPath = join36(dirname12(fileURLToPath2(import.meta.url)), "..", "package.json");
22129
22300
  version = JSON.parse(readFileSync19(pkgPath, "utf8")).version ?? version;
22130
22301
  } catch {
22131
22302
  }
@@ -22277,7 +22448,7 @@ if (pruned > 0) console.log(`pruned ${pruned} dangling engine build(s)`);
22277
22448
  var provision = new ProvisionState();
22278
22449
  var build = new BuildState();
22279
22450
  var updates = new UpdateChecker();
22280
- var enginesDir = join35(store.dir(), "engines");
22451
+ var enginesDir = join36(store.dir(), "engines");
22281
22452
  void seedDefaultEngines(registry, enginesDir, provision).then(() => registry.ensureProbed());
22282
22453
  var manager = new Manager(store);
22283
22454
  var scanner = new Scanner(store);
@@ -22360,7 +22531,7 @@ function openBrowser(url) {
22360
22531
  cmd = "xdg-open";
22361
22532
  args = [url];
22362
22533
  }
22363
- const child = spawn8(cmd, args, { detached: true, stdio: "ignore" });
22534
+ const child = spawn9(cmd, args, { detached: true, stdio: "ignore" });
22364
22535
  child.unref();
22365
22536
  child.on("error", () => {
22366
22537
  console.log(` Could not open browser automatically. Visit the URL above manually.`);
@@ -22464,11 +22635,11 @@ var restarting = false;
22464
22635
  function spawnReplacement() {
22465
22636
  let out = "ignore";
22466
22637
  try {
22467
- out = openSync3(join35(store.dir(), "restart.log"), "a");
22638
+ out = openSync3(join36(store.dir(), "restart.log"), "a");
22468
22639
  } catch {
22469
22640
  out = "ignore";
22470
22641
  }
22471
- const child = spawn8(process.execPath, process.argv.slice(1), {
22642
+ const child = spawn9(process.execPath, process.argv.slice(1), {
22472
22643
  cwd: process.cwd(),
22473
22644
  detached: true,
22474
22645
  stdio: ["ignore", out, out]