blun-king-cli 9.1.595 → 9.1.597

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/blun.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:bb2a99b699d299ac283bd9b157d0c18555ae0ab62c58c7b3c10d8311dfa87a0d
2
+ // BLUN_BUILD_INPUT_SHA256:631215107f223f910482352d64f8081de5c7ebb282f9e66a53e7cd6dc93ba305
3
3
  import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
4
4
  import { dirname as __cjsShimDirname } from 'node:path';
5
5
  const __filename = __cjsShimFileURLToPath(import.meta.url);
@@ -1536,6 +1536,7 @@ function parseExplicitMaxContextTokens(message) {
1536
1536
  return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : void 0;
1537
1537
  }
1538
1538
  function isRetryableGenerateError(error) {
1539
+ if (error instanceof APIContextOverflowError) return false;
1539
1540
  if (error instanceof CompactionStallError$1) return true;
1540
1541
  if (error instanceof APIConnectionError || error instanceof APITimeoutError) return true;
1541
1542
  if (error instanceof APIEmptyResponseError) return true;
@@ -1572,7 +1573,8 @@ function isProviderQuotaExhaustedMessage(message) {
1572
1573
  return PROVIDER_QUOTA_EXHAUSTED_MESSAGE_PATTERN.test(lowerMessage) && PROVIDER_QUOTA_EXHAUSTED_CONTEXT_PATTERNS.some((pattern) => pattern.test(lowerMessage));
1573
1574
  }
1574
1575
  function isContextOverflowStatusError(statusCode, message) {
1575
- if (statusCode !== 400 && statusCode !== 413 && statusCode !== 422) return false;
1576
+ const wrappedRejection = statusCode === 502 && /\bHTTP(?: Error)? (?:400|413|422):/i.test(message);
1577
+ if (statusCode !== 400 && statusCode !== 413 && statusCode !== 422 && !wrappedRejection) return false;
1576
1578
  const lowerMessage = message.toLowerCase();
1577
1579
  return CONTEXT_OVERFLOW_MESSAGE_PATTERNS.some((pattern) => pattern.test(lowerMessage));
1578
1580
  }
@@ -1998,7 +2000,8 @@ var init_fetch_http_client = __esmMin((() => {
1998
2000
  const startedAt = performance.now();
1999
2001
  try {
2000
2002
  const response = await this.fetch(`${this.baseUrl}${pathname}`, init);
2001
- const willRetry = attempt < maxRetries && shouldRetryResponse(response);
2003
+ const gatewayError = response.status === 502 ? await responseError(response) : void 0;
2004
+ const willRetry = attempt < maxRetries && shouldRetryResponse(response) && !(gatewayError instanceof APIContextOverflowError);
2002
2005
  notifyTransportAttempt(requestOptions?.onTransportAttempt, {
2003
2006
  transportAttempt: attempt + 1,
2004
2007
  ...requestBytes === void 0 ? {} : { requestBytes },
@@ -2010,12 +2013,12 @@ var init_fetch_http_client = __esmMin((() => {
2010
2013
  if (response.ok) return response;
2011
2014
  if (willRetry) {
2012
2015
  const delayMs = retryDelayMs(response.headers, attempt);
2013
- await response.body?.cancel();
2016
+ if (gatewayError === void 0) await response.body?.cancel();
2014
2017
  await waitForRetry(delayMs, init.signal);
2015
2018
  attempt += 1;
2016
2019
  continue;
2017
2020
  }
2018
- throw await responseError(response);
2021
+ throw gatewayError ?? await responseError(response);
2019
2022
  } catch (error) {
2020
2023
  if (error instanceof ChatProviderError) throw error;
2021
2024
  const willRetry = attempt < maxRetries && init.signal?.aborted !== true && isRetryableTransportFailure(error);
@@ -2578,9 +2581,23 @@ function extractUsageFromChunk(chunk) {
2578
2581
  if (choiceUsage !== null && choiceUsage !== void 0 && typeof choiceUsage === "object") return choiceUsage;
2579
2582
  return null;
2580
2583
  }
2584
+ function contextOverflowOutputCap(error, currentCap) {
2585
+ const maxContext = error.maxContextTokens;
2586
+ const counts = /requested ([\d,_]+) output tokens and your prompt contains (?:at least )?([\d,_]+) input tokens/i.exec(error.message);
2587
+ if (maxContext === void 0 || counts === null || !Number.isSafeInteger(maxContext) || maxContext <= 0) return;
2588
+ const requestedOutput = Number(counts[1].replaceAll(/[,_]/g, ""));
2589
+ const input = Number(counts[2].replaceAll(/[,_]/g, ""));
2590
+ if (!Number.isSafeInteger(input) || input < 0 || !Number.isSafeInteger(requestedOutput) || requestedOutput <= 0 || !Number.isSafeInteger(input + requestedOutput) || input + requestedOutput <= maxContext) return;
2591
+ const available = maxContext - input;
2592
+ const headroom = Math.min(1024, Math.max(1, Math.ceil(available * .01)));
2593
+ const cap = Math.min(requestedOutput - 1, available - headroom);
2594
+ if (cap < 1 || currentCap !== void 0 && (typeof currentCap !== "number" || !Number.isSafeInteger(currentCap) || cap >= currentCap)) return;
2595
+ return cap;
2596
+ }
2581
2597
  var BLUN_TOOL_CALL_ID_POLICY, BLUN_INBOUND_REASONING_KEYS, BLUN_VISION_DATA_URL, BLUN_VISION_MAX_ATTACHMENTS, BLUN_VISION_MAX_IMAGE_BYTES, BlunStreamedMessage, BlunChatProvider;
2582
2598
  var init_blun = __esmMin((() => {
2583
2599
  init_blun_schema();
2600
+ init_errors$10();
2584
2601
  init_blun_files();
2585
2602
  init_chat_completions_wire();
2586
2603
  init_chat_completions_stream();
@@ -2844,8 +2861,23 @@ var init_blun = __esmMin((() => {
2844
2861
  ...options.onTransportAttempt !== void 0 ? { onTransportAttempt: options.onTransportAttempt } : {}
2845
2862
  } : void 0;
2846
2863
  options?.onRequestSent?.();
2847
- return new BlunStreamedMessage(await client.chat.completions.create(createParams, requestOptions), this._stream, reasoningRequested);
2864
+ let response;
2865
+ try {
2866
+ response = await client.chat.completions.create(createParams, requestOptions);
2867
+ } catch (error) {
2868
+ const rejection = normalizeFetchHttpError(error);
2869
+ const cap = rejection instanceof APIContextOverflowError ? contextOverflowOutputCap(rejection, createParams["max_completion_tokens"]) : void 0;
2870
+ if (cap === void 0) throw rejection;
2871
+ options?.signal?.throwIfAborted();
2872
+ options?.onRequestSent?.();
2873
+ response = await client.chat.completions.create({
2874
+ ...createParams,
2875
+ max_completion_tokens: cap
2876
+ }, requestOptions);
2877
+ }
2878
+ return new BlunStreamedMessage(response, this._stream, reasoningRequested);
2848
2879
  } catch (error) {
2880
+ if (options?.signal?.aborted === true) throw options.signal.reason ?? error;
2849
2881
  throw normalizeFetchHttpError(error);
2850
2882
  }
2851
2883
  }
@@ -530546,6 +530578,7 @@ var SessionEventHandler = class {
530546
530578
  currentTurnHasAssistantText = false;
530547
530579
  currentTurnStartedAtMs;
530548
530580
  currentTurnTokenCount;
530581
+ pendingChannelInterruption;
530549
530582
  pendingModelBlockedFallback;
530550
530583
  queuedGoalPromotionPending = false;
530551
530584
  queuedGoalPromotionInFlight = false;
@@ -530566,6 +530599,7 @@ var SessionEventHandler = class {
530566
530599
  this.currentTurnHasAssistantText = false;
530567
530600
  this.currentTurnStartedAtMs = void 0;
530568
530601
  this.currentTurnTokenCount = void 0;
530602
+ this.pendingChannelInterruption = void 0;
530569
530603
  this.pendingModelBlockedFallback = void 0;
530570
530604
  this.queuedGoalPromotionPending = false;
530571
530605
  this.queuedGoalPromotionInFlight = false;
@@ -530578,6 +530612,27 @@ var SessionEventHandler = class {
530578
530612
  clearAgentSwarmProgress() {
530579
530613
  this.subAgentEventHandler.clearAgentSwarmProgress();
530580
530614
  }
530615
+ async interruptForQueuedChannel() {
530616
+ const session = this.host.session;
530617
+ if (session === void 0) return;
530618
+ const pending = {
530619
+ sessionId: session.id,
530620
+ turnId: this.host.streamingUI.getTurnContext().turnId
530621
+ };
530622
+ this.pendingChannelInterruption = pending;
530623
+ try {
530624
+ await session.cancel();
530625
+ } catch (error) {
530626
+ if (this.pendingChannelInterruption === pending) this.pendingChannelInterruption = void 0;
530627
+ throw error;
530628
+ }
530629
+ }
530630
+ consumeChannelInterruption(sessionId, turnId) {
530631
+ const pending = this.pendingChannelInterruption;
530632
+ if (pending === void 0 || pending.sessionId !== sessionId || pending.turnId !== void 0 && pending.turnId !== String(turnId)) return false;
530633
+ this.pendingChannelInterruption = void 0;
530634
+ return true;
530635
+ }
530581
530636
  hasActiveAgentSwarmToolCall() {
530582
530637
  return this.subAgentEventHandler.hasActiveAgentSwarmToolCall();
530583
530638
  }
@@ -530741,6 +530796,7 @@ var SessionEventHandler = class {
530741
530796
  this.mcpServerStatusSpinners.clear();
530742
530797
  }
530743
530798
  handleTurnBegin(event) {
530799
+ this.pendingChannelInterruption = void 0;
530744
530800
  this.host.goalChannelReplies?.beginTurn(event.sessionId, this.host.state.appState.goal?.goalId, event.turnId);
530745
530801
  this.hideCompactionRetry();
530746
530802
  const sessionId = this.host.session?.id;
@@ -530781,6 +530837,7 @@ var SessionEventHandler = class {
530781
530837
  });
530782
530838
  }
530783
530839
  handleTurnEnd(event, sendQueued) {
530840
+ this.consumeChannelInterruption(event.sessionId, event.turnId);
530784
530841
  this.host.restoreQueuedSteerAtTurnEnd(event.turnId);
530785
530842
  const sessionId = this.host.session?.id;
530786
530843
  if (sessionId !== void 0) finishPersonalMemoryRememberIntentTurn(sessionId, event.turnId);
@@ -530867,10 +530924,11 @@ var SessionEventHandler = class {
530867
530924
  this.host.streamingUI.resetToolUi();
530868
530925
  this.host.streamingUI.finalizeLiveTextBuffers("idle");
530869
530926
  const reason = event.reason;
530927
+ const channelInterruption = this.consumeChannelInterruption(event.sessionId, event.turnId);
530870
530928
  if (reason === "error") return;
530871
530929
  if (reason === "aborted" || reason === void 0 || reason === "") {
530872
530930
  this.markActiveAgentSwarmsCancelled();
530873
- this.host.showStatus(uiText("sessionEvent.interrupted.user"), "error");
530931
+ this.host.showStatus(channelInterruption ? uiText("sessionEvent.interrupted.reason", { reason: "Telegram" }) : uiText("sessionEvent.interrupted.user"), channelInterruption ? "warning" : "error");
530874
530932
  return;
530875
530933
  }
530876
530934
  this.host.showError(reason === "max_steps" ? uiText("sessionEvent.interrupted.maxSteps") : uiText("sessionEvent.interrupted.reason", { reason }));
@@ -537787,7 +537845,7 @@ var BlunTUI = class {
537787
537845
  deliverOne: () => this.deliverQueuedChannelHead(),
537788
537846
  canInterruptWaitingWork: () => this.canInterruptForQueuedChannel(),
537789
537847
  interruptWaitingWork: async () => {
537790
- await this.session?.cancel();
537848
+ await this.sessionEventHandler.interruptForQueuedChannel();
537791
537849
  }
537792
537850
  });
537793
537851
  this.managedQuotaWarningController = new ManagedQuotaWarningController({ onChange: (warning) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.595",
3
+ "version": "9.1.597",
4
4
  "description": "BLUN CLI - your own AI agent with a Telegram channel. Get it done. With BLUN.",
5
5
  "license": "MIT",
6
6
  "bin": {
package/worker-host.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // BLUN_BUILD_INPUT_SHA256:bb2a99b699d299ac283bd9b157d0c18555ae0ab62c58c7b3c10d8311dfa87a0d
2
+ // BLUN_BUILD_INPUT_SHA256:631215107f223f910482352d64f8081de5c7ebb282f9e66a53e7cd6dc93ba305
3
3
  import { fileURLToPath as __cjsShimFileURLToPath } from 'node:url';
4
4
  import { dirname as __cjsShimDirname } from 'node:path';
5
5
  const __filename = __cjsShimFileURLToPath(import.meta.url);
@@ -1664,6 +1664,7 @@ var CompactionStallError$1 = class extends ChatProviderError {
1664
1664
  }
1665
1665
  };
1666
1666
  function isRetryableGenerateError(error) {
1667
+ if (error instanceof APIContextOverflowError) return false;
1667
1668
  if (error instanceof CompactionStallError$1) return true;
1668
1669
  if (error instanceof APIConnectionError || error instanceof APITimeoutError) return true;
1669
1670
  if (error instanceof APIEmptyResponseError) return true;
@@ -1728,7 +1729,8 @@ function isProviderQuotaExhaustedMessage(message) {
1728
1729
  return PROVIDER_QUOTA_EXHAUSTED_MESSAGE_PATTERN.test(lowerMessage) && PROVIDER_QUOTA_EXHAUSTED_CONTEXT_PATTERNS.some((pattern) => pattern.test(lowerMessage));
1729
1730
  }
1730
1731
  function isContextOverflowStatusError(statusCode, message) {
1731
- if (statusCode !== 400 && statusCode !== 413 && statusCode !== 422) return false;
1732
+ const wrappedRejection = statusCode === 502 && /\bHTTP(?: Error)? (?:400|413|422):/i.test(message);
1733
+ if (statusCode !== 400 && statusCode !== 413 && statusCode !== 422 && !wrappedRejection) return false;
1732
1734
  const lowerMessage = message.toLowerCase();
1733
1735
  return CONTEXT_OVERFLOW_MESSAGE_PATTERNS.some((pattern) => pattern.test(lowerMessage));
1734
1736
  }
@@ -1854,7 +1856,8 @@ var FetchTransport = class {
1854
1856
  const startedAt = performance.now();
1855
1857
  try {
1856
1858
  const response = await this.fetch(`${this.baseUrl}${pathname}`, init);
1857
- const willRetry = attempt < maxRetries && shouldRetryResponse(response);
1859
+ const gatewayError = response.status === 502 ? await responseError(response) : void 0;
1860
+ const willRetry = attempt < maxRetries && shouldRetryResponse(response) && !(gatewayError instanceof APIContextOverflowError);
1858
1861
  notifyTransportAttempt(requestOptions?.onTransportAttempt, {
1859
1862
  transportAttempt: attempt + 1,
1860
1863
  ...requestBytes === void 0 ? {} : { requestBytes },
@@ -1866,12 +1869,12 @@ var FetchTransport = class {
1866
1869
  if (response.ok) return response;
1867
1870
  if (willRetry) {
1868
1871
  const delayMs = retryDelayMs(response.headers, attempt);
1869
- await response.body?.cancel();
1872
+ if (gatewayError === void 0) await response.body?.cancel();
1870
1873
  await waitForRetry(delayMs, init.signal);
1871
1874
  attempt += 1;
1872
1875
  continue;
1873
1876
  }
1874
- throw await responseError(response);
1877
+ throw gatewayError ?? await responseError(response);
1875
1878
  } catch (error) {
1876
1879
  if (error instanceof ChatProviderError) throw error;
1877
1880
  const willRetry = attempt < maxRetries && init.signal?.aborted !== true && isRetryableTransportFailure(error);
@@ -2698,6 +2701,19 @@ var BlunStreamedMessage = class {
2698
2701
  }
2699
2702
  }
2700
2703
  };
2704
+ function contextOverflowOutputCap(error, currentCap) {
2705
+ const maxContext = error.maxContextTokens;
2706
+ const counts = /requested ([\d,_]+) output tokens and your prompt contains (?:at least )?([\d,_]+) input tokens/i.exec(error.message);
2707
+ if (maxContext === void 0 || counts === null || !Number.isSafeInteger(maxContext) || maxContext <= 0) return;
2708
+ const requestedOutput = Number(counts[1].replaceAll(/[,_]/g, ""));
2709
+ const input = Number(counts[2].replaceAll(/[,_]/g, ""));
2710
+ if (!Number.isSafeInteger(input) || input < 0 || !Number.isSafeInteger(requestedOutput) || requestedOutput <= 0 || !Number.isSafeInteger(input + requestedOutput) || input + requestedOutput <= maxContext) return;
2711
+ const available = maxContext - input;
2712
+ const headroom = Math.min(1024, Math.max(1, Math.ceil(available * .01)));
2713
+ const cap = Math.min(requestedOutput - 1, available - headroom);
2714
+ if (cap < 1 || currentCap !== void 0 && (typeof currentCap !== "number" || !Number.isSafeInteger(currentCap) || cap >= currentCap)) return;
2715
+ return cap;
2716
+ }
2701
2717
  var BlunChatProvider = class {
2702
2718
  name = "blun";
2703
2719
  _model;
@@ -2852,8 +2868,23 @@ var BlunChatProvider = class {
2852
2868
  ...options.onTransportAttempt !== void 0 ? { onTransportAttempt: options.onTransportAttempt } : {}
2853
2869
  } : void 0;
2854
2870
  options?.onRequestSent?.();
2855
- return new BlunStreamedMessage(await client.chat.completions.create(createParams, requestOptions), this._stream, reasoningRequested);
2871
+ let response;
2872
+ try {
2873
+ response = await client.chat.completions.create(createParams, requestOptions);
2874
+ } catch (error) {
2875
+ const rejection = normalizeFetchHttpError(error);
2876
+ const cap = rejection instanceof APIContextOverflowError ? contextOverflowOutputCap(rejection, createParams["max_completion_tokens"]) : void 0;
2877
+ if (cap === void 0) throw rejection;
2878
+ options?.signal?.throwIfAborted();
2879
+ options?.onRequestSent?.();
2880
+ response = await client.chat.completions.create({
2881
+ ...createParams,
2882
+ max_completion_tokens: cap
2883
+ }, requestOptions);
2884
+ }
2885
+ return new BlunStreamedMessage(response, this._stream, reasoningRequested);
2856
2886
  } catch (error) {
2887
+ if (options?.signal?.aborted === true) throw options.signal.reason ?? error;
2857
2888
  throw normalizeFetchHttpError(error);
2858
2889
  }
2859
2890
  }