turbollm 1.7.2 → 1.7.4

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 (119) hide show
  1. package/README.md +23 -20
  2. package/dist/chunk-TPYU5ZAX.js +175 -0
  3. package/dist/cli.js +2125 -1948
  4. package/dist/distiller-UROKYEBN.js +9 -0
  5. package/dist/webdist/assets/AgentEditPage-BOnwuNEo.js +1 -0
  6. package/dist/webdist/assets/ChatScreen-C-etPxFP.js +65 -0
  7. package/dist/webdist/assets/CustomizeScreen-CBS-4CGF.js +4 -0
  8. package/dist/webdist/assets/DeveloperScreen-Dm9p8rsC.js +1 -0
  9. package/dist/webdist/assets/EnginesScreen-KF8kClsH.js +7 -0
  10. package/dist/webdist/assets/FsBrowser-Un46d4qe.js +1 -0
  11. package/dist/webdist/assets/{MessageBubble-COuZ8jTd.js → MessageBubble-5F87qAxY.js} +15 -16
  12. package/dist/webdist/assets/ModelsScreen-6VbHhlyf.js +2 -0
  13. package/dist/webdist/assets/SettingsScreen-DjoLkPpP.js +1 -0
  14. package/dist/webdist/assets/SkillEditPage-C5Evktgw.js +1 -0
  15. package/dist/webdist/assets/WorkspaceScreen-CiKb7f1I.js +1 -0
  16. package/dist/webdist/assets/agent-api-D3hMsrHk.js +1 -0
  17. package/dist/webdist/assets/{arc-BrQAHE5c.js → arc-BhnuNjta.js} +1 -1
  18. package/dist/webdist/assets/{architectureDiagram-3BPJPVTR-DzOwDk0E.js → architectureDiagram-3BPJPVTR-c3DASz95.js} +1 -1
  19. package/dist/webdist/assets/{arrow-left-Dp4XRnbs.js → arrow-left-BZ7U2kS9.js} +1 -1
  20. package/dist/webdist/assets/{blockDiagram-GPEHLZMM-Cbs2lNCh.js → blockDiagram-GPEHLZMM-CkpfHyuv.js} +1 -1
  21. package/dist/webdist/assets/bot-B3GTFQ2J.js +1 -0
  22. package/dist/webdist/assets/{c4Diagram-AAUBKEIU-BAull9Yv.js → c4Diagram-AAUBKEIU-B5dn9Zuf.js} +1 -1
  23. package/dist/webdist/assets/channel-CJpy6WaL.js +1 -0
  24. package/dist/webdist/assets/chat-api-Dz6qvO1a.js +3 -0
  25. package/dist/webdist/assets/check-C39bHy3w.js +1 -0
  26. package/dist/webdist/assets/chevron-left-Bm2f38an.js +1 -0
  27. package/dist/webdist/assets/chevron-right-DGQJG4QN.js +1 -0
  28. package/dist/webdist/assets/{chunk-2J33WTMH-B38k4sJL.js → chunk-2J33WTMH-_x1EGF5n.js} +1 -1
  29. package/dist/webdist/assets/{chunk-4BX2VUAB-VxAkYvb7.js → chunk-4BX2VUAB-pQD6zp-n.js} +1 -1
  30. package/dist/webdist/assets/{chunk-55IACEB6-CAz9ZWyR.js → chunk-55IACEB6-DIsjXVDw.js} +1 -1
  31. package/dist/webdist/assets/{chunk-727SXJPM-CvLX5ghS.js → chunk-727SXJPM-CHDF4RtM.js} +1 -1
  32. package/dist/webdist/assets/{chunk-AQP2D5EJ-CZ0fsnc1.js → chunk-AQP2D5EJ-CaQFwmUk.js} +1 -1
  33. package/dist/webdist/assets/{chunk-FMBD7UC4-tjHz_yw8.js → chunk-FMBD7UC4-DENyT0Cw.js} +1 -1
  34. package/dist/webdist/assets/{chunk-ND2GUHAM-DVSh5E5_.js → chunk-ND2GUHAM-Cv6Yek6j.js} +1 -1
  35. package/dist/webdist/assets/{chunk-QZHKN3VN-D1rUM2SU.js → chunk-QZHKN3VN-9ATQymUl.js} +1 -1
  36. package/dist/webdist/assets/circle-x-DsCSoQSk.js +1 -0
  37. package/dist/webdist/assets/classDiagram-4FO5ZUOK-BwNVmSJo.js +1 -0
  38. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-BwNVmSJo.js +1 -0
  39. package/dist/webdist/assets/collapsible-Bqsm-UEI.js +1 -0
  40. package/dist/webdist/assets/common-DHHaSVnF.js +1 -0
  41. package/dist/webdist/assets/{cose-bilkent-S5V4N54A-DIUqY7o2.js → cose-bilkent-S5V4N54A-r9UktTKw.js} +1 -1
  42. package/dist/webdist/assets/{dagre-BM42HDAG-BzTcsgZ1.js → dagre-BM42HDAG-BnhFpJ0u.js} +1 -1
  43. package/dist/webdist/assets/{diagram-2AECGRRQ-DanHH-W_.js → diagram-2AECGRRQ-C8R_FKFE.js} +1 -1
  44. package/dist/webdist/assets/{diagram-5GNKFQAL-DQ5sWHUL.js → diagram-5GNKFQAL-Cy3ObA7-.js} +1 -1
  45. package/dist/webdist/assets/{diagram-KO2AKTUF-Jqr_pLwu.js → diagram-KO2AKTUF-BKMSyIfH.js} +1 -1
  46. package/dist/webdist/assets/{diagram-LMA3HP47-BZ89-cp0.js → diagram-LMA3HP47-M3eYUN5j.js} +1 -1
  47. package/dist/webdist/assets/{diagram-OG6HWLK6-cidBc5qa.js → diagram-OG6HWLK6-9mfuWNo6.js} +1 -1
  48. package/dist/webdist/assets/dialog-DTDvWQ5-.js +1 -0
  49. package/dist/webdist/assets/{erDiagram-TEJ5UH35-DEAKZN7b.js → erDiagram-TEJ5UH35-3pYBvVYw.js} +1 -1
  50. package/dist/webdist/assets/{flowDiagram-I6XJVG4X-Cyx8gMzN.js → flowDiagram-I6XJVG4X-DkeUYo0c.js} +1 -1
  51. package/dist/webdist/assets/folder-input-CjdiPWWo.js +1 -0
  52. package/dist/webdist/assets/{ganttDiagram-6RSMTGT7-CqXYW44d.js → ganttDiagram-6RSMTGT7-Co8G6rQq.js} +1 -1
  53. package/dist/webdist/assets/{gitGraphDiagram-PVQCEYII-0dv_zKYA.js → gitGraphDiagram-PVQCEYII-BeKTVG6x.js} +1 -1
  54. package/dist/webdist/assets/{index-DHmOrvk5.js → index-BiJl342p.js} +17 -17
  55. package/dist/webdist/assets/index-CxonSCte.css +1 -0
  56. package/dist/webdist/assets/index-MBZ7hSNo.js +10 -0
  57. package/dist/webdist/assets/{infoDiagram-5YYISTIA-PxeM6E3u.js → infoDiagram-5YYISTIA-CSNZJB_2.js} +1 -1
  58. package/dist/webdist/assets/{ishikawaDiagram-YF4QCWOH-BnNdTjj1.js → ishikawaDiagram-YF4QCWOH-Bl5G3Tv8.js} +1 -1
  59. package/dist/webdist/assets/{journeyDiagram-JHISSGLW-D3AHUGxz.js → journeyDiagram-JHISSGLW-8vGboHEi.js} +1 -1
  60. package/dist/webdist/assets/{kanban-definition-UN3LZRKU-YARl6SUt.js → kanban-definition-UN3LZRKU-Exhgn-QL.js} +1 -1
  61. package/dist/webdist/assets/{linear-CMEhaWGh.js → linear-BQ7-Rkhb.js} +1 -1
  62. package/dist/webdist/assets/{mermaid.core-Dfl2pPvv.js → mermaid.core-CQX56k_o.js} +4 -4
  63. package/dist/webdist/assets/{mindmap-definition-RKZ34NQL-BojWC6jk.js → mindmap-definition-RKZ34NQL-Bo7PZvAB.js} +1 -1
  64. package/dist/webdist/assets/pencil-D52zQ528.js +1 -0
  65. package/dist/webdist/assets/personas-BvApKJbI.js +305 -0
  66. package/dist/webdist/assets/{pieDiagram-4H26LBE5-cjsdMtJJ.js → pieDiagram-4H26LBE5-q8hQguI2.js} +1 -1
  67. package/dist/webdist/assets/{quadrantDiagram-W4KKPZXB-DYVcgQuL.js → quadrantDiagram-W4KKPZXB-D7oJYa2u.js} +1 -1
  68. package/dist/webdist/assets/{radix-BY-fjzwm.js → radix-DHuxYtIT.js} +2 -2
  69. package/dist/webdist/assets/refresh-cw-CCa9KASO.js +1 -0
  70. package/dist/webdist/assets/{requirementDiagram-4Y6WPE33-Dkj58nhB.js → requirementDiagram-4Y6WPE33-BB3AxfxX.js} +1 -1
  71. package/dist/webdist/assets/rotate-ccw-CjT25zbE.js +1 -0
  72. package/dist/webdist/assets/{sankeyDiagram-5OEKKPKP-l3GwUuNl.js → sankeyDiagram-5OEKKPKP-rBs95ARp.js} +1 -1
  73. package/dist/webdist/assets/{sequenceDiagram-3UESZ5HK-CWj1Dplo.js → sequenceDiagram-3UESZ5HK-Dl8lIjVG.js} +1 -1
  74. package/dist/webdist/assets/star-CGoRw01k.js +1 -0
  75. package/dist/webdist/assets/{stateDiagram-AJRCARHV-zbnPRcoE.js → stateDiagram-AJRCARHV-CLoQmP-w.js} +1 -1
  76. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-ByjTcwsY.js +1 -0
  77. package/dist/webdist/assets/{timeline-definition-PNZ67QCA-C_zAo1WZ.js → timeline-definition-PNZ67QCA-xQjF3Uja.js} +1 -1
  78. package/dist/webdist/assets/{trash-2-DJmVqx0w.js → trash-2-DtSyrOcz.js} +1 -1
  79. package/dist/webdist/assets/useIsDesktop-BFaxGM2l.js +1 -0
  80. package/dist/webdist/assets/{vendor-W5GIuFcv.js → vendor-BE0HADM8.js} +1 -1
  81. package/dist/webdist/assets/{vennDiagram-CIIHVFJN-DxfcocQu.js → vennDiagram-CIIHVFJN-CAcPzo7k.js} +1 -1
  82. package/dist/webdist/assets/{wardley-L42UT6IY-BnZ8BinJ.js → wardley-L42UT6IY-BPakyqHF.js} +1 -1
  83. package/dist/webdist/assets/{wardleyDiagram-YWT4CUSO-DMk9z7hr.js → wardleyDiagram-YWT4CUSO-DFeQJlAo.js} +1 -1
  84. package/dist/webdist/assets/wrench-BhEqkhsG.js +1 -0
  85. package/dist/webdist/assets/{xychartDiagram-2RQKCTM6-BUtTPXPF.js → xychartDiagram-2RQKCTM6-CmW19GSM.js} +1 -1
  86. package/dist/webdist/index.html +4 -4
  87. package/package.json +1 -1
  88. package/dist/webdist/assets/AgentsScreen-sMeMB3IQ.js +0 -2
  89. package/dist/webdist/assets/ChatScreen-rXB7PrWJ.js +0 -65
  90. package/dist/webdist/assets/CustomizeScreen-BBwvtk28.js +0 -4
  91. package/dist/webdist/assets/DeveloperScreen-fXV-qXB5.js +0 -1
  92. package/dist/webdist/assets/EnginesScreen-CrmYzhio.js +0 -7
  93. package/dist/webdist/assets/ModelDetailDialog-Dlt5uF3h.js +0 -1
  94. package/dist/webdist/assets/ModelDirs-BYN-DmHL.js +0 -1
  95. package/dist/webdist/assets/ModelsScreen-DncNvsj6.js +0 -2
  96. package/dist/webdist/assets/SettingsScreen-DXX3r4Ep.js +0 -1
  97. package/dist/webdist/assets/channel-Bt5-ol5R.js +0 -1
  98. package/dist/webdist/assets/check-jMeH6YsH.js +0 -1
  99. package/dist/webdist/assets/chevron-down-C5ZMFCtb.js +0 -1
  100. package/dist/webdist/assets/chevron-right-DNVnXbZ1.js +0 -1
  101. package/dist/webdist/assets/circle-check-qnghY_qF.js +0 -1
  102. package/dist/webdist/assets/circle-x-FM28MwOL.js +0 -1
  103. package/dist/webdist/assets/classDiagram-4FO5ZUOK-jfnUpxOr.js +0 -1
  104. package/dist/webdist/assets/classDiagram-v2-Q7XG4LA2-jfnUpxOr.js +0 -1
  105. package/dist/webdist/assets/collapsible-CwqYFc9v.js +0 -1
  106. package/dist/webdist/assets/common-BD4Wa3KL.js +0 -1
  107. package/dist/webdist/assets/copy-button-DypRNkqI.js +0 -1
  108. package/dist/webdist/assets/dialog-3hyZ9j4e.js +0 -1
  109. package/dist/webdist/assets/index-B_WtFoLC.css +0 -1
  110. package/dist/webdist/assets/index-CriwC5VH.js +0 -10
  111. package/dist/webdist/assets/pencil-CS33zp9q.js +0 -1
  112. package/dist/webdist/assets/personas-CzcXp8zg.js +0 -75
  113. package/dist/webdist/assets/plus-BO3vGy6f.js +0 -1
  114. package/dist/webdist/assets/refresh-cw-Cco9RapO.js +0 -1
  115. package/dist/webdist/assets/save-CSISNMk8.js +0 -1
  116. package/dist/webdist/assets/skeleton-CBRf-twG.js +0 -1
  117. package/dist/webdist/assets/stateDiagram-v2-BHNVJYJU-Ukx8AVKZ.js +0 -1
  118. package/dist/webdist/assets/vram-Dn53zt7R.js +0 -1
  119. package/dist/webdist/assets/wrench-Di5WLTz2.js +0 -1
