opencami 1.9.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/client/assets/{CSPContext-B3PAVjBL.js → CSPContext-CxrBvJ99.js} +1 -1
  2. package/dist/client/assets/{DirectionContext-CR5CCisG.js → DirectionContext-BOL0P_1j.js} +1 -1
  3. package/dist/client/assets/_sessionKey-CTtu2ipR.js +25 -0
  4. package/dist/client/assets/agents-D7JS19Lo.js +2 -0
  5. package/dist/client/assets/{agents-screen--ZMzN5cB.js → agents-screen-CqdzZKaR.js} +1 -1
  6. package/dist/client/assets/bots-DAInzfLx.js +2 -0
  7. package/dist/client/assets/{bots-screen-C_uJBNwI.js → bots-screen-RYovD6cu.js} +1 -1
  8. package/dist/client/assets/button-Bv-7bwZZ.js +1 -0
  9. package/dist/client/assets/{composite-DArWbFHm.js → composite-DwQIZFe9.js} +1 -1
  10. package/dist/client/assets/{connect-B48HecjN.js → connect-DZSrhhNT.js} +1 -1
  11. package/dist/client/assets/{dashboard-BVKx9FMy.js → dashboard-y8KVUVF-.js} +1 -1
  12. package/dist/client/assets/{event-BCwqPPkP.js → event-Cnr9OETn.js} +1 -1
  13. package/dist/client/assets/file-explorer-screen-C_mYUJsr.js +1 -0
  14. package/dist/client/assets/files-CG0jFYdO.js +2 -0
  15. package/dist/client/assets/follow-up-suggestions-C6RzpstA.js +2 -0
  16. package/dist/client/assets/{index-0gdwm1_H.js → index-3-ASY4Cl.js} +1 -1
  17. package/dist/client/assets/index-BZxJSF4T.js +3 -0
  18. package/dist/client/assets/{keyboard-shortcuts-dialog-CK_XTzLr.js → keyboard-shortcuts-dialog-jm4-DftO.js} +1 -1
  19. package/dist/client/assets/{main-B2CrcRuC.js → main-CbISK-pm.js} +3 -3
  20. package/dist/client/assets/{markdown-CdkuX06F.js → markdown-UD7RzskQ.js} +1 -1
  21. package/dist/client/assets/memory-BYRJ1l_4.js +2 -0
  22. package/dist/client/assets/{memory-screen-C1RfLy-d.js → memory-screen-BRsm-Q8A.js} +1 -1
  23. package/dist/client/assets/{menu-CIwnliij.js → menu-DRjoHFvG.js} +1 -1
  24. package/dist/client/assets/{opencami-logo-SVuYD55V.js → opencami-logo-D2mv84sX.js} +1 -1
  25. package/dist/client/assets/{proxy-BijR8W1L.js → proxy-DtxqfBHY.js} +1 -1
  26. package/dist/client/assets/{react-DWx7OvUo.js → react-C_7UlEtd.js} +1 -1
  27. package/dist/client/assets/{search-dialog-CB4KE8ec.js → search-dialog-lsT8JUtT.js} +1 -1
  28. package/dist/client/assets/{search-sources-badge-B8Z-8sSf.js → search-sources-badge-BnqKxoia.js} +1 -1
  29. package/dist/client/assets/{session-export-dialog-tDiFuv3a.js → session-export-dialog-DHHpvwI8.js} +1 -1
  30. package/dist/client/assets/settings-dialog-vrd0eJGw.js +1 -0
  31. package/dist/client/assets/skills-Svd-7oxQ.js +2 -0
  32. package/dist/client/assets/{skills-panel-fjJQVMog.js → skills-panel-Dkuo0yyD.js} +1 -1
  33. package/dist/client/assets/styles-CWabEzNU.css +1 -0
  34. package/dist/client/assets/{switch-Bn5uei2k.js → switch-BYFBYPmi.js} +1 -1
  35. package/dist/client/assets/{tabs-DOBNAUVE.js → tabs-1jrPkdUE.js} +1 -1
  36. package/dist/client/assets/{thinking-dfGrFAMV.js → thinking-D9BBn5kK.js} +1 -1
  37. package/dist/client/assets/{tooltip-DOKkNFvu.js → tooltip-DzFgDY86.js} +1 -1
  38. package/dist/client/assets/{use-file-explorer-state-BAa6Cxyr.js → use-file-explorer-state-Dwhw6Mpg.js} +2 -2
  39. package/dist/client/assets/{useBaseUiId-DFpBD0sg.js → useBaseUiId-BdQC9KF3.js} +1 -1
  40. package/dist/client/assets/useCompositeItem-BI09zNFD.js +1 -0
  41. package/dist/client/assets/{useControlled-CQHE0ITz.js → useControlled-Bc2Emhlx.js} +1 -1
  42. package/dist/client/assets/{useMutation-BFl-7GnD.js → useMutation-CMp81lDO.js} +1 -1
  43. package/dist/client/assets/{useOnFirstRender-DlXHIIGk.js → useOnFirstRender-DpB3w0o4.js} +1 -1
  44. package/dist/client/assets/{useQuery-D-sF8Tld.js → useQuery-_BEWdUe1.js} +1 -1
  45. package/dist/server/assets/{_sessionKey-D8TGrDRM.js → _sessionKey-COz7RLKC.js} +170 -279
  46. package/dist/server/assets/_tanstack-start-manifest_v-D0f-0Utn.js +4 -0
  47. package/dist/server/assets/{connect-CTVBm0Vc.js → connect-sd_NDvQN.js} +1 -1
  48. package/dist/server/assets/follow-up-suggestions-DRQqO8_S.js +183 -0
  49. package/dist/server/assets/{index-B_F4DTUu.js → index-197lqzv2.js} +2 -0
  50. package/dist/server/assets/{index-gRco4Ina.js → index-sXe2QC5V.js} +1 -1
  51. package/dist/server/assets/{markdown-CFdYXCRQ.js → markdown-Dk1YdosA.js} +1 -1
  52. package/dist/server/assets/{memory-screen-vqXczcVo.js → memory-screen-DA_o-N1Z.js} +2 -2
  53. package/dist/server/assets/{memory-rBB015W-.js → memory-woPzJFfs.js} +1 -1
  54. package/dist/server/assets/{router-DaKDqc9w.js → router-CZhxPkYW.js} +347 -355
  55. package/dist/server/assets/{search-dialog-DSSK93kq.js → search-dialog-DlvWfq1e.js} +5 -5
  56. package/dist/server/assets/{settings-dialog-DyWNblva.js → settings-dialog-BGtrS2RJ.js} +63 -255
  57. package/dist/server/assets/{thinking-CU0FRlzT.js → thinking-fzwT_aVT.js} +5 -5
  58. package/dist/server/server.js +2 -2
  59. package/package.json +1 -1
  60. package/dist/client/assets/_sessionKey-Bg_9uype.js +0 -23
  61. package/dist/client/assets/agents-BiTHBb6Z.js +0 -2
  62. package/dist/client/assets/bots-DxhRnQp5.js +0 -2
  63. package/dist/client/assets/button-BciDmec0.js +0 -1
  64. package/dist/client/assets/file-explorer-screen-DPs-FWeA.js +0 -1
  65. package/dist/client/assets/files-SEycwYCa.js +0 -2
  66. package/dist/client/assets/follow-up-suggestions-BPjWBpiy.js +0 -5
  67. package/dist/client/assets/index-CGeJcqZ3.js +0 -3
  68. package/dist/client/assets/memory-C7lKdkmc.js +0 -2
  69. package/dist/client/assets/settings-dialog-aL-AH4Rt.js +0 -1
  70. package/dist/client/assets/skills-D1T6uemU.js +0 -2
  71. package/dist/client/assets/styles-D0L88B64.css +0 -1
  72. package/dist/client/assets/useCompositeItem-B-Axq9-D.js +0 -1
  73. package/dist/server/assets/_tanstack-start-manifest_v-DalBo2bY.js +0 -4
  74. package/dist/server/assets/follow-up-suggestions-C65ptDij.js +0 -336
