blun-king-cli 9.1.285 → 9.1.287

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.
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ const MAX_MODEL_RETRY_HINT_CHARS = 32;
4
+ const MAX_MODEL_RETRY_ATTEMPTS = 99;
5
+ const MAX_MODEL_RETRY_DELAY_MS = 600_000;
6
+
7
+ function boundedInteger(value, minimum, maximum, fallback) {
8
+ if (!Number.isFinite(value)) return fallback;
9
+ return Math.min(maximum, Math.max(minimum, Math.trunc(value)));
10
+ }
11
+
12
+ function formatRetryDelay(delayMs) {
13
+ const bounded = boundedInteger(delayMs, 0, MAX_MODEL_RETRY_DELAY_MS, 0);
14
+ if (bounded === 0) return '';
15
+ if (bounded < 1_000) return `${bounded}ms`;
16
+ return `${Math.ceil(bounded / 100) / 10}s`;
17
+ }
18
+
19
+ function formatModelRetryProgress(event = {}) {
20
+ const maxAttempts = boundedInteger(
21
+ event.maxAttempts,
22
+ 1,
23
+ MAX_MODEL_RETRY_ATTEMPTS,
24
+ 1,
25
+ );
26
+ const failedAttempt = boundedInteger(
27
+ event.failedAttempt,
28
+ 0,
29
+ maxAttempts - 1,
30
+ 0,
31
+ );
32
+ const nextAttempt = boundedInteger(
33
+ event.nextAttempt,
34
+ 1,
35
+ maxAttempts,
36
+ Math.min(maxAttempts, failedAttempt + 1),
37
+ );
38
+ const delay = formatRetryDelay(event.delayMs);
39
+ const hint = `Retry ${nextAttempt}/${maxAttempts}${delay ? ` | ${delay}` : ''}`;
40
+ return hint.slice(0, MAX_MODEL_RETRY_HINT_CHARS);
41
+ }
42
+
43
+ module.exports = {
44
+ MAX_MODEL_RETRY_HINT_CHARS,
45
+ formatModelRetryProgress,
46
+ };
@@ -4,6 +4,7 @@ const TOOL_SCHEMA_BUDGET_RATIO = 0.25;
4
4
  const TOOL_SCHEMA_MAX_TOKENS = 32_000;
5
5
  const DEFERRED_TOOL_LOADER_NAME = 'ToolSearch';
6
6
  const MAX_PERSISTENT_DEFERRED_TOOLS = 4;
7
+ const MAX_TOOL_SEARCH_QUERY_CHARS = 1_000;
7
8
  const TOOL_SEARCH_INTENT_WORDS = new Set([
8
9
  'find', 'fetch', 'get', 'holen', 'list', 'read', 'search', 'show', 'anzeigen', 'lesen', 'suchen',
9
10
  ]);
@@ -83,8 +84,18 @@ function deferredToolCatalog(tools) {
83
84
  .sort((left, right) => left.localeCompare(right));
84
85
  }
85
86
 
