@aituber-onair/chat 0.37.0 → 0.39.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 (89) hide show
  1. package/README.ja.md +47 -1
  2. package/README.md +46 -1
  3. package/dist/cjs/constants/openai.d.ts +2 -1
  4. package/dist/cjs/constants/openai.d.ts.map +1 -1
  5. package/dist/cjs/constants/openai.js +3 -4
  6. package/dist/cjs/constants/openai.js.map +1 -1
  7. package/dist/cjs/constants/openrouter.d.ts +1 -0
  8. package/dist/cjs/constants/openrouter.d.ts.map +1 -1
  9. package/dist/cjs/constants/openrouter.js +2 -1
  10. package/dist/cjs/constants/openrouter.js.map +1 -1
  11. package/dist/cjs/services/providers/gemini/GeminiChatService.d.ts +5 -0
  12. package/dist/cjs/services/providers/gemini/GeminiChatService.d.ts.map +1 -1
  13. package/dist/cjs/services/providers/gemini/GeminiChatService.js +22 -6
  14. package/dist/cjs/services/providers/gemini/GeminiChatService.js.map +1 -1
  15. package/dist/cjs/services/providers/openai/OpenAIChatServiceProvider.d.ts +6 -0
  16. package/dist/cjs/services/providers/openai/OpenAIChatServiceProvider.d.ts.map +1 -1
  17. package/dist/cjs/services/providers/openai/OpenAIChatServiceProvider.js +17 -7
  18. package/dist/cjs/services/providers/openai/OpenAIChatServiceProvider.js.map +1 -1
  19. package/dist/cjs/services/providers/openai/responsesParser.d.ts +7 -2
  20. package/dist/cjs/services/providers/openai/responsesParser.d.ts.map +1 -1
  21. package/dist/cjs/services/providers/openai/responsesParser.js +9 -7
  22. package/dist/cjs/services/providers/openai/responsesParser.js.map +1 -1
  23. package/dist/cjs/services/providers/openrouter/OpenRouterChatService.d.ts.map +1 -1
  24. package/dist/cjs/services/providers/openrouter/OpenRouterChatService.js +7 -5
  25. package/dist/cjs/services/providers/openrouter/OpenRouterChatService.js.map +1 -1
  26. package/dist/cjs/services/providers/openrouter/OpenRouterChatServiceProvider.d.ts.map +1 -1
  27. package/dist/cjs/services/providers/openrouter/OpenRouterChatServiceProvider.js +1 -0
  28. package/dist/cjs/services/providers/openrouter/OpenRouterChatServiceProvider.js.map +1 -1
  29. package/dist/cjs/utils/index.d.ts +1 -0
  30. package/dist/cjs/utils/index.d.ts.map +1 -1
  31. package/dist/cjs/utils/index.js +1 -0
  32. package/dist/cjs/utils/index.js.map +1 -1
  33. package/dist/cjs/utils/mcpSchemaFetcher.d.ts +20 -0
  34. package/dist/cjs/utils/mcpSchemaFetcher.d.ts.map +1 -1
  35. package/dist/cjs/utils/mcpSchemaFetcher.js +60 -44
  36. package/dist/cjs/utils/mcpSchemaFetcher.js.map +1 -1
  37. package/dist/cjs/utils/openaiCompatibleSse.d.ts +3 -2
  38. package/dist/cjs/utils/openaiCompatibleSse.d.ts.map +1 -1
  39. package/dist/cjs/utils/openaiCompatibleSse.js +4 -3
  40. package/dist/cjs/utils/openaiCompatibleSse.js.map +1 -1
  41. package/dist/cjs/utils/safeJsonParse.d.ts +4 -0
  42. package/dist/cjs/utils/safeJsonParse.d.ts.map +1 -0
  43. package/dist/cjs/utils/safeJsonParse.js +21 -0
  44. package/dist/cjs/utils/safeJsonParse.js.map +1 -0
  45. package/dist/esm/constants/openai.d.ts +2 -1
  46. package/dist/esm/constants/openai.d.ts.map +1 -1
  47. package/dist/esm/constants/openai.js +3 -4
  48. package/dist/esm/constants/openai.js.map +1 -1
  49. package/dist/esm/constants/openrouter.d.ts +1 -0
  50. package/dist/esm/constants/openrouter.d.ts.map +1 -1
  51. package/dist/esm/constants/openrouter.js +1 -0
  52. package/dist/esm/constants/openrouter.js.map +1 -1
  53. package/dist/esm/services/providers/gemini/GeminiChatService.d.ts +5 -0
  54. package/dist/esm/services/providers/gemini/GeminiChatService.d.ts.map +1 -1
  55. package/dist/esm/services/providers/gemini/GeminiChatService.js +22 -6
  56. package/dist/esm/services/providers/gemini/GeminiChatService.js.map +1 -1
  57. package/dist/esm/services/providers/openai/OpenAIChatServiceProvider.d.ts +6 -0
  58. package/dist/esm/services/providers/openai/OpenAIChatServiceProvider.d.ts.map +1 -1
  59. package/dist/esm/services/providers/openai/OpenAIChatServiceProvider.js +17 -7
  60. package/dist/esm/services/providers/openai/OpenAIChatServiceProvider.js.map +1 -1
  61. package/dist/esm/services/providers/openai/responsesParser.d.ts +7 -2
  62. package/dist/esm/services/providers/openai/responsesParser.d.ts.map +1 -1
  63. package/dist/esm/services/providers/openai/responsesParser.js +9 -7
  64. package/dist/esm/services/providers/openai/responsesParser.js.map +1 -1
  65. package/dist/esm/services/providers/openrouter/OpenRouterChatService.d.ts.map +1 -1
  66. package/dist/esm/services/providers/openrouter/OpenRouterChatService.js +7 -5
  67. package/dist/esm/services/providers/openrouter/OpenRouterChatService.js.map +1 -1
  68. package/dist/esm/services/providers/openrouter/OpenRouterChatServiceProvider.d.ts.map +1 -1
  69. package/dist/esm/services/providers/openrouter/OpenRouterChatServiceProvider.js +2 -1
  70. package/dist/esm/services/providers/openrouter/OpenRouterChatServiceProvider.js.map +1 -1
  71. package/dist/esm/utils/index.d.ts +1 -0
  72. package/dist/esm/utils/index.d.ts.map +1 -1
  73. package/dist/esm/utils/index.js +1 -0
  74. package/dist/esm/utils/index.js.map +1 -1
  75. package/dist/esm/utils/mcpSchemaFetcher.d.ts +20 -0
  76. package/dist/esm/utils/mcpSchemaFetcher.d.ts.map +1 -1
  77. package/dist/esm/utils/mcpSchemaFetcher.js +60 -44
  78. package/dist/esm/utils/mcpSchemaFetcher.js.map +1 -1
  79. package/dist/esm/utils/openaiCompatibleSse.d.ts +3 -2
  80. package/dist/esm/utils/openaiCompatibleSse.d.ts.map +1 -1
  81. package/dist/esm/utils/openaiCompatibleSse.js +4 -3
  82. package/dist/esm/utils/openaiCompatibleSse.js.map +1 -1
  83. package/dist/esm/utils/safeJsonParse.d.ts +4 -0
  84. package/dist/esm/utils/safeJsonParse.d.ts.map +1 -0
  85. package/dist/esm/utils/safeJsonParse.js +16 -0
  86. package/dist/esm/utils/safeJsonParse.js.map +1 -0
  87. package/dist/umd/aituber-onair-chat.js +145 -72
  88. package/dist/umd/aituber-onair-chat.min.js +10 -10
  89. package/package.json +1 -1
