qwenproxy-cli 1.4.8 → 1.5.1

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/dist/index.js CHANGED
@@ -2986,7 +2986,15 @@ function defaultCooldownDurationMs() {
2986
2986
  return computeQuotaCooldownMs(Date.now());
2987
2987
  }
2988
2988
  function markAccountRateLimited(accountId, cooldownMs, reason, options = {}) {
2989
+ if (cooldownMs !== void 0 && cooldownMs <= 0) {
2990
+ clearAccountCooldown(accountId);
2991
+ return;
2992
+ }
2989
2993
  const duration = cooldownMs ?? defaultCooldownDurationMs();
2994
+ if (duration <= 0) {
2995
+ clearAccountCooldown(accountId);
2996
+ return;
2997
+ }
2990
2998
  const until = Date.now() + duration;
2991
2999
  const cooldownReason = reason ?? "RateLimited";
2992
3000
  cooldowns.set(accountId, {
@@ -3986,7 +3994,8 @@ function buildQwenRequestHeaders(opts) {
3986
3994
  "bx-v": opts.bxV || "2.5.37",
3987
3995
  source: "web",
3988
3996
  version: opts.version || getQwenWebVersion(),
3989
- timezone: opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
3997
+ timezone: opts.extra?.timezone || opts.extra?.Timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
3998
+ Timezone: opts.extra?.Timezone || opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
3990
3999
  // Use the real browser client-hints when captured (anti-hardcoded); fall
3991
4000
  // back to the static fingerprint otherwise.
3992
4001
  "sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
@@ -4528,26 +4537,6 @@ async function isPageLoggedIn(page, timeoutMs = SESSION_PROBE_NAVIGATION_TIMEOUT
4528
4537
  }
4529
4538
  } catch {
4530
4539
  }
4531
- try {
4532
- const refreshRes = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
4533
- method: "GET",
4534
- credentials: "include",
4535
- signal: AbortSignal.timeout(3e3)
4536
- });
4537
- if (refreshRes.status === 200) {
4538
- const refreshJson = await refreshRes.json().catch(() => null);
4539
- if (refreshJson && refreshJson.success === false) {
4540
- const code = refreshJson.data?.code || refreshJson.code;
4541
- const details = String(refreshJson.data?.details || "");
4542
- if (code === "Unauthorized" || details.includes("revogado") || details.includes("revoked")) {
4543
- return false;
4544
- }
4545
- }
4546
- } else if (refreshRes.status === 401 || refreshRes.status === 403) {
4547
- return false;
4548
- }
4549
- } catch {
4550
- }
4551
4540
  return true;
4552
4541
  } catch {
4553
4542
  return false;
@@ -5675,7 +5664,7 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
5675
5664
  `\u26A0\uFE0F [Playwright] Initial navigation check for ${maskEmail(account.email)}: ${err.message}`
5676
5665
  );
5677
5666
  }
5678
- if (!options.skipHeaderCapture) {
5667
+ if (!options.skipHeaderCapture && !isAccountHeadersReady(account.id)) {
5679
5668
  acctPage.__qwenChatHomeLoaded = true;
5680
5669
  await captureQwenHeaders(account.id);
5681
5670
  }
@@ -6005,7 +5994,7 @@ async function loginToQwen(accountId, email, password) {
6005
5994
  );
6006
5995
  markAccountRateLimited(
6007
5996
  accountId,
6008
- 24 * 3600 * 1e3,
5997
+ 300 * 1e3,
6009
5998
  "AuthFailed: All login methods exhausted"
6010
5999
  );
6011
6000
  return false;
@@ -6282,38 +6271,49 @@ async function loginViaUi(page, email, password) {
6282
6271
  try {
6283
6272
  const emailEl = document.querySelector('input[name="email"], input[type="email"], input[type="text"]');
6284
6273
  const passEl = document.querySelector('input[type="password"], input[name="password"]');
6285
- if (emailEl) {
6286
- emailEl.dispatchEvent(new Event("input", { bubbles: true }));
6287
- emailEl.dispatchEvent(new Event("change", { bubbles: true }));
6288
- emailEl.dispatchEvent(new Event("blur", { bubbles: true }));
6289
- }
6290
- if (passEl) {
6291
- passEl.dispatchEvent(new Event("input", { bubbles: true }));
6292
- passEl.dispatchEvent(new Event("change", { bubbles: true }));
6293
- passEl.dispatchEvent(new Event("blur", { bubbles: true }));
6294
- }
6274
+ const setReactValue = (el, val) => {
6275
+ const proto = Object.getPrototypeOf(el);
6276
+ const setter = Object.getOwnPropertyDescriptor(proto, "value")?.set;
6277
+ if (setter) {
6278
+ setter.call(el, val);
6279
+ } else {
6280
+ el.value = val;
6281
+ }
6282
+ el.dispatchEvent(new Event("input", { bubbles: true }));
6283
+ el.dispatchEvent(new Event("change", { bubbles: true }));
6284
+ el.dispatchEvent(new Event("blur", { bubbles: true }));
6285
+ };
6286
+ if (emailEl) setReactValue(emailEl, emailEl.value);
6287
+ if (passEl) setReactValue(passEl, passEl.value);
6295
6288
  } catch {
6296
6289
  }
6297
6290
  }).catch(() => {
6298
6291
  });
