turbollm 1.11.8 → 1.12.0

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 (130) hide show
  1. package/README.md +1 -1
  2. package/dist/cli.js +188 -26
  3. package/dist/webdist/assets/{AgentEditPage-BEYv2dqk.js → AgentEditPage-Cl9VgfYt.js} +1 -1
  4. package/dist/webdist/assets/{ChatScreen-Be3s4Apt.js → ChatScreen-D0JIgp7C.js} +1 -1
  5. package/dist/webdist/assets/{CodeComposer-UXWorvxM.js → CodeComposer-Cqtowy4i.js} +1 -1
  6. package/dist/webdist/assets/{CodeHomeScreen-DoLM7wZ7.js → CodeHomeScreen-fGeu4-IS.js} +1 -1
  7. package/dist/webdist/assets/{CodeSessionScreen-B2_E1nGk.js → CodeSessionScreen-CqBDC3e4.js} +1 -1
  8. package/dist/webdist/assets/{CustomizeScreen-oXSoHBmc.js → CustomizeScreen-BTzZdj7z.js} +1 -1
  9. package/dist/webdist/assets/{DeveloperScreen-XtW_r1xe.js → DeveloperScreen-D40_GOxu.js} +1 -1
  10. package/dist/webdist/assets/{EnginesScreen-DFPKVQmY.js → EnginesScreen-CPKMB1MS.js} +1 -1
  11. package/dist/webdist/assets/ModelDetailDialog-PySMSzRc.js +1 -0
  12. package/dist/webdist/assets/{ModelLoadMenu-DIsk-jZz.js → ModelLoadMenu-BgkEIVli.js} +1 -1
  13. package/dist/webdist/assets/{ModelsScreen-CaPBgqJn.js → ModelsScreen-Bi_cdOOR.js} +1 -1
  14. package/dist/webdist/assets/{OnboardingScreen-DKB8rxlq.js → OnboardingScreen-BVyp58am.js} +1 -1
  15. package/dist/webdist/assets/{RoutineEditPage-HxYBqm4v.js → RoutineEditPage-BtL6uHNS.js} +1 -1
  16. package/dist/webdist/assets/{RoutinesPanel-DG2Sxz7g.js → RoutinesPanel-Bb0LP98-.js} +1 -1
  17. package/dist/webdist/assets/{SettingsScreen-CXP_EZLC.js → SettingsScreen-Dsz1JZPU.js} +1 -1
  18. package/dist/webdist/assets/{SidebarResizeHandle-BEjYiHxx.js → SidebarResizeHandle-B9IvMWnc.js} +1 -1
  19. package/dist/webdist/assets/{SkillEditPage-CFLxX-PG.js → SkillEditPage-zPuoTSEK.js} +1 -1
  20. package/dist/webdist/assets/{TokensScreen-EAg6SpPg.js → TokensScreen-DGfd8Z_h.js} +1 -1
  21. package/dist/webdist/assets/ToolApprovalBar-CjF6OSEN.js +21 -0
  22. package/dist/webdist/assets/WorkspaceScreen-BvQS-6fo.js +1 -0
  23. package/dist/webdist/assets/{abnfDiagram-N423BO3Z-Com8iOkP.js → abnfDiagram-N423BO3Z-BHtFnGEG.js} +1 -1
  24. package/dist/webdist/assets/{agent-api-Bbbnu920.js → agent-api-C-qZrrYO.js} +1 -1
  25. package/dist/webdist/assets/{alert-dialog-DBTMGpVB.js → alert-dialog-CjxDGiZR.js} +1 -1
  26. package/dist/webdist/assets/{arc-CxAPh_XT.js → arc-CxTBdZ4R.js} +1 -1
  27. package/dist/webdist/assets/{architectureDiagram-T3A2C74G-cmEV3ecV.js → architectureDiagram-T3A2C74G-7PcBBjT5.js} +1 -1
  28. package/dist/webdist/assets/{arrow-left-CqNHa_nR.js → arrow-left-B6hlnKUy.js} +1 -1
  29. package/dist/webdist/assets/{badge-DLVv8YlA.js → badge-DUS8w956.js} +1 -1
  30. package/dist/webdist/assets/{blockDiagram-VBNYF7ZC-Dzszoo8X.js → blockDiagram-VBNYF7ZC-yJQM294Z.js} +1 -1
  31. package/dist/webdist/assets/{bot-DFV9NaDI.js → bot-92SGv3lK.js} +1 -1
  32. package/dist/webdist/assets/{c4Diagram-5PPSVZJV-CT06LjAB.js → c4Diagram-5PPSVZJV-DGo_IrnA.js} +1 -1
  33. package/dist/webdist/assets/channel-B6B2Q5Vd.js +1 -0
  34. package/dist/webdist/assets/{chat-api-CmDwSCUy.js → chat-api-DUobixOH.js} +1 -1
  35. package/dist/webdist/assets/{chat-queries-opwsCDIy.js → chat-queries-DIGUy3mB.js} +1 -1
  36. package/dist/webdist/assets/{chevron-down-DiyKHxC-.js → chevron-down-Cg4gkXkY.js} +1 -1
  37. package/dist/webdist/assets/{chevron-left-CwO2nWWZ.js → chevron-left-CRkSrIuw.js} +1 -1
  38. package/dist/webdist/assets/{chevron-right-Ck02yYBx.js → chevron-right-D6YKa1nw.js} +1 -1
  39. package/dist/webdist/assets/{chunk-2GRJ4B5K-Blj1u1OW.js → chunk-2GRJ4B5K-4BRNCKh4.js} +1 -1
  40. package/dist/webdist/assets/{chunk-2Q5K7J3B-VRS9aN4X.js → chunk-2Q5K7J3B-VVErlkzz.js} +1 -1
  41. package/dist/webdist/assets/{chunk-5RXB4S5H-CoTOA-QY.js → chunk-5RXB4S5H-BdrEcHfH.js} +1 -1
  42. package/dist/webdist/assets/{chunk-5VM5RSS4-DigOrHns.js → chunk-5VM5RSS4-B7ZoD7jT.js} +1 -1
  43. package/dist/webdist/assets/{chunk-6Q2QTUOP-MsJl24GT.js → chunk-6Q2QTUOP-CZboDQFa.js} +1 -1
  44. package/dist/webdist/assets/{chunk-GF5L2VYU-xgSZn13t.js → chunk-GF5L2VYU-Bxfejn_T.js} +1 -1
  45. package/dist/webdist/assets/{chunk-JWPE2WC7-DJ7ETxvg.js → chunk-JWPE2WC7-CR53ctin.js} +1 -1
  46. package/dist/webdist/assets/{chunk-KBJHAD2P-DypQCgcq.js → chunk-KBJHAD2P-CI9fYV8V.js} +1 -1
  47. package/dist/webdist/assets/{chunk-RYQCIY6F-0xN4i17N.js → chunk-RYQCIY6F-CEka5LWj.js} +1 -1
  48. package/dist/webdist/assets/{chunk-XXDRQBXY-CLumzVtu.js → chunk-XXDRQBXY-B6ToGKiT.js} +1 -1
  49. package/dist/webdist/assets/{circle-check-CM_EzD0B.js → circle-check-NUkbKKsX.js} +1 -1
  50. package/dist/webdist/assets/{circle-x-DDCTEjWL.js → circle-x-BI2C2Gpz.js} +1 -1
  51. package/dist/webdist/assets/classDiagram-JCYQIIEL-Nr9H5qaY.js +1 -0
  52. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-Nr9H5qaY.js +1 -0
  53. package/dist/webdist/assets/{common-BmIDcygp.js → common-BDUb2Jp_.js} +1 -1
  54. package/dist/webdist/assets/{cose-bilkent-JH36ORCC-CctbomzF.js → cose-bilkent-JH36ORCC-n-mMCinT.js} +1 -1
  55. package/dist/webdist/assets/{cynefin-VYW2F7L2-CAOiUD35.js → cynefin-VYW2F7L2--M6hM_u0.js} +1 -1
  56. package/dist/webdist/assets/{cynefinDiagram-MW4NZA55-BmenpAyg.js → cynefinDiagram-MW4NZA55-C1WV5_wf.js} +1 -1
  57. package/dist/webdist/assets/{dagre-VZM6K2ZE-CUSeYeIs.js → dagre-VZM6K2ZE-BrvE8li2.js} +1 -1
  58. package/dist/webdist/assets/{diagram-7IWD3JNH-CTmJsE0m.js → diagram-7IWD3JNH-BAtvwqyn.js} +1 -1
  59. package/dist/webdist/assets/{diagram-B4RE2ZJO-D5tXNWQT.js → diagram-B4RE2ZJO-DIvlrS5g.js} +1 -1
  60. package/dist/webdist/assets/{diagram-LBJQPF4R-5tR892LO.js → diagram-LBJQPF4R-BzTddnYn.js} +1 -1
  61. package/dist/webdist/assets/{diagram-Q27KOJAE-BgZg9KkM.js → diagram-Q27KOJAE-U-iZs4DE.js} +1 -1
  62. package/dist/webdist/assets/{diagram-UB23O5K3-DzD2EEfH.js → diagram-UB23O5K3-CaFGOdAj.js} +1 -1
  63. package/dist/webdist/assets/{dropdown-menu-CaMJbKX5.js → dropdown-menu-D5Vf7U3W.js} +1 -1
  64. package/dist/webdist/assets/{ebnfDiagram-BXEA7PRR-BsE4pbuB.js → ebnfDiagram-BXEA7PRR-WEkh8Cpp.js} +1 -1
  65. package/dist/webdist/assets/{erDiagram-JOGREHBK-BSNniK5W.js → erDiagram-JOGREHBK-DIPsvu0C.js} +1 -1
  66. package/dist/webdist/assets/{external-link-B73_NSLw.js → external-link-DuVH0_sU.js} +1 -1
  67. package/dist/webdist/assets/{file-text-SkW8GJLf.js → file-text-B8I_V3Ev.js} +1 -1
  68. package/dist/webdist/assets/{flowDiagram-UKHOOZJN-LTHTwtbo.js → flowDiagram-UKHOOZJN-D0Il5rli.js} +1 -1
  69. package/dist/webdist/assets/{folder-input-DdDYio8z.js → folder-input-n_yC89H9.js} +1 -1
  70. package/dist/webdist/assets/{folder-plus-By4aZIqn.js → folder-plus-CC1f-3oh.js} +1 -1
  71. package/dist/webdist/assets/{ganttDiagram-PKOTCBZU-DhWk0fZI.js → ganttDiagram-PKOTCBZU-8KyY9Pa4.js} +1 -1
  72. package/dist/webdist/assets/{git-branch-C6MWgM1t.js → git-branch-CQA2JECJ.js} +1 -1
  73. package/dist/webdist/assets/{gitGraphDiagram-DS77QQ5N-Cnp_ieRP.js → gitGraphDiagram-DS77QQ5N-DnwR5_g_.js} +1 -1
  74. package/dist/webdist/assets/index-D44YD6EJ.css +1 -0
  75. package/dist/webdist/assets/{index-DDdTmEU4.js → index-hEBbEVm0.js} +2 -2
  76. package/dist/webdist/assets/{infoDiagram-6WML65LV-BfVAgcHy.js → infoDiagram-6WML65LV-B4Ao9xBO.js} +1 -1
  77. package/dist/webdist/assets/{ishikawaDiagram-WSZJBQD7-rRt3dfj6.js → ishikawaDiagram-WSZJBQD7-BIH-3xyR.js} +1 -1
  78. package/dist/webdist/assets/{journeyDiagram-NVQOT4AX-BSy-shCM.js → journeyDiagram-NVQOT4AX-CB7VA190.js} +1 -1
  79. package/dist/webdist/assets/{kanban-definition-27J2QSJJ-DGOAYCO1.js → kanban-definition-27J2QSJJ-t0MLGJxS.js} +1 -1
  80. package/dist/webdist/assets/{linear-CS_iPZyK.js → linear-C2UcHtLH.js} +1 -1
  81. package/dist/webdist/assets/{link-queries-DvYdzQm8.js → link-queries-Dd_UY0A-.js} +1 -1
  82. package/dist/webdist/assets/{lock-_paqX_bJ.js → lock-BaRe3TFE.js} +1 -1
  83. package/dist/webdist/assets/{mermaid.core-CPhj_3A-.js → mermaid.core-Do_AkPs7.js} +4 -4
  84. package/dist/webdist/assets/{mindmap-definition-FAOFIHXS-CZbX1tkX.js → mindmap-definition-FAOFIHXS-C0FjLEZb.js} +1 -1
  85. package/dist/webdist/assets/{monitor-Buq_3Xsq.js → monitor-nPLFkAL-.js} +1 -1
  86. package/dist/webdist/assets/{pegDiagram-VL7TDLO6-PpIZIssA.js → pegDiagram-VL7TDLO6-DMP_1SBp.js} +1 -1
  87. package/dist/webdist/assets/{pencil-D9a-4tCA.js → pencil-DIAK3lkm.js} +1 -1
  88. package/dist/webdist/assets/personas-Dkq9JD72.js +109 -0
  89. package/dist/webdist/assets/{pieDiagram-7S7Q4E2Y-vt-POtMx.js → pieDiagram-7S7Q4E2Y-RG_slaJ2.js} +1 -1
  90. package/dist/webdist/assets/{plus-DP3JYEma.js → plus-Cd2hb3vG.js} +1 -1
  91. package/dist/webdist/assets/{quadrantDiagram-CIZ2JOQS-Yr9F3dX4.js → quadrantDiagram-CIZ2JOQS-6wV3EG0F.js} +1 -1
  92. package/dist/webdist/assets/{railroadDiagram-AXF67PYL-CZ7FpINV.js → railroadDiagram-AXF67PYL-flgltuTY.js} +1 -1
  93. package/dist/webdist/assets/{refresh-cw-bRtvR0Iv.js → refresh-cw-MAW86aWv.js} +1 -1
  94. package/dist/webdist/assets/{remote-failure-agE6nknQ.js → remote-failure-DTP9EDi2.js} +1 -1
  95. package/dist/webdist/assets/{requirementDiagram-LRYGKXZP-BUmCzzYZ.js → requirementDiagram-LRYGKXZP-CXpi-87D.js} +1 -1
  96. package/dist/webdist/assets/{rocket-BFudVOBf.js → rocket-B5uDLuGn.js} +1 -1
  97. package/dist/webdist/assets/{rotate-ccw-CTfSFG3x.js → rotate-ccw-DVWlxS6M.js} +1 -1
  98. package/dist/webdist/assets/{sankeyDiagram-W5VNT64P-BuLGkmEP.js → sankeyDiagram-W5VNT64P-C50KvAmS.js} +1 -1
  99. package/dist/webdist/assets/{sequenceDiagram-SI44F4Z6-D8vmzIyW.js → sequenceDiagram-SI44F4Z6-CsT039Tm.js} +1 -1
  100. package/dist/webdist/assets/{sizeCapture-X5ZJPWSS-DYgUs44t.js → sizeCapture-X5ZJPWSS-BImTTCUc.js} +1 -1
  101. package/dist/webdist/assets/{skeleton-kNhR6J5G.js → skeleton-DnvNxbj8.js} +1 -1
  102. package/dist/webdist/assets/{sliders-horizontal-GlSfzSwu.js → sliders-horizontal-DsS2Uee5.js} +1 -1
  103. package/dist/webdist/assets/{sparkles-CJPHP-dp.js → sparkles-BU19nV47.js} +1 -1
  104. package/dist/webdist/assets/{star-BDNzpKYY.js → star-DcvDKUjt.js} +1 -1
  105. package/dist/webdist/assets/{stateDiagram-OKZ733FA-Sztzkc4z.js → stateDiagram-OKZ733FA-Go1nImaV.js} +1 -1
  106. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-CNOImueL.js +1 -0
  107. package/dist/webdist/assets/{swimlanes-SLNWSIFB-CWR36cS5.js → swimlanes-SLNWSIFB-D8aYBeg_.js} +2 -2
  108. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-u93o-5nq.js +8 -0
  109. package/dist/webdist/assets/{switch-CJAukDSs.js → switch-DRDUQUBQ.js} +1 -1
  110. package/dist/webdist/assets/{terminal-5a1B-6y4.js → terminal-BaGcFc0D.js} +1 -1
  111. package/dist/webdist/assets/{timeline-definition-Z64GVDOM-Bs1ZNlGU.js → timeline-definition-Z64GVDOM-BCYicvUs.js} +1 -1
  112. package/dist/webdist/assets/{trash-2-BNNKBgll.js → trash-2-dTSieVsH.js} +1 -1
  113. package/dist/webdist/assets/{triangle-alert-vMymArLW.js → triangle-alert-BQok4bN-.js} +1 -1
  114. package/dist/webdist/assets/{useIsDesktop-BWN1ktaj.js → useIsDesktop-D7McLbCu.js} +1 -1
  115. package/dist/webdist/assets/{vennDiagram-T6HMQDX7-bfP-4l5-.js → vennDiagram-T6HMQDX7-BvWE6mUb.js} +1 -1
  116. package/dist/webdist/assets/{wardleyDiagram-T6FBY63Y-CT_Drudb.js → wardleyDiagram-T6FBY63Y-DId8SDFX.js} +1 -1
  117. package/dist/webdist/assets/{wrench-BleM5jpH.js → wrench-wnE0c5Ir.js} +1 -1
  118. package/dist/webdist/assets/{xychartDiagram-ELKLHX3M-BeJSlb68.js → xychartDiagram-ELKLHX3M-DAFHE1Oy.js} +1 -1
  119. package/dist/webdist/index.html +2 -2
  120. package/package.json +1 -1
  121. package/dist/webdist/assets/ModelDetailDialog-Bf99Dj9I.js +0 -1
  122. package/dist/webdist/assets/ToolApprovalBar-BdNLuB0r.js +0 -21
  123. package/dist/webdist/assets/WorkspaceScreen-C2X82u27.js +0 -1
  124. package/dist/webdist/assets/channel-C1ooZeJ8.js +0 -1
  125. package/dist/webdist/assets/classDiagram-JCYQIIEL-c_ApQIjk.js +0 -1
  126. package/dist/webdist/assets/classDiagram-v2-OCEON4UE-c_ApQIjk.js +0 -1
  127. package/dist/webdist/assets/index-rQri1Mzm.css +0 -1
  128. package/dist/webdist/assets/personas-B7q-7L3z.js +0 -109
  129. package/dist/webdist/assets/stateDiagram-v2-UEYNNEHI-CaotCWUJ.js +0 -1
  130. package/dist/webdist/assets/swimlanesDiagram-ULZ7WXOC-BwXNVqW3.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"]),
