turbollm 1.7.3 → 1.7.4

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 (111) hide show
  1. package/README.md +10 -9
  2. package/dist/cli.js +19 -18
  3. package/dist/webdist/assets/AgentEditPage-BOnwuNEo.js +1 -0
  4. package/dist/webdist/assets/{ChatScreen-BOZjtqGu.js → ChatScreen-C-etPxFP.js} +16 -16
  5. package/dist/webdist/assets/CustomizeScreen-CBS-4CGF.js +4 -0
  6. package/dist/webdist/assets/DeveloperScreen-Dm9p8rsC.js +1 -0
  7. package/dist/webdist/assets/EnginesScreen-KF8kClsH.js +7 -0
  8. package/dist/webdist/assets/FsBrowser-Un46d4qe.js +1 -0
  9. package/dist/webdist/assets/{MessageBubble-mJwFfOab.js → MessageBubble-5F87qAxY.js} +4 -4
  10. package/dist/webdist/assets/ModelsScreen-6VbHhlyf.js +2 -0
  11. package/dist/webdist/assets/SettingsScreen-DjoLkPpP.js +1 -0
  12. package/dist/webdist/assets/SkillEditPage-C5Evktgw.js +1 -0
  13. package/dist/webdist/assets/WorkspaceScreen-CiKb7f1I.js +1 -0
  14. package/dist/webdist/assets/{agent-api-B3GXwr4Y.js → agent-api-D3hMsrHk.js} +1 -1
  15. package/dist/webdist/assets/{arc-CRcL8nar.js → arc-BhnuNjta.js} +1 -1
  16. package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-BmOKGMd0.js → architectureDiagram-3BPJPVTR-c3DASz95.js} +1 -1
  17. package/dist/webdist/assets/arrow-left-BZ7U2kS9.js +1 -0
  18. package/dist/webdist/assets/{blockDiagram-GPEHLZMM-C4vusmQ-.js → blockDiagram-GPEHLZMM-CkpfHyuv.js} +1 -1
  19. package/dist/webdist/assets/bot-B3GTFQ2J.js +1 -0
  20. package/dist/webdist/assets/{c4Diagram-AAUBKEIU-DdY0YXKs.js → c4Diagram-AAUBKEIU-B5dn9Zuf.js} +1 -1
  21. package/dist/webdist/assets/channel-CJpy6WaL.js +1 -0
  22. package/dist/webdist/assets/{chat-api-DrMO_g5m.js → chat-api-Dz6qvO1a.js} +1 -1
  23. package/dist/webdist/assets/check-C39bHy3w.js +1 -0
  24. package/dist/webdist/assets/{chevron-left-CfyO2AXW.js → chevron-left-Bm2f38an.js} +1 -1
  25. package/dist/webdist/assets/{chevron-right--1OIa5uV.js → chevron-right-DGQJG4QN.js} +1 -1
  26. package/dist/webdist/assets/{chunk-2J33WTMH-DzCJsNN1.js → chunk-2J33WTMH-_x1EGF5n.js} +1 -1
  27. package/dist/webdist/assets/{chunk-4BX2VUAB-BNPfO47j.js → chunk-4BX2VUAB-pQD6zp-n.js} +1 -1
  28. package/dist/webdist/assets/{chunk-55IACEB6-_vELiE2B.js → chunk-55IACEB6-DIsjXVDw.js} +1 -1
  29. package/dist/webdist/assets/{chunk-727SXJPM-CsQCgUxY.js → chunk-727SXJPM-CHDF4RtM.js} +1 -1
  30. package/dist/webdist/assets/{chunk-AQP2D5EJ-G0waQ7sq.js → chunk-AQP2D5EJ-CaQFwmUk.js} +1 -1
  31. package/dist/webdist/assets/{chunk-FMBD7UC4-CkNtvlrw.js → chunk-FMBD7UC4-DENyT0Cw.js} +1 -1
  32. package/dist/webdist/assets/{chunk-ND2GUHAM-DGnw4n1I.js → chunk-ND2GUHAM-Cv6Yek6j.js} +1 -1
  33. package/dist/webdist/assets/{chunk-QZHKN3VN-BpJ4HH0V.js → chunk-QZHKN3VN-9ATQymUl.js} +1 -1
  34. package/dist/webdist/assets/circle-x-DsCSoQSk.js +1 -0
  35. package/dist/webdist/assets/classDiagram-4FO5ZUOK-BwNVmSJo.js +1 -0
  36. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-BwNVmSJo.js +1 -0
  37. package/dist/webdist/assets/collapsible-Bqsm-UEI.js +1 -0
  38. package/dist/webdist/assets/common-DHHaSVnF.js +1 -0
  39. package/dist/webdist/assets/{cose-bilkent-S5V4N54A-fol9bGNP.js → cose-bilkent-S5V4N54A-r9UktTKw.js} +1 -1
  40. package/dist/webdist/assets/{dagre-BM42HDAG-kyg4YiiE.js → dagre-BM42HDAG-BnhFpJ0u.js} +1 -1
  41. package/dist/webdist/assets/{diagram-2AECGRRQ-CK_mQs9l.js → diagram-2AECGRRQ-C8R_FKFE.js} +1 -1
  42. package/dist/webdist/assets/{diagram-5GNKFQAL-MBl66jsh.js → diagram-5GNKFQAL-Cy3ObA7-.js} +1 -1
  43. package/dist/webdist/assets/{diagram-KO2AKTUF-IxjaUqTu.js → diagram-KO2AKTUF-BKMSyIfH.js} +1 -1
  44. package/dist/webdist/assets/{diagram-LMA3HP47-Cc_fuuLo.js → diagram-LMA3HP47-M3eYUN5j.js} +1 -1
  45. package/dist/webdist/assets/{diagram-OG6HWLK6-DvprGLFT.js → diagram-OG6HWLK6-9mfuWNo6.js} +1 -1
  46. package/dist/webdist/assets/dialog-DTDvWQ5-.js +1 -0
  47. package/dist/webdist/assets/{erDiagram-TEJ5UH35-BEebeQO0.js → erDiagram-TEJ5UH35-3pYBvVYw.js} +1 -1
  48. package/dist/webdist/assets/{flowDiagram-I6XJVG4X--NtrYHee.js → flowDiagram-I6XJVG4X-DkeUYo0c.js} +1 -1
  49. package/dist/webdist/assets/{folder-input-C5Td4Oz0.js → folder-input-CjdiPWWo.js} +1 -1
  50. package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-DOx3A2YH.js → ganttDiagram-6RSMTGT7-Co8G6rQq.js} +1 -1
  51. package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-BEY69HKU.js → gitGraphDiagram-PVQCEYII-BeKTVG6x.js} +1 -1
  52. package/dist/webdist/assets/{index-Tpho8qZ0.js → index-BiJl342p.js} +17 -17
  53. package/dist/webdist/assets/index-CxonSCte.css +1 -0
  54. package/dist/webdist/assets/{index-tVO2x-Rw.js → index-MBZ7hSNo.js} +3 -3
  55. package/dist/webdist/assets/{infoDiagram-5YYISTIA-C4X5I5eI.js → infoDiagram-5YYISTIA-CSNZJB_2.js} +1 -1
  56. package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-BhUZzSmP.js → ishikawaDiagram-YF4QCWOH-Bl5G3Tv8.js} +1 -1
  57. package/dist/webdist/assets/{journeyDiagram-JHISSGLW-R-rmKLZ-.js → journeyDiagram-JHISSGLW-8vGboHEi.js} +1 -1
  58. package/dist/webdist/assets/{kanban-definition-UN3LZRKU-DUcDGDiT.js → kanban-definition-UN3LZRKU-Exhgn-QL.js} +1 -1
  59. package/dist/webdist/assets/{linear-Cbr5iYFb.js → linear-BQ7-Rkhb.js} +1 -1
  60. package/dist/webdist/assets/{mermaid.core-DJVxydy1.js → mermaid.core-CQX56k_o.js} +4 -4
  61. package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-BSOgl-Cn.js → mindmap-definition-RKZ34NQL-Bo7PZvAB.js} +1 -1
  62. package/dist/webdist/assets/{pencil-Blh_Q7or.js → pencil-D52zQ528.js} +1 -1
  63. package/dist/webdist/assets/{personas-Dm0JD8kn.js → personas-BvApKJbI.js} +23 -23
  64. package/dist/webdist/assets/{pieDiagram-4H26LBE5-BwQ854zg.js → pieDiagram-4H26LBE5-q8hQguI2.js} +1 -1
  65. package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-BOQGjhZD.js → quadrantDiagram-W4KKPZXB-D7oJYa2u.js} +1 -1
  66. package/dist/webdist/assets/{radix-DVGeLgPN.js → radix-DHuxYtIT.js} +1 -1
  67. package/dist/webdist/assets/refresh-cw-CCa9KASO.js +1 -0
  68. package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-b3dNYy6K.js → requirementDiagram-4Y6WPE33-BB3AxfxX.js} +1 -1
  69. package/dist/webdist/assets/{rotate-ccw-B7QiZxpr.js → rotate-ccw-CjT25zbE.js} +1 -1
  70. package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-C26ntsbR.js → sankeyDiagram-5OEKKPKP-rBs95ARp.js} +1 -1
  71. package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-Bp8i1DIR.js → sequenceDiagram-3UESZ5HK-Dl8lIjVG.js} +1 -1
  72. package/dist/webdist/assets/{star-BCxsXqcv.js → star-CGoRw01k.js} +1 -1
  73. package/dist/webdist/assets/{stateDiagram-AJRCARHV-CmZpIELB.js → stateDiagram-AJRCARHV-CLoQmP-w.js} +1 -1
  74. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-ByjTcwsY.js +1 -0
  75. package/dist/webdist/assets/{timeline-definition-PNZ67QCA-slNuxmat.js → timeline-definition-PNZ67QCA-xQjF3Uja.js} +1 -1
  76. package/dist/webdist/assets/{trash-2-DGVXsIsP.js → trash-2-DtSyrOcz.js} +1 -1
  77. package/dist/webdist/assets/useIsDesktop-BFaxGM2l.js +1 -0
  78. package/dist/webdist/assets/{vennDiagram-CIIHVFJN-Lf4xPS3i.js → vennDiagram-CIIHVFJN-CAcPzo7k.js} +1 -1
  79. package/dist/webdist/assets/{wardley-L42UT6IY-DbEeYkcX.js → wardley-L42UT6IY-BPakyqHF.js} +1 -1
  80. package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-WNGbDcCj.js → wardleyDiagram-YWT4CUSO-DFeQJlAo.js} +1 -1
  81. package/dist/webdist/assets/wrench-BhEqkhsG.js +1 -0
  82. package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-D2kBZ8cG.js → xychartDiagram-2RQKCTM6-CmW19GSM.js} +1 -1
  83. package/dist/webdist/index.html +3 -3
  84. package/package.json +1 -1
  85. package/dist/webdist/assets/AgentEditPage-DLuckXeR.js +0 -1
  86. package/dist/webdist/assets/CustomizeScreen-DoVGTM4C.js +0 -4
  87. package/dist/webdist/assets/DeveloperScreen-BJ7m4ZxK.js +0 -1
  88. package/dist/webdist/assets/EnginesScreen-D3b9VNx0.js +0 -7
  89. package/dist/webdist/assets/ModelDetailDialog-M2tPWvP4.js +0 -1
  90. package/dist/webdist/assets/ModelDirs-CaIf1TUh.js +0 -1
  91. package/dist/webdist/assets/ModelsScreen--sPDS_sx.js +0 -2
  92. package/dist/webdist/assets/SettingsScreen-lBpo_MAD.js +0 -1
  93. package/dist/webdist/assets/SkillEditPage-B5S9Gtlh.js +0 -1
  94. package/dist/webdist/assets/WorkspaceScreen-Cpm-gLG1.js +0 -1
  95. package/dist/webdist/assets/channel-BB6NPD_B.js +0 -1
  96. package/dist/webdist/assets/check-BLFw3EpE.js +0 -1
  97. package/dist/webdist/assets/circle-check-ByN99MCf.js +0 -1
  98. package/dist/webdist/assets/classDiagram-4FO5ZUOK-DwCs4wFr.js +0 -1
  99. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-DwCs4wFr.js +0 -1
  100. package/dist/webdist/assets/collapsible-B_M28W08.js +0 -1
  101. package/dist/webdist/assets/common-Cy1uxtS_.js +0 -1
  102. package/dist/webdist/assets/copy-button-DgYGL35x.js +0 -1
  103. package/dist/webdist/assets/dialog-d6eLRBbl.js +0 -1
  104. package/dist/webdist/assets/index-B8H9eSFq.css +0 -1
  105. package/dist/webdist/assets/plus-D4IwdkJ3.js +0 -1
  106. package/dist/webdist/assets/refresh-cw-BVSLL9xa.js +0 -1
  107. package/dist/webdist/assets/save-DI4M5kP-.js +0 -1
  108. package/dist/webdist/assets/skeleton-D6toQA7p.js +0 -1
  109. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-CswfYrBO.js +0 -1
  110. package/dist/webdist/assets/vram-DVAmNgRC.js +0 -1
  111. package/dist/webdist/assets/wrench-DzoqtmhJ.js +0 -1
