turbollm 1.7.1 → 1.7.3

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 (116) hide show
  1. package/README.md +16 -14
  2. package/bin/turbollm.mjs +29 -25
  3. package/dist/chunk-TPYU5ZAX.js +175 -0
  4. package/dist/cli.js +2232 -1971
  5. package/dist/distiller-UROKYEBN.js +9 -0
  6. package/dist/webdist/assets/AgentEditPage-DLuckXeR.js +1 -0
  7. package/dist/webdist/assets/ChatScreen-BOZjtqGu.js +65 -0
  8. package/dist/webdist/assets/CustomizeScreen-DoVGTM4C.js +4 -0
  9. package/dist/webdist/assets/DeveloperScreen-BJ7m4ZxK.js +1 -0
  10. package/dist/webdist/assets/EnginesScreen-D3b9VNx0.js +7 -0
  11. package/dist/webdist/assets/{MessageBubble-DG7ZWPCw.js → MessageBubble-mJwFfOab.js} +15 -16
  12. package/dist/webdist/assets/ModelDetailDialog-M2tPWvP4.js +1 -0
  13. package/dist/webdist/assets/ModelDirs-CaIf1TUh.js +1 -0
  14. package/dist/webdist/assets/{ModelsScreen-Bl0OP-BY.js → ModelsScreen--sPDS_sx.js} +2 -2
  15. package/dist/webdist/assets/SettingsScreen-lBpo_MAD.js +1 -0
  16. package/dist/webdist/assets/SkillEditPage-B5S9Gtlh.js +1 -0
  17. package/dist/webdist/assets/WorkspaceScreen-Cpm-gLG1.js +1 -0
  18. package/dist/webdist/assets/agent-api-B3GXwr4Y.js +1 -0
  19. package/dist/webdist/assets/{arc-DVrcAJc9.js → arc-CRcL8nar.js} +1 -1
  20. package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-BaL05OA8.js → architectureDiagram-3BPJPVTR-BmOKGMd0.js} +1 -1
  21. package/dist/webdist/assets/{blockDiagram-GPEHLZMM-D8aCVMG0.js → blockDiagram-GPEHLZMM-C4vusmQ-.js} +1 -1
  22. package/dist/webdist/assets/{c4Diagram-AAUBKEIU-Bpj96pxf.js → c4Diagram-AAUBKEIU-DdY0YXKs.js} +1 -1
  23. package/dist/webdist/assets/channel-BB6NPD_B.js +1 -0
  24. package/dist/webdist/assets/chat-api-DrMO_g5m.js +3 -0
  25. package/dist/webdist/assets/check-BLFw3EpE.js +1 -0
  26. package/dist/webdist/assets/chevron-left-CfyO2AXW.js +1 -0
  27. package/dist/webdist/assets/chevron-right--1OIa5uV.js +1 -0
  28. package/dist/webdist/assets/{chunk-2J33WTMH-DA6mFoN-.js → chunk-2J33WTMH-DzCJsNN1.js} +1 -1
  29. package/dist/webdist/assets/{chunk-4BX2VUAB-B_fmKUNu.js → chunk-4BX2VUAB-BNPfO47j.js} +1 -1
  30. package/dist/webdist/assets/{chunk-55IACEB6-3cWMWgYr.js → chunk-55IACEB6-_vELiE2B.js} +1 -1
  31. package/dist/webdist/assets/{chunk-727SXJPM-Cwb5Sv7F.js → chunk-727SXJPM-CsQCgUxY.js} +1 -1
  32. package/dist/webdist/assets/{chunk-AQP2D5EJ-7QIg1x7-.js → chunk-AQP2D5EJ-G0waQ7sq.js} +1 -1
  33. package/dist/webdist/assets/{chunk-FMBD7UC4-D_Qfl5Jc.js → chunk-FMBD7UC4-CkNtvlrw.js} +1 -1
  34. package/dist/webdist/assets/{chunk-ND2GUHAM-pgqeq7NP.js → chunk-ND2GUHAM-DGnw4n1I.js} +1 -1
  35. package/dist/webdist/assets/{chunk-QZHKN3VN-EUqDaxxx.js → chunk-QZHKN3VN-BpJ4HH0V.js} +1 -1
  36. package/dist/webdist/assets/circle-check-ByN99MCf.js +1 -0
  37. package/dist/webdist/assets/classDiagram-4FO5ZUOK-DwCs4wFr.js +1 -0
  38. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-DwCs4wFr.js +1 -0
  39. package/dist/webdist/assets/collapsible-B_M28W08.js +1 -0
  40. package/dist/webdist/assets/{common-Dn21s6g5.js → common-Cy1uxtS_.js} +1 -1
  41. package/dist/webdist/assets/{copy-button-D6JkFOO9.js → copy-button-DgYGL35x.js} +1 -1
  42. package/dist/webdist/assets/{cose-bilkent-S5V4N54A-BzxdTTj-.js → cose-bilkent-S5V4N54A-fol9bGNP.js} +1 -1
  43. package/dist/webdist/assets/{dagre-BM42HDAG-YQn0mFsV.js → dagre-BM42HDAG-kyg4YiiE.js} +1 -1
  44. package/dist/webdist/assets/{diagram-2AECGRRQ-DtSRGaVH.js → diagram-2AECGRRQ-CK_mQs9l.js} +1 -1
  45. package/dist/webdist/assets/{diagram-5GNKFQAL-Dko3hF44.js → diagram-5GNKFQAL-MBl66jsh.js} +1 -1
  46. package/dist/webdist/assets/{diagram-KO2AKTUF-CQAm-tYF.js → diagram-KO2AKTUF-IxjaUqTu.js} +1 -1
  47. package/dist/webdist/assets/{diagram-LMA3HP47-gz5BqFhM.js → diagram-LMA3HP47-Cc_fuuLo.js} +1 -1
  48. package/dist/webdist/assets/{diagram-OG6HWLK6-BqsSoNIn.js → diagram-OG6HWLK6-DvprGLFT.js} +1 -1
  49. package/dist/webdist/assets/{dialog-B_Yd5di3.js → dialog-d6eLRBbl.js} +1 -1
  50. package/dist/webdist/assets/{erDiagram-TEJ5UH35-D8rPsHw-.js → erDiagram-TEJ5UH35-BEebeQO0.js} +1 -1
  51. package/dist/webdist/assets/{flowDiagram-I6XJVG4X-DhGWyp50.js → flowDiagram-I6XJVG4X--NtrYHee.js} +1 -1
  52. package/dist/webdist/assets/folder-input-C5Td4Oz0.js +1 -0
  53. package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-WEinw7mP.js → ganttDiagram-6RSMTGT7-DOx3A2YH.js} +1 -1
  54. package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-BJ-BzBFO.js → gitGraphDiagram-PVQCEYII-BEY69HKU.js} +1 -1
  55. package/dist/webdist/assets/index-B8H9eSFq.css +1 -0
  56. package/dist/webdist/assets/{index-DHmOrvk5.js → index-Tpho8qZ0.js} +1 -1
  57. package/dist/webdist/assets/index-tVO2x-Rw.js +10 -0
  58. package/dist/webdist/assets/{infoDiagram-5YYISTIA-BA9oHMe2.js → infoDiagram-5YYISTIA-C4X5I5eI.js} +1 -1
  59. package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-BJ5McdEJ.js → ishikawaDiagram-YF4QCWOH-BhUZzSmP.js} +1 -1
  60. package/dist/webdist/assets/{journeyDiagram-JHISSGLW-Bu6WJyQy.js → journeyDiagram-JHISSGLW-R-rmKLZ-.js} +1 -1
  61. package/dist/webdist/assets/{kanban-definition-UN3LZRKU-C_Y8cJ9D.js → kanban-definition-UN3LZRKU-DUcDGDiT.js} +1 -1
  62. package/dist/webdist/assets/{linear-BssoHTW-.js → linear-Cbr5iYFb.js} +1 -1
  63. package/dist/webdist/assets/{mermaid.core-C331Qhyf.js → mermaid.core-DJVxydy1.js} +4 -4
  64. package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-B6NCYWu3.js → mindmap-definition-RKZ34NQL-BSOgl-Cn.js} +1 -1
  65. package/dist/webdist/assets/pencil-Blh_Q7or.js +1 -0
  66. package/dist/webdist/assets/personas-Dm0JD8kn.js +305 -0
  67. package/dist/webdist/assets/{pieDiagram-4H26LBE5-CV23vNlv.js → pieDiagram-4H26LBE5-BwQ854zg.js} +1 -1
  68. package/dist/webdist/assets/plus-D4IwdkJ3.js +1 -0
  69. package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-DxWTWFL7.js → quadrantDiagram-W4KKPZXB-BOQGjhZD.js} +1 -1
  70. package/dist/webdist/assets/{radix-BY-fjzwm.js → radix-DVGeLgPN.js} +2 -2
  71. package/dist/webdist/assets/{refresh-cw-Cl7RshCH.js → refresh-cw-BVSLL9xa.js} +1 -1
  72. package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-DRRulwpq.js → requirementDiagram-4Y6WPE33-b3dNYy6K.js} +1 -1
  73. package/dist/webdist/assets/rotate-ccw-B7QiZxpr.js +1 -0
  74. package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-BTBSg_uE.js → sankeyDiagram-5OEKKPKP-C26ntsbR.js} +1 -1
  75. package/dist/webdist/assets/{save-YXge1rTt.js → save-DI4M5kP-.js} +1 -1
  76. package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-HiTl9dyH.js → sequenceDiagram-3UESZ5HK-Bp8i1DIR.js} +1 -1
  77. package/dist/webdist/assets/{skeleton-BMhnZFjx.js → skeleton-D6toQA7p.js} +1 -1
  78. package/dist/webdist/assets/star-BCxsXqcv.js +1 -0
  79. package/dist/webdist/assets/{stateDiagram-AJRCARHV-SGw6a8ke.js → stateDiagram-AJRCARHV-CmZpIELB.js} +1 -1
  80. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-CswfYrBO.js +1 -0
  81. package/dist/webdist/assets/{timeline-definition-PNZ67QCA-B_X3zBz0.js → timeline-definition-PNZ67QCA-slNuxmat.js} +1 -1
  82. package/dist/webdist/assets/{trash-2-BNQkpp4n.js → trash-2-DGVXsIsP.js} +1 -1
  83. package/dist/webdist/assets/{vendor-W5GIuFcv.js → vendor-BE0HADM8.js} +1 -1
  84. package/dist/webdist/assets/{vennDiagram-CIIHVFJN-C8ibsbKD.js → vennDiagram-CIIHVFJN-Lf4xPS3i.js} +1 -1
  85. package/dist/webdist/assets/{vram-D4ttIJVy.js → vram-DVAmNgRC.js} +1 -1
  86. package/dist/webdist/assets/{wardley-L42UT6IY-ClFBGt2Y.js → wardley-L42UT6IY-DbEeYkcX.js} +1 -1
  87. package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-1iaMKpVQ.js → wardleyDiagram-YWT4CUSO-WNGbDcCj.js} +1 -1
  88. package/dist/webdist/assets/wrench-DzoqtmhJ.js +1 -0
  89. package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-FIIVikoF.js → xychartDiagram-2RQKCTM6-D2kBZ8cG.js} +1 -1
  90. package/dist/webdist/index.html +4 -4
  91. package/package.json +2 -2
  92. package/dist/webdist/assets/AgentsScreen-BToQ_CZg.js +0 -2
  93. package/dist/webdist/assets/ChatScreen-B4T7fIcx.js +0 -65
  94. package/dist/webdist/assets/CustomizeScreen-IL-QlVq2.js +0 -4
  95. package/dist/webdist/assets/DeveloperScreen-DN-FWjXV.js +0 -1
  96. package/dist/webdist/assets/EnginesScreen-GZYaJJNx.js +0 -7
  97. package/dist/webdist/assets/ModelDetailDialog-DhCCmMnp.js +0 -1
  98. package/dist/webdist/assets/ModelDirs-D0d7xetP.js +0 -1
  99. package/dist/webdist/assets/SettingsScreen-DXy_TCXo.js +0 -1
  100. package/dist/webdist/assets/arrow-left-nmoUPZoB.js +0 -1
  101. package/dist/webdist/assets/channel-M9YFYffr.js +0 -1
  102. package/dist/webdist/assets/check-CD6cKVwJ.js +0 -1
  103. package/dist/webdist/assets/chevron-down-DgTSOyZG.js +0 -1
  104. package/dist/webdist/assets/chevron-right-BshRZEvM.js +0 -1
  105. package/dist/webdist/assets/circle-check-Dey3SdiG.js +0 -1
  106. package/dist/webdist/assets/circle-x-M54_eiYo.js +0 -1
  107. package/dist/webdist/assets/classDiagram-4FO5ZUOK-BtQ5UIn2.js +0 -1
  108. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-BtQ5UIn2.js +0 -1
  109. package/dist/webdist/assets/collapsible-CwqYFc9v.js +0 -1
  110. package/dist/webdist/assets/index-B_WtFoLC.css +0 -1
  111. package/dist/webdist/assets/index-C-xjjrht.js +0 -10
  112. package/dist/webdist/assets/pencil-bwNY7waZ.js +0 -1
  113. package/dist/webdist/assets/personas-C_q63e_2.js +0 -75
  114. package/dist/webdist/assets/plus-12BmUfjg.js +0 -1
  115. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-C7Pv9LnA.js +0 -1
  116. package/dist/webdist/assets/wrench-Chhk_zTw.js +0 -1
