alvin-bot 4.18.2 → 4.18.3

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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  All notable changes to Alvin Bot are documented here.
4
4
 
5
+ ## [4.18.3] — 2026-04-23
6
+
7
+ ### 🐛 Hotfix: 4.18.2 triggered unwanted failover to Ollama
8
+
9
+ **Bug in 4.18.2:** The empty-stream detector yielded an `error` chunk, which the registry's `queryWithFallback()` interprets as "primary provider failed" and immediately switches to the fallback (Ollama/Gemma 4). User saw `⚡ Claude (Agent SDK) unavailable — switching to Gemma 4 E4B` after every token rotation — the opposite of the intended behavior.
10
+
11
+ **Fix:** yield a `text` chunk instead of `error`. Same user-visible message, same cache-invalidation, but no failover cascade. The next CLI subprocess spawns with the fresh Keychain token automatically, and claude-sdk stays selected.
12
+
5
13
  ## [4.18.2] — 2026-04-23
6
14
 
7
15
  ### 🐛 Fix: silent empty-stream after OAuth-token rotation
@@ -309,33 +309,40 @@ export class ClaudeSDKProvider {
309
309
  ? (usage.input_tokens || 0) + (usage.cache_creation_input_tokens || 0) + (usage.cache_read_input_tokens || 0)
310
310
  : 0;
311
311
  const outputTok = usage?.output_tokens || 0;
312
- // v4.18.2 — Silent-empty-stream detection.
312
+ // v4.18.3 — Silent-empty-stream detection (replaces 4.18.2 approach).
313
313
  //
314
314
  // If the stream terminated cleanly but produced ZERO text chunks,
315
- // something went wrong that the SDK didn't surface as an error:
316
- // most commonly a stale OAuth token after /extra-usage or /login
317
- // rotated the Keychain entry while our in-memory SDK client was
318
- // still holding the old one. The CLI subprocess silently gets a
319
- // 401, emits no text, and we complete the stream with
320
- // accumulatedText === "". The user sees "(Keine Antwort)".
315
+ // something went wrong that the SDK didn't surface as an error.
316
+ // Most common cause: the OAuth token in the Keychain was rotated
317
+ // (e.g. right after /extra-usage or /login) while our in-memory
318
+ // SDK client still held the old one the CLI subprocess silently
319
+ // gets a 401, emits no text, and we complete with
320
+ // accumulatedText === "".
321
321
  //
322
- // We flip this from silent failure to explicit error. Clearing
323
- // the availability cache forces the next heartbeat probe to
324
- // re-check `claude auth status` with a fresh subprocess (which
325
- // reads the current Keychain entry).
322
+ // CRITICAL: we must NOT yield an "error" chunk here — the registry's
323
+ // queryWithFallback() treats that as "primary failed" and kicks off
324
+ // a full failover to the next provider (Ollama). That's exactly
325
+ // wrong: the next CLI subprocess would have picked up the fresh
326
+ // token by itself. Instead we:
327
+ // 1. Invalidate the availability cache so the next heartbeat
328
+ // re-probes `claude auth status` with a fresh subprocess.
329
+ // 2. Return a friendly "text" chunk explaining what happened,
330
+ // so the user sees a clear message (not "(Keine Antwort)")
331
+ // and knows to resend — without tripping the failover.
326
332
  if (accumulatedText === "" && outputTok === 0) {
327
333
  this.invalidateAvailabilityCache();
334
+ const hint = "⚠️ Claude antwortete mit leerem Stream (meist nach /extra-usage, /login oder Token-Refresh). " +
335
+ "Der SDK-Token-Cache wurde geleert — bitte schick die Nachricht einfach nochmal.";
328
336
  yield {
329
- type: "error",
330
- error: "Claude returned an empty response. " +
331
- "This can happen right after /extra-usage, /login, or a token refresh — " +
332
- "the SDK held a stale auth token. I've invalidated the cache; please resend your message.",
337
+ type: "text",
338
+ text: hint,
339
+ delta: hint,
340
+ sessionId: resultMsg.session_id || capturedSessionId,
333
341
  };
334
- return;
335
342
  }
336
343
  yield {
337
344
  type: "done",
338
- text: accumulatedText,
345
+ text: accumulatedText || "",
339
346
  sessionId: resultMsg.session_id || capturedSessionId,
340
347
  costUsd: "total_cost_usd" in resultMsg ? resultMsg.total_cost_usd : 0,
341
348
  inputTokens: inputTok,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alvin-bot",
3
- "version": "4.18.2",
3
+ "version": "4.18.3",
4
4
  "description": "Alvin Bot \u2014 Your personal AI agent on Telegram, WhatsApp, Discord, Signal, and Web.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",