package/README.md CHANGED
@@ -177,8 +177,8 @@ the detail:
177
177
  - **Saved per-model profiles, per engine** — tune once per (model, engine) pair, so
178
178
  switching engines (or between two installs of the same engine, e.g. a fork) never
179
179
  overwrites another engine's tuning for the same model.
180
- - **Configurable VRAM headroom** (Settings → Engine, 300 MB–2 GB, default 1 GB) — tell auto-tune
181
- how much VRAM to keep free for other GPU workloads instead of a fixed margin.
180
+ - **Configurable VRAM headroom** (Settings → Models & loading → Advanced, 300 MB–2 GB, default
181
+ 1 GB) — tell auto-tune how much VRAM to keep free for other GPU workloads instead of a fixed margin.
182
182
 
183
183
  </details>
184
184
 
@@ -216,7 +216,7 @@ the detail:
216
216
  in every chat with no restart. A **Research** persona forces multi-step web search and cites sources inline.
217
217
  - **Tool-call approval gate** — every tool call asks for your approval by default before it runs,
218
218
  with **Deny**, **Allow**, **Allow for this chat**, or **Always Allow** on an inline bar above the
219
- composer. Set per-tool defaults globally from DeveloperTool permissions.
219
+ composer. Set per-tool defaults globally from SettingsTools & safety.
220
220
 