@@ -163,6 +163,7 @@ var AITuberOnAirChat = (() => {
163
163
  MODEL_OPENAI_GPT_LATEST: () => MODEL_OPENAI_GPT_LATEST,
164
164
  MODEL_OPENAI_GPT_MINI_LATEST: () => MODEL_OPENAI_GPT_MINI_LATEST,
165
165
  MODEL_OPENROUTER_AUTO: () => MODEL_OPENROUTER_AUTO,
166
+ MODEL_OPENROUTER_FUSION: () => MODEL_OPENROUTER_FUSION,
166
167
  MODEL_ZAI_GLM_4_5_AIR: () => MODEL_ZAI_GLM_4_5_AIR,
167
168
  MODEL_ZAI_GLM_4_5_AIR_FREE: () => MODEL_ZAI_GLM_4_5_AIR_FREE,
168
169
  MODEL_ZAI_GLM_4_7_FLASH: () => MODEL_ZAI_GLM_4_7_FLASH,
@@ -213,6 +214,8 @@ var AITuberOnAirChat = (() => {
213
214
  refreshOpenRouterFreeModels: () => refreshOpenRouterFreeModels,
214
215
  resolveVisionModel: () => resolveVisionModel,
215
216
  runOnceText: () => runOnceText,
217
+ safeJsonParse: () => safeJsonParse,
218
+ safeParseToolCallInput: () => safeParseToolCallInput,
216
219
  screenplayToText: () => screenplayToText,
217
220
  textToScreenplay: () => textToScreenplay,
218
221
  textsToScreenplay: () => textsToScreenplay
@@ -286,7 +289,7 @@ var AITuberOnAirChat = (() => {
286
289
  return model !== MODEL_GPT_5_4_PRO;
287
290
  }
288
291
  function getDefaultReasoningEffortForGPT5Model(model) {
289
- if (model === MODEL_GPT_5_1 || model === MODEL_GPT_5_4 || model === MODEL_GPT_5_5) {
292
+ if (allowsReasoningNone(model)) {
290
293
  return "none";
291
294
  }
292
295
  return "medium";
@@ -358,6 +361,7 @@ var AITuberOnAirChat = (() => {
358
361
  // src/constants/openrouter.ts
359
362
  var ENDPOINT_OPENROUTER_API = "https://openrouter.ai/api/v1/chat/completions";
360
363
  var MODEL_OPENROUTER_AUTO = "openrouter/auto";
364
+ var MODEL_OPENROUTER_FUSION = "openrouter/fusion";
361
365
  var MODEL_GPT_OSS_20B_FREE = "openai/gpt-oss-20b:free";
362
366
  var MODEL_MOONSHOTAI_KIMI_K2_5 = "moonshotai/kimi-k2.5";
363
367
  var MODEL_MOONSHOTAI_KIMI_LATEST = "~moonshotai/kimi-latest";
@@ -836,6 +840,22 @@ If it's in another language, summarize in that language.
836
840
  return StreamTextAccumulator.getFullText(blocks);
837
841
  }
838
842
 
843
+ // src/utils/safeJsonParse.ts
844
+ var safeJsonParse = (payload, fallback, onJsonError) => {
845
+ try {
846
+ return JSON.parse(payload);
847
+ } catch (error) {
848
+ onJsonError?.(payload, error);
849
+ return fallback;
850
+ }
851
+ };
852
+ var safeParseToolCallInput = (payload, onJsonError) => {
853
+ if (!payload) {
854
+ return {};
855
+ }
856
+ return safeJsonParse(payload, {}, onJsonError);
857
+ };
858
+
839
859
  // src/utils/openaiCompatibleSse.ts
840
860
  var parseJsonPayload = (payload, onJsonError) => {
841
861
  try {
@@ -943,7 +963,7 @@ If it's in another language, summarize in that language.
943
963
  type: "tool_use",
944
964
  id: e.id,
945
965
  name: e.name,
946
- input: JSON.parse(e.args || "{}")
966
+ input: safeParseToolCallInput(e.args, options.onJsonError)
947
967
  }));
948
968
  const blocks = [...textBlocks, ...toolBlocks];
949
969
  return {
@@ -954,7 +974,7 @@ If it's in another language, summarize in that language.
954
974
  usage
955
975
  };
956
976
  }
957
- function parseOpenAICompatibleOneShot(data) {
977
+ function parseOpenAICompatibleOneShot(data, options = {}) {
958
978
  const choice = data?.choices?.[0];
959
979
  const blocks = [];
960
980
  if (choice?.message?.tool_calls?.length) {
@@ -963,7 +983,10 @@ If it's in another language, summarize in that language.
963
983
  type: "tool_use",
964
984
  id: c.id,
965
985
  name: c.function?.name,
966
- input: JSON.parse(c.function?.arguments || "{}")
986
+ input: safeParseToolCallInput(
987
+ c.function?.arguments,
988
+ options.onJsonError
989
+ )
967
990
  })
968
991
  );
969
992
  } else {
@@ -1755,7 +1778,7 @@ If it's in another language, summarize in that language.
1755
1778
  };
1756
1779
 
1757
1780
  // src/services/providers/openai/responsesParser.ts
1758
- async function parseOpenAIResponsesStream(res, onPartial) {
1781
+ async function parseOpenAIResponsesStream(res, onPartial, options = {}) {
1759
1782
  const reader = res.body.getReader();
1760
1783
  const dec = new TextDecoder();
1761
1784
  const textBlocks = [];
@@ -1787,6 +1810,7 @@ If it's in another language, summarize in that language.
1787
1810
  onPartial,
1788
1811
  textBlocks,
1789
1812
  toolCallsMap,
1813
+ options,
1790
1814
  (metadata) => {
1791
1815
  if (metadata.responseStatus !== void 0) {
1792
1816
  responseStatus = metadata.responseStatus;
@@ -1799,7 +1823,8 @@ If it's in another language, summarize in that language.
1799
1823
  }
1800
1824
  }
1801
1825
  );
1802
- } catch {
1826
+ } catch (error) {
1827
+ options.onJsonError?.(eventData, error);
1803
1828
  console.warn("Failed to parse SSE data:", eventData);
1804
1829
  }
1805
1830
  eventType = "";
@@ -1824,7 +1849,7 @@ If it's in another language, summarize in that language.
1824
1849
  usage
1825
1850
  };
1826
1851
  }
1827
- function handleResponsesSSEEvent(eventType, data, onPartial, textBlocks, toolCallsMap, onMetadata) {
1852
+ function handleResponsesSSEEvent(eventType, data, onPartial, textBlocks, toolCallsMap, options, onMetadata) {
1828
1853
  switch (eventType) {
1829
1854
  case "response.output_item.added":
1830
1855
  if (data.item?.type === "message" && Array.isArray(data.item.content)) {
@@ -1838,7 +1863,10 @@ If it's in another language, summarize in that language.
1838
1863
  toolCallsMap.set(data.item.id, {
1839
1864
  id: data.item.id,
1840
1865
  name: data.item.name,
1841
- input: data.item.arguments ? JSON.parse(data.item.arguments) : {}
1866
+ input: safeParseToolCallInput(
1867
+ data.item.arguments,
1868
+ options.onJsonError
1869
+ )
1842
1870
  });
1843
1871
  }
1844
1872
  break;
@@ -1881,7 +1909,7 @@ If it's in another language, summarize in that language.
1881
1909
  usage: response?.usage
1882
1910
  };
1883
1911
  }
1884
- function parseOpenAIResponsesOneShot(data) {
1912
+ function parseOpenAIResponsesOneShot(data, options = {}) {
1885
1913
  const blocks = [];
1886
1914
  if (data.output && Array.isArray(data.output)) {
1887
1915
  data.output.forEach((outputItem) => {
@@ -1897,7 +1925,10 @@ If it's in another language, summarize in that language.
1897
1925
  type: "tool_use",
1898
1926
  id: outputItem.id,
1899
1927
  name: outputItem.name,
1900
- input: outputItem.arguments ? JSON.parse(outputItem.arguments) : {}
1928
+ input: safeParseToolCallInput(
1929
+ outputItem.arguments,
1930
+ options.onJsonError
1931
+ )
1901
1932
  });
1902
1933
  }
1903
1934
  });
@@ -2414,6 +2445,20 @@ If it's in another language, summarize in that language.
2414
2445
  };
2415
2446
 
2416
2447
  // src/utils/mcpSchemaFetcher.ts
2448
+ var createGenericSearchTool = (serverConfig, reason) => ({
2449
+ name: `mcp_${serverConfig.name}_search`,
2450
+ description: reason === "schema-fetch-failed" ? `Search using ${serverConfig.name} MCP server (schema fetch failed)` : `Search using ${serverConfig.name} MCP server`,
2451
+ parameters: {
2452
+ type: "object",
2453
+ properties: {
2454
+ query: {
2455
+ type: "string",
2456
+ description: "Search query"
2457
+ }
2458
+ },
2459
+ required: ["query"]
2460
+ }
2461
+ });
2417
2462
  var MCPSchemaFetcher = class {
2418
2463
  /**
2419
2464
  * Fetch tool schemas from MCP server
@@ -2421,6 +2466,15 @@ If it's in another language, summarize in that language.
2421
2466
  * @returns Array of tool definitions
2422
2467
  */
2423
2468
  static async fetchToolSchemas(serverConfig) {
2469
+ const result = await this.fetchToolSchemasWithStatus(serverConfig);
2470
+ return result.schemas;
2471
+ }
2472
+ /**
2473
+ * Fetch tool schemas from MCP server with failure metadata.
2474
+ * @param serverConfig MCP server configuration
2475
+ * @returns Tool schemas and failure metadata if fallback was used
2476
+ */
2477
+ static async fetchToolSchemasWithStatus(serverConfig) {
2424
2478
  try {
2425
2479
  const headers = {
2426
2480
  "Content-Type": "application/json"
@@ -2435,53 +2489,32 @@ If it's in another language, summarize in that language.
2435
2489
  );
2436
2490
  const toolsData = await response.json();
2437
2491
  if (Array.isArray(toolsData.tools)) {
2438
- return toolsData.tools.map((tool) => ({
2439
- name: `mcp_${serverConfig.name}_${tool.name}`,
2440
- description: tool.description || `Tool from ${serverConfig.name} MCP server`,
2441
- parameters: tool.inputSchema || {
2442
- type: "object",
2443
- properties: {},
2444
- required: []
2445
- }
2446
- }));
2492
+ return {
2493
+ schemas: toolsData.tools.map((tool) => ({
2494
+ name: `mcp_${serverConfig.name}_${tool.name}`,
2495
+ description: tool.description || `Tool from ${serverConfig.name} MCP server`,
2496
+ parameters: tool.inputSchema || {
2497
+ type: "object",
2498
+ properties: {},
2499
+ required: []
2500
+ }
2501
+ })),
2502
+ failures: []
2503
+ };
2447
2504
  }
2448
- return [
2449
- {
2450
- name: `mcp_${serverConfig.name}_search`,
2451
- description: `Search using ${serverConfig.name} MCP server`,
2452
- parameters: {
2453
- type: "object",
2454
- properties: {
2455
- query: {
2456
- type: "string",
2457
- description: "Search query"
2458
- }
2459
- },
2460
- required: ["query"]
2461
- }
2462
- }
2463
- ];
2505
+ return {
2506
+ schemas: [createGenericSearchTool(serverConfig)],
2507
+ failures: []
2508
+ };
2464
2509
  } catch (error) {
2465
2510
  console.warn(
2466
2511
  `Failed to fetch MCP schemas from ${serverConfig.name}:`,
2467
2512
  error
2468
2513
  );
2469
- return [
2470
- {
2471
- name: `mcp_${serverConfig.name}_search`,
2472
- description: `Search using ${serverConfig.name} MCP server (schema fetch failed)`,
2473
- parameters: {
2474
- type: "object",
2475
- properties: {
2476
- query: {
2477
- type: "string",
2478
- description: "Search query"
2479
- }
2480
- },
2481
- required: ["query"]
2482
- }
2483
- }
2484
- ];
2514
+ return {
2515
+ schemas: [createGenericSearchTool(serverConfig, "schema-fetch-failed")],
2516
+ failures: [{ server: serverConfig, error }]
2517
+ };
2485
2518
  }
2486
2519
  }
2487
2520
  /**
@@ -2490,16 +2523,28 @@ If it's in another language, summarize in that language.
2490
2523
  * @returns Array of all tool definitions
2491
2524
  */
2492
2525
  static async fetchAllToolSchemas(mcpServers) {
2526
+ const result = await this.fetchAllToolSchemasWithStatus(mcpServers);
2527
+ return result.schemas;
2528
+ }
2529
+ /**
2530
+ * Fetch all tool schemas from multiple MCP servers with failure metadata.
2531
+ * @param mcpServers Array of MCP server configurations
2532
+ * @returns Array of all tool definitions and failures
2533
+ */
2534
+ static async fetchAllToolSchemasWithStatus(mcpServers) {
2493
2535
  const allSchemas = [];
2536
+ const failures = [];
2494
2537
  for (const server of mcpServers) {
2495
2538
  try {
2496
- const schemas = await this.fetchToolSchemas(server);
2497
- allSchemas.push(...schemas);
2539
+ const result = await this.fetchToolSchemasWithStatus(server);
2540
+ allSchemas.push(...result.schemas);
2541
+ failures.push(...result.failures);
2498
2542
  } catch (error) {
2499
2543
  console.error(`Failed to fetch schemas from ${server.name}:`, error);
2544
+ failures.push({ server, error });
2500
2545
  }
2501
2546
  }
2502
- return allSchemas;
2547
+ return { schemas: allSchemas, failures };
2503
2548
  }
2504
2549
  };
2505
2550
 
@@ -2555,7 +2600,7 @@ If it's in another language, summarize in that language.
2555
2600
  {
2556
2601
  functionResponse: {
2557
2602
  name: funcName,
2558
- response: normalizeToolResult(safeJsonParse(msg.content))
2603
+ response: normalizeToolResult(safeJsonParse2(msg.content))
2559
2604
  }
2560
2605
  }
2561
2606
  ]
@@ -2602,7 +2647,7 @@ If it's in another language, summarize in that language.
2602
2647
  functionResponse: {
2603
2648
  name: funcName,
2604
2649
  response: normalizeToolResult(
2605
- safeJsonParse(msg.content)
2650
+ safeJsonParse2(msg.content)
2606
2651
  )
2607
2652
  }
2608
2653
  }
@@ -2651,7 +2696,7 @@ If it's in another language, summarize in that language.
2651
2696
  }
