turbollm 1.6.4 → 1.6.5
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.
- package/README.md +8 -4
- package/dist/cli.js +140 -20
- package/dist/webdist/assets/{AgentsScreen-z7V9yJTT.js → AgentsScreen-D0J8ZEtf.js} +1 -1
- package/dist/webdist/assets/ChatScreen-7bIHB35Z.js +85 -0
- package/dist/webdist/assets/{CustomizeScreen-CAQemkBC.js → CustomizeScreen-z5568762.js} +1 -1
- package/dist/webdist/assets/{DeveloperScreen-BfW2INcc.js → DeveloperScreen-C5opqGrE.js} +1 -1
- package/dist/webdist/assets/{EnginesScreen-DeVQWBY-.js → EnginesScreen-NqAMc4rS.js} +6 -6
- package/dist/webdist/assets/{ModelDetailDialog-CQxlHshX.js → ModelDetailDialog-CtjNu2Qc.js} +19 -19
- package/dist/webdist/assets/ModelDirs-C-riHLWK.js +1 -0
- package/dist/webdist/assets/{ModelsScreen-CVyA8shA.js → ModelsScreen-DnASH2bF.js} +2 -2
- package/dist/webdist/assets/{SettingsScreen-DVJQTfi8.js → SettingsScreen-v-LAgJFH.js} +1 -1
- package/dist/webdist/assets/{arc-BMLeTMlL.js → arc-OPogq9vx.js} +1 -1
- package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-CG46ZIfD.js → architectureDiagram-3BPJPVTR-DI3zlQZC.js} +1 -1
- package/dist/webdist/assets/{arrow-left-ClFIxfDf.js → arrow-left-DWtpbvUs.js} +1 -1
- package/dist/webdist/assets/{blockDiagram-GPEHLZMM-Ct5vmWMX.js → blockDiagram-GPEHLZMM-bbqbctKR.js} +1 -1
- package/dist/webdist/assets/{c4Diagram-AAUBKEIU-CAC8CrWx.js → c4Diagram-AAUBKEIU-CGj5BFj-.js} +1 -1
- package/dist/webdist/assets/channel-aq4Rip4D.js +1 -0
- package/dist/webdist/assets/check-CMGnr_Ph.js +1 -0
- package/dist/webdist/assets/chevron-right-CIKvJUaG.js +1 -0
- package/dist/webdist/assets/{chunk-2J33WTMH-Boq1pvZ0.js → chunk-2J33WTMH-CZKSYaHn.js} +1 -1
- package/dist/webdist/assets/{chunk-4BX2VUAB-BxOCNoyH.js → chunk-4BX2VUAB-D3SQCGwz.js} +1 -1
- package/dist/webdist/assets/{chunk-55IACEB6-xrXYZmka.js → chunk-55IACEB6-DDmZTA0L.js} +1 -1
- package/dist/webdist/assets/{chunk-727SXJPM-BFEC_Ow4.js → chunk-727SXJPM-DRutYNQM.js} +1 -1
- package/dist/webdist/assets/{chunk-AQP2D5EJ-DFTdPrur.js → chunk-AQP2D5EJ-B_u0ow9I.js} +1 -1
- package/dist/webdist/assets/{chunk-FMBD7UC4-D41UJ-1d.js → chunk-FMBD7UC4-Cu73-Koh.js} +1 -1
- package/dist/webdist/assets/{chunk-ND2GUHAM-BeavMz7M.js → chunk-ND2GUHAM-Z71l0U4D.js} +1 -1
- package/dist/webdist/assets/{chunk-QZHKN3VN-CUHX28cH.js → chunk-QZHKN3VN-D2PeraEx.js} +1 -1
- package/dist/webdist/assets/circle-check-DXOwCXEz.js +1 -0
- package/dist/webdist/assets/circle-x-caNFmCHm.js +1 -0
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-C5KHEB9t.js +1 -0
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-C5KHEB9t.js +1 -0
- package/dist/webdist/assets/common-CQQGhjlM.js +1 -0
- package/dist/webdist/assets/{copy-button-CTWXbfiP.js → copy-button-Dt4Afcgb.js} +1 -1
- package/dist/webdist/assets/{cose-bilkent-S5V4N54A-Bx_lty5d.js → cose-bilkent-S5V4N54A-D3rxBv0J.js} +1 -1
- package/dist/webdist/assets/{dagre-BM42HDAG-0sn0yk1w.js → dagre-BM42HDAG--G2XvGhI.js} +1 -1
- package/dist/webdist/assets/{diagram-2AECGRRQ-B7dlDz_q.js → diagram-2AECGRRQ-Te1rwFql.js} +1 -1
- package/dist/webdist/assets/{diagram-5GNKFQAL--tcSUKb3.js → diagram-5GNKFQAL-B5vALx3-.js} +1 -1
- package/dist/webdist/assets/{diagram-KO2AKTUF-DWmLxntr.js → diagram-KO2AKTUF-CBxjZi0b.js} +1 -1
- package/dist/webdist/assets/{diagram-LMA3HP47-DBOpUcpv.js → diagram-LMA3HP47-BGiirNkw.js} +1 -1
- package/dist/webdist/assets/{diagram-OG6HWLK6-CdRckAFu.js → diagram-OG6HWLK6-BVgMIJkt.js} +1 -1
- package/dist/webdist/assets/dialog-coezioYj.js +1 -0
- package/dist/webdist/assets/dropdown-menu-Ce842M-W.js +1 -0
- package/dist/webdist/assets/{erDiagram-TEJ5UH35-Bg-nfhtr.js → erDiagram-TEJ5UH35-DowgN2O9.js} +1 -1
- package/dist/webdist/assets/{flowDiagram-I6XJVG4X-CghDUz8j.js → flowDiagram-I6XJVG4X-BD0xs8q_.js} +1 -1
- package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-B76rXmmo.js → ganttDiagram-6RSMTGT7-BHHhhU_7.js} +1 -1
- package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-DYwR4vbe.js → gitGraphDiagram-PVQCEYII-Brchy1Vz.js} +1 -1
- package/dist/webdist/assets/index-CVyS_zSS.css +1 -0
- package/dist/webdist/assets/{index-DxM70nEl.js → index-oSSYX1Lk.js} +4 -4
- package/dist/webdist/assets/{infoDiagram-5YYISTIA-BvoZYlup.js → infoDiagram-5YYISTIA-D4HdfOcs.js} +1 -1
- package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-D5TRR4WP.js → ishikawaDiagram-YF4QCWOH-wmMjgjbp.js} +1 -1
- package/dist/webdist/assets/{journeyDiagram-JHISSGLW-2aK1csut.js → journeyDiagram-JHISSGLW-CaF09KKI.js} +1 -1
- package/dist/webdist/assets/{kanban-definition-UN3LZRKU-CWEMWeUg.js → kanban-definition-UN3LZRKU-BuBI9QG2.js} +1 -1
- package/dist/webdist/assets/{linear-D7FhiD9R.js → linear-DHVGKuTX.js} +1 -1
- package/dist/webdist/assets/{mermaid.core-DQIxkS1K.js → mermaid.core-CrU-xSQY.js} +4 -4
- package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-DaZEBIU3.js → mindmap-definition-RKZ34NQL-cBLMFEJ9.js} +1 -1
- package/dist/webdist/assets/pdf.worker.min-DEtVeC4l.mjs +29 -0
- package/dist/webdist/assets/pencil-BBioAa4x.js +1 -0
- package/dist/webdist/assets/{personas-DbG2zH5T.js → personas-DaUrwq7m.js} +7 -3
- package/dist/webdist/assets/{pieDiagram-4H26LBE5-C6AqueQK.js → pieDiagram-4H26LBE5-BJnfOXlL.js} +1 -1
- package/dist/webdist/assets/plus-C8MMVqVA.js +1 -0
- package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-C3ufHf4n.js → quadrantDiagram-W4KKPZXB-Cm-qlQtE.js} +1 -1
- package/dist/webdist/assets/{radix-jAkiiept.js → radix-BY-fjzwm.js} +1 -1
- package/dist/webdist/assets/refresh-cw-DTqN58WZ.js +1 -0
- package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-DKjCoTy-.js → requirementDiagram-4Y6WPE33-DHY3RUt1.js} +1 -1
- package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-D3Lhx_3A.js → sankeyDiagram-5OEKKPKP-DQkDAi3z.js} +1 -1
- package/dist/webdist/assets/save-PN9--sf1.js +1 -0
- package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-Bg7qUaiJ.js → sequenceDiagram-3UESZ5HK-OJf-ZzIm.js} +1 -1
- package/dist/webdist/assets/skeleton-Dpg8YusE.js +1 -0
- package/dist/webdist/assets/{stateDiagram-AJRCARHV-CEe0Lqmt.js → stateDiagram-AJRCARHV-QupYS2FV.js} +1 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-DPP0kwbC.js +1 -0
- package/dist/webdist/assets/{timeline-definition-PNZ67QCA-ILud0hYG.js → timeline-definition-PNZ67QCA-B7kdKKeO.js} +1 -1
- package/dist/webdist/assets/trash-2-C5mszq8p.js +1 -0
- package/dist/webdist/assets/{vennDiagram-CIIHVFJN-UmbDOfzC.js → vennDiagram-CIIHVFJN-DCckmGsZ.js} +1 -1
- package/dist/webdist/assets/{wardley-L42UT6IY-CJPgfZGE.js → wardley-L42UT6IY-C66hVI6t.js} +1 -1
- package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-COswOSfT.js → wardleyDiagram-YWT4CUSO-0WD2JYDy.js} +1 -1
- package/dist/webdist/assets/{wrench-BtDcuPD1.js → wrench-BotIMKYs.js} +1 -1
- package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-pBHtZvWZ.js → xychartDiagram-2RQKCTM6-Dc0w49oN.js} +1 -1
- package/dist/webdist/index.html +3 -3
- package/package.json +1 -1
- package/dist/webdist/assets/ChatScreen-7b_l3eo1.js +0 -29
- package/dist/webdist/assets/ModelDirs-D_Pl9O4e.js +0 -1
- package/dist/webdist/assets/channel-3lZ_p1cA.js +0 -1
- package/dist/webdist/assets/check-DQ6z92nS.js +0 -1
- package/dist/webdist/assets/chevron-right-DGMGRuqq.js +0 -1
- package/dist/webdist/assets/circle-check-B6TnDVnx.js +0 -1
- package/dist/webdist/assets/circle-x-zQ-pYHWJ.js +0 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-qxOwHzBq.js +0 -1
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-qxOwHzBq.js +0 -1
- package/dist/webdist/assets/common-stymjhMS.js +0 -1
- package/dist/webdist/assets/dialog-D-C0Tc3g.js +0 -1
- package/dist/webdist/assets/dropdown-menu-Co-guMgc.js +0 -1
- package/dist/webdist/assets/index-CT5lTdUf.css +0 -1
- package/dist/webdist/assets/pencil-zupSY7XP.js +0 -1
- package/dist/webdist/assets/plus-Ct45XaAU.js +0 -1
- package/dist/webdist/assets/refresh-cw-CUUblWFA.js +0 -1
- package/dist/webdist/assets/save-Cu00OCua.js +0 -1
- package/dist/webdist/assets/skeleton-DAp10ou3.js +0 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-BSX42oue.js +0 -1
- package/dist/webdist/assets/trash-2-Dxm0bLnm.js +0 -1
package/README.md
CHANGED
|
@@ -174,7 +174,9 @@ the detail:
|
|
|
174
174
|
carry a draft head, threads auto — safely gated to what your engine actually accepts.
|
|
175
175
|
- **Multi-GPU, per model** — split a model across cards (layer/row split + main-GPU pick on
|
|
176
176
|
llama.cpp, tensor-parallel on vLLM). Defaults are no-ops, so single-GPU rigs are untouched.
|
|
177
|
-
- **Saved per-model profiles** — tune once
|
|
177
|
+
- **Saved per-model profiles, per engine** — tune once per (model, engine) pair, so
|
|
178
|
+
switching engines (or between two installs of the same engine, e.g. a fork) never
|
|
179
|
+
overwrites another engine's tuning for the same model.
|
|
178
180
|
|
|
179
181
|
</details>
|
|
180
182
|
|
|
@@ -196,11 +198,13 @@ the detail:
|
|
|
196
198
|
Creative · Research) per conversation, no prompt-wrangling required. The **Designer** persona
|
|
197
199
|
produces polished, self-contained, previewable designs by default.
|
|
198
200
|
- **Edit, regenerate, delete, copy** any message; **persistent, searchable conversations**
|
|
199
|
-
with rename, delete, and **auto-generated titles
|
|
201
|
+
with rename, delete, and **auto-generated titles**, organized into **drag-resizable,
|
|
202
|
+
collapsible folders** you create/rename/move conversations into.
|
|
200
203
|
- **Per-chat system prompt** and **per-chat sampling** overrides — temperature, top-p/k, min-p,
|
|
201
204
|
repeat/presence/frequency penalties, and **stop strings**.
|
|
202
|
-
- **Image input** for vision models, and
|
|
203
|
-
|
|
205
|
+
- **Image input** for vision models, **PDF and code/text attachments** (real extracted text,
|
|
206
|
+
not raw bytes), and **TurboLLM Expert** — a built-in assistant that knows the app and your
|
|
207
|
+
hardware for onboarding and troubleshooting without leaving the UI.
|
|
204
208
|
- **Agentic tools** — built-in `web_search` (Tavily), `fetch_url`, and sandboxed `run_code`, plus
|
|
205
209
|
an **MCP marketplace** in Customize: one-click connect for hosted MCPs (GitHub, Linear, Stripe,
|
|
206
210
|
Atlassian, Neon, Supabase, Cloudflare, Zapier, Apify, Mixpanel) and open-source local MCPs
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import { randomUUID } from "crypto";
|
|
|
12
12
|
import { cpSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "fs";
|
|
13
13
|
import { homedir } from "os";
|
|
14
14
|
import { dirname, join } from "path";
|
|
15
|
-
var SCHEMA_VERSION =
|
|
15
|
+
var SCHEMA_VERSION = 3;
|
|
16
16
|
var ValueError = class extends Error {
|
|
17
17
|
constructor(field, msg2) {
|
|
18
18
|
super(`${field}: ${msg2}`);
|
|
@@ -176,7 +176,7 @@ function migrate(raw, _from) {
|
|
|
176
176
|
}
|
|
177
177
|
cfg2.version = SCHEMA_VERSION;
|
|
178
178
|
for (const [k, v] of Object.entries(raw)) {
|
|
179
|
-
if (
|
|
179
|
+
if (k !== "engine" && k !== "host" && k !== "port" && k !== "version") {
|
|
180
180
|
;
|
|
181
181
|
cfg2[k] = v;
|
|
182
182
|
}
|
|
@@ -217,6 +217,11 @@ function normalize(c) {
|
|
|
217
217
|
c.modelDirs ??= [];
|
|
218
218
|
c.primaryModelDir ??= "";
|
|
219
219
|
c.modelProfiles ??= {};
|
|
220
|
+
for (const [modelKey, val] of Object.entries(c.modelProfiles)) {
|
|
221
|
+
if (val && typeof val === "object" && typeof val.ctx === "number") {
|
|
222
|
+
c.modelProfiles[modelKey] = { "*": { profile: val, updatedAt: (/* @__PURE__ */ new Date()).toISOString() } };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
220
225
|
c.benchResults ??= {};
|
|
221
226
|
c.autoLoadOnStart ??= false;
|
|
222
227
|
c.featuredOverrideUrl ??= "";
|
|
@@ -299,6 +304,27 @@ function isAbsolutePath(p) {
|
|
|
299
304
|
function findEngine(engines, id) {
|
|
300
305
|
return engines.find((e) => e.id === id);
|
|
301
306
|
}
|
|
307
|
+
function getModelProfile(cfg2, modelKey, engineId) {
|
|
308
|
+
const byEngine = cfg2.modelProfiles[modelKey];
|
|
309
|
+
if (!byEngine) return void 0;
|
|
310
|
+
const exact = byEngine[engineId];
|
|
311
|
+
if (exact) return exact.profile;
|
|
312
|
+
let mostRecent;
|
|
313
|
+
for (const entry of Object.values(byEngine)) {
|
|
314
|
+
if (!mostRecent || entry.updatedAt >= mostRecent.updatedAt) mostRecent = entry;
|
|
315
|
+
}
|
|
316
|
+
return mostRecent?.profile;
|
|
317
|
+
}
|
|
318
|
+
function setModelProfile(cfg2, modelKey, engineId, profile) {
|
|
319
|
+
;
|
|
320
|
+
(cfg2.modelProfiles[modelKey] ??= {})[engineId] = { profile, updatedAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
321
|
+
}
|
|
322
|
+
function deleteModelProfile(cfg2, modelKey, engineId) {
|
|
323
|
+
const byEngine = cfg2.modelProfiles[modelKey];
|
|
324
|
+
if (!byEngine) return;
|
|
325
|
+
delete byEngine[engineId];
|
|
326
|
+
if (Object.keys(byEngine).length === 0) delete cfg2.modelProfiles[modelKey];
|
|
327
|
+
}
|
|
302
328
|
function clampMaxTokens(requested, limit) {
|
|
303
329
|
if (!Number.isFinite(limit) || limit <= 0) return requested ?? void 0;
|
|
304
330
|
if (requested == null || !Number.isFinite(requested) || requested <= 0) return limit;
|
|
@@ -3843,16 +3869,24 @@ function profileToArgs(p, m, caps, cores = 0) {
|
|
|
3843
3869
|
if (p.chatTemplateFile && has("--chat-template-file")) a.push("--chat-template-file", p.chatTemplateFile);
|
|
3844
3870
|
const specType = has("--spec-type");
|
|
3845
3871
|
const specAccepts = (v) => caps.flags.length === 0 || caps.flags.includes(`spec-type:${v}`);
|
|
3872
|
+
let specActive = false;
|
|
3846
3873
|
if (p.speculative === "mtp" && p.mtpHeadPath && has("--mtp-head")) {
|
|
3847
3874
|
if (specType) a.push("--spec-type", "mtp");
|
|
3848
3875
|
a.push("--mtp-head", p.mtpHeadPath);
|
|
3876
|
+
specActive = true;
|
|
3849
3877
|
} else if (p.speculative === "nextn" && specType && has("--model-draft")) {
|
|
3850
3878
|
const nextnVal = ["nextn", "draft-mtp"].find((v) => specAccepts(v));
|
|
3851
|
-
if (nextnVal)
|
|
3879
|
+
if (nextnVal) {
|
|
3880
|
+
a.push("--spec-type", nextnVal, "--model-draft", m.path);
|
|
3881
|
+
specActive = true;
|
|
3882
|
+
}
|
|
3852
3883
|
} else if (p.speculative === "draft" && p.draftModelPath && has("--model-draft")) {
|
|
3853
3884
|
if (specType) a.push("--spec-type", "draft");
|
|
3854
|
-
a.push("--model-draft", p.draftModelPath
|
|
3885
|
+
a.push("--model-draft", p.draftModelPath);
|
|
3886
|
+
specActive = true;
|
|
3855
3887
|
}
|
|
3888
|
+
if (specActive && has("--draft-max")) a.push("--draft-max", String(p.draftMax ?? 16));
|
|
3889
|
+
if (specActive && has("--draft-min")) a.push("--draft-min", String(p.draftMin ?? 1));
|
|
3856
3890
|
if (p.sampling.temp !== 0.8 && has("--temp")) a.push("--temp", String(p.sampling.temp));
|
|
3857
3891
|
if (p.sampling.topP !== 0.95 && has("--top-p")) a.push("--top-p", String(p.sampling.topP));
|
|
3858
3892
|
if (p.sampling.topK !== 40 && has("--top-k")) a.push("--top-k", String(p.sampling.topK));
|
|
@@ -3899,7 +3933,10 @@ function safeJson(s) {
|
|
|
3899
3933
|
}
|
|
3900
3934
|
}
|
|
3901
3935
|
function rowToConv(r) {
|
|
3902
|
-
return { id: r.id, title: r.title, systemPrompt: r.system_prompt, modelKey: r.model_key, sampling: safeJson(r.sampling), expertMode: r.expert_mode === 1, toolPolicy: r.tool_policy ?? void 0, kind: r.kind === "agent" ? "agent" : "chat", createdAt: r.created_at, updatedAt: r.updated_at };
|
|
3936
|
+
return { id: r.id, title: r.title, systemPrompt: r.system_prompt, modelKey: r.model_key, sampling: safeJson(r.sampling), expertMode: r.expert_mode === 1, toolPolicy: r.tool_policy ?? void 0, kind: r.kind === "agent" ? "agent" : "chat", folderId: r.folder_id ?? null, createdAt: r.created_at, updatedAt: r.updated_at };
|
|
3937
|
+
}
|
|
3938
|
+
function rowToFolder(r) {
|
|
3939
|
+
return { id: r.id, name: r.name, sortOrder: r.sort_order, createdAt: r.created_at, updatedAt: r.updated_at };
|
|
3903
3940
|
}
|
|
3904
3941
|
function rowToAgentRun(r) {
|
|
3905
3942
|
return {
|
|
@@ -4008,6 +4045,18 @@ var ConversationStore = class {
|
|
|
4008
4045
|
PRAGMA user_version = 9;
|
|
4009
4046
|
`);
|
|
4010
4047
|
}
|
|
4048
|
+
if (v < 10) {
|
|
4049
|
+
this.db.exec(`
|
|
4050
|
+
CREATE TABLE IF NOT EXISTS folders (
|
|
4051
|
+
id TEXT PRIMARY KEY, name TEXT NOT NULL,
|
|
4052
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
4053
|
+
created_at TEXT NOT NULL, updated_at TEXT NOT NULL
|
|
4054
|
+
);
|
|
4055
|
+
ALTER TABLE conversations ADD COLUMN folder_id TEXT;
|
|
4056
|
+
CREATE INDEX IF NOT EXISTS idx_conversations_folder ON conversations(folder_id);
|
|
4057
|
+
PRAGMA user_version = 10;
|
|
4058
|
+
`);
|
|
4059
|
+
}
|
|
4011
4060
|
}
|
|
4012
4061
|
listConversations(q, kind = "all") {
|
|
4013
4062
|
if (q) {
|
|
@@ -4029,7 +4078,7 @@ var ConversationStore = class {
|
|
|
4029
4078
|
createConversation(partial) {
|
|
4030
4079
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4031
4080
|
const id = randomUUID3();
|
|
4032
|
-
this.db.prepare(`INSERT INTO conversations (id,title,system_prompt,model_key,sampling,expert_mode,tool_policy,kind,created_at,updated_at) VALUES ($id,$title,$sp,$mk,$samp,$expert,$tp,$kind,$now,$now)`).run({ $id: id, $title: partial?.title ?? "New chat", $sp: partial?.systemPrompt ?? "", $mk: partial?.modelKey ?? "", $samp: JSON.stringify(partial?.sampling ?? {}), $expert: partial?.expertMode ? 1 : 0, $tp: partial?.toolPolicy ?? null, $kind: partial?.kind ?? "chat", $now: now });
|
|
4081
|
+
this.db.prepare(`INSERT INTO conversations (id,title,system_prompt,model_key,sampling,expert_mode,tool_policy,kind,folder_id,created_at,updated_at) VALUES ($id,$title,$sp,$mk,$samp,$expert,$tp,$kind,$fid,$now,$now)`).run({ $id: id, $title: partial?.title ?? "New chat", $sp: partial?.systemPrompt ?? "", $mk: partial?.modelKey ?? "", $samp: JSON.stringify(partial?.sampling ?? {}), $expert: partial?.expertMode ? 1 : 0, $tp: partial?.toolPolicy ?? null, $kind: partial?.kind ?? "chat", $fid: partial?.folderId ?? null, $now: now });
|
|
4033
4082
|
return this.getConversation(id);
|
|
4034
4083
|
}
|
|
4035
4084
|
getConversation(id, withMessages = false) {
|
|
@@ -4140,6 +4189,41 @@ var ConversationStore = class {
|
|
|
4140
4189
|
const row = this.db.prepare(`SELECT * FROM messages WHERE conv_id = $id ORDER BY seq DESC LIMIT 1`).get({ $id: convId });
|
|
4141
4190
|
return row ? rowToMsg(row) : null;
|
|
4142
4191
|
}
|
|
4192
|
+
// ── Folder methods (v10 migration) ────────────────────────────────────────
|
|
4193
|
+
/** All folders in insertion order (sort_order asc, then created_at). */
|
|
4194
|
+
listFolders() {
|
|
4195
|
+
return this.db.prepare(`SELECT * FROM folders ORDER BY sort_order ASC, created_at ASC`).all().map(rowToFolder);
|
|
4196
|
+
}
|
|
4197
|
+
getFolder(id) {
|
|
4198
|
+
const row = this.db.prepare(`SELECT * FROM folders WHERE id = $id`).get({ $id: id });
|
|
4199
|
+
return row ? rowToFolder(row) : null;
|
|
4200
|
+
}
|
|
4201
|
+
createFolder(name) {
|
|
4202
|
+
const id = randomUUID3();
|
|
4203
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4204
|
+
this.db.prepare(`INSERT INTO folders (id,name,sort_order,created_at,updated_at) VALUES ($id,$name,0,$now,$now)`).run({ $id: id, $name: name, $now: now });
|
|
4205
|
+
return this.getFolder(id);
|
|
4206
|
+
}
|
|
4207
|
+
renameFolder(id, name) {
|
|
4208
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4209
|
+
return this.db.prepare(`UPDATE folders SET name = $name, updated_at = $now WHERE id = $id`).run({ $id: id, $name: name, $now: now }).changes > 0;
|
|
4210
|
+
}
|
|
4211
|
+
/** Delete a folder. Member conversations are UNASSIGNED (folder_id → NULL), never
|
|
4212
|
+
* cascade-deleted. Returns false if the folder did not exist. */
|
|
4213
|
+
deleteFolder(id) {
|
|
4214
|
+
this.db.prepare(`UPDATE conversations SET folder_id = NULL WHERE folder_id = $id`).run({ $id: id });
|
|
4215
|
+
return this.db.prepare(`DELETE FROM folders WHERE id = $id`).run({ $id: id }).changes > 0;
|
|
4216
|
+
}
|
|
4217
|
+
/** Move a conversation into a folder (or out of any folder when folderId is null).
|
|
4218
|
+
* When folderId is non-null the folder must exist. Returns:
|
|
4219
|
+
* - { ok: true } on success,
|
|
4220
|
+
* - { ok: false, reason } when the conversation or the target folder is missing. */
|
|
4221
|
+
moveConversationToFolder(convId, folderId) {
|
|
4222
|
+
if (folderId !== null && !this.getFolder(folderId)) return { ok: false, reason: "folder_not_found" };
|
|
4223
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4224
|
+
const changed = this.db.prepare(`UPDATE conversations SET folder_id = $fid, updated_at = $now WHERE id = $id`).run({ $id: convId, $fid: folderId, $now: now }).changes > 0;
|
|
4225
|
+
return changed ? { ok: true } : { ok: false, reason: "conversation_not_found" };
|
|
4226
|
+
}
|
|
4143
4227
|
// ── Agent run methods (v8 migration) ──────────────────────────────────────
|
|
4144
4228
|
createAgentRun(params) {
|
|
4145
4229
|
const id = randomUUID3();
|
|
@@ -5188,7 +5272,7 @@ var BenchRunner = class {
|
|
|
5188
5272
|
saveResult() {
|
|
5189
5273
|
const w = this.winning;
|
|
5190
5274
|
if (!w) return false;
|
|
5191
|
-
const record = this.persistBest(w.modelKey, w.profile, w.cand);
|
|
5275
|
+
const record = this.persistBest(w.modelKey, w.profile, w.cand, w.engineId);
|
|
5192
5276
|
this.queueTelemetry(record, w.entry, w.sys, this.version, w.engineVersion);
|
|
5193
5277
|
this.winning = null;
|
|
5194
5278
|
this.state = { ...this.state, result: void 0 };
|
|
@@ -5257,7 +5341,7 @@ var BenchRunner = class {
|
|
|
5257
5341
|
this.runLogMeta = { modelKey, startedAt: (/* @__PURE__ */ new Date()).toISOString(), sys };
|
|
5258
5342
|
const active = this.registry.active();
|
|
5259
5343
|
const caps = active?.capabilities ?? { flags: [], kvTypes: [] };
|
|
5260
|
-
const saved = this.store.snapshot()
|
|
5344
|
+
const saved = getModelProfile(this.store.snapshot(), modelKey, active?.id ?? "*");
|
|
5261
5345
|
const defaults = this.store.snapshot().modelDefaults;
|
|
5262
5346
|
const resolved = resolveProfile(entry, sys, saved, base2, defaults);
|
|
5263
5347
|
const baseProfile = { ...resolved, speculative: "off", mtpHeadPath: "", draftModelPath: "" };
|
|
@@ -5293,7 +5377,7 @@ var BenchRunner = class {
|
|
|
5293
5377
|
return;
|
|
5294
5378
|
}
|
|
5295
5379
|
const profile = recommended && hasAnySampling(recommended) ? { ...best.profile, sampling: { ...best.profile.sampling, ...recommended } } : best.profile;
|
|
5296
|
-
this.winning = { modelKey, profile, cand: best.cand, entry, sys, engineVersion: active?.version ?? "" };
|
|
5380
|
+
this.winning = { modelKey, profile, cand: best.cand, entry, sys, engineVersion: active?.version ?? "", engineId: active?.id ?? "" };
|
|
5297
5381
|
this.logWinner = { params: best.cand.params, tps: best.cand.tps ?? 0, prefillTps: best.cand.prefillTps, ttftMs: best.cand.ttftMs ?? 0, vramMb: best.cand.vramMb };
|
|
5298
5382
|
this.state = {
|
|
5299
5383
|
running: false,
|
|
@@ -5843,7 +5927,7 @@ var BenchRunner = class {
|
|
|
5843
5927
|
}
|
|
5844
5928
|
/** Save the winning profile as the model's saved profile (tunedBy:'bench') and
|
|
5845
5929
|
* persist a benchResults row. Both via the same ConfigStore the route uses. */
|
|
5846
|
-
persistBest(modelKey, profile, cand) {
|
|
5930
|
+
persistBest(modelKey, profile, cand, engineId) {
|
|
5847
5931
|
const record = {
|
|
5848
5932
|
modelKey,
|
|
5849
5933
|
tps: cand.tps ?? 0,
|
|
@@ -5854,7 +5938,7 @@ var BenchRunner = class {
|
|
|
5854
5938
|
};
|
|
5855
5939
|
const tuned = { ...profile, tunedBy: "bench" };
|
|
5856
5940
|
this.store.update((cfg2) => {
|
|
5857
|
-
cfg2
|
|
5941
|
+
setModelProfile(cfg2, modelKey, engineId || "*", tuned);
|
|
5858
5942
|
cfg2.benchResults[modelKey] = record;
|
|
5859
5943
|
});
|
|
5860
5944
|
return record;
|
|
@@ -6174,7 +6258,7 @@ var ModelRouter = class {
|
|
|
6174
6258
|
const cfg2 = this.store.snapshot();
|
|
6175
6259
|
const sys = getSysInfo();
|
|
6176
6260
|
if (entry.format !== "gguf") {
|
|
6177
|
-
const savedProfile = cfg2
|
|
6261
|
+
const savedProfile = getModelProfile(cfg2, entry.key, engine.id);
|
|
6178
6262
|
return {
|
|
6179
6263
|
engine,
|
|
6180
6264
|
model: { key: entry.key, name: entry.name, quant: entry.quant, ctx: entry.nativeCtx, vision: false },
|
|
@@ -6184,7 +6268,7 @@ var ModelRouter = class {
|
|
|
6184
6268
|
tensorParallelSize: savedProfile?.gpu?.tensorParallelSize
|
|
6185
6269
|
};
|
|
6186
6270
|
}
|
|
6187
|
-
const saved = cfg2
|
|
6271
|
+
const saved = getModelProfile(cfg2, entry.key, engine.id);
|
|
6188
6272
|
const profile = resolveProfile(entry, sys, saved, void 0, cfg2.modelDefaults);
|
|
6189
6273
|
const extraArgs = engine.kind === "koboldcpp" ? koboldcppProfileToArgs(profile, sys.gpus[0]?.vendor ?? "unknown", sys.gpus.length > 0) : profileToArgs(profile, entry, engine.capabilities, sys.cores);
|
|
6190
6274
|
return {
|
|
@@ -8482,6 +8566,28 @@ async function body(c) {
|
|
|
8482
8566
|
}
|
|
8483
8567
|
function registerChatRoutes(app2, d) {
|
|
8484
8568
|
const { db: db2 } = d;
|
|
8569
|
+
app2.get("/api/v1/folders", (c) => {
|
|
8570
|
+
return c.json({ folders: db2.listFolders() });
|
|
8571
|
+
});
|
|
8572
|
+
app2.post("/api/v1/folders", async (c) => {
|
|
8573
|
+
const b = await body(c);
|
|
8574
|
+
const name = (b.name ?? "").trim();
|
|
8575
|
+
if (!name) return err(c, 400, "invalid_input", "Folder name required.");
|
|
8576
|
+
return c.json(db2.createFolder(name), 201);
|
|
8577
|
+
});
|
|
8578
|
+
app2.patch("/api/v1/folders/:id", async (c) => {
|
|
8579
|
+
const b = await body(c);
|
|
8580
|
+
const name = (b.name ?? "").trim();
|
|
8581
|
+
if (!name) return err(c, 400, "invalid_input", "Folder name required.");
|
|
8582
|
+
const ok = db2.renameFolder(c.req.param("id"), name);
|
|
8583
|
+
if (!ok) return err(c, 404, "not_found", "Folder not found.");
|
|
8584
|
+
return c.json(db2.getFolder(c.req.param("id")));
|
|
8585
|
+
});
|
|
8586
|
+
app2.delete("/api/v1/folders/:id", (c) => {
|
|
8587
|
+
const ok = db2.deleteFolder(c.req.param("id"));
|
|
8588
|
+
if (!ok) return err(c, 404, "not_found", "Folder not found.");
|
|
8589
|
+
return c.json({ ok: true });
|
|
8590
|
+
});
|
|
8485
8591
|
app2.get("/api/v1/conversations", (c) => {
|
|
8486
8592
|
const q = c.req.query("q");
|
|
8487
8593
|
return c.json({ conversations: db2.listConversations(q, "chat") });
|
|
@@ -8507,6 +8613,16 @@ function registerChatRoutes(app2, d) {
|
|
|
8507
8613
|
if (!ok) return err(c, 404, "not_found", "Conversation not found.");
|
|
8508
8614
|
return c.json({ ok: true });
|
|
8509
8615
|
});
|
|
8616
|
+
app2.patch("/api/v1/conversations/:id/folder", async (c) => {
|
|
8617
|
+
const b = await body(c);
|
|
8618
|
+
const folderId = b.folderId ?? null;
|
|
8619
|
+
const res = db2.moveConversationToFolder(c.req.param("id"), folderId);
|
|
8620
|
+
if (!res.ok) {
|
|
8621
|
+
if (res.reason === "folder_not_found") return err(c, 404, "not_found", "Folder not found.");
|
|
8622
|
+
return err(c, 404, "not_found", "Conversation not found.");
|
|
8623
|
+
}
|
|
8624
|
+
return c.json(db2.getConversation(c.req.param("id")));
|
|
8625
|
+
});
|
|
8510
8626
|
app2.post("/api/v1/conversations/:id/messages", async (c) => {
|
|
8511
8627
|
const convId = c.req.param("id");
|
|
8512
8628
|
const b = await body(c);
|
|
@@ -11181,7 +11297,7 @@ function registerApi(app2, d) {
|
|
|
11181
11297
|
}
|
|
11182
11298
|
let opts2;
|
|
11183
11299
|
if (entry.format !== "gguf") {
|
|
11184
|
-
const savedProfile = cfg2
|
|
11300
|
+
const savedProfile = getModelProfile(cfg2, entry.key, active.id);
|
|
11185
11301
|
const extraArgs = active.kind === "mlx" ? mlxSamplingArgs(savedProfile?.sampling) : active.kind === "vllm" ? vllmProfileToArgs(resolveProfile(entry, sys, savedProfile, b.profileOverrides, cfg2.modelDefaults)) : [];
|
|
11186
11302
|
opts2 = {
|
|
11187
11303
|
engine: active,
|
|
@@ -11191,7 +11307,7 @@ function registerApi(app2, d) {
|
|
|
11191
11307
|
tensorParallelSize: savedProfile?.gpu?.tensorParallelSize
|
|
11192
11308
|
};
|
|
11193
11309
|
} else {
|
|
11194
|
-
const saved = cfg2
|
|
11310
|
+
const saved = getModelProfile(cfg2, entry.key, active.id);
|
|
11195
11311
|
const profile = resolveProfile(entry, sys, saved, b.profileOverrides, cfg2.modelDefaults);
|
|
11196
11312
|
const extraArgs = active.kind === "koboldcpp" ? koboldcppProfileToArgs(profile, primaryVendor(sys), sys.gpus.length > 0) : profileToArgs(profile, entry, active.capabilities, sys.cores);
|
|
11197
11313
|
opts2 = {
|
|
@@ -11382,7 +11498,8 @@ function registerApi(app2, d) {
|
|
|
11382
11498
|
if (!e) return err2(c, 404, "no_such_model", "No model with that key.");
|
|
11383
11499
|
const sys = getSysInfo();
|
|
11384
11500
|
const snap = d.store.snapshot();
|
|
11385
|
-
const
|
|
11501
|
+
const engineId = c.req.query("engine") || d.registry.active()?.id || "*";
|
|
11502
|
+
const saved = getModelProfile(snap, e.key, engineId);
|
|
11386
11503
|
const profile = resolveProfile(e, sys, saved, void 0, snap.modelDefaults);
|
|
11387
11504
|
return c.json({ ...overlayModel(e, d), profile, vramFit: estimateVram(profile, e, sys), gpu: sys.gpus[0] ?? null, gpus: sys.gpus, cores: sys.cores });
|
|
11388
11505
|
});
|
|
@@ -11409,15 +11526,17 @@ function registerApi(app2, d) {
|
|
|
11409
11526
|
return err2(c, 400, "invalid_profile_value", "gpu.tensorParallelSize must be an integer \u2265 1.");
|
|
11410
11527
|
}
|
|
11411
11528
|
}
|
|
11529
|
+
const engineId = c.req.query("engine") || d.registry.active()?.id || "*";
|
|
11412
11530
|
d.store.update((cfg2) => {
|
|
11413
|
-
cfg2
|
|
11531
|
+
setModelProfile(cfg2, key, engineId, p);
|
|
11414
11532
|
});
|
|
11415
11533
|
return c.json(p);
|
|
11416
11534
|
});
|
|
11417
11535
|
app2.post("/api/v1/models/:key/profile/reset", (c) => {
|
|
11418
11536
|
const key = decodeURIComponent(c.req.param("key"));
|
|
11537
|
+
const engineId = c.req.query("engine") || d.registry.active()?.id || "*";
|
|
11419
11538
|
d.store.update((cfg2) => {
|
|
11420
|
-
|
|
11539
|
+
deleteModelProfile(cfg2, key, engineId);
|
|
11421
11540
|
});
|
|
11422
11541
|
return c.json({ ok: true });
|
|
11423
11542
|
});
|
|
@@ -11819,7 +11938,8 @@ function overlayModel(e, d, lastTpsMap) {
|
|
|
11819
11938
|
const compatibleWithActiveEngine = active ? engineAcceptsFormat(active.kind, e.format) : true;
|
|
11820
11939
|
const provRepo = d.downloads.provenance().find((p) => p.dest === e.path && p.repo)?.repo;
|
|
11821
11940
|
const sourceRepo = provRepo ?? inferRepoFromPath(e.path, snap.modelDirs);
|
|
11822
|
-
|
|
11941
|
+
const hasProfile = !!profiles[e.key] && Object.keys(profiles[e.key]).length > 0;
|
|
11942
|
+
return { ...e, loaded, hasProfile, lastTps, liveTps, benchTps, compatibleWithActiveEngine, sourceRepo };
|
|
11823
11943
|
}
|
|
11824
11944
|
function formatMismatchMessage(engineKind, format) {
|
|
11825
11945
|
if (engineKind === "mlx")
|
|
@@ -13156,7 +13276,7 @@ void (async () => {
|
|
|
13156
13276
|
extraArgs: []
|
|
13157
13277
|
};
|
|
13158
13278
|
} else {
|
|
13159
|
-
const saved = cfg
|
|
13279
|
+
const saved = getModelProfile(cfg, entry.key, active.id);
|
|
13160
13280
|
const profile = resolveProfile(entry, sys, saved, void 0, cfg.modelDefaults);
|
|
13161
13281
|
opts = {
|
|
13162
13282
|
engine: active,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{j as e}from"./radix-
|
|
1
|
+
import{j as e}from"./radix-BY-fjzwm.js";import{r as u}from"./vendor-W5GIuFcv.js";import{c as w,d as k,A as T,u as v,L as h,e as S,a as b,f as C,g as q,X as A}from"./index-oSSYX1Lk.js";import{P as N}from"./plus-C8MMVqVA.js";import{C as y}from"./circle-x-caNFmCHm.js";import{C as O}from"./circle-check-DXOwCXEz.js";import{W as E}from"./wrench-BotIMKYs.js";const _=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["circle",{cx:"12",cy:"12",r:"1",key:"41hilf"}]],D=w("circle-dot",_);const L=[["circle",{cx:"12",cy:"12",r:"10",key:"1mglay"}],["path",{d:"M12 6v6l4 2",key:"mmk7yg"}]],P=w("clock",L),p={all:["agent-runs"],list:()=>[...p.all,"list"],detail:t=>[...p.all,"detail",t]};async function g(t,a){const l={Accept:"application/json",...k(),...a?.headers??{}},i=await fetch(t,{...a,headers:l});if(i.status===204)return;const r=await i.text(),o=r?JSON.parse(r):void 0;if(!i.ok){const d=o;throw new T(d?.error?.code??"http_error",d?.error?.message??`Request failed with status ${i.status}.`,i.status)}return o}async function $(){return g("/api/v1/agents/runs")}async function I(t){return g(`/api/v1/agents/runs/${t}`)}async function Q(t){return g("/api/v1/agents/runs",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)})}async function K(t){await g(`/api/v1/agents/runs/${t}`,{method:"DELETE"})}async function*F(t,a=0,l){const i=`/api/v1/agents/runs/${t}/stream?fromSeq=${a}`,r=await fetch(i,{headers:{Accept:"text/event-stream",...k()},signal:l});if(!r.ok||!r.body)throw new Error(`Stream error: ${r.status}`);const o=r.body.getReader(),d=new TextDecoder;let m="";try{for(;;){const{done:s,value:x}=await o.read();if(s)break;m+=d.decode(x,{stream:!0});const c=m.split(`
|
|
2
2
|
`);m=c.pop()??"";let n="message";for(const f of c){if(f.startsWith("event: ")){n=f.slice(7).trim();continue}if(f.startsWith("data: ")){const j=f.slice(6).trim();if(j==="[DONE]")return;try{const R=JSON.parse(j);if(yield{event:n,data:R},n==="done"||n==="error")return}catch{}n="message"}}}}finally{o.cancel()}}const J={queued:e.jsx(P,{size:12}),running:e.jsx(h,{size:12,className:"animate-spin"}),done:e.jsx(O,{size:12}),failed:e.jsx(y,{size:12}),cancelled:e.jsx(y,{size:12}),interrupted:e.jsx(y,{size:12})},M={queued:"text-muted",running:"text-accent",done:"text-green-500",failed:"text-red-500",cancelled:"text-muted",interrupted:"text-orange-500"};function z({status:t}){return e.jsxs("span",{className:b("flex items-center gap-1 text-[11px] capitalize",M[t]),children:[J[t],t]})}function W(t){const a=Date.now()-new Date(t).getTime();return a<6e4?"just now":a<36e5?`${Math.floor(a/6e4)}m ago`:a<864e5?`${Math.floor(a/36e5)}h ago`:new Date(t).toLocaleDateString()}const U=[{id:"web_search",label:"Web search",hint:"Search the web (needs a provider configured in Settings)"},{id:"fetch_url",label:"Fetch URL",hint:"Read the contents of a web page"},{id:"run_code",label:"Run code",hint:"Execute JS in a sandbox — no mid-run confirmation, opt in only if you trust the task"}];function B({onClose:t,onCreated:a}){const[l,i]=u.useState(""),[r,o]=u.useState(""),[d,m]=u.useState({web_search:!0,fetch_url:!0,run_code:!1}),s=C(),x=n=>m(f=>({...f,[n]:!f[n]})),c=q({mutationFn:()=>Q({title:l.slice(0,60)||"Agent run",systemPrompt:r||void 0,userMessage:l,allowedTools:Object.entries(d).filter(([,n])=>n).map(([n])=>n)}),onSuccess:n=>{s.invalidateQueries({queryKey:p.list()}),a(n.id),t()}});return e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/40",onClick:t,children:e.jsxs("div",{className:"w-full max-w-lg rounded-xl border border-border bg-panel p-5 shadow-xl",onClick:n=>n.stopPropagation(),children:[e.jsxs("div",{className:"mb-4 flex items-center justify-between",children:[e.jsx("h2",{className:"text-[15px] font-semibold",children:"New agent run"}),e.jsx("button",{type:"button",onClick:t,className:"text-faint hover:text-ink",children:e.jsx(A,{size:16})})]}),e.jsx("label",{className:"mb-1 block text-[12px] text-muted",children:"Task"}),e.jsx("textarea",{autoFocus:!0,value:l,onChange:n=>i(n.target.value),placeholder:"Describe what the agent should do…",rows:4,className:"mb-3 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsxs("label",{className:"mb-1 block text-[12px] text-muted",children:["System prompt ",e.jsx("span",{className:"text-faint",children:"(optional)"})]}),e.jsx("textarea",{value:r,onChange:n=>o(n.target.value),placeholder:"You are a helpful assistant…",rows:2,className:"mb-4 w-full resize-none rounded-lg border border-border bg-input px-3 py-2 text-[13px] text-ink placeholder:text-faint focus:border-accent focus:outline-none"}),e.jsx("label",{className:"mb-1.5 block text-[12px] text-muted",children:"Tools"}),e.jsx("div",{className:"mb-4 space-y-1.5",children:U.map(n=>e.jsxs("label",{className:"flex cursor-pointer items-start gap-2 rounded-lg border border-border px-2.5 py-2 hover:bg-panel-2",children:[e.jsx("input",{type:"checkbox",checked:d[n.id],onChange:()=>x(n.id),className:"mt-0.5 accent-[var(--accent)]"}),e.jsxs("span",{className:"min-w-0",children:[e.jsx("span",{className:"block text-[13px] font-medium text-ink",children:n.label}),e.jsx("span",{className:"block text-[11px] leading-snug text-faint",children:n.hint})]})]},n.id))}),e.jsxs("div",{className:"flex items-center justify-end gap-2",children:[e.jsx("button",{type:"button",onClick:t,className:"rounded-lg px-3 py-1.5 text-[13px] text-muted hover:text-ink",children:"Cancel"}),e.jsxs("button",{type:"button",disabled:!l.trim()||c.isPending,onClick:()=>c.mutate(),className:"flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white disabled:opacity-50",children:[c.isPending?e.jsx(h,{size:13,className:"animate-spin"}):e.jsx(S,{size:13}),"Run agent"]})]}),c.isError&&e.jsx("p",{className:"mt-2 text-[12px]",style:{color:"var(--err)"},children:c.error.message})]})})}function X({call:t}){const[a,l]=u.useState(!1),i=typeof t.args?.query=="string"?t.args.query:typeof t.args?.url=="string"?t.args.url:JSON.stringify(t.args??{});return e.jsxs("div",{className:"w-full max-w-[85%] rounded-lg border border-border bg-panel px-2.5 py-1.5 text-[12px]",children:[e.jsxs("button",{type:"button",onClick:()=>l(r=>!r),className:"flex w-full items-center gap-1.5 text-left text-muted hover:text-ink",children:[e.jsx(E,{size:12,className:"shrink-0 text-accent"}),e.jsx("span",{className:"font-medium text-ink",children:t.name}),e.jsx("span",{className:"truncate text-faint",children:i})]}),a&&e.jsx("pre",{className:"mt-1.5 max-h-48 overflow-auto whitespace-pre-wrap break-words rounded bg-panel-2 p-2 text-[11px] text-muted",children:t.result})]})}function H({run:t}){const a=C(),l=u.useRef(null),[i,r]=u.useState(""),o=u.useRef(null),d=q({mutationFn:()=>K(t.id),onSuccess:()=>{a.invalidateQueries({queryKey:p.list()})}});u.useEffect(()=>{if(t.status!=="running"&&t.status!=="queued")return;const s=new AbortController;l.current=s;let x="";return(async()=>{try{for await(const c of F(t.id,0,s.signal))if(c.event==="delta")x+=c.data.delta,r(x);else if(c.event==="done"||c.event==="error"){r(""),a.invalidateQueries({queryKey:p.detail(t.id)}),a.invalidateQueries({queryKey:p.list()});break}}catch{}})(),()=>{s.abort(),l.current=null}},[t.id,t.status,a]),u.useEffect(()=>{o.current?.scrollIntoView({behavior:"smooth"})},[i,t.messages?.length]);const m=t.messages??[];return e.jsxs("div",{className:"flex h-full flex-col",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-4 py-2.5",children:[e.jsxs("div",{children:[e.jsx("p",{className:"text-[14px] font-medium text-ink",children:t.title}),e.jsx(z,{status:t.status})]}),(t.status==="running"||t.status==="queued")&&e.jsx("button",{type:"button",onClick:()=>d.mutate(),className:"rounded-lg border border-border px-3 py-1 text-[12px] text-muted transition-colors hover:border-red-400 hover:text-red-500",children:d.isPending?e.jsx(h,{size:12,className:"inline animate-spin"}):"Cancel"})]}),e.jsxs("div",{className:"flex-1 space-y-3 overflow-y-auto p-4",children:[m.map(s=>e.jsxs("div",{className:b("flex flex-col gap-1.5",s.role==="user"?"items-end":"items-start"),children:[s.toolCalls?.map(x=>e.jsx(X,{call:x},x.id)),(s.content||s.role==="user"||t.status==="running")&&e.jsx("div",{className:b("max-w-[85%] rounded-xl px-3 py-2 text-[13px] leading-relaxed",s.role==="user"?"bg-accent/15 text-ink":"bg-panel-2 text-ink"),children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:s.content||(s.role==="assistant"&&t.status==="running"?"…":"")})})]},s.id)),i&&e.jsx("div",{className:"flex gap-2",children:e.jsx("div",{className:"max-w-[85%] rounded-xl bg-panel-2 px-3 py-2 text-[13px] leading-relaxed text-ink",children:e.jsx("p",{className:"whitespace-pre-wrap break-words",children:i})})}),t.error&&e.jsxs("div",{className:"rounded-lg bg-red-500/10 px-3 py-2 text-[12px] text-red-500",children:["Error: ",t.error]}),e.jsx("div",{ref:o})]})]})}function ne(){const[t,a]=u.useState(null),[l,i]=u.useState(!1),r=v({queryKey:p.list(),queryFn:$,refetchInterval:s=>(s.state.data??[]).some(n=>n.status==="queued"||n.status==="running")?3e3:!1}),o=v({queryKey:p.detail(t??""),queryFn:()=>I(t),enabled:!!t,refetchInterval:s=>{const x=s.state.data;return x?.status==="queued"||x?.status==="running"?5e3:!1}}),d=r.data??[],m=o.data;return e.jsxs("div",{className:"flex h-full",children:[e.jsxs("div",{className:"flex w-64 shrink-0 flex-col border-r border-border",children:[e.jsxs("div",{className:"flex items-center justify-between border-b border-border px-3 py-2.5",children:[e.jsx("span",{className:"text-[13px] font-semibold text-ink",children:"Agents"}),e.jsxs("button",{type:"button",title:"New agent run",onClick:()=>i(!0),className:"flex items-center gap-1 rounded-lg bg-accent px-2 py-1 text-[11px] font-medium text-white transition-opacity hover:opacity-90",children:[e.jsx(N,{size:11})," New"]})]}),e.jsxs("div",{className:"flex-1 overflow-y-auto",children:[r.isLoading&&e.jsx("div",{className:"flex h-20 items-center justify-center",children:e.jsx(h,{size:16,className:"animate-spin text-faint"})}),d.length===0&&!r.isLoading&&e.jsxs("div",{className:"px-4 py-8 text-center",children:[e.jsx(S,{size:28,className:"mx-auto mb-2 text-faint"}),e.jsx("p",{className:"text-[12px] text-muted",children:"No agent runs yet."}),e.jsx("p",{className:"mt-0.5 text-[11px] text-faint",children:"Click “New” to start one."})]}),d.map(s=>e.jsxs("button",{type:"button",onClick:()=>a(s.id),className:b("w-full border-b border-border px-3 py-2.5 text-left transition-colors hover:bg-panel-2",t===s.id&&"bg-panel-2"),children:[e.jsx("p",{className:"mb-0.5 truncate text-[13px] font-medium text-ink",children:s.title}),e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsx(z,{status:s.status}),e.jsx("span",{className:"text-[10px] text-faint",children:W(s.createdAt)})]})]},s.id))]})]}),e.jsx("div",{className:"flex min-w-0 flex-1 flex-col",children:t?o.isLoading?e.jsx("div",{className:"flex h-full items-center justify-center",children:e.jsx(h,{size:20,className:"animate-spin text-faint"})}):m?e.jsx(H,{run:m}):null:e.jsxs("div",{className:"flex h-full flex-col items-center justify-center text-center",children:[e.jsx(D,{size:32,className:"mb-3 text-faint"}),e.jsx("p",{className:"text-[14px] font-medium text-ink",children:"Select a run"}),e.jsx("p",{className:"mt-1 text-[12px] text-muted",children:"or create a new agent run to get started"}),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:"mt-4 flex items-center gap-1.5 rounded-lg bg-accent px-3 py-1.5 text-[13px] font-medium text-white hover:opacity-90",children:[e.jsx(N,{size:13})," New agent run"]})]})}),l&&e.jsx(B,{onClose:()=>i(!1),onCreated:s=>{a(s),r.refetch()}})]})}export{ne as AgentsScreen};
|