@markus-global/cli 0.9.7-rc.0 → 0.9.7-rc.5

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 (63) hide show
  1. package/dist/markus.mjs +292 -17
  2. package/dist/web-ui/assets/{CommentInput-Dvlq2svZ.js → CommentInput-H50XI7sG.js} +1 -1
  3. package/dist/web-ui/assets/{ContentRenderer-BfnvpFM7.js → ContentRenderer-CbJWw6ni.js} +2 -2
  4. package/dist/web-ui/assets/{DeliverableShareModal-DkGpQmZ8.js → DeliverableShareModal-CHdOyC9c.js} +1 -1
  5. package/dist/web-ui/assets/{Deliverables-CtPvquKO.js → Deliverables-B48EGVOS.js} +1 -1
  6. package/dist/web-ui/assets/{Notifications-C_e9_V74.js → Notifications-Q29_jC_0.js} +1 -1
  7. package/dist/web-ui/assets/{Search-DZBGpN3c.js → Search-BYZ8QKgz.js} +1 -1
  8. package/dist/web-ui/assets/Settings-Kck8VNiS.js +8 -0
  9. package/dist/web-ui/assets/{Store-dbJSFCFw.js → Store-afxOnzIy.js} +1 -1
  10. package/dist/web-ui/assets/{Team-vmqHkmh8.js → Team-B7UAO190.js} +4 -4
  11. package/dist/web-ui/assets/{Work-Ds7q6pZg.js → Work-DhJSUJ8s.js} +1 -1
  12. package/dist/web-ui/assets/{arc-BLoofCdL.js → arc-oi2C2tM_.js} +1 -1
  13. package/dist/web-ui/assets/{architectureDiagram-3BPJPVTR-BGO4nFRN.js → architectureDiagram-3BPJPVTR-CK_H0la_.js} +1 -1
  14. package/dist/web-ui/assets/{blockDiagram-GPEHLZMM-Cn1E0037.js → blockDiagram-GPEHLZMM-B6SjWtm1.js} +1 -1
  15. package/dist/web-ui/assets/{c4Diagram-AAUBKEIU-DXFAnq3E.js → c4Diagram-AAUBKEIU-DVBOL2Mr.js} +1 -1
  16. package/dist/web-ui/assets/channel-B2Rur3o9.js +1 -0
  17. package/dist/web-ui/assets/{chunk-2J33WTMH-BXCSmrWD.js → chunk-2J33WTMH-p074NkEK.js} +1 -1
  18. package/dist/web-ui/assets/{chunk-4BX2VUAB-CIWJeena.js → chunk-4BX2VUAB-Dgt_oQ5Z.js} +1 -1
  19. package/dist/web-ui/assets/{chunk-55IACEB6-BuQ5-tdz.js → chunk-55IACEB6-Db-juaJ9.js} +1 -1
  20. package/dist/web-ui/assets/{chunk-727SXJPM-q9Yx_12l.js → chunk-727SXJPM-D8nAWxeV.js} +1 -1
  21. package/dist/web-ui/assets/{chunk-AQP2D5EJ-DqaehwKB.js → chunk-AQP2D5EJ-CxZV02WA.js} +1 -1
  22. package/dist/web-ui/assets/{chunk-FMBD7UC4-BFs2IYjK.js → chunk-FMBD7UC4-B5-ZRwUd.js} +1 -1
  23. package/dist/web-ui/assets/{chunk-ND2GUHAM-DyYGWpbj.js → chunk-ND2GUHAM-Dhk2IHCQ.js} +1 -1
  24. package/dist/web-ui/assets/{chunk-QZHKN3VN-DmbA4AU_.js → chunk-QZHKN3VN-EcAQZTUP.js} +1 -1
  25. package/dist/web-ui/assets/classDiagram-4FO5ZUOK-DxHkl_eV.js +1 -0
  26. package/dist/web-ui/assets/classDiagram-v2-Q7XG4LA2-DxHkl_eV.js +1 -0
  27. package/dist/web-ui/assets/{cose-bilkent-S5V4N54A-BLWm27gv.js → cose-bilkent-S5V4N54A-DLL4WcR1.js} +1 -1
  28. package/dist/web-ui/assets/{dagre-BM42HDAG-Cb2YgHjF.js → dagre-BM42HDAG-DrKXWZlt.js} +1 -1
  29. package/dist/web-ui/assets/{diagram-2AECGRRQ-hMyyF4SK.js → diagram-2AECGRRQ-DLOwNUpQ.js} +1 -1
  30. package/dist/web-ui/assets/{diagram-5GNKFQAL-DQvYRI_z.js → diagram-5GNKFQAL-BWcPBTuv.js} +1 -1
  31. package/dist/web-ui/assets/{diagram-KO2AKTUF-CPOv2Kga.js → diagram-KO2AKTUF-CdHvIQGV.js} +1 -1
  32. package/dist/web-ui/assets/{diagram-LMA3HP47-DvgOU39M.js → diagram-LMA3HP47-Dm80PSxk.js} +1 -1
  33. package/dist/web-ui/assets/{diagram-OG6HWLK6-DhLJ9Bog.js → diagram-OG6HWLK6-CV3Ynpw5.js} +1 -1
  34. package/dist/web-ui/assets/{erDiagram-TEJ5UH35-C4MKvJYc.js → erDiagram-TEJ5UH35-BiOoSELr.js} +1 -1
  35. package/dist/web-ui/assets/{flowDiagram-I6XJVG4X-C4KiUPBX.js → flowDiagram-I6XJVG4X-BuBzkN5x.js} +1 -1
  36. package/dist/web-ui/assets/{ganttDiagram-6RSMTGT7-CFiAe8nx.js → ganttDiagram-6RSMTGT7-CvPvDHVe.js} +1 -1
  37. package/dist/web-ui/assets/{gitGraphDiagram-PVQCEYII-y6rZo1sT.js → gitGraphDiagram-PVQCEYII-CmqSg9ZW.js} +1 -1
  38. package/dist/web-ui/assets/{index-CcppNtga.js → index-B-xjev59.js} +6 -6
  39. package/dist/web-ui/assets/{infoDiagram-5YYISTIA-LLlnmiVb.js → infoDiagram-5YYISTIA-C_griWOm.js} +1 -1
  40. package/dist/web-ui/assets/{ishikawaDiagram-YF4QCWOH-Brs-NT84.js → ishikawaDiagram-YF4QCWOH-wLNhIFRK.js} +1 -1
  41. package/dist/web-ui/assets/{journeyDiagram-JHISSGLW-um06-20P.js → journeyDiagram-JHISSGLW-BJUPzH74.js} +1 -1
  42. package/dist/web-ui/assets/{kanban-definition-UN3LZRKU-yws0Rgw8.js → kanban-definition-UN3LZRKU-C-exBcTE.js} +1 -1
  43. package/dist/web-ui/assets/{mermaid.core-C0Q8mcc_.js → mermaid.core-BcQakp0U.js} +5 -5
  44. package/dist/web-ui/assets/{mindmap-definition-RKZ34NQL-xCtHajk4.js → mindmap-definition-RKZ34NQL-B7jG8seW.js} +1 -1
  45. package/dist/web-ui/assets/{pieDiagram-4H26LBE5-BhKiwY7r.js → pieDiagram-4H26LBE5-CN4AAPgP.js} +1 -1
  46. package/dist/web-ui/assets/{quadrantDiagram-W4KKPZXB-CGmxw13s.js → quadrantDiagram-W4KKPZXB-rK-x9Rbd.js} +1 -1
  47. package/dist/web-ui/assets/{requirementDiagram-4Y6WPE33-CYJRHrZE.js → requirementDiagram-4Y6WPE33-XAjhwp64.js} +1 -1
  48. package/dist/web-ui/assets/{sankeyDiagram-5OEKKPKP-CgkdSCTc.js → sankeyDiagram-5OEKKPKP-jxToLEBs.js} +1 -1
  49. package/dist/web-ui/assets/{sequenceDiagram-3UESZ5HK-BD2dXCDe.js → sequenceDiagram-3UESZ5HK-BqiwRyol.js} +1 -1
  50. package/dist/web-ui/assets/{stateDiagram-AJRCARHV-DdEvCrm2.js → stateDiagram-AJRCARHV-CiFFWJwO.js} +1 -1
  51. package/dist/web-ui/assets/stateDiagram-v2-BHNVJYJU-C9aZNCon.js +1 -0
  52. package/dist/web-ui/assets/{timeline-definition-PNZ67QCA-CWfdKPN1.js → timeline-definition-PNZ67QCA-CWbiUHvH.js} +1 -1
  53. package/dist/web-ui/assets/{vennDiagram-CIIHVFJN-BTiDdRt6.js → vennDiagram-CIIHVFJN-CwOu2L0b.js} +1 -1
  54. package/dist/web-ui/assets/{wardley-L42UT6IY-eDkltTBC.js → wardley-L42UT6IY-EdI9vfy5.js} +1 -1
  55. package/dist/web-ui/assets/{wardleyDiagram-YWT4CUSO-DLOZOKTX.js → wardleyDiagram-YWT4CUSO-DZzukmtq.js} +1 -1
  56. package/dist/web-ui/assets/{xychartDiagram-2RQKCTM6-RU8GlPu6.js → xychartDiagram-2RQKCTM6-BN5doc0x.js} +1 -1
  57. package/dist/web-ui/index.html +1 -1
  58. package/package.json +8 -1
  59. package/dist/web-ui/assets/Settings-6xm2sHhm.js +0 -8
  60. package/dist/web-ui/assets/channel-Cw25sYRm.js +0 -1
  61. package/dist/web-ui/assets/classDiagram-4FO5ZUOK-CDWjvgnK.js +0 -1
  62. package/dist/web-ui/assets/classDiagram-v2-Q7XG4LA2-CDWjvgnK.js +0 -1
  63. package/dist/web-ui/assets/stateDiagram-v2-BHNVJYJU-DKPteYMC.js +0 -1