package/README.md CHANGED
@@ -196,9 +196,11 @@ the detail:
196
196
  the model draws when a comparison, trend, or hierarchy is genuinely worth a visual.
197
197
  - **Live artifacts** — `html`, `svg`, and `mermaid` replies render as **sandboxed, offline
198
198
  previews** shown as an image, with one-click export to **PNG / JPEG / SVG / animated GIF / HTML**.
199
- - **Personas** — pick a style (Default · **Designer** · Concise · Detailed · Blunt · Formal · Tutor ·
200
- Creative · Research) per conversation, no prompt-wrangling required. The **Designer** persona
201
- produces polished, self-contained, previewable designs by default.
199
+ - **Agents** — pick a style (Default · **Designer** · Concise · Detailed · Blunt · Formal · Tutor ·
200
+ Creative · Research · **Lite** · **Code**) per conversation, no prompt-wrangling required. The
201
+ **Designer** persona produces polished, self-contained, previewable designs by default; **Lite**
202
+ strips the hidden prompt to the bare minimum for the fastest responses. See below for editing
203
+ built-ins or creating your own.
202
204
  - **Edit, regenerate, delete, copy** any message; **persistent, searchable conversations**
203
205
  with rename, delete, and **auto-generated titles**, organized into **drag-resizable,
