turbollm 1.11.9 → 1.12.1

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 (131) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +166 -65
  3. package/dist/webdist/.omc/state/agent-replay-d4811781-2640-44e0-8399-662ec162ff43.jsonl +10 -0
  4. package/dist/webdist/.omc/state/subagent-tracking.json +7 -0
  5. package/dist/webdist/assets/{AgentEditPage-Cfbfakkc.js → AgentEditPage-B9mKK6Ob.js} +1 -1
  6. package/dist/webdist/assets/{ChatScreen-CYggEGs5.js → ChatScreen-DLD1CDS1.js} +1 -1
  7. package/dist/webdist/assets/{CodeComposer-CPq9MB0A.js → CodeComposer-DHE_iTkC.js} +1 -1
  8. package/dist/webdist/assets/{CodeHomeScreen-DJd7UNqS.js → CodeHomeScreen-6CKrrrZ5.js} +1 -1
  9. package/dist/webdist/assets/{CodeSessionScreen-DANRbQym.js → CodeSessionScreen-JUpzeYHF.js} +1 -1
  10. package/dist/webdist/assets/{CustomizeScreen-BHsYmBrR.js → CustomizeScreen-C7f4ZRSa.js} +1 -1
  11. package/dist/webdist/assets/{DeveloperScreen-C_m-yLRt.js → DeveloperScreen-n6gJC3-E.js} +1 -1
  12. package/dist/webdist/assets/{EnginesScreen-WNm64lCr.js → EnginesScreen-BWRix4d7.js} +1 -1
  13. package/dist/webdist/assets/ModelDetailDialog-BdPGMTTz.js +1 -0
  14. package/dist/webdist/assets/{ModelLoadMenu-BoD8vGEb.js → ModelLoadMenu-D3zo79dw.js} +1 -1
  15. package/dist/webdist/assets/ModelsScreen-7AJUC2wa.js +2 -0
  16. package/dist/webdist/assets/{OnboardingScreen-Dp2_T3xE.js → OnboardingScreen-CGbbx6bS.js} +1 -1
  17. package/dist/webdist/assets/{RoutineEditPage-_ego0aCV.js → RoutineEditPage-Dd_snYdQ.js} +1 -1
  18. package/dist/webdist/assets/{RoutinesPanel-DHwZQ9Gk.js → RoutinesPanel-5M00XOUM.js} +1 -1
  19. package/dist/webdist/assets/{SettingsScreen-DbKvB_mp.js → SettingsScreen-B489HDiC.js} +1 -1
  20. package/dist/webdist/assets/{SidebarResizeHandle-Bc3QBs84.js → SidebarResizeHandle-B0wgsZJ6.js} +1 -1
  21. package/dist/webdist/assets/{SkillEditPage-CwGusPDb.js → SkillEditPage-CVYFfzLc.js} +1 -1
  22. package/dist/webdist/assets/{TokensScreen-UaGvsqtF.js → TokensScreen-CQ4m20pP.js} +1 -1
  23. package/dist/webdist/assets/{ToolApprovalBar-Chx2y-tr.js → ToolApprovalBar-B2nX56dI.js} +3 -3
  24. package/dist/webdist/assets/WorkspaceScreen-RLGQrw5_.js +1 -0
  25. package/dist/webdist/assets/{abnfDiagram-N423BO3Z-iiGkfE9c.js → abnfDiagram-N423BO3Z-DoOYvhD1.js} +1 -1
  26. package/dist/webdist/assets/{agent-api-CmhXBBT6.js → agent-api-BAMDY8-v.js} +1 -1
  27. package/dist/webdist/assets/{alert-dialog-B7ASyTxr.js → alert-dialog-rMeq-XIF.js} +1 -1
  28. package/dist/webdist/assets/{arc-CitM3TjF.js → arc-BmVPVvg0.js} +1 -1
  29. package/dist/webdist/assets/{architectureDiagram-T3A2C74G-B81sGCwi.js → architectureDiagram-T3A2C74G-Bh4HLM3j.js} +1 -1
  30. package/dist/webdist/assets/{arrow-left-Cx0V34bU.js → arrow-left-Bawx_G0m.js} +1 -1
  31. package/dist/webdist/assets/{badge-BJPgZflb.js → badge-BK8Xn6K3.js} +1 -1
  32. package/dist/webdist/assets/{blockDiagram-VBNYF7ZC-StpQormu.js → blockDiagram-VBNYF7ZC-D4dEMw56.js} +1 -1
  33. package/dist/webdist/assets/{bot-XI_iobdp.js → bot-DmBk_i45.js} +1 -1
  34. package/dist/webdist/assets/{c4Diagram-5PPSVZJV-Csa8aK4v.js → c4Diagram-5PPSVZJV-cnPwu_pX.js} +1 -1
  35. package/dist/webdist/assets/channel-BoFujHnv.js +1 -0
  36. package/dist/webdist/assets/{chat-api-Zyxy4wka.js → chat-api-Bgrb0rnf.js} +1 -1
  37. package/dist/webdist/assets/{chat-queries-DIm_a9NN.js → chat-queries-zz2ch8OT.js} +1 -1
  38. package/dist/webdist/assets/{chevron-down-BWfjeD2C.js → chevron-down-CNDC69O9.js} +1 -1
  39. package/dist/webdist/assets/{chevron-left-CBtdrcDZ.js → chevron-left-CxvUI3ck.js} +1 -1
  40. package/dist/webdist/assets/{chevron-right-DRLCoDa8.js → chevron-right-CpdnK5S2.js} +1 -1
  41. package/dist/webdist/assets/{chunk-2GRJ4B5K-2MKQX9Og.js → chunk-2GRJ4B5K-CtDLA9D7.js} +1 -1
  42. package/dist/webdist/assets/{chunk-2Q5K7J3B-B_27rm1N.js → chunk-2Q5K7J3B-CclvIDBO.js} +1 -1
  43. package/dist/webdist/assets/{chunk-5RXB4S5H-BlhTP0-3.js → chunk-5RXB4S5H-DFYQzU9L.js} +1 -1
  44. package/dist/webdist/assets/{chunk-5VM5RSS4-C7IJCBfZ.js → chunk-5VM5RSS4-CGpXaRvE.js} +1 -1
  45. package/dist/webdist/assets/{chunk-6Q2QTUOP-Bj14OW8u.js → chunk-6Q2QTUOP-CI7s7JEA.js} +1 -1
  46. package/dist/webdist/assets/{chunk-GF5L2VYU-aT8ppbPL.js → chunk-GF5L2VYU-y-SeWQKo.js} +1 -1
  47. package/dist/webdist/assets/{chunk-JWPE2WC7-Bt4dl9CN.js → chunk-JWPE2WC7-BvVsBdrN.js} +1 -1
  48. package/dist/webdist/assets/{chunk-KBJHAD2P-J4coc1ol.js → chunk-KBJHAD2P-CUc-YWns.js} +1 -1
  49. package/dist/webdist/assets/{chunk-RYQCIY6F-Dv_7Q2hX.js → chunk-RYQCIY6F-CABY0N-y.js} +1 -1
  50. package/dist/webdist/assets/{chunk-XXDRQBXY-zQnucktD.js → chunk-XXDRQBXY-ChEtYkzy.js} +1 -1
  51. package/dist/webdist/assets/{circle-check-loUJak9B.js → circle-check-D-vn9nY2.js} +1 -1
  52. package/dist/webdist/assets/{circle-x-Bgnj4qQZ.js → circle-x-BcnmRVTe.js} +1 -1
  53. package/dist/webdist/assets/classDiagram-JCYQIIEL-CWuM4mUX.js +1 -0
  54. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-CWuM4mUX.js +1 -0
  55. package/dist/webdist/assets/{common-B5JH_OFR.js → common-xWkNwbHY.js} +1 -1
  56. package/dist/webdist/assets/{cose-bilkent-JH36ORCC-CvYv-f5s.js → cose-bilkent-JH36ORCC-CJpKH0B3.js} +1 -1
  57. package/dist/webdist/assets/{cynefin-VYW2F7L2-DVwHpekX.js → cynefin-VYW2F7L2-B97tFXrq.js} +1 -1
  58. package/dist/webdist/assets/{cynefinDiagram-MW4NZA55-BcdPKvtX.js → cynefinDiagram-MW4NZA55-CTmmk7Xt.js} +1 -1
  59. package/dist/webdist/assets/{dagre-VZM6K2ZE-4sSe5M8W.js → dagre-VZM6K2ZE-BMkcnGJJ.js} +1 -1
  60. package/dist/webdist/assets/{diagram-7IWD3JNH-COLSWeIR.js → diagram-7IWD3JNH-DxA708mT.js} +1 -1
  61. package/dist/webdist/assets/{diagram-B4RE2ZJO-ER5prsGm.js → diagram-B4RE2ZJO-Bkjy34TD.js} +1 -1
  62. package/dist/webdist/assets/{diagram-LBJQPF4R-FfvD4Hay.js → diagram-LBJQPF4R-BorjsNJT.js} +1 -1
  63. package/dist/webdist/assets/{diagram-Q27KOJAE-DnwPL2tG.js → diagram-Q27KOJAE-hW5SDR9k.js} +1 -1
  64. package/dist/webdist/assets/{diagram-UB23O5K3-B6lZME5q.js → diagram-UB23O5K3-c07HJJ5p.js} +1 -1
  65. package/dist/webdist/assets/{dropdown-menu-DRRO0ftt.js → dropdown-menu-CaecUWTU.js} +1 -1
  66. package/dist/webdist/assets/{ebnfDiagram-BXEA7PRR-DLx8zqdK.js → ebnfDiagram-BXEA7PRR-CvgAWjhG.js} +1 -1
  67. package/dist/webdist/assets/{erDiagram-JOGREHBK-DdSmD2ix.js → erDiagram-JOGREHBK-C66LM90T.js} +1 -1
  68. package/dist/webdist/assets/{external-link-dolckdL9.js → external-link-NvytYVfT.js} +1 -1
  69. package/dist/webdist/assets/{file-text-BJHbdxSm.js → file-text-BsC3M8zz.js} +1 -1
  70. package/dist/webdist/assets/{flowDiagram-UKHOOZJN-C71SbKr7.js → flowDiagram-UKHOOZJN-BcXu-_m9.js} +1 -1
  71. package/dist/webdist/assets/{folder-input-ubj-7_tQ.js → folder-input-B5dvV2Rt.js} +1 -1
  72. package/dist/webdist/assets/{folder-plus-Bz7B7UnH.js → folder-plus-CfgNM2Bq.js} +1 -1
  73. package/dist/webdist/assets/{ganttDiagram-PKOTCBZU-C5doF3y3.js → ganttDiagram-PKOTCBZU-DtorJ7JE.js} +1 -1
  74. package/dist/webdist/assets/{git-branch-CNpNkvMr.js → git-branch-BRH6t3pJ.js} +1 -1
  75. package/dist/webdist/assets/{gitGraphDiagram-DS77QQ5N-iCuj4BOQ.js → gitGraphDiagram-DS77QQ5N-krn_sqdS.js} +1 -1
  76. package/dist/webdist/assets/index-B2S9teij.css +1 -0
  77. package/dist/webdist/assets/{index-D0Emka7d.js → index-C9ZXfFYo.js} +2 -2
  78. package/dist/webdist/assets/{infoDiagram-6WML65LV-CVbutaQs.js → infoDiagram-6WML65LV-DdSRJqp_.js} +1 -1
  79. package/dist/webdist/assets/{ishikawaDiagram-WSZJBQD7-CZyy_met.js → ishikawaDiagram-WSZJBQD7-CXU7wA7v.js} +1 -1
  80. package/dist/webdist/assets/{journeyDiagram-NVQOT4AX-BIwZvXA4.js → journeyDiagram-NVQOT4AX-CIzJLnh4.js} +1 -1
  81. package/dist/webdist/assets/{kanban-definition-27J2QSJJ-DOERNBKz.js → kanban-definition-27J2QSJJ-D7PciwFU.js} +1 -1
  82. package/dist/webdist/assets/{linear-Bf8xCMRw.js → linear-DJzOvrgv.js} +1 -1
  83. package/dist/webdist/assets/{link-queries-DBf0L0p4.js → link-queries-BfuN5xQI.js} +1 -1
  84. package/dist/webdist/assets/{lock-CnmBV2sW.js → lock-BpNNUDlH.js} +1 -1
  85. package/dist/webdist/assets/{mermaid.core-DcQR7vsm.js → mermaid.core-DEfoMsry.js} +4 -4
  86. package/dist/webdist/assets/{mindmap-definition-FAOFIHXS-C3T3OIGM.js → mindmap-definition-FAOFIHXS-B-p8gHME.js} +1 -1
  87. package/dist/webdist/assets/{monitor-Dj5hVZG9.js → monitor-3-JqrGh0.js} +1 -1
  88. package/dist/webdist/assets/{pegDiagram-VL7TDLO6-J5nkSsO9.js → pegDiagram-VL7TDLO6-CVN-UtZ2.js} +1 -1
  89. package/dist/webdist/assets/{pencil-CJDKBSrc.js → pencil-CLBnFmc5.js} +1 -1
  90. package/dist/webdist/assets/{personas-CeAQ6niU.js → personas-Dkq9JD72.js} +1 -1
  91. package/dist/webdist/assets/{pieDiagram-7S7Q4E2Y-2RGDZc8u.js → pieDiagram-7S7Q4E2Y-Cyowg7jU.js} +1 -1
  92. package/dist/webdist/assets/{plus-CtCCCtT9.js → plus-BsBN79CH.js} +1 -1
  93. package/dist/webdist/assets/{quadrantDiagram-CIZ2JOQS-DsYpj2qg.js → quadrantDiagram-CIZ2JOQS-CLEre7Bo.js} +1 -1
  94. package/dist/webdist/assets/{railroadDiagram-AXF67PYL-DvObbGSI.js → railroadDiagram-AXF67PYL-CstFYJPW.js} +1 -1
  95. package/dist/webdist/assets/{refresh-cw-Qd40C8Nj.js → refresh-cw-DHnSGytz.js} +1 -1
  96. package/dist/webdist/assets/{remote-failure-B7wlj-_A.js → remote-failure-DQsv9RYM.js} +1 -1
  97. package/dist/webdist/assets/{requirementDiagram-LRYGKXZP-CdeOzUDb.js → requirementDiagram-LRYGKXZP-BVS10AjP.js} +1 -1
  98. package/dist/webdist/assets/{rocket-D2AvWWtP.js → rocket-0CLawS4F.js} +1 -1
  99. package/dist/webdist/assets/{rotate-ccw-BpPatpxx.js → rotate-ccw-kHM_pFpt.js} +1 -1
  100. package/dist/webdist/assets/{sankeyDiagram-W5VNT64P-DVrYRLqU.js → sankeyDiagram-W5VNT64P-DWg6hEFJ.js} +1 -1
  101. package/dist/webdist/assets/{sequenceDiagram-SI44F4Z6-BZAK6xg6.js → sequenceDiagram-SI44F4Z6-CPe4ECQK.js} +1 -1
  102. package/dist/webdist/assets/{sizeCapture-X5ZJPWSS-BHOY8h1t.js → sizeCapture-X5ZJPWSS-tYK0sN8k.js} +1 -1
  103. package/dist/webdist/assets/{skeleton-C1KITEIJ.js → skeleton-1pxWnguF.js} +1 -1
  104. package/dist/webdist/assets/{sliders-horizontal-D48--I0k.js → sliders-horizontal-CJnL4ema.js} +1 -1
  105. package/dist/webdist/assets/{sparkles-DDOla5js.js → sparkles-CtVbnpZt.js} +1 -1
  106. package/dist/webdist/assets/{star-B2buMvXV.js → star-BWg7n83j.js} +1 -1
  107. package/dist/webdist/assets/{stateDiagram-OKZ733FA-VUJTdkvj.js → stateDiagram-OKZ733FA-DdMFYbZ9.js} +1 -1
  108. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-Pg2RCR6-.js +1 -0
  109. package/dist/webdist/assets/{swimlanes-SLNWSIFB-MjxXyCvR.js → swimlanes-SLNWSIFB-DgWqKJOe.js} +2 -2
  110. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-D-HgKzxH.js +8 -0
  111. package/dist/webdist/assets/{switch-Cs9dlnwb.js → switch-ChtEKTkl.js} +1 -1
  112. package/dist/webdist/assets/{terminal-CiOZKaJb.js → terminal-BksEQh4H.js} +1 -1
  113. package/dist/webdist/assets/{timeline-definition-Z64GVDOM-Bw8Iivgn.js → timeline-definition-Z64GVDOM-CrwOPdAi.js} +1 -1
  114. package/dist/webdist/assets/{trash-2-DbQqCHSH.js → trash-2-BXZkODa0.js} +1 -1
  115. package/dist/webdist/assets/{triangle-alert-Buguln0S.js → triangle-alert-tsQmbLn8.js} +1 -1
  116. package/dist/webdist/assets/{useIsDesktop-BdbkbmUH.js → useIsDesktop-BKLpQjiw.js} +1 -1
  117. package/dist/webdist/assets/{vennDiagram-T6HMQDX7-UptwMBTb.js → vennDiagram-T6HMQDX7-B7_yA6Pn.js} +1 -1
  118. package/dist/webdist/assets/{wardleyDiagram-T6FBY63Y-DBzcNfG5.js → wardleyDiagram-T6FBY63Y-CImMUYg_.js} +1 -1
  119. package/dist/webdist/assets/{wrench-ByDKNN2K.js → wrench-CuSXx_Zn.js} +1 -1
  120. package/dist/webdist/assets/{xychartDiagram-ELKLHX3M-DrO-tWl0.js → xychartDiagram-ELKLHX3M-B2wGD83d.js} +1 -1
  121. package/dist/webdist/index.html +2 -2
  122. package/package.json +1 -1
  123. package/dist/webdist/assets/ModelDetailDialog-CCmZhT_-.js +0 -1
  124. package/dist/webdist/assets/ModelsScreen-B1eaJCK_.js +0 -2
  125. package/dist/webdist/assets/WorkspaceScreen-a7V07mMZ.js +0 -1
  126. package/dist/webdist/assets/channel-DS6QHRxP.js +0 -1
  127. package/dist/webdist/assets/classDiagram-JCYQIIEL-DmMoUKDN.js +0 -1
  128. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-DmMoUKDN.js +0 -1
  129. package/dist/webdist/assets/index-D44YD6EJ.css +0 -1
  130. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-DslkN__b.js +0 -1
  131. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-CQoli8MP.js +0 -8
