opencami 2.0.0 → 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 (69) hide show
  1. package/dist/client/assets/{CSPContext-CrlIQW7-.js → CSPContext-CxrBvJ99.js} +1 -1
  2. package/dist/client/assets/{DirectionContext-X-0CRn1O.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-BhOVp_S6.js → agents-screen-CqdzZKaR.js} +1 -1
  6. package/dist/client/assets/bots-DAInzfLx.js +2 -0
  7. package/dist/client/assets/{bots-screen-DLFd0ydi.js → bots-screen-RYovD6cu.js} +1 -1
  8. package/dist/client/assets/{button-D0n2Qsd_.js → button-Bv-7bwZZ.js} +1 -1
  9. package/dist/client/assets/{composite-GtKwZKbV.js → composite-DwQIZFe9.js} +1 -1
  10. package/dist/client/assets/{connect-vQWL0_11.js → connect-DZSrhhNT.js} +1 -1
  11. package/dist/client/assets/{dashboard-Knwc61i1.js → dashboard-y8KVUVF-.js} +1 -1
  12. package/dist/client/assets/{event-CHpdjYFR.js → event-Cnr9OETn.js} +1 -1
  13. package/dist/client/assets/{file-explorer-screen-FoYNs9zK.js → file-explorer-screen-C_mYUJsr.js} +1 -1
  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-CNPHef4O.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-DP8ptQ7N.js → keyboard-shortcuts-dialog-jm4-DftO.js} +1 -1
  19. package/dist/client/assets/{main-xPlWrMhO.js → main-CbISK-pm.js} +2 -2
  20. package/dist/client/assets/{markdown-3Js_RbUp.js → markdown-UD7RzskQ.js} +1 -1
  21. package/dist/client/assets/memory-BYRJ1l_4.js +2 -0
  22. package/dist/client/assets/{memory-screen-nzRra2Qi.js → memory-screen-BRsm-Q8A.js} +1 -1
  23. package/dist/client/assets/{menu-BnSEqetd.js → menu-DRjoHFvG.js} +1 -1
  24. package/dist/client/assets/{opencami-logo-B_hLbomw.js → opencami-logo-D2mv84sX.js} +1 -1
  25. package/dist/client/assets/{proxy-BnlGpgC1.js → proxy-DtxqfBHY.js} +1 -1
  26. package/dist/client/assets/{react-BgjQyJHw.js → react-C_7UlEtd.js} +1 -1
  27. package/dist/client/assets/{search-dialog-Bib2QY9u.js → search-dialog-lsT8JUtT.js} +1 -1
  28. package/dist/client/assets/{search-sources-badge-COHcYFRB.js → search-sources-badge-BnqKxoia.js} +1 -1
  29. package/dist/client/assets/{session-export-dialog-ooPnfHh_.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-D2uMdCHp.js → skills-panel-Dkuo0yyD.js} +1 -1
  33. package/dist/client/assets/{switch-BUQ0qH6r.js → switch-BYFBYPmi.js} +1 -1
  34. package/dist/client/assets/{tabs-BEyU6TjN.js → tabs-1jrPkdUE.js} +1 -1
  35. package/dist/client/assets/{thinking-CA48yhOE.js → thinking-D9BBn5kK.js} +1 -1
  36. package/dist/client/assets/{tooltip-CcIdgcV0.js → tooltip-DzFgDY86.js} +1 -1
  37. package/dist/client/assets/{use-file-explorer-state-CN_IJGcd.js → use-file-explorer-state-Dwhw6Mpg.js} +1 -1
  38. package/dist/client/assets/{useBaseUiId-ClbEYEil.js → useBaseUiId-BdQC9KF3.js} +1 -1
  39. package/dist/client/assets/{useCompositeItem-B_OxfJee.js → useCompositeItem-BI09zNFD.js} +1 -1
  40. package/dist/client/assets/{useControlled-CyT-lqbs.js → useControlled-Bc2Emhlx.js} +1 -1
  41. package/dist/client/assets/{useMutation-eQUrsn-X.js → useMutation-CMp81lDO.js} +1 -1
  42. package/dist/client/assets/{useOnFirstRender-CR_o2MK_.js → useOnFirstRender-DpB3w0o4.js} +1 -1
  43. package/dist/client/assets/{useQuery-k6EMRoMD.js → useQuery-_BEWdUe1.js} +1 -1
  44. package/dist/server/assets/{_sessionKey-CaFqmyhU.js → _sessionKey-COz7RLKC.js} +121 -23
  45. package/dist/server/assets/_tanstack-start-manifest_v-D0f-0Utn.js +4 -0
  46. package/dist/server/assets/{connect-CTVBm0Vc.js → connect-sd_NDvQN.js} +1 -1
  47. package/dist/server/assets/follow-up-suggestions-DRQqO8_S.js +183 -0
  48. package/dist/server/assets/{index-B_F4DTUu.js → index-197lqzv2.js} +2 -0
  49. package/dist/server/assets/{index-C7lmufwX.js → index-sXe2QC5V.js} +1 -1
  50. package/dist/server/assets/{markdown-CFdYXCRQ.js → markdown-Dk1YdosA.js} +1 -1
  51. package/dist/server/assets/{memory-screen-vqXczcVo.js → memory-screen-DA_o-N1Z.js} +2 -2
  52. package/dist/server/assets/{memory-rBB015W-.js → memory-woPzJFfs.js} +1 -1
  53. package/dist/server/assets/{router-X2L0PDPI.js → router-CZhxPkYW.js} +305 -150
  54. package/dist/server/assets/{search-dialog-CXhofdoP.js → search-dialog-DlvWfq1e.js} +5 -5
  55. package/dist/server/assets/{settings-dialog-CPdftvjz.js → settings-dialog-BGtrS2RJ.js} +62 -1
  56. package/dist/server/assets/{thinking-YkRSlXtf.js → thinking-fzwT_aVT.js} +5 -5
  57. package/dist/server/server.js +2 -2
  58. package/package.json +1 -1
  59. package/dist/client/assets/_sessionKey-yNQ57svB.js +0 -23
  60. package/dist/client/assets/agents-WqWjsymD.js +0 -2
  61. package/dist/client/assets/bots-DNqiFT7w.js +0 -2
  62. package/dist/client/assets/files-BtR_gArr.js +0 -2
  63. package/dist/client/assets/follow-up-suggestions-DVXNLqga.js +0 -5
  64. package/dist/client/assets/index-CTT0Y1ya.js +0 -3
  65. package/dist/client/assets/memory-BRGPq5t6.js +0 -2
  66. package/dist/client/assets/settings-dialog-B8mz99u-.js +0 -1
  67. package/dist/client/assets/skills-DA9J_tsC.js +0 -2
  68. package/dist/server/assets/_tanstack-start-manifest_v-P3skSR3R.js +0 -4
  69. package/dist/server/assets/follow-up-suggestions-DHv2_XzB.js +0 -275