@@ -5617,7 +5639,11 @@ async function withPinnedModel(deps2, pinnedModel, fn) {
5617
5639
  return { outcome: "ran" };
5618
5640
  }
5619
5641
  const swapResult = await deps2.modelRouter.loadExplicit(pinnedModel);
5620
- if ("status" in swapResult) return { outcome: "skip-load-failed", message: swapResult.message };
5642
+ if ("status" in swapResult) {
5643
+ if (swapResult.message.includes("ComfyUI is rendering"))
5644
+ return { outcome: "skip-comfyui-busy" };
5645
+ return { outcome: "skip-load-failed", message: swapResult.message };
5646
+ }
5621
5647
  try {
5622
5648
  await fn();
5623
5649
  } finally {
@@ -6412,6 +6438,27 @@ function isQualifiedId(id) {
6412
6438
 
6413
6439
  // src/cli-launch.ts
6414
6440
  var AUTH_TOKEN = "turbollm-local";
6441
+ var PI_SEARCH_PACKAGE_NAME = "@heyhuynhgiabuu/pi-search";
6442
+ var PI_SEARCH_PACKAGE_SPEC = `${PI_SEARCH_PACKAGE_NAME}@0.3.0`;
6443
+ var PI_SEARCH_INSTALL_DISABLED = () => ["1", "true", "yes"].includes(process.env.TOBOLLM_PI_DISABLE_SEARCH_INSTALL?.toLowerCase() ?? "");
6444
+ var PI_SEARCH_PACKAGES_PREFIX = `npm:${PI_SEARCH_PACKAGE_NAME}@`;
6445
+ var PI_SEARCH_INSTALL_TIMEOUT_MS = 3e4;
6446
+ async function piSearchPackagePresent(fs2) {
6447
+ const path = join17(fs2.home, ".pi", "agent", "settings.json");
6448
+ let raw;
6449
+ try {
6450
+ raw = await fs2.readFile(path);
6451
+ } catch {
6452
+ return false;
6453
+ }
6454
+ let cfg2;
6455
+ try {
6456
+ cfg2 = JSON.parse(stripJsonComments(raw));
6457
+ } catch {
6458
+ return false;
6459
+ }
6460
+ return Array.isArray(cfg2.packages) && cfg2.packages.some((p) => typeof p === "string" && p.startsWith(PI_SEARCH_PACKAGES_PREFIX));
6461
+ }
6415
6462
  var SUPPORTED = {
6416
6463
  claude: {
6417
6464
  bin: "claude",
@@ -6495,6 +6542,18 @@ var realSpawn = (cmd, args, opts) => {
6495
6542
  }
6496
6543
  return spawn4(buildShellCommand(cmd, args), opts);
6497
6544
  };
6545
+ function runWithTimeout(spawnImpl, bin, args, timeoutMs) {
6546
+ return new Promise((resolve6) => {
6547
+ const timer2 = setTimeout(() => resolve6(false), timeoutMs);
6548
+ const child = spawnImpl(bin, args, { stdio: "ignore", shell: process.platform === "win32" });
6549
+ const settle = (code) => {
6550
+ clearTimeout(timer2);
6551
+ resolve6(code === 0);
6552
+ };
6553
+ child.on("error", () => settle(null));
6554
+ child.on("exit", (code) => settle(code));
6555
+ });
6556
+ }
6498
6557
  async function fetchStatus(base2, _fetch = fetch) {
6499
6558
  try {
6500
6559
  const res = await _fetch(`${base2}/api/v1/status`, { signal: AbortSignal.timeout(3e3) });
@@ -6975,6 +7034,27 @@ async function syncHarnessModelConfig(target, opts, fs2 = realFs) {
6975
7034
  AUTH_TOKEN
6976
7035
  );
6977
7036
  }
7037
+ async function ensurePiSearchPackage(run2 = realRunCommand, fs2 = realFs) {
7038
+ if (PI_SEARCH_INSTALL_DISABLED()) return;
7039
+ if (await piSearchPackagePresent(fs2)) return;
7040
+ let ok = false;
7041
+ try {
7042
+ ok = await run2("pi", ["install", `npm:${PI_SEARCH_PACKAGE_SPEC}`]);
7043
+ } catch {
7044
+ ok = false;
7045
+ }
7046
+ if (ok) {
7047
+ process.stderr.write(
7048
+ `Note: installed pi research tools (${PI_SEARCH_PACKAGE_SPEC}); web search is now available. Opt out any time with TOBOLLM_PI_DISABLE_SEARCH_INSTALL=1.
7049
+ `
7050
+ );
7051
+ } else {
7052
+ process.stderr.write(
7053
+ `Note: could not auto-install pi research tools (${PI_SEARCH_PACKAGE_SPEC}); web search will be unavailable until you run: pi install npm:${PI_SEARCH_PACKAGE_SPEC}.
7054
+ `
7055
+ );
7056
+ }
7057
+ }
6978
7058
  async function launchCli(target, port2, passthrough, _spawn = realSpawn, modelKey, _fetch = fetch, authToken, _mcpFs = realFs) {
6979
7059
  const spec = SUPPORTED[target];
6980
7060
  if (!target || !spec) {
@@ -7120,6 +7200,10 @@ Open ${base2} \u2192 Models \u2192 Load a model, then run this again.
7120
7200
  return 1;
7121
7201
  }
7122
7202
  }
7203
+ if (target === "pi") {
7204
+ const piSearchRun = (bin, args2) => runWithTimeout(_spawn, bin, args2, PI_SEARCH_INSTALL_TIMEOUT_MS);
7205
+ await ensurePiSearchPackage(piSearchRun, _mcpFs);
7206
+ }
7123
7207
  const env = {
7124
7208
  ...inheritedEnv(process.env, spec.parentEnvMarkers),
7125
7209
  ...await spec.env?.(launchCtx) ?? {}
@@ -8416,6 +8500,10 @@ function finalizeSwapOutcome(d, runId, swap, ranStatus) {
8416
8500
  d.db.updateRoutineRun(runId, { status: "skipped", skipReason: "model_busy", endedAt });
8417
8501
  return "skipped";
8418
8502
  }
8503
+ if (swap.outcome === "skip-comfyui-busy") {
8504
+ d.db.updateRoutineRun(runId, { status: "skipped", skipReason: "comfyui_busy", endedAt });
8505
+ return "skipped";
8506
+ }
8419
8507
  d.db.updateRoutineRun(runId, { status: "errored", error: swap.message, endedAt });
8420
8508
  return "errored";
8421
8509
  }
@@ -9040,7 +9128,7 @@ function estimateVramPerGpu(p, m, sys) {
9040
9128
  const blocks = m.blockCount > 0 ? m.blockCount : 1;
9041
9129
  const sizeMb = m.sizeBytes / 1e6;
9042
9130
  const perLayerMb = sizeMb * GPU_WEIGHT_FRACTION / blocks;
9043
- const residentLayers = m.moe ? blocks : Math.max(0, Math.min(p.ngl, blocks));
9131
+ const residentLayers = Math.max(0, Math.min(p.ngl, blocks));
9044
9132
  const nCpuMoe = m.moe ? Math.max(0, Math.min(p.nCpuMoe, blocks)) : 0;
9045
9133
  const layers = new Array(n).fill(0);
9046
9134
  if (p.gpu?.splitMode === "none" || n === 1) {
@@ -9247,6 +9335,10 @@ function profileToArgs(p, m, caps, cores = 0, sys, binPath) {
9247
9335
  if (specType) a.push("--spec-type", "draft");
9248
9336
  a.push("--model-draft", p.draftModelPath);
9249
9337
  specActive = true;
9338
+ } else if (p.speculative === "dflash" && p.draftModelPath && specType && specAccepts("draft-dflash") && has("--spec-draft-model")) {
9339
+ a.push("--spec-type", "draft-dflash");
9340
+ a.push("--spec-draft-model", p.draftModelPath);
9341
+ specActive = true;
9250
9342
  }
9251
9343
  if (specActive) {
9252
9344
  if (has("--draft-max")) a.push("--draft-max", String(p.draftMax ?? 16));
@@ -10758,6 +10850,7 @@ import {
10758
10850
  SettingsManager,
10759
10851
  isEditToolResult
10760
10852
  } from "@earendil-works/pi-coding-agent";
10853
+ import { estimateTokens, calculateContextTokens } from "@earendil-works/pi-coding-agent";
10761
10854
 
10762
10855
  // src/code/robust-bash.ts
10763
10856
  import { access as fsAccess } from "fs/promises";
@@ -12516,7 +12609,35 @@ ${lines.join("\n")}${more}]`
12516
12609
  }
12517
12610
  const finalText = session.getLastAssistantText() ?? "";
12518
12611
  const stats = session.getSessionStats();
12519
- const contextUsed = stats.contextUsage?.tokens ?? stats.tokens.total ?? 0;
12612
+ let contextUsed;
12613
+ if (stats.contextUsage?.tokens != null) {
12614
+ contextUsed = stats.contextUsage.tokens;
12615
+ } else {
12616
+ let lastUsage;
12617
+ let lastUsageIdx = -1;
12618
+ for (let i = session.messages.length - 1; i >= 0; i--) {
12619
+ const m = session.messages[i];
12620
+ if (m.role === "assistant" && "usage" in m) {
12621
+ const u = m.usage;
12622
+ if (u && calculateContextTokens(u) > 0) {
12623
+ lastUsage = u;
12624
+ lastUsageIdx = i;
12625
+ break;
12626
+ }
12627
+ }
12628
+ }
12629
+ if (lastUsage) {
12630
+ contextUsed = calculateContextTokens(lastUsage);
12631
+ for (let i = lastUsageIdx + 1; i < session.messages.length; i++) {
12632
+ contextUsed += estimateTokens(session.messages[i]);
12633
+ }
12634
+ } else {
12635
+ contextUsed = 0;
12636
+ for (const m of session.messages) {
12637
+ contextUsed += estimateTokens(m);
12638
+ }
12639
+ }
12640
+ }
12520
12641
  const contextMax = stats.contextUsage?.contextWindow ?? contextWindow;
12521
12642
  const usage = foldTurnUsage(stats.tokens, { promptMsTotal, genMsTotal, ttftMs, totalMs: Date.now() - turnStartedAt });
12522
12643
  session.dispose();
@@ -12616,7 +12737,10 @@ async function compactCodeSession(params) {
12616
12737
  compactionUpToMessageId: upToMessageId,
12617
12738
  compactionTokensBefore: result.tokensBefore
12618
12739
  });
12619
- return { summary: result.summary, upToMessageId, tokensBefore: result.tokensBefore };
12740
+ const summaryTokens = Math.ceil(result.summary.length / 4);
12741
+ const ctxUsed = compactionSettings.keepRecentTokens + summaryTokens;
12742
+ d.db.setCodeSessionCtxUsed(sessionId, ctxUsed);
12743
+ return { summary: result.summary, upToMessageId, tokensBefore: result.tokensBefore, ctxUsed };
12620
12744
  }
12621
12745
  async function lookbackPreCompactionHistory(params) {
12622
12746
  const { d, convId, sessionId, repoRoot, question, signal } = params;
@@ -15260,6 +15384,10 @@ var ConversationStore = class {
15260
15384
  `);
15261
15385
  this.db.exec(`PRAGMA user_version = 48;`);
15262
15386
  }
15387
+ if (v < 49) {
15388
+ if (!this.hasColumn("api_usage", "ctx_used")) this.db.exec(`ALTER TABLE api_usage ADD COLUMN ctx_used INTEGER DEFAULT 0;`);
15389
+ this.db.exec(`PRAGMA user_version = 49;`);
15390
+ }
15263
15391
  }
15264
15392
  listConversations(q, kind = "all") {
15265
15393
  if (q) {
@@ -15630,8 +15758,8 @@ var ConversationStore = class {
15630
15758
  recordApiUsage(rec) {
15631
15759
  const rate = (v) => v != null && Number.isFinite(v) && v > 0 ? v : null;
15632
15760
  this.db.prepare(`
15633
- INSERT INTO api_usage (id, created_at, source, model_key, prompt_tokens, gen_tokens, code_session_id, duration_ms, prompt_tps, gen_tps, harness)
15634
- VALUES ($id, $createdAt, $source, $modelKey, $promptTokens, $genTokens, $codeSessionId, $durationMs, $promptTps, $genTps, $harness)
15761
+ 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)
15762
+ VALUES ($id, $createdAt, $source, $modelKey, $promptTokens, $genTokens, $codeSessionId, $durationMs, $promptTps, $genTps, $harness, $ctxUsed)
15635
15763
  `).run({
15636
15764
  $id: randomUUID6(),
15637
15765
  $createdAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -15643,9 +15771,40 @@ var ConversationStore = class {
15643
15771
  $durationMs: rec.durationMs != null && Number.isFinite(rec.durationMs) ? Math.max(0, Math.floor(rec.durationMs)) : null,
15644
15772
  $promptTps: rate(rec.promptTps),
15645
15773
  $genTps: rate(rec.genTps),
15646
- $harness: rec.harness ?? null
15774
+ $harness: rec.harness ?? null,
15775
+ $ctxUsed: Math.max(0, Math.floor(rec.promptTokens) || 0)
15647
15776
  });
15648
15777
  }
15778
+ /** Lower the current context size for a Code session after /compact, so the ctx gauge
15779
+ * reflects post-compaction reality (ADR-386). Updates the row with the MAX ctx_used
15780
+ * to the new value — this is what allows /compact to lower the ring's frozen ceiling.
15781
+ * If no row exists yet (fresh session compacted before first gateway request), creates one. */
15782
+ setCodeSessionCtxUsed(codeSessionId, ctxUsed) {
15783
+ const maxRow = this.db.prepare(`
15784
+ SELECT id FROM api_usage
15785
+ WHERE code_session_id = $codeSessionId AND ctx_used = (
15786
+ SELECT MAX(ctx_used) FROM api_usage WHERE code_session_id = $codeSessionId
15787
+ )
15788
+ LIMIT 1
15789
+ `).get({ $codeSessionId: codeSessionId });
15790
+ if (maxRow) {
15791
+ this.db.prepare(`
15792
+ UPDATE api_usage SET ctx_used = $ctxUsed, prompt_tokens = $promptTokens, created_at = $createdAt
15793
+ WHERE id = $id
15794
+ `).run({ $ctxUsed: ctxUsed, $promptTokens: ctxUsed, $createdAt: (/* @__PURE__ */ new Date()).toISOString(), $id: maxRow.id });
15795
+ } else {
15796
+ this.db.prepare(`
15797
+ INSERT INTO api_usage (id, created_at, source, prompt_tokens, gen_tokens, code_session_id, ctx_used)
15798
+ VALUES ($id, $createdAt, 'compact', $promptTokens, 0, $codeSessionId, $ctxUsed)
15799
+ `).run({
15800
+ $id: randomUUID6(),
15801
+ $createdAt: (/* @__PURE__ */ new Date()).toISOString(),
15802
+ $promptTokens: ctxUsed,
15803
+ $codeSessionId: codeSessionId,
15804
+ $ctxUsed: ctxUsed
15805
+ });
15806
+ }
15807
+ }
15649
15808
  /** The session's own gateway-request stats (ADR-284, revised twice — founder-reported "ctx
15650
15809
  * fill jumps between full and empty in the same session", then a review pass on that same fix
15651
15810
  * catching that it had gone on to freeze the LAST-TURN stats too). Two different questions,
@@ -15679,14 +15838,14 @@ var ConversationStore = class {
15679
15838
  * non-terminal-agent one). */
15680
15839
  getLastApiUsageForSession(codeSessionId) {
15681
15840
  const row = this.db.prepare(`
15682
- SELECT prompt_tokens, gen_tokens, duration_ms, prompt_tps, gen_tps FROM api_usage
15841
+ SELECT prompt_tokens, gen_tokens, duration_ms, prompt_tps, gen_tps, ctx_used FROM api_usage
15683
15842
  WHERE code_session_id = $codeSessionId
15684
15843
  ORDER BY created_at DESC LIMIT 1
15685
15844
  `).get({ $codeSessionId: codeSessionId });
15686
15845
  if (!row) return null;
15687
15846
  const ctxUsed = this.db.prepare(`
15688
- SELECT MAX(prompt_tokens) as maxPromptTokens FROM api_usage WHERE code_session_id = $codeSessionId
15689
- `).get({ $codeSessionId: codeSessionId }).maxPromptTokens;
15847
+ SELECT MAX(ctx_used) as maxCtxUsed FROM api_usage WHERE code_session_id = $codeSessionId
15848
+ `).get({ $codeSessionId: codeSessionId }).maxCtxUsed;
15690
15849
  const seconds = row.duration_ms != null && row.duration_ms > 0 ? row.duration_ms / 1e3 : null;
15691
15850
  return {
15692
15851
  ctxUsed,
@@ -22448,7 +22607,10 @@ var ALL = [
22448
22607
  support: "stable",
22449
22608
  // llama.cpp expands into the backend sub-picker (existing UI); it has no single
22450
22609
  // install endpoint of its own.
22451
- installEndpoint: ""
22610
+ installEndpoint: "",
22611
+ // Its installed state comes from the separate backend-build detection (LlamaCppBackendRows),
22612
+ // not the generic sourceRepo match below — see the field's own doc comment (ADR-388).
22613
+ excludeFromSourceMatch: true
22452
22614
  },
22453
22615
  {
22454
22616
  // Upstream publishes NO Linux CUDA prebuilt (download.ts availableBackends() — Linux
@@ -24443,11 +24605,11 @@ function registerApi(app2, d) {
24443
24605
  installed2 = existsSync26(llamafileBinPath(enginesRoot));
24444
24606
  enabled = regEngines.some((x) => x.kind === "llamafile");
24445
24607
  }
24446
- const srcEng = regEngines.find(
24608
+ const srcEng = e.excludeFromSourceMatch ? void 0 : regEngines.find(
24447
24609
  (x) => sameRepo(x.sourceRepo, e.homepage) && (x.sourceCommit ?? "") === (e.sourceCommit ?? "") && (x.sourcePatchUrl ?? "") === (e.patchUrl ?? "")
24448
24610
  );
24449
24611
  let sourceBinPath = srcEng?.binPath;
24450
- if (!srcEng) sourceBinPath = sourceBuildBinary(enginesRoot, e.homepage, void 0, e.sourceCommit) ?? void 0;
24612
+ if (!srcEng && !e.excludeFromSourceMatch) sourceBinPath = sourceBuildBinary(enginesRoot, e.homepage, void 0, e.sourceCommit) ?? void 0;
24451
24613
  const sourceBuilt = !!srcEng || !!sourceBinPath;
24452
24614
  if (srcEng) {
24453
24615
  installed2 = true;
@@ -24546,7 +24708,7 @@ function registerApi(app2, d) {
24546
24708
  } catch {
24547
24709
  }
24548
24710
  }
24549
- const eng = (await d.registry.add(prior?.name ?? name ?? "", out.binPath, {
24711
+ const eng = (await d.registry.add(chooseEngineName(name, prior?.name), out.binPath, {
24550
24712
  sourceRepo: repoUrl,
24551
24713
  sourceBranch: branch,
24552
24714
  sourceCommit: commit,
@@ -31090,14 +31252,14 @@ import { streamSSE as streamSSE5 } from "hono/streaming";
31090
31252
  // src/ext/context-limit.ts
31091
31253
  var CHARS_PER_TOKEN2 = 3.5;
31092
31254
  var REPLY_HEADROOM = 512;
31093
- function estimateTokens(messages) {
31255
+ function estimateTokens2(messages) {
31094
31256
  let chars = 0;
31095
31257
  for (const m of messages) chars += m.content.length + m.role.length + 4;
31096
31258
  return Math.ceil(chars / CHARS_PER_TOKEN2);
31097
31259
  }
31098
31260
  function checkContextFits(d, messages) {
31099
31261
  const limit = d.manager.status().model?.ctx ?? 0;
31100
- const estimated = estimateTokens(messages);
31262
+ const estimated = estimateTokens2(messages);
31101
31263
  if (!limit) return { fits: true, estimated, limit: 0 };
31102
31264
  return { fits: estimated + REPLY_HEADROOM <= limit, estimated, limit };
31103
31265
  }
@@ -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-DDdTmEU4.js";import{f as V}from"./chat-api-CmDwSCUy.js";import{f as X,a as Z}from"./agent-api-Bbbnu920.js";import{P as B}from"./personas-B7q-7L3z.js";import{C as ee}from"./chevron-left-CwO2nWWZ.js";import{R as te}from"./rotate-ccw-CTfSFG3x.js";import{T as se}from"./trash-2-BNNKBgll.js";import{C as ne}from"./chevron-right-Ck02yYBx.js";import"./dropdown-menu-CaMJbKX5.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-hEBbEVm0.js";import{f as V}from"./chat-api-DUobixOH.js";import{f as X,a as Z}from"./agent-api-C-qZrrYO.js";import{P as B}from"./personas-Dkq9JD72.js";import{C as ee}from"./chevron-left-CRkSrIuw.js";import{R as te}from"./rotate-ccw-DVWlxS6M.js";import{T as se}from"./trash-2-dTSieVsH.js";import{C as ne}from"./chevron-right-D6YKa1nw.js";import"./dropdown-menu-D5Vf7U3W.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};