2652
2697
  return geminiMessages;
2653
2698
  }
2654
- function safeJsonParse(str) {
2699
+ function safeJsonParse2(str) {
2655
2700
  try {
2656
2701
  return JSON.parse(str);
2657
2702
  } catch {
@@ -2810,6 +2855,7 @@ If it's in another language, summarize in that language.
2810
2855
  addMCPServer(serverConfig) {
2811
2856
  this.mcpServers.push(serverConfig);
2812
2857
  this.mcpSchemasInitialized = false;
2858
+ this.mcpSchemaInitializationError = void 0;
2813
2859
  }
2814
2860
  /**
2815
2861
  * Remove MCP server by name
@@ -2820,6 +2866,7 @@ If it's in another language, summarize in that language.
2820
2866
  (server) => server.name !== serverName
2821
2867
  );
2822
2868
  this.mcpSchemasInitialized = false;
2869
+ this.mcpSchemaInitializationError = void 0;
2823
2870
  }
2824
2871
  /**
2825
2872
  * Check if MCP servers are configured
@@ -2828,6 +2875,12 @@ If it's in another language, summarize in that language.
2828
2875
  hasMCPServers() {
2829
2876
  return this.mcpServers.length > 0;
2830
2877
  }
2878
+ /**
2879
+ * Get the last MCP schema initialization error, if schema fallback was used.
2880
+ */
2881
+ getMCPSchemaInitializationError() {
2882
+ return this.mcpSchemaInitializationError;
2883
+ }
2831
2884
  /**
2832
2885
  * Initialize MCP tool schemas by fetching from servers
2833
2886
  * @private
@@ -2840,16 +2893,16 @@ If it's in another language, summarize in that language.
2840
2893
  const timeoutPromise = new Promise(
2841
2894
  (_, reject) => setTimeout(() => reject(new Error("MCP schema fetch timeout")), 5e3)
2842
2895
  );
2843
- const schemasPromise = MCPSchemaFetcher.fetchAllToolSchemas(
2896
+ const schemasPromise = MCPSchemaFetcher.fetchAllToolSchemasWithStatus(
2844
2897
  this.mcpServers
2845
2898
  );
2846
- this.mcpToolSchemas = await Promise.race([
2847
- schemasPromise,
2848
- timeoutPromise
2849
- ]);
2899
+ const result = await Promise.race([schemasPromise, timeoutPromise]);
2900
+ this.mcpToolSchemas = result.schemas;
2850
2901
  this.mcpSchemasInitialized = true;
2902
+ this.mcpSchemaInitializationError = result.failures[0]?.error;
2851
2903
  } catch (error) {
2852
2904
  console.warn("Failed to initialize MCP schemas, using fallback:", error);
2905
+ this.mcpSchemaInitializationError = error;
2853
2906
  this.mcpToolSchemas = createFallbackMCPToolSchemas(this.mcpServers);
2854
2907
  this.mcpSchemasInitialized = true;
2855
2908
  }
@@ -2970,7 +3023,14 @@ If it's in another language, summarize in that language.
2970
3023
  } catch (e) {
2971
3024
  const looksLikeVersionMismatch = /Unknown name|Cannot find field|404/.test(e?.message || "") || e?.status === 404;
2972
3025
  if (!requiresV1beta && looksLikeVersionMismatch) {
2973
- return await fetchOnce("v1beta", this.adaptKeysForApi(body));
3026
+ try {
3027
+ return await fetchOnce("v1beta", this.adaptKeysForApi(body));
3028
+ } catch (fallbackError) {
3029
+ if (fallbackError instanceof Error) {
3030
+ fallbackError.cause = e;
3031
+ }
3032
+ throw fallbackError;
3033
+ }
2974
3034
  }
2975
3035
  throw e;
2976
3036
  }
@@ -3895,28 +3955,38 @@ If it's in another language, summarize in that language.
3895
3955
  }
3896
3956
  /**
3897
3957
  * Normalize reasoning effort to a model-supported value
3958
+ *
3959
+ * Unsupported values are rounded to the nearest supported level instead of
3960
+ * resetting to the model default, so a low-latency request stays low
3961
+ * (e.g. 'minimal' on GPT-5.4 resolves to 'none', not 'medium') and a
3962
+ * high-effort request stays high (e.g. 'xhigh' on GPT-5.1 resolves to
3963
+ * 'high', not 'none').
3898
3964
  */
3899
3965
  normalizeReasoningEffort(modelName, effort) {
3900
3966
  if (!effort) {
3901
3967
  return void 0;
3902
3968
  }
3903
- if (effort === "none" && !allowsReasoningNone(modelName)) {
3904
- return getDefaultReasoningEffortForGPT5Model(modelName);
3905
- }
3906
- if (effort === "minimal" && !allowsReasoningMinimal(modelName)) {
3907
- return getDefaultReasoningEffortForGPT5Model(modelName);
3969
+ if (effort === "none" && !allowsReasoningNone(modelName) || effort === "minimal" && !allowsReasoningMinimal(modelName)) {
3970
+ if (effort === "minimal" && allowsReasoningNone(modelName)) {
3971
+ return "none";
3972
+ }
3973
+ if (effort === "none" && allowsReasoningMinimal(modelName)) {
3974
+ return "minimal";
3975
+ }
3976
+ return allowsReasoningLow(modelName) ? "low" : "medium";
3908
3977
  }
3909
3978
  if (effort === "low" && !allowsReasoningLow(modelName)) {
3910
- return getDefaultReasoningEffortForGPT5Model(modelName);
3979
+ return "medium";
3911
3980
  }
3912
3981
  if (effort === "xhigh" && !allowsReasoningXHigh(modelName)) {
3913
- return getDefaultReasoningEffortForGPT5Model(modelName);
3982
+ return "high";
3914
3983
  }
3915
3984
  return effort;
3916
3985
  }
3917
3986
  };
3918
3987
 
3919
3988
  // src/services/providers/openrouter/OpenRouterChatService.ts
3989
+ var isTokenLimitUnsupportedModel = (model) => model.trim() === MODEL_GPT_OSS_20B_FREE;
3920
3990
  var OpenRouterChatService = class {
3921
3991
  /**
3922
3992
  * Constructor
@@ -4110,10 +4180,12 @@ If it's in another language, summarize in that language.
4110
4180
  stream
4111
4181
  };
4112
4182
  const tokenLimit = maxTokens !== void 0 ? maxTokens : getMaxTokensForResponseLength(this.responseLength);
4113
- if (tokenLimit) {
4183
+ if (tokenLimit && isTokenLimitUnsupportedModel(model)) {
4114
4184
  console.warn(
4115
- `OpenRouter: Token limits are not supported for gpt-oss-20b model due to known issues. Using unlimited tokens instead.`
4185
+ `OpenRouter: Token limits are not supported for ${model} due to known issues. Using unlimited tokens instead.`
4116
4186
  );
4187
+ } else if (tokenLimit) {
4188
+ body.max_tokens = tokenLimit;
4117
4189
  }
4118
4190
  if (this.reasoning_effort !== void 0 || this.includeReasoning !== void 0 || this.reasoningMaxTokens) {
4119
4191
  body.reasoning = {};
@@ -4210,6 +4282,7 @@ If it's in another language, summarize in that language.
4210
4282
  return [
4211
4283
  // OpenRouter routing
4212
4284
  MODEL_OPENROUTER_AUTO,
4285
+ MODEL_OPENROUTER_FUSION,
4213
4286
  // Free models
4214
4287
  MODEL_GPT_OSS_20B_FREE,
4215
4288
  MODEL_ZAI_GLM_4_5_AIR_FREE,