qwenproxy-cli 1.6.4 → 1.6.6

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
@@ -2816,6 +2816,7 @@ var init_mutex = __esm({
2816
2816
  locked = false;
2817
2817
  lockedAt = 0;
2818
2818
  lockedByKey = "";
2819
+ epoch = 0;
2819
2820
  async acquire(timeoutMs = 3e5, key = "") {
2820
2821
  const holdLimitMs = this.maxHoldMs;
2821
2822
  if (this.locked && Date.now() - this.lockedAt > holdLimitMs) {
@@ -2826,6 +2827,7 @@ var init_mutex = __esm({
2826
2827
  this.locked = false;
2827
2828
  this.lockedAt = 0;
2828
2829
  this.lockedByKey = "";
2830
+ this.epoch++;
2829
2831
  }
2830
2832
  if (!this.locked) {
2831
2833
  this.locked = true;
@@ -2864,10 +2866,12 @@ var init_mutex = __esm({
2864
2866
  }
2865
2867
  }
2866
2868
  createRelease() {
2869
+ const epoch = this.epoch;
2867
2870
  let released = false;
2868
2871
  return () => {
2869
2872
  if (released) return;
2870
2873
  released = true;
2874
+ if (epoch !== this.epoch) return;
2871
2875
  this.release();
2872
2876
  };
2873
2877
  }
@@ -4016,6 +4020,49 @@ var init_waf_isolation = __esm({
4016
4020
  }
4017
4021
  });
4018
4022
 
4023
+ // src/utils/timezone.ts
4024
+ function formatAsciiTimezone(date = /* @__PURE__ */ new Date()) {
4025
+ const dayName = DAYS[date.getDay()];
4026
+ const monthName = MONTHS[date.getMonth()];
4027
+ const day = String(date.getDate()).padStart(2, "0");
4028
+ const year = date.getFullYear();
4029
+ const hours = String(date.getHours()).padStart(2, "0");
4030
+ const minutes = String(date.getMinutes()).padStart(2, "0");
4031
+ const seconds = String(date.getSeconds()).padStart(2, "0");
4032
+ const offsetMinutes = -date.getTimezoneOffset();
4033
+ const sign = offsetMinutes >= 0 ? "+" : "-";
4034
+ const absOffset = Math.abs(offsetMinutes);
4035
+ const offsetHours = String(Math.floor(absOffset / 60)).padStart(2, "0");
4036
+ const offsetMins = String(absOffset % 60).padStart(2, "0");
4037
+ const tzOffset = `GMT${sign}${offsetHours}${offsetMins}`;
4038
+ return `${dayName} ${monthName} ${day} ${year} ${hours}:${minutes}:${seconds} ${tzOffset}`;
4039
+ }
4040
+ function sanitizeHeaderForFetch(val) {
4041
+ if (typeof val !== "string") return "";
4042
+ return val.replace(/[^\x00-\xFF]/g, "");
4043
+ }
4044
+ var DAYS, MONTHS;
4045
+ var init_timezone = __esm({
4046
+ "src/utils/timezone.ts"() {
4047
+ "use strict";
4048
+ DAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
4049
+ MONTHS = [
4050
+ "Jan",
4051
+ "Feb",
4052
+ "Mar",
4053
+ "Apr",
4054
+ "May",
4055
+ "Jun",
4056
+ "Jul",
4057
+ "Aug",
4058
+ "Sep",
4059
+ "Oct",
4060
+ "Nov",
4061
+ "Dec"
4062
+ ];
4063
+ }
4064
+ });
4065
+
4019
4066
  // src/services/qwen-headers.ts
4020
4067
  import { v4 as uuidv4 } from "uuid";
4021
4068
  function getSystemAcceptLanguage(customLocale) {
@@ -4059,8 +4106,8 @@ function buildQwenRequestHeaders(opts) {
4059
4106
  "bx-v": opts.bxV || "2.5.37",
4060
4107
  source: "web",
4061
4108
  version: opts.version || getQwenWebVersion(),
4062
- timezone: opts.extra?.timezone || opts.extra?.Timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4063
- Timezone: opts.extra?.Timezone || opts.extra?.timezone || (/* @__PURE__ */ new Date()).toString().split(" (")[0],
4109
+ timezone: opts.extra?.timezone || opts.extra?.Timezone || formatAsciiTimezone(),
4110
+ Timezone: opts.extra?.Timezone || opts.extra?.timezone || formatAsciiTimezone(),
4064
4111
  // Use the real browser client-hints when captured (anti-hardcoded); fall
4065
4112
  // back to the static fingerprint otherwise.
4066
4113
  "sec-ch-ua": opts.secChUa || `"Google Chrome";v="${getChromeMajor()}", "Chromium";v="${getChromeMajor()}", "Not.A/Brand";v="99"`,
@@ -4087,6 +4134,7 @@ var init_qwen_headers = __esm({
4087
4134
  init_qwen_url();
4088
4135
  init_config();
4089
4136
  init_fingerprint();
4137
+ init_timezone();
4090
4138
  dynamicWebVersion = null;
4091
4139
  QWEN_WEB_VERSION = config.qwen.webVersion;
4092
4140
  DEFAULT_QWEN_USER_AGENT = getDefaultQwenUserAgent(151);
@@ -5509,6 +5557,13 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
5509
5557
  await existingInit;
5510
5558
  return;
5511
5559
  }
5560
+ let resolveInit;
5561
+ let rejectInit;
5562
+ const inFlightDeferred = new Promise((res, rej) => {
5563
+ resolveInit = res;
5564
+ rejectInit = rej;
5565
+ });
5566
+ inFlightAccountInits.set(account.id, inFlightDeferred);
5512
5567
  const initPromise = (async () => {
5513
5568
  const release = await acquireAccountMutex(
5514
5569
  account.id,
@@ -5723,6 +5778,22 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
5723
5778
  }
5724
5779
  }
5725
5780
  }
5781
+ const currentCache = getHeaderCache(account.id);
5782
+ const currentCookie = currentCache?.headers?.cookie || "";
5783
+ if (currentCookie && isTokenExpiringSoon(currentCookie, 5)) {
5784
+ try {
5785
+ const refreshRes = await refreshAccountToken(account.id);
5786
+ if (refreshRes.success) {
5787
+ console.log(
5788
+ `\u26A1 [Playwright] Silently refreshed expired token restored from DB for ${maskEmail(account.email)}`
5789
+ );
5790
+ }
5791
+ } catch (err) {
5792
+ console.warn(
5793
+ `\u26A0\uFE0F [Playwright] Failed to proactively refresh expired token from DB for ${maskEmail(account.email)}: ${err?.message || err}`
5794
+ );
5795
+ }
5796
+ }
5726
5797
  return;
5727
5798
  }
5728
5799
  const cookies = await acctContext.cookies();
@@ -5777,9 +5848,12 @@ async function initPlaywrightForAccount(rawAccount, headless = config.playwright
5777
5848
  release();
5778
5849
  }
5779
5850
  })();
5780
- inFlightAccountInits.set(account.id, initPromise);
5781
5851
  try {
5782
5852
  await initPromise;
5853
+ resolveInit();
5854
+ } catch (error) {
5855
+ rejectInit(error);
5856
+ throw error;
5783
5857
  } finally {
5784
5858
  inFlightAccountInits.delete(account.id);
5785
5859
  }
@@ -6010,7 +6084,7 @@ async function startEmailOtpLogin(email, options = {}) {
6010
6084
  email: normalizedEmail,
6011
6085
  requestId,
6012
6086
  version: getQwenWebVersion() || "0.3.12",
6013
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
6087
+ timezone: formatAsciiTimezone()
6014
6088
  }
6015
6089
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
6016
6090
  if (!evalRes.ok || evalRes.data?.success !== true) {
@@ -6105,7 +6179,7 @@ async function verifyEmailOtpLogin(sessionId, code) {
6105
6179
  code: cleanCode,
6106
6180
  requestId,
6107
6181
  version: getQwenWebVersion() || "0.3.12",
6108
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0]
6182
+ timezone: formatAsciiTimezone()
6109
6183
  }
6110
6184
  ).catch((err) => ({ ok: false, error: err?.message || String(err) }));
6111
6185
  if (!evalRes.ok || evalRes.data?.success !== true || !evalRes.data?.data) {
@@ -6386,7 +6460,7 @@ async function loginViaApi(page, email, password) {
6386
6460
  const signinUrl = "https://auth.qwen.ai/api/v2/auths/signin";
6387
6461
  const requestId = crypto5.randomUUID();
6388
6462
  const evalRes = await page.evaluate(
6389
- async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2 }) => {
6463
+ async ({ email: email2, password: password2, signinUrl: signinUrl2, requestId: requestId2, tz }) => {
6390
6464
  try {
6391
6465
  const response = await fetch(signinUrl2, {
6392
6466
  method: "POST",
@@ -6398,7 +6472,7 @@ async function loginViaApi(page, email, password) {
6398
6472
  source: "web",
6399
6473
  version: "0.3.11",
6400
6474
  "x-request-origin": "https://chat.qwen.ai",
6401
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
6475
+ timezone: tz,
6402
6476
  "x-request-id": requestId2
6403
6477
  },
6404
6478
  body: JSON.stringify({ email: email2, password: password2 })
@@ -6409,7 +6483,7 @@ async function loginViaApi(page, email, password) {
6409
6483
  return { ok: false, error: e.message };
6410
6484
  }
6411
6485
  },
6412
- { email, password: hashedPassword, signinUrl, requestId }
6486
+ { email, password: hashedPassword, signinUrl, requestId, tz: formatAsciiTimezone() }
6413
6487
  ).catch(() => null);
6414
6488
  let data = evalRes?.data;
6415
6489
  let signinSuccess = Boolean(
@@ -6418,7 +6492,7 @@ async function loginViaApi(page, email, password) {
6418
6492
  if (!signinSuccess && !data?.data?.code) {
6419
6493
  const fallbackUrl = qwenUrl("/api/v2/auths/signin");
6420
6494
  const fallbackRes = await page.evaluate(
6421
- async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2 }) => {
6495
+ async ({ email: email2, password: password2, fallbackUrl: fallbackUrl2, requestId: requestId2, tz }) => {
6422
6496
  try {
6423
6497
  const response = await fetch(fallbackUrl2, {
6424
6498
  method: "POST",
@@ -6429,7 +6503,7 @@ async function loginViaApi(page, email, password) {
6429
6503
  "content-type": "application/json",
6430
6504
  source: "web",
6431
6505
  version: "0.3.11",
6432
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
6506
+ timezone: tz,
6433
6507
  "x-request-id": requestId2
6434
6508
  },
6435
6509
  body: JSON.stringify({ email: email2, password: password2 })
@@ -6440,7 +6514,7 @@ async function loginViaApi(page, email, password) {
6440
6514
  return { ok: false, error: e.message };
6441
6515
  }
6442
6516
  },
6443
- { email, password: hashedPassword, fallbackUrl, requestId }
6517
+ { email, password: hashedPassword, fallbackUrl, requestId, tz: formatAsciiTimezone() }
6444
6518
  ).catch(() => null);
6445
6519
  if (fallbackRes?.data) {
6446
6520
  data = fallbackRes.data;
@@ -6772,7 +6846,7 @@ async function refreshAccountToken(accountId) {
6772
6846
  if (refreshToken) {
6773
6847
  try {
6774
6848
  const refreshResult = await page.evaluate(
6775
- async ({ rTok }) => {
6849
+ async ({ rTok, tz }) => {
6776
6850
  try {
6777
6851
  const response = await fetch("https://auth.qwen.ai/api/v2/auths/refresh", {
6778
6852
  method: "GET",
@@ -6782,7 +6856,7 @@ async function refreshAccountToken(accountId) {
6782
6856
  source: "web",
6783
6857
  version: "0.3.11",
6784
6858
  "x-request-origin": "https://chat.qwen.ai",
6785
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
6859
+ timezone: tz,
6786
6860
  authorization: `Bearer ${rTok}`
6787
6861
  }
6788
6862
  });
@@ -6792,7 +6866,7 @@ async function refreshAccountToken(accountId) {
6792
6866
  return { ok: false, error: e.message };
6793
6867
  }
6794
6868
  },
6795
- { rTok: refreshToken }
6869
+ { rTok: refreshToken, tz: formatAsciiTimezone() }
6796
6870
  );
6797
6871
  const json = refreshResult?.data;
6798
6872
  if (json && json.success === true && json.data) {
@@ -6830,6 +6904,7 @@ async function refreshAccountToken(accountId) {
6830
6904
  const cache3 = getHeaderCache(accountId);
6831
6905
  cache3.headers.cookie = cookieStr;
6832
6906
  cache3.lastRefresh = Date.now();
6907
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
6833
6908
  markAccountHeadersReady(accountId);
6834
6909
  saveAuthSession2(accountId, {
6835
6910
  cookie: cookieStr,
@@ -6864,6 +6939,7 @@ async function refreshAccountToken(accountId) {
6864
6939
  const cache3 = getHeaderCache(accountId);
6865
6940
  cache3.headers.cookie = cookieStr;
6866
6941
  cache3.lastRefresh = Date.now();
6942
+ cookieCaches.set(accountId, { cookie: cookieStr, timestamp: Date.now() });
6867
6943
  markAccountHeadersReady(accountId);
6868
6944
  saveAuthSession2(accountId, {
6869
6945
  cookie: cookieStr,
@@ -7918,11 +7994,11 @@ async function keepAlivePlaywrightAccount(accountId) {
7918
7994
  const now = Date.now();
7919
7995
  const currentUrl = page.url();
7920
7996
  const lastRefreshAttempt = lastProactiveTokenRefresh.get(accountId) ?? 0;
7921
- const cookie = await getCookies(accountId);
7997
+ const cookie = await getCookies(accountId, true);
7922
7998
  if (cookie && isTokenExpiringSoon(cookie, 5) && now - lastRefreshAttempt > 6e4) {
7923
7999
  lastProactiveTokenRefresh.set(accountId, now);
7924
- console.log(
7925
- `\u{1F493} [SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
8000
+ logger.debug(
8001
+ `[SessionKeeper] Account ${accountId} access token expiring soon (<5m); silently refreshing...`
7926
8002
  );
7927
8003
  const res = await refreshAccountToken(accountId);
7928
8004
  if (res.success) {
@@ -7930,6 +8006,9 @@ async function keepAlivePlaywrightAccount(accountId) {
7930
8006
  touchAccountActivity(accountId);
7931
8007
  return true;
7932
8008
  }
8009
+ logger.warn(
8010
+ `[SessionKeeper] Failed to silently refresh expiring token for ${accountId}: ${res.error || "unknown error"}`
8011
+ );
7933
8012
  }
7934
8013
  const lastNavigation = lastKeepAliveNavigation.get(accountId) ?? 0;
7935
8014
  const shouldNavigate = !currentUrl.startsWith(qwenOrigin()) || now - lastNavigation > config.sessionKeeper.navigationIntervalMs;
@@ -8167,6 +8246,7 @@ var init_playwright = __esm({
8167
8246
  init_qwen_headers();
8168
8247
  init_paths();
8169
8248
  init_jwt();
8249
+ init_timezone();
8170
8250
  requireLocal = createRequire(import.meta.url);
8171
8251
  contextInitHooks = [];
8172
8252
  ACCOUNT_MUTEX_MAX_HOLD_MS = 18e4;
@@ -8180,7 +8260,7 @@ var init_playwright = __esm({
8180
8260
  headerCaches = /* @__PURE__ */ new Map();
8181
8261
  HEADER_CACHE_TTL = 24 * 60 * 60 * 1e3;
8182
8262
  HEADER_REFRESH_THRESHOLD = 0.8;
8183
- COOKIE_CACHE_TTL = 15 * 60 * 1e3;
8263
+ COOKIE_CACHE_TTL = 60 * 1e3;
8184
8264
  cookieCaches = /* @__PURE__ */ new Map();
8185
8265
  lastAccountActivity = /* @__PURE__ */ new Map();
8186
8266
  lastKeepAliveNavigation = /* @__PURE__ */ new Map();
@@ -9599,8 +9679,8 @@ function createUsageAccumulator(estimatedPromptTokens) {
9599
9679
  function applyUpstreamUsage(accumulator, candidate) {
9600
9680
  if (!candidate || typeof candidate !== "object") return;
9601
9681
  const usage = candidate;
9602
- const promptTokens = asFiniteNumber(usage.input_tokens);
9603
- const completionTokens = asFiniteNumber(usage.output_tokens);
9682
+ const promptTokens = asFiniteNumber(usage.input_tokens) ?? asFiniteNumber(usage.prompt_tokens);
9683
+ const completionTokens = asFiniteNumber(usage.output_tokens) ?? asFiniteNumber(usage.completion_tokens);
9604
9684
  const totalTokens = asFiniteNumber(usage.total_tokens);
9605
9685
  if (promptTokens !== null) {
9606
9686
  accumulator.promptTokens = promptTokens;
@@ -11632,6 +11712,8 @@ __export(qwen_exports, {
11632
11712
  QwenUpstreamError: () => QwenUpstreamError,
11633
11713
  QwenUpstreamUnavailableError: () => QwenUpstreamUnavailableError,
11634
11714
  RetryableQwenStreamError: () => RetryableQwenStreamError,
11715
+ STREAM_SLOT_MAX_HOLD_MS: () => STREAM_SLOT_MAX_HOLD_MS,
11716
+ acquireAccountStreamLock: () => acquireAccountStreamLock,
11635
11717
  acquireNewQwenChatSession: () => acquireNewQwenChatSession,
11636
11718
  buildCapturedQwenHeaders: () => buildCapturedQwenHeaders,
11637
11719
  buildChatNewBody: () => buildChatNewBody,
@@ -11645,10 +11727,13 @@ __export(qwen_exports, {
11645
11727
  deleteAllQwenChats: () => deleteAllQwenChats,
11646
11728
  deleteSingleQwenChat: () => deleteSingleQwenChat,
11647
11729
  disableNativeTools: () => disableNativeTools,
11730
+ ensureStaleSlotSweeperStarted: () => ensureStaleSlotSweeperStarted,
11648
11731
  fetchQwenModels: () => fetchQwenModels,
11649
11732
  fetchRemoteQwenChats: () => fetchRemoteQwenChats,
11650
11733
  flushLogicalThreadState: () => flushLogicalThreadState,
11734
+ getAccountStreamMutex: () => getAccountStreamMutex,
11651
11735
  getAnyCachedQwenModels: () => getAnyCachedQwenModels,
11736
+ getBrowserFetchHeaders: () => getBrowserFetchHeaders,
11652
11737
  getInFlightPersonalizationCount: () => getInFlightPersonalizationCount,
11653
11738
  getLogicalThreadState: () => getLogicalThreadState,
11654
11739
  getQwenErrorCode: () => getQwenErrorCode,
@@ -11664,6 +11749,8 @@ __export(qwen_exports, {
11664
11749
  requestQwenSettingsDirectFetch: () => requestQwenSettingsDirectFetch,
11665
11750
  requestQwenTextInBrowser: () => requestQwenTextInBrowser,
11666
11751
  setToolCapNotice: () => setToolCapNotice,
11752
+ stopStaleSlotSweeperForTests: () => stopStaleSlotSweeperForTests,
11753
+ sweepStaleAccountStreamSlots: () => sweepStaleAccountStreamSlots,
11667
11754
  syncQwenRequestPersonalization: () => syncQwenRequestPersonalization,
11668
11755
  updateLogicalThreadParent: () => updateLogicalThreadParent,
11669
11756
  updateLogicalThreadState: () => updateLogicalThreadState,
@@ -11950,7 +12037,7 @@ function buildCapturedQwenHeaders(headers, options = {}) {
11950
12037
  function getAccountStreamMutex(accountId) {
11951
12038
  let slots2 = accountStreamMutexes.get(accountId);
11952
12039
  if (!slots2) {
11953
- slots2 = { active: 0, queue: [] };
12040
+ slots2 = { activeSlots: /* @__PURE__ */ new Map(), queue: [] };
11954
12041
  accountStreamMutexes.set(accountId, slots2);
11955
12042
  }
11956
12043
  return slots2;
@@ -11958,27 +12045,71 @@ function getAccountStreamMutex(accountId) {
11958
12045
  function streamSlotCapacity() {
11959
12046
  return Math.max(1, config.concurrency.maxStreamsPerAccount);
11960
12047
  }
11961
- function createStreamSlotRelease(slots2) {
12048
+ function createStreamSlotRelease(slots2, slotId) {
11962
12049
  let released = false;
11963
12050
  return () => {
11964
12051
  if (released) return;
11965
12052
  released = true;
11966
- slots2.active -= 1;
12053
+ slots2.activeSlots.delete(slotId);
11967
12054
  const next = slots2.queue.shift();
11968
12055
  if (next) {
11969
- slots2.active += 1;
11970
- next();
12056
+ const nextId = nextStreamSlotId++;
12057
+ slots2.activeSlots.set(nextId, Date.now());
12058
+ next(createStreamSlotRelease(slots2, nextId));
11971
12059
  }
11972
12060
  };
11973
12061
  }
12062
+ function sweepStaleAccountStreamSlots(maxHoldMs = STREAM_SLOT_MAX_HOLD_MS) {
12063
+ const now = Date.now();
12064
+ let sweptCount = 0;
12065
+ for (const [accountId, slots2] of accountStreamMutexes.entries()) {
12066
+ for (const [slotId, startedAt] of Array.from(slots2.activeSlots.entries())) {
12067
+ if (now - startedAt >= maxHoldMs) {
12068
+ logger.warn(
12069
+ `[Qwen] Force-releasing stale stream slot | account=${accountId} | slotId=${slotId} | heldFor=${now - startedAt}ms | maxHold=${maxHoldMs}ms`
12070
+ );
12071
+ slots2.activeSlots.delete(slotId);
12072
+ sweptCount++;
12073
+ const next = slots2.queue.shift();
12074
+ if (next) {
12075
+ const nextId = nextStreamSlotId++;
12076
+ slots2.activeSlots.set(nextId, now);
12077
+ next(createStreamSlotRelease(slots2, nextId));
12078
+ }
12079
+ }
12080
+ }
12081
+ }
12082
+ return sweptCount;
12083
+ }
12084
+ function ensureStaleSlotSweeperStarted() {
12085
+ if (staleSlotSweeperTimer) return;
12086
+ staleSlotSweeperTimer = setInterval(() => {
12087
+ try {
12088
+ sweepStaleAccountStreamSlots();
12089
+ } catch (err) {
12090
+ logger.error("[Qwen] Error in stale stream slot sweeper", {
12091
+ error: err instanceof Error ? err.message : String(err)
12092
+ });
12093
+ }
12094
+ }, 6e4);
12095
+ staleSlotSweeperTimer.unref();
12096
+ }
12097
+ function stopStaleSlotSweeperForTests() {
12098
+ if (staleSlotSweeperTimer) {
12099
+ clearInterval(staleSlotSweeperTimer);
12100
+ staleSlotSweeperTimer = null;
12101
+ }
12102
+ }
11974
12103
  async function acquireAccountStreamLock(accountId) {
12104
+ ensureStaleSlotSweeperStarted();
11975
12105
  const slots2 = getAccountStreamMutex(accountId);
11976
- if (slots2.active < streamSlotCapacity()) {
11977
- slots2.active += 1;
11978
- return Promise.resolve(createStreamSlotRelease(slots2));
12106
+ if (slots2.activeSlots.size < streamSlotCapacity()) {
12107
+ const slotId = nextStreamSlotId++;
12108
+ slots2.activeSlots.set(slotId, Date.now());
12109
+ return Promise.resolve(createStreamSlotRelease(slots2, slotId));
11979
12110
  }
11980
12111
  return new Promise((resolve) => {
11981
- slots2.queue.push(() => resolve(createStreamSlotRelease(slots2)));
12112
+ slots2.queue.push((releaseFn) => resolve(releaseFn));
11982
12113
  });
11983
12114
  }
11984
12115
  function buildQwenSettingsUpdatePayload(currentSettings, instruction) {
@@ -12070,11 +12201,13 @@ function getBrowserFetchHeaders(headers) {
12070
12201
  "x-request-id",
12071
12202
  "x-accel-buffering"
12072
12203
  ]);
12073
- return Object.fromEntries(
12074
- Object.entries(headers).filter(
12075
- ([name]) => browserAllowedHeaders.has(name.toLowerCase())
12076
- )
12077
- );
12204
+ const result = {};
12205
+ for (const [name, value] of Object.entries(headers)) {
12206
+ if (browserAllowedHeaders.has(name.toLowerCase())) {
12207
+ result[name] = sanitizeHeaderForFetch(String(value ?? ""));
12208
+ }
12209
+ }
12210
+ return result;
12078
12211
  }
12079
12212
  async function requestQwenTextInBrowser(accountId, method, path21, headers, body, options = {}) {
12080
12213
  const url = qwenUrl(path21);
@@ -12095,7 +12228,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
12095
12228
  browserHeaders["Content-Type"] = "application/json";
12096
12229
  }
12097
12230
  const evaluateRequest = (page) => page.evaluate(
12098
- async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs }) => {
12231
+ async ({ url: url2, method: method2, headers: headers2, body: body2, referrer, timeoutMs, tz }) => {
12099
12232
  const controller = new AbortController();
12100
12233
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
12101
12234
  try {
@@ -12119,7 +12252,7 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
12119
12252
  source: "web",
12120
12253
  version: "0.3.11",
12121
12254
  "x-request-origin": "https://chat.qwen.ai",
12122
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
12255
+ timezone: tz,
12123
12256
  authorization: `Bearer ${rTok}`
12124
12257
  },
12125
12258
  signal: AbortSignal.timeout(5e3)
@@ -12164,7 +12297,8 @@ async function requestQwenTextInBrowser(accountId, method, path21, headers, body
12164
12297
  headers: browserHeaders,
12165
12298
  body,
12166
12299
  referrer: options.referrer,
12167
- timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4)
12300
+ timeoutMs: options.timeoutMs ?? Math.min(config.timeouts.page, 2e4),
12301
+ tz: formatAsciiTimezone()
12168
12302
  }
12169
12303
  );
12170
12304
  const recoverOnTimeout = !options.noMutexRecovery;
@@ -12842,7 +12976,7 @@ async function disableNativeTools(accountId) {
12842
12976
  async (page) => {
12843
12977
  const requestId = crypto7.randomUUID();
12844
12978
  const response = await page.evaluate(
12845
- async ({ payload: payload2, timeoutMs, requestId: requestId2 }) => {
12979
+ async ({ payload: payload2, timeoutMs, requestId: requestId2, tz }) => {
12846
12980
  const controller = new AbortController();
12847
12981
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
12848
12982
  try {
@@ -12854,7 +12988,7 @@ async function disableNativeTools(accountId) {
12854
12988
  accept: "application/json, text/plain, */*",
12855
12989
  "content-type": "application/json",
12856
12990
  "x-request-id": requestId2,
12857
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
12991
+ timezone: tz,
12858
12992
  source: "web"
12859
12993
  },
12860
12994
  body: JSON.stringify(payload2),
@@ -12866,7 +13000,7 @@ async function disableNativeTools(accountId) {
12866
13000
  clearTimeout(timeoutId);
12867
13001
  }
12868
13002
  },
12869
- { payload, timeoutMs: config.timeouts.http, requestId }
13003
+ { payload, timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
12870
13004
  );
12871
13005
  return response;
12872
13006
  }
@@ -13083,7 +13217,7 @@ async function deleteSingleQwenChat(accountId, chatId) {
13083
13217
  const requestHeaders = {
13084
13218
  source: "web",
13085
13219
  version: "0.2.89",
13086
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
13220
+ timezone: formatAsciiTimezone(),
13087
13221
  "x-request-id": crypto7.randomUUID(),
13088
13222
  Referer: qwenUrl(`/c/${encodeURIComponent(chatId)}`)
13089
13223
  };
@@ -13127,7 +13261,7 @@ async function fetchRemoteQwenChats(accountId) {
13127
13261
  }
13128
13262
  const requestHeaders = {
13129
13263
  version: "0.2.89",
13130
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
13264
+ timezone: formatAsciiTimezone(),
13131
13265
  "x-request-id": crypto7.randomUUID(),
13132
13266
  Referer: qwenUrl("/settings/chats")
13133
13267
  };
@@ -13179,7 +13313,7 @@ async function fetchQwenModels(accountId) {
13179
13313
  async (page) => {
13180
13314
  const requestId = crypto7.randomUUID();
13181
13315
  const response2 = await page.evaluate(
13182
- async ({ timeoutMs, requestId: requestId2 }) => {
13316
+ async ({ timeoutMs, requestId: requestId2, tz }) => {
13183
13317
  const controller = new AbortController();
13184
13318
  const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
13185
13319
  try {
@@ -13188,7 +13322,7 @@ async function fetchQwenModels(accountId) {
13188
13322
  headers: {
13189
13323
  accept: "application/json, text/plain, */*",
13190
13324
  "x-request-id": requestId2,
13191
- timezone: (/* @__PURE__ */ new Date()).toString().split(" (")[0],
13325
+ timezone: tz,
13192
13326
  source: "web"
13193
13327
  },
13194
13328
  signal: controller.signal
@@ -13198,7 +13332,7 @@ async function fetchQwenModels(accountId) {
13198
13332
  clearTimeout(timeoutId);
13199
13333
  }
13200
13334
  },
13201
- { timeoutMs: config.timeouts.http, requestId }
13335
+ { timeoutMs: config.timeouts.http, requestId, tz: formatAsciiTimezone() }
13202
13336
  );
13203
13337
  return response2;
13204
13338
  }
@@ -13237,7 +13371,7 @@ async function fetchQwenModels(accountId) {
13237
13371
  bxUa,
13238
13372
  bxUmidtoken,
13239
13373
  extra: {
13240
- timezone: (/* @__PURE__ */ new Date()).toString()
13374
+ timezone: formatAsciiTimezone()
13241
13375
  }
13242
13376
  }),
13243
13377
  void 0,
@@ -14000,7 +14134,7 @@ async function createQwenStreamInternal(prompt, enableThinking, modelId, forcedP
14000
14134
  clearTimeout(timeoutId);
14001
14135
  }
14002
14136
  }
14003
- var sleep5, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, accountStreamMutexes, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
14137
+ var sleep5, BROWSER_STREAM_BINDING, BROWSER_ABORTERS_KEY, BROWSER_STREAM_FLUSH_BYTES, BROWSER_STREAM_FLUSH_MS, METADATA_TIMEOUT_PER_PAYLOAD_MB_MS, POST_CAPTCHA_METADATA_GRACE_MS, browserStreamStates, browserStreamBindingPages, browserStreamBindingContexts, MODEL_CACHE_TTL_MS, modelsCache, nativeToolsDisabled, disablingNativeToolsInProgress, lastSyncedPersonalizationHashes, directSettingsFetchConsecutiveFailures, DIRECT_SETTINGS_FETCH_BLOCK_THRESHOLD, directSettingsFetchBlocked, DIRECT_SETTINGS_FETCH_TIMEOUT_MS, activePersonalizationByAccount, STREAM_SLOT_MAX_HOLD_MS, accountStreamMutexes, nextStreamSlotId, staleSlotSweeperTimer, QWEN_SAFE_SETTINGS_PATCH, QWEN_SAFE_SETTINGS_HASH, inFlightPersonalizationSyncs, DISABLE_TOOLS_MAX_RETRIES, DISABLE_TOOLS_BACKOFF_MS, UPSTREAM_RESPONSE_PREVIEW_BYTES;
14004
14138
  var init_qwen = __esm({
14005
14139
  "src/services/qwen.ts"() {
14006
14140
  "use strict";
@@ -14018,6 +14152,7 @@ var init_qwen = __esm({
14018
14152
  init_captcha_coordinator();
14019
14153
  init_captcha_solver();
14020
14154
  init_account_concurrency();
14155
+ init_timezone();
14021
14156
  init_qwen_errors();
14022
14157
  init_qwen_thread_state();
14023
14158
  init_qwen_chat_pool();
@@ -14047,7 +14182,10 @@ var init_qwen = __esm({
14047
14182
  directSettingsFetchBlocked = /* @__PURE__ */ new Set();
14048
14183
  DIRECT_SETTINGS_FETCH_TIMEOUT_MS = 1e4;
14049
14184
  activePersonalizationByAccount = /* @__PURE__ */ new Map();
14185
+ STREAM_SLOT_MAX_HOLD_MS = 30 * 60 * 1e3;
14050
14186
  accountStreamMutexes = /* @__PURE__ */ new Map();
14187
+ nextStreamSlotId = 1;
14188
+ staleSlotSweeperTimer = null;
14051
14189
  QWEN_SAFE_SETTINGS_PATCH = {
14052
14190
  ui: {
14053
14191
  autoTags: false,
@@ -16777,7 +16915,7 @@ function buildContextMeterSnapshot(input, options = getContextMeterOptions()) {
16777
16915
  function enrichUsageWithContextMeter(usage, snapshot, options = getContextMeterOptions()) {
16778
16916
  if (!snapshot || !options.enabled) return usage;
16779
16917
  const upstreamPromptTokens = Number.isFinite(usage.prompt_tokens) && usage.prompt_tokens > 0 ? usage.prompt_tokens : null;
16780
- const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ?? snapshot.estimatedContextTokens : usage.prompt_tokens;
16918
+ const reportedPromptTokens = options.reportUsage ? upstreamPromptTokens ?? usage.prompt_tokens : usage.prompt_tokens;
16781
16919
  const contextMeter = {
16782
16920
  ...snapshot,
16783
16921
  upstreamPromptTokens,
@@ -20405,6 +20543,35 @@ var init_parser = __esm({
20405
20543
  }
20406
20544
  });
20407
20545
 
20546
+ // src/tools/tool-call-heuristics.ts
20547
+ function isToolHesitationText(text) {
20548
+ if (!text) return false;
20549
+ return /\btools?\b[^.\n]{0,24}?\b(?:does\s*n[o']?t|do\s*n[o']?t|does\s*not|do\s*not)\b[^.\n]{0,12}?\bexist/i.test(
20550
+ text
20551
+ );
20552
+ }
20553
+ function decideMissingToolCallRetry(input) {
20554
+ const {
20555
+ declaredToolCount,
20556
+ successfulToolCallCount,
20557
+ malformedToolCallCount,
20558
+ emittedToolCallCount,
20559
+ text
20560
+ } = input;
20561
+ if (declaredToolCount <= 0) return { retry: false, reason: null };
20562
+ if (successfulToolCallCount > 0) return { retry: false, reason: null };
20563
+ if (malformedToolCallCount > 0) return { retry: true, reason: "malformed" };
20564
+ if (emittedToolCallCount === 0 && isToolHesitationText(text)) {
20565
+ return { retry: true, reason: "no_call" };
20566
+ }
20567
+ return { retry: false, reason: null };
20568
+ }
20569
+ var init_tool_call_heuristics = __esm({
20570
+ "src/tools/tool-call-heuristics.ts"() {
20571
+ "use strict";
20572
+ }
20573
+ });
20574
+
20408
20575
  // src/routes/chat/errors.ts
20409
20576
  function isWafChallenge(value) {
20410
20577
  const normalized = value.toLowerCase();
@@ -21104,10 +21271,17 @@ async function processNonStreamingResponse(params) {
21104
21271
  message.tool_calls = toolCallsOut;
21105
21272
  }
21106
21273
  const finishReason = toolCallsOut.length ? "tool_calls" : "stop";
21107
- const allToolsFailed = toolParser && toolParser.getMalformedToolCalls().length > 0 && toolCallsOut.length === 0;
21274
+ const retryDecision = toolParser ? decideMissingToolCallRetry({
21275
+ declaredToolCount: declaredTools.length,
21276
+ successfulToolCallCount: toolParser.getSuccessfulToolCallCount(),
21277
+ malformedToolCallCount: toolParser.getMalformedToolCalls().length,
21278
+ emittedToolCallCount: toolParser.getEmittedToolCallCount(),
21279
+ text: finalContent
21280
+ }) : { retry: false, reason: null };
21281
+ const allToolsFailed = retryDecision.retry;
21108
21282
  const malformedRetryCount = midStreamRetry?.malformedRetryCount ?? 0;
21109
21283
  if (allToolsFailed && config.retry.autoRetryMalformedTools !== false && midStreamRetry && malformedRetryCount < config.retry.autoRetryMalformedToolsMax) {
21110
- const malformedCalls = toolParser.getMalformedToolCalls();
21284
+ const malformedCalls = toolParser?.getMalformedToolCalls() ?? [];
21111
21285
  const malformedCount = malformedCalls.length;
21112
21286
  const undeclaredNames = malformedCalls.flatMap((mc) => mc.undeclaredNames || []).filter((name, index, self) => self.indexOf(name) === index);
21113
21287
  const availableToolNames = declaredTools.map((t) => t.type === "function" ? t.function?.name : t.name).filter((n) => !!n);
@@ -21115,7 +21289,9 @@ async function processNonStreamingResponse(params) {
21115
21289
 
21116
21290
  Available tools: ${availableToolNames.join(", ")}` : "";
21117
21291
  let errorMessage;
21118
- if (undeclaredNames.length > 0) {
21292
+ if (retryDecision.reason === "no_call") {
21293
+ errorMessage = `You did not emit any tool call. Respond with a single tool-call block using one of the declared tools.${toolsHint}`;
21294
+ } else if (undeclaredNames.length > 0) {
21119
21295
  errorMessage = `Your previous ${malformedCount} tool call(s) used undeclared tool names: ${undeclaredNames.join(", ")}. Only declared tools can be executed. Please retry with valid tool names.${toolsHint}`;
21120
21296
  } else {
21121
21297
  const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
@@ -21191,7 +21367,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
21191
21367
  new Error("client aborted during malformed-tool retry")
21192
21368
  );
21193
21369
  }
21194
- console.log(`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${config.retry.autoRetryMalformedToolsMax}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason=malformed_tool_calls`);
21370
+ console.log(`\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${config.retry.autoRetryMalformedToolsMax}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason=${retryDecision.reason === "no_call" ? "missing_tool_call" : "malformed_tool_calls"}`);
21195
21371
  return processNonStreamingResponse({
21196
21372
  ...params,
21197
21373
  stream: newStreamResult.stream,
@@ -21347,7 +21523,7 @@ async function processStreamingResponse(params) {
21347
21523
  let chatInProgressSameAccountRetries = 0;
21348
21524
  let antiBotSameAccountRetries = 0;
21349
21525
  const totalConfiguredAccounts = loadAccounts().length;
21350
- let quotaRotationsLeft = Math.min(Math.max(1, totalConfiguredAccounts - 1), 6);
21526
+ let quotaRotationsLeft = Math.max(1, totalConfiguredAccounts - 1);
21351
21527
  let streamCompletedOk = false;
21352
21528
  let stoppedByToolCap = false;
21353
21529
  const runDisconnectTeardown = () => {
@@ -22287,7 +22463,14 @@ async function processStreamingResponse(params) {
22287
22463
  let activeRetryStream = null;
22288
22464
  while (malformedRetryCount < maxMalformedRetries) {
22289
22465
  if (!toolParser) break;
22290
- const allToolsFailed = toolParser.getMalformedToolCalls().length > 0 && toolParser.getSuccessfulToolCallCount() === 0;
22466
+ const retryDecision = decideMissingToolCallRetry({
22467
+ declaredToolCount: declaredTools.length,
22468
+ successfulToolCallCount: toolParser.getSuccessfulToolCallCount(),
22469
+ malformedToolCallCount: toolParser.getMalformedToolCalls().length,
22470
+ emittedToolCallCount: toolParser.getEmittedToolCallCount(),
22471
+ text: finalContent
22472
+ });
22473
+ const allToolsFailed = retryDecision.retry;
22291
22474
  if (!allToolsFailed) break;
22292
22475
  const malformedCalls = toolParser.getMalformedToolCalls();
22293
22476
  const malformedCount = malformedCalls.length;
@@ -22297,7 +22480,9 @@ async function processStreamingResponse(params) {
22297
22480
 
22298
22481
  Available tools: ${availableToolNames.join(", ")}` : "";
22299
22482
  let errorMessage;
22300
- if (undeclaredNames.length > 0) {
22483
+ if (retryDecision.reason === "no_call") {
22484
+ errorMessage = `You did not emit any tool call. Respond with a single tool-call block using one of the declared tools.${toolsHint}`;
22485
+ } else if (undeclaredNames.length > 0) {
22301
22486
  errorMessage = `Your previous ${malformedCount} tool call(s) used undeclared tool names: ${undeclaredNames.join(", ")}. Only declared tools can be executed. Please retry with valid tool names.${toolsHint}`;
22302
22487
  } else {
22303
22488
  const previews = malformedCalls.slice(0, 3).map((mc) => mc.contentPreview?.substring(0, 100) || "(empty)").join("\n - ");
@@ -22374,7 +22559,7 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
22374
22559
  break;
22375
22560
  }
22376
22561
  console.log(
22377
- `\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${maxMalformedRetries}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason=malformed_tool_calls`
22562
+ `\u{1F504} [Chat] Auto-retry (${malformedRetryCount + 1}/${maxMalformedRetries}) | ${newStreamResult.activeAccountLabel} | ${body.model} | chat=${newStreamResult.uiSessionId.substring(0, 12)} | reason=${retryDecision.reason === "no_call" ? "missing_tool_call" : "malformed_tool_calls"}`
22378
22563
  );
22379
22564
  const retryEntry = getStream(newStreamResult.completionId);
22380
22565
  removeStream(newStreamResult.completionId);
@@ -22775,28 +22960,32 @@ Please retry your tool call(s) with correct JSON and valid tool names from the a
22775
22960
  logger.error("[Chat] Stream callback error", errorDetails);
22776
22961
  }
22777
22962
  try {
22778
- const userFriendlyNotice = `
22963
+ const isNetwork = isNetworkLikeError(err);
22964
+ const isModeration = normalizedErrorCode === "data_inspection_failed" || normalizedErrorCode === "fail_sys_user_validate" || err.message?.toLowerCase().includes("content moderation") || err.message?.toLowerCase().includes("security verification");
22965
+ if (!isNetwork && isModeration) {
22966
+ const userFriendlyNotice = `
22779
22967
 
22780
- \u26A0\uFE0F **[Qwen Security / Erro]** ${err.message || "A resposta foi interrompida pelo Qwen."}
22968
+ \u26A0\uFE0F **[Qwen Modera\xE7\xE3o / Seguran\xE7a]** ${err.message || "A resposta foi interrompida pelas pol\xEDticas de seguran\xE7a do Qwen."}
22781
22969
 
22782
22970
  `;
22783
- const errorDelta = {
22784
- id: completionId,
22785
- object: "chat.completion.chunk",
22786
- created: Math.floor(Date.now() / 1e3),
22787
- model: body.model,
22788
- choices: [
22789
- {
22790
- index: 0,
22791
- delta: { content: userFriendlyNotice },
22792
- logprobs: null,
22793
- finish_reason: "stop"
22794
- }
22795
- ]
22796
- };
22797
- await errorStream.write(`data: ${JSON.stringify(errorDelta)}
22971
+ const errorDelta = {
22972
+ id: completionId,
22973
+ object: "chat.completion.chunk",
22974
+ created: Math.floor(Date.now() / 1e3),
22975
+ model: body.model,
22976
+ choices: [
22977
+ {
22978
+ index: 0,
22979
+ delta: { content: userFriendlyNotice },
22980
+ logprobs: null,
22981
+ finish_reason: "content_filter"
22982
+ }
22983
+ ]
22984
+ };
22985
+ await errorStream.write(`data: ${JSON.stringify(errorDelta)}
22798
22986
 
22799
22987
  `);
22988
+ }
22800
22989
  await errorStream.write(
22801
22990
  `data: ${JSON.stringify({
22802
22991
  error: {
@@ -22875,6 +23064,7 @@ var init_streaming = __esm({
22875
23064
  init_retry_policy();
22876
23065
  init_account();
22877
23066
  init_parser();
23067
+ init_tool_call_heuristics();
22878
23068
  init_stream_registry();
22879
23069
  init_metrics();
22880
23070
  init_logger();
@@ -24283,7 +24473,7 @@ function chatCompletionsToResponses(chatRes, requestModel, originalRequest) {
24283
24473
  cached_tokens: chatRes.usage.prompt_tokens_details?.cached_tokens ?? 0
24284
24474
  },
24285
24475
  output_tokens_details: {
24286
- reasoning_tokens: chatRes.usage.completion_tokens_details?.reasoning_tokens ?? 0
24476
+ reasoning_tokens: chatRes.usage.completion_tokens_details?.reasoning_tokens ?? chatRes.usage.output_tokens_details?.reasoning_tokens ?? 0
24287
24477
  },
24288
24478
  ...chatRes.usage.context_meter ? { context_meter: chatRes.usage.context_meter } : {}
24289
24479
  };
@@ -25744,7 +25934,7 @@ function translateOpenAIToAnthropic(openaiResponse, requestModel) {
25744
25934
  content_filter: "end_turn"
25745
25935
  };
25746
25936
  const hasToolUse = content.some((b) => b.type === "tool_use");
25747
- const stopReason = hasToolUse ? "tool_use" : stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
25937
+ const stopReason = choice.finish_reason === "length" ? "max_tokens" : choice.finish_reason === "content_filter" ? "end_turn" : hasToolUse ? "tool_use" : stopReasonMap[choice.finish_reason || "stop"] || "end_turn";
25748
25938
  return {
25749
25939
  id: generateMessageId2(),
25750
25940
  type: "message",
@@ -25882,7 +26072,7 @@ function translateStreamChunk(chunk, state) {
25882
26072
  state.contentBlockIndex++;
25883
26073
  state.currentBlockType = null;
25884
26074
  }
25885
- const stopReason = state.hasEmittedToolUse ? "tool_use" : choice.finish_reason === "length" ? "max_tokens" : "end_turn";
26075
+ const stopReason = choice.finish_reason === "length" ? "max_tokens" : choice.finish_reason === "content_filter" ? "end_turn" : state.hasEmittedToolUse ? "tool_use" : "end_turn";
25886
26076
  events.push(
25887
26077
  JSON.stringify({
25888
26078
  type: "message_delta",
@@ -26828,7 +27018,7 @@ function getAppVersion() {
26828
27018
  }
26829
27019
  } catch {
26830
27020
  }
26831
- cachedVersion = "v1.6.4";
27021
+ cachedVersion = "v1.6.6";
26832
27022
  return cachedVersion;
26833
27023
  }
26834
27024
  var cachedVersion;