@@ -12,7 +12,7 @@ import { execFile, execSync } from "node:child_process";
12
12
  import { promisify } from "node:util";
13
13
  import { readFile, mkdir, writeFile, rename, stat, readdir, rm, realpath, lstat } from "node:fs/promises";
14
14
  import { posix } from "path";
15
- const appCss = "/assets/styles-D0L88B64.css";
15
+ const appCss = "/assets/styles-CWabEzNU.css";
16
16
  const swRegisterScript = `
17
17
  (() => {
18
18
  // Skip PWA service worker inside Capacitor native shell — they conflict
@@ -223,7 +223,7 @@ function NotFoundRedirect() {
223
223
  }
224
224
  return null;
225
225
  }
226
- const Route$B = createRootRoute({
226
+ const Route$C = createRootRoute({
227
227
  notFoundComponent: NotFoundRedirect,
228
228
  head: () => ({
229
229
  meta: [
@@ -332,11 +332,11 @@ function RootDocument({ children }) {
332
332
  ] });
333
333
  }
334
334
  const $$splitComponentImporter$9 = () => import("./skills-BXUivxuo.js");
335
- const Route$A = createFileRoute("/skills")({
335
+ const Route$B = createFileRoute("/skills")({
336
336
  component: lazyRouteComponent($$splitComponentImporter$9, "component")
337
337
  });
338
338
  const $$splitComponentImporter$8 = () => import("./new-Dzk5YxE9.js");
339
- const Route$z = createFileRoute("/new")({
339
+ const Route$A = createFileRoute("/new")({
340
340
  beforeLoad: function redirectToNewChat() {
341
341
  throw redirect({
342
342
  to: "/chat/$sessionKey",
@@ -348,36 +348,36 @@ const Route$z = createFileRoute("/new")({
348
348
  },
349
349
  component: lazyRouteComponent($$splitComponentImporter$8, "component")
350
350
  });
351
- const $$splitComponentImporter$7 = () => import("./memory-rBB015W-.js");
352
- const Route$y = createFileRoute("/memory")({
351
+ const $$splitComponentImporter$7 = () => import("./memory-woPzJFfs.js");
352
+ const Route$z = createFileRoute("/memory")({
353
353
  component: lazyRouteComponent($$splitComponentImporter$7, "component")
354
354
  });
355
355
  const $$splitComponentImporter$6 = () => import("./files-DLxqp-h5.js");
356
- const Route$x = createFileRoute("/files")({
356
+ const Route$y = createFileRoute("/files")({
357
357
  component: lazyRouteComponent($$splitComponentImporter$6, "component")
358
358
  });
359
359
  const $$splitComponentImporter$5 = () => import("./dashboard-UYRCu_mQ.js");
360
- const Route$w = createFileRoute("/dashboard")({
360
+ const Route$x = createFileRoute("/dashboard")({
361
361
  component: lazyRouteComponent($$splitComponentImporter$5, "component")
362
362
  });
363
- const $$splitComponentImporter$4 = () => import("./connect-CTVBm0Vc.js");
364
- const Route$v = createFileRoute("/connect")({
363
+ const $$splitComponentImporter$4 = () => import("./connect-sd_NDvQN.js");
364
+ const Route$w = createFileRoute("/connect")({
365
365
  component: lazyRouteComponent($$splitComponentImporter$4, "component")
366
366
  });
367
367
  const $$splitComponentImporter$3 = () => import("./bots-BDHeSvSQ.js");
368
- const Route$u = createFileRoute("/bots")({
368
+ const Route$v = createFileRoute("/bots")({
369
369
  component: lazyRouteComponent($$splitComponentImporter$3, "component")
370
370
  });
371
371
  const $$splitComponentImporter$2 = () => import("./agents-BuE0Yum3.js");
372
- const Route$t = createFileRoute("/agents")({
372
+ const Route$u = createFileRoute("/agents")({
373
373
  component: lazyRouteComponent($$splitComponentImporter$2, "component")
374
374
  });
375
- const $$splitComponentImporter$1 = () => import("./index-gRco4Ina.js");
376
- const Route$s = createFileRoute("/")({
375
+ const $$splitComponentImporter$1 = () => import("./index-sXe2QC5V.js");
376
+ const Route$t = createFileRoute("/")({
377
377
  component: lazyRouteComponent($$splitComponentImporter$1, "component")
378
378
  });
379
- const $$splitComponentImporter = () => import("./_sessionKey-D8TGrDRM.js").then((n) => n.$);
380
- const Route$r = createFileRoute("/chat/$sessionKey")({
379
+ const $$splitComponentImporter = () => import("./_sessionKey-COz7RLKC.js").then((n) => n.$);
380
+ const Route$s = createFileRoute("/chat/$sessionKey")({
381
381
  component: lazyRouteComponent($$splitComponentImporter, "component")
382
382
  });
383
383
  function getGatewayConfig() {
@@ -1064,7 +1064,7 @@ async function ttsEdge(text, voice) {
1064
1064
  }
1065
1065
  });
1066
1066
  }
1067
- const Route$q = createFileRoute("/api/tts")({
1067
+ const Route$r = createFileRoute("/api/tts")({
1068
1068
  server: {
1069
1069
  handlers: {
1070
1070
  POST: async ({ request }) => {
@@ -1219,7 +1219,7 @@ async function sttOpenAI(audioBlob, apiKey, language) {
1219
1219
  const data = await res.json();
1220
1220
  return { text: data.text || "" };
1221
1221
  }
1222
- const Route$p = createFileRoute("/api/stt")({
1222
+ const Route$q = createFileRoute("/api/stt")({
1223
1223
  server: {
1224
1224
  handlers: {
1225
1225
  POST: async ({ request }) => {
@@ -1321,7 +1321,7 @@ const Route$p = createFileRoute("/api/stt")({
1321
1321
  }
1322
1322
  }
1323
1323
  });
1324
- const Route$o = createFileRoute("/api/stream")({
1324
+ const Route$p = createFileRoute("/api/stream")({
1325
1325
  server: {
1326
1326
  handlers: {
1327
1327
  GET: async ({ request }) => {
@@ -1504,7 +1504,7 @@ function parseSearchResults(output) {
1504
1504
  return { slug: line.trim(), displayName: line.trim(), version: "", summary: "" };
1505
1505
  });
1506
1506
  }
1507
- const Route$n = createFileRoute("/api/skills")({
1507
+ const Route$o = createFileRoute("/api/skills")({
1508
1508
  server: {
1509
1509
  handlers: {
1510
1510
  GET: async ({ request }) => {
@@ -1618,7 +1618,7 @@ function normalizeSessions(payload) {
1618
1618
  });
1619
1619
  return { sessions: normalized };
1620
1620
  }
1621
- const Route$m = createFileRoute("/api/sessions")({
1621
+ const Route$n = createFileRoute("/api/sessions")({
1622
1622
  server: {
1623
1623
  handlers: {
1624
1624
  GET: async () => {
@@ -1771,7 +1771,7 @@ const Route$m = createFileRoute("/api/sessions")({
1771
1771
  }
1772
1772
  }
1773
1773
  });
1774
- const Route$l = createFileRoute("/api/send")({
1774
+ const Route$m = createFileRoute("/api/send")({
1775
1775
  server: {
1776
1776
  handlers: {
1777
1777
  POST: async ({ request }) => {
@@ -1837,7 +1837,7 @@ const Route$l = createFileRoute("/api/send")({
1837
1837
  }
1838
1838
  }
1839
1839
  });
1840
- const Route$k = createFileRoute("/api/ping")({
1840
+ const Route$l = createFileRoute("/api/ping")({
1841
1841
  server: {
1842
1842
  handlers: {
1843
1843
  GET: async () => {
@@ -1887,7 +1887,7 @@ const categories = { "core": [{ "id": "cami", "name": "Cami", "emoji": "🦎", "
1887
1887
  const personasData = {
1888
1888
  categories
1889
1889
  };
1890
- const Route$j = createFileRoute("/api/personas")({
1890
+ const Route$k = createFileRoute("/api/personas")({
1891
1891
  server: {
1892
1892
  handlers: {
1893
1893
  GET: async () => {
@@ -1932,7 +1932,7 @@ function resolveSessionsDir() {
1932
1932
  )
1933
1933
  };
1934
1934
  }
1935
- const Route$i = createFileRoute("/api/paths")({
1935
+ const Route$j = createFileRoute("/api/paths")({
1936
1936
  server: {
1937
1937
  handlers: {
1938
1938
  GET: () => {
@@ -1957,7 +1957,7 @@ function parseModelName(modelId) {
1957
1957
  return word.charAt(0).toUpperCase() + word.slice(1);
1958
1958
  }).join(" ");
1959
1959
  }
1960
- const Route$h = createFileRoute("/api/models")({
1960
+ const Route$i = createFileRoute("/api/models")({
1961
1961
  server: {
1962
1962
  handlers: {
1963
1963
  GET: async () => {
@@ -2017,180 +2017,259 @@ const Route$h = createFileRoute("/api/models")({
2017
2017
  }
2018
2018
  }
2019
2019
  });
2020
- const DEFAULT_BASE_URL = "https://api.openai.com/v1";
2021
- const DEFAULT_TIMEOUT_MS = 1e4;
2022
- const MODEL_FALLBACK_CHAIN = [
2023
- "gpt-4.1-nano",
2024
- "gpt-4o-mini",
2025
- "gpt-3.5-turbo"
2026
- ];
2020
+ const ALLOWED_AGENT_IDS = /* @__PURE__ */ new Set([
2021
+ "main",
2022
+ "codex",
2023
+ "gpt54",
2024
+ "gpt54mini",
2025
+ "kimi",
2026
+ "grok",
2027
+ "gemini",
2028
+ "gh-sonnet",
2029
+ "gh-haiku",
2030
+ "oc-mimo-pro",
2031
+ "oc-mimo-omni",
2032
+ "hermes"
2033
+ ]);
2034
+ const AGENT_DISPLAY_NAMES = {
2035
+ main: "Main (MiMo)",
2036
+ codex: "Codex (GPT-5.4)",
2037
+ gpt54: "GPT-5.4",
2038
+ gpt54mini: "GPT-5.4 Mini",
2039
+ kimi: "Kimi K2.5",
2040
+ grok: "Grok 4.20",
2041
+ gemini: "Gemini 3.1",
2042
+ "gh-sonnet": "Claude Sonnet (GitHub)",
2043
+ "gh-haiku": "Claude Haiku (GitHub)",
2044
+ "oc-mimo-pro": "MiMo Pro",
2045
+ "oc-mimo-omni": "MiMo Omni",
2046
+ hermes: "Hermes"
2047
+ };
2048
+ const GATEWAY_MODELS_URL = (process.env.CLAWDBOT_GATEWAY_URL?.replace(/^ws/, "http") ?? "http://127.0.0.1:18789") + "/v1/models";
2049
+ function getGatewayToken$1() {
2050
+ return process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() || process.env.OPENCLAW_GATEWAY_TOKEN?.trim() || null;
2051
+ }
2052
+ const Route$h = createFileRoute("/api/llm-models")({
2053
+ server: {
2054
+ handlers: {
2055
+ GET: async () => {
2056
+ try {
2057
+ const token = getGatewayToken$1();
2058
+ const response = await fetch(GATEWAY_MODELS_URL, {
2059
+ headers: {
2060
+ ...token ? { Authorization: `Bearer ${token}` } : {}
2061
+ }
2062
+ });
2063
+ if (!response.ok) {
2064
+ const errorText = await response.text().catch(() => "");
2065
+ throw new Error(
2066
+ `Gateway models request failed: ${response.status}${errorText ? ` ${errorText}` : ""}`
2067
+ );
2068
+ }
2069
+ const data = await response.json();
2070
+ const seenAgentIds = /* @__PURE__ */ new Set();
2071
+ const models = (data.data ?? []).map((model) => model.id?.trim()).filter((id) => Boolean(id?.startsWith("openclaw/"))).map((id) => {
2072
+ const agentId = id.slice("openclaw/".length);
2073
+ return { id, agentId };
2074
+ }).filter(({ agentId }) => ALLOWED_AGENT_IDS.has(agentId)).filter(({ agentId }) => {
2075
+ if (seenAgentIds.has(agentId)) return false;
2076
+ seenAgentIds.add(agentId);
2077
+ return true;
2078
+ }).map(({ id, agentId }) => ({
2079
+ id,
2080
+ agentId,
2081
+ displayName: AGENT_DISPLAY_NAMES[agentId] ?? agentId
2082
+ }));
2083
+ return json({
2084
+ ok: true,
2085
+ models,
2086
+ defaultModel: "gpt54mini"
2087
+ });
2088
+ } catch (err) {
2089
+ console.error("[llm-models] Error fetching models:", err);
2090
+ return json(
2091
+ {
2092
+ ok: false,
2093
+ models: [
2094
+ {
2095
+ id: "openclaw/gpt54mini",
2096
+ agentId: "gpt54mini",
2097
+ displayName: AGENT_DISPLAY_NAMES.gpt54mini
2098
+ }
2099
+ ],
2100
+ defaultModel: "gpt54mini",
2101
+ error: err instanceof Error ? err.message : String(err)
2102
+ },
2103
+ { status: 500 }
2104
+ );
2105
+ }
2106
+ }
2107
+ }
2108
+ }
2109
+ });
2110
+ const OPENCLAW_GATEWAY_URL = (process.env.CLAWDBOT_GATEWAY_URL?.replace(/^ws/, "http") ?? "http://127.0.0.1:18789") + "/v1/chat/completions";
2111
+ const OPENCLAW_MODEL_OVERRIDE = process.env.OPENCAMI_LLM_FEATURES_MODEL?.trim() || process.env.OPENCLAW_LLM_FEATURES_MODEL?.trim() || "";
2112
+ const OPENCLAW_MODEL_FALLBACKS = OPENCLAW_MODEL_OVERRIDE ? [OPENCLAW_MODEL_OVERRIDE] : ["openclaw/gpt54mini", "openclaw"];
2113
+ const DEFAULT_TIMEOUT_MS = 3e4;
2114
+ function getGatewayToken() {
2115
+ return process.env.OPENCLAW_GATEWAY_TOKEN?.trim() || process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() || null;
2116
+ }
2027
2117
  async function chatCompletion(messages, options) {
2028
- const {
2029
- apiKey,
2030
- baseUrl = DEFAULT_BASE_URL,
2031
- model,
2032
- timeoutMs = DEFAULT_TIMEOUT_MS,
2033
- maxTokens = 100
2034
- } = options;
2035
- const modelsToTry = model ? [model] : MODEL_FALLBACK_CHAIN;
2118
+ const token = getGatewayToken();
2036
2119
  let lastError = null;
2037
- for (const currentModel of modelsToTry) {
2120
+ const models = options?.model ? [options.model] : OPENCLAW_MODEL_FALLBACKS;
2121
+ for (const model of models) {
2038
2122
  const controller = new AbortController();
2039
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
2123
+ const timeoutId = setTimeout(
2124
+ () => controller.abort(),
2125
+ options?.timeoutMs ?? DEFAULT_TIMEOUT_MS
2126
+ );
2040
2127
  try {
2041
- const response = await fetch(`${baseUrl}/chat/completions`, {
2128
+ const response = await fetch(OPENCLAW_GATEWAY_URL, {
2042
2129
  method: "POST",
2043
2130
  headers: {
2044
2131
  "Content-Type": "application/json",
2045
- "Authorization": `Bearer ${apiKey}`
2132
+ ...token ? { Authorization: `Bearer ${token}` } : {}
2046
2133
  },
2047
2134
  body: JSON.stringify({
2048
- model: currentModel,
2135
+ model,
2049
2136
  messages,
2050
- max_tokens: maxTokens,
2051
- temperature: 0.7
2137
+ max_tokens: options?.maxTokens ?? 200,
2138
+ temperature: options?.temperature ?? 0.7
2052
2139
  }),
2053
2140
  signal: controller.signal
2054
2141
  });
2055
- clearTimeout(timeoutId);
2056
2142
  if (!response.ok) {
2057
- const errorText = await response.text();
2058
- if (response.status === 404 || errorText.includes("model_not_found")) {
2059
- console.log(`[llm-client] Model ${currentModel} not available, trying next...`);
2060
- lastError = new Error(`Model ${currentModel} not found`);
2061
- continue;
2062
- }
2063
- throw new Error(`OpenAI API error: ${response.status} ${errorText}`);
2143
+ const errorText = await response.text().catch(() => "");
2144
+ lastError = new Error(
2145
+ `OpenClaw Gateway error (${model}): ${response.status}${errorText ? ` ${errorText}` : ""}`
2146
+ );
2147
+ continue;
2064
2148
  }
2065
2149
  const data = await response.json();
2066
- const content = data.choices[0]?.message?.content?.trim() || "";
2067
- if (content) {
2068
- return content;
2150
+ const content = data.choices?.[0]?.message?.content?.trim() || "";
2151
+ if (!content) {
2152
+ lastError = new Error(`OpenClaw Gateway returned empty content for ${model}`);
2153
+ continue;
2069
2154
  }
2070
- lastError = new Error(`Model ${currentModel} returned empty response`);
2071
- continue;
2155
+ return content;
2072
2156
  } catch (error) {
2073
- clearTimeout(timeoutId);
2074
2157
  if (error instanceof Error && error.name === "AbortError") {
2075
- lastError = new Error("OpenAI API request timed out");
2158
+ lastError = new Error(`OpenClaw Gateway request timed out for ${model}`);
2076
2159
  continue;
2077
2160
  }
2078
2161
  lastError = error instanceof Error ? error : new Error(String(error));
2079
- continue;
2162
+ } finally {
2163
+ clearTimeout(timeoutId);
2080
2164
  }
2081
2165
  }
2082
- throw lastError || new Error("All models failed");
2166
+ throw lastError || new Error("OpenClaw Gateway request failed for all models");
2167
+ }
2168
+ function dedupeFollowUps(lines) {
2169
+ const seen = /* @__PURE__ */ new Set();
2170
+ return lines.filter((line) => {
2171
+ const key = line.toLocaleLowerCase("de-DE");
2172
+ if (seen.has(key)) {
2173
+ return false;
2174
+ }
2175
+ seen.add(key);
2176
+ return true;
2177
+ });
2178
+ }
2179
+ function looksLikeQuestion(line) {
2180
+ if (line.endsWith("?")) return true;
2181
+ if (line.length < 8 || line.length > 90) return false;
2182
+ if (/[.!:]$/.test(line)) return false;
2183
+ if (/\b(?:ja|nein|ok|okay|danke|bitte)\b/i.test(line)) return false;
2184
+ return /^(?:wie|was|warum|wieso|weshalb|wo|wohin|woher|wann|welche(?:r|s|n)?|welcher|welches|welchen|wer|wen|wem|kann(?:st)?|könn(?:te|test)|soll(?:te|st)|möcht(?:est|et)|willst|gibt|brauche|zeigt|erklär|hilf|nenn|sag)\b/i.test(
2185
+ line
2186
+ );
2187
+ }
2188
+ function parseFollowUps(text) {
2189
+ const candidates = text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => line.replace(/^["']|["']$/g, "").trim()).map((line) => line.replace(/^\d+[.)\s]+/, "").trim()).map((line) => line.replace(/^[-•*]\s*/, "").trim()).map((line) => line.replace(/\s+/g, " ")).filter((line) => line.length > 0 && line.length < 150).filter((line) => !/[`*_#]/.test(line)).filter((line) => !/^#{1,6}\s/.test(line));
2190
+ const explicitQuestions = candidates.filter((line) => line.endsWith("?"));
2191
+ const inferredQuestions = candidates.filter((line) => looksLikeQuestion(line));
2192
+ const inlineQuestions = text.match(/[^\n?.!]*\?/g)?.map((line) => line.trim()) ?? [];
2193
+ return dedupeFollowUps([
2194
+ ...explicitQuestions,
2195
+ ...inferredQuestions,
2196
+ ...inlineQuestions
2197
+ ]).slice(0, 3);
2083
2198
  }