@@ -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-C7lmufwX.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-CaFqmyhU.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,75 +2017,204 @@ const Route$h = createFileRoute("/api/models")({
2017
2017
  }
2018
2018
  }
2019
2019
  });
2020
- const OPENCLAW_GATEWAY_URL = "http://127.0.0.1:18789/v1/chat/completions";
2021
- const OPENCLAW_MODEL = "openclaw";
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"];
2022
2113
  const DEFAULT_TIMEOUT_MS = 3e4;
2023
2114
  function getGatewayToken() {
2024
2115
  return process.env.OPENCLAW_GATEWAY_TOKEN?.trim() || process.env.CLAWDBOT_GATEWAY_TOKEN?.trim() || null;
2025
2116
  }
2026
2117
  async function chatCompletion(messages, options) {
2027
- const controller = new AbortController();
2028
- const timeoutId = setTimeout(
2029
- () => controller.abort(),
2030
- options?.timeoutMs ?? DEFAULT_TIMEOUT_MS
2031
- );
2032
- try {
2033
- const token = getGatewayToken();
2034
- const response = await fetch(OPENCLAW_GATEWAY_URL, {
2035
- method: "POST",
2036
- headers: {
2037
- "Content-Type": "application/json",
2038
- ...token ? { Authorization: `Bearer ${token}` } : {}
2039
- },
2040
- body: JSON.stringify({
2041
- model: OPENCLAW_MODEL,
2042
- messages,
2043
- max_tokens: options?.maxTokens ?? 200,
2044
- temperature: options?.temperature ?? 0.7
2045
- }),
2046
- signal: controller.signal
2047
- });
2048
- if (!response.ok) {
2049
- const errorText = await response.text().catch(() => "");
2050
- throw new Error(
2051
- `OpenClaw Gateway error: ${response.status}${errorText ? ` ${errorText}` : ""}`
2052
- );
2053
- }
2054
- const data = await response.json();
2055
- const content = data.choices?.[0]?.message?.content?.trim() || "";
2056
- if (!content) {
2057
- throw new Error("OpenClaw Gateway returned empty content");
2058
- }
2059
- return content;
2060
- } catch (error) {
2061
- if (error instanceof Error && error.name === "AbortError") {
2062
- throw new Error("OpenClaw Gateway request timed out");
2118
+ const token = getGatewayToken();
2119
+ let lastError = null;
2120
+ const models = options?.model ? [options.model] : OPENCLAW_MODEL_FALLBACKS;
2121
+ for (const model of models) {
2122
+ const controller = new AbortController();
2123
+ const timeoutId = setTimeout(
2124
+ () => controller.abort(),
2125
+ options?.timeoutMs ?? DEFAULT_TIMEOUT_MS
2126
+ );
2127
+ try {
2128
+ const response = await fetch(OPENCLAW_GATEWAY_URL, {
2129
+ method: "POST",
2130
+ headers: {
2131
+ "Content-Type": "application/json",
2132
+ ...token ? { Authorization: `Bearer ${token}` } : {}
2133
+ },
2134
+ body: JSON.stringify({
2135
+ model,
2136
+ messages,
2137
+ max_tokens: options?.maxTokens ?? 200,
2138
+ temperature: options?.temperature ?? 0.7
2139
+ }),
2140
+ signal: controller.signal
2141
+ });
2142
+ if (!response.ok) {
2143
+ const errorText = await response.text().catch(() => "");
2144
+ lastError = new Error(
2145
+ `OpenClaw Gateway error (${model}): ${response.status}${errorText ? ` ${errorText}` : ""}`
2146
+ );
2147
+ continue;
2148
+ }
2149
+ const data = await response.json();
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;
2154
+ }
2155
+ return content;
2156
+ } catch (error) {
2157
+ if (error instanceof Error && error.name === "AbortError") {
2158
+ lastError = new Error(`OpenClaw Gateway request timed out for ${model}`);
2159
+ continue;
2160
+ }
2161
+ lastError = error instanceof Error ? error : new Error(String(error));
2162
+ } finally {
2163
+ clearTimeout(timeoutId);
2063
2164
  }
2064
- throw error instanceof Error ? error : new Error(String(error));
2065
- } finally {
2066
- clearTimeout(timeoutId);
2067
2165
  }
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
+ );
2068
2187
  }
