@pentoshi/clai 2.0.52 → 2.0.54

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 (45) hide show
  1. package/README.md +6 -3
  2. package/dist/agent/runner.js +100 -69
  3. package/dist/agent/runner.js.map +1 -1
  4. package/dist/agent/tool-call-parser.js +6 -1
  5. package/dist/agent/tool-call-parser.js.map +1 -1
  6. package/dist/commands/providers.js +2 -0
  7. package/dist/commands/providers.js.map +1 -1
  8. package/dist/commands/update.js +1 -1
  9. package/dist/llm/capabilities.js +5 -2
  10. package/dist/llm/capabilities.js.map +1 -1
  11. package/dist/llm/gemini.js +74 -22
  12. package/dist/llm/gemini.js.map +1 -1
  13. package/dist/llm/groq.d.ts +1 -0
  14. package/dist/llm/groq.js +14 -0
  15. package/dist/llm/groq.js.map +1 -1
  16. package/dist/llm/http.d.ts +10 -2
  17. package/dist/llm/http.js +107 -45
  18. package/dist/llm/http.js.map +1 -1
  19. package/dist/llm/nvidia.js +5 -1
  20. package/dist/llm/nvidia.js.map +1 -1
  21. package/dist/llm/provider.js +7 -1
  22. package/dist/llm/provider.js.map +1 -1
  23. package/dist/llm/qwen-cloud.d.ts +2 -0
  24. package/dist/llm/qwen-cloud.js +81 -0
  25. package/dist/llm/qwen-cloud.js.map +1 -0
  26. package/dist/llm/router.js +9 -3
  27. package/dist/llm/router.js.map +1 -1
  28. package/dist/prompts/index.d.ts +6 -0
  29. package/dist/prompts/index.js +49 -0
  30. package/dist/prompts/index.js.map +1 -1
  31. package/dist/repl/slash-commands.js +7 -0
  32. package/dist/repl/slash-commands.js.map +1 -1
  33. package/dist/repl.js +35 -34
  34. package/dist/repl.js.map +1 -1
  35. package/dist/store/config.js +1 -0
  36. package/dist/store/config.js.map +1 -1
  37. package/dist/tui/App.js +126 -71
  38. package/dist/tui/App.js.map +1 -1
  39. package/dist/tui/hooks/useAgentRunner.d.ts +3 -1
  40. package/dist/tui/hooks/useAgentRunner.js +8 -3
  41. package/dist/tui/hooks/useAgentRunner.js.map +1 -1
  42. package/dist/types.d.ts +8 -1
  43. package/dist/types.js +1 -0
  44. package/dist/types.js.map +1 -1
  45. package/package.json +1 -1
@@ -1,12 +1,13 @@
1
1
  import { defaultModels, } from "./provider.js";
2
- import { readJson, readStreamLines } from "./http.js";
2
+ import { ProviderError, readJson, readStreamLines } from "./http.js";
3
3
  function geminiContents(messages) {
4
- return messages
5
- .filter((message) => message.role !== "system")
6
- .map((message) => {
4
+ const contents = [];
5
+ for (const message of messages) {
6
+ if (message.role === "system")
7
+ continue;
7
8
  const role = message.role === "assistant" ? "model" : "user";
8
9
  const parts = [];
9
- if (message.content)
10
+ if (message.content.trim())
10
11
  parts.push({ text: message.content });
11
12
  if (role === "user" && message.images) {
12
13
  for (const img of message.images) {
@@ -15,44 +16,87 @@ function geminiContents(messages) {
15
16
  });
16
17
  }
17
18
  }
18
- // Gemini requires at least one part per content entry.
19
+ // Never serialize an empty content part. Apart from being meaningless,
20
+ // Gemini can subsequently return empty candidates after an empty `model`
21
+ // turn. If a caller omitted an assistant turn, merge the adjacent user
22
+ // instructions instead to retain Gemini's alternating-role invariant.
19
23
  if (parts.length === 0)
20
- parts.push({ text: "" });
21
- return { role, parts };
22
- });
24
+ continue;
25
+ const previous = contents.at(-1);
26
+ if (previous?.role === role) {
27
+ previous.parts.push(...parts);
28
+ }
29
+ else {
30
+ contents.push({ role, parts });
31
+ }
32
+ }
33
+ return contents;
23
34
  }
24
35
  function systemInstruction(messages) {
25
36
  const system = messages.find((message) => message.role === "system");
26
37
  return system ? { parts: [{ text: system.content }] } : undefined;
27
38
  }
28
- function geminiSupportsThinking(model) {
29
- return /gemini-(?:2\.5|3|3\.\d)/i.test(model);
39
+ function isGemini3Model(model) {
40
+ return /gemini-3(?:[.-]|$)/i.test(model);
30
41
  }
31
- function geminiThinkingBudget(reasoning, model) {
32
- if (!reasoning?.enabled)
42
+ function geminiThinkingConfig(reasoning, model) {
43
+ if (!reasoning)
33
44
  return undefined;
34
- if (!geminiSupportsThinking(model))
45
+ if (isGemini3Model(model)) {
46
+ // Gemini 3 models use `thinkingLevel`, not Gemini 2.5's token budget.
47
+ // Flash-Lite supports `minimal`, which is the closest available recovery
48
+ // mode when the runner needs a visible answer instead of a long thought.
49
+ // 3.1 Pro does not support `minimal`, so `low` is its least costly mode.
50
+ const effort = reasoning?.effort ?? "medium";
51
+ const wantsMinimal = !reasoning?.enabled || effort === "none" || effort === "minimal";
52
+ const isPro = /gemini-3(?:\.\d)?-pro/i.test(model);
53
+ const thinkingLevel = wantsMinimal
54
+ ? isPro
55
+ ? "low"
56
+ : "minimal"
57
+ : effort === "low"
58
+ ? "low"
59
+ : effort === "high" || effort === "xhigh"
60
+ ? "high"
61
+ : "medium";
62
+ return {
63
+ thinkingLevel,
64
+ // On a recovery retry, keep Gemini's minimal internal reasoning but do
65
+ // not stream thought summaries back as an apparent empty completion.
66
+ ...(reasoning?.enabled ? { includeThoughts: true } : {}),
67
+ };
68
+ }
69
+ if (!/gemini-2\.5/i.test(model))
35
70
  return undefined;
71
+ if (!reasoning?.enabled) {
72
+ // Flash and Flash-Lite support an explicit zero budget. Gemini 2.5 Pro
73
+ // cannot disable thinking, so omit the control rather than send an
74
+ // invalid value.
75
+ return /gemini-2\.5-(?:flash|flash-lite)/i.test(model)
76
+ ? { thinkingBudget: 0 }
77
+ : undefined;
78
+ }
36
79
  switch (reasoning.effort) {
37
80
  case "low":
38
- return 1_024;
81
+ return { thinkingBudget: 1_024, includeThoughts: true };
39
82
  case "high":
40
- return 16_384;
83
+ case "xhigh":
84
+ return { thinkingBudget: 16_384, includeThoughts: true };
41
85
  default:
42
- return 4_096;
86
+ return { thinkingBudget: 4_096, includeThoughts: true };
43
87
  }
44
88
  }
45
89
  export function geminiBody(request) {
46
90
  const model = request.model ?? defaultModels.gemini;
47
- const thinkingBudget = geminiThinkingBudget(request.thinking, model);
48
- const defaultMaxTokens = thinkingBudget !== undefined ? 8_192 : 4_096;
91
+ const thinkingConfig = geminiThinkingConfig(request.thinking, model);
92
+ const defaultMaxTokens = request.thinking?.enabled ? 8_192 : 4_096;
49
93
  const body = {
50
94
  contents: geminiContents(request.messages),
51
95
  generationConfig: {
52
96
  temperature: request.temperature ?? 0.2,
53
97
  maxOutputTokens: request.maxTokens ?? defaultMaxTokens,
54
- ...(thinkingBudget !== undefined
55
- ? { thinkingConfig: { thinkingBudget, includeThoughts: true } }
98
+ ...(thinkingConfig !== undefined
99
+ ? { thinkingConfig }
56
100
  : {}),
57
101
  },
58
102
  };
@@ -109,7 +153,7 @@ export const geminiProvider = {
109
153
  .join("")
110
154
  .trim();
111
155
  if (!text) {
112
- throw new Error("Gemini returned no completion text");
156
+ throw new ProviderError("Gemini completed without a visible answer.");
113
157
  }
114
158
  const final = thought ? `<think>${thought}</think>${text}` : text;
115
159
  return { text: final, provider: "gemini", model };
@@ -131,6 +175,7 @@ export const geminiProvider = {
131
175
  throw new Error("Gemini returned no stream body");
132
176
  }
133
177
  let full = "";
178
+ let visible = "";
134
179
  let inThought = false;
135
180
  const enterThought = () => {
136
181
  if (inThought)
@@ -155,6 +200,9 @@ export const geminiProvider = {
155
200
  const payload = trimmed.slice(5).trim();
156
201
  if (payload === "[DONE]") {
157
202
  exitThought();
203
+ if (!visible.trim()) {
204
+ throw new ProviderError("Gemini completed without a visible answer.");
205
+ }
158
206
  return { text: full, provider: "gemini", model };
159
207
  }
160
208
  try {
@@ -171,6 +219,7 @@ export const geminiProvider = {
171
219
  else {
172
220
  if (inThought)
173
221
  exitThought();
222
+ visible += part.text;
174
223
  full += part.text;
175
224
  onToken(part.text);
176
225
  }
@@ -181,6 +230,9 @@ export const geminiProvider = {
181
230
  }
182
231
  }
183
232
  exitThought();
233
+ if (!visible.trim()) {
234
+ throw new ProviderError("Gemini completed without a visible answer.");
235
+ }
184
236
  return { text: full, provider: "gemini", model };
185
237
  },
186
238
  };
@@ -1 +1 @@
1
- {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/llm/gemini.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/llm/gemini.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { type LlmProvider } from "./provider.js";
2
2
  export declare const groqFallbackModels: string[];
3
+ export declare function groqInputTokenBudget(model: string): number | undefined;
3
4
  export declare function groqMaxTokens(model: string, requested: number | undefined): number | undefined;
4
5
  export declare const groqProvider: LlmProvider;
package/dist/llm/groq.js CHANGED
@@ -12,6 +12,20 @@ export const groqFallbackModels = [
12
12
  "groq/compound-mini",
13
13
  "groq/compound",
14
14
  ];
15
+ /**
16
+ * Conservative input budgets for Groq models whose free-tier TPM limit is
17
+ * smaller than clai's normal agent prompt. Leave room for the current user
18
+ * message and protocol overhead instead of treating the published TPM number
19
+ * as usable prompt space.
20
+ */
21
+ const groqInputTokenBudgets = [
22
+ { pattern: /qwen\/qwen3-32b/i, tokens: 5_500 },
23
+ { pattern: /openai\/gpt-oss-20b/i, tokens: 7_500 },
24
+ ];
25
+ export function groqInputTokenBudget(model) {
26
+ return groqInputTokenBudgets.find(({ pattern }) => pattern.test(model))
27
+ ?.tokens;
28
+ }
15
29
  let cachedModels = null;
16
30
  let lastFetchTime = 0;
17
31
  const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour cache TTL
@@ -1 +1 @@
1
- {"version":3,"file":"groq.js","sourceRoot":"","sources":["../../src/llm/groq.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"groq.js","sourceRoot":"","sources":["../../src/llm/groq.ts"],"names":[],"mappings":"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"}
@@ -6,9 +6,11 @@ export declare class ProviderError extends Error {
6
6
  constructor(message: string, status?: number | undefined, body?: string | undefined, retryAfterSeconds?: number | undefined);
7
7
  }
8
8
  export declare function readJson<T>(response: Response): Promise<T>;
9
+ /** Default no-byte watchdog for provider streams. */
10
+ export declare const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 30000;
9
11
  /**
10
12
  * Shared SSE/JSONL stream reader. Wraps a fetch response body in:
11
- * - an idle watchdog (default 90s, configurable per call) that aborts
13
+ * - an idle watchdog (default 30s, configurable per call) that aborts
12
14
  * when no bytes arrive,
13
15
  * - a hard total-byte cap (default 16MB) so a runaway provider can't
14
16
  * grow our memory unbounded,
@@ -71,8 +73,14 @@ export declare function openAiCompatibleStream(options: {
71
73
  onToken: (token: string) => void;
72
74
  reasoning?: ReasoningPreference | undefined;
73
75
  reasoningStyle?: ReasoningStyle | undefined;
74
- /** Abort a stream that produces no bytes for this long. Default 90s. */
76
+ /** Abort a stream that produces no bytes for this long. Default 30s. */
75
77
  idleTimeoutMs?: number | undefined;
78
+ /**
79
+ * Allow a slower cold start before the first SSE byte. Once streaming has
80
+ * begun, `idleTimeoutMs` takes over so a wedged connection still aborts
81
+ * promptly. Defaults to the regular idle timeout.
82
+ */
83
+ initialIdleTimeoutMs?: number | undefined;
76
84
  }): Promise<string>;
77
85
  export declare function openAiCompatiblePing(baseUrl: string, apiKey: string, headers?: Record<string, string> | undefined): Promise<void>;
78
86
  export {};
package/dist/llm/http.js CHANGED
@@ -49,7 +49,7 @@ function statusCodeHint(status) {
49
49
  return " — the provider rejected the request body (model name or parameter mismatch)";
50
50
  }
51
51
  if (status === 413) {
52
- return " — request too large; try `/compact` or pick a model with a larger context window";
52
+ return " — request exceeded the provider input limit; retry with a compact prompt or pick another model";
53
53
  }
54
54
  if (status >= 500 && status < 600) {
55
55
  return " — upstream provider error; try again or switch with `/provider`";
@@ -159,10 +159,51 @@ async function readBodyCapped(response, maxBytes) {
159
159
  }
160
160
  return collected;
161
161
  }
162
+ /** Default no-byte watchdog for provider streams. */
163
+ export const DEFAULT_STREAM_IDLE_TIMEOUT_MS = 30_000;
164
+ /**
165
+ * Read one chunk without allowing a wedged ReadableStream implementation to
166
+ * hide a caller abort. `reader.cancel()` is best-effort: some sockets do not
167
+ * settle an already-pending `read()` promptly, so the abort signal also races
168
+ * the read and rejects the awaiting request immediately.
169
+ */
170
+ function readWithAbort(reader, signal) {
171
+ if (signal.aborted) {
172
+ return Promise.reject(signal.reason ?? new Error("Stream aborted"));
173
+ }
174
+ return new Promise((resolve, reject) => {
175
+ let settled = false;
176
+ const cleanup = () => signal.removeEventListener("abort", abort);
177
+ const succeed = (value) => {
178
+ if (settled)
179
+ return;
180
+ settled = true;
181
+ cleanup();
182
+ resolve(value);
183
+ };
184
+ const fail = (error) => {
185
+ if (settled)
186
+ return;
187
+ settled = true;
188
+ cleanup();
189
+ reject(error);
190
+ };
191
+ const abort = () => {
192
+ fail(signal.reason ?? new Error("Stream aborted"));
193
+ };
194
+ signal.addEventListener("abort", abort, { once: true });
195
+ try {
196
+ void reader.read().then(succeed, fail);
197
+ }
198
+ catch (error) {
199
+ fail(error);
200
+ }
201
+ });
202
+ }
162
203
  export async function* readStreamLines(response, options = {}) {
163
204
  if (!response.body)
164
205
  return;
165
- const idleTimeoutMs = options.idleTimeoutMs ?? 90_000;
206
+ const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS;
166
207
  const maxBytes = options.maxBytes ?? 16 * 1024 * 1024;
167
208
  const idleController = new AbortController();
168
209
  let idleTimer;
@@ -176,7 +217,7 @@ export async function* readStreamLines(response, options = {}) {
176
217
  }, idleTimeoutMs);
177
218
  };
178
219
  resetIdleTimer();
179
- const onCallerAbort = () => idleController.abort();
220
+ const onCallerAbort = () => idleController.abort(options.signal?.reason);
180
221
  options.signal?.addEventListener("abort", onCallerAbort, { once: true });
181
222
  // If either signal is already aborted, bail before starting the loop.
182
223
  if (idleController.signal.aborted) {
@@ -205,7 +246,17 @@ export async function* readStreamLines(response, options = {}) {
205
246
  }
206
247
  break;
207
248
  }
208
- const { done, value } = await reader.read();
249
+ let readResult;
250
+ try {
251
+ readResult = await readWithAbort(reader, idleController.signal);
252
+ }
253
+ catch (error) {
254
+ if (idleFired) {
255
+ throw new ProviderError(`Provider stream stalled — no data for ${Math.round(idleTimeoutMs / 1000)}s.`);
256
+ }
257
+ throw error;
258
+ }
259
+ const { done, value } = readResult;
209
260
  if (done) {
210
261
  if (idleFired) {
211
262
  throw new ProviderError(`Provider stream stalled — no data for ${Math.round(idleTimeoutMs / 1000)}s.`);
@@ -234,12 +285,9 @@ export async function* readStreamLines(response, options = {}) {
234
285
  clearTimeout(idleTimer);
235
286
  options.signal?.removeEventListener("abort", onCallerAbort);
236
287
  idleController.signal.removeEventListener("abort", cancelReaderOnAbort);
237
- try {
238
- await reader.cancel().catch(() => undefined);
239
- }
240
- catch {
241
- // best-effort
242
- }
288
+ // Do not await cancellation here. A wedged transport can leave
289
+ // `reader.cancel()` pending too; the caller's abort must still return.
290
+ void reader.cancel().catch(() => undefined);
243
291
  try {
244
292
  reader.releaseLock();
245
293
  }
@@ -334,9 +382,12 @@ export function buildReasoningPayload(reasoning, style, model) {
334
382
  return { reasoning_effort: enabled ? "default" : "none" };
335
383
  }
336
384
  if (/openai\/gpt-oss-(?:20b|120b)/.test(m)) {
337
- if (!enabled)
338
- return {};
339
- return { reasoning_effort: clampEffort(effort) };
385
+ // GPT-OSS cannot disable reasoning effort, but it can hide reasoning
386
+ // from the response. Retrying an empty visible answer must not silently
387
+ // fall back to the provider's medium default effort.
388
+ return enabled
389
+ ? { reasoning_effort: clampEffort(effort), include_reasoning: true }
390
+ : { reasoning_effort: "low", include_reasoning: false };
340
391
  }
341
392
  return {};
342
393
  }
@@ -398,8 +449,15 @@ export function buildReasoningPayload(reasoning, style, model) {
398
449
  chat_template_kwargs: { enable_thinking: enabled },
399
450
  };
400
451
  case "effort-only":
401
- if (!enabled)
402
- return {};
452
+ // NVIDIA GPT-OSS accepts only low/medium/high, so a retry cannot
453
+ // fully disable it. Keep it at the lowest supported effort instead
454
+ // of omitting the field and reverting to NVIDIA's medium default.
455
+ if (!enabled && /gpt-oss/i.test(model ?? "")) {
456
+ return { reasoning_effort: "low" };
457
+ }
458
+ if (!enabled && /qwen3|mistral-/i.test(model ?? "")) {
459
+ return { reasoning_effort: "none" };
460
+ }
403
461
  return { reasoning_effort: clampEffort(effort) };
404
462
  case "none":
405
463
  default:
@@ -418,7 +476,9 @@ function buildChatBody(options) {
418
476
  // thinking is on, 2K otherwise — keep small for fast non-reasoning
419
477
  // paths so kimi-k2.6 etc. respond instantly).
420
478
  const reasoningOn = Boolean(options.reasoning?.enabled);
421
- const isMinimaxM3 = options.model === "minimaxai/minimax-m3";
479
+ // Kimchi exposes this model as `minimax-m3`; NVIDIA uses the longer
480
+ // `minimaxai/minimax-m3` ID. Both require the same sampling settings.
481
+ const isMinimaxM3 = /minimax-m3/i.test(options.model);
422
482
  const defaultMaxTokens = isMinimaxM3 ? 8_192 : reasoningOn ? 8_192 : 4_096;
423
483
  const defaultTemperature = isMinimaxM3 ? 1.0 : 0.2;
424
484
  const body = {
@@ -493,20 +553,24 @@ export async function openAiCompatibleComplete(options) {
493
553
  export async function openAiCompatibleStream(options) {
494
554
  // Combine the caller's abort signal with an idle watchdog so a stuck
495
555
  // connection on a thinking model can't wedge the REPL forever.
496
- const idleTimeoutMs = options.idleTimeoutMs ?? 90_000;
556
+ const idleTimeoutMs = options.idleTimeoutMs ?? DEFAULT_STREAM_IDLE_TIMEOUT_MS;
557
+ const initialIdleTimeoutMs = options.initialIdleTimeoutMs ?? idleTimeoutMs;
497
558
  const idleController = new AbortController();
498
559
  let idleTimer;
499
560
  let idleFired = false;
561
+ let sawStreamBytes = false;
562
+ let activeIdleTimeoutMs = initialIdleTimeoutMs;
500
563
  const resetIdleTimer = () => {
501
564
  if (idleTimer)
502
565
  clearTimeout(idleTimer);
566
+ activeIdleTimeoutMs = sawStreamBytes ? idleTimeoutMs : initialIdleTimeoutMs;
503
567
  idleTimer = setTimeout(() => {
504
568
  idleFired = true;
505
569
  idleController.abort();
506
- }, idleTimeoutMs);
570
+ }, activeIdleTimeoutMs);
507
571
  };
508
572
  resetIdleTimer();
509
- const onCallerAbort = () => idleController.abort();
573
+ const onCallerAbort = () => idleController.abort(options.signal?.reason);
510
574
  options.signal?.addEventListener("abort", onCallerAbort, { once: true });
511
575
  const supportsVision = modelSupportsVision(options.provider.toLowerCase(), options.model);
512
576
  const requestBody = buildChatBody({
@@ -543,7 +607,7 @@ export async function openAiCompatibleStream(options) {
543
607
  clearTimeout(idleTimer);
544
608
  options.signal?.removeEventListener("abort", onCallerAbort);
545
609
  if (idleFired) {
546
- throw new ProviderError(`${options.provider} request timed out before any response (${Math.round(idleTimeoutMs / 1000)}s)`);
610
+ throw new ProviderError(`${options.provider} request timed out before any response (${Math.round(activeIdleTimeoutMs / 1000)}s)`);
547
611
  }
548
612
  throw error;
549
613
  }
@@ -629,13 +693,14 @@ export async function openAiCompatibleStream(options) {
629
693
  if (idleController.signal.aborted) {
630
694
  throw new Error("Stream aborted");
631
695
  }
632
- const { done, value } = await reader.read();
696
+ const { done, value } = await readWithAbort(reader, idleController.signal);
633
697
  options.signal?.throwIfAborted();
634
698
  if (idleController.signal.aborted) {
635
699
  throw new Error("Stream aborted");
636
700
  }
637
701
  if (done)
638
702
  break;
703
+ sawStreamBytes = true;
639
704
  resetIdleTimer();
640
705
  buffer += decoder.decode(value, { stream: true });
641
706
  const lines = buffer.split("\n");
@@ -648,14 +713,16 @@ export async function openAiCompatibleStream(options) {
648
713
  if (payload === "[DONE]") {
649
714
  exitReasoning();
650
715
  cleanup();
651
- if (!visible.trim() &&
652
- reasoningSeen.trim() &&
653
- finishReason === "length") {
654
- // The model spent its entire budget on hidden reasoning and
655
- // never produced a visible answer. Surfacing this as an error
656
- // instead of an empty string — is far less confusing than
657
- // a frozen-looking REPL.
658
- throw new ProviderError(`${options.provider} hit the max_tokens limit while still thinking (no visible answer). Try /variants off, lower the effort, or raise max_tokens.`);
716
+ if (!visible.trim()) {
717
+ const cause = reasoningSeen.trim()
718
+ ? finishReason === "length"
719
+ ? "hit the max_tokens limit while still thinking"
720
+ : "completed after reasoning without a visible answer"
721
+ : "completed without a visible answer";
722
+ // Do not return a reasoning-only completion to the agent. It
723
+ // would poison recovery history and cause no-op retry loops.
724
+ // Router-level fallback can now try a healthy provider instead.
725
+ throw new ProviderError(`${options.provider} ${cause}.`);
659
726
  }
660
727
  return full;
661
728
  }
@@ -690,15 +757,13 @@ export async function openAiCompatibleStream(options) {
690
757
  }
691
758
  exitReasoning();
692
759
  cleanup();
693
- // Only treat "reasoning only, no visible answer" as a hard error when the
694
- // model actually ran out of budget (finish_reason "length") — mirroring
695
- // the [DONE] branch above. Some providers close the socket normally
696
- // (finish_reason "stop"/undefined, no explicit [DONE] line) after a
697
- // reasoning-only turn; throwing there discarded the reasoning entirely
698
- // and surfaced a confusing error instead of the graceful "model produced
699
- // only thinking" recovery the caller already has for this exact case.
700
- if (!visible.trim() && reasoningSeen.trim() && finishReason === "length") {
701
- throw new ProviderError(`${options.provider} hit the max_tokens limit while still thinking (no visible answer). Try /variants off, lower the effort, or raise max_tokens.`);
760
+ if (!visible.trim()) {
761
+ const cause = reasoningSeen.trim()
762
+ ? finishReason === "length"
763
+ ? "hit the max_tokens limit while still thinking"
764
+ : "completed after reasoning without a visible answer"
765
+ : "completed without a visible answer";
766
+ throw new ProviderError(`${options.provider} ${cause}.`);
702
767
  }
703
768
  return full;
704
769
  }
@@ -708,12 +773,9 @@ export async function openAiCompatibleStream(options) {
708
773
  // stream is already errored (eg from the same abort that triggered
709
774
  // this catch). Swallow it so it never escalates to an unhandled
710
775
  // rejection that kills the whole REPL.
711
- try {
712
- await reader.cancel().catch(() => undefined);
713
- }
714
- catch {
715
- // best-effort cleanup
716
- }
776
+ // Do not await cancellation here. Some stalled sockets never settle the
777
+ // pending cancel promise, and waiting for it would negate the abort race.
778
+ void reader.cancel().catch(() => undefined);
717
779
  try {
718
780
  reader.releaseLock();
719
781
  }
@@ -721,7 +783,7 @@ export async function openAiCompatibleStream(options) {
721
783
  // already released
722
784
  }
723
785
  if (idleFired) {
724
- throw new ProviderError(`${options.provider} stream stalled — no data for ${Math.round(idleTimeoutMs / 1000)}s. Try a smaller model or disable thinking with /variants off.`);
786
+ throw new ProviderError(`${options.provider} stream stalled — no data for ${Math.round(activeIdleTimeoutMs / 1000)}s. Try a smaller model or disable thinking with /variants off.`);
725
787
  }
726
788
  throw error;
727
789
  }