221
221
  </details>
222
222
 
@@ -255,7 +255,7 @@ curl http://127.0.0.1:6996/v1/chat/completions \
255
255
  - **Anthropic-compatible** `/v1/messages` — including **tool use and streaming** — which powers
256
256
  Claude Code below. No other local host offers this.
257
257
  - **Structured output** — constrain any response to a **GBNF grammar** (or JSON shape).
258
- - **API-key auth** you can require when sharing over a LAN (Settings → Network).
258
+ - **API-key auth** you can require when sharing over a LAN (Settings → Network & sharing).
259
259
 
260
260
  **The gateway loads models for you.** Most local hosts make you load a model first, then call it.
261
261
  TurboLLM's gateway reads the `model` field of any incoming request, **fuzzy-matches it to your
@@ -267,7 +267,7 @@ coding model, a vision model, and an embedder just names each one and it works
267
267
  `opencode`, `kilo`, `openclaw`, and `hermes` — each gets pointed at TurboLLM the way that tool
268
268
  expects (config file merge, or its own CLI command) instead of a manual copy-paste setup. Inside
269
269
  Claude Code, `/model` lists your local models directly (with **Auto Model Swap** on in Settings →
270
- Gateway) so you can switch mid-session instead of only at launch.
270
+ Models & loading) so you can switch mid-session instead of only at launch.
271
271
 