package/README.md CHANGED
@@ -177,8 +177,8 @@ the detail:
177
177
  - **Saved per-model profiles, per engine** — tune once per (model, engine) pair, so
178
178
  switching engines (or between two installs of the same engine, e.g. a fork) never
179
179
  overwrites another engine's tuning for the same model.
180
- - **Configurable VRAM headroom** (Settings → Engine, 300 MB–2 GB, default 1 GB) — tell auto-tune
181
- how much VRAM to keep free for other GPU workloads instead of a fixed margin.
180
+ - **Configurable VRAM headroom** (Settings → Models & loading → Advanced, 300 MB–2 GB, default
181
+ 1 GB) — tell auto-tune how much VRAM to keep free for other GPU workloads instead of a fixed margin.
182
182
 
183
183
  </details>
184
184
 
@@ -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.
@@ -214,22 +216,22 @@ the detail:
214
216
  in every chat with no restart. A **Research** persona forces multi-step web search and cites sources inline.
215
217
  - **Tool-call approval gate** — every tool call asks for your approval by default before it runs,
216
218
  with **Deny**, **Allow**, **Allow for this chat**, or **Always Allow** on an inline bar above the
217
- composer. Set per-tool defaults globally from DeveloperTool permissions.
219
+ composer. Set per-tool defaults globally from SettingsTools & safety.
218
220
 
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
 