87
+ function normalizeDeferredToolQuery(value) {
88
+ const query = String(value ?? '').trim();
89
+ if (query.length <= MAX_TOOL_SEARCH_QUERY_CHARS) return query;
90
+ const bounded = query.slice(0, MAX_TOOL_SEARCH_QUERY_CHARS);
91
+ const wordBoundary = bounded.lastIndexOf(' ');
92
+ return (wordBoundary >= MAX_TOOL_SEARCH_QUERY_CHARS * 0.8
93
+ ? bounded.slice(0, wordBoundary)
94
+ : bounded).trimEnd();
95
+ }
96
+
86
97
  function searchDeferredTools(tools, query) {
87
- const normalized = String(query || '').trim().toLowerCase();
98
+ const normalized = normalizeDeferredToolQuery(query).toLowerCase();
88
99
  if (!normalized) return [];
89
100
  if (normalized.startsWith('select:')) {
90
101
  const selector = normalized.slice('select:'.length).trim();
@@ -132,7 +143,7 @@ function relatedSearchTerms(token) {
132
143
  }
133
144
 
134
145
  function searchRelatedDeferredTools(tools, query, options = {}) {
135
- const normalized = String(query || '').trim().toLowerCase();
146
+ const normalized = normalizeDeferredToolQuery(query).toLowerCase();
136
147
  if (!normalized || normalized.startsWith('select:')) return [];
137
148
  const tokens = normalized.split(/\s+/).filter(Boolean);
138
149
  const requestedNameTokens = tokens
@@ -230,6 +241,7 @@ function createDeferredToolLoader(selectedTools, deferredTools, loadedToolNames
230
241
  properties: {
231
242
  query: {
232
243
  type: 'string',
244
+ maxLength: MAX_TOOL_SEARCH_QUERY_CHARS,
233
245
  description: 'Exact selection or keyword query, for example select:download_attachment or +slack send.',
234
246
  },
235
247
  },
@@ -237,7 +249,7 @@ function createDeferredToolLoader(selectedTools, deferredTools, loadedToolNames
237
249
  additionalProperties: false,
238
250
  },
239
251
  resolveExecution(args) {
240
- const query = String(args?.query || '').trim();
252
+ const query = normalizeDeferredToolQuery(args?.query);
241
253
  return {
242
254
  description: query ? `Searching tool schemas for ${query}` : 'Searching tool schemas',
243
255
  approvalRule: DEFERRED_TOOL_LOADER_NAME,
@@ -302,12 +314,14 @@ module.exports = {
302
314
  CORE_TOOL_NAMES,
303
315
  DEFERRED_TOOL_LOADER_NAME,
304
316
  MAX_PERSISTENT_DEFERRED_TOOLS,
317
+ MAX_TOOL_SEARCH_QUERY_CHARS,
305
318
  TOOL_SCHEMA_BUDGET_RATIO,
306
319
  TOOL_SCHEMA_MAX_TOKENS,
307
320
  createDeferredToolLoader,
308
321
  deferredToolCatalog,
309
322
  deferredToolNames,
310
323
  mediaGenerationToolNamesForText,
324
+ normalizeDeferredToolQuery,
311
325
  rememberDeferredToolAfterNotFound,
312
326
  toolSchemaBudgetTokens,
313
327
  };
package/blun.mjs CHANGED
@@ -507180,6 +507180,7 @@ function normalizeJson(value) {
507180
507180
  }
507181
507181
  //#endregion
507182
507182
  //#region src/tui/controllers/session-event-handler.ts
507183
+ var { formatModelRetryProgress } = createRequire(import.meta.url)("./bin/model-retry-progress-policy.cjs");
507183
507184
  const MANAGED_TELEGRAM_MCP_SERVER = "plugin-telegram:telegram";
507184
507185
  function isManagedTelegramMissingTokenFailure(server) {
507185
507186
  return server.name === MANAGED_TELEGRAM_MCP_SERVER && server.status === "failed" && server.error?.includes("BLUN_TELEGRAM_BOT_TOKEN required") === true;
@@ -507220,7 +507221,9 @@ var SessionEventHandler = class {
507220
507221
  queuedGoalPromotionInFlight = false;
507221
507222
  queuedGoalPromotionTimer;
507222
507223
  lastCompactionInstruction;
507224
+ modelRetryHint;
507223
507225
  resetRuntimeState() {
507226
+ this.clearModelRetryHint();
507224
507227
  this.turnWatchdog.stop();
507225
507228
  this.backgroundTasks.clear();
507226
507229
  this.backgroundTaskTranscriptedTerminal.clear();
@@ -507320,7 +507323,9 @@ var SessionEventHandler = class {
507320
507323
  this.handleStepCompleted(event);
507321
507324
  this.host.releaseChannelQueueAtSafePoint();
507322
507325
  break;
507323
- case "turn.step.retrying": break;
507326
+ case "turn.step.retrying":
507327
+ this.handleStepRetrying(event);
507328
+ break;
507324
507329
  case "tool.progress":
507325
507330
  this.handleToolProgress(event);
507326
507331
  break;
@@ -507409,6 +507414,7 @@ var SessionEventHandler = class {
507409
507414
  this.mcpServerStatusSpinners.clear();
507410
507415
  }
507411
507416
  handleTurnBegin(event) {
507417
+ this.clearModelRetryHint();
507412
507418
  this.turnWatchdog.start();
507413
507419
  const sessionId = this.host.session?.id;
507414
507420
  if (sessionId !== void 0) bindPersonalMemoryRememberIntentTurn(sessionId, event.turnId);
@@ -507451,6 +507457,7 @@ var SessionEventHandler = class {
507451
507457
  }, 0);
507452
507458
  }
507453
507459
  handleTurnEnd(event, sendQueued) {
507460
+ this.clearModelRetryHint();
507454
507461
  this.turnWatchdog.stop();
507455
507462
  this.host.restoreQueuedSteerAtTurnEnd(event.turnId);
507456
507463
  const sessionId = this.host.session?.id;
@@ -507477,6 +507484,7 @@ var SessionEventHandler = class {
507477
507484
  }
507478
507485
  handleStepBegin(event) {
507479
507486
  this.turnWatchdog.recordProgress();
507487
+ this.clearModelRetryHint();
507480
507488
  this.host.commitQueuedSteerAtStepStart(event.turnId);
507481
507489
  this.host.streamingUI.flushNow();
507482
507490
  this.host.streamingUI.setStep(event.step);
@@ -507493,6 +507501,7 @@ var SessionEventHandler = class {
507493
507501
  });
507494
507502
  }
507495
507503
  handleStepCompleted(event) {
507504
+ this.clearModelRetryHint();
507496
507505
  this.host.streamingUI.flushNow();
507497
507506
  if (event.usage !== void 0) this.currentTurnTokenCount = (this.currentTurnTokenCount ?? 0) + totalTokenUsage(event.usage);
507498
507507
  this.maybeShowDebugTiming(event);
@@ -507535,6 +507544,7 @@ var SessionEventHandler = class {
507535
507544
  this.subAgentEventHandler.markActiveAgentSwarmsCancelled();
507536
507545
  }
507537
507546
  handleStepInterrupted(event) {
507547
+ this.clearModelRetryHint();
507538
507548
  this.host.streamingUI.flushNow();
507539
507549
  this.host.streamingUI.resetToolUi();
507540
507550
  this.host.streamingUI.finalizeLiveTextBuffers("idle");
@@ -507547,10 +507557,26 @@ var SessionEventHandler = class {
507547
507557
  }
507548
507558
  this.host.showError(reason === "max_steps" ? uiText("sessionEvent.interrupted.maxSteps") : uiText("sessionEvent.interrupted.reason", { reason }));
507549
507559
  }
507560
+ handleStepRetrying(event) {
507561
+ this.turnWatchdog.recordProgress();
507562
+ const hint = formatModelRetryProgress(event);
507563
+ this.modelRetryHint = hint;
507564
+ this.host.state.footer.setTransientHint(hint);
507565
+ this.host.state.ui.requestRender();
507566
+ }
507567
+ clearModelRetryHint() {
507568
+ if (this.modelRetryHint === void 0) return;
507569
+ if (this.host.state.footer.getTransientHint() === this.modelRetryHint) {
507570
+ this.host.state.footer.setTransientHint(null);
507571
+ this.host.state.ui.requestRender();
507572
+ }
507573
+ this.modelRetryHint = void 0;
507574
+ }
507550
507575
  handleThinkingDelta(event) {
507551
507576
  const { state, streamingUI } = this.host;
507552
507577
  if (event.delta.length === 0 && !streamingUI.hasThinkingDraft()) return;
507553
507578
  if (event.delta.length > 0) this.turnWatchdog.recordProgress();
507579
+ if (event.delta.length > 0) this.clearModelRetryHint();
507554
507580
  streamingUI.appendThinkingDelta(event.delta);
507555
507581
  this.host.patchLivePane({ mode: "idle" });
507556
507582
  if (state.appState.streamingPhase !== "thinking") this.host.setAppState({
@@ -507564,6 +507590,7 @@ var SessionEventHandler = class {
507564
507590
  if (streamingUI.hasThinkingDraft()) streamingUI.flushThinkingToTranscript("idle");
507565
507591
  if (event.delta.trim().length > 0) {
507566
507592
  this.turnWatchdog.recordProgress();
507593
+ this.clearModelRetryHint();
507567
507594
  this.currentTurnHasAssistantText = true;
507568
507595
  this.pendingModelBlockedFallback = void 0;
507569
507596
  }
@@ -507601,6 +507628,7 @@ var SessionEventHandler = class {
507601
507628
  });
507602
507629
  }
507603
507630
  handleToolCall(event) {
507631
+ this.clearModelRetryHint();
507604
507632
  this.turnWatchdog.recordToolCall(event.toolCallId, event.name, event.args);
507605
507633
  const { streamingUI } = this.host;
507606
507634
  streamingUI.flushNow();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.285",
3
+ "version": "9.1.287",
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": {