272
272
  </details>
273
273
 
@@ -285,7 +285,7 @@ fight for memory (and one usually OOMs). TurboLLM can hand the GPU over automati
285
285
  It's **push-based, not polling** — ComfyUI signals TurboLLM the moment a job starts/ends, so the
286
286
  handoff is immediate and deterministic (the model is gone *before* ComfyUI executes).
287
287
 
288
- **One-time setup** (Settings → ComfyUI): turn on **Pause for ComfyUI**, enter your ComfyUI folder
288
+ **One-time setup** (Settings → Network & sharing → ComfyUI): turn on **Pause for ComfyUI**, enter your ComfyUI folder
289
289
  (the one containing `custom_nodes`), click **Install gate** (it writes a small custom node wired to
290
290
  this daemon), then **restart ComfyUI** once. The panel shows a live indicator (rendering / idle /
291
291
  connected); **Remove** undoes it.
@@ -333,7 +333,7 @@ daemon any time with **Ctrl+C**.
333
333
  No other local-LLM app lets you run **whatever inference engine you want**. TurboLLM treats
334
334
  the engine as a swappable component.
335
335
 
336
- **Add a custom engine** (Engines screen → **Add engine**):
336
+ **Add a custom engine** (Engines screen → **Add your own engine**):
337
337
 