package/README.md CHANGED
@@ -208,7 +208,7 @@ the detail:
208
208
  - **Full load-parameter UI**, a superset of what other tools expose: context length, GPU offload
209
209
  (`-ngl`), **MoE CPU-offload (`--n-cpu-moe`)**, parallel slots, **independent K and V cache-quant
210
210
  type** (incl. low-bit on supporting forks), CPU threads, flash attention, **speculative decoding
211
- (NextN / MTP / draft, with a configurable draft min/max window)**, a **pinned engine port** per
211
+ (NextN / MTP / draft / DFlash, with a configurable draft min/max window)**, a **pinned engine port** per
212
212
  model, and a **custom/raw flags** field for anything not exposed as its own control.
213
213
  - **Copy the exact launch command** for a loaded model — runs the same config standalone with
214
214
  llama.cpp/the fork directly, no TurboLLM required.
package/dist/cli.js CHANGED
@@ -2637,9 +2637,12 @@ function detectKvTypes(helpText, hasCacheTypeFlag) {
2637
2637
  return kvTypes;
2638
2638
  }
2639
2639
  function parseEnumList(blockText) {
2640
- const labeled = /allowed values:\s*([\s\S]+)/i.exec(blockText);
2641
- const bracketed = /\[([^\]\n]+)\]/.exec(blockText);
2642
- const raw = labeled?.[1] ?? bracketed?.[1];
2640
+ let raw = /allowed values:\s*([\s\S]+)/i.exec(blockText)?.[1] ?? /(?:^|[ \t])\[([^\]\n]+)\]/.exec(blockText)?.[1];
2641
+ if (!raw) {
2642
+ const braceMatch = /(?:^|[ \t])\{([^}]+)\}/.exec(blockText);
2643
+ const parenMatch = /(?:^|[ \t])\(([^)|]+)\)/.exec(blockText);
2644
+ raw = braceMatch?.[1] ?? parenMatch?.[1];
2645
+ }
2643
2646
  if (!raw) return [];