package/dist/markus.mjs CHANGED
@@ -72159,7 +72159,7 @@ var init_ollama = __esm({
72159
72159
  async chat(request) {
72160
72160
  const messages2 = this.convertMessages(request.messages);
72161
72161
  const body = {
72162
- model: this.model,
72162
+ model: request.model ?? this.model,
72163
72163
  messages: messages2,
72164
72164
  stream: false,
72165
72165
  options: {
@@ -72194,7 +72194,7 @@ var init_ollama = __esm({
72194
72194
  async chatStream(request, onEvent, signal) {
72195
72195
  const messages2 = this.convertMessages(request.messages);
72196
72196
  const body = {
72197
- model: this.model,
72197
+ model: request.model ?? this.model,
72198
72198
  messages: messages2,
72199
72199
  stream: true,
72200
72200
  options: {
@@ -75095,12 +75095,13 @@ function costTierFromPrice(inputPer1M) {
75095
75095
  return "$$$";
75096
75096
  return "$$$$";
75097
75097
  }
75098
- var log57, CAPABILITY_KEY_MAP, MINIMAX_NAMES, DASHSCOPE_NAMES, FIREWORKS_NAMES, REGIONAL_PROVIDER_ALIASES, ALL_COMPLEXITY, LLMRouter, PROVIDER_DISPLAY_NAMES, BUILTIN_MODEL_CATALOG;
75098
+ var log57, OLLAMA_DEFAULT_CONTEXT_WINDOW, OLLAMA_DEFAULT_MAX_OUTPUT, DEFAULT_CONTEXT_WINDOW_FALLBACK, DEFAULT_MAX_OUTPUT_FALLBACK, CAPABILITY_KEY_MAP, MINIMAX_NAMES, DASHSCOPE_NAMES, FIREWORKS_NAMES, REGIONAL_PROVIDER_ALIASES, ALL_COMPLEXITY, LLMRouter, PROVIDER_DISPLAY_NAMES, BUILTIN_MODEL_CATALOG;
75099
75099
  var init_router2 = __esm({
75100
75100
  "../core/dist/llm/router.js"() {
75101
75101
  "use strict";
75102
75102
  init_dist();
75103
75103
  init_tracing();
75104
+ init_provider();
75104
75105
  init_anthropic();
75105
75106
  init_openai();
75106
75107
  init_minimax();
@@ -75115,6 +75116,10 @@ var init_router2 = __esm({
75115
75116
  init_auth_profiles();
75116
75117
  init_oauth_manager();
75117
75118
  log57 = createLogger("llm-router");
75119
+ OLLAMA_DEFAULT_CONTEXT_WINDOW = 8192;
75120
+ OLLAMA_DEFAULT_MAX_OUTPUT = 4096;
75121
+ DEFAULT_CONTEXT_WINDOW_FALLBACK = 128e3;
75122
+ DEFAULT_MAX_OUTPUT_FALLBACK = DEFAULT_REQUEST_MAX_TOKENS;
75118
75123
  CAPABILITY_KEY_MAP = {
75119
75124
  image_generation: "imageGeneration",
75120
75125
  image_recognition: "vision",
@@ -75419,6 +75424,12 @@ var init_router2 = __esm({
75419
75424
  return hubModels.map((m) => this.enrichModelFromCatalog(m));
75420
75425
  }
75421
75426
  }
75427
+ if (providerName === "ollama") {
75428
+ const localModels = this.customModelCatalog.get("ollama");
75429
+ if (localModels && localModels.length > 0) {
75430
+ return localModels.map((m) => this.enrichModelFromCatalog(m));
75431
+ }
75432
+ }
75422
75433
  let builtinModels = BUILTIN_MODEL_CATALOG.filter((m) => m.provider === providerName);
75423
75434
  if (builtinModels.length === 0 && REGIONAL_PROVIDER_ALIASES[providerName]) {
75424
75435
  const parent = REGIONAL_PROVIDER_ALIASES[providerName];
@@ -75477,6 +75488,154 @@ var init_router2 = __esm({
75477
75488
  }
75478
75489
  return defs.length;
75479
75490
  }
75491
+ /**
75492
+ * Probe a local Ollama server (/api/tags) and record the actually-pulled
75493
+ * models into the provider's custom catalog. This makes local models appear
75494
+ * in both the Settings panel and the chat model picker (which both read
75495
+ * getEnhancedSettings / getProviderModels). On success the local list
75496
+ * REPLACES the static catalog for Ollama (only real models are usable).
75497
+ * On failure we keep any previously-cached local models (last-good), so a
75498
+ * transient Ollama stop does not wipe the picker.
75499
+ */
75500
+ async refreshOllamaLocalModels(baseUrl) {
75501
+ const provider = this.providers.get("ollama");
75502
+ const effectiveBase = baseUrl ?? provider?.baseUrl ?? process.env["OLLAMA_BASE_URL"] ?? "http://localhost:11434";
75503
+ const base = String(effectiveBase).replace(/\/+$/, "");
75504
+ try {
75505
+ const ctrl = new AbortController();
75506
+ const tmr = setTimeout(() => ctrl.abort(), 5e3);
75507
+ const res = await fetch(`${base}/api/tags`, {
75508
+ signal: ctrl.signal
75509
+ });
75510
+ clearTimeout(tmr);
75511
+ if (!res.ok) {
75512
+ log57.warn("Ollama /api/tags HTTP error", { baseUrl: base, status: res.status });
75513
+ return this.customModelCatalog.get("ollama")?.length ?? 0;
75514
+ }
75515
+ const data = await res.json();
75516
+ const models = await Promise.all((data.models ?? []).map(async (m) => {
75517
+ const d = m.details;
75518
+ const tag = d?.parameter_size || d?.quantization_level;
75519
+ const info2 = await this.probeOllamaModel(base, m.name);
75520
+ const caps = info2.capabilities;
75521
+ const hasVision = caps.includes("vision");
75522
+ const hasReasoning = caps.includes("thinking");
75523
+ return {
75524
+ id: m.name,
75525
+ name: m.name,
75526
+ provider: "ollama",
75527
+ contextWindow: info2.contextWindow,
75528
+ maxOutputTokens: OLLAMA_DEFAULT_MAX_OUTPUT,
75529
+ cost: { input: 0, output: 0 },
75530
+ reasoning: hasReasoning || void 0,
75531
+ inputTypes: hasVision ? ["text", "image"] : ["text"],
75532
+ capabilities: caps.length > 0 ? caps : void 0,
75533
+ description: tag ? tag : void 0
75534
+ };
75535
+ }));
75536
+ this.customModelCatalog.set("ollama", models);
75537
+ log57.info(`Synced ${models.length} local Ollama models`, { baseUrl: base });
75538
+ return models.length;
75539
+ } catch (err) {
75540
+ log57.warn("Ollama local model sync failed", { error: String(err), baseUrl: base });
75541
+ return this.customModelCatalog.get("ollama")?.length ?? 0;
75542
+ }
75543
+ }
75544
+ /**
75545
+ * Probe a local Ollama model's real context length and capabilities via `/api/show`.
75546
+ * Reads `model_info["*.context_length"]` (key name varies by model family,
75547
+ * e.g. `llama.context_length`, `qwen3_5.context_length`) for the context window,
75548
+ * plus the top-level `capabilities` array (e.g. `['completion','vision','tools','thinking']`)
75549
+ * for multimodal / function-calling / reasoning support. Falls back to a sane
75550
+ * default so a probe failure never blocks agent budget planning.
75551
+ */
75552
+ async probeOllamaModel(base, modelId) {
75553
+ try {
75554
+ const ctrl = new AbortController();
75555
+ const tmr = setTimeout(() => ctrl.abort(), 4e3);
75556
+ const res = await fetch(`${base}/api/show`, {
75557
+ method: "POST",
75558
+ headers: { "Content-Type": "application/json" },
75559
+ body: JSON.stringify({ model: modelId }),
75560
+ signal: ctrl.signal
75561
+ });
75562
+ clearTimeout(tmr);
75563
+ if (!res.ok)
75564
+ return { contextWindow: OLLAMA_DEFAULT_CONTEXT_WINDOW, capabilities: [] };
75565
+ const data = await res.json();
75566
+ const vals = Object.entries(data.model_info ?? {}).filter(([k]) => k.toLowerCase().includes("context_length")).map(([, v]) => Number(v)).filter((n2) => Number.isFinite(n2) && n2 > 0);
75567
+ const ctx = vals.length ? Math.max(...vals) : 0;
75568
+ const capabilities = Array.isArray(data.capabilities) ? data.capabilities : [];
75569
+ if (ctx > 0)
75570
+ log57.info(`Ollama local model ${modelId}: context_window=${ctx}, capabilities=${capabilities.join(",")}`);
75571
+ return {
75572
+ contextWindow: ctx > 0 ? ctx : OLLAMA_DEFAULT_CONTEXT_WINDOW,
75573
+ capabilities
75574
+ };
75575
+ } catch {
75576
+ return { contextWindow: OLLAMA_DEFAULT_CONTEXT_WINDOW, capabilities: [] };
75577
+ }
75578
+ }
75579
+ /**
75580
+ * Pull a live /v1/models list for an OpenAI-compatible provider into its
75581
+ * custom catalog. Used for newly-added providers that have no builtin entry
75582
+ * (and therefore would otherwise show zero models in pickers). If the
75583
+ * provider already has a usable catalog (builtin/custom) we skip the call.
75584
+ */
75585
+ async refreshProviderLiveModels(providerName, baseUrl, apiKey) {
75586
+ if (this.getProviderModels(providerName).length > 0)
75587
+ return 0;
75588
+ const provider = this.providers.get(providerName);
75589
+ const effectiveBase = baseUrl ?? provider?.baseUrl;
75590
+ if (!effectiveBase)
75591
+ return 0;
75592
+ const effectiveKey = apiKey ?? provider?.apiKey;
75593
+ try {
75594
+ const headers = {};
75595
+ if (effectiveKey)
75596
+ headers["Authorization"] = `Bearer ${effectiveKey}`;
75597
+ const ctrl = new AbortController();
75598
+ const tmr = setTimeout(() => ctrl.abort(), 8e3);
75599
+ const res = await fetch(`${String(effectiveBase).replace(/\/+$/, "")}/v1/models`, {
75600
+ headers,
75601
+ signal: ctrl.signal
75602
+ });
75603
+ clearTimeout(tmr);
75604
+ if (!res.ok)
75605
+ return 0;
75606
+ const data = await res.json();
75607
+ const ids = (data.data ?? []).map((m) => m.id).filter((x) => !!x);
75608
+ if (ids.length === 0)
75609
+ return 0;
75610
+ const defs = (data.data ?? []).map((m) => {
75611
+ const id = m.id ?? "";
75612
+ const raw = m;
75613
+ const capsArr = Array.isArray(raw.capabilities) ? raw.capabilities : [];
75614
+ const capsObj = raw.capabilities ?? {};
75615
+ const arch4 = raw.architecture ?? {};
75616
+ const modalities = arch4.input_modalities ?? raw.input_modalities ?? [];
75617
+ const flagTrue = (key2) => capsObj[key2] === true || capsArr.includes(key2) || raw[key2] === true;
75618
+ const hasVision = modalities.includes("image") || flagTrue("vision");
75619
+ const hasReasoning = flagTrue("reasoning") || flagTrue("thinking");
75620
+ return {
75621
+ id,
75622
+ name: id,
75623
+ provider: providerName,
75624
+ contextWindow: 0,
75625
+ maxOutputTokens: 0,
75626
+ cost: { input: 0, output: 0 },
75627
+ reasoning: hasReasoning || void 0,
75628
+ inputTypes: hasVision ? ["text", "image"] : ["text"]
75629
+ };
75630
+ }).filter((m) => m.id.length > 0);
75631
+ this.customModelCatalog.set(providerName, defs);
75632
+ log57.info(`Synced ${defs.length} live models for provider ${providerName}`, { baseUrl: effectiveBase });
75633
+ return defs.length;
75634
+ } catch (err) {
75635
+ log57.warn(`Live model sync failed for provider ${providerName}`, { error: String(err), baseUrl: effectiveBase });
75636
+ return 0;
75637
+ }
75638
+ }
75480
75639
  /**
75481
75640
  * Drop sticky Markus defaults that are no longer in the Hub catalog
75482
75641
  * (e.g. CN geo-filter removes anthropic/claude-opus-5 while chip still shows it).
@@ -76161,7 +76320,7 @@ var init_router2 = __esm({
76161
76320
  * Used by the test endpoint to verify a single provider's connectivity.
76162
76321
  * Also returns the baseUrl used for diagnostics.
76163
76322
  */
76164
- async chatDirect(request, providerName) {
76323
+ async chatDirect(request, providerName, altModel) {
76165
76324
  const provider = this.providers.get(providerName);
76166
76325
  if (!provider) {
76167
76326
  throw new Error(`Provider "${providerName}" not registered`);
@@ -76170,9 +76329,24 @@ var init_router2 = __esm({
76170
76329
  throw new Error(`Provider "${providerName}" is disabled`);
76171
76330
  }
76172
76331
  request = this.resolveMaxTokens(request, providerName);
76173
- const response = await provider.chat(request);
76174
- const baseUrl = provider.baseUrl ?? provider.config?.baseUrl;
76175
- return Object.assign(response, { _providerBaseUrl: baseUrl });
76332
+ const originalModel = provider.model;
76333
+ const useRequestModelOnly = providerName === "markus" || provider instanceof MarkusProvider;
76334
+ const activeModel = altModel ?? originalModel;
76335
+ let switched = false;
76336
+ if (!useRequestModelOnly && altModel && altModel !== originalModel) {
76337
+ provider.configure({ provider: providerName, model: altModel });
76338
+ switched = true;
76339
+ }
76340
+ const directRequest = useRequestModelOnly && altModel ? { ...request, model: altModel } : request;
76341
+ try {
76342
+ const response = await provider.chat(directRequest);
76343
+ const baseUrl = provider.baseUrl ?? provider.config?.baseUrl;
76344
+ return Object.assign(response, { _providerBaseUrl: baseUrl, _model: activeModel });
76345
+ } finally {
76346
+ if (switched) {
76347
+ provider.configure({ provider: providerName, model: originalModel });
76348
+ }
76349
+ }
76176
76350
  }
76177
76351
  /**
76178
76352
  * Try a streaming chat on a specific provider, optionally with an alternate model.
@@ -76522,7 +76696,8 @@ var init_router2 = __esm({
76522
76696
  });
76523
76697
  const ctx = catalogEntry?.contextWindow;
76524
76698
  if (!ctx || ctx <= 0) {
76525
- throw new Error(name === "markus" ? `Cannot resolve context_window for markus model "${provider.model || "(unset)"}": it is not in the loaded Hub catalog (catalog may be empty). Please check the Hub connection and refresh the model list, or reconfigure the model. No default is substituted.` : `Cannot resolve context_window for provider "${name}" (model "${provider.model || "(unset)"}"): the model is unknown to the built-in catalog. Please configure the model or fix the model id. No default is substituted.`);
76699
+ log57.warn(`No context_window for provider "${name}" model "${provider.model || "(unset)"}" \u2014 using fallback ${DEFAULT_CONTEXT_WINDOW_FALLBACK}. Configure the model for accurate budgeting.`);
76700
+ return DEFAULT_CONTEXT_WINDOW_FALLBACK;
76526
76701
  }
76527
76702
  return ctx;
76528
76703
  }
@@ -76544,7 +76719,8 @@ var init_router2 = __esm({
76544
76719
  });
76545
76720
  const out = catalogEntry?.maxOutputTokens;
76546
76721
  if (!out || out <= 0) {
76547
- throw new Error(`Cannot resolve max_output_tokens for provider "${name}" (model "${provider.model || "(unset)"}"). The model catalog must supply a real value \u2014 refresh the Hub catalog / reconfigure the model. No default is substituted.`);
76722
+ log57.warn(`No max_output_tokens for provider "${name}" model "${provider.model || "(unset)"}" \u2014 using fallback ${DEFAULT_MAX_OUTPUT_FALLBACK}.`);
76723
+ return DEFAULT_MAX_OUTPUT_FALLBACK;
76548
76724
  }
76549
76725
  return out;
76550
76726
  }
@@ -215631,6 +215807,53 @@ EXPLANATION_END`;
215631
215807
  }
215632
215808
  return;
215633
215809
  }
215810
+ if (providerName === "ollama") {
215811
+ const ollamaUrl = process.env["OLLAMA_BASE_URL"] || "http://localhost:11434";
215812
+ const providerInstance = this.llmRouter?.getProvider("ollama");
215813
+ const configuredBaseUrl = providerInstance?.baseUrl;
215814
+ const baseUrl = configuredBaseUrl ?? ollamaUrl;
215815
+ try {
215816
+ const ctrl = new AbortController();
215817
+ const tmr = setTimeout(() => ctrl.abort(), 5e3);
215818
+ const ollamaRes = await fetch(`${String(baseUrl).replace(/\/+$/, "")}/api/tags`, { signal: ctrl.signal });
215819
+ clearTimeout(tmr);
215820
+ if (!ollamaRes.ok) {
215821
+ this.json(res, 200, { provider: "ollama", models: [], source: "error", error: `Ollama returned HTTP ${ollamaRes.status}` });
215822
+ return;
215823
+ }
215824
+ const data = await ollamaRes.json();
215825
+ const models = await Promise.all((data.models ?? []).map(async (m) => {
215826
+ const caps = await this.probeOllamaCapabilities(String(baseUrl).replace(/\/+$/, ""), m.name);
215827
+ return {
215828
+ id: m.name,
215829
+ provider: "ollama",
215830
+ mode: "chat",
215831
+ maxInputTokens: 0,
215832
+ maxOutputTokens: 0,
215833
+ inputCostPer1MTokens: 0,
215834
+ outputCostPer1MTokens: 0,
215835
+ capabilities: {
215836
+ vision: caps.includes("vision"),
215837
+ functionCalling: caps.includes("tools"),
215838
+ reasoning: caps.includes("thinking"),
215839
+ promptCaching: false,
215840
+ webSearch: false,
215841
+ audioInput: false,
215842
+ audioOutput: false
215843
+ },
215844
+ size: m.size,
215845
+ modifiedAt: m.modified_at,
215846
+ parameterSize: m.details?.parameter_size,
215847
+ family: m.details?.family,
215848
+ quantization: m.details?.quantization_level
215849
+ };
215850
+ }));
215851
+ this.json(res, 200, { provider: "ollama", models, source: "live", count: models.length });
215852
+ } catch (err) {
215853
+ this.json(res, 200, { provider: "ollama", models: [], source: "error", error: err instanceof Error ? err.message : String(err) });
215854
+ }
215855
+ return;
215856
+ }
215634
215857
  try {
215635
215858
  const providerInstance = this.llmRouter?.getProvider(providerName);
215636
215859
  const apiKey = providerInstance?.apiKey ?? "";
@@ -215677,6 +215900,10 @@ EXPLANATION_END`;
215677
215900
  }
215678
215901
  } catch {
215679
215902
  }
215903
+ try {
215904
+ await this.llmRouter.refreshOllamaLocalModels?.();
215905
+ } catch {
215906
+ }
215680
215907
  this.json(res, 200, this.llmRouter.getEnhancedSettings());
215681
215908
  return;
215682
215909
  }
@@ -216883,6 +217110,11 @@ data: ${JSON.stringify({ error: msg })}
216883
217110
  apiKey,
216884
217111
  baseUrl
216885
217112
  });
217113
+ if (name === "ollama") {
217114
+ await this.llmRouter.refreshOllamaLocalModels?.(baseUrl);
217115
+ } else {
217116
+ await this.llmRouter.refreshProviderLiveModels?.(name, baseUrl, apiKey);
217117
+ }
216886
217118
  if (enabled === false) {
216887
217119
  this.llmRouter.setProviderEnabled(name, false);
216888
217120
  }
@@ -216965,6 +217197,11 @@ data: ${JSON.stringify({ error: msg })}
216965
217197
  if (typeof enabled === "boolean") {
216966
217198
  this.llmRouter.setProviderEnabled(providerName, enabled);
216967
217199
  }
217200
+ if (providerName === "ollama") {
217201
+ await this.llmRouter.refreshOllamaLocalModels?.(baseUrl ?? void 0);
217202
+ } else {
217203
+ await this.llmRouter.refreshProviderLiveModels?.(providerName, baseUrl ?? void 0, apiKey);
217204
+ }
216968
217205
  if (contextWindow || maxOutputTokens || cost) {
216969
217206
  this.llmRouter.updateProviderModelConfig(providerName, {
216970
217207
  ...contextWindow ? { contextWindow } : {},
@@ -217245,26 +217482,30 @@ data: ${JSON.stringify({ error: msg })}
217245
217482
  this.json(res, 404, { ok: false, error: `Provider "${providerName}" not found or not configured` });
217246
217483
  return;
217247
217484
  }
217485
+ const body = await this.readBody(req);
217486
+ const requestedModel = typeof body?.model === "string" && body.model.trim() ? body.model.trim() : void 0;
217248
217487
  const requestBody = {
217249
217488
  messages: [{ role: "user", content: "Reply with exactly one word: hello" }],
217250
217489
  // No maxTokens cap: a reasoning model spends the whole budget on hidden
217251
217490
  // reasoning under a tight cap, returning empty content and producing a
217252
217491
  // false "empty response / misconfigured" result. Let the router fill in
217253
217492
  // the model's real limit; the model stops after the one-word reply.
217254
- temperature: 0
217493
+ temperature: 0,
217494
+ ...requestedModel ? { model: requestedModel } : {}
217255
217495
  };
217256
217496
  const baseUrl = provider.baseUrl ?? provider.config?.baseUrl ?? "";
217257
217497
  try {
217258
217498
  const startMs = Date.now();
217259
- const response = await this.llmRouter.chatDirect(requestBody, providerName);
217499
+ const response = await this.llmRouter.chatDirect(requestBody, providerName, requestedModel);
217260
217500
  const durationMs = Date.now() - startMs;
217261
217501
  const reply = (response.content ?? "").trim();
217262
217502
  const requestUrl = response._providerBaseUrl ?? baseUrl;
217503
+ const resultModel = response._model ?? requestedModel ?? provider.model;
217263
217504
  if (!reply) {
217264
217505
  this.json(res, 200, {
217265
217506
  ok: false,
217266
217507
  error: "Model returned empty response \u2014 API key or model may be misconfigured",
217267
- model: provider.model,
217508
+ model: resultModel,
217268
217509
  durationMs,
217269
217510
  requestUrl,
217270
217511
  requestBody
@@ -217273,7 +217514,7 @@ data: ${JSON.stringify({ error: msg })}
217273
217514
  this.json(res, 200, {
217274
217515
  ok: true,
217275
217516
  durationMs,
217276
- model: provider.model,
217517
+ model: resultModel,
217277
217518
  reply: reply.slice(0, 100),
217278
217519
  usage: response.usage,
217279
217520
  requestUrl,
@@ -217300,7 +217541,7 @@ data: ${JSON.stringify({ error: msg })}
217300
217541
  ok: false,
217301
217542
  error: errorMsg.slice(0, 500),
217302
217543
  errorCode,
217303
- model: provider.model,
217544
+ model: requestedModel ?? provider.model,
217304
217545
  requestUrl: baseUrl,
217305
217546
  requestBody
217306
217547
  });
@@ -220742,6 +220983,30 @@ You can now:
220742
220983
  failures
220743
220984
  };
220744
220985
  }
220986
+ /**
220987
+ * Probe a local Ollama model's capabilities (`vision` / `tools` / `thinking`)
220988
+ * via `/api/show`. Returns an empty array on any failure so callers can keep
220989
+ * a conservative default without breaking model listing.
220990
+ */
220991
+ async probeOllamaCapabilities(baseUrl, modelId) {
220992
+ try {
220993
+ const ctrl = new AbortController();
220994
+ const tmr = setTimeout(() => ctrl.abort(), 4e3);
220995
+ const res = await fetch(`${String(baseUrl).replace(/\/+$/, "")}/api/show`, {
220996
+ method: "POST",
220997
+ headers: { "Content-Type": "application/json" },
220998
+ body: JSON.stringify({ model: modelId }),
220999
+ signal: ctrl.signal
221000
+ });
221001
+ clearTimeout(tmr);
221002
+ if (!res.ok)
221003
+ return [];
221004
+ const data = await res.json();
221005
+ return Array.isArray(data.capabilities) ? data.capabilities : [];
221006
+ } catch {
221007
+ return [];
221008
+ }
221009
+ }
220745
221010
  async validateProviderKey(provider, apiKey, baseUrl) {
220746
221011
  const PROVIDER_BASE_URLS = {
220747
221012
  anthropic: "https://api.anthropic.com",
@@ -220833,7 +221098,9 @@ You can now:
220833
221098
  maxOutputTokens: gm.outputTokenLimit || 8192,
220834
221099
  inputCostPer1MTokens: 0,
220835
221100
  outputCostPer1MTokens: 0,
220836
- capabilities: { vision: false, functionCalling: false, reasoning: false, promptCaching: false, webSearch: false, audioInput: false, audioOutput: false }
221101
+ // Gemini `generateContent` models are multimodal (image-in capable)
221102
+ // unless they are embedding-only, which are filtered out above.
221103
+ capabilities: { vision: true, functionCalling: true, reasoning: false, promptCaching: false, webSearch: false, audioInput: false, audioOutput: false }
220837
221104
  });
220838
221105
  }
220839
221106
  }
@@ -220972,7 +221239,15 @@ You can now:
220972
221239
  inputCostPer1M = parseFloat(pricing.prompt || "0") * 1e6;
220973
221240
  outputCostPer1M = parseFloat(pricing.completion || "0") * 1e6;
220974
221241
  }
220975
- return { contextLength, maxOutput, inputCostPer1M, outputCostPer1M };
221242
+ const arch4 = remoteModel.architecture ?? {};
221243
+ const capsArr = Array.isArray(remoteModel.capabilities) ? remoteModel.capabilities : [];
221244
+ const capsObj = remoteModel.capabilities ?? {};
221245
+ const modalities = arch4.input_modalities ?? remoteModel.input_modalities ?? [];
221246
+ const flagTrue = (key2) => capsObj[key2] === true || capsArr.includes(key2) || remoteModel[key2] === true;
221247
+ const vision = modalities.includes("image") || flagTrue("vision");
221248
+ const reasoning = flagTrue("reasoning") || flagTrue("thinking");
221249
+ const functionCalling = flagTrue("function_calling") || flagTrue("functionCalling") || flagTrue("tools");
221250
+ return { contextLength, maxOutput, inputCostPer1M, outputCostPer1M, vision, reasoning, functionCalling };
220976
221251
  };
220977
221252
  let providerFallbackContext = 128e3;
220978
221253
  if (catalogModels.length > 0) {
@@ -221010,7 +221285,7 @@ You can now:
221010
221285
  maxOutputTokens: apiMeta.maxOutput || 8192,
221011
221286
  inputCostPer1MTokens: apiMeta.inputCostPer1M,
221012
221287
  outputCostPer1MTokens: apiMeta.outputCostPer1M,
221013
- capabilities: { vision: false, functionCalling: false, reasoning: false, promptCaching: false, webSearch: false, audioInput: false, audioOutput: false }
221288
+ capabilities: { vision: apiMeta.vision, functionCalling: apiMeta.functionCalling, reasoning: apiMeta.reasoning, promptCaching: false, webSearch: false, audioInput: false, audioOutput: false }
221014
221289
  });
221015
221290
  }
221016
221291
  }
@@ -1 +1 @@
1
- import{j as t,u as T,r as a,k as ee}from"./index-CcppNtga.js";const te=1920,ne=10*1024*1024,W=5,re=.8;function se(l,u,i){return new Promise((g,f)=>{const y=new Image;y.onload=()=>{let{width:s,height:r}=y;if(s<=u&&r<=u){g(l);return}s>r?(r=Math.round(r*(u/s)),s=u):(s=Math.round(s*(u/r)),r=u);const x=document.createElement("canvas");x.width=s,x.height=r;const b=x.getContext("2d");if(!b){f(new Error("Failed to get canvas context"));return}b.drawImage(y,0,0,s,r),g(x.toDataURL("image/jpeg",i))},y.onerror=()=>f(new Error("Failed to load image")),y.src=l})}function ae({candidates:l,filter:u,anchorRef:i,onSelect:g,selectedIndex:f,onIndexChange:y}){const{t:s}=T("common"),[r,x]=a.useState(null),b=a.useRef(null),w=l.filter(d=>d.name.toLowerCase().includes(u));return a.useEffect(()=>{const d=i.current;if(!d)return;const h=d.getBoundingClientRect();x({top:h.top+window.scrollY-4,left:h.left+window.scrollX+8})},[i,u]),a.useEffect(()=>{if(!b.current||!r)return;if(b.current.getBoundingClientRect().top<8){const h=i.current;if(!h)return;const c=h.getBoundingClientRect();x({top:c.bottom+window.scrollY+4,left:r.left})}},[r,i]),a.useEffect(()=>{f>=w.length&&y(Math.max(0,w.length-1))},[w.length,f,y]),a.useEffect(()=>{if(!b.current)return;b.current.querySelector('[data-selected="true"]')?.scrollIntoView({block:"nearest"})},[f]),!r||w.length===0?null:ee.createPortal(t.jsxs("div",{ref:b,style:{position:"absolute",top:r.top,left:r.left,transform:"translateY(-100%)"},className:"w-60 bg-surface-overlay border border-border-default rounded-lg shadow-xl z-[9999] max-h-48 overflow-y-auto",children:[t.jsx("div",{className:"px-2.5 py-1.5 text-[10px] text-fg-tertiary font-medium uppercase tracking-wider border-b border-border-default",children:s("commentInput.mentionHeading")}),w.map((d,h)=>t.jsxs("button",{"data-selected":h===f,onMouseDown:c=>{c.preventDefault(),g(d)},onMouseEnter:()=>y(h),className:`w-full text-left px-3 py-2 text-xs flex items-center gap-2 transition-colors ${h===f?"bg-brand-500/15 text-brand-500":"hover:bg-surface-elevated"}`,children:[t.jsx("span",{className:`w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold shrink-0 ${d.type==="human"?"bg-green-500/20 text-green-500":"bg-brand-500/20 text-brand-500"}`,children:d.name.slice(0,2).toUpperCase()}),t.jsxs("div",{className:"flex-1 min-w-0",children:[t.jsx("span",{className:"text-fg-primary font-medium",children:d.name}),t.jsx("span",{className:"text-fg-tertiary text-[10px] ml-1.5",children:d.subtitle??d.type})]}),d.type==="agent"&&t.jsx("span",{className:`w-1.5 h-1.5 rounded-full shrink-0 ${d.online?"bg-green-400":"bg-gray-500"}`})]},d.id))]}),document.body)}function oe(l){return/^\w+$/.test(l)?`@${l}`:`@[${l}]`}const K=/@\[([^\]]+)\]|@([\w\p{L}\p{N}]+)/gu;function ie(l){const u=[];let i;for(K.lastIndex=0;(i=K.exec(l))!==null;)u.push(i[1]??i[2]);return u}function ce(l,u,i){const g=[];let f=0;const y=u.map(r=>r.name).sort((r,x)=>x.length-r.length);let s=0;for(;s<l.length;){const r=l.indexOf("@",s);if(r<0){g.push(t.jsx("span",{children:l.slice(s)},f++));break}if(r>s&&g.push(t.jsx("span",{children:l.slice(s,r)},f++)),l[r+1]==="["){const c=l.indexOf("]",r+2);if(c>r+2){const m=l.slice(r+2,c),j=u.find(k=>k.name.toLowerCase()===m.toLowerCase());g.push(t.jsxs("span",{className:`text-brand-500 font-medium ${i&&j?"cursor-pointer hover:underline":""}`,onClick:i&&j?k=>i(j,k):void 0,title:j?`${j.name} (${j.role})`:void 0,children:["@",m]},f++)),s=c+1;continue}}const x=l.slice(r+1),b=x.toLowerCase(),w=y.find(c=>b.startsWith(c.toLowerCase()));if(w){const c=u.find(m=>m.name.toLowerCase()===w.toLowerCase());g.push(t.jsxs("span",{className:`text-brand-500 font-medium ${i&&c?"cursor-pointer hover:underline":""}`,onClick:i&&c?m=>i(c,m):void 0,title:c?`${c.name} (${c.role})`:void 0,children:["@",w]},f++)),s=r+1+w.length;continue}const d=/^([\w\p{L}\p{N}]+)/u,h=x.match(d);if(h){const c=h[1],m=u.find(j=>j.name.toLowerCase()===c.toLowerCase());g.push(t.jsxs("span",{className:`text-brand-500 font-medium ${i&&m?"cursor-pointer hover:underline":""}`,onClick:i&&m?j=>i(m,j):void 0,title:m?`${m.name} (${m.role})`:void 0,children:["@",c]},f++)),s=r+1+c.length;continue}g.push(t.jsx("span",{children:"@"},f++)),s=r+1}return g.length>0?g:[t.jsx("span",{children:l},0)]}function de({agents:l,humans:u,onSubmit:i,placeholder:g,replyTo:f,onCancelReply:y}){const{t:s}=T("common"),[r,x]=a.useState(""),[b,w]=a.useState(!1),[d,h]=a.useState(null),[c,m]=a.useState(!1),[j,k]=a.useState(""),[C,S]=a.useState([]),[B,I]=a.useState(0),[N,M]=a.useState([]),[z,A]=a.useState(!1),F=a.useRef(null),U=a.useRef(null),L=a.useRef(null),$=a.useMemo(()=>{const e=(u??[]).map(n=>({id:n.id,name:n.name,type:"human",subtitle:n.role}));for(const n of l)e.push({id:n.id,name:n.name,type:"agent",subtitle:n.role,online:n.status==="idle"||n.status==="working"});return e},[l,u]),D=$.filter(e=>e.name.toLowerCase().includes(j)),E=a.useCallback(e=>{const n=Array.from(e).filter(o=>o.type.startsWith("image/"));if(n.length!==0)for(const o of n){if(o.size>ne)continue;const p=new FileReader;p.onload=()=>{const R=p.result;se(R,te,re).then(v=>{M(_=>_.length>=W?_:[..._,{id:`img_${Date.now()}_${Math.random().toString(36).slice(2,6)}`,dataUrl:v,name:o.name}])})},p.readAsDataURL(o)}},[]),X=a.useCallback(e=>{M(n=>n.filter(o=>o.id!==e))},[]),Y=a.useCallback(e=>{const n=e.clipboardData?.files;if(n&&n.length>0){const o=Array.from(n).filter(p=>p.type.startsWith("image/"));o.length>0&&(e.preventDefault(),E(o))}},[E]),q=a.useCallback(e=>{e.preventDefault(),A(!1);const n=e.dataTransfer?.files;n&&n.length>0&&E(Array.from(n).filter(o=>o.type.startsWith("image/")))},[E]),G=a.useCallback(e=>{e.preventDefault(),A(!0)},[]),H=a.useCallback(()=>{A(!1)},[]),O=async()=>{if(!(!r.trim()&&N.length===0)){w(!0),h(null),L.current={content:r,mentions:[...C],images:[...N]};try{await i(r,C,[...N],f?.id),x(""),S([]),M([]),L.current=null,y?.()}catch(e){h(String(e instanceof Error?e.message:e)||s("commentInput.sendFailed"))}w(!1)}},Q=async()=>{if(!L.current)return;const{content:e,mentions:n,images:o}=L.current;w(!0),h(null);try{await i(e,n,o),x(""),S([]),M([]),L.current=null}catch(p){h(String(p instanceof Error?p.message:p)||s("commentInput.sendFailed"))}w(!1)},P=a.useCallback(e=>{x(n=>{const o=n.lastIndexOf("@"),p=oe(e.name);return o>=0?n.slice(0,o)+p+" ":n+p+" "}),C.includes(e.id)||S(n=>[...n,e.id]),m(!1),k(""),I(0),F.current?.focus()},[C]),V=e=>{x(e),h(null);const n=e.lastIndexOf("@");if(n>=0&&(n===0||e[n-1]===" ")){const p=e.slice(n+1).toLowerCase();if($.some(v=>v.name.toLowerCase().includes(p))){k(p),m(!0),I(0);return}}m(!1)},Z=e=>{if(c&&D.length>0){const n=e.key==="ArrowUp"||e.ctrlKey&&e.key==="p",o=e.key==="ArrowDown"||e.ctrlKey&&e.key==="n",p=e.key==="Enter"||e.key==="Tab",R=e.key==="Escape";if(n){e.preventDefault(),I(v=>(v-1+D.length)%D.length);return}if(o){e.preventDefault(),I(v=>(v+1)%D.length);return}if(p){e.preventDefault();const v=D[B];v&&P(v);return}if(R){e.preventDefault(),m(!1);return}}e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),O())},J=a.useCallback(()=>{setTimeout(()=>m(!1),200)},[]);return t.jsxs("div",{className:"relative",onDragOver:G,onDragLeave:H,onDrop:q,children:[z&&t.jsx("div",{className:"absolute inset-0 z-50 flex items-center justify-center bg-brand-500/10 rounded-lg border-2 border-dashed border-brand-500",children:t.jsx("span",{className:"text-sm text-brand-500 font-medium",children:s("commentInput.dragDropHint")})}),c&&t.jsx(ae,{candidates:$,filter:j,anchorRef:F,onSelect:P,selectedIndex:B,onIndexChange:I}),t.jsxs("div",{className:"border border-border-default rounded-lg bg-surface-elevated",children:[f&&t.jsxs("div",{className:"flex items-center gap-2 px-2.5 pt-2 pb-1 border-b border-border-default/50",children:[t.jsxs("div",{className:"flex-1 min-w-0 pl-2 border-l-2 border-brand-500/50",children:[t.jsx("span",{className:"text-[10px] font-medium text-brand-500",children:f.authorName}),t.jsx("p",{className:"text-[10px] text-fg-tertiary truncate",children:f.content})]}),t.jsx("button",{onClick:y,className:"text-fg-tertiary hover:text-fg-secondary shrink-0 p-0.5",children:t.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[t.jsx("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),t.jsx("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]})})]}),C.length>0&&t.jsx("div",{className:"flex gap-1 px-2.5 pt-2 flex-wrap",children:C.map(e=>{const n=$.find(o=>o.id===e);return t.jsxs("span",{className:`inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] rounded-full ${n?.type==="human"?"bg-green-500/15 text-green-500":"bg-brand-500/15 text-brand-500"}`,children:["@",n?.name??e,t.jsx("button",{onClick:()=>S(o=>o.filter(p=>p!==e)),className:"hover:text-red-400",children:"×"})]},e)})}),t.jsx("textarea",{ref:F,value:r,onChange:e=>V(e.target.value),onKeyDown:Z,onBlur:J,onPaste:Y,placeholder:g??s("commentInput.placeholder"),rows:2,className:"w-full px-2.5 py-2 text-xs bg-transparent text-fg-primary placeholder-fg-tertiary outline-none resize-none"}),N.length>0&&t.jsx("div",{className:"flex items-center gap-1.5 px-2.5 pb-1.5 overflow-x-auto",children:N.map(e=>t.jsxs("div",{className:"relative group/img shrink-0",children:[t.jsx("img",{src:e.dataUrl,alt:e.name,className:"w-12 h-12 rounded-lg object-cover border border-border-default"}),t.jsx("button",{onClick:()=>X(e.id),className:"absolute -top-1.5 -right-1.5 w-4 h-4 bg-surface-secondary border border-gray-600 rounded-full flex items-center justify-center text-fg-secondary hover:text-red-500 hover:border-red-500 text-[10px] opacity-0 group-hover/img:opacity-100 transition-opacity",title:s("commentInput.removeImage"),children:"×"})]},e.id))}),d&&t.jsxs("div",{className:"flex items-center gap-2 px-2.5 pb-1.5",children:[t.jsx("span",{className:"text-[11px] text-red-400 flex-1 truncate",children:d}),t.jsxs("button",{onClick:Q,disabled:b,className:"text-[11px] text-amber-500 hover:text-amber-400 font-medium transition-colors flex items-center gap-1",children:[t.jsxs("svg",{className:"w-3 h-3",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[t.jsx("polyline",{points:"23 4 23 10 17 10"}),t.jsx("path",{d:"M20.49 15a9 9 0 11-2.12-9.36L23 10"})]}),s("commentInput.retry")]})]}),t.jsxs("div",{className:"flex items-center justify-between px-2.5 pb-2",children:[t.jsx("button",{type:"button",onClick:()=>U.current?.click(),disabled:b||N.length>=W,className:"p-1 text-fg-tertiary hover:text-brand-500 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",title:s("commentInput.attachImage"),children:t.jsxs("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[t.jsx("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),t.jsx("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),t.jsx("polyline",{points:"21 15 16 10 5 21"})]})}),t.jsx("input",{ref:U,type:"file",accept:"image/*",multiple:!0,className:"hidden",onChange:e=>{e.target.files&&E(e.target.files),e.target.value=""}}),t.jsx("button",{onClick:O,disabled:!r.trim()&&N.length===0||b,className:"px-3 py-1 text-[11px] font-medium bg-brand-500 text-white rounded-md hover:bg-brand-600 disabled:opacity-40 disabled:cursor-not-allowed transition-colors",children:s(b?"commentInput.sending":"commentInput.submit")})]})]}),t.jsx("p",{className:"mt-1 text-center text-[10px] text-fg-tertiary/80 select-none",children:s("aiDisclaimer")})]})}export{de as C,ie as p,ce as r};
1
+ import{j as t,u as T,r as a,k as ee}from"./index-B-xjev59.js";const te=1920,ne=10*1024*1024,W=5,re=.8;function se(l,u,i){return new Promise((g,f)=>{const y=new Image;y.onload=()=>{let{width:s,height:r}=y;if(s<=u&&r<=u){g(l);return}s>r?(r=Math.round(r*(u/s)),s=u):(s=Math.round(s*(u/r)),r=u);const x=document.createElement("canvas");x.width=s,x.height=r;const b=x.getContext("2d");if(!b){f(new Error("Failed to get canvas context"));return}b.drawImage(y,0,0,s,r),g(x.toDataURL("image/jpeg",i))},y.onerror=()=>f(new Error("Failed to load image")),y.src=l})}function ae({candidates:l,filter:u,anchorRef:i,onSelect:g,selectedIndex:f,onIndexChange:y}){const{t:s}=T("common"),[r,x]=a.useState(null),b=a.useRef(null),w=l.filter(d=>d.name.toLowerCase().includes(u));return a.useEffect(()=>{const d=i.current;if(!d)return;const h=d.getBoundingClientRect();x({top:h.top+window.scrollY-4,left:h.left+window.scrollX+8})},[i,u]),a.useEffect(()=>{if(!b.current||!r)return;if(b.current.getBoundingClientRect().top<8){const h=i.current;if(!h)return;const c=h.getBoundingClientRect();x({top:c.bottom+window.scrollY+4,left:r.left})}},[r,i]),a.useEffect(()=>{f>=w.length&&y(Math.max(0,w.length-1))},[w.length,f,y]),a.useEffect(()=>{if(!b.current)return;b.current.querySelector('[data-selected="true"]')?.scrollIntoView({block:"nearest"})},[f]),!r||w.length===0?null:ee.createPortal(t.jsxs("div",{ref:b,style:{position:"absolute",top:r.top,left:r.left,transform:"translateY(-100%)"},className:"w-60 bg-surface-overlay border border-border-default rounded-lg shadow-xl z-[9999] max-h-48 overflow-y-auto",children:[t.jsx("div",{className:"px-2.5 py-1.5 text-[10px] text-fg-tertiary font-medium uppercase tracking-wider border-b border-border-default",children:s("commentInput.mentionHeading")}),w.map((d,h)=>t.jsxs("button",{"data-selected":h===f,onMouseDown:c=>{c.preventDefault(),g(d)},onMouseEnter:()=>y(h),className:`w-full text-left px-3 py-2 text-xs flex items-center gap-2 transition-colors ${h===f?"bg-brand-500/15 text-brand-500":"hover:bg-surface-elevated"}`,children:[t.jsx("span",{className:`w-6 h-6 rounded-full flex items-center justify-center text-[10px] font-bold shrink-0 ${d.type==="human"?"bg-green-500/20 text-green-500":"bg-brand-500/20 text-brand-500"}`,children:d.name.slice(0,2).toUpperCase()}),t.jsxs("div",{className:"flex-1 min-w-0",children:[t.jsx("span",{className:"text-fg-primary font-medium",children:d.name}),t.jsx("span",{className:"text-fg-tertiary text-[10px] ml-1.5",children:d.subtitle??d.type})]}),d.type==="agent"&&t.jsx("span",{className:`w-1.5 h-1.5 rounded-full shrink-0 ${d.online?"bg-green-400":"bg-gray-500"}`})]},d.id))]}),document.body)}function oe(l){return/^\w+$/.test(l)?`@${l}`:`@[${l}]`}const K=/@\[([^\]]+)\]|@([\w\p{L}\p{N}]+)/gu;function ie(l){const u=[];let i;for(K.lastIndex=0;(i=K.exec(l))!==null;)u.push(i[1]??i[2]);return u}function ce(l,u,i){const g=[];let f=0;const y=u.map(r=>r.name).sort((r,x)=>x.length-r.length);let s=0;for(;s<l.length;){const r=l.indexOf("@",s);if(r<0){g.push(t.jsx("span",{children:l.slice(s)},f++));break}if(r>s&&g.push(t.jsx("span",{children:l.slice(s,r)},f++)),l[r+1]==="["){const c=l.indexOf("]",r+2);if(c>r+2){const m=l.slice(r+2,c),j=u.find(k=>k.name.toLowerCase()===m.toLowerCase());g.push(t.jsxs("span",{className:`text-brand-500 font-medium ${i&&j?"cursor-pointer hover:underline":""}`,onClick:i&&j?k=>i(j,k):void 0,title:j?`${j.name} (${j.role})`:void 0,children:["@",m]},f++)),s=c+1;continue}}const x=l.slice(r+1),b=x.toLowerCase(),w=y.find(c=>b.startsWith(c.toLowerCase()));if(w){const c=u.find(m=>m.name.toLowerCase()===w.toLowerCase());g.push(t.jsxs("span",{className:`text-brand-500 font-medium ${i&&c?"cursor-pointer hover:underline":""}`,onClick:i&&c?m=>i(c,m):void 0,title:c?`${c.name} (${c.role})`:void 0,children:["@",w]},f++)),s=r+1+w.length;continue}const d=/^([\w\p{L}\p{N}]+)/u,h=x.match(d);if(h){const c=h[1],m=u.find(j=>j.name.toLowerCase()===c.toLowerCase());g.push(t.jsxs("span",{className:`text-brand-500 font-medium ${i&&m?"cursor-pointer hover:underline":""}`,onClick:i&&m?j=>i(m,j):void 0,title:m?`${m.name} (${m.role})`:void 0,children:["@",c]},f++)),s=r+1+c.length;continue}g.push(t.jsx("span",{children:"@"},f++)),s=r+1}return g.length>0?g:[t.jsx("span",{children:l},0)]}function de({agents:l,humans:u,onSubmit:i,placeholder:g,replyTo:f,onCancelReply:y}){const{t:s}=T("common"),[r,x]=a.useState(""),[b,w]=a.useState(!1),[d,h]=a.useState(null),[c,m]=a.useState(!1),[j,k]=a.useState(""),[C,S]=a.useState([]),[B,I]=a.useState(0),[N,M]=a.useState([]),[z,A]=a.useState(!1),F=a.useRef(null),U=a.useRef(null),L=a.useRef(null),$=a.useMemo(()=>{const e=(u??[]).map(n=>({id:n.id,name:n.name,type:"human",subtitle:n.role}));for(const n of l)e.push({id:n.id,name:n.name,type:"agent",subtitle:n.role,online:n.status==="idle"||n.status==="working"});return e},[l,u]),D=$.filter(e=>e.name.toLowerCase().includes(j)),E=a.useCallback(e=>{const n=Array.from(e).filter(o=>o.type.startsWith("image/"));if(n.length!==0)for(const o of n){if(o.size>ne)continue;const p=new FileReader;p.onload=()=>{const R=p.result;se(R,te,re).then(v=>{M(_=>_.length>=W?_:[..._,{id:`img_${Date.now()}_${Math.random().toString(36).slice(2,6)}`,dataUrl:v,name:o.name}])})},p.readAsDataURL(o)}},[]),X=a.useCallback(e=>{M(n=>n.filter(o=>o.id!==e))},[]),Y=a.useCallback(e=>{const n=e.clipboardData?.files;if(n&&n.length>0){const o=Array.from(n).filter(p=>p.type.startsWith("image/"));o.length>0&&(e.preventDefault(),E(o))}},[E]),q=a.useCallback(e=>{e.preventDefault(),A(!1);const n=e.dataTransfer?.files;n&&n.length>0&&E(Array.from(n).filter(o=>o.type.startsWith("image/")))},[E]),G=a.useCallback(e=>{e.preventDefault(),A(!0)},[]),H=a.useCallback(()=>{A(!1)},[]),O=async()=>{if(!(!r.trim()&&N.length===0)){w(!0),h(null),L.current={content:r,mentions:[...C],images:[...N]};try{await i(r,C,[...N],f?.id),x(""),S([]),M([]),L.current=null,y?.()}catch(e){h(String(e instanceof Error?e.message:e)||s("commentInput.sendFailed"))}w(!1)}},Q=async()=>{if(!L.current)return;const{content:e,mentions:n,images:o}=L.current;w(!0),h(null);try{await i(e,n,o),x(""),S([]),M([]),L.current=null}catch(p){h(String(p instanceof Error?p.message:p)||s("commentInput.sendFailed"))}w(!1)},P=a.useCallback(e=>{x(n=>{const o=n.lastIndexOf("@"),p=oe(e.name);return o>=0?n.slice(0,o)+p+" ":n+p+" "}),C.includes(e.id)||S(n=>[...n,e.id]),m(!1),k(""),I(0),F.current?.focus()},[C]),V=e=>{x(e),h(null);const n=e.lastIndexOf("@");if(n>=0&&(n===0||e[n-1]===" ")){const p=e.slice(n+1).toLowerCase();if($.some(v=>v.name.toLowerCase().includes(p))){k(p),m(!0),I(0);return}}m(!1)},Z=e=>{if(c&&D.length>0){const n=e.key==="ArrowUp"||e.ctrlKey&&e.key==="p",o=e.key==="ArrowDown"||e.ctrlKey&&e.key==="n",p=e.key==="Enter"||e.key==="Tab",R=e.key==="Escape";if(n){e.preventDefault(),I(v=>(v-1+D.length)%D.length);return}if(o){e.preventDefault(),I(v=>(v+1)%D.length);return}if(p){e.preventDefault();const v=D[B];v&&P(v);return}if(R){e.preventDefault(),m(!1);return}}e.key==="Enter"&&!e.shiftKey&&(e.preventDefault(),O())},J=a.useCallback(()=>{setTimeout(()=>m(!1),200)},[]);return t.jsxs("div",{className:"relative",onDragOver:G,onDragLeave:H,onDrop:q,children:[z&&t.jsx("div",{className:"absolute inset-0 z-50 flex items-center justify-center bg-brand-500/10 rounded-lg border-2 border-dashed border-brand-500",children:t.jsx("span",{className:"text-sm text-brand-500 font-medium",children:s("commentInput.dragDropHint")})}),c&&t.jsx(ae,{candidates:$,filter:j,anchorRef:F,onSelect:P,selectedIndex:B,onIndexChange:I}),t.jsxs("div",{className:"border border-border-default rounded-lg bg-surface-elevated",children:[f&&t.jsxs("div",{className:"flex items-center gap-2 px-2.5 pt-2 pb-1 border-b border-border-default/50",children:[t.jsxs("div",{className:"flex-1 min-w-0 pl-2 border-l-2 border-brand-500/50",children:[t.jsx("span",{className:"text-[10px] font-medium text-brand-500",children:f.authorName}),t.jsx("p",{className:"text-[10px] text-fg-tertiary truncate",children:f.content})]}),t.jsx("button",{onClick:y,className:"text-fg-tertiary hover:text-fg-secondary shrink-0 p-0.5",children:t.jsxs("svg",{width:"12",height:"12",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[t.jsx("line",{x1:"18",y1:"6",x2:"6",y2:"18"}),t.jsx("line",{x1:"6",y1:"6",x2:"18",y2:"18"})]})})]}),C.length>0&&t.jsx("div",{className:"flex gap-1 px-2.5 pt-2 flex-wrap",children:C.map(e=>{const n=$.find(o=>o.id===e);return t.jsxs("span",{className:`inline-flex items-center gap-1 px-1.5 py-0.5 text-[10px] rounded-full ${n?.type==="human"?"bg-green-500/15 text-green-500":"bg-brand-500/15 text-brand-500"}`,children:["@",n?.name??e,t.jsx("button",{onClick:()=>S(o=>o.filter(p=>p!==e)),className:"hover:text-red-400",children:"×"})]},e)})}),t.jsx("textarea",{ref:F,value:r,onChange:e=>V(e.target.value),onKeyDown:Z,onBlur:J,onPaste:Y,placeholder:g??s("commentInput.placeholder"),rows:2,className:"w-full px-2.5 py-2 text-xs bg-transparent text-fg-primary placeholder-fg-tertiary outline-none resize-none"}),N.length>0&&t.jsx("div",{className:"flex items-center gap-1.5 px-2.5 pb-1.5 overflow-x-auto",children:N.map(e=>t.jsxs("div",{className:"relative group/img shrink-0",children:[t.jsx("img",{src:e.dataUrl,alt:e.name,className:"w-12 h-12 rounded-lg object-cover border border-border-default"}),t.jsx("button",{onClick:()=>X(e.id),className:"absolute -top-1.5 -right-1.5 w-4 h-4 bg-surface-secondary border border-gray-600 rounded-full flex items-center justify-center text-fg-secondary hover:text-red-500 hover:border-red-500 text-[10px] opacity-0 group-hover/img:opacity-100 transition-opacity",title:s("commentInput.removeImage"),children:"×"})]},e.id))}),d&&t.jsxs("div",{className:"flex items-center gap-2 px-2.5 pb-1.5",children:[t.jsx("span",{className:"text-[11px] text-red-400 flex-1 truncate",children:d}),t.jsxs("button",{onClick:Q,disabled:b,className:"text-[11px] text-amber-500 hover:text-amber-400 font-medium transition-colors flex items-center gap-1",children:[t.jsxs("svg",{className:"w-3 h-3",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[t.jsx("polyline",{points:"23 4 23 10 17 10"}),t.jsx("path",{d:"M20.49 15a9 9 0 11-2.12-9.36L23 10"})]}),s("commentInput.retry")]})]}),t.jsxs("div",{className:"flex items-center justify-between px-2.5 pb-2",children:[t.jsx("button",{type:"button",onClick:()=>U.current?.click(),disabled:b||N.length>=W,className:"p-1 text-fg-tertiary hover:text-brand-500 disabled:opacity-30 disabled:cursor-not-allowed transition-colors",title:s("commentInput.attachImage"),children:t.jsxs("svg",{className:"w-4 h-4",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",strokeLinecap:"round",strokeLinejoin:"round",children:[t.jsx("rect",{x:"3",y:"3",width:"18",height:"18",rx:"2",ry:"2"}),t.jsx("circle",{cx:"8.5",cy:"8.5",r:"1.5"}),t.jsx("polyline",{points:"21 15 16 10 5 21"})]})}),t.jsx("input",{ref:U,type:"file",accept:"image/*",multiple:!0,className:"hidden",onChange:e=>{e.target.files&&E(e.target.files),e.target.value=""}}),t.jsx("button",{onClick:O,disabled:!r.trim()&&N.length===0||b,className:"px-3 py-1 text-[11px] font-medium bg-brand-500 text-white rounded-md hover:bg-brand-600 disabled:opacity-40 disabled:cursor-not-allowed transition-colors",children:s(b?"commentInput.sending":"commentInput.submit")})]})]}),t.jsx("p",{className:"mt-1 text-center text-[10px] text-fg-tertiary/80 select-none",children:s("aiDisclaimer")})]})}export{de as C,ie as p,ce as r};
@@ -1,5 +1,5 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-CcppNtga.js","assets/index-BbQpGkiM.css"])))=>i.map(i=>d[i]);
2
- import{j as a,r as i,_ as f}from"./index-CcppNtga.js";const x=i.lazy(()=>f(()=>import("./index-CcppNtga.js").then(e=>e.aE),__vite__mapDeps([0,1])).then(e=>({default:e.MarkdownMessage}))),b=new Set(["ts","tsx","js","jsx","mjs","cjs","py","go","rs","java","kt","kts","c","cc","cpp","cxx","h","hpp","hxx","cs","rb","php","swift","scala","sh","bash","zsh","fish","ps1","sql","yaml","yml","toml","xml","css","scss","less","vue","svelte","graphql","gql","proto","dockerfile","makefile","cmake","rsx","r","lua","pl","pm","ex","exs","erl","hs","clj","zig","nim","dart","gradle","tf","hcl"]);function v(e){const t=e.split(/[/\\]/).pop()?.toLowerCase()||"";if(t==="dockerfile"||t==="makefile")return"code";const r=t.includes(".")?t.split(".").pop():"";switch(r){case"md":case"markdown":return"markdown";case"html":case"htm":return"html";case"json":case"jsonc":return"json";case"csv":case"tsv":return"csv";default:return r&&b.has(r)?"code":r?"text":void 0}}function w(e){const t=e.trimStart();if(/^<!doctype\s+html/i.test(t)||/^<html[\s>]/i.test(t))return"html";if(t.startsWith("{")||t.startsWith("["))try{return JSON.parse(e),"json"}catch{}}function S(e){if(e.format)return e.format;if(e.reference){const t=v(e.reference);if(t)return t}if(e.content){const t=w(e.content);if(t)return t}return e.reference?"text":"markdown"}function y({content:e,className:t,onSelection:r}){const c=i.useRef(null),[l,p]=i.useState(null);i.useEffect(()=>{const o=d=>{d.data?.type==="__htmlpreview_height"&&typeof d.data.height=="number"&&p(d.data.height),d.data?.type==="__htmlpreview_selection"&&r&&r(d.data)};return window.addEventListener("message",o),()=>window.removeEventListener("message",o)},[r]);const h=`<script>
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-B-xjev59.js","assets/index-BbQpGkiM.css"])))=>i.map(i=>d[i]);
2
+ import{j as a,r as i,_ as f}from"./index-B-xjev59.js";const x=i.lazy(()=>f(()=>import("./index-B-xjev59.js").then(e=>e.aE),__vite__mapDeps([0,1])).then(e=>({default:e.MarkdownMessage}))),b=new Set(["ts","tsx","js","jsx","mjs","cjs","py","go","rs","java","kt","kts","c","cc","cpp","cxx","h","hpp","hxx","cs","rb","php","swift","scala","sh","bash","zsh","fish","ps1","sql","yaml","yml","toml","xml","css","scss","less","vue","svelte","graphql","gql","proto","dockerfile","makefile","cmake","rsx","r","lua","pl","pm","ex","exs","erl","hs","clj","zig","nim","dart","gradle","tf","hcl"]);function v(e){const t=e.split(/[/\\]/).pop()?.toLowerCase()||"";if(t==="dockerfile"||t==="makefile")return"code";const r=t.includes(".")?t.split(".").pop():"";switch(r){case"md":case"markdown":return"markdown";case"html":case"htm":return"html";case"json":case"jsonc":return"json";case"csv":case"tsv":return"csv";default:return r&&b.has(r)?"code":r?"text":void 0}}function w(e){const t=e.trimStart();if(/^<!doctype\s+html/i.test(t)||/^<html[\s>]/i.test(t))return"html";if(t.startsWith("{")||t.startsWith("["))try{return JSON.parse(e),"json"}catch{}}function S(e){if(e.format)return e.format;if(e.reference){const t=v(e.reference);if(t)return t}if(e.content){const t=w(e.content);if(t)return t}return e.reference?"text":"markdown"}function y({content:e,className:t,onSelection:r}){const c=i.useRef(null),[l,p]=i.useState(null);i.useEffect(()=>{const o=d=>{d.data?.type==="__htmlpreview_height"&&typeof d.data.height=="number"&&p(d.data.height),d.data?.type==="__htmlpreview_selection"&&r&&r(d.data)};return window.addEventListener("message",o),()=>window.removeEventListener("message",o)},[r]);const h=`<script>
3
3
  (function(){
4
4
  function getXPath(el){
5
5
  if(!el||el.nodeType!==1)return'';