204
206
  collapsible folders** you create/rename/move conversations into.
@@ -219,17 +221,17 @@ the detail:
219
221
  </details>
220
222
 
221
223
  <details>
222
- <summary><strong>🤖 Background agentslong-running tasks that don't tie up your chat</strong></summary>
224
+ <summary><strong>🤖 Customize Agents edit any built-in, or build your own</strong></summary>
223
225
 
224
226
  <br/>
225
227
 
226
- - **Launch an agent and walk away.** The **Agents** screen runs tasks in the daemon, separate from
227
- the chat tab — describe the task, pick which tools it may use (web search / fetch URL / run code),
228
- and let it work.
229
- - **Live, reconnectable progress.** Watch the run stream in real time; navigate away or reload and
230
- the view **reconnects** to the in-progress output. Runs **queue** behind any active run and
231
- **persist** across restarts.
232
- - **Cancel anytime**, and review completed runs (messages + the tool calls they made) later.
228
+ - **Edit any built-in agent in place** system prompt, which shared skills it uses, and which
229
+ tools it may call with a one-click **Reset** back to the original.
230
+ - **Create your own agent** from scratch: a name, description, and system prompt, plus a
231
+ checklist of which shared skills and which tools it's allowed to use (everything on by
232
+ default). Pick it from the same in-chat agent picker as the built-ins.
233
+ - **MCP tools grouped by server** in the tool checklist — one toggle selects or deselects an
234
+ entire server's tools at once, or expand it to pick individually.
233
235
 