338
338
  1. Compile or download any `llama-server`-compatible binary — stock
339
339
  [llama.cpp](https://github.com/ggml-org/llama.cpp), a community fork, or your own build.
@@ -398,13 +398,14 @@ any OpenAI- or Anthropic-compatible tool (Open WebUI, Kilo Code, opencode, …).
398
398
  ## Use it from any device on your network
399
399
 
400
400
  The UI runs in the browser, so any phone, tablet, or laptop on your LAN can use the model on
401
- your GPU box:
401
+ your GPU box — every screen is responsive down to phone widths, with the desktop layout
402
+ untouched:
402
403
 
403
404
  ```bash
404
405
  turbollm --addr 0.0.0.0:6996 # bind all interfaces, then open http://<your-ip>:6996
405
406
  ```
406
407
 
407
- Turn on **Require API key** in Settings → Network when you expose it.
408
+ Turn on **Require API key** in Settings → Network & sharing when you expose it.
408
409
 
409
410
  ---
410
411
 
package/dist/cli.js CHANGED
@@ -8086,7 +8086,7 @@ import { Agent, setGlobalDispatcher } from "undici";
8086
8086
  // src/api/routes.ts
8087
8087
  import { streamSSE as streamSSE2 } from "hono/streaming";
8088
8088
  import { existsSync as existsSync21, mkdirSync as mkdirSync13, readFileSync as readFileSync10, readdirSync as readdirSync8, realpathSync, rmSync as rmSync11, statSync as statSync5, writeFileSync as writeFileSync10 } from "fs";
8089
- import { basename as basename3, dirname as dirname9, join as join24, resolve, sep } from "path";
8089
+ import { basename as basename3, dirname as dirname9, join as join24, resolve } from "path";
8090
8090
 
8091
8091
  // src/comfyui/gate-template.ts
8092
8092
  var GATE_VERSION = 2;
@@ -11722,18 +11722,19 @@ function registerApi(app2, d) {
11722
11722
  }
11723
11723
  });