2644
2647
  const values = raw.split(/[,|]/).map((s) => s.trim()).filter((s) => /^[a-z][a-z0-9_-]*$/i.test(s));
2645
2648
  return values.length >= 2 ? values : [];
@@ -2648,7 +2651,7 @@ function classifyFlag(flagName, helpText) {
2648
2651
  const escaped = flagName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2649
2652
  const ALIAS = "-{1,2}[a-zA-Z][a-zA-Z0-9-]*";
2650
2653
  const re = new RegExp(
2651
- `(?:^|\\n)[ \\t]*(?:${ALIAS}[ \\t]*,[ \\t]*)*${escaped}(?![-a-zA-Z0-9])(?:[ \\t]*,[ \\t]*${ALIAS})*( *)(\\S+)?([\\s\\S]{0,400}?)(?=\\n\\s*\\(default|\\n[ \\t]*${ALIAS}|\\n\\n|$)`,
2654
+ `(?:^|\\n)[ \\t]*(?:${ALIAS}[ \\t]*,[ \\t]*)*${escaped}(?![-a-zA-Z0-9])(?:[ \\t]*,[ \\t]*${ALIAS})*( *)(\\S+)?([\\s\\S]+?)(?=\\n\\s*\\(default|\\n[ \\t]*${ALIAS}|\\n\\n|$)`,
2652
2655
  "i"
2653
2656
  );
2654
2657
  const m = re.exec(helpText);
@@ -2656,11 +2659,24 @@ function classifyFlag(flagName, helpText) {
2656
2659
  const gap = m[1] ?? "";
2657
2660
  const placeholder = m[2] ?? "";
2658
2661
  const block = m[3] ?? "";
2659
- const enumValues = parseEnumList(placeholder + "\n" + block);
2662
+ const nextFlagLine = block.match(/\n[ \t]*-{1,2}[a-zA-Z][a-zA-Z0-9-]*/);
2663
+ const ownBlock = nextFlagLine ? block.slice(0, nextFlagLine.index) : block;
2664
+ const scanText = placeholder + "\n" + ownBlock;
2665
+ const enumValues = parseEnumList(scanText);
2660
2666
  if (enumValues.length > 0) return { name: flagName, kind: "enum", enumValues };
2667
+ const bracket = parseEnumBrackets(scanText);
2668
+ if (bracket.length > 0) return { name: flagName, kind: "enum", enumValues: bracket };
2661
2669
  if (gap.length !== 1 || !placeholder) return { name: flagName, kind: "boolean" };
2662
2670
  return { name: flagName, kind: "valued" };
2663
2671
  }
2672
+ function parseEnumBrackets(blockText) {
2673
+ const braceMatch = /(?:^|[ \t])\{([^}]+)\}/.exec(blockText);
2674
+ const parenMatch = /(?:^|[ \t])\(([^)|]+)\)/.exec(blockText);
2675
+ const raw = braceMatch?.[1] ?? parenMatch?.[1];
2676
+ if (!raw) return [];
2677
+ const values = raw.split(/[|,]/).map((s) => s.trim()).filter((s) => /^[a-z][a-z0-9_-]*$/i.test(s));
2678
+ return values.length >= 2 ? values : [];
2679
+ }
2664
2680
  function firstNonEmptyLine(s) {
2665
2681
  for (const ln of s.split("\n")) {
2666
2682
  const t = ln.trim();
@@ -2887,9 +2903,11 @@ function suggestEngineName(binPath, version2) {
2887
2903
 
2888
2904
  // src/engines/build-runner.ts
2889
2905
  var execFileP8 = promisify8(execFile10);
2906
+ function chooseEngineName(name, priorName) {
2907
+ return name ?? priorName ?? "";
2908
+ }
2890
2909
  function buildDirName(repoUrl, branch, commit) {
2891
- const last = repoUrl.trim().replace(/\/+$/, "").split(/[\\/]/).pop() ?? "";
2892
- const repo = last.replace(/\.git$/i, "").trim() || "engine";
2910
+ const repo = normRepoUrl(repoUrl) || "engine";
2893
2911
  const b = (branch ?? "").trim();
2894
2912
  const sha = (commit ?? "").trim();
2895
2913
  const raw = sha ? `${repo}-${sha.slice(0, 12)}` : b ? `${repo}-${b}` : repo;
@@ -2900,7 +2918,7 @@ function sameRepo(a, b) {
2900
2918
  return na !== "" && na === normRepoUrl(b);
2901
2919
  }
2902
2920
  function normRepoUrl(s) {
2903
- return (s ?? "").trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^github\.com\//, "").replace(/\.git$/, "").replace(/\/+$/, "");
2921
+ return (s ?? "").trim().toLowerCase().replace(/^https?:\/\//, "").replace(/^github\.com\//, "").replace(/\/+$/, "").replace(/\.git$/, "");
2904
2922
  }
2905
2923
  function sourceBuildBinary(enginesRoot, repoUrl, branch, commit) {
2906
2924
  const root = join13(enginesRoot, "build", buildDirName(repoUrl, branch, commit));
@@ -3662,7 +3680,11 @@ function customSourceKey(s) {
3662
3680
  }
3663
3681
  function isStaleCapabilities(flags, flagInfo) {
3664
3682
  if (flags.includes("--spec-type") && !flags.some((f2) => f2.startsWith("spec-type:"))) return true;
3665
- if (["--draft-max", "--draft-min", "--draft", "--draft-n", "--draft-n-min"].some((f2) => flags.includes(f2))) return true;
3683
+ const removed = ["--draft-max", "--draft-min", "--draft", "--draft-n", "--draft-n-min"];
3684
+ const successor = ["--spec-draft-n-max", "--spec-draft-n-min"];
3685
+ const hasRemoved = removed.some((f2) => flags.includes(f2));
3686
+ const hasSuccessor = successor.some((f2) => flags.includes(f2));
3687
+ if (hasRemoved && hasSuccessor) return true;
3666
3688
  if (flagInfo === void 0) return true;
3667
3689
  return false;
3668
3690
  }
@@ -3830,7 +3852,7 @@ var modelLoad = defineEvent({
3830
3852
  batchSize: f.int({ optional: true }),
3831
3853
  uBatchSize: f.int({ optional: true }),
3832
3854
  cacheReuse: f.int(),
3833
- speculative: f.enum(["off", "mtp", "nextn", "draft"]),
3855
+ speculative: f.enum(["off", "mtp", "nextn", "draft", "dflash"]),
3834
3856
  contextOverflow: f.enum(["shift", "keep"]),
3835
3857
  nKeep: f.int(),
3836
3858
  ropeScalingType: f.enum(["none", "linear", "yarn"]),
@@ -4614,7 +4636,20 @@ import { EventEmitter as EventEmitter2 } from "events";
4614
4636
 
4615
4637
  // src/chat/chat-routes.ts
4616
4638
  import { streamSSE } from "hono/streaming";
4639
+
4640
+ // src/net.ts
4617
4641
  import { networkInterfaces } from "os";
4642
+ var VIRTUAL_ADAPTER_NAME = /^(vEthernet|Loopback|Virtual|VMware|VirtualBox|Hyper-V|Docker|WSL|Tailscale|ZeroTier|Npcap|TAP-|PPP|Bluetooth)/i;
4643
+ function getLanIp() {
4644
+ const candidates = [];
4645
+ for (const [name, ifaces] of Object.entries(networkInterfaces())) {
4646
+ for (const iface of ifaces ?? []) {
4647
+ if (iface.family === "IPv4" && !iface.internal) candidates.push({ name, address: iface.address });
4648
+ }
4649
+ }
4650
+ const real = candidates.find((c) => !VIRTUAL_ADAPTER_NAME.test(c.name));
4651
+ return (real ?? candidates[0])?.address ?? null;
4652
+ }
4618
4653
 
4619
4654
  // src/chat/parser.ts
4620
4655
  var THINK_OPEN = "<think>";
@@ -5617,7 +5652,11 @@ async function withPinnedModel(deps2, pinnedModel, fn) {
5617
5652
  return { outcome: "ran" };
5618
5653
  }
5619
5654
  const swapResult = await deps2.modelRouter.loadExplicit(pinnedModel);
5620
- if ("status" in swapResult) return { outcome: "skip-load-failed", message: swapResult.message };
5655
+ if ("status" in swapResult) {
5656
+ if (swapResult.message.includes("ComfyUI is rendering"))
5657
+ return { outcome: "skip-comfyui-busy" };
5658
+ return { outcome: "skip-load-failed", message: swapResult.message };
5659
+ }
5621
5660
  try {
5622
5661
  await fn();
5623
5662
  } finally {
@@ -8474,6 +8513,10 @@ function finalizeSwapOutcome(d, runId, swap, ranStatus) {
8474
8513
  d.db.updateRoutineRun(runId, { status: "skipped", skipReason: "model_busy", endedAt });
8475
8514
  return "skipped";
8476
8515
  }
8516
+ if (swap.outcome === "skip-comfyui-busy") {
8517
+ d.db.updateRoutineRun(runId, { status: "skipped", skipReason: "comfyui_busy", endedAt });
8518
+ return "skipped";
8519
+ }
8477
8520
  d.db.updateRoutineRun(runId, { status: "errored", error: swap.message, endedAt });
8478
8521
  return "errored";
8479
8522
  }
@@ -9098,7 +9141,7 @@ function estimateVramPerGpu(p, m, sys) {
9098
9141
  const blocks = m.blockCount > 0 ? m.blockCount : 1;
9099
9142
  const sizeMb = m.sizeBytes / 1e6;
9100
9143
  const perLayerMb = sizeMb * GPU_WEIGHT_FRACTION / blocks;
9101
- const residentLayers = m.moe ? blocks : Math.max(0, Math.min(p.ngl, blocks));
9144
+ const residentLayers = Math.max(0, Math.min(p.ngl, blocks));
9102
9145
  const nCpuMoe = m.moe ? Math.max(0, Math.min(p.nCpuMoe, blocks)) : 0;
9103
9146
  const layers = new Array(n).fill(0);
9104
9147
  if (p.gpu?.splitMode === "none" || n === 1) {
@@ -9305,6 +9348,10 @@ function profileToArgs(p, m, caps, cores = 0, sys, binPath) {
9305
9348
  if (specType) a.push("--spec-type", "draft");
9306
9349
  a.push("--model-draft", p.draftModelPath);
9307
9350
  specActive = true;
9351
+ } else if (p.speculative === "dflash" && p.draftModelPath && specType && specAccepts("draft-dflash") && has("--spec-draft-model")) {
9352
+ a.push("--spec-type", "draft-dflash");
9353
+ a.push("--spec-draft-model", p.draftModelPath);
9354
+ specActive = true;
9308
9355
  }
9309
9356
  if (specActive) {
9310
9357
  if (has("--draft-max")) a.push("--draft-max", String(p.draftMax ?? 16));
@@ -10142,14 +10189,7 @@ function importOpenAiMessages(c, db2, messages, titleField, modelField) {
10142
10189
  return c.json({ id: newConv.id }, 201);
10143
10190
  }
10144
10191
  function getLanIpForShare() {
10145
- const nets = networkInterfaces();
10146
- for (const ifaces of Object.values(nets)) {
10147
- if (!ifaces) continue;
10148
- for (const iface of ifaces) {
10149
- if (iface.family === "IPv4" && !iface.internal) return iface.address;
10150
- }
10151
- }
10152
- return "127.0.0.1";
10192
+ return getLanIp() ?? "127.0.0.1";
10153
10193
  }
10154
10194
  function reportChatBenchResult(d, ms, stats) {
10155
10195
  try {
@@ -10816,6 +10856,7 @@ import {
10816
10856
  SettingsManager,
10817
10857
  isEditToolResult
10818
10858
  } from "@earendil-works/pi-coding-agent";
10859
+ import { estimateTokens, calculateContextTokens } from "@earendil-works/pi-coding-agent";
10819
10860
 
10820
10861
  // src/code/robust-bash.ts
10821
10862
  import { access as fsAccess } from "fs/promises";
@@ -12574,7 +12615,35 @@ ${lines.join("\n")}${more}]`
12574
12615
  }
12575
12616
  const finalText = session.getLastAssistantText() ?? "";
12576
12617
  const stats = session.getSessionStats();
12577
- const contextUsed = stats.contextUsage?.tokens ?? stats.tokens.total ?? 0;
12618
+ let contextUsed;
12619
+ if (stats.contextUsage?.tokens != null) {
12620
+ contextUsed = stats.contextUsage.tokens;
12621
+ } else {
12622
+ let lastUsage;
12623
+ let lastUsageIdx = -1;
12624
+ for (let i = session.messages.length - 1; i >= 0; i--) {
12625
+ const m = session.messages[i];
12626
+ if (m.role === "assistant" && "usage" in m) {
12627
+ const u = m.usage;
12628
+ if (u && calculateContextTokens(u) > 0) {
12629
+ lastUsage = u;
12630
+ lastUsageIdx = i;
12631
+ break;
12632
+ }
12633
+ }
12634
+ }
12635
+ if (lastUsage) {
12636
+ contextUsed = calculateContextTokens(lastUsage);
12637
+ for (let i = lastUsageIdx + 1; i < session.messages.length; i++) {
12638
+ contextUsed += estimateTokens(session.messages[i]);
12639
+ }
12640
+ } else {
12641
+ contextUsed = 0;
12642
+ for (const m of session.messages) {
12643
+ contextUsed += estimateTokens(m);
12644
+ }
12645
+ }
12646
+ }
12578
12647
  const contextMax = stats.contextUsage?.contextWindow ?? contextWindow;
12579
12648
  const usage = foldTurnUsage(stats.tokens, { promptMsTotal, genMsTotal, ttftMs, totalMs: Date.now() - turnStartedAt });
12580
12649
  session.dispose();
@@ -12674,7 +12743,10 @@ async function compactCodeSession(params) {
12674
12743
  compactionUpToMessageId: upToMessageId,
12675
12744
  compactionTokensBefore: result.tokensBefore
12676
12745
  });
12677
- return { summary: result.summary, upToMessageId, tokensBefore: result.tokensBefore };
12746
+ const summaryTokens = Math.ceil(result.summary.length / 4);
12747
+ const ctxUsed = compactionSettings.keepRecentTokens + summaryTokens;
12748
+ d.db.setCodeSessionCtxUsed(sessionId, ctxUsed);
12749
+ return { summary: result.summary, upToMessageId, tokensBefore: result.tokensBefore, ctxUsed };
12678
12750
  }
12679
12751
  async function lookbackPreCompactionHistory(params) {
12680
12752
  const { d, convId, sessionId, repoRoot, question, signal } = params;
@@ -13758,7 +13830,7 @@ var EMBED_ARCHS = /* @__PURE__ */ new Set([
13758
13830
  "xlm-roberta",
13759
13831
  "electra"
13760
13832
  ]);
13761
- var EMBED_FILE_RE = /\b(bge[-_]|nomic[-_]embed|all[-_]minilm|e5[-_]|gte[-_]|stella[-_]embed|jina[-_]embed|mxbai[-_]embed)\b/i;
13833
+ var EMBED_FILE_RE = /\b(bge[-_]|nomic[-_]embed|all[-_]minilm|e5[-_]|gte[-_]|stella[-_]embed|jina[-_]embed|mxbai[-_]embed|embed(ding)?)\b/i;
13762
13834
  function isEmbeddingModel(arch2, name) {
13763
13835
  return EMBED_ARCHS.has(arch2.toLowerCase()) || EMBED_FILE_RE.test(name);
13764
13836
  }
@@ -15318,6 +15390,10 @@ var ConversationStore = class {
15318
15390
  `);
15319
15391
  this.db.exec(`PRAGMA user_version = 48;`);
15320
15392
  }
15393
+ if (v < 49) {
15394
+ if (!this.hasColumn("api_usage", "ctx_used")) this.db.exec(`ALTER TABLE api_usage ADD COLUMN ctx_used INTEGER DEFAULT 0;`);
15395
+ this.db.exec(`PRAGMA user_version = 49;`);
15396
+ }
15321
15397
  }
15322
15398
  listConversations(q, kind = "all") {
15323
15399
  if (q) {
@@ -15688,8 +15764,8 @@ var ConversationStore = class {
15688
15764
  recordApiUsage(rec) {
15689
15765
  const rate = (v) => v != null && Number.isFinite(v) && v > 0 ? v : null;
15690
15766
  this.db.prepare(`
15691
- INSERT INTO api_usage (id, created_at, source, model_key, prompt_tokens, gen_tokens, code_session_id, duration_ms, prompt_tps, gen_tps, harness)
15692
- VALUES ($id, $createdAt, $source, $modelKey, $promptTokens, $genTokens, $codeSessionId, $durationMs, $promptTps, $genTps, $harness)
15767
+ INSERT INTO api_usage (id, created_at, source, model_key, prompt_tokens, gen_tokens, code_session_id, duration_ms, prompt_tps, gen_tps, harness, ctx_used)
15768
+ VALUES ($id, $createdAt, $source, $modelKey, $promptTokens, $genTokens, $codeSessionId, $durationMs, $promptTps, $genTps, $harness, $ctxUsed)
15693
15769
  `).run({
15694
15770
  $id: randomUUID6(),
15695
15771
  $createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -15701,9 +15777,40 @@ var ConversationStore = class {
15701
15777
  $durationMs: rec.durationMs != null && Number.isFinite(rec.durationMs) ? Math.max(0, Math.floor(rec.durationMs)) : null,
15702
15778
  $promptTps: rate(rec.promptTps),
15703
15779
  $genTps: rate(rec.genTps),
15704
- $harness: rec.harness ?? null
15780
+ $harness: rec.harness ?? null,
15781
+ $ctxUsed: Math.max(0, Math.floor(rec.promptTokens) || 0)
15705
15782
  });
15706
15783
  }
15784
+ /** Lower the current context size for a Code session after /compact, so the ctx gauge
15785
+ * reflects post-compaction reality (ADR-386). Updates the row with the MAX ctx_used
15786
+ * to the new value — this is what allows /compact to lower the ring's frozen ceiling.
15787
+ * If no row exists yet (fresh session compacted before first gateway request), creates one. */
15788
+ setCodeSessionCtxUsed(codeSessionId, ctxUsed) {
15789
+ const maxRow = this.db.prepare(`
15790
+ SELECT id FROM api_usage
15791
+ WHERE code_session_id = $codeSessionId AND ctx_used = (
15792
+ SELECT MAX(ctx_used) FROM api_usage WHERE code_session_id = $codeSessionId
15793
+ )
15794
+ LIMIT 1
15795
+ `).get({ $codeSessionId: codeSessionId });
15796
+ if (maxRow) {
15797
+ this.db.prepare(`
15798
+ UPDATE api_usage SET ctx_used = $ctxUsed, prompt_tokens = $promptTokens, created_at = $createdAt
15799
+ WHERE id = $id
15800
+ `).run({ $ctxUsed: ctxUsed, $promptTokens: ctxUsed, $createdAt: (/* @__PURE__ */ new Date()).toISOString(), $id: maxRow.id });
15801
+ } else {
15802
+ this.db.prepare(`
15803
+ INSERT INTO api_usage (id, created_at, source, prompt_tokens, gen_tokens, code_session_id, ctx_used)
15804
+ VALUES ($id, $createdAt, 'compact', $promptTokens, 0, $codeSessionId, $ctxUsed)
15805
+ `).run({
15806
+ $id: randomUUID6(),
15807
+ $createdAt: (/* @__PURE__ */ new Date()).toISOString(),
15808
+ $promptTokens: ctxUsed,
15809
+ $codeSessionId: codeSessionId,
15810
+ $ctxUsed: ctxUsed
15811
+ });
15812
+ }
15813
+ }
15707
15814
  /** The session's own gateway-request stats (ADR-284, revised twice — founder-reported "ctx
15708
15815
  * fill jumps between full and empty in the same session", then a review pass on that same fix
15709
15816
  * catching that it had gone on to freeze the LAST-TURN stats too). Two different questions,
@@ -15737,14 +15844,14 @@ var ConversationStore = class {
15737
15844
  * non-terminal-agent one). */
15738
15845
  getLastApiUsageForSession(codeSessionId) {
15739
15846
  const row = this.db.prepare(`
15740
- SELECT prompt_tokens, gen_tokens, duration_ms, prompt_tps, gen_tps FROM api_usage
15847
+ SELECT prompt_tokens, gen_tokens, duration_ms, prompt_tps, gen_tps, ctx_used FROM api_usage
15741
15848
  WHERE code_session_id = $codeSessionId
15742
15849
  ORDER BY created_at DESC LIMIT 1
15743
15850
  `).get({ $codeSessionId: codeSessionId });
15744
15851
  if (!row) return null;
15745
15852
  const ctxUsed = this.db.prepare(`
15746
- SELECT MAX(prompt_tokens) as maxPromptTokens FROM api_usage WHERE code_session_id = $codeSessionId
15747
- `).get({ $codeSessionId: codeSessionId }).maxPromptTokens;
15853
+ SELECT MAX(ctx_used) as maxCtxUsed FROM api_usage WHERE code_session_id = $codeSessionId
15854
+ `).get({ $codeSessionId: codeSessionId }).maxCtxUsed;
15748
15855
  const seconds = row.duration_ms != null && row.duration_ms > 0 ? row.duration_ms / 1e3 : null;
15749
15856
  return {
15750
15857
  ctxUsed,
@@ -19915,12 +20022,12 @@ var ModelRouter = class {
19915
20022
  * skipped. Callers are responsible for their OWN idle-vs-busy decision before calling this
19916
20023
  * (see routines/model-swap.ts) — this method has no opinion on whether now is a safe time to
19917
20024
  * swap, only on HOW to swap once that's decided. */
19918
- async loadExplicit(modelKey) {
20025
+ async loadExplicit(modelKey, overrides) {
19919
20026
  const remote = this.resolveRemote(modelKey);
19920
20027
  if (remote) return remote;
19921
20028
  const entry = this.resolveEntry(modelKey);
19922
20029
  if (!entry) return { status: 503, message: `No model matching '${modelKey}' found. Add one in TurboLLM.` };
19923
- return this.withSwapLock(() => this.doLoad(entry));
20030
+ return this.withSwapLock(() => this.doLoad(entry, overrides));
19924
20031
  }
19925
20032
  /** Acquire the SAME swap-serialization queue `route()` uses, then run `fn` exclusively with
19926
20033
  * respect to any other swap (manual or auto). `route()` itself is just this wrapping
@@ -19983,7 +20090,7 @@ var ModelRouter = class {
19983
20090
  return keys;
19984
20091
  }
19985
20092
  // ── internal ──────────────────────────────────────────────────────────────
19986
- async doLoad(entry) {
20093
+ async doLoad(entry, overrides) {
19987
20094
  {
19988
20095
  const ms = this.manager.status();
19989
20096
  if (ms.state === "running" && ms.model && this.keysMatch(ms.model.key, entry)) {
@@ -20006,7 +20113,7 @@ var ModelRouter = class {
20006
20113
  if (!engineAcceptsFormat(active.kind, entry.format)) {
20007
20114
  return { status: 503, message: `Active engine cannot load model format '${entry.format}'.` };
20008
20115
  }
20009
- const opts = this.buildOpts(entry, active);
20116
+ const opts = this.buildOpts(entry, active, overrides);
20010
20117
  if (!opts) return { status: 503, message: "Model is incomplete or unreadable." };
20011
20118
  const keepN = Math.max(1, this.store.snapshot().gateway.keepN);
20012
20119
  const needsNewSlot = entry.embedding || this.chatSlotCount() < keepN;
@@ -20144,13 +20251,13 @@ var ModelRouter = class {
20144
20251
  keysMatch(loadedKey, entry) {
20145
20252
  return loadedKey === entry.key || loadedKey === entry.path;
20146
20253
  }
20147
- buildOpts(entry, engine) {
20254
+ buildOpts(entry, engine, overrides) {
20148
20255
  if (entry.incomplete || entry.parseError) return null;
20149
20256
  const cfg2 = this.store.snapshot();
20150
20257
  const sys = getSysInfo();
20151
20258
  if (entry.format !== "gguf") {
20152
20259
  const savedProfile = getModelProfile(cfg2, entry.key, engine.id);
20153
- const profile2 = resolveProfile(entry, sys, savedProfile, void 0, cfg2.modelDefaults);
20260
+ const profile2 = resolveProfile(entry, sys, savedProfile, overrides, cfg2.modelDefaults);
20154
20261
  return {
20155
20262
  engine,
20156
20263
  model: { key: entry.key, name: entry.name, quant: entry.quant, ctx: entry.nativeCtx, vision: entry.vision },
@@ -20163,7 +20270,7 @@ var ModelRouter = class {
20163
20270
  };
20164
20271
  }
20165
20272
  const saved = getModelProfile(cfg2, entry.key, engine.id);
20166
- const profile = resolveProfile(entry, sys, saved, void 0, cfg2.modelDefaults);
20273
+ const profile = resolveProfile(entry, sys, saved, overrides, cfg2.modelDefaults);
20167
20274
  const extraArgs = engine.kind === "koboldcpp" ? koboldcppProfileToArgs(profile, sys.gpus[0]?.vendor ?? "unknown", sys.gpus.length > 0) : profileToArgs(profile, entry, engine.capabilities, sys.cores, sys, engine.binPath);
20168
20275
  return {
20169
20276
  engine,
@@ -22400,7 +22507,7 @@ __all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
22400
22507
 
22401
22508
  // src/api/routes.ts
22402
22509
  import { randomUUID as randomUUID11 } from "crypto";
22403
- import { homedir as homedir4, networkInterfaces as networkInterfaces2 } from "os";
22510
+ import { homedir as homedir4 } from "os";
22404
22511
 
22405
22512
  // src/config/config-bounds.ts
22406
22513
  var CONFIG_BOUNDS = {
@@ -22506,7 +22613,10 @@ var ALL = [
22506
22613
  support: "stable",
22507
22614
  // llama.cpp expands into the backend sub-picker (existing UI); it has no single
22508
22615
  // install endpoint of its own.
22509
- installEndpoint: ""
22616
+ installEndpoint: "",
22617
+ // Its installed state comes from the separate backend-build detection (LlamaCppBackendRows),
22618
+ // not the generic sourceRepo match below — see the field's own doc comment (ADR-388).
22619
+ excludeFromSourceMatch: true
22510
22620
  },
22511
22621
  {
22512
22622
  // Upstream publishes NO Linux CUDA prebuilt (download.ts availableBackends() — Linux
@@ -23972,9 +24082,6 @@ async function startEngine(c, d, b) {
23972
24082
  const active = d.registry.active();
23973
24083
  if (!active) return err4(c, 409, "no_active_engine", "Register and select an engine first.");
23974
24084
  if (d.comfy?.isBlocked()) return err4(c, 409, "comfyui_busy", "ComfyUI is rendering \u2014 model loading is paused until its queue finishes.");
23975
- d.bench.cancel();
23976
- abortAllInFlightChats();
23977
- await d.bench.waitIdle();
23978
24085
  const cfg2 = d.store.snapshot();
23979
24086
  const sys = getSysInfo();
23980
24087
  let key = b.modelKey ?? "";
@@ -23997,6 +24104,13 @@ async function startEngine(c, d, b) {
23997
24104
  `${engineLabel} cannot load models with an audio tower \u2014 the audio encoder ${certainty} due to an upstream mlx-vlm bug in the sanitizer for these architectures. Switch to the MLX engine instead.`
23998
24105
  );
23999
24106
  }
24107
+ if (entry.embedding) {
24108
+ void d.modelRouter.loadExplicit(entry.key, b.profileOverrides).catch((e) => console.warn(`engine load failed: ${e}`));
24109
+ return c.json({ ok: true }, 202);
24110
+ }
24111
+ d.bench.cancel();
24112
+ abortAllInFlightChats();
24113
+ await d.bench.waitIdle();
24000
24114
  let opts2;
24001
24115
  if (entry.format !== "gguf") {
24002
24116
  const savedProfile = getModelProfile(cfg2, entry.key, active.id);
@@ -24041,6 +24155,9 @@ async function startEngine(c, d, b) {
24041
24155
  name = cfg2.devModel.label;
24042
24156
  }
24043
24157
  if (!modelPath) return err4(c, 409, "no_such_model", "No model specified. Pick one from the Models screen.");
24158
+ d.bench.cancel();
24159
+ abortAllInFlightChats();
24160
+ await d.bench.waitIdle();
24044
24161
  const opts = { engine: active, model: deriveModel(modelPath, name, extra), modelPath, extraArgs: extra };
24045
24162
  void d.modelRouter.withSwapLock(() => d.manager.load(opts, { beforeStart: () => d.comfy?.freeComfyUIBeforeLoad() ?? Promise.resolve() })).then(() => d.modelRouter.markPrimaryLoaded()).catch((e) => console.warn(`engine load failed: ${e}`));
24046
24163
  return c.json({ ok: true }, 202);
@@ -24501,11 +24618,11 @@ function registerApi(app2, d) {
24501
24618
  installed2 = existsSync26(llamafileBinPath(enginesRoot));
24502
24619
  enabled = regEngines.some((x) => x.kind === "llamafile");
24503
24620
  }
24504
- const srcEng = regEngines.find(
24621
+ const srcEng = e.excludeFromSourceMatch ? void 0 : regEngines.find(
24505
24622
  (x) => sameRepo(x.sourceRepo, e.homepage) && (x.sourceCommit ?? "") === (e.sourceCommit ?? "") && (x.sourcePatchUrl ?? "") === (e.patchUrl ?? "")
24506
24623
  );
24507
24624
  let sourceBinPath = srcEng?.binPath;
24508
- if (!srcEng) sourceBinPath = sourceBuildBinary(enginesRoot, e.homepage, void 0, e.sourceCommit) ?? void 0;
24625
+ if (!srcEng && !e.excludeFromSourceMatch) sourceBinPath = sourceBuildBinary(enginesRoot, e.homepage, void 0, e.sourceCommit) ?? void 0;
24509
24626
  const sourceBuilt = !!srcEng || !!sourceBinPath;
24510
24627
  if (srcEng) {
24511
24628
  installed2 = true;
@@ -24604,7 +24721,7 @@ function registerApi(app2, d) {
24604
24721
  } catch {
24605
24722
  }
24606
24723
  }
24607
- const eng = (await d.registry.add(prior?.name ?? name ?? "", out.binPath, {
24724
+ const eng = (await d.registry.add(chooseEngineName(name, prior?.name), out.binPath, {
24608
24725
  sourceRepo: repoUrl,
24609
24726
  sourceBranch: branch,
24610
24727
  sourceCommit: commit,
@@ -25501,7 +25618,7 @@ function registerApi(app2, d) {
25501
25618
  const cfg2 = d.store.snapshot();
25502
25619
  return c.json({
25503
25620
  lanBind: cfg2.daemon.lanBind,
25504
- lanUrl: `http://${getLanIp()}:${cfg2.daemon.port}`,
25621
+ lanUrl: `http://${getLanIp() ?? "127.0.0.1"}:${cfg2.daemon.port}`,
25505
25622
  hasApiKey: cfg2.apiKeys.length > 0,
25506
25623
  requireApiKey: cfg2.daemon.requireApiKey,
25507
25624
  isHost: isLocalRequest(c, d)
@@ -25671,7 +25788,7 @@ function registerApi(app2, d) {
25671
25788
  const cli = c.req.param("cli");
25672
25789
  const cfg2 = d.store.snapshot();
25673
25790
  const { port: port2, lanBind } = cfg2.daemon;
25674
- const host2 = lanBind ? getLanIp() : "127.0.0.1";
25791
+ const host2 = lanBind ? getLanIp() ?? "127.0.0.1" : "127.0.0.1";
25675
25792
  const base2 = `http://${host2}:${port2}`;
25676
25793
  const ms = d.manager.status();
25677
25794
  const modelName = ms.state === "running" ? ms.model?.name ?? "local" : "local";
@@ -25989,16 +26106,6 @@ function listWindowsDrives() {
25989
26106
  }
25990
26107
  return drives;
25991
26108
  }
25992
- function getLanIp() {
25993
- const nets = networkInterfaces2();
25994
- for (const ifaces of Object.values(nets)) {
25995
- if (!ifaces) continue;
25996
- for (const iface of ifaces) {
25997
- if (iface.family === "IPv4" && !iface.internal) return iface.address;
25998
- }
25999
- }
26000
- return "127.0.0.1";
26001
- }
26002
26109
  function buildConnectSnippets(cli, base2, apiKey, modelName, modelKey = modelName) {
26003
26110
  switch (cli) {
26004
26111
  case "claude-code":
@@ -26162,7 +26269,6 @@ function buildConnectSnippets(cli, base2, apiKey, modelName, modelKey = modelNam
26162
26269
 
26163
26270
  // src/api/link-admin-routes.ts
26164
26271
  import { randomUUID as randomUUID12 } from "crypto";
26165
- import { networkInterfaces as networkInterfaces3 } from "os";
26166
26272
 
26167
26273
  // src/link/link-state.ts
26168
26274
  function nextStatus(current, probe2) {
@@ -27134,12 +27240,7 @@ function remoteFailure(c, rec, probe2) {
27134
27240
  );
27135
27241
  }
27136
27242
  function lanHost() {
27137
- for (const iface of Object.values(networkInterfaces3())) {
27138
- for (const a of iface ?? []) {
27139
- if (a.family === "IPv4" && !a.internal) return a.address;
27140
- }
27141
- }
27142
- return "localhost";
27243
+ return getLanIp() ?? "localhost";
27143
27244
  }
27144
27245
 
27145
27246
  // src/chat/chat-agent-routes.ts
@@ -31148,14 +31249,14 @@ import { streamSSE as streamSSE5 } from "hono/streaming";
31148
31249
  // src/ext/context-limit.ts
31149
31250
  var CHARS_PER_TOKEN2 = 3.5;
31150
31251
  var REPLY_HEADROOM = 512;
31151
- function estimateTokens(messages) {
31252
+ function estimateTokens2(messages) {
31152
31253
  let chars = 0;
31153
31254
  for (const m of messages) chars += m.content.length + m.role.length + 4;
31154
31255
  return Math.ceil(chars / CHARS_PER_TOKEN2);
31155
31256
  }
31156
31257
  function checkContextFits(d, messages) {
31157
31258
  const limit = d.manager.status().model?.ctx ?? 0;
31158
- const estimated = estimateTokens(messages);
31259
+ const estimated = estimateTokens2(messages);
31159
31260
  if (!limit) return { fits: true, estimated, limit: 0 };
31160
31261
  return { fits: estimated + REPLY_HEADROOM <= limit, estimated, limit };
31161
31262
  }
@@ -0,0 +1,10 @@
1
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
2
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
3
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
4
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
5
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
6
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
7
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
8
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
9
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
10
+ {"t":0,"agent":"a0686ab","agent_type":"unknown","event":"agent_stop","success":true}
@@ -0,0 +1,7 @@
1
+ {
2
+ "agents": [],
3
+ "total_spawned": 0,
4
+ "total_completed": 0,
5
+ "total_failed": 0,
6
+ "last_updated": "2026-09-01T14:34:13.310Z"
7
+ }
@@ -1 +1 @@
1
- import{j as e}from"./radix-B0YBjihE.js";import{b as W,u as $,r as b}from"./vendor-C1I6euDx.js";import{u as G,d as H,e as Y,f as J,g as U,a as z,B as C,t as j,c as q,b as h,A as _}from"./index-D0Emka7d.js";import{f as V}from"./chat-api-Zyxy4wka.js";import{f as X,a as Z}from"./agent-api-CmhXBBT6.js";import{P as B}from"./personas-CeAQ6niU.js";import{C as ee}from"./chevron-left-CBtdrcDZ.js";import{R as te}from"./rotate-ccw-BpPatpxx.js";import{T as se}from"./trash-2-DbQqCHSH.js";import{C as ne}from"./chevron-right-DRLCoDa8.js";import"./dropdown-menu-DRRO0ftt.js";const ae=()=>({name:"",description:"",systemPrompt:"",skillIds:[],tools:[]});function oe(s){const d=[],x=new Map;for(const n of s){const a=/^mcp__([^_]+(?:_[^_]+)*)__/.exec(n.name);if(!a){d.push(n);continue}const i=a[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,a)=>n.label.localeCompare(a.label))}}function le(s){return s.replace(/^mcp__[^_]+(?:_[^_]+)*__/,"")}function re(s){return s.replace(/^\[[^\]]+\]\s*/,"")}function D({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:le(s.name)}),s.description&&e.jsx("span",{className:"text-faint",children:re(s.description)})]})]})}function ie({group:s,selected:d,onToggleAll:x,onToggleOne:n}){const[a,i]=b.useState(!1),p=s.tools.map(l=>l.name),c=p.filter(l=>d.has(l)).length,o=c===p.length,g=c>0&&!o,u=b.useRef(null);return b.useEffect(()=>{u.current&&(u.current.indeterminate=g)},[g]),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:o,onChange:()=>x(p,!o)}),e.jsxs("button",{type:"button",onClick:()=>{j("agents","toggle_agent_tool_group"),i(l=>!l)},className:"flex flex-1 items-center gap-1.5 text-left text-[13px] text-ink",children:[e.jsx(ne,{size:12,className:q("shrink-0 text-faint transition-transform",a&&"rotate-90")}),s.label,e.jsxs("span",{className:"text-[11px] text-faint",children:["(",c,"/",p.length,")"]})]})]}),a&&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(D,{tool:l,checked:d.has(l.name),onToggle:()=>n(l.name)},l.name))})]})}function ce({tools:s,selected:d,onChange:x}){const n=new Set(d),{builtin:a,groups:i}=oe(s),p=o=>x(n.has(o)?d.filter(g=>g!==o):[...d,o]),c=(o,g)=>{const u=d.filter(l=>!o.includes(l));x(g?[...u,...o]: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:[a.length>0&&e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:a.map(o=>e.jsx(D,{tool:o,checked:n.has(o.name),onToggle:()=>p(o.name)},o.name))}),i.map(o=>e.jsx(ie,{group:o,selected:n,onToggleAll:c,onToggleOne:p},o.key))]})}function je(){const{agentId:s="new"}=W(),d=$(),x=G(),n=s==="new",a=!n&&B.some(t=>t.id===s),i=a?B.find(t=>t.id===s):void 0,p=H(),c=!n&&!a?(p.data??[]).find(t=>t.id===s):void 0,o=Y(),g=J(),u=U(),l=a?g.data?.[s]:void 0,I=z({queryKey:Z.list(),queryFn:X,staleTime:0}),k=I.data??[],w=z({queryKey:["available-tools"],queryFn:V}),N=w.data??[],[m,f]=b.useState(ae()),[S,T]=b.useState(!1),[A,P]=b.useState(!1),[L,R]=b.useState(!1),[E,Q]=b.useState("skills");n&&!S&&(k.length||N.length)&&(f(t=>({...t,skillIds:k.map(r=>r.id),tools:N.map(r=>r.name)})),T(!0)),!n&&!a&&!S&&c&&(f({name:c.name,description:c.description,systemPrompt:c.systemPrompt,skillIds:c.skillIds,tools:c.tools}),T(!0)),a&&!S&&i&&g.isSuccess&&(k.length||N.length)&&(f({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??N.map(t=>t.name)}),T(!0));const y=()=>{j("agents","back_to_agents"),d("/customize")},O=t=>f(r=>({...r,skillIds:r.skillIds.includes(t)?r.skillIds.filter(v=>v!==t):[...r.skillIds,t]})),F=()=>{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(a){u.save.mutate({id:s,override:t},{onSuccess:()=>{h.success("Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}});return}const r={onSuccess:()=>{x.invalidateQueries({queryKey:["chat-agents"]}),h.success(n?"Agent created.":"Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}};n?o.add.mutate(t,r):o.update.mutate({id:s,patch:t},r)},K=()=>{u.reset.mutate(s,{onSuccess:()=>{h.success("Reset to default."),i&&f({name:i.name,description:i.description,systemPrompt:i.systemPrompt,skillIds:k.map(t=>t.id),tools:N.map(t=>t.name)})},onError:t=>h.error(t instanceof _?t.message:"Could not reset agent.")})},M=()=>{o.remove.mutate(s,{onSuccess:()=>{h.success("Agent deleted."),y()},onError:t=>h.error(t instanceof _?t.message:"Could not delete agent.")})};return!n&&!a&&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(C,{size:"sm",variant:"outline",onClick:y,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:y,className:"text-faint hover:text-ink",title:"Back to agents",children:e.jsx(ee,{size:18})}),e.jsx("span",{className:"text-[15px] font-medium text-ink",children:n?"New agent":a?l?"Edit agent (modified built-in)":"Edit agent (built-in)":"Edit agent"}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[a&&e.jsxs(C,{size:"sm",variant:"outline",onClick:()=>{j("agents","reset_agent_to_default"),K()},disabled:u.reset.isPending,children:[e.jsx(te,{size:13})," Reset to default"]}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Cancel"}),e.jsx(C,{size:"sm",onClick:()=>{j("agents","save_agent"),F()},disabled:A||!m.name.trim(),children:A?"Saving…":"Save"})]})]}),a&&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=>f(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=>f(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=>f(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:()=>{j("agents","switch_agent_form_tab"),Q(t)},className:q("rounded px-3 py-1.5 text-[13px] font-medium transition-colors",E===t?"bg-accent/12 text-accent":"text-muted hover:text-ink"),children:t==="skills"?`Skills (${m.skillIds.length})`:`Tools (${m.tools.length})`},t))}),E==="skills"?I.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:()=>O(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))}):w.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):e.jsx(ce,{tools:N,selected:m.tools,onChange:t=>f(r=>({...r,tools:t}))})]}),!n&&!a&&(L?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:()=>{j("agents","delete_agent"),M()},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:()=>{j("agents","cancel_delete_agent"),R(!1)},className:"rounded px-2 py-1 text-faint hover:text-ink",children:"Cancel"})]}):e.jsxs("button",{type:"button",onClick:()=>R(!0),className:"flex items-center gap-1.5 self-start text-[12px] text-faint hover:text-[color:var(--err)]",children:[e.jsx(se,{size:13})," Delete agent"]}))]})}export{je as AgentEditPage};
1
+ import{j as e}from"./radix-B0YBjihE.js";import{b as W,u as $,r as b}from"./vendor-C1I6euDx.js";import{u as G,d as H,e as Y,f as J,g as U,a as z,B as C,t as j,c as q,b as h,A as _}from"./index-C9ZXfFYo.js";import{f as V}from"./chat-api-Bgrb0rnf.js";import{f as X,a as Z}from"./agent-api-BAMDY8-v.js";import{P as B}from"./personas-Dkq9JD72.js";import{C as ee}from"./chevron-left-CxvUI3ck.js";import{R as te}from"./rotate-ccw-kHM_pFpt.js";import{T as se}from"./trash-2-BXZkODa0.js";import{C as ne}from"./chevron-right-CpdnK5S2.js";import"./dropdown-menu-CaecUWTU.js";const ae=()=>({name:"",description:"",systemPrompt:"",skillIds:[],tools:[]});function oe(s){const d=[],x=new Map;for(const n of s){const a=/^mcp__([^_]+(?:_[^_]+)*)__/.exec(n.name);if(!a){d.push(n);continue}const i=a[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,a)=>n.label.localeCompare(a.label))}}function le(s){return s.replace(/^mcp__[^_]+(?:_[^_]+)*__/,"")}function re(s){return s.replace(/^\[[^\]]+\]\s*/,"")}function D({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:le(s.name)}),s.description&&e.jsx("span",{className:"text-faint",children:re(s.description)})]})]})}function ie({group:s,selected:d,onToggleAll:x,onToggleOne:n}){const[a,i]=b.useState(!1),p=s.tools.map(l=>l.name),c=p.filter(l=>d.has(l)).length,o=c===p.length,g=c>0&&!o,u=b.useRef(null);return b.useEffect(()=>{u.current&&(u.current.indeterminate=g)},[g]),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:o,onChange:()=>x(p,!o)}),e.jsxs("button",{type:"button",onClick:()=>{j("agents","toggle_agent_tool_group"),i(l=>!l)},className:"flex flex-1 items-center gap-1.5 text-left text-[13px] text-ink",children:[e.jsx(ne,{size:12,className:q("shrink-0 text-faint transition-transform",a&&"rotate-90")}),s.label,e.jsxs("span",{className:"text-[11px] text-faint",children:["(",c,"/",p.length,")"]})]})]}),a&&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(D,{tool:l,checked:d.has(l.name),onToggle:()=>n(l.name)},l.name))})]})}function ce({tools:s,selected:d,onChange:x}){const n=new Set(d),{builtin:a,groups:i}=oe(s),p=o=>x(n.has(o)?d.filter(g=>g!==o):[...d,o]),c=(o,g)=>{const u=d.filter(l=>!o.includes(l));x(g?[...u,...o]: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:[a.length>0&&e.jsx("div",{className:"flex flex-col gap-2 rounded-md border border-border bg-panel-2 p-3",children:a.map(o=>e.jsx(D,{tool:o,checked:n.has(o.name),onToggle:()=>p(o.name)},o.name))}),i.map(o=>e.jsx(ie,{group:o,selected:n,onToggleAll:c,onToggleOne:p},o.key))]})}function je(){const{agentId:s="new"}=W(),d=$(),x=G(),n=s==="new",a=!n&&B.some(t=>t.id===s),i=a?B.find(t=>t.id===s):void 0,p=H(),c=!n&&!a?(p.data??[]).find(t=>t.id===s):void 0,o=Y(),g=J(),u=U(),l=a?g.data?.[s]:void 0,I=z({queryKey:Z.list(),queryFn:X,staleTime:0}),k=I.data??[],w=z({queryKey:["available-tools"],queryFn:V}),N=w.data??[],[m,f]=b.useState(ae()),[S,T]=b.useState(!1),[A,P]=b.useState(!1),[L,R]=b.useState(!1),[E,Q]=b.useState("skills");n&&!S&&(k.length||N.length)&&(f(t=>({...t,skillIds:k.map(r=>r.id),tools:N.map(r=>r.name)})),T(!0)),!n&&!a&&!S&&c&&(f({name:c.name,description:c.description,systemPrompt:c.systemPrompt,skillIds:c.skillIds,tools:c.tools}),T(!0)),a&&!S&&i&&g.isSuccess&&(k.length||N.length)&&(f({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??N.map(t=>t.name)}),T(!0));const y=()=>{j("agents","back_to_agents"),d("/customize")},O=t=>f(r=>({...r,skillIds:r.skillIds.includes(t)?r.skillIds.filter(v=>v!==t):[...r.skillIds,t]})),F=()=>{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(a){u.save.mutate({id:s,override:t},{onSuccess:()=>{h.success("Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}});return}const r={onSuccess:()=>{x.invalidateQueries({queryKey:["chat-agents"]}),h.success(n?"Agent created.":"Agent saved."),y()},onError:v=>{h.error(v instanceof _?v.message:"Could not save agent."),P(!1)}};n?o.add.mutate(t,r):o.update.mutate({id:s,patch:t},r)},K=()=>{u.reset.mutate(s,{onSuccess:()=>{h.success("Reset to default."),i&&f({name:i.name,description:i.description,systemPrompt:i.systemPrompt,skillIds:k.map(t=>t.id),tools:N.map(t=>t.name)})},onError:t=>h.error(t instanceof _?t.message:"Could not reset agent.")})},M=()=>{o.remove.mutate(s,{onSuccess:()=>{h.success("Agent deleted."),y()},onError:t=>h.error(t instanceof _?t.message:"Could not delete agent.")})};return!n&&!a&&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(C,{size:"sm",variant:"outline",onClick:y,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:y,className:"text-faint hover:text-ink",title:"Back to agents",children:e.jsx(ee,{size:18})}),e.jsx("span",{className:"text-[15px] font-medium text-ink",children:n?"New agent":a?l?"Edit agent (modified built-in)":"Edit agent (built-in)":"Edit agent"}),e.jsxs("div",{className:"ml-auto flex items-center gap-2",children:[a&&e.jsxs(C,{size:"sm",variant:"outline",onClick:()=>{j("agents","reset_agent_to_default"),K()},disabled:u.reset.isPending,children:[e.jsx(te,{size:13})," Reset to default"]}),e.jsx(C,{size:"sm",variant:"outline",onClick:y,children:"Cancel"}),e.jsx(C,{size:"sm",onClick:()=>{j("agents","save_agent"),F()},disabled:A||!m.name.trim(),children:A?"Saving…":"Save"})]})]}),a&&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=>f(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=>f(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=>f(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:()=>{j("agents","switch_agent_form_tab"),Q(t)},className:q("rounded px-3 py-1.5 text-[13px] font-medium transition-colors",E===t?"bg-accent/12 text-accent":"text-muted hover:text-ink"),children:t==="skills"?`Skills (${m.skillIds.length})`:`Tools (${m.tools.length})`},t))}),E==="skills"?I.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:()=>O(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))}):w.isLoading?e.jsx("p",{className:"text-[12px] text-faint",children:"Loading…"}):e.jsx(ce,{tools:N,selected:m.tools,onChange:t=>f(r=>({...r,tools:t}))})]}),!n&&!a&&(L?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:()=>{j("agents","delete_agent"),M()},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:()=>{j("agents","cancel_delete_agent"),R(!1)},className:"rounded px-2 py-1 text-faint hover:text-ink",children:"Cancel"})]}):e.jsxs("button",{type:"button",onClick:()=>R(!0),className:"flex items-center gap-1.5 self-start text-[12px] text-faint hover:text-[color:var(--err)]",children:[e.jsx(se,{size:13})," Delete agent"]}))]})}export{je as AgentEditPage};