234
236
  </details>
235
237
 
@@ -446,7 +448,7 @@ Use `--config <file>` to point at an alternate config (its directory becomes the
446
448
 
447
449
  ## Requirements
448
450
 
449
- - **Node.js 22 or newer** — enforced at startup with a clear message. <https://nodejs.org>
451
+ - **Node.js 22.13.0 or newer** — enforced at startup with a clear message. <https://nodejs.org>
450
452
  - **Windows, macOS, or Linux.**
451
453
  - A GPU is recommended but **not required** — a CPU build is provisioned as a fallback.
452
454
  - On Windows, the first time the auto-downloaded `llama-server` runs, SmartScreen/Defender may
@@ -492,7 +494,7 @@ Prefer Open WebUI's chat breadth? It works great pointed at TurboLLM's OpenAI en
492
494
 
493
495
  ## Troubleshooting
494
496
 
495
- - **`TurboLLM requires Node.js 22 or newer`** — upgrade Node: <https://nodejs.org>.
497
+ - **`TurboLLM requires Node.js 22.13.0 or newer`** — upgrade Node: <https://nodejs.org>.
496
498
  - **Model won't load / OOM** — pick a smaller quant (the VRAM verdict warns you), lower GPU
497
499
  offload, or close other GPU apps. Failures surface in the Engines screen with the engine log.
498
500
  - **Windows Defender / SmartScreen prompt** — that's the upstream `llama-server` binary on
@@ -519,7 +521,7 @@ node dist/cli.js --port 6996
519
521
  Frontend hot-reload: `cd web && npm run dev` (proxies `/api` and `/v1` to the daemon on
520
522
  :6996).
521
523
 
522
- **Stack:** Node ≥22 · TypeScript · Hono · `node:sqlite` · tsup — and a React 19 + Tailwind v4 +
524
+ **Stack:** Node ≥22.13 · TypeScript · Hono · `node:sqlite` · tsup — and a React 19 + Tailwind v4 +
523
525
  shadcn/ui frontend. One TypeScript codebase, shipped as an npm package.
524
526
 
525
527
  ---
package/bin/turbollm.mjs CHANGED
@@ -1,25 +1,29 @@
1
- #!/usr/bin/env node
2
- // Hand-written launcher shim — NOT bundled by tsup. It runs before the bundled
3
- // daemon (dist/cli.js) loads, so it can do two things that must happen before
4
- // node:sqlite is imported (the bundle hoists that import, emitting an experimental
5
- // warning that PowerShell renders as a scary red error block on every run):
6
- // 1. Guard the Node version with a friendly message (node:sqlite needs Node 22+).
7
- // 2. Register a 'warning' filter that swallows the node:sqlite experimental notice
8
- // while still printing every other warning.
9
- // Then it hands off to the real CLI via dynamic import (same process; argv intact).
10
- const major = Number(process.versions.node.split('.')[0])
11
- if (major < 22) {
12
- process.stderr.write(
13
- `TurboLLM requires Node.js 22 or newer.\n` +
14
- `You are running Node.js ${process.versions.node}.\n` +
15
- `Please upgrade: https://nodejs.org\n`,
16
- )
17
- process.exit(1)
18
- }
19
-
20
- process.on('warning', (w) => {
21
- if (w.name === 'ExperimentalWarning' && /SQLite/i.test(w.message)) return
22
- process.stderr.write(`${w.stack ?? `${w.name}: ${w.message}`}\n`)
23
- })
24
-
25
- await import('../dist/cli.js')
1
+ #!/usr/bin/env node
2
+ // Hand-written launcher shim — NOT bundled by tsup. It runs before the bundled
3
+ // daemon (dist/cli.js) loads, so it can do two things that must happen before
4
+ // node:sqlite is imported (the bundle hoists that import, emitting an experimental
5
+ // warning that PowerShell renders as a scary red error block on every run):
6
+ // 1. Guard the Node version with a friendly message (node:sqlite needs Node 22.13+
7
+ // that's when node:sqlite became available WITHOUT the --experimental-sqlite flag;
8
+ // on 22.5.0-22.12.x it's registered only behind that flag, so importing it bare
9
+ // throws ERR_UNKNOWN_BUILTIN_MODULE: "No such built-in module: node:sqlite"
10
+ // (GitHub #40) even though `node -v` reports 22.x).
11
+ // 2. Register a 'warning' filter that swallows the node:sqlite experimental notice
12
+ // while still printing every other warning.
13
+ // Then it hands off to the real CLI via dynamic import (same process; argv intact).
14
+ const [major, minor] = process.versions.node.split('.').map(Number)
15
+ if (major < 22 || (major === 22 && minor < 13)) {
16
+ process.stderr.write(
17
+ `TurboLLM requires Node.js 22.13.0 or newer (needs node:sqlite unflagged).\n` +
18
+ `You are running Node.js ${process.versions.node}.\n` +
19
+ `Please upgrade: https://nodejs.org\n`,
20
+ )
21
+ process.exit(1)
22
+ }
23
+
24
+ process.on('warning', (w) => {
25
+ if (w.name === 'ExperimentalWarning' && /SQLite/i.test(w.message)) return
26
+ process.stderr.write(`${w.stack ?? `${w.name}: ${w.message}`}\n`)
27
+ })
28
+
29
+ await import('../dist/cli.js')
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/agents/distiller.ts
4
+ import { readFileSync, readdirSync, statSync, existsSync } from "fs";
5
+ import { join } from "path";
6
+
7
+ // src/engines/compat.ts
8
+ function engineAcceptsFormat(engineKind, format) {
9
+ if (engineKind === "mlx") return format === "mlx";
10
+ if (engineKind === "vllm") return format === "mlx";
11
+ return format === "gguf";
12
+ }
13
+ var ENGINE_MODEL_ALIAS = "default_model";
14
+ function engineModelAlias(engineKind) {
15
+ return engineKind === "mlx" || engineKind === "vllm" || engineKind === "sglang" ? ENGINE_MODEL_ALIAS : null;
16
+ }
17
+ var VENDOR_DISPLAY = {
18
+ nvidia: "NVIDIA",
19
+ amd: "AMD",
20
+ intel: "Intel",
21
+ apple: "Apple Silicon",
22
+ unknown: "no detected GPU"
23
+ };
24
+ var PLATFORM_DISPLAY = {
25
+ darwin: "macOS",
26
+ win32: "Windows",
27
+ linux: "Linux"
28
+ };
29
+ function platformName(p) {
30
+ return PLATFORM_DISPLAY[p] ?? p;
31
+ }
32
+ function platformReason(allowed) {
33
+ const names = allowed.map(platformName);
34
+ const joined = names.length <= 1 ? names.join("") : `${names.slice(0, -1).join(", ")} & ${names[names.length - 1]}`;
35
+ return `${joined} only`;
36
+ }
37
+ function evaluateVariant(p, r) {
38
+ if (r.platform && !r.platform.includes(p.platform)) {
39
+ return { ok: false, reason: platformReason(r.platform) };
40
+ }
41
+ if (r.arch && !r.arch.includes(p.arch)) {
42
+ return { ok: false, reason: `Needs ${r.arch.join(" / ")}` };
43
+ }
44
+ if (r.gpuVendor && !r.gpuVendor.includes(p.gpuVendor)) {
45
+ const needs = r.gpuVendor.map((v) => VENDOR_DISPLAY[v]).join(" / ");
46
+ const article = /^[NAEIO]/.test(needs) ? "an" : "a";
47
+ return { ok: false, reason: `Needs ${article} ${needs} GPU \u2014 you have ${VENDOR_DISPLAY[p.gpuVendor]}` };
48
+ }
49
+ if (r.minVramMb !== void 0 && p.vramMb > 0 && p.vramMb < r.minVramMb) {
50
+ const gb = Math.round(r.minVramMb / 1024);
51
+ return { ok: false, reason: `Needs ~${gb} GB VRAM` };
52
+ }
53
+ return { ok: true };
54
+ }
55
+
56
+ // src/agents/distiller.ts
57
+ var DISTILL_SYSTEM = `You write a single reusable SKILL from the material provided. A skill captures a repeatable approach so it can be applied to similar future tasks.
58
+
59
+ Output a JSON object: {"name": <short kebab-case id>, "description": <one sentence: when to use this skill>, "procedure": <concise step-by-step procedure, markdown>}
60
+
61
+ Rules:
62
+ - The skill must be GENERAL and reusable, not a one-off answer to this specific input.
63
+ - "name" is a short kebab-case identifier (e.g. "csv-summary", "extract-config-values").
64
+ - "description" is one sentence describing WHEN to apply it.
65
+ - "procedure" is a concise, numbered set of steps \u2014 the how.
66
+ - If the material does not contain a clear reusable approach, output {"name": null, "description": null, "procedure": null}.
67
+ Respond with ONLY the JSON object, nothing else.`;
68
+ async function distill(d, material) {
69
+ const ms = d.manager.status();
70
+ const target = d.manager.target();
71
+ if (ms.state !== "running" || !ms.model || !target) return { name: null, description: null, procedure: null };
72
+ const model = engineModelAlias(d.registry.active()?.kind ?? "") ?? ms.model.key;
73
+ try {
74
+ const res = await fetch(`${target}/v1/chat/completions`, {
75
+ method: "POST",
76
+ headers: { "Content-Type": "application/json" },
77
+ body: JSON.stringify({
78
+ model,
79
+ messages: [
80
+ { role: "system", content: DISTILL_SYSTEM },
81
+ { role: "user", content: `Material:
82
+
83
+ ${material.slice(0, 24e3)}
84
+
85
+ Write ONE reusable skill as raw JSON \u2014 no markdown fences.` }
86
+ ],
87
+ temperature: 0.3,
88
+ max_tokens: 800,
89
+ reasoning_budget: 0,
90
+ chat_template_kwargs: { enable_thinking: false },
91
+ response_format: { type: "json_object" },
92
+ stream: false
93
+ }),
94
+ signal: AbortSignal.timeout(9e4)
95
+ });
96
+ if (!res.ok) return { name: null, description: null, procedure: null };
97
+ const data = await res.json();
98
+ let txt = data.choices?.[0]?.message?.content ?? "";
99
+ txt = txt.replace(/<think>[\s\S]*?<\/think>/gi, "").replace(/```json|```/gi, "").trim();
100
+ const m = txt.match(/\{[\s\S]*\}/);
101
+ if (!m) return { name: null, description: null, procedure: null };
102
+ const j = JSON.parse(m[0]);
103
+ const valid = (s) => typeof s === "string" && s.trim() && s.toLowerCase() !== "null" ? s.trim() : null;
104
+ const name = valid(j.name);
105
+ const procedure = valid(j.procedure);
106
+ if (!name || !procedure) return { name: null, description: null, procedure: null };
107
+ return { name: name.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64), description: valid(j.description) ?? "", procedure };
108
+ } catch {
109
+ return { name: null, description: null, procedure: null };
110
+ }
111
+ }
112
+ function distillFromConversation(d, transcript) {
113
+ const text = transcript.map((m) => `${m.role.toUpperCase()}: ${m.content}`).join("\n");
114
+ return distill(d, `A successful completed task:
115
+
116
+ ${text}`);
117
+ }
118
+ var FOLDER_TEXT = /\.(md|txt|json|ya?ml|csv|js|ts|py|sh|html?|css)$/i;
119
+ function collectFolderFiles(folder, max) {
120
+ const files = [];
121
+ const walk = (dir, depth) => {
122
+ if (depth > 4 || files.length >= max) return;
123
+ let entries;
124
+ try {
125
+ entries = readdirSync(dir, { withFileTypes: true });
126
+ } catch {
127
+ return;
128
+ }
129
+ for (const e of entries) {
130
+ if (files.length >= max) return;
131
+ const p = join(dir, e.name);
132
+ if (e.isDirectory()) {
133
+ walk(p, depth + 1);
134
+ continue;
135
+ }
136
+ if (!FOLDER_TEXT.test(e.name)) continue;
137
+ try {
138
+ if (statSync(p).size > 2e5) continue;
139
+ const content = readFileSync(p, "utf8").slice(0, 6e3).trim();
140
+ if (content) files.push({ name: e.name, content });
141
+ } catch {
142
+ }
143
+ }
144
+ };
145
+ walk(folder, 0);
146
+ return files;
147
+ }
148
+ async function distillSkillsFromFolder(d, folder, opts = {}) {
149
+ if (!existsSync(folder) || !statSync(folder).isDirectory()) return [];
150
+ const max = opts.max ?? 12;
151
+ const files = collectFolderFiles(folder, max);
152
+ if (!files.length) return [];
153
+ const out = [];
154
+ const seen = /* @__PURE__ */ new Set();
155
+ for (let i = 0; i < files.length; i++) {
156
+ const f = files[i];
157
+ opts.onProgress?.(i, files.length, f.name);
158
+ const s = await distill(d, `A reusable approach captured in "${f.name}":
159
+
160
+ ${f.content}`);
161
+ if (s.name && s.procedure && !seen.has(s.name)) {
162
+ seen.add(s.name);
163
+ out.push(s);
164
+ }
165
+ }
166
+ return out;
167
+ }
168
+
169
+ export {
170
+ engineAcceptsFormat,
171
+ engineModelAlias,
172
+ evaluateVariant,
173
+ distillFromConversation,
174
+ distillSkillsFromFolder
175
+ };