2084
- async function generateSessionTitle(message, options) {
2085
- const systemPrompt = `Generate a concise 3-6 word title for this conversation.
2199
+ async function generateTitleViaOpenclaw(message, model) {
2200
+ const systemPrompt = `You are a title generator. Output ONLY a short title, nothing else.
2086
2201
  Rules:
2087
- - No quotes or punctuation at the end
2088
- - Capture the main topic/intent
2089
- - Be specific, not generic
2090
- - Use title case`;
2202
+ - 3-6 words maximum
2203
+ - No quotes, no punctuation at the end
2204
+ - No explanation, no markdown
2205
+ - Just the title text
2206
+ - Use the same language as the input
2207
+
2208
+ Example: "Nginx Reverse Proxy Setup"`;
2091
2209
  return chatCompletion(
2092
2210
  [
2093
2211
  { role: "system", content: systemPrompt },
2094
2212
  { role: "user", content: message }
2095
2213
  ],
2096
- { ...options, maxTokens: 200 }
2214
+ { maxTokens: 60, temperature: 0.3, model }
2097
2215
  );
2098
2216
  }
2099
- async function generateFollowUps(conversationContext, options) {
2100
- const systemPrompt = `Based on this conversation, suggest 3 natural follow-up questions the user might ask.
2101
- Rules:
2102
- - Each question max 10 words
2103
- - Make them specific to the conversation context
2104
- - Vary the types: clarification, deeper dive, related topic
2105
- - Return ONLY a JSON array of 3 strings, nothing else`;
2217
+ async function generateFollowUpsViaOpenclaw(responseText, contextSummary, model) {
2218
+ const truncatedResponse = responseText.length > 1500 ? `${responseText.slice(0, 1500)}...` : responseText;
2219
+ const trimmedSummary = contextSummary?.slice(0, 500).trim() || "";
2220
+ const userPrompt = trimmedSummary ? `Context: ${trimmedSummary}
2221
+
2222
+ Assistant's response:
2223
+ ${truncatedResponse}` : `Assistant's response:
2224
+ ${truncatedResponse}`;
2225
+ const systemPrompt = `You generate follow-up suggestions for a chat UI.
2226
+
2227
+ Return exactly 3 short follow-up QUESTIONS in the SAME LANGUAGE as the conversation context.
2228
+
2229
+ Hard rules:
2230
+ - Output exactly 3 lines
2231
+ - Every line must be a natural question in the same language as the input
2232
+ - No markdown
2233
+ - No bullet points
2234
+ - No numbering
2235
+ - No asterisks
2236
+ - No explanations
2237
+ - No intro or outro text
2238
+ - No quotes
2239
+ - Keep each question concise and relevant
2240
+ - Prefer under 60 characters when possible
2241
+ - Vary them across clarification, deeper detail, and practical use
2242
+
2243
+ Example output for English input:
2244
+ How exactly does this work?
2245
+ When would you typically use this?
2246
+ What should I watch out for in practice?
2247
+
2248
+ Example output for German input:
2249
+ Wie funktioniert das genau?
2250
+ Wann wird das typischerweise verwendet?
2251
+ Worauf sollte ich in der Praxis achten?`;
2106
2252
  const response = await chatCompletion(
2107
2253
  [
2108
2254
  { role: "system", content: systemPrompt },
2109
- { role: "user", content: conversationContext }
2255
+ { role: "user", content: userPrompt }
2110
2256
  ],
2111
- { ...options, maxTokens: 500 }
2257
+ { maxTokens: 200, temperature: 0.2, model }
2112
2258
  );
2113
- try {
2114
- let jsonStr = response.trim();
2115
- if (jsonStr.startsWith("```")) {
2116
- jsonStr = jsonStr.replace(/^```(?:json)?\n?/, "").replace(/\n?```$/, "");
2117
- }
2118
- const parsed = JSON.parse(jsonStr);
2119
- if (Array.isArray(parsed) && parsed.length > 0) {
2120
- return parsed.slice(0, 3).map(String);
2121
- }
2122
- } catch {
2123
- }
2124
- return [];
2259
+ return parseFollowUps(response);
2125
2260
  }
2126
- async function testApiKey(options) {
2127
- const llmOptions = typeof options === "string" ? { apiKey: options } : options;
2261
+ async function isOpenclawAvailable() {
2128
2262
  try {
2129
- await chatCompletion(
2130
- [{ role: "user", content: "Hi" }],
2131
- { ...llmOptions, maxTokens: 1, timeoutMs: 5e3 }
2132
- );
2263
+ await chatCompletion([{ role: "user", content: "Hi" }], {
2264
+ maxTokens: 1,
2265
+ temperature: 0,
2266
+ timeoutMs: 1e4
2267
+ });
2133
2268
  return true;
2134
2269
  } catch {
2135
2270
  return false;
2136
2271
  }
2137
2272
  }
2138
- const DEFAULT_OPENAI_BASE_URL = "https://api.openai.com/v1";
2139
- const PRESET_BASE_URL_ORIGINS = /* @__PURE__ */ new Set([
2140
- "https://api.openai.com",
2141
- "https://openrouter.ai",
2142
- "https://api.kilo.ai",
2143
- "http://localhost:11434",
2144
- "http://127.0.0.1:11434"
2145
- ]);
2146
- function getOrigin(rawBaseUrl) {
2147
- try {
2148
- return new URL(rawBaseUrl).origin;
2149
- } catch {
2150
- return null;
2151
- }
2152
- }
2153
- function isAllowedClientBaseUrl(rawBaseUrl) {
2154
- const parsed = new URL(rawBaseUrl);
2155
- if (!["http:", "https:"].includes(parsed.protocol)) return false;
2156
- if (parsed.username || parsed.password) return false;
2157
- const hostname = parsed.hostname.toLowerCase();
2158
- const isLocalHost = hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
2159
- if (!isLocalHost && parsed.protocol !== "https:") return false;
2160
- const origin = parsed.origin;
2161
- if (PRESET_BASE_URL_ORIGINS.has(origin)) return true;
2162
- const envBaseUrl = process.env.LLM_BASE_URL?.trim();
2163
- const envOrigin = envBaseUrl ? getOrigin(envBaseUrl) : null;
2164
- return Boolean(envOrigin && envOrigin === origin);
2165
- }
2166
- function detectProvider(rawBaseUrl) {
2167
- const baseUrl = rawBaseUrl?.toLowerCase() || "";
2168
- if (baseUrl.includes("openrouter.ai")) return "openrouter";
2169
- if (baseUrl.includes("kilo.ai")) return "kilocode";
2170
- return "openai";
2171
- }
2172
- function getLlmConfig(request) {
2173
- const headerKey = request.headers.get("X-OpenAI-API-Key");
2174
- const headerBaseUrl = request.headers.get("X-LLM-Base-URL")?.trim() || null;
2175
- const envBaseUrl = process.env.LLM_BASE_URL?.trim() || null;
2176
- if (headerBaseUrl) {
2177
- const origin = getOrigin(headerBaseUrl);
2178
- if (!origin || !isAllowedClientBaseUrl(headerBaseUrl)) {
2179
- return {
2180
- apiKey: null,
2181
- baseUrl: null,
2182
- model: null,
2183
- error: "Disallowed X-LLM-Base-URL value"
2184
- };
2185
- }
2186
- }
2187
- const baseUrl = headerBaseUrl || envBaseUrl || DEFAULT_OPENAI_BASE_URL;
2188
- const provider = detectProvider(baseUrl);
2189
- const envKey = provider === "openrouter" ? process.env.OPENROUTER_API_KEY?.trim() || process.env.OPENAI_API_KEY?.trim() : provider === "kilocode" ? process.env.KILOCODE_API_KEY?.trim() || process.env.OPENAI_API_KEY?.trim() : process.env.OPENAI_API_KEY?.trim();
2190
- const apiKey = headerKey?.trim() || envKey || null;
2191
- const model = request.headers.get("X-LLM-Model")?.trim() || process.env.LLM_MODEL?.trim() || null;
2192
- return { apiKey, baseUrl, model, error: null };
2193
- }
2194
2273
  function generateHeuristicTitle(message) {
2195
2274
  let text = message.replace(/```[\s\S]*?```/g, " ");
2196
2275
  text = text.replace(/`[^`]+`/g, " ");
@@ -2214,88 +2293,56 @@ function generateHeuristicTitle(message) {
2214
2293
  const Route$g = createFileRoute("/api/llm-features")({
2215
2294
  server: {
2216
2295
  handlers: {
2217
- /**
2218
- * GET /api/llm-features - Check LLM features status
2219
- */
2220
2296
  GET: async () => {
2221
2297
  try {
2222
- const hasEnvKey = Boolean(process.env.OPENAI_API_KEY?.trim());
2223
- const hasOpenRouterKey = Boolean(process.env.OPENROUTER_API_KEY?.trim());
2224
- const hasKilocodeKey = Boolean(process.env.KILOCODE_API_KEY?.trim());
2298
+ const available = await isOpenclawAvailable();
2225
2299
  return json({
2226
2300
  ok: true,
2227
- hasEnvKey,
2228
- hasOpenRouterKey,
2229
- hasKilocodeKey
2301
+ available
2230
2302
  });
2231
2303
  } catch (err) {
2232
2304
  return json({
2233
2305
  ok: false,
2234
- hasEnvKey: false,
2306
+ available: false,
2235
2307
  error: err instanceof Error ? err.message : String(err)
2236
2308
  });
2237
2309
  }
2238
2310
  },
2239
- /**
2240
- * POST /api/llm-features - Handle LLM feature requests
2241
- *
2242
- * Request body should include an "action" field:
2243
- * - action: "title" - Generate session title
2244
- * - action: "followups" - Generate follow-up suggestions
2245
- * - action: "test" - Test API key validity
2246
- */
2247
2311
  POST: async ({ request }) => {
2248
2312
  try {
2249
2313
  const body = await request.json().catch(() => ({}));
2250
2314
  const action = body.action;
2251
2315
  switch (action) {
2252
2316
  case "title": {
2253
- const { message } = body;
2317
+ const { message, model } = body;
2254
2318
  if (!message || typeof message !== "string" || message.trim().length < 3) {
2255
- return json({
2256
- ok: false,
2257
- error: "Message is required and must be at least 3 characters"
2258
- });
2259
- }
2260
- const llmConfig = getLlmConfig(request);
2261
- if (llmConfig.error) {
2262
- return json({
2263
- ok: false,
2264
- error: llmConfig.error
2265
- }, { status: 400 });
2266
- }
2267
- if (!llmConfig.apiKey && !llmConfig.baseUrl?.includes("localhost")) {
2268
- const title = generateHeuristicTitle(message);
2269
- return json({
2270
- ok: true,
2271
- title,
2272
- source: "heuristic"
2273
- });
2319
+ return json(
2320
+ {
2321
+ ok: false,
2322
+ error: "Message is required and must be at least 3 characters"
2323
+ },
2324
+ { status: 400 }
2325
+ );
2274
2326
  }
2275
2327
  try {
2276
- const title = await generateSessionTitle(message, {
2277
- apiKey: llmConfig.apiKey || "",
2278
- ...llmConfig.baseUrl ? { baseUrl: llmConfig.baseUrl } : {},
2279
- ...llmConfig.model ? { model: llmConfig.model } : {}
2280
- });
2328
+ const title = await generateTitleViaOpenclaw(message, model);
2281
2329
  return json({
2282
2330
  ok: true,
2283
2331
  title,
2284
- source: "llm"
2332
+ source: "openclaw"
2285
2333
  });
2286
2334
  } catch (err) {
2287
2335
  console.error("[llm-features] Title generation error:", err);
2288
- const title = generateHeuristicTitle(message);
2289
2336
  return json({
2290
2337
  ok: true,
2291
- title,
2338
+ title: generateHeuristicTitle(message),
2292
2339
  source: "heuristic",
2293
- error: err instanceof Error ? err.message : "LLM error, used heuristic"
2340
+ error: err instanceof Error ? err.message : "OpenClaw error, used heuristic"
2294
2341
  });
2295
2342
  }
2296
2343
  }
2297
2344
  case "followups": {
2298
- const { conversationContext } = body;
2345
+ const { conversationContext, model } = body;
2299
2346
  if (!conversationContext || typeof conversationContext !== "string" || conversationContext.trim().length < 10) {
2300
2347
  return json({
2301
2348
  ok: true,
@@ -2303,30 +2350,16 @@ const Route$g = createFileRoute("/api/llm-features")({
2303
2350
  source: "heuristic"
2304
2351
  });
2305
2352
  }
2306
- const llmConfig = getLlmConfig(request);
2307
- if (llmConfig.error) {
2308
- return json({
2309
- ok: false,
2310
- error: llmConfig.error
2311
- }, { status: 400 });
2312
- }
2313
- if (!llmConfig.apiKey && !llmConfig.baseUrl?.includes("localhost")) {
2314
- return json({
2315
- ok: true,
2316
- suggestions: [],
2317
- source: "heuristic"
2318
- });
2319
- }
2320
2353
  try {
2321
- const suggestions = await generateFollowUps(conversationContext, {
2322
- apiKey: llmConfig.apiKey || "",
2323
- ...llmConfig.baseUrl ? { baseUrl: llmConfig.baseUrl } : {},
2324
- ...llmConfig.model ? { model: llmConfig.model } : {}
2325
- });
2354
+ const suggestions = await generateFollowUpsViaOpenclaw(
2355
+ conversationContext,
2356
+ void 0,
2357
+ model
2358
+ );
2326
2359
  return json({
2327
2360
  ok: true,
2328
2361
  suggestions,
2329
- source: "llm"
2362
+ source: "openclaw"
2330
2363
  });
2331
2364
  } catch (err) {
2332
2365
  console.error("[llm-features] Follow-ups generation error:", err);
@@ -2334,54 +2367,35 @@ const Route$g = createFileRoute("/api/llm-features")({
2334
2367
  ok: true,
2335
2368
  suggestions: [],
2336
2369
  source: "heuristic",
2337
- error: err instanceof Error ? err.message : "LLM error"
2370
+ error: err instanceof Error ? err.message : "OpenClaw error"
2338
2371
  });
2339
2372
  }
2340
2373
  }
2341
2374
  case "test": {
2342
- const llmConfig = getLlmConfig(request);
2343
- if (llmConfig.error) {
2344
- return json({
2345
- ok: false,
2346
- error: llmConfig.error
2347
- }, { status: 400 });
2348
- }
2349
- if (!llmConfig.apiKey && !llmConfig.baseUrl?.includes("localhost")) {
2350
- return json({
2351
- ok: false,
2352
- error: "API key required (or use Ollama for keyless access)"
2353
- });
2354
- }
2355
- try {
2356
- const valid = await testApiKey({
2357
- apiKey: llmConfig.apiKey || "",
2358
- ...llmConfig.baseUrl ? { baseUrl: llmConfig.baseUrl } : {},
2359
- ...llmConfig.model ? { model: llmConfig.model } : {}
2360
- });
2361
- return json({
2362
- ok: true,
2363
- valid
2364
- });
2365
- } catch (err) {
2366
- return json({
2367
- ok: true,
2368
- valid: false,
2369
- error: err instanceof Error ? err.message : "Test failed"
2370
- });
2371
- }
2375
+ const available = await isOpenclawAvailable();
2376
+ return json({
2377
+ ok: true,
2378
+ available
2379
+ });
2372
2380
  }
2373
2381
  default:
2374
- return json({
2375
- ok: false,
2376
- error: `Unknown action: ${action}. Valid actions: title, followups, test`
2377
- }, { status: 400 });
2382
+ return json(
2383
+ {
2384
+ ok: false,
2385
+ error: `Unknown action: ${action}. Valid actions: title, followups, test`
2386
+ },
2387
+ { status: 400 }
2388
+ );
2378
2389
  }
2379
2390
  } catch (err) {
2380
2391
  console.error("[llm-features] Error:", err);
2381
- return json({
2382
- ok: false,
2383
- error: err instanceof Error ? err.message : String(err)
2384
- }, { status: 500 });
2392
+ return json(
2393
+ {
2394
+ ok: false,
2395
+ error: err instanceof Error ? err.message : String(err)
2396
+ },
2397
+ { status: 500 }
2398
+ );
2385
2399
  }
2386
2400
  }
2387
2401
  }
@@ -2435,21 +2449,6 @@ const Route$f = createFileRoute("/api/history")({
2435
2449
  }
2436
2450
  }
2437
2451
  });
2438
- const FOLLOW_UP_SYSTEM_PROMPT = `You are a helpful assistant that generates follow-up question suggestions.
2439
- Given the assistant's last response, generate exactly 3 short, natural follow-up questions the user might want to ask.
2440
-
2441
- Rules:
2442
- - Each suggestion should be a single, concise question (under 60 characters preferred)
2443
- - Make them contextually relevant to the response
2444
- - Vary the types: clarification, deeper exploration, practical application
2445
- - Use natural, conversational language
2446
- - Do not number them or add any prefix
2447
-
2448
- Output format: Return ONLY the 3 questions, one per line, nothing else.`;
2449
- function parseFollowUps(text) {
2450
- const lines = text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).map((line) => line.replace(/^\d+[.)\s]+/, "").trim()).map((line) => line.replace(/^[-•*]\s*/, "").trim()).map((line) => line.replace(/^["']|["']$/g, "").trim()).filter((line) => line.length > 0 && line.length < 150);
2451
- return lines.slice(0, 3);
2452
- }
2453
2452
  const Route$e = createFileRoute("/api/follow-ups")({
2454
2453
  server: {
2455
2454
  handlers: {
@@ -2460,24 +2459,11 @@ const Route$e = createFileRoute("/api/follow-ups")({
2460
2459
  if (!responseText || responseText.length < 30) {
2461
2460
  return json({ ok: true, suggestions: [] });
2462
2461
  }
2463
- const truncatedResponse = responseText.length > 1500 ? responseText.slice(0, 1500) + "..." : responseText;
2464
- const contextSummary = typeof body.contextSummary === "string" ? body.contextSummary.slice(0, 500) : "";
2465
- const userPrompt = contextSummary ? `Context: ${contextSummary}
2466
-
2467
- Assistant's response:
2468
- ${truncatedResponse}` : `Assistant's response:
2469
- ${truncatedResponse}`;
2470
- const res = await gatewayRpc("chat.complete", {
2471
- messages: [
2472
- { role: "system", content: FOLLOW_UP_SYSTEM_PROMPT },
2473
- { role: "user", content: userPrompt }
2474
- ],
2475
- maxTokens: 200,
2476
- temperature: 0.7
2477
- // Use session's default model (no hardcoding!)
2478
- });
2479
- const content = res.content || res.message?.content || res.choices?.[0]?.message?.content || "";
2480
- const suggestions = parseFollowUps(content);
2462
+ const contextSummary = typeof body.contextSummary === "string" ? body.contextSummary : void 0;
2463
+ const suggestions = await generateFollowUpsViaOpenclaw(
2464
+ responseText,
2465
+ contextSummary
2466
+ );
2481
2467
  return json({ ok: true, suggestions });
2482
2468
  } catch (err) {
2483
2469
  console.error("[follow-ups] Error generating suggestions:", err);
@@ -4003,190 +3989,195 @@ const Route = createFileRoute("/api/dashboard/crons")({
4003
3989
  }
4004
3990
  }
4005
3991
  });
4006
- const SkillsRoute = Route$A.update({
3992
+ const SkillsRoute = Route$B.update({
4007
3993
  id: "/skills",
4008
3994
  path: "/skills",
4009
- getParentRoute: () => Route$B
3995
+ getParentRoute: () => Route$C
4010
3996
  });
4011
- const NewRoute = Route$z.update({
3997
+ const NewRoute = Route$A.update({
4012
3998
  id: "/new",
4013
3999
  path: "/new",
4014
- getParentRoute: () => Route$B
4000
+ getParentRoute: () => Route$C
4015
4001
  });
4016
- const MemoryRoute = Route$y.update({
4002
+ const MemoryRoute = Route$z.update({
4017
4003
  id: "/memory",
4018
4004
  path: "/memory",
4019
- getParentRoute: () => Route$B
4005
+ getParentRoute: () => Route$C
4020
4006
  });
4021
- const FilesRoute = Route$x.update({
4007
+ const FilesRoute = Route$y.update({
4022
4008
  id: "/files",
4023
4009
  path: "/files",
4024
- getParentRoute: () => Route$B
4010
+ getParentRoute: () => Route$C
4025
4011
  });
4026
- const DashboardRoute = Route$w.update({
4012
+ const DashboardRoute = Route$x.update({
4027
4013
  id: "/dashboard",
4028
4014
  path: "/dashboard",
4029
- getParentRoute: () => Route$B
4015
+ getParentRoute: () => Route$C
4030
4016
  });
4031
- const ConnectRoute = Route$v.update({
4017
+ const ConnectRoute = Route$w.update({
4032
4018
  id: "/connect",
4033
4019
  path: "/connect",
4034
- getParentRoute: () => Route$B
4020
+ getParentRoute: () => Route$C
4035
4021
  });
4036
- const BotsRoute = Route$u.update({
4022
+ const BotsRoute = Route$v.update({
4037
4023
  id: "/bots",
4038
4024
  path: "/bots",
4039
- getParentRoute: () => Route$B
4025
+ getParentRoute: () => Route$C
4040
4026
  });
4041
- const AgentsRoute = Route$t.update({
4027
+ const AgentsRoute = Route$u.update({
4042
4028
  id: "/agents",
4043
4029
  path: "/agents",
4044
- getParentRoute: () => Route$B
4030
+ getParentRoute: () => Route$C
4045
4031
  });
4046
- const IndexRoute = Route$s.update({
4032
+ const IndexRoute = Route$t.update({
4047
4033
  id: "/",
4048
4034
  path: "/",
4049
- getParentRoute: () => Route$B
4035
+ getParentRoute: () => Route$C
4050
4036
  });
4051
- const ChatSessionKeyRoute = Route$r.update({
4037
+ const ChatSessionKeyRoute = Route$s.update({
4052
4038
  id: "/chat/$sessionKey",
4053
4039
  path: "/chat/$sessionKey",
4054
- getParentRoute: () => Route$B
4040
+ getParentRoute: () => Route$C
4055
4041
  });
4056
- const ApiTtsRoute = Route$q.update({
4042
+ const ApiTtsRoute = Route$r.update({
4057
4043
  id: "/api/tts",
4058
4044
  path: "/api/tts",
4059
- getParentRoute: () => Route$B
4045
+ getParentRoute: () => Route$C
4060
4046
  });
4061
- const ApiSttRoute = Route$p.update({
4047
+ const ApiSttRoute = Route$q.update({
4062
4048
  id: "/api/stt",
4063
4049
  path: "/api/stt",
4064
- getParentRoute: () => Route$B
4050
+ getParentRoute: () => Route$C
4065
4051
  });
4066
- const ApiStreamRoute = Route$o.update({
4052
+ const ApiStreamRoute = Route$p.update({
4067
4053
  id: "/api/stream",
4068
4054
  path: "/api/stream",
4069
- getParentRoute: () => Route$B
4055
+ getParentRoute: () => Route$C
4070
4056
  });
4071
- const ApiSkillsRoute = Route$n.update({
4057
+ const ApiSkillsRoute = Route$o.update({
4072
4058
  id: "/api/skills",
4073
4059
  path: "/api/skills",
4074
- getParentRoute: () => Route$B
4060
+ getParentRoute: () => Route$C
4075
4061
  });
4076
- const ApiSessionsRoute = Route$m.update({
4062
+ const ApiSessionsRoute = Route$n.update({
4077
4063
  id: "/api/sessions",
4078
4064
  path: "/api/sessions",
4079
- getParentRoute: () => Route$B
4065
+ getParentRoute: () => Route$C
4080
4066
  });
4081
- const ApiSendRoute = Route$l.update({
4067
+ const ApiSendRoute = Route$m.update({
4082
4068
  id: "/api/send",
4083
4069
  path: "/api/send",
4084
- getParentRoute: () => Route$B
4070
+ getParentRoute: () => Route$C
4085
4071
  });
4086
- const ApiPingRoute = Route$k.update({
4072
+ const ApiPingRoute = Route$l.update({
4087
4073
  id: "/api/ping",
4088
4074
  path: "/api/ping",
4089
- getParentRoute: () => Route$B
4075
+ getParentRoute: () => Route$C
4090
4076
  });
4091
- const ApiPersonasRoute = Route$j.update({
4077
+ const ApiPersonasRoute = Route$k.update({
4092
4078
  id: "/api/personas",
4093
4079
  path: "/api/personas",
4094
- getParentRoute: () => Route$B
4080
+ getParentRoute: () => Route$C
4095
4081
  });
4096
- const ApiPathsRoute = Route$i.update({
4082
+ const ApiPathsRoute = Route$j.update({
4097
4083
  id: "/api/paths",
4098
4084
  path: "/api/paths",
4099
- getParentRoute: () => Route$B
4085
+ getParentRoute: () => Route$C
4100
4086
  });
4101
- const ApiModelsRoute = Route$h.update({
4087
+ const ApiModelsRoute = Route$i.update({
4102
4088
  id: "/api/models",
4103
4089
  path: "/api/models",
4104
- getParentRoute: () => Route$B
4090
+ getParentRoute: () => Route$C
4091
+ });
4092
+ const ApiLlmModelsRoute = Route$h.update({
4093
+ id: "/api/llm-models",
4094
+ path: "/api/llm-models",
4095
+ getParentRoute: () => Route$C
4105
4096
  });
4106
4097
  const ApiLlmFeaturesRoute = Route$g.update({
4107
4098
  id: "/api/llm-features",
4108
4099
  path: "/api/llm-features",
4109
- getParentRoute: () => Route$B
4100
+ getParentRoute: () => Route$C
4110
4101
  });
4111
4102
  const ApiHistoryRoute = Route$f.update({
4112
4103
  id: "/api/history",
4113
4104
  path: "/api/history",
4114
- getParentRoute: () => Route$B
4105
+ getParentRoute: () => Route$C
4115
4106
  });
4116
4107
  const ApiFollowUpsRoute = Route$e.update({
4117
4108
  id: "/api/follow-ups",
4118
4109
  path: "/api/follow-ups",
4119
- getParentRoute: () => Route$B
4110
+ getParentRoute: () => Route$C
4120
4111
  });
4121
4112
  const ApiCronRoute = Route$d.update({
4122
4113
  id: "/api/cron",
4123
4114
  path: "/api/cron",
4124
- getParentRoute: () => Route$B
4115
+ getParentRoute: () => Route$C
4125
4116
  });
4126
4117
  const ApiAgentsRoute = Route$c.update({
4127
4118
  id: "/api/agents",
4128
4119
  path: "/api/agents",
4129
- getParentRoute: () => Route$B
4120
+ getParentRoute: () => Route$C
4130
4121
  });
4131
4122
  const ApiFilesUploadRoute = Route$b.update({
4132
4123
  id: "/api/files/upload",
4133
4124
  path: "/api/files/upload",
4134
- getParentRoute: () => Route$B
4125
+ getParentRoute: () => Route$C
4135
4126
  });
4136
4127
  const ApiFilesSaveRoute = Route$a.update({
4137
4128
  id: "/api/files/save",
4138
4129
  path: "/api/files/save",
4139
- getParentRoute: () => Route$B
4130
+ getParentRoute: () => Route$C
4140
4131
  });
4141
4132
  const ApiFilesRenameRoute = Route$9.update({
4142
4133
  id: "/api/files/rename",
4143
4134
  path: "/api/files/rename",
4144
- getParentRoute: () => Route$B
4135
+ getParentRoute: () => Route$C
4145
4136
  });
4146
4137
  const ApiFilesReadRoute = Route$8.update({
4147
4138
  id: "/api/files/read",
4148
4139
  path: "/api/files/read",
4149
- getParentRoute: () => Route$B
4140
+ getParentRoute: () => Route$C
4150
4141
  });
4151
4142
  const ApiFilesMkdirRoute = Route$7.update({
4152
4143
  id: "/api/files/mkdir",
4153
4144
  path: "/api/files/mkdir",
4154
- getParentRoute: () => Route$B
4145
+ getParentRoute: () => Route$C
4155
4146
  });
4156
4147
  const ApiFilesListRoute = Route$6.update({
4157
4148
  id: "/api/files/list",
4158
4149
  path: "/api/files/list",
4159
- getParentRoute: () => Route$B
4150
+ getParentRoute: () => Route$C
4160
4151
  });
4161
4152
  const ApiFilesInfoRoute = Route$5.update({
4162
4153
  id: "/api/files/info",
4163
4154
  path: "/api/files/info",
4164
- getParentRoute: () => Route$B
4155
+ getParentRoute: () => Route$C
4165
4156
  });
4166
4157
  const ApiFilesDownloadRoute = Route$4.update({
4167
4158
  id: "/api/files/download",
4168
4159
  path: "/api/files/download",
4169
- getParentRoute: () => Route$B
4160
+ getParentRoute: () => Route$C
4170
4161
  });
4171
4162
  const ApiFilesDeleteRoute = Route$3.update({
4172
4163
  id: "/api/files/delete",
4173
4164
  path: "/api/files/delete",
4174
- getParentRoute: () => Route$B
4165
+ getParentRoute: () => Route$C
4175
4166
  });
4176
4167
  const ApiDashboardSystemRoute = Route$2.update({
4177
4168
  id: "/api/dashboard/system",
4178
4169
  path: "/api/dashboard/system",
4179
- getParentRoute: () => Route$B
4170
+ getParentRoute: () => Route$C
4180
4171
  });
4181
4172
  const ApiDashboardGatewayRoute = Route$1.update({
4182
4173
  id: "/api/dashboard/gateway",
4183
4174
  path: "/api/dashboard/gateway",
4184
- getParentRoute: () => Route$B
4175
+ getParentRoute: () => Route$C
4185
4176
  });
4186
4177
  const ApiDashboardCronsRoute = Route.update({
4187
4178
  id: "/api/dashboard/crons",
4188
4179
  path: "/api/dashboard/crons",
4189
- getParentRoute: () => Route$B
4180
+ getParentRoute: () => Route$C
4190
4181
  });
4191
4182
  const rootRouteChildren = {
4192
4183
  IndexRoute,
@@ -4203,6 +4194,7 @@ const rootRouteChildren = {
4203
4194
  ApiFollowUpsRoute,
4204
4195
  ApiHistoryRoute,
4205
4196
  ApiLlmFeaturesRoute,
4197
+ ApiLlmModelsRoute,
4206
4198
  ApiModelsRoute,
4207
4199
  ApiPathsRoute,
4208
4200
  ApiPersonasRoute,
@@ -4227,7 +4219,7 @@ const rootRouteChildren = {
4227
4219
  ApiFilesSaveRoute,
4228
4220
  ApiFilesUploadRoute
4229
4221
  };
4230
- const routeTree = Route$B._addFileChildren(rootRouteChildren)._addFileTypes();
4222
+ const routeTree = Route$C._addFileChildren(rootRouteChildren)._addFileTypes();
4231
4223
  const getRouter = () => {
4232
4224
  const router2 = createRouter({
4233
4225
  routeTree,
@@ -4242,7 +4234,7 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4242
4234
  getRouter
4243
4235
  }, Symbol.toStringTag, { value: "Module" }));
4244
4236
  export {
4245
- Route$s as R,
4246
- Route$r as a,
4237
+ Route$t as R,
4238
+ Route$s as a,
4247
4239
  router as r
4248
4240
  };