11724
11724
  app2.get("/api/v1/fs/browse", (c) => {
11725
- const home = realHome();
11725
+ if (!isLocalRequest(c, d))
11726
+ return err2(c, 403, "forbidden", "Browsing the filesystem is only available on the machine running TurboLLM.");
11726
11727
  const raw = (c.req.query("path") ?? "").trim();
11727
- const target = raw ? resolve(raw) : home;
11728
+ if (raw === FS_DRIVES_ROOT && process.platform === "win32") {
11729
+ return c.json({ path: FS_DRIVES_ROOT, parent: null, entries: listWindowsDrives() });
11730
+ }
11731
+ const target = raw ? resolve(raw) : homedir4();
11728
11732
  let real;
11729
11733
  try {
11730
11734
  real = realpathSync(target);
11731
11735
  } catch {
11732
11736
  real = target;
11733
11737
  }
11734
- if (!isWithinHome(real, home)) {
11735
- return err2(c, 403, "path_outside_home", "That folder is outside your home directory.");
11736
- }
11737
11738
  let entries;
11738
11739
  try {
11739
11740
  entries = readdirSync8(real, { withFileTypes: true }).filter((d2) => !d2.name.startsWith(".")).map((d2) => {
@@ -11750,8 +11751,8 @@ function registerApi(app2, d) {
11750
11751
  } catch {
11751
11752
  return err2(c, 400, "fs_read_failed", "Could not read that folder (permission denied or not a directory).");
11752
11753
  }
11753
- const parentDir = real === home ? null : dirname9(real);
11754
- const parent = parentDir && isWithinHome(parentDir, home) ? parentDir : null;
11754
+ const parentDir = dirname9(real);
11755
+ const parent = parentDir === real ? process.platform === "win32" ? FS_DRIVES_ROOT : null : parentDir;
11755
11756
  return c.json({ path: real, parent, entries });
11756
11757
  });
11757
11758
  app2.post("/api/v1/engine/start", async (c) => {
@@ -12644,17 +12645,17 @@ function readTail2(path, n) {
12644
12645
  return [];
12645
12646
  }
12646
12647
  }
12647
- function realHome() {
12648
- const h = homedir4();
12649
- try {
12650
- return realpathSync(h);
12651
- } catch {
12652
- return h;
12648
+ var FS_DRIVES_ROOT = "::drives";
12649
+ function listWindowsDrives() {
12650
+ const drives = [];
12651
+ for (let i = 65; i <= 90; i++) {
12652
+ const root = `${String.fromCharCode(i)}:\\`;
12653
+ try {
12654
+ if (existsSync21(root)) drives.push({ name: root, path: root, isDir: true });
12655
+ } catch {
12656
+ }
12653
12657
  }
12654
- }
12655
- function isWithinHome(p, home) {
12656
- if (p === home) return true;
12657
- return p.startsWith(home.endsWith(sep) ? home : home + sep);
12658
+ return drives;
12658
12659
  }
12659
12660
  function getLanIp() {
12660
12661
  const nets = networkInterfaces2();
@@ -0,0 +1 @@
1
+ import{j as e}from"./radix-DHuxYtIT.js";import{u as M,b as W,r as b}from"./vendor-BE0HADM8.js";import{u as $,b as G,c as H,d as Y,e as J,a as E,B as y,f as B,t as h,A as C}from"./index-MBZ7hSNo.js";import{f as U}from"./chat-api-Dz6qvO1a.js";import{f as V,a as X}from"./agent-api-D3hMsrHk.js";import{P as z}from"./personas-BvApKJbI.js";import{C as Z}from"./chevron-left-Bm2f38an.js";import{R as ee}from"./rotate-ccw-CjT25zbE.js";import{T as te}from"./trash-2-DtSyrOcz.js";import{C as se}from"./chevron-right-DGQJG4QN.js";const ne=()=>({name:"",description:"",systemPrompt:"",skillIds:[],tools:[]});function oe(s){const d=[],x=new Map;for(const n of s){const o=/^mcp__([^_]+(?:_[^_]+)*)__/.exec(n.name);if(!o){d.push(n);continue}const i=o[1],p=/^\[([^\]]+)\]/.exec(n.description),c=p?p[1]:i.replace(/_/g," ");x.has(i)||x.set(i,{key:i,label:c,tools:[]}),x.get(i).tools.push(n)}return{builtin:d,groups:[...x.values()].sort((n,o)=>n.label.localeCompare(o.label))}}function ae(s){return s.replace(/^mcp__[^_]+(?:_[^_]+)*__/,"")}function le(s){return s.replace(/^\[[^\]]+\]\s*/,"")}function q({tool:s,checked:d,onToggle:x}){return e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[12px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:d,onChange:x}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"font-mono text-ink",children:ae(s.name)}),s.description&&e.jsx("span",{className:"text-faint",children:le(s.description)})]})]})}function re({group:s,selected:d,onToggleAll:x,onToggleOne:n}){const[o,i]=b.useState(!1),p=s.tools.map(l=>l.name),c=p.filter(l=>d.has(l)).length,a=c===p.length,f=c>0&&!a,u=b.useRef(null);return b.useEffect(()=>{u.current&&(u.current.indeterminate=f)},[f]),e.jsxs("div",{className:"rounded-md border border-border",children:[e.jsxs("div",{className:"flex items-center gap-2 px-2.5 py-2",children:[e.jsx("input",{ref:u,type:"checkbox",checked:a,onChange:()=>x(p,!a)}),e.jsxs("button",{type:"button",onClick:()=>i(l=>!l),className:"flex flex-1 items-center gap-1.5 text-left text-[13px] text-ink",children:[e.jsx(se,{size:12,className:B("shrink-0 text-faint transition-transform",o&&"rotate-90")}),s.label,e.jsxs("span",{className:"text-[11px] text-faint",children:["(",c,"/",p.length,")"]})]})]}),o&&e.jsx("div",{className:"flex flex-col gap-1.5 border-t border-border px-3 py-2 pl-9",children:s.tools.map(l=>e.jsx(q,{tool:l,checked:d.has(l.name),onToggle:()=>n(l.name)},l.name))})]})}function ie({tools:s,selected:d,onChange:x}){const n=new Set(d),{builtin:o,groups:i}=oe(s),p=a=>x(n.has(a)?d.filter(f=>f!==a):[...d,a]),c=(a,f)=>{const u=d.filter(l=>!a.includes(l));x(f?[...u,...a]:u)};return s.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No tools available yet."}):e.jsxs("div",{className:"flex flex-col gap-2",children:[o.length>0&&e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:o.map(a=>e.jsx(q,{tool:a,checked:n.has(a.name),onToggle:()=>p(a.name)},a.name))}),i.map(a=>e.jsx(re,{group:a,selected:n,onToggleAll:c,onToggleOne:p},a.key))]})}function ke(){const{agentId:s="new"}=M(),d=W(),x=$(),n=s==="new",o=!n&&z.some(t=>t.id===s),i=o?z.find(t=>t.id===s):void 0,p=G(),c=!n&&!o?(p.data??[]).find(t=>t.id===s):void 0,a=H(),f=Y(),u=J(),l=o?f.data?.[s]:void 0,_=E({queryKey:X.list(),queryFn:V,staleTime:0}),k=_.data??[],I=E({queryKey:["available-tools"],queryFn:U}),j=I.data??[],[m,g]=b.useState(ne()),[S,T]=b.useState(!1),[A,P]=b.useState(!1),[D,w]=b.useState(!1),[R,L]=b.useState("skills");n&&!S&&(k.length||j.length)&&(g(t=>({...t,skillIds:k.map(r=>r.id),tools:j.map(r=>r.name)})),T(!0)),!n&&!o&&!S&&c&&(g({name:c.name,description:c.description,systemPrompt:c.systemPrompt,skillIds:c.skillIds,tools:c.tools}),T(!0)),o&&!S&&i&&f.isSuccess&&(k.length||j.length)&&(g({name:l?.name??i.name,description:l?.description??i.description,systemPrompt:l?.systemPrompt??i.systemPrompt,skillIds:l?.skillIds??k.map(t=>t.id),tools:l?.tools??j.map(t=>t.name)}),T(!0));const N=()=>d("/customize"),Q=t=>g(r=>({...r,skillIds:r.skillIds.includes(t)?r.skillIds.filter(v=>v!==t):[...r.skillIds,t]})),O=()=>{if(!m.name.trim()){h.error("Name is required.");return}P(!0);const t={name:m.name.trim(),description:m.description.trim(),systemPrompt:m.systemPrompt,skillIds:m.skillIds,tools:m.tools};if(o){u.save.mutate({id:s,override:t},{onSuccess:()=>{h.success("Agent saved."),N()},onError:v=>{h.error(v instanceof C?v.message:"Could not save agent."),P(!1)}});return}const r={onSuccess:()=>{x.invalidateQueries({queryKey:["chat-agents"]}),h.success(n?"Agent created.":"Agent saved."),N()},onError:v=>{h.error(v instanceof C?v.message:"Could not save agent."),P(!1)}};n?a.add.mutate(t,r):a.update.mutate({id:s,patch:t},r)},F=()=>{u.reset.mutate(s,{onSuccess:()=>{h.success("Reset to default."),i&&g({name:i.name,description:i.description,systemPrompt:i.systemPrompt,skillIds:k.map(t=>t.id),tools:j.map(t=>t.name)})},onError:t=>h.error(t instanceof C?t.message:"Could not reset agent.")})},K=()=>{a.remove.mutate(s,{onSuccess:()=>{h.success("Agent deleted."),N()},onError:t=>h.error(t instanceof C?t.message:"Could not delete agent.")})};return!n&&!o&&p.isSuccess&&!c?e.jsxs("div",{className:"flex w-full flex-col items-center gap-3 px-8 py-16",children:[e.jsx("p",{className:"text-[14px] text-muted",children:"That agent no longer exists."}),e.jsx(y,{size:"sm",variant:"outline",onClick:N,children:"Back to agents"})]}):e.jsxs("div",{className:"flex w-full max-w-4xl flex-col gap-5 px-4 py-6 md:px-8",children:[e.jsxs("div",{className:"flex flex-wrap items-center gap-2",children:[e.jsx("button",{type:"button",onClick:N,className:"text-faint hover:text-ink",title:"Back to agents",children:e.jsx(Z,{size:18})}),e.jsx("span",{className:"text-[15px] font-medium text-ink",children:n?"New agent":o?l?"Edit agent (modified built-in)":"Edit agent (built-in)":"Edit agent"}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[o&&e.jsxs(y,{size:"sm",variant:"outline",onClick:F,disabled:u.reset.isPending,children:[e.jsx(ee,{size:13})," Reset to default"]}),e.jsx(y,{size:"sm",variant:"outline",onClick:N,children:"Cancel"}),e.jsx(y,{size:"sm",onClick:O,disabled:A||!m.name.trim(),children:A?"Saving…":"Save"})]})]}),o&&e.jsx("p",{className:"rounded-md border border-border bg-panel px-3 py-2 text-[12px] text-muted",children:"Built-in agents can be edited in place — Reset to default discards your changes and restores TurboLLM's original prompt and access."}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Name"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"Release Notes Writer",value:m.name,onChange:t=>g(r=>({...r,name:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"Description"}),e.jsx("input",{className:"rounded-md border border-border bg-bg px-3 py-1.5 text-[13px] text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"What this agent is for, in one line",value:m.description,onChange:t=>g(r=>({...r,description:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-1.5",children:[e.jsx("label",{className:"text-[12px] font-medium text-muted",children:"System prompt"}),e.jsx("textarea",{className:"min-h-[160px] resize-y rounded-md border border-border bg-bg px-3 py-2 font-mono text-[12px] leading-relaxed text-ink outline-none focus:border-accent placeholder:text-faint",placeholder:"You are…",value:m.systemPrompt,onChange:t=>g(r=>({...r,systemPrompt:t.target.value}))})]}),e.jsxs("div",{className:"flex flex-col gap-2.5",children:[e.jsx("div",{className:"inline-flex w-fit rounded-md border border-border p-0.5",children:["skills","tools"].map(t=>e.jsx("button",{type:"button",onClick:()=>L(t),className:B("rounded px-3 py-1.5 text-[13px] font-medium transition-colors",R===t?"bg-accent/12 text-accent":"text-muted hover:text-ink"),children:t==="skills"?`Skills (${m.skillIds.length})`:`Tools (${m.tools.length})`},t))}),R==="skills"?_.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):k.length===0?e.jsx("p",{className:"text-[12px] text-faint",children:"No skills yet — create one under Customize → Skills."}):e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:k.map(t=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 text-[13px]",children:[e.jsx("input",{type:"checkbox",className:"mt-0.5",checked:m.skillIds.includes(t.id),onChange:()=>Q(t.id)}),e.jsxs("span",{className:"flex flex-col",children:[e.jsx("span",{className:"text-ink",children:t.name}),t.description&&e.jsx("span",{className:"text-[12px] text-muted",children:t.description})]})]},t.id))}):I.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):e.jsx(ie,{tools:j,selected:m.tools,onChange:t=>g(r=>({...r,tools:t}))})]}),!n&&!o&&(D?e.jsxs("div",{className:"flex items-center gap-2 self-start rounded-md border border-border bg-panel p-2 text-[12px]",children:[e.jsx("span",{className:"flex-1 text-muted",children:"Delete this agent?"}),e.jsx("button",{type:"button",onClick:K,className:"rounded px-2 py-1 text-[color:var(--err)] hover:bg-[color:color-mix(in_srgb,var(--err)_12%,transparent)]",children:"Delete"}),e.jsx("button",{type:"button",onClick:()=>w(!1),className:"rounded px-2 py-1 text-faint hover:text-ink",children:"Cancel"})]}):e.jsxs("button",{type:"button",onClick:()=>w(!0),className:"flex items-center gap-1.5 self-start text-[12px] text-faint hover:text-[color:var(--err)]",children:[e.jsx(te,{size:13})," Delete agent"]}))]})}export{ke as AgentEditPage};