2069
2188
  function parseFollowUps(text) {
2070
- 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);
2071
- return lines.slice(0, 3);
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);
2072
2198
  }
2073
- async function generateTitleViaOpenclaw(message) {
2074
- 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.
2075
2201
  Rules:
2076
- - No quotes or punctuation at the end
2077
- - Capture the main topic or intent
2078
- - Be specific, not generic
2079
- - 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"`;
2080
2209
  return chatCompletion(
2081
2210
  [
2082
2211
  { role: "system", content: systemPrompt },
2083
2212
  { role: "user", content: message }
2084
2213
  ],
2085
- { maxTokens: 60, temperature: 0.3 }
2214
+ { maxTokens: 60, temperature: 0.3, model }
2086
2215
  );
2087
2216
  }
2088
- async function generateFollowUpsViaOpenclaw(responseText, contextSummary) {
2217
+ async function generateFollowUpsViaOpenclaw(responseText, contextSummary, model) {
2089
2218
  const truncatedResponse = responseText.length > 1500 ? `${responseText.slice(0, 1500)}...` : responseText;
2090
2219
  const trimmedSummary = contextSummary?.slice(0, 500).trim() || "";
2091
2220
  const userPrompt = trimmedSummary ? `Context: ${trimmedSummary}
@@ -2093,23 +2222,39 @@ async function generateFollowUpsViaOpenclaw(responseText, contextSummary) {
2093
2222
  Assistant's response:
2094
2223
  ${truncatedResponse}` : `Assistant's response:
2095
2224
  ${truncatedResponse}`;
2096
- const systemPrompt = `You are a helpful assistant that generates follow-up question suggestions.
2097
- Given the assistant's last response, generate exactly 3 short, natural follow-up questions the user might want to ask.
2225
+ const systemPrompt = `You generate follow-up suggestions for a chat UI.
2098
2226
 
2099
- Rules:
2100
- - Each suggestion should be a single, concise question (under 60 characters preferred)
2101
- - Make them contextually relevant to the response
2102
- - Vary the types: clarification, deeper exploration, practical application
2103
- - Use natural, conversational language
2104
- - Do not number them or add any prefix
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
2105
2242
 
2106
- Output format: Return ONLY the 3 questions, one per line, nothing else.`;
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?`;
2107
2252
  const response = await chatCompletion(
2108
2253
  [
2109
2254
  { role: "system", content: systemPrompt },
2110
2255
  { role: "user", content: userPrompt }
2111
2256
  ],
2112
- { maxTokens: 200, temperature: 0.7 }
2257
+ { maxTokens: 200, temperature: 0.2, model }
2113
2258
  );
2114
2259
  return parseFollowUps(response);
2115
2260
  }
@@ -2169,7 +2314,7 @@ const Route$g = createFileRoute("/api/llm-features")({
2169
2314
  const action = body.action;
2170
2315
  switch (action) {
2171
2316
  case "title": {
2172
- const { message } = body;
2317
+ const { message, model } = body;
2173
2318
  if (!message || typeof message !== "string" || message.trim().length < 3) {
2174
2319
  return json(
2175
2320
  {
@@ -2180,7 +2325,7 @@ const Route$g = createFileRoute("/api/llm-features")({
2180
2325
  );
2181
2326
  }
2182
2327
  try {
2183
- const title = await generateTitleViaOpenclaw(message);
2328
+ const title = await generateTitleViaOpenclaw(message, model);
2184
2329
  return json({
2185
2330
  ok: true,
2186
2331
  title,
@@ -2197,7 +2342,7 @@ const Route$g = createFileRoute("/api/llm-features")({
2197
2342
  }
2198
2343
  }
2199
2344
  case "followups": {
2200
- const { conversationContext } = body;
2345
+ const { conversationContext, model } = body;
2201
2346
  if (!conversationContext || typeof conversationContext !== "string" || conversationContext.trim().length < 10) {
2202
2347
  return json({
2203
2348
  ok: true,
@@ -2206,7 +2351,11 @@ const Route$g = createFileRoute("/api/llm-features")({
2206
2351
  });
2207
2352
  }
2208
2353
  try {
2209
- const suggestions = await generateFollowUpsViaOpenclaw(conversationContext);
2354
+ const suggestions = await generateFollowUpsViaOpenclaw(
2355
+ conversationContext,
2356
+ void 0,
2357
+ model
2358
+ );
2210
2359
  return json({
2211
2360
  ok: true,
2212
2361
  suggestions,
@@ -3840,190 +3989,195 @@ const Route = createFileRoute("/api/dashboard/crons")({
3840
3989
  }
3841
3990
  }
3842
3991
  });
3843
- const SkillsRoute = Route$A.update({
3992
+ const SkillsRoute = Route$B.update({
3844
3993
  id: "/skills",
3845
3994
  path: "/skills",
3846
- getParentRoute: () => Route$B
3995
+ getParentRoute: () => Route$C
3847
3996
  });
3848
- const NewRoute = Route$z.update({
3997
+ const NewRoute = Route$A.update({
3849
3998
  id: "/new",
3850
3999
  path: "/new",
3851
- getParentRoute: () => Route$B
4000
+ getParentRoute: () => Route$C
3852
4001
  });
3853
- const MemoryRoute = Route$y.update({
4002
+ const MemoryRoute = Route$z.update({
3854
4003
  id: "/memory",
3855
4004
  path: "/memory",
3856
- getParentRoute: () => Route$B
4005
+ getParentRoute: () => Route$C
3857
4006
  });
3858
- const FilesRoute = Route$x.update({
4007
+ const FilesRoute = Route$y.update({
3859
4008
  id: "/files",
3860
4009
  path: "/files",
3861
- getParentRoute: () => Route$B
4010
+ getParentRoute: () => Route$C
3862
4011
  });
3863
- const DashboardRoute = Route$w.update({
4012
+ const DashboardRoute = Route$x.update({
3864
4013
  id: "/dashboard",
3865
4014
  path: "/dashboard",
3866
- getParentRoute: () => Route$B
4015
+ getParentRoute: () => Route$C
3867
4016
  });
3868
- const ConnectRoute = Route$v.update({
4017
+ const ConnectRoute = Route$w.update({
3869
4018
  id: "/connect",
3870
4019
  path: "/connect",
3871
- getParentRoute: () => Route$B
4020
+ getParentRoute: () => Route$C
3872
4021
  });
3873
- const BotsRoute = Route$u.update({
4022
+ const BotsRoute = Route$v.update({
3874
4023
  id: "/bots",
3875
4024
  path: "/bots",
3876
- getParentRoute: () => Route$B
4025
+ getParentRoute: () => Route$C
3877
4026
  });
3878
- const AgentsRoute = Route$t.update({
4027
+ const AgentsRoute = Route$u.update({
3879
4028
  id: "/agents",
3880
4029
  path: "/agents",
3881
- getParentRoute: () => Route$B
4030
+ getParentRoute: () => Route$C
3882
4031
  });
3883
- const IndexRoute = Route$s.update({
4032
+ const IndexRoute = Route$t.update({
3884
4033
  id: "/",
3885
4034
  path: "/",
3886
- getParentRoute: () => Route$B
4035
+ getParentRoute: () => Route$C
3887
4036
  });
3888
- const ChatSessionKeyRoute = Route$r.update({
4037
+ const ChatSessionKeyRoute = Route$s.update({
3889
4038
  id: "/chat/$sessionKey",
3890
4039
  path: "/chat/$sessionKey",
3891
- getParentRoute: () => Route$B
4040
+ getParentRoute: () => Route$C
3892
4041
  });
3893
- const ApiTtsRoute = Route$q.update({
4042
+ const ApiTtsRoute = Route$r.update({
3894
4043
  id: "/api/tts",
3895
4044
  path: "/api/tts",
3896
- getParentRoute: () => Route$B
4045
+ getParentRoute: () => Route$C
3897
4046
  });
3898
- const ApiSttRoute = Route$p.update({
4047
+ const ApiSttRoute = Route$q.update({
3899
4048
  id: "/api/stt",
3900
4049
  path: "/api/stt",
3901
- getParentRoute: () => Route$B
4050
+ getParentRoute: () => Route$C
3902
4051
  });
3903
- const ApiStreamRoute = Route$o.update({
4052
+ const ApiStreamRoute = Route$p.update({
3904
4053
  id: "/api/stream",
3905
4054
  path: "/api/stream",
3906
- getParentRoute: () => Route$B
4055
+ getParentRoute: () => Route$C
3907
4056
  });
3908
- const ApiSkillsRoute = Route$n.update({
4057
+ const ApiSkillsRoute = Route$o.update({
3909
4058
  id: "/api/skills",
3910
4059
  path: "/api/skills",
3911
- getParentRoute: () => Route$B
4060
+ getParentRoute: () => Route$C
3912
4061
  });
3913
- const ApiSessionsRoute = Route$m.update({
4062
+ const ApiSessionsRoute = Route$n.update({
3914
4063
  id: "/api/sessions",
3915
4064
  path: "/api/sessions",
3916
- getParentRoute: () => Route$B
4065
+ getParentRoute: () => Route$C
3917
4066
  });
3918
- const ApiSendRoute = Route$l.update({
4067
+ const ApiSendRoute = Route$m.update({
3919
4068
  id: "/api/send",
3920
4069
  path: "/api/send",
3921
- getParentRoute: () => Route$B
4070
+ getParentRoute: () => Route$C
3922
4071
  });
3923
- const ApiPingRoute = Route$k.update({
4072
+ const ApiPingRoute = Route$l.update({
3924
4073
  id: "/api/ping",
3925
4074
  path: "/api/ping",
3926
- getParentRoute: () => Route$B
4075
+ getParentRoute: () => Route$C
3927
4076
  });
3928
- const ApiPersonasRoute = Route$j.update({
4077
+ const ApiPersonasRoute = Route$k.update({
3929
4078
  id: "/api/personas",
3930
4079
  path: "/api/personas",
3931
- getParentRoute: () => Route$B
4080
+ getParentRoute: () => Route$C
3932
4081
  });
3933
- const ApiPathsRoute = Route$i.update({
4082
+ const ApiPathsRoute = Route$j.update({
3934
4083
  id: "/api/paths",
3935
4084
  path: "/api/paths",
3936
- getParentRoute: () => Route$B
4085
+ getParentRoute: () => Route$C
3937
4086
  });
3938
- const ApiModelsRoute = Route$h.update({
4087
+ const ApiModelsRoute = Route$i.update({
3939
4088
  id: "/api/models",
3940
4089
  path: "/api/models",
3941
- 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
3942
4096
  });
3943
4097
  const ApiLlmFeaturesRoute = Route$g.update({
3944
4098
  id: "/api/llm-features",
3945
4099
  path: "/api/llm-features",
3946
- getParentRoute: () => Route$B
4100
+ getParentRoute: () => Route$C
3947
4101
  });
3948
4102
  const ApiHistoryRoute = Route$f.update({
3949
4103
  id: "/api/history",
3950
4104
  path: "/api/history",
3951
- getParentRoute: () => Route$B
4105
+ getParentRoute: () => Route$C
3952
4106
  });
3953
4107
  const ApiFollowUpsRoute = Route$e.update({
3954
4108
  id: "/api/follow-ups",
3955
4109
  path: "/api/follow-ups",
3956
- getParentRoute: () => Route$B
4110
+ getParentRoute: () => Route$C
3957
4111
  });
3958
4112
  const ApiCronRoute = Route$d.update({
3959
4113
  id: "/api/cron",
3960
4114
  path: "/api/cron",
3961
- getParentRoute: () => Route$B
4115
+ getParentRoute: () => Route$C
3962
4116
  });
3963
4117
  const ApiAgentsRoute = Route$c.update({
3964
4118
  id: "/api/agents",
3965
4119
  path: "/api/agents",
3966
- getParentRoute: () => Route$B
4120
+ getParentRoute: () => Route$C
3967
4121
  });
3968
4122
  const ApiFilesUploadRoute = Route$b.update({
3969
4123
  id: "/api/files/upload",
3970
4124
  path: "/api/files/upload",
3971
- getParentRoute: () => Route$B
4125
+ getParentRoute: () => Route$C
3972
4126
  });
3973
4127
  const ApiFilesSaveRoute = Route$a.update({
3974
4128
  id: "/api/files/save",
3975
4129
  path: "/api/files/save",
3976
- getParentRoute: () => Route$B
4130
+ getParentRoute: () => Route$C
3977
4131
  });
3978
4132
  const ApiFilesRenameRoute = Route$9.update({
3979
4133
  id: "/api/files/rename",
3980
4134
  path: "/api/files/rename",
3981
- getParentRoute: () => Route$B
4135
+ getParentRoute: () => Route$C
3982
4136
  });
3983
4137
  const ApiFilesReadRoute = Route$8.update({
3984
4138
  id: "/api/files/read",
3985
4139
  path: "/api/files/read",
3986
- getParentRoute: () => Route$B
4140
+ getParentRoute: () => Route$C
3987
4141
  });
3988
4142
  const ApiFilesMkdirRoute = Route$7.update({
3989
4143
  id: "/api/files/mkdir",
3990
4144
  path: "/api/files/mkdir",
3991
- getParentRoute: () => Route$B
4145
+ getParentRoute: () => Route$C
3992
4146
  });
3993
4147
  const ApiFilesListRoute = Route$6.update({
3994
4148
  id: "/api/files/list",
3995
4149
  path: "/api/files/list",
3996
- getParentRoute: () => Route$B
4150
+ getParentRoute: () => Route$C
3997
4151
  });
3998
4152
  const ApiFilesInfoRoute = Route$5.update({
3999
4153
  id: "/api/files/info",
4000
4154
  path: "/api/files/info",
4001
- getParentRoute: () => Route$B
4155
+ getParentRoute: () => Route$C
4002
4156
  });
4003
4157
  const ApiFilesDownloadRoute = Route$4.update({
4004
4158
  id: "/api/files/download",
4005
4159
  path: "/api/files/download",
4006
- getParentRoute: () => Route$B
4160
+ getParentRoute: () => Route$C
4007
4161
  });
4008
4162
  const ApiFilesDeleteRoute = Route$3.update({
4009
4163
  id: "/api/files/delete",
4010
4164
  path: "/api/files/delete",
4011
- getParentRoute: () => Route$B
4165
+ getParentRoute: () => Route$C
4012
4166
  });
4013
4167
  const ApiDashboardSystemRoute = Route$2.update({
4014
4168
  id: "/api/dashboard/system",
4015
4169
  path: "/api/dashboard/system",
4016
- getParentRoute: () => Route$B
4170
+ getParentRoute: () => Route$C
4017
4171
  });
4018
4172
  const ApiDashboardGatewayRoute = Route$1.update({
4019
4173
  id: "/api/dashboard/gateway",
4020
4174
  path: "/api/dashboard/gateway",
4021
- getParentRoute: () => Route$B
4175
+ getParentRoute: () => Route$C
4022
4176
  });
4023
4177
  const ApiDashboardCronsRoute = Route.update({
4024
4178
  id: "/api/dashboard/crons",
4025
4179
  path: "/api/dashboard/crons",
4026
- getParentRoute: () => Route$B
4180
+ getParentRoute: () => Route$C
4027
4181
  });
4028
4182
  const rootRouteChildren = {
4029
4183
  IndexRoute,
@@ -4040,6 +4194,7 @@ const rootRouteChildren = {
4040
4194
  ApiFollowUpsRoute,
4041
4195
  ApiHistoryRoute,
4042
4196
  ApiLlmFeaturesRoute,
4197
+ ApiLlmModelsRoute,
4043
4198
  ApiModelsRoute,
4044
4199
  ApiPathsRoute,
4045
4200
  ApiPersonasRoute,
@@ -4064,7 +4219,7 @@ const rootRouteChildren = {
4064
4219
  ApiFilesSaveRoute,
4065
4220
  ApiFilesUploadRoute
4066
4221
  };
4067
- const routeTree = Route$B._addFileChildren(rootRouteChildren)._addFileTypes();
4222
+ const routeTree = Route$C._addFileChildren(rootRouteChildren)._addFileTypes();
4068
4223
  const getRouter = () => {
4069
4224
  const router2 = createRouter({
4070
4225
  routeTree,
@@ -4079,7 +4234,7 @@ const router = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
4079
4234
  getRouter
4080
4235
  }, Symbol.toStringTag, { value: "Module" }));
4081
4236
  export {
4082
- Route$s as R,
4083
- Route$r as a,
4237
+ Route$t as R,
4238
+ Route$s as a,
4084
4239
  router as r
4085
4240
  };