turbollm 1.9.7 → 1.9.8
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 +16 -14
- package/dist/cli.js +178 -46
- package/dist/webdist/assets/{AgentEditPage-C8RGdWJF.js → AgentEditPage-D2MKlhC4.js} +1 -1
- package/dist/webdist/assets/{ChatScreen-N3uMBH5o.js → ChatScreen-BT8jCGTN.js} +1 -1
- package/dist/webdist/assets/{CodeComposer-CiCzwc9F.js → CodeComposer-BCwZMgGI.js} +1 -1
- package/dist/webdist/assets/{CodeHomeScreen-CL0EstEl.js → CodeHomeScreen-Wcb_8M4g.js} +1 -1
- package/dist/webdist/assets/{CodeSessionScreen-Cmpd5nk-.js → CodeSessionScreen-CgANva49.js} +1 -1
- package/dist/webdist/assets/{CustomizeScreen-BIdHpMx-.js → CustomizeScreen-DV9OIXsG.js} +1 -1
- package/dist/webdist/assets/{DeveloperScreen-Du9T6j_f.js → DeveloperScreen-vndr1xLZ.js} +1 -1
- package/dist/webdist/assets/{EnginesScreen-CFhygpHp.js → EnginesScreen-DZ20QyGt.js} +1 -1
- package/dist/webdist/assets/{FsBrowser-yCDndpa8.js → FsBrowser-BVRCuSAt.js} +1 -1
- package/dist/webdist/assets/{ModelLoadMenu-B6OgZ7AZ.js → ModelLoadMenu-DHeG2IT7.js} +1 -1
- package/dist/webdist/assets/{ModelsScreen-DAcHEngS.js → ModelsScreen-BxpUJPoo.js} +1 -1
- package/dist/webdist/assets/{SettingsScreen-CgwmrEYG.js → SettingsScreen-DxbU7U2g.js} +1 -1
- package/dist/webdist/assets/{SkillEditPage-swxEgg6z.js → SkillEditPage-C8z0asiI.js} +1 -1
- package/dist/webdist/assets/{TokensScreen-1gAoLbZq.js → TokensScreen-27qcX_mG.js} +1 -1
- package/dist/webdist/assets/{ToolApprovalBar-D-VPdhAA.js → ToolApprovalBar-qM2wqtNL.js} +3 -3
- package/dist/webdist/assets/WorkspaceScreen-BVZ7QWkL.js +1 -0
- package/dist/webdist/assets/{agent-api-C4rG-Jhm.js → agent-api-B8x62EvQ.js} +1 -1
- package/dist/webdist/assets/{arc-DN-erBAi.js → arc-BW8F_BQA.js} +1 -1
- package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-C_Y3sT8y.js → architectureDiagram-3BPJPVTR-zfH49ZGf.js} +1 -1
- package/dist/webdist/assets/{arrow-left-Dv89StCL.js → arrow-left-A509Fetv.js} +1 -1
- package/dist/webdist/assets/{badge-C367M9ui.js → badge-BEGHN2Gs.js} +1 -1
- package/dist/webdist/assets/{blockDiagram-GPEHLZMM-DIhPpxoy.js → blockDiagram-GPEHLZMM-B36qo5WU.js} +1 -1
- package/dist/webdist/assets/{bot-CkS7SpAq.js → bot-DmS4yJ_3.js} +1 -1
- package/dist/webdist/assets/{c4Diagram-AAUBKEIU-DhxTzCHS.js → c4Diagram-AAUBKEIU-DAnyJ1zY.js} +1 -1
- package/dist/webdist/assets/channel-6n-J43t-.js +1 -0
- package/dist/webdist/assets/{chat-api-BjLw__7G.js → chat-api-D8Zk6kRh.js} +1 -1
- package/dist/webdist/assets/{chat-queries-DkW-crMK.js → chat-queries-M23Lkm4X.js} +1 -1
- package/dist/webdist/assets/{chevron-left-CvC9QlMY.js → chevron-left-BwGlXiTi.js} +1 -1
- package/dist/webdist/assets/{chevron-right-dxKQD1jQ.js → chevron-right-B8nbhuzl.js} +1 -1
- package/dist/webdist/assets/{chunk-2J33WTMH-BStbX98A.js → chunk-2J33WTMH-CKSa-SWg.js} +1 -1
- package/dist/webdist/assets/{chunk-4BX2VUAB-Dt_NYZlI.js → chunk-4BX2VUAB-BJ5ORVQz.js} +1 -1
- package/dist/webdist/assets/{chunk-55IACEB6-Bhp_kq7k.js → chunk-55IACEB6-Cg3bYYac.js} +1 -1
- package/dist/webdist/assets/{chunk-727SXJPM-DSlmyTXu.js → chunk-727SXJPM-uerTg8Pr.js} +1 -1
- package/dist/webdist/assets/{chunk-AQP2D5EJ-DTjPEmPv.js → chunk-AQP2D5EJ-DqdFefiE.js} +1 -1
- package/dist/webdist/assets/{chunk-FMBD7UC4-CJFy7xKH.js → chunk-FMBD7UC4-DHVbvOSl.js} +1 -1
- package/dist/webdist/assets/{chunk-ND2GUHAM-U5KxKYoG.js → chunk-ND2GUHAM-Bd_Jx54f.js} +1 -1
- package/dist/webdist/assets/{chunk-QZHKN3VN-DqU8C1o_.js → chunk-QZHKN3VN-iIS0rOO4.js} +1 -1
- package/dist/webdist/assets/{circle-check-X2gW5Lao.js → circle-check-CxTKIoUQ.js} +1 -1
- package/dist/webdist/assets/{circle-x-EHdG_-2l.js → circle-x-DtTsKVOs.js} +1 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-aMec4COx.js +1 -0
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-aMec4COx.js +1 -0
- package/dist/webdist/assets/{common-CR_Lyg91.js → common-BLhKAx-E.js} +1 -1
- package/dist/webdist/assets/{cose-bilkent-S5V4N54A-BYKK4Cf1.js → cose-bilkent-S5V4N54A-Q0XTfDW4.js} +1 -1
- package/dist/webdist/assets/{dagre-BM42HDAG-g4hhdP8F.js → dagre-BM42HDAG-BpEjp0Mo.js} +1 -1
- package/dist/webdist/assets/{diagram-2AECGRRQ-Dr_agpgo.js → diagram-2AECGRRQ-CQgcaxwM.js} +1 -1
- package/dist/webdist/assets/{diagram-5GNKFQAL-HhGT-1mW.js → diagram-5GNKFQAL-BAXLYnn8.js} +1 -1
- package/dist/webdist/assets/{diagram-KO2AKTUF-DAbDCiEN.js → diagram-KO2AKTUF-hS4UiX21.js} +1 -1
- package/dist/webdist/assets/{diagram-LMA3HP47-69Vaqnrj.js → diagram-LMA3HP47-BSH8Svf2.js} +1 -1
- package/dist/webdist/assets/{diagram-OG6HWLK6-BfoIa3zL.js → diagram-OG6HWLK6-ChkrDAcn.js} +1 -1
- package/dist/webdist/assets/{dialog-J9fwiM_6.js → dialog-Cif52qlh.js} +1 -1
- package/dist/webdist/assets/{dropdown-menu-CzGm9m23.js → dropdown-menu-CEXJQu9r.js} +1 -1
- package/dist/webdist/assets/{erDiagram-TEJ5UH35-_kvCnb2f.js → erDiagram-TEJ5UH35-CoSbJaoh.js} +1 -1
- package/dist/webdist/assets/{flowDiagram-I6XJVG4X-CyoD5NFD.js → flowDiagram-I6XJVG4X-CxmlDLjH.js} +1 -1
- package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-BHbeuBsb.js → ganttDiagram-6RSMTGT7-D9DcHOHi.js} +1 -1
- package/dist/webdist/assets/{git-branch-DwkQBy50.js → git-branch-wVIAC1y2.js} +1 -1
- package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-CLiLu2eD.js → gitGraphDiagram-PVQCEYII-PZXq-NT0.js} +1 -1
- package/dist/webdist/assets/{index-sdlWNoLv.js → index-BQecSQnE.js} +9 -9
- package/dist/webdist/assets/index-DsKBTDFr.css +1 -0
- package/dist/webdist/assets/{infoDiagram-5YYISTIA-DUsHPouL.js → infoDiagram-5YYISTIA-OdiZto4C.js} +1 -1
- package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-dmOwTeXN.js → ishikawaDiagram-YF4QCWOH-ByqCES2J.js} +1 -1
- package/dist/webdist/assets/{journeyDiagram-JHISSGLW-B-ROjE-9.js → journeyDiagram-JHISSGLW-iAvir64R.js} +1 -1
- package/dist/webdist/assets/{kanban-definition-UN3LZRKU-BcmeWTJv.js → kanban-definition-UN3LZRKU-Coq1b_4K.js} +1 -1
- package/dist/webdist/assets/{linear-D_7tH0LK.js → linear-CFHN7GNP.js} +1 -1
- package/dist/webdist/assets/{lock-B6DBc8H2.js → lock-C-qBN6Je.js} +1 -1
- package/dist/webdist/assets/{mermaid.core-COPgjG_G.js → mermaid.core-BbE5ZQQb.js} +4 -4
- package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-DYa-6SsO.js → mindmap-definition-RKZ34NQL-BJI67qBG.js} +1 -1
- package/dist/webdist/assets/{pencil-dcCQp7PP.js → pencil-CLrsEWvO.js} +1 -1
- package/dist/webdist/assets/{personas-sJAzYoeu.js → personas-DjECKK03.js} +1 -1
- package/dist/webdist/assets/{pieDiagram-4H26LBE5-Btte96Dk.js → pieDiagram-4H26LBE5-ClFRQd-P.js} +1 -1
- package/dist/webdist/assets/{plus-BvjpqMip.js → plus-CnpqRmY0.js} +1 -1
- package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-NS-IUaYv.js → quadrantDiagram-W4KKPZXB-C6h_Xp8y.js} +1 -1
- package/dist/webdist/assets/{refresh-cw-CJO18sMX.js → refresh-cw-C92HxozQ.js} +1 -1
- package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-6S8LhS5B.js → requirementDiagram-4Y6WPE33-otwWbKYv.js} +1 -1
- package/dist/webdist/assets/{rotate-ccw-Dg4hTNG6.js → rotate-ccw-CTVQYcfd.js} +1 -1
- package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-DzP_aBJ0.js → sankeyDiagram-5OEKKPKP-DbXQbMGj.js} +1 -1
- package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-DbM6WtvX.js → sequenceDiagram-3UESZ5HK-DVeUokYb.js} +1 -1
- package/dist/webdist/assets/{skeleton-BWg_uQbt.js → skeleton-Bgx_ejYi.js} +1 -1
- package/dist/webdist/assets/{sliders-horizontal-BEN8YNq8.js → sliders-horizontal-kCZOXks3.js} +1 -1
- package/dist/webdist/assets/{sparkles-BXKX67cv.js → sparkles-CK3xY-de.js} +1 -1
- package/dist/webdist/assets/{star-UYX7iaVC.js → star-Bl6Mcr3J.js} +1 -1
- package/dist/webdist/assets/{stateDiagram-AJRCARHV-DQd2hln9.js → stateDiagram-AJRCARHV-BTTdXeL2.js} +1 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-PhwvulL3.js +1 -0
- package/dist/webdist/assets/{switch-CbjIB99k.js → switch-_1we_sIC.js} +1 -1
- package/dist/webdist/assets/{terminal-DVNTdtpQ.js → terminal-BITU7jD3.js} +1 -1
- package/dist/webdist/assets/{timeline-definition-PNZ67QCA-BnfK2zzg.js → timeline-definition-PNZ67QCA-DloHmMh_.js} +1 -1
- package/dist/webdist/assets/{trash-2-DkWlMuY9.js → trash-2-D0azs_Tt.js} +1 -1
- package/dist/webdist/assets/{useIsDesktop-DvCa8GTi.js → useIsDesktop-DaHDDrDA.js} +1 -1
- package/dist/webdist/assets/{vennDiagram-CIIHVFJN-tPTfe0OZ.js → vennDiagram-CIIHVFJN-Ccv3ZcVV.js} +1 -1
- package/dist/webdist/assets/{wardley-L42UT6IY-D7FeB03v.js → wardley-L42UT6IY-DpBTXmML.js} +1 -1
- package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-BDRCgpY_.js → wardleyDiagram-YWT4CUSO-CNQLbvwQ.js} +1 -1
- package/dist/webdist/assets/{wrench-BoYGiR8D.js → wrench-B_ypp0gU.js} +1 -1
- package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-CVQgje-B.js → xychartDiagram-2RQKCTM6-Dkv0G4Ag.js} +1 -1
- package/dist/webdist/index.html +2 -2
- package/package.json +1 -1
- package/dist/webdist/assets/WorkspaceScreen-DA4l7OyH.js +0 -1
- package/dist/webdist/assets/channel-B5rf-0pa.js +0 -1
- package/dist/webdist/assets/classDiagram-4FO5ZUOK-B6iRj6y1.js +0 -1
- package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-B6iRj6y1.js +0 -1
- package/dist/webdist/assets/index-DKAl71q8.css +0 -1
- package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-DnzMvaiS.js +0 -1
package/README.md
CHANGED
|
@@ -99,9 +99,10 @@ TurboLLM does the opposite:
|
|
|
99
99
|
- **🔀 A gateway that loads models for you.** Name any model in your API request and TurboLLM
|
|
100
100
|
loads it on demand, keeping your favorites hot in a small pool — so an agent that hops between
|
|
101
101
|
models just works, with nothing to pre-wire.
|
|
102
|
-
- **🔒 Offline-first & private.** No account, no backend, no internet required. Telemetry
|
|
103
|
-
**
|
|
104
|
-
|
|
102
|
+
- **🔒 Offline-first & private.** No account, no backend, no internet required. Telemetry
|
|
103
|
+
**defaults to on** (usage + benchmarks + crash reports) so setup issues and real-hardware speed
|
|
104
|
+
get found — change your level any time in Settings, and see exactly what leaves your machine,
|
|
105
|
+
verified in [Privacy](#privacy).
|
|
105
106
|
|
|
106
107
|
---
|
|
107
108
|
|
|
@@ -402,8 +403,8 @@ connected); **Remove** undoes it.
|
|
|
402
403
|
<br/>
|
|
403
404
|
|
|
404
405
|
- A **~7 MB npm package** on Node — no Electron, no bundled Chromium, no Python.
|
|
405
|
-
- **Offline-first** — no account, no backend, no internet required. Telemetry
|
|
406
|
-
|
|
406
|
+
- **Offline-first** — no account, no backend, no internet required. Telemetry defaults to on,
|
|
407
|
+
changeable any time; see [Privacy](#privacy).
|
|
407
408
|
- **Windows · macOS · Linux**, with a CPU fallback when there's no GPU.
|
|
408
409
|
|
|
409
410
|
</details>
|
|
@@ -589,18 +590,19 @@ TurboLLM is **offline-first**: core local use needs no account, no backend, and
|
|
|
589
590
|
choice.** That's not a promise you have to take on faith: the client is source-available, so the
|
|
590
591
|
claim is checkable against the actual code that ships.
|
|
591
592
|
|
|
592
|
-
**Telemetry
|
|
593
|
-
|
|
593
|
+
**Telemetry defaults to on** (Usage + benchmarks + crash reports), so setup problems and
|
|
594
|
+
real-hardware speed get found and fixed — verifiable telemetry only helps if enough people are
|
|
595
|
+
actually sending it. Change your level any time from **Settings → Privacy & telemetry**:
|
|
594
596
|
|
|
595
597
|
- **Off** — sends only that one-time choice, nothing else, ever.
|
|
596
598
|
- **Anonymous usage + benchmarks** — which features you use, your hardware, model names, and
|
|
597
599
|
measured speed. Never prompts, files, paths, or keys.
|
|
598
|
-
-
|
|
600
|
+
- **Usage + benchmarks + crash reports** (default) — adds error fingerprints (never your content).
|
|
599
601
|
|
|
600
602
|
You can preview the exact payload before anything is sent, and inspect a running log of every
|
|
601
|
-
event this machine has *actually* transmitted, from
|
|
602
|
-
|
|
603
|
-
|
|
603
|
+
event this machine has *actually* transmitted, from the same Settings section. For scripted or
|
|
604
|
+
unattended use, `--no-telemetry` (or `TURBOLLM_TELEMETRY=off`) is a hard, one-way kill switch,
|
|
605
|
+
independent of the saved level.
|
|
604
606
|
|
|
605
607
|
---
|
|
606
608
|
|
|
@@ -621,13 +623,13 @@ Marks reflect mid-2026; verify the moving rows against each tool's current docs.
|
|
|
621
623
|
| Speculative decoding (draft / MTP) | ✅ | ✅ | ◐ env flag | ❌ |
|
|
622
624
|
| Web UI from any LAN device | ✅ | ❌ | ❌ | ✅ |
|
|
623
625
|
| **Lightweight** (no Electron / no Python) | ✅ npm | ❌ Electron | ✅ Go | ❌ Python |
|
|
624
|
-
| Offline-first · **
|
|
626
|
+
| Offline-first · **verifiable telemetry** (on by default) | ✅ verifiable (source-available) | ◐ closed app — not verifiable | ✅ | ✅ |
|
|
625
627
|
|
|
626
628
|
LM Studio and Ollama both added Anthropic `/v1/messages` endpoints in 2026, so the API rows are
|
|
627
629
|
now parity — Claude Code works against any of them. TurboLLM's durable edges are **any engine
|
|
628
630
|
including community forks**, **benchmark-based auto-tuning with a VRAM-fit verdict + measured t/s
|
|
629
|
-
before you commit**, and **telemetry you can actually verify** —
|
|
630
|
-
checkable against source-available code rather than taken on trust.
|
|
631
|
+
before you commit**, and **telemetry you can actually verify** — on by default, changeable any
|
|
632
|
+
time, and checkable against source-available code rather than taken on trust.
|
|
631
633
|
|
|
632
634
|
Prefer Open WebUI's chat breadth? It works great pointed at TurboLLM's OpenAI endpoint.
|
|
633
635
|
|
package/dist/cli.js
CHANGED
|
@@ -109,7 +109,7 @@ function defaultConfig() {
|
|
|
109
109
|
autoMemoryEnabled: false,
|
|
110
110
|
experimental: { memory: false, cloudDeploy: false }
|
|
111
111
|
},
|
|
112
|
-
telemetry: { level: "
|
|
112
|
+
telemetry: { level: "full", machineId: "" },
|
|
113
113
|
apiKeys: [],
|
|
114
114
|
engines: [],
|
|
115
115
|
customEngineSources: [],
|
|
@@ -223,7 +223,7 @@ function migrate(raw, from) {
|
|
|
223
223
|
cfg2[k] = v;
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
-
if (from < 4 && cfg2.telemetry.level === "off") cfg2.telemetry.level = "
|
|
226
|
+
if (from < 4 && cfg2.telemetry.level === "off") cfg2.telemetry.level = "full";
|
|
227
227
|
return cfg2;
|
|
228
228
|
}
|
|
229
229
|
function splitLaunchArgs(args) {
|
|
@@ -404,8 +404,9 @@ function normalize(c) {
|
|
|
404
404
|
c.version = SCHEMA_VERSION;
|
|
405
405
|
}
|
|
406
406
|
function normalizeTelemetryLevel(level) {
|
|
407
|
-
if (level === "
|
|
407
|
+
if (level === "off" || level === "anon" || level === "full") return level;
|
|
408
408
|
if (level === "benchmarks" || level === "anonymous") return "anon";
|
|
409
|
+
if (level === void 0 || level === null || level === "unset") return "full";
|
|
409
410
|
return "off";
|
|
410
411
|
}
|
|
411
412
|
function validate(c) {
|
|
@@ -8077,7 +8078,11 @@ var BenchRunner = class {
|
|
|
8077
8078
|
* see `reportModelLoad`'s own once-only claim — so a re-tune later on
|
|
8078
8079
|
* cannot overwrite it. */
|
|
8079
8080
|
reportFirstLoad(outcome, failReason) {
|
|
8080
|
-
if (this.telemetry)
|
|
8081
|
+
if (!this.telemetry) return;
|
|
8082
|
+
if (claimOnce(this.store.dir(), "once:onboarding_step:first_load")) {
|
|
8083
|
+
this.telemetry.emit("onboarding_step", { step: "first_load", outcome });
|
|
8084
|
+
}
|
|
8085
|
+
reportModelLoad(this.store.dir(), this.telemetry, outcome, failReason);
|
|
8081
8086
|
}
|
|
8082
8087
|
/** Stop the engine — force-killed immediately when the run is cancelled (the user is actively
|
|
8083
8088
|
* waiting for it to stop, ADR-220), gracefully otherwise (lets llama-server release resources
|
|
@@ -11614,14 +11619,14 @@ function runMcpServer(baseUrl, version2) {
|
|
|
11614
11619
|
// src/server.ts
|
|
11615
11620
|
import { Hono } from "hono";
|
|
11616
11621
|
import { cors } from "hono/cors";
|
|
11617
|
-
import { existsSync as
|
|
11622
|
+
import { existsSync as existsSync26, readFileSync as readFileSync17, statSync as statSync6 } from "fs";
|
|
11618
11623
|
import { fileURLToPath } from "url";
|
|
11619
11624
|
import { dirname as dirname11, join as join33, normalize as normalize2 } from "path";
|
|
11620
11625
|
import { Agent, setGlobalDispatcher } from "undici";
|
|
11621
11626
|
|
|
11622
11627
|
// src/api/routes.ts
|
|
11623
11628
|
import { streamSSE as streamSSE2 } from "hono/streaming";
|
|
11624
|
-
import { existsSync as
|
|
11629
|
+
import { existsSync as existsSync24, mkdirSync as mkdirSync16, readFileSync as readFileSync14, readdirSync as readdirSync10, realpathSync, rmSync as rmSync14, statSync as statSync5, writeFileSync as writeFileSync14 } from "fs";
|
|
11625
11630
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
11626
11631
|
import { basename as basename3, dirname as dirname9, join as join29, resolve } from "path";
|
|
11627
11632
|
|
|
@@ -13639,6 +13644,7 @@ function enabledFeatures() {
|
|
|
13639
13644
|
}
|
|
13640
13645
|
|
|
13641
13646
|
// src/terminal/terminal-routes.ts
|
|
13647
|
+
import { existsSync as existsSync21 } from "fs";
|
|
13642
13648
|
import { createRequire as createRequire2 } from "module";
|
|
13643
13649
|
|
|
13644
13650
|
// src/code/session-auth.ts
|
|
@@ -14166,6 +14172,7 @@ var TerminalManager = class {
|
|
|
14166
14172
|
|
|
14167
14173
|
// src/code/worktree.ts
|
|
14168
14174
|
import { spawn as spawn5 } from "child_process";
|
|
14175
|
+
import { existsSync as existsSync20 } from "fs";
|
|
14169
14176
|
import { appendFile, readFile as readFile2, mkdir as mkdir2 } from "fs/promises";
|
|
14170
14177
|
import { join as join25 } from "path";
|
|
14171
14178
|
var WORKTREES_SUBDIR = join25(".turbollm", "worktrees");
|
|
@@ -14288,6 +14295,7 @@ async function createSessionWorktree(params) {
|
|
|
14288
14295
|
return { ok: true, path, branch };
|
|
14289
14296
|
}
|
|
14290
14297
|
async function removeSessionWorktree(repoRoot, worktreePath) {
|
|
14298
|
+
if (!existsSync20(worktreePath) || !existsSync20(repoRoot)) return { ok: true };
|
|
14291
14299
|
const r = await runGit(repoRoot, ["worktree", "remove", worktreePath], 6e4);
|
|
14292
14300
|
if (r.ok) return { ok: true };
|
|
14293
14301
|
const err7 = (r.stderr || r.stdout).trim();
|
|
@@ -14384,6 +14392,16 @@ function registerTerminalRoutes(app2, d) {
|
|
|
14384
14392
|
if (agent === "turbollm") {
|
|
14385
14393
|
return err2(c, 400, "invalid_input", "This session uses the built-in chat UI, not a terminal agent.");
|
|
14386
14394
|
}
|
|
14395
|
+
const cwd = agentCwd(run);
|
|
14396
|
+
if (!existsSync21(cwd)) {
|
|
14397
|
+
const isWorktree = !!run.worktreePath && cwd === run.worktreePath;
|
|
14398
|
+
return err2(
|
|
14399
|
+
c,
|
|
14400
|
+
400,
|
|
14401
|
+
"cwd_missing",
|
|
14402
|
+
isWorktree ? `This session's worktree is gone: ${cwd}. It was probably removed outside TurboLLM (\`git worktree remove\`, or deleting the folder). The session's branch and any commits on it are unaffected \u2014 delete this session and start a new one on the same repo.` : `This session's folder no longer exists: ${cwd}. It may have been moved, renamed or deleted, or it lives on a drive that isn't connected. Reconnect or restore it, or delete this session.`
|
|
14403
|
+
);
|
|
14404
|
+
}
|
|
14387
14405
|
const b = await body2(c);
|
|
14388
14406
|
const cols = typeof b.cols === "number" && b.cols > 0 ? Math.floor(b.cols) : 80;
|
|
14389
14407
|
const rows = typeof b.rows === "number" && b.rows > 0 ? Math.floor(b.rows) : 24;
|
|
@@ -14412,7 +14430,7 @@ function registerTerminalRoutes(app2, d) {
|
|
|
14412
14430
|
mode,
|
|
14413
14431
|
firstMessage
|
|
14414
14432
|
);
|
|
14415
|
-
const terminalId = m.create(
|
|
14433
|
+
const terminalId = m.create(cwd, run.id, cols, rows, launchCommand);
|
|
14416
14434
|
if (!run.terminalLaunchedOnce) d.db.updateAgentRun(run.id, { terminalLaunchedOnce: true });
|
|
14417
14435
|
return c.json({ terminalId }, 201);
|
|
14418
14436
|
} catch (e) {
|
|
@@ -14938,7 +14956,7 @@ function catalogEngine(id) {
|
|
|
14938
14956
|
|
|
14939
14957
|
// src/engines/cuda-provision.ts
|
|
14940
14958
|
import { execFile as execFile11 } from "child_process";
|
|
14941
|
-
import { cpSync as cpSync2, existsSync as
|
|
14959
|
+
import { cpSync as cpSync2, existsSync as existsSync22, mkdirSync as mkdirSync14, readdirSync as readdirSync9, rmSync as rmSync13 } from "fs";
|
|
14942
14960
|
import { tmpdir } from "os";
|
|
14943
14961
|
import { join as join26 } from "path";
|
|
14944
14962
|
import { promisify as promisify8 } from "util";
|
|
@@ -14997,7 +15015,7 @@ async function provisionCuda(dataDir, onProgress, signal) {
|
|
|
14997
15015
|
});
|
|
14998
15016
|
const root = join26(dataDir, "cuda", version2);
|
|
14999
15017
|
const binDir = join26(root, "bin");
|
|
15000
|
-
if (
|
|
15018
|
+
if (existsSync22(join26(binDir, "nvcc.exe"))) {
|
|
15001
15019
|
onProgress({ phase: "assembling", message: `CUDA ${version2} already downloaded \u2014 reusing it.` });
|
|
15002
15020
|
return { root, binDir, version: version2 };
|
|
15003
15021
|
}
|
|
@@ -15028,12 +15046,12 @@ async function provisionCuda(dataDir, onProgress, signal) {
|
|
|
15028
15046
|
mkdirSync14(exDir, { recursive: true });
|
|
15029
15047
|
onProgress({ phase: "extracting", message: `Extracting ${id}\u2026` });
|
|
15030
15048
|
await extractArchive(zip, exDir);
|
|
15031
|
-
const inner = readdirSync9(exDir).map((n) => join26(exDir, n)).find((p) =>
|
|
15049
|
+
const inner = readdirSync9(exDir).map((n) => join26(exDir, n)).find((p) => existsSync22(join26(p, "include")) || existsSync22(join26(p, "bin")) || existsSync22(join26(p, "lib")));
|
|
15032
15050
|
const srcRoot = inner ?? join26(exDir, readdirSync9(exDir)[0] ?? "");
|
|
15033
15051
|
cpSync2(srcRoot, stageRoot, { recursive: true, force: true });
|
|
15034
15052
|
rmSync13(zip, { force: true });
|
|
15035
15053
|
}
|
|
15036
|
-
if (!
|
|
15054
|
+
if (!existsSync22(join26(stageRoot, "bin", "nvcc.exe"))) {
|
|
15037
15055
|
throw new Error("Assembled CUDA toolkit is missing bin/nvcc.exe \u2014 the component set may have changed.");
|
|
15038
15056
|
}
|
|
15039
15057
|
rmSync13(root, { recursive: true, force: true });
|
|
@@ -15118,7 +15136,7 @@ function recommendEngines(p, engines) {
|
|
|
15118
15136
|
}
|
|
15119
15137
|
|
|
15120
15138
|
// src/artifacts/screenshot.ts
|
|
15121
|
-
import { existsSync as
|
|
15139
|
+
import { existsSync as existsSync23 } from "fs";
|
|
15122
15140
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
15123
15141
|
import { join as join27 } from "path";
|
|
15124
15142
|
function chromeCandidates() {
|
|
@@ -15162,7 +15180,7 @@ function chromeCandidates() {
|
|
|
15162
15180
|
function findChrome() {
|
|
15163
15181
|
for (const p of chromeCandidates()) {
|
|
15164
15182
|
try {
|
|
15165
|
-
if (p &&
|
|
15183
|
+
if (p && existsSync23(p)) return p;
|
|
15166
15184
|
} catch {
|
|
15167
15185
|
}
|
|
15168
15186
|
}
|
|
@@ -15387,7 +15405,7 @@ function registerApi(app2, d) {
|
|
|
15387
15405
|
app2.get("/api/v1/engines", (c) => {
|
|
15388
15406
|
const { engines, activeEngineId } = d.registry.list();
|
|
15389
15407
|
const liveKeys = new Set(engines.map((e) => customSourceKey(e)));
|
|
15390
|
-
const customDisabled = d.registry.customSources().filter((s) => !liveKeys.has(customSourceKey(s))).map((s) => ({ ...s, binPathExists:
|
|
15408
|
+
const customDisabled = d.registry.customSources().filter((s) => !liveKeys.has(customSourceKey(s))).map((s) => ({ ...s, binPathExists: existsSync24(s.binPath) }));
|
|
15391
15409
|
return c.json({ engines, activeEngineId, customDisabled });
|
|
15392
15410
|
});
|
|
15393
15411
|
let provisionAbort = null;
|
|
@@ -15418,7 +15436,7 @@ function registerApi(app2, d) {
|
|
|
15418
15436
|
};
|
|
15419
15437
|
});
|
|
15420
15438
|
const mlxVenvPy = join29(root, "mlx", "venv", process.platform === "win32" ? "Scripts/python.exe" : "bin/python");
|
|
15421
|
-
const mlxInstalledOnDisk =
|
|
15439
|
+
const mlxInstalledOnDisk = existsSync24(mlxVenvPy);
|
|
15422
15440
|
const mlxEngine = regEngines.find((e) => e.kind === "mlx");
|
|
15423
15441
|
const mlx = {
|
|
15424
15442
|
supported: process.platform === "darwin",
|
|
@@ -15620,17 +15638,17 @@ function registerApi(app2, d) {
|
|
|
15620
15638
|
"venv",
|
|
15621
15639
|
process.platform === "win32" ? "Scripts/python.exe" : "bin/python"
|
|
15622
15640
|
);
|
|
15623
|
-
installed2 =
|
|
15641
|
+
installed2 = existsSync24(pyPath);
|
|
15624
15642
|
enabled = regEngines.some((x) => x.kind === e.kind);
|
|
15625
15643
|
} else if (e.id === "turboquant") {
|
|
15626
15644
|
const tqDir = join29(enginesRoot, "turboquant");
|
|
15627
|
-
installed2 =
|
|
15645
|
+
installed2 = existsSync24(tqDir);
|
|
15628
15646
|
enabled = regEngines.some((x) => /[\\/]engines[\\/]turboquant[\\/]/.test(x.binPath));
|
|
15629
15647
|
} else if (e.id === "koboldcpp") {
|
|
15630
|
-
installed2 =
|
|
15648
|
+
installed2 = existsSync24(koboldcppBinPath(enginesRoot));
|
|
15631
15649
|
enabled = regEngines.some((x) => x.kind === "koboldcpp");
|
|
15632
15650
|
} else if (e.id === "llamafile") {
|
|
15633
|
-
installed2 =
|
|
15651
|
+
installed2 = existsSync24(llamafileBinPath(enginesRoot));
|
|
15634
15652
|
enabled = regEngines.some((x) => x.kind === "llamafile");
|
|
15635
15653
|
}
|
|
15636
15654
|
const srcEng = regEngines.find(
|
|
@@ -15857,7 +15875,7 @@ function registerApi(app2, d) {
|
|
|
15857
15875
|
d.provision.start("turboquant");
|
|
15858
15876
|
if (upgrade) {
|
|
15859
15877
|
const tqDir = join29(root, "turboquant");
|
|
15860
|
-
if (
|
|
15878
|
+
if (existsSync24(tqDir)) rmSync14(tqDir, { recursive: true, force: true });
|
|
15861
15879
|
}
|
|
15862
15880
|
const bin = await provisionTurboquant(
|
|
15863
15881
|
root,
|
|
@@ -15893,7 +15911,7 @@ function registerApi(app2, d) {
|
|
|
15893
15911
|
d.provision.start("koboldcpp");
|
|
15894
15912
|
if (upgrade) {
|
|
15895
15913
|
const dir = koboldcppDir(root);
|
|
15896
|
-
if (
|
|
15914
|
+
if (existsSync24(dir)) rmSync14(dir, { recursive: true, force: true });
|
|
15897
15915
|
}
|
|
15898
15916
|
const rt = await ensureKoboldcpp(root, hasNvidia, (p) => d.provision.progress(p.phase, p.pct, p.part, p.parts));
|
|
15899
15917
|
const eng = d.registry.addKoboldcpp(`KoboldCpp (${rt.version})`, rt.binPath, rt.version);
|
|
@@ -15923,7 +15941,7 @@ function registerApi(app2, d) {
|
|
|
15923
15941
|
d.provision.start("llamafile");
|
|
15924
15942
|
if (upgrade) {
|
|
15925
15943
|
const dir = llamafileDir(root);
|
|
15926
|
-
if (
|
|
15944
|
+
if (existsSync24(dir)) rmSync14(dir, { recursive: true, force: true });
|
|
15927
15945
|
}
|
|
15928
15946
|
const rt = await ensureLlamafile(root, (p) => d.provision.progress(p.phase, p.pct, p.part, p.parts));
|
|
15929
15947
|
const eng = d.registry.addLlamafile(`llamafile (${rt.version})`, rt.binPath, rt.version);
|
|
@@ -16025,7 +16043,7 @@ function registerApi(app2, d) {
|
|
|
16025
16043
|
if (purge && eng) {
|
|
16026
16044
|
const enginesRoot = join29(d.store.dir(), "engines");
|
|
16027
16045
|
const purgeDir = engineInstallDir(eng, enginesRoot);
|
|
16028
|
-
if (purgeDir &&
|
|
16046
|
+
if (purgeDir && existsSync24(purgeDir)) {
|
|
16029
16047
|
rmSync14(purgeDir, { recursive: true, force: true });
|
|
16030
16048
|
}
|
|
16031
16049
|
d.registry.forgetCustomSource(customSourceKey(eng));
|
|
@@ -16132,7 +16150,7 @@ function registerApi(app2, d) {
|
|
|
16132
16150
|
const raw = (c.req.query("path") ?? "").trim();
|
|
16133
16151
|
if (!raw) return err3(c, 400, "invalid_input", "path is required.");
|
|
16134
16152
|
const cwd = resolve(raw);
|
|
16135
|
-
if (!
|
|
16153
|
+
if (!existsSync24(cwd) || !statSync5(cwd).isDirectory()) {
|
|
16136
16154
|
return c.json({ isRepo: false, branch: "", branches: [] });
|
|
16137
16155
|
}
|
|
16138
16156
|
const git2 = (args) => execFileSync6("git", args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"], timeout: 5e3, windowsHide: true }).trim();
|
|
@@ -16255,12 +16273,12 @@ function registerApi(app2, d) {
|
|
|
16255
16273
|
const raw = (b.path ?? "").trim();
|
|
16256
16274
|
if (!raw) return err3(c, 400, "invalid_config_value", "Enter the path to your ComfyUI folder.");
|
|
16257
16275
|
const root = resolve(raw);
|
|
16258
|
-
if (!
|
|
16276
|
+
if (!existsSync24(root) || !statSync5(root).isDirectory()) {
|
|
16259
16277
|
return err3(c, 400, "invalid_config_value", "That folder does not exist.");
|
|
16260
16278
|
}
|
|
16261
16279
|
let customNodes;
|
|
16262
16280
|
if (basename3(root).toLowerCase() === "custom_nodes") customNodes = root;
|
|
16263
|
-
else if (
|
|
16281
|
+
else if (existsSync24(join29(root, "custom_nodes"))) customNodes = join29(root, "custom_nodes");
|
|
16264
16282
|
else {
|
|
16265
16283
|
return err3(c, 400, "invalid_config_value", "No 'custom_nodes' folder here. Point me at your ComfyUI folder or its custom_nodes folder.");
|
|
16266
16284
|
}
|
|
@@ -16281,7 +16299,7 @@ function registerApi(app2, d) {
|
|
|
16281
16299
|
});
|
|
16282
16300
|
app2.post("/api/v1/comfyui/uninstall", (c) => {
|
|
16283
16301
|
const dir = d.store.snapshot().comfyui.gatePath;
|
|
16284
|
-
if (dir &&
|
|
16302
|
+
if (dir && existsSync24(dir)) {
|
|
16285
16303
|
try {
|
|
16286
16304
|
rmSync14(dir, { recursive: true, force: true });
|
|
16287
16305
|
} catch (e) {
|
|
@@ -16316,7 +16334,7 @@ function registerApi(app2, d) {
|
|
|
16316
16334
|
});
|
|
16317
16335
|
while (!aborted) {
|
|
16318
16336
|
const path = d.manager.logPath();
|
|
16319
|
-
if (path &&
|
|
16337
|
+
if (path && existsSync24(path)) {
|
|
16320
16338
|
const lines = readFileSync14(path, "utf8").split("\n");
|
|
16321
16339
|
for (; sent < lines.length - 1; sent++) {
|
|
16322
16340
|
await stream.writeSSE({ event: "line", data: JSON.stringify({ line: lines[sent].replace(/\r$/, "") }) });
|
|
@@ -16715,7 +16733,7 @@ function registerApi(app2, d) {
|
|
|
16715
16733
|
const b = await body3(c);
|
|
16716
16734
|
const dir = (b.dir ?? "").trim();
|
|
16717
16735
|
if (!dir || !/^([a-zA-Z]:[\\/]|[\\/])/.test(dir)) return err3(c, 400, "invalid_config_value", "Path must be absolute.");
|
|
16718
|
-
if (!
|
|
16736
|
+
if (!existsSync24(dir)) return err3(c, 400, "invalid_config_value", "That folder does not exist.");
|
|
16719
16737
|
try {
|
|
16720
16738
|
d.store.update((cfg2) => {
|
|
16721
16739
|
if (!cfg2.modelDirs.includes(dir)) cfg2.modelDirs.push(dir);
|
|
@@ -16951,13 +16969,6 @@ function settingsPayload(d) {
|
|
|
16951
16969
|
lanBind: cfg2.daemon.lanBind,
|
|
16952
16970
|
requireApiKey: cfg2.daemon.requireApiKey,
|
|
16953
16971
|
telemetryLevel,
|
|
16954
|
-
// Whether the user has ever actually answered (ADR-299 Decision 4).
|
|
16955
|
-
// `telemetryLevel` above deliberately collapses the first-run 'unset'
|
|
16956
|
-
// sentinel to 'off' so every consumer fails safe — but that also makes
|
|
16957
|
-
// "never asked" indistinguishable from "chose Off", and the first-run
|
|
16958
|
-
// consent card needs exactly that distinction. Hence a separate flag
|
|
16959
|
-
// rather than leaking 'unset' into the level enum.
|
|
16960
|
-
telemetryDecided: cfg2.telemetry.level !== "unset",
|
|
16961
16972
|
modelDefaults: cfg2.modelDefaults,
|
|
16962
16973
|
comfyui: cfg2.comfyui,
|
|
16963
16974
|
gateway: cfg2.gateway,
|
|
@@ -17139,7 +17150,7 @@ function cleanModelName(p) {
|
|
|
17139
17150
|
return basename3(p).replace(/\.gguf$/i, "");
|
|
17140
17151
|
}
|
|
17141
17152
|
function readTail2(path, n) {
|
|
17142
|
-
if (!path || !
|
|
17153
|
+
if (!path || !existsSync24(path)) return [];
|
|
17143
17154
|
try {
|
|
17144
17155
|
const lines = readFileSync14(path, "utf8").replace(/[\r\n]+$/, "").split("\n").map((l) => l.replace(/\r$/, ""));
|
|
17145
17156
|
return lines.length > n ? lines.slice(-n) : lines;
|
|
@@ -17153,7 +17164,7 @@ function listWindowsDrives() {
|
|
|
17153
17164
|
for (let i = 65; i <= 90; i++) {
|
|
17154
17165
|
const root = `${String.fromCharCode(i)}:\\`;
|
|
17155
17166
|
try {
|
|
17156
|
-
if (
|
|
17167
|
+
if (existsSync24(root)) drives.push({ name: root, path: root, isDir: true });
|
|
17157
17168
|
} catch {
|
|
17158
17169
|
}
|
|
17159
17170
|
}
|
|
@@ -19877,7 +19888,7 @@ function subscribeToBuffer(buffer, emitter, opts) {
|
|
|
19877
19888
|
}
|
|
19878
19889
|
|
|
19879
19890
|
// src/code/revert.ts
|
|
19880
|
-
import { existsSync as
|
|
19891
|
+
import { existsSync as existsSync25, readFileSync as readFileSync16, writeFileSync as writeFileSync15 } from "fs";
|
|
19881
19892
|
import { resolve as resolve4 } from "path";
|
|
19882
19893
|
import { applyPatch, parsePatch, reversePatch } from "diff";
|
|
19883
19894
|
function revertFileEdits(messages, repoRoot) {
|
|
@@ -19900,7 +19911,7 @@ function revertFileEdits(messages, repoRoot) {
|
|
|
19900
19911
|
continue;
|
|
19901
19912
|
}
|
|
19902
19913
|
const absPath = resolve4(repoRoot, path);
|
|
19903
|
-
if (!
|
|
19914
|
+
if (!existsSync25(absPath)) {
|
|
19904
19915
|
failed.push(path);
|
|
19905
19916
|
continue;
|
|
19906
19917
|
}
|
|
@@ -20733,6 +20744,7 @@ function registerCodeRoutes(app2, d) {
|
|
|
20733
20744
|
// src/gateway/gateway.ts
|
|
20734
20745
|
import { randomUUID as randomUUID12 } from "crypto";
|
|
20735
20746
|
import { streamSSE as streamSSE4 } from "hono/streaming";
|
|
20747
|
+
import { createPatch } from "diff";
|
|
20736
20748
|
|
|
20737
20749
|
// src/gateway/anthropic.ts
|
|
20738
20750
|
import { randomUUID as randomUUID10 } from "crypto";
|
|
@@ -20918,11 +20930,12 @@ function mapFromOpenAI(oai, modelName) {
|
|
|
20918
20930
|
usage: { input_tokens: usage?.prompt_tokens ?? 0, output_tokens: usage?.completion_tokens ?? 0 }
|
|
20919
20931
|
};
|
|
20920
20932
|
}
|
|
20921
|
-
async function* streamToAnthropic(oaiStream, modelName, msgId, onUsage, onLive) {
|
|
20933
|
+
async function* streamToAnthropic(oaiStream, modelName, msgId, onUsage, onLive, onToolCalls) {
|
|
20922
20934
|
let blockIdx = 0;
|
|
20923
20935
|
let inThinking = false;
|
|
20924
20936
|
let inText = false;
|
|
20925
20937
|
let activeToolIdx = -1;
|
|
20938
|
+
const toolAcc = /* @__PURE__ */ new Map();
|
|
20926
20939
|
let stopReason = "end_turn";
|
|
20927
20940
|
let outputTokens = 0;
|
|
20928
20941
|
let inputTokens = 0;
|
|
@@ -21014,6 +21027,14 @@ async function* streamToAnthropic(oaiStream, modelName, msgId, onUsage, onLive)
|
|
|
21014
21027
|
blockIdx++;
|
|
21015
21028
|
}
|
|
21016
21029
|
for (const tc of delta.tool_calls) {
|
|
21030
|
+
let acc = toolAcc.get(tc.index);
|
|
21031
|
+
if (!acc) {
|
|
21032
|
+
acc = { id: "", name: "", args: "" };
|
|
21033
|
+
toolAcc.set(tc.index, acc);
|
|
21034
|
+
}
|
|
21035
|
+
if (!acc.id && tc.id) acc.id = tc.id;
|
|
21036
|
+
if (!acc.name && tc.function?.name) acc.name = tc.function.name;
|
|
21037
|
+
if (tc.function?.arguments) acc.args += tc.function.arguments;
|
|
21017
21038
|
if (tc.index !== activeToolIdx) {
|
|
21018
21039
|
if (activeToolIdx >= 0) {
|
|
21019
21040
|
yield cbStop(blockIdx);
|
|
@@ -21071,6 +21092,19 @@ async function* streamToAnthropic(oaiStream, modelName, msgId, onUsage, onLive)
|
|
|
21071
21092
|
} catch {
|
|
21072
21093
|
}
|
|
21073
21094
|
}
|
|
21095
|
+
if (onToolCalls) {
|
|
21096
|
+
const calls = [];
|
|
21097
|
+
for (const acc of toolAcc.values()) {
|
|
21098
|
+
try {
|
|
21099
|
+
calls.push({ id: acc.id, name: acc.name, input: JSON.parse(acc.args) });
|
|
21100
|
+
} catch {
|
|
21101
|
+
}
|
|
21102
|
+
}
|
|
21103
|
+
try {
|
|
21104
|
+
onToolCalls(calls);
|
|
21105
|
+
} catch {
|
|
21106
|
+
}
|
|
21107
|
+
}
|
|
21074
21108
|
}
|
|
21075
21109
|
}
|
|
21076
21110
|
function sse(event, data) {
|
|
@@ -21484,6 +21518,7 @@ function registerGateway(app2, d) {
|
|
|
21484
21518
|
}
|
|
21485
21519
|
const { token: anthropicToken, codeSessionId: anthropicCodeSessionId } = resolveCodeSession(c);
|
|
21486
21520
|
if (anthropicCodeSessionId) {
|
|
21521
|
+
commitConfirmedCodeToolCalls(d, anthropicCodeSessionId, req);
|
|
21487
21522
|
const override = sessionAuth.getThinkingBudgetForToken(anthropicToken);
|
|
21488
21523
|
if (override !== null) {
|
|
21489
21524
|
req.thinking = override > 0 ? { type: "enabled", budget_tokens: override } : void 0;
|
|
@@ -21598,6 +21633,15 @@ function registerGateway(app2, d) {
|
|
|
21598
21633
|
d.manager.setLiveGen(live);
|
|
21599
21634
|
} catch {
|
|
21600
21635
|
}
|
|
21636
|
+
},
|
|
21637
|
+
// Coding-activity attribution for terminal-agent sessions — see
|
|
21638
|
+
// observeCodeSessionTurn. Nothing to attribute for any other client.
|
|
21639
|
+
(calls) => {
|
|
21640
|
+
if (!anthropicCodeSessionId) return;
|
|
21641
|
+
try {
|
|
21642
|
+
observeCodeSessionTurn(d, anthropicCodeSessionId, calls);
|
|
21643
|
+
} catch {
|
|
21644
|
+
}
|
|
21601
21645
|
}
|
|
21602
21646
|
);
|
|
21603
21647
|
return streamSSE4(c, async (stream) => {
|
|
@@ -21616,6 +21660,12 @@ function registerGateway(app2, d) {
|
|
|
21616
21660
|
try {
|
|
21617
21661
|
const oaiRes = await res.json();
|
|
21618
21662
|
recordOpenAiUsage(d, oaiRes, "anthropic", req.model ?? null, anthropicCodeSessionId, Date.now() - requestStart);
|
|
21663
|
+
if (anthropicCodeSessionId) {
|
|
21664
|
+
try {
|
|
21665
|
+
observeCodeSessionTurn(d, anthropicCodeSessionId, openAiToolCalls(oaiRes));
|
|
21666
|
+
} catch {
|
|
21667
|
+
}
|
|
21668
|
+
}
|
|
21619
21669
|
return c.json(mapFromOpenAI(oaiRes, modelName));
|
|
21620
21670
|
} finally {
|
|
21621
21671
|
d.manager.generationEnd();
|
|
@@ -21764,6 +21814,82 @@ function registerGateway(app2, d) {
|
|
|
21764
21814
|
return new Response(res.body, { status: res.status, headers: res.headers });
|
|
21765
21815
|
});
|
|
21766
21816
|
}
|
|
21817
|
+
var CODE_ACTIVITY_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "multiedit"]);
|
|
21818
|
+
function openAiToolCalls(oai) {
|
|
21819
|
+
const raw = oai.choices?.[0]?.message?.tool_calls;
|
|
21820
|
+
if (!Array.isArray(raw)) return [];
|
|
21821
|
+
const calls = [];
|
|
21822
|
+
for (const tc of raw) {
|
|
21823
|
+
try {
|
|
21824
|
+
calls.push({ id: tc.id ?? "", name: tc.function?.name ?? "", input: JSON.parse(tc.function?.arguments ?? "") });
|
|
21825
|
+
} catch {
|
|
21826
|
+
}
|
|
21827
|
+
}
|
|
21828
|
+
return calls;
|
|
21829
|
+
}
|
|
21830
|
+
var MAX_DIFF_INPUT_CHARS = 64 * 1024;
|
|
21831
|
+
var pendingCodeToolCalls = /* @__PURE__ */ new Map();
|
|
21832
|
+
var MAX_PENDING_PER_SESSION = 256;
|
|
21833
|
+
function observeCodeSessionTurn(d, codeSessionId, calls) {
|
|
21834
|
+
try {
|
|
21835
|
+
const run = d.db.getAgentRun(codeSessionId);
|
|
21836
|
+
if (!run) return;
|
|
21837
|
+
let pending2 = pendingCodeToolCalls.get(codeSessionId);
|
|
21838
|
+
for (const call of calls) {
|
|
21839
|
+
const name = call.name.toLowerCase();
|
|
21840
|
+
if (!CODE_ACTIVITY_TOOLS.has(name)) continue;
|
|
21841
|
+
if (!call.id) continue;
|
|
21842
|
+
const input = call.input ?? {};
|
|
21843
|
+
const path = typeof input.file_path === "string" ? input.file_path : "";
|
|
21844
|
+
if (!path) continue;
|
|
21845
|
+
let record;
|
|
21846
|
+
if (name === "write") {
|
|
21847
|
+
record = { id: call.id, name: "write", args: { path } };
|
|
21848
|
+
} else if (name === "edit") {
|
|
21849
|
+
const oldString = typeof input.old_string === "string" ? input.old_string : "";
|
|
21850
|
+
const newString = typeof input.new_string === "string" ? input.new_string : "";
|
|
21851
|
+
record = oldString.length + newString.length > MAX_DIFF_INPUT_CHARS ? { id: call.id, name: "edit", args: { path } } : { id: call.id, name: "edit", args: { path }, diff: createPatch(path, oldString, newString) };
|
|
21852
|
+
} else {
|
|
21853
|
+
record = { id: call.id, name: "edit", args: { path } };
|
|
21854
|
+
}
|
|
21855
|
+
if (!pending2) {
|
|
21856
|
+
pending2 = /* @__PURE__ */ new Map();
|
|
21857
|
+
pendingCodeToolCalls.set(codeSessionId, pending2);
|
|
21858
|
+
}
|
|
21859
|
+
pending2.set(call.id, record);
|
|
21860
|
+
}
|
|
21861
|
+
while (pending2 && pending2.size > MAX_PENDING_PER_SESSION) {
|
|
21862
|
+
const oldest = pending2.keys().next().value;
|
|
21863
|
+
if (oldest === void 0) break;
|
|
21864
|
+
pending2.delete(oldest);
|
|
21865
|
+
}
|
|
21866
|
+
d.db.updateAgentRun(run.id, { status: "done", endedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
21867
|
+
} catch {
|
|
21868
|
+
}
|
|
21869
|
+
}
|
|
21870
|
+
function commitConfirmedCodeToolCalls(d, codeSessionId, req) {
|
|
21871
|
+
try {
|
|
21872
|
+
const pending2 = pendingCodeToolCalls.get(codeSessionId);
|
|
21873
|
+
if (!pending2 || pending2.size === 0) return;
|
|
21874
|
+
const confirmed = [];
|
|
21875
|
+
for (const msg2 of req.messages ?? []) {
|
|
21876
|
+
const raw = msg2.content;
|
|
21877
|
+
if (!Array.isArray(raw)) continue;
|
|
21878
|
+
for (const block of raw) {
|
|
21879
|
+
if (block.type !== "tool_result") continue;
|
|
21880
|
+
const record = pending2.get(block.tool_use_id);
|
|
21881
|
+
if (!record) continue;
|
|
21882
|
+
pending2.delete(block.tool_use_id);
|
|
21883
|
+
if (block.is_error !== true) confirmed.push(record);
|
|
21884
|
+
}
|
|
21885
|
+
}
|
|
21886
|
+
if (confirmed.length === 0) return;
|
|
21887
|
+
const run = d.db.getAgentRun(codeSessionId);
|
|
21888
|
+
if (!run) return;
|
|
21889
|
+
d.db.addMessage(run.convId, "assistant", "", { toolCalls: confirmed });
|
|
21890
|
+
} catch {
|
|
21891
|
+
}
|
|
21892
|
+
}
|
|
21767
21893
|
function recordOpenAiUsage(d, oai, source, modelKey, codeSessionId = null, durationMs = null) {
|
|
21768
21894
|
try {
|
|
21769
21895
|
const usage = oai.usage;
|
|
@@ -21924,10 +22050,10 @@ function createApp(d) {
|
|
|
21924
22050
|
return c.json({ error: { code: "not_found", message: "Unknown endpoint." } }, 404);
|
|
21925
22051
|
}
|
|
21926
22052
|
let file = normalize2(join33(WEB_ROOT, path || "index.html"));
|
|
21927
|
-
if (!file.startsWith(WEB_ROOT) || !
|
|
22053
|
+
if (!file.startsWith(WEB_ROOT) || !existsSync26(file) || statSync6(file).isDirectory()) {
|
|
21928
22054
|
file = join33(WEB_ROOT, "index.html");
|
|
21929
22055
|
}
|
|
21930
|
-
if (!
|
|
22056
|
+
if (!existsSync26(file)) return c.text("web ui not built \u2014 run `npm run build:web`", 500);
|
|
21931
22057
|
return new Response(readFileSync17(file), { status: 200, headers: { "Content-Type": contentType(file) } });
|
|
21932
22058
|
});
|
|
21933
22059
|
return app2;
|
|
@@ -21958,7 +22084,7 @@ import { mkdirSync as mkdirSync18, readFileSync as readFileSync18, readdirSync a
|
|
|
21958
22084
|
import { join as join35 } from "path";
|
|
21959
22085
|
|
|
21960
22086
|
// src/tunnel/provision.ts
|
|
21961
|
-
import { chmodSync as chmodSync3, existsSync as
|
|
22087
|
+
import { chmodSync as chmodSync3, existsSync as existsSync27, mkdirSync as mkdirSync17, rmSync as rmSync15 } from "fs";
|
|
21962
22088
|
import { join as join34 } from "path";
|
|
21963
22089
|
var CLOUDFLARED_REPO = "cloudflare/cloudflared";
|
|
21964
22090
|
function cloudflaredDir(dataDir) {
|
|
@@ -21994,7 +22120,7 @@ async function ensureCloudflared(dataDir, onProgress, signal) {
|
|
|
21994
22120
|
const binPath = cloudflaredBinPath(dataDir);
|
|
21995
22121
|
const rel = await latestGithubRelease(CLOUDFLARED_REPO, signal);
|
|
21996
22122
|
const version2 = rel.tag_name ?? "";
|
|
21997
|
-
if (
|
|
22123
|
+
if (existsSync27(binPath)) return { binPath, version: version2 };
|
|
21998
22124
|
const asset = pickCloudflaredAsset(rel.assets ?? []);
|
|
21999
22125
|
if (!asset) throw new Error("no_release_asset");
|
|
22000
22126
|
mkdirSync17(dir, { recursive: true });
|
|
@@ -22480,7 +22606,13 @@ deps.agentTasks = new AgentTaskState();
|
|
|
22480
22606
|
var telemetry = new Emitter({ dataDir: store.dir(), store, version, os: getSysInfo().os });
|
|
22481
22607
|
deps.telemetry = telemetry;
|
|
22482
22608
|
telemetry.dailyActive();
|
|
22483
|
-
manager.onLoadSettled = (ok, err7) =>
|
|
22609
|
+
manager.onLoadSettled = (ok, err7) => {
|
|
22610
|
+
const outcome = ok ? "ok" : "fail";
|
|
22611
|
+
if (claimOnce(store.dir(), "once:onboarding_step:first_load")) {
|
|
22612
|
+
telemetry.emit("onboarding_step", { step: "first_load", outcome });
|
|
22613
|
+
}
|
|
22614
|
+
reportModelLoad(store.dir(), telemetry, outcome, ok ? void 0 : classifyLoadFailure(err7));
|
|
22615
|
+
};
|
|
22484
22616
|
bench.telemetry = telemetry;
|
|
22485
22617
|
build.onSettled = (ok) => telemetry.emit("onboarding_step", { step: "engine_build", outcome: ok ? "ok" : "fail" });
|
|
22486
22618
|
provision.onSettled = (ok) => telemetry.emit("onboarding_step", { step: "engine_install", outcome: ok ? "ok" : "fail" });
|