@@ -253,7 +255,7 @@ curl http://127.0.0.1:6996/v1/chat/completions \
253
255
  - **Anthropic-compatible** `/v1/messages` — including **tool use and streaming** — which powers
254
256
  Claude Code below. No other local host offers this.
255
257
  - **Structured output** — constrain any response to a **GBNF grammar** (or JSON shape).
256
- - **API-key auth** you can require when sharing over a LAN (Settings → Network).
258
+ - **API-key auth** you can require when sharing over a LAN (Settings → Network & sharing).
257
259
 
258
260
  **The gateway loads models for you.** Most local hosts make you load a model first, then call it.
259
261
  TurboLLM's gateway reads the `model` field of any incoming request, **fuzzy-matches it to your
@@ -265,7 +267,7 @@ coding model, a vision model, and an embedder just names each one and it works
265
267
  `opencode`, `kilo`, `openclaw`, and `hermes` — each gets pointed at TurboLLM the way that tool
266
268
  expects (config file merge, or its own CLI command) instead of a manual copy-paste setup. Inside
267
269
  Claude Code, `/model` lists your local models directly (with **Auto Model Swap** on in Settings →
268
- Gateway) so you can switch mid-session instead of only at launch.
270
+ Models & loading) so you can switch mid-session instead of only at launch.
269
271
 
270
272
  </details>
271
273
 
@@ -283,7 +285,7 @@ fight for memory (and one usually OOMs). TurboLLM can hand the GPU over automati
283
285
  It's **push-based, not polling** — ComfyUI signals TurboLLM the moment a job starts/ends, so the
284
286
  handoff is immediate and deterministic (the model is gone *before* ComfyUI executes).
285
287
 
286
- **One-time setup** (Settings → ComfyUI): turn on **Pause for ComfyUI**, enter your ComfyUI folder
288
+ **One-time setup** (Settings → Network & sharing → ComfyUI): turn on **Pause for ComfyUI**, enter your ComfyUI folder
287
289
  (the one containing `custom_nodes`), click **Install gate** (it writes a small custom node wired to
288
290
  this daemon), then **restart ComfyUI** once. The panel shows a live indicator (rendering / idle /
289
291
  connected); **Remove** undoes it.
@@ -331,7 +333,7 @@ daemon any time with **Ctrl+C**.
331
333
  No other local-LLM app lets you run **whatever inference engine you want**. TurboLLM treats
332
334
  the engine as a swappable component.
333
335
 
334
- **Add a custom engine** (Engines screen → **Add engine**):
336
+ **Add a custom engine** (Engines screen → **Add your own engine**):
335
337
 
336
338
  1. Compile or download any `llama-server`-compatible binary — stock
337
339
  [llama.cpp](https://github.com/ggml-org/llama.cpp), a community fork, or your own build.
@@ -396,13 +398,14 @@ any OpenAI- or Anthropic-compatible tool (Open WebUI, Kilo Code, opencode, …).
396
398
  ## Use it from any device on your network
397
399
 
398
400
  The UI runs in the browser, so any phone, tablet, or laptop on your LAN can use the model on
399
- your GPU box:
401
+ your GPU box — every screen is responsive down to phone widths, with the desktop layout
402
+ untouched:
400
403
 
401
404
  ```bash
402
405
  turbollm --addr 0.0.0.0:6996 # bind all interfaces, then open http://<your-ip>:6996
403
406
  ```
404
407
 
405
- Turn on **Require API key** in Settings → Network when you expose it.
408
+ Turn on **Require API key** in Settings → Network & sharing when you expose it.
406
409
 
407
410
  ---
408
411
 
@@ -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
+ };