6292
+ await sleep2(500);
6299
6293
  const submitSelector = 'button.qwenchat-auth-pc-submit-button, button[type="submit"], button:has-text("Log in"), button:has-text("Sign in"), button:has-text("Fazer login"), button:has-text("Entrar"), button:has-text("Iniciar sesi\xF3n"), button:has-text("\u767B\u5F55")';
6300
6294
  const submitButton = page.locator(submitSelector).first();
6295
+ await page.evaluate((sel) => {
6296
+ try {
6297
+ const btn = document.querySelector(sel);
6298
+ if (btn) {
6299
+ btn.removeAttribute("disabled");
6300
+ btn.classList.remove("ant-btn-disabled");
6301
+ btn.click();
6302
+ }
6303
+ } catch {
6304
+ }
6305
+ }, submitSelector).catch(() => {
6306
+ });
6301
6307
  try {
6302
- await page.waitForSelector('button[type="submit"]:not([disabled])', {
6303
- timeout: 4e3
6304
- });
6305
- await submitButton.click();
6306
- } catch {
6307
- const isDisabled = await submitButton.isDisabled().catch(() => true);
6308
- if (!isDisabled) {
6309
- await submitButton.click({ force: true }).catch(() => {
6308
+ if (await submitButton.isVisible({ timeout: 2e3 }).catch(() => false)) {
6309
+ await submitButton.click({ force: true, timeout: 2e3 }).catch(() => {
6310
6310
  });
6311
- } else {
6312
- if (!page.url().includes("/auth") && await isPageLoggedIn(page, 2e3)) {
6313
- return { success: true };
6314
- }
6315
6311
  }
6312
+ } catch {
6316
6313
  }
6314
+ const passInput = page.locator(passwordSelector).first();
6315
+ await passInput.press("Enter", { timeout: 2e3 }).catch(() => {
6316
+ });
6317
6317
  const postSubmitDeadline = Date.now() + 15e3;
6318
6318
  while (Date.now() < postSubmitDeadline) {
6319
6319
  if (page.isClosed()) break;
@@ -9087,8 +9087,11 @@ ${thought}`);
9087
9087
  }
9088
9088
  return sections.join("\n\n");
9089
9089
  }
9090
+ function isAnswerPhase(phase) {
9091
+ return typeof phase === "string" && ANSWER_PHASES.has(phase);
9092
+ }
9090
9093
  function isThinkingPhase(phase) {
9091
- return phase === "think" || phase === "thinking_summary";
9094
+ return typeof phase === "string" && THINKING_PHASES.has(phase);
9092
9095
  }
9093
9096
  function extractThinkingContent(delta) {
9094
9097
  if (Array.isArray(delta?.extra?.summary_title?.content) || Array.isArray(delta?.extra?.summary_thought?.content)) {
@@ -9179,9 +9182,25 @@ function buildUsage(accumulator) {
9179
9182
  }
9180
9183
  return usage;
9181
9184
  }
9185
+ var ANSWER_PHASES, THINKING_PHASES;
9182
9186
  var init_helpers = __esm({
9183
9187
  "src/routes/chat/helpers.ts"() {
9184
9188
  "use strict";
9189
+ ANSWER_PHASES = /* @__PURE__ */ new Set([
9190
+ "answer",
9191
+ "deep_research_answer",
9192
+ "ReportGeneration",
9193
+ "PdfMdGen",
9194
+ "image",
9195
+ "image_generation",
9196
+ "image_gen",
9197
+ "t2i"
9198
+ ]);
9199
+ THINKING_PHASES = /* @__PURE__ */ new Set([
9200
+ "think",
9201
+ "thinking_summary",
9202
+ "DeepThinking"
9203
+ ]);
9185
9204
  }
9186
9205
  });
9187
9206
 
@@ -9355,7 +9374,7 @@ function classifyRetryAction(err, options) {
9355
9374
  switchAccount: true,
9356
9375
  forceNewChat: true,
9357
9376
  retryAfterMs: baseDelayMs,
9358
- accountCooldownMs: config.concurrency.initFailureCooldownMs,
9377
+ accountCooldownMs: 0,
9359
9378
  accountCooldownReason: "PersonalizationFailed"
9360
9379
  });
9361
9380
  }
@@ -9365,8 +9384,8 @@ function classifyRetryAction(err, options) {
9365
9384
  forceNewChat: true,
9366
9385
  retryWithFullPrompt: true,
9367
9386
  retryAfterMs: Math.min(baseDelayMs, 1e3),
9368
- accountCooldownMs: config.concurrency.initFailureCooldownMs,
9369
- accountCooldownReason: "AuthInitFailed"
9387
+ accountCooldownMs: 0,
9388
+ accountCooldownReason: "AuthExpired"
9370
9389
  });
9371
9390
  }
9372
9391
  if (isCorruptedChatHistoryError(err)) {
@@ -9783,8 +9802,8 @@ async function requestCompletionsInBrowser(params) {
9783
9802
  const choice = chunk?.choices?.[0];
9784
9803
  const delta = choice?.delta;
9785
9804
  const phase = delta?.phase;
9786
- const isAnswerPhase = !phase || phase === "answer";
9787
- if (choice?.finish_reason || delta?.status === "finished" && isAnswerPhase) {
9805
+ const isAnswerPhase2 = !phase || phase === "answer";
9806
+ if (choice?.finish_reason || delta?.status === "finished" && isAnswerPhase2) {
9788
9807
  finished = true;
9789
9808
  break;
9790
9809
  }
@@ -10835,13 +10854,15 @@ async function createQwenChatSession(headers, model, accountId, chatMode = "thre
10835
10854
  } catch {
10836
10855
  }
10837
10856
  }
10838
- if (json?.data?.code === "Unauthorized" || json?.code === "Unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access")) {
10857
+ const isUnauthorized = json?.data?.code?.toLowerCase() === "unauthorized" || json?.code?.toLowerCase() === "unauthorized" || rawLower.includes("unauthorized") || rawLower.includes("permission to access") || rawLower.includes("token has expired") || rawLower.includes("401");
10858
+ if (isUnauthorized) {
10839
10859
  if (accountId) {
10840
10860
  try {
10841
10861
  const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
10842
10862
  const refreshed = await refreshAccountToken2(accountId);
10843
10863
  if (refreshed.success) {
10844
- return createQwenChatSession(headers, model, accountId, chatMode);
10864
+ const { headers: freshHeaders } = await getQwenHeaders(false, accountId);
10865
+ return createQwenChatSession(freshHeaders, model, accountId, chatMode);
10845
10866
  }
10846
10867
  } catch {
10847
10868
  }
@@ -12835,8 +12856,18 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
12835
12856
  );
12836
12857
  }
12837
12858
  };
12859
+ let forceRefresh = options?.forceNewChat === true;
12860
+ if (!forceRefresh && accountId) {
12861
+ try {
12862
+ const basic = await getBasicHeaders2(accountId);
12863
+ if (isTokenExpiringSoon(basic.cookie, 3)) {
12864
+ forceRefresh = true;
12865
+ }
12866
+ } catch {
12867
+ }
12868
+ }
12838
12869
  const captured = await getQwenHeaders(
12839
- options?.forceNewChat === true,
12870
+ forceRefresh,
12840
12871
  accountId
12841
12872
  );
12842
12873
  ensureNotAborted();
@@ -12850,7 +12881,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
12850
12881
  if (options && "chatSessionId" in options) {
12851
12882
  if (options.chatSessionId === null || options.chatSessionId === "") {
12852
12883
  const acquired = await acquireNewQwenChatSession(
12853
- headers,
12884
+ activeHeaders,
12854
12885
  model,
12855
12886
  accountId,
12856
12887
  options?.chatMode ?? "thread"
@@ -12858,6 +12889,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
12858
12889
  chatSessionId = acquired.chatId;
12859
12890
  leasedWarmChat = acquired.leasedFromPool;
12860
12891
  createdNewChat = true;
12892
+ try {
12893
+ const fresh = await getQwenHeaders(false, accountId);
12894
+ activeHeaders = fresh.headers;
12895
+ } catch {
12896
+ }
12861
12897
  } else {
12862
12898
  chatSessionId = options.chatSessionId;
12863
12899
  }
@@ -12865,7 +12901,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
12865
12901
  chatSessionId = captured.chatSessionId;
12866
12902
  if (!chatSessionId) {
12867
12903
  const acquired = await acquireNewQwenChatSession(
12868
- headers,
12904
+ activeHeaders,
12869
12905
  model,
12870
12906
  accountId,
12871
12907
  options?.chatMode ?? "thread"
@@ -12873,6 +12909,11 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
12873
12909
  chatSessionId = acquired.chatId;
12874
12910
  leasedWarmChat = acquired.leasedFromPool;
12875
12911
  createdNewChat = true;
12912
+ try {
12913
+ const fresh = await getQwenHeaders(false, accountId);
12914
+ activeHeaders = fresh.headers;
12915
+ } catch {
12916
+ }
12876
12917
  }
12877
12918
  }
12878
12919
  ensureNotAborted();
@@ -14031,10 +14072,12 @@ async function getSTSToken(filename, filesize, filetype, headers, accountId) {
14031
14072
  }
14032
14073
  data = await response.json().catch(() => null);
14033
14074
  }
14034
- const is401 = data?.success === false && (data?.data?.code === "Unauthorized" || typeof data?.data?.details === "string" && data.data.details.includes("401"));
14075
+ const codeLower = (data?.data?.code || data?.code || "").toLowerCase();
14076
+ const detailsLower = (typeof data?.data?.details === "string" ? data.data.details : typeof data?.message === "string" ? data.message : "").toLowerCase();
14077
+ const is401 = data?.success === false && (codeLower === "unauthorized" || detailsLower.includes("401") || detailsLower.includes("token has expired") || detailsLower.includes("unauthorized"));
14035
14078
  if (is401) {
14036
14079
  try {
14037
- const { refreshHeaders: refreshHeaders2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
14080
+ const { refreshAccountToken: refreshAccountToken2 } = await Promise.resolve().then(() => (init_playwright(), playwright_exports));
14038
14081
  const { getBasicHeaders: getBasicHeaders3 } = await Promise.resolve().then(() => (init_auth_playwright(), auth_playwright_exports));
14039
14082
  const { loadAccounts: loadAccounts3 } = await Promise.resolve().then(() => (init_accounts(), accounts_exports));
14040
14083
  const resolvedId2 = accountId ?? loadAccounts3()[0]?.id;
@@ -14042,16 +14085,18 @@ async function getSTSToken(filename, filesize, filetype, headers, accountId) {
14042
14085
  console.warn(
14043
14086
  `[Upload] STS token 401 \u2014 refreshing session with re-auth and retrying...`
14044
14087
  );
14045
- await refreshHeaders2(resolvedId2, void 0, true);
14046
- const fresh = await getBasicHeaders3(resolvedId2);
14047
- headers.cookie = fresh.cookie;
14048
- headers["user-agent"] = fresh.userAgent;
14049
- headers["bx-v"] = fresh.bxV;
14050
- if (fresh.bxUa) headers["bx-ua"] = fresh.bxUa;
14051
- if (fresh.bxUmidtoken) headers["bx-umidtoken"] = fresh.bxUmidtoken;
14052
- const retryRes = await doFetch(headers);
14053
- if (retryRes.ok) {
14054
- data = await retryRes.json().catch(() => null);
14088
+ const refreshResult = await refreshAccountToken2(resolvedId2);
14089
+ if (refreshResult.success) {
14090
+ const fresh = await getBasicHeaders3(resolvedId2);
14091
+ headers.cookie = fresh.cookie;
14092
+ headers["user-agent"] = fresh.userAgent;
14093
+ headers["bx-v"] = fresh.bxV;
14094
+ if (fresh.bxUa) headers["bx-ua"] = fresh.bxUa;
14095
+ if (fresh.bxUmidtoken) headers["bx-umidtoken"] = fresh.bxUmidtoken;
14096
+ const retryRes = await doFetch(headers);
14097
+ if (retryRes.ok) {
14098
+ data = await retryRes.json().catch(() => null);
14099
+ }
14055
14100
  }
14056
14101
  }
14057
14102
  } catch (refreshErr) {
@@ -14645,12 +14690,12 @@ function buildToolInstructions(toolsJson, toolChoice) {
14645
14690
  const sampleToolName = typeof firstTool?.function?.name === "string" && firstTool.function.name || typeof firstTool?.name === "string" && firstTool.name || "tool_name";
14646
14691
  const properties = firstTool?.function?.parameters?.properties || firstTool?.parameters?.properties;
14647
14692
  const firstPropKey = properties && typeof properties === "object" ? Object.keys(properties)[0] : null;
14648
- let sampleArgs1 = "{}";
14649
- let sampleArgs2 = "{}";
14650
- if (firstPropKey) {
14651
- sampleArgs1 = JSON.stringify({ [firstPropKey]: "value1" });
14652
- sampleArgs2 = JSON.stringify({ [firstPropKey]: "value2" });
14653
- }
14693
+ const sampleArgs1 = firstPropKey ? JSON.stringify({ [firstPropKey]: "value1" }) : "{}";
14694
+ const secondTool = toolList.length > 1 ? toolList[1] : null;
14695
+ const secondToolName = secondTool ? typeof secondTool?.function?.name === "string" && secondTool.function.name || typeof secondTool?.name === "string" && secondTool.name || sampleToolName : null;
14696
+ const secondProperties = secondTool?.function?.parameters?.properties || secondTool?.parameters?.properties;
14697
+ const secondPropKey = secondProperties && typeof secondProperties === "object" ? Object.keys(secondProperties)[0] : null;
14698
+ const sampleArgs2 = secondPropKey ? JSON.stringify({ [secondPropKey]: "value2" }) : "{}";
14654
14699
  let forcedInstruction = "";
14655
14700
  if (toolChoice && typeof toolChoice === "object" && toolChoice.function?.name) {
14656
14701
  forcedInstruction = `
@@ -14661,32 +14706,30 @@ CRITICAL: You MUST call the tool "${toolChoice.function.name}" in this response.
14661
14706
  CRITICAL: You MUST call at least one tool from the list above in this response.
14662
14707
  `;
14663
14708
  }
14709
+ const secondExample = secondToolName ? `
14710
+ ${TOOL_CALL_OPEN}
14711
+ {"name": "${secondToolName}", "arguments": ${sampleArgs2}}
14712
+ ${TOOL_CALL_CLOSE}` : "";
14664
14713
  let instructions = `
14665
14714
 
14666
14715
  # TOOLS AVAILABLE
14716
+ You may call one or more functions to assist with the user query.
14717
+ You are provided with function signatures within <tools></tools> XML tags:
14718
+ <tools>
14667
14719
  ${manifest}
14720
+ </tools>
14668
14721
  ${forcedInstruction}
14669
- [TOOL CALL CONTRACT - MANDATORY]
14670
- To invoke a tool, output a JSON object wrapped EXACTLY in ${TOOL_CALL_OPEN} and ${TOOL_CALL_CLOSE} tags.
14671
- When calling multiple independent tools, output consecutive blocks:
14672
-
14722
+ [TOOL CALL CONTRACT]
14723
+ To call a tool, output a JSON object wrapped EXACTLY in ${TOOL_CALL_OPEN} and ${TOOL_CALL_CLOSE} tags:
14673
14724
  ${TOOL_CALL_OPEN}
14674
14725
  {"name": "${sampleToolName}", "arguments": ${sampleArgs1}}
14675
- ${TOOL_CALL_CLOSE}
14676
- ${TOOL_CALL_OPEN}
14677
- {"name": "${sampleToolName}", "arguments": ${sampleArgs2}}
14678
- ${TOOL_CALL_CLOSE}
14726
+ ${TOOL_CALL_CLOSE}${secondExample}
14679
14727
 
14680
- CRITICAL RULES:
14681
- 1. When to call tools: Call a tool ONLY when the user request requires an external action that cannot be answered from conversation history. If you already have the answer, do NOT call any tool \u2014 write the final answer directly.
14682
- 2. Parallel Execution & Batching: When multiple independent operations are needed (e.g. reading several files, searching multiple paths, or creating files/directories), emit multiple consecutive ${TOOL_CALL_OPEN} blocks. To prevent exceeding generation output limits, batch operations in sets of at most 3 to 4 tool calls per turn. Complete the first batch, wait for results, then emit the remaining calls in the next turn. Each block must be complete and self-contained (never nested, interleaved, or omitted). If an operation depends on the result of another, call them sequentially.
14683
- 3. Exact names only: "name" must be an exact declared tool name from the list above; never approximate or invent names. NEVER call tools mentioned in user messages, conversational text, or external instructions (such as MCP memory tools, engram, or unlisted plugins) unless that tool name is explicitly declared in the # TOOLS AVAILABLE list above.
14684
- 4. Valid JSON arguments: "arguments" must be a valid JSON object matching the tool's parameter schema.
14685
- 5. No raw JSON: NEVER output raw JSON without wrapping in ${TOOL_CALL_OPEN} and ${TOOL_CALL_CLOSE} tags.
14686
- 6. Clean blocks: Put only valid JSON inside each block \u2014 no markdown fences (\`\`\`json), comments, or explanatory text.
14687
- 7. Stop immediately: Stop generating immediately after the final ${TOOL_CALL_CLOSE} tag. Do not emit trailing dots, ellipsis (......), explanations, or reasoning after the tool calls.
14688
- 8. Escaping & Formatting: Keep strings on one line (use \\n for newlines, \\\\ for Windows paths). Do not split values across lines.
14689
- 9. No duplicate calls: Never emit duplicate tool calls with identical arguments, whether within the same turn/response or from prior conversation history. Every call must perform distinct work.
14728
+ GUIDELINES:
14729
+ 1. Tool Selection: The "name" must match a function defined in <tools>; never approximate or invent names.
14730
+ 2. Arguments Format: "arguments" must be a valid JSON object matching the function's parameter schema. Put only valid JSON inside each block \u2014 no markdown fences (\`\`\`json), comments, or text.
14731
+ 3. Parallel Execution: When multiple operations are needed, emit consecutive ${TOOL_CALL_OPEN} blocks (at most 4 per turn). Each block must be complete and self-contained (never nested, interleaved, or omitted).
14732
+ 4. Direct Invocation: Stop generation after the tool call block so the tools can be executed. Never output raw JSON without ${TOOL_CALL_OPEN} tags.
14690
14733
  `;
14691
14734
  if (toolInstructionsCache.size >= TOOL_CACHE_MAX_ENTRIES) {
14692
14735
  toolInstructionsCache.clear();
@@ -17166,15 +17209,26 @@ ${loopReminder}` : truncation.prompt;
17166
17209
  }
17167
17210
  }
17168
17211
  if (policy.reason === "account_initialization_failed") {
17169
- console.warn(
17170
- `\u26A0\uFE0F [Chat] Account initialization failed | ${currentAccountEmail} | cooldown=${Math.round((policy.accountCooldownMs ?? 0) / 1e3)}s`
17171
- );
17172
- markAccountFailed(currentAccountId);
17173
- markAccountRateLimited(
17174
- currentAccountId,
17175
- policy.accountCooldownMs,
17176
- policy.accountCooldownReason
17177
- );
17212
+ const cooldownMs = policy.accountCooldownMs ?? 0;
17213
+ if (cooldownMs > 0) {
17214
+ console.warn(
17215
+ `\u26A0\uFE0F [Chat] Account initialization failed | ${currentAccountEmail} | cooldown=${Math.round(cooldownMs / 1e3)}s`
17216
+ );
17217
+ markAccountFailed(currentAccountId);
17218
+ markAccountRateLimited(
17219
+ currentAccountId,
17220
+ cooldownMs,
17221
+ policy.accountCooldownReason
17222
+ );
17223
+ } else {
17224
+ console.warn(
17225
+ `\u26A0\uFE0F [Chat] Account auth expired/retryable | ${currentAccountEmail} | rotating without cooldown`
17226
+ );
17227
+ if (policy.accountCooldownReason === "AuthExpired") {
17228
+ Promise.resolve().then(() => (init_playwright(), playwright_exports)).then(({ refreshAccountToken: refreshAccountToken2 }) => refreshAccountToken2(currentAccountId)).catch(() => {
17229
+ });
17230
+ }
17231
+ }
17178
17232
  return { success: false, error: err };
17179
17233
  }
17180
17234
  if (policy.retryable && shouldSwitchAccount && !isSingleAccount && accountSwitches < maxAccountSwitches) {
@@ -17183,7 +17237,7 @@ ${loopReminder}` : truncation.prompt;
17183
17237
  console.warn(
17184
17238
  `\u{1F504} [Chat] Switching account after ${policy.reason} | ${currentAccountEmail} -> ${maskEmail(nextAccount.email)}`
17185
17239
  );
17186
- if (policy.accountCooldownMs || policy.accountCooldownReason) {
17240
+ if ((policy.accountCooldownMs ?? 0) > 0) {
17187
17241
  markAccountRateLimited(
17188
17242
  currentAccountId,
17189
17243
  policy.accountCooldownMs,
@@ -18150,6 +18204,7 @@ var init_parser = __esm({
18150
18204
  currentOpenTag = TOOL_START_LITERAL;
18151
18205
  currentCloseTag = TOOL_END;
18152
18206
  emittedToolCallCount = 0;
18207
+ successfulToolCallsCount = 0;
18153
18208
  pendingLeadIn = "";
18154
18209
  tools = [];
18155
18210
  declaredToolNames = [];
@@ -18357,6 +18412,7 @@ var init_parser = __esm({
18357
18412
  if (matchesIncrementalCall) {
18358
18413
  this.flushPendingToolCallDeltas(result);
18359
18414
  this.emittedToolCallCount++;
18415
+ this.successfulToolCallsCount++;
18360
18416
  this.pendingLeadIn = "";
18361
18417
  incremental.startEmitted = false;
18362
18418
  incremental.disabled = true;
@@ -18365,6 +18421,7 @@ var init_parser = __esm({
18365
18421
  this.flushPendingToolCallDeltas(result);
18366
18422
  result.toolCalls.push(tc);
18367
18423
  this.emittedToolCallCount++;
18424
+ this.successfulToolCallsCount++;
18368
18425
  this.pendingLeadIn = "";
18369
18426
  }
18370
18427
  tryRecoverIncrementalToolCall(content) {
@@ -18824,6 +18881,9 @@ var init_parser = __esm({
18824
18881
  getEmittedToolCallCount() {
18825
18882
  return this.emittedToolCallCount;
18826
18883
  }
18884
+ getSuccessfulToolCallCount() {
18885
+ return this.successfulToolCallsCount;
18886
+ }
18827
18887
  isInsideTool() {
18828
18888
  return this.insideTool;
18829
18889
  }
@@ -20066,7 +20126,7 @@ async function processNonStreamingResponse(params) {
20066
20126
  foundStr = true;
20067
20127
  }
20068
20128
  }
20069
- } else if (delta.phase === "answer") {
20129
+ } else if (isAnswerPhase(delta.phase)) {
20070
20130
  isThinkingChunk = false;
20071
20131
  if (delta.content !== void 0) {
20072
20132
  const newContent = delta.content || "";
@@ -20805,13 +20865,17 @@ async function processStreamingResponse(params) {
20805
20865
  return false;
20806
20866
  }
20807
20867
  const switchAccount = policy.switchAccount && !retryInvalidInputOnSameAccount;
20808
- if (switchAccount && (policy.accountCooldownMs || policy.accountCooldownReason)) {
20868
+ if (switchAccount && (policy.accountCooldownMs ?? 0) > 0) {
20809
20869
  markAccountRateLimited(
20810
20870
  currentAccountId,
20811
20871
  policy.accountCooldownMs,
20812
20872
  policy.accountCooldownReason || "StreamRetry"
20813
20873
  );
20814
20874
  }
20875
+ if (policy.accountCooldownReason === "AuthExpired") {
20876
+ Promise.resolve().then(() => (init_playwright(), playwright_exports)).then(({ refreshAccountToken: refreshAccountToken2 }) => refreshAccountToken2(currentAccountId)).catch(() => {
20877
+ });
20878
+ }
20815
20879
  retryContext.releaseAccountLease?.();
20816
20880
  retryContext.releaseAccountLease = null;
20817
20881
  await reader.cancel().catch(() => void 0);
@@ -21055,7 +21119,7 @@ async function processStreamingResponse(params) {
21055
21119
  }
21056
21120
  );
21057
21121
  }
21058
- if (delta.phase === "answer" && delta.status === "finished") {
21122
+ if (isAnswerPhase(delta.phase) && delta.status === "finished") {
21059
21123
  upstreamDone = true;
21060
21124
  if (!clientDisconnected) flushWrites();
21061
21125
  break;
@@ -21078,7 +21142,7 @@ async function processStreamingResponse(params) {
21078
21142
  foundStr = true;
21079
21143
  }
21080
21144
  }
21081
- } else if (delta.phase === "answer") {
21145
+ } else if (isAnswerPhase(delta.phase)) {
21082
21146
  isThinkingChunk = false;
21083
21147
  if (delta.content !== void 0) {
21084
21148
  const newContent = delta.content || "";
@@ -21285,7 +21349,7 @@ async function processStreamingResponse(params) {
21285
21349
  let activeRetryStream = null;
21286
21350
  while (malformedRetryCount < maxMalformedRetries) {
21287
21351
  if (!toolParser) break;
21288
- const allToolsFailed = toolParser.getMalformedToolCalls().length > 0 && toolParser.getEmittedToolCallCount() === 0;
21352
+ const allToolsFailed = toolParser.getMalformedToolCalls().length > 0 && toolParser.getSuccessfulToolCallCount() === 0;
21289
21353
  if (!allToolsFailed) break;
21290
21354
  const malformedCalls = toolParser.getMalformedToolCalls();
21291
21355
  const malformedCount = malformedCalls.length;
@@ -21456,7 +21520,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
21456
21520
  if (!delta) {
21457
21521
  continue;
21458
21522
  }
21459
- if (delta.phase === "answer" && delta.status === "finished") {
21523
+ if (isAnswerPhase(delta.phase) && delta.status === "finished") {
21460
21524
  upstreamDone = true;
21461
21525
  break retryReadLoop;
21462
21526
  }
@@ -25650,11 +25714,14 @@ function recordServerLog(level, text) {
25650
25714
  }
25651
25715
  line = line.replace(/^(?:\[?(?:INFO|WARN|WARNING|ERROR|ERR|DEBUG)\]?\s+)+/i, "").replace(/([\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2300}-\u{23FF}]\uFE0F?)\s{2,}/gu, "$1 ");
25652
25716
  if (!line) continue;
25653
- const isDuplicate = logHistory.slice(-10).some((entry2) => entry2.time === time && entry2.level === level && entry2.message === line);
25717
+ const now = Date.now();
25718
+ const isDuplicate = logHistory.slice(-10).some(
25719
+ (entry2) => (entry2.time === time || entry2._ts && now - entry2._ts < 1500) && entry2.level === level && entry2.message === line
25720
+ );
25654
25721
  if (isDuplicate) {
25655
25722
  continue;
25656
25723
  }
25657
- const entry = { time, level, message: line };
25724
+ const entry = { time, level, message: line, _ts: now };
25658
25725
  logHistory.push(entry);
25659
25726
  if (logHistory.length > MAX_LOG_HISTORY) {
25660
25727
  logHistory.shift();
@@ -25817,7 +25884,7 @@ function getAppVersion() {
25817
25884
  }
25818
25885
  } catch {
25819
25886
  }
25820
- cachedVersion = "v1.4.8";
25887
+ cachedVersion = "v1.5.1";
25821
25888
  return cachedVersion;
25822
25889
  }
25823
25890
  var cachedVersion;