blun-king-cli 9.1.395 → 9.1.396

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/LIESMICH.txt CHANGED
@@ -364,6 +364,23 @@ Folgeturns hinweg gespeichert. Solange er offen ist, bleibt `GetMedia`
364
364
  verfügbar; King prüft den tatsächlichen Status, statt fälschlich zu behaupten,
365
365
  die Medienerzeugung sei nicht verfügbar.
366
366
 
367
+ Passende Werkzeuge ohne Such-Zwischenschritt
368
+ ---------------------------------------------
369
+
370
+ Ab BLUN King 9.1.396 vergleicht King die aktuelle Anfrage mit den bereits
371
+ registrierten Werkzeugbeschreibungen. Bei eindeutiger Übereinstimmung lädt er
372
+ für diesen Zug höchstens zwei passende, sonst zurückgestellte Schemata direkt.
373
+ Name, Beschreibung, Parameterhinweise und Beispiele dürfen zur Auswahl
374
+ beitragen; frühere Nachrichten außerhalb des aktuellen Telegram-Kanalblocks
375
+ zählen nicht als neue Absicht.
376
+
377
+ ToolSearch bleibt für unklare, unbekannte und mehrdeutige Anfragen vollständig
378
+ verfügbar. Eine automatische Auswahl wird nicht dauerhaft gespeichert und
379
+ vergrößert den residenten Werkzeugsatz nicht. Dadurch kann eine natürliche
380
+ Anweisung wie "Zeig mir den letzten Telegram-Verlauf" das passende Werkzeug im
381
+ selben Modellschritt erhalten, während Begrüßungen und fachfremde Anfragen keine
382
+ zusätzlichen Schemata laden.
383
+
367
384
  Angehängte Bilder werden weiterhin mit ReadMediaFile gelesen. Bild-, Video- und
368
385
  Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
369
386
  nutzbar bleibt. GenerateVideo kann außerdem einen abgeschlossenen Bildauftrag
package/README.md CHANGED
@@ -375,6 +375,22 @@ Folgeturns hinweg gespeichert. Solange er offen ist, bleibt `GetMedia`
375
375
  verfügbar; King prüft den tatsächlichen Status, statt fälschlich zu behaupten,
376
376
  die Medienerzeugung sei nicht verfügbar.
377
377
 
378
+ ## Passende Werkzeuge ohne Such-Zwischenschritt
379
+
380
+ Ab BLUN King 9.1.396 vergleicht King die aktuelle Anfrage mit den bereits
381
+ registrierten Werkzeugbeschreibungen. Bei eindeutiger Übereinstimmung lädt er
382
+ für diesen Zug höchstens zwei passende, sonst zurückgestellte Schemata direkt.
383
+ Name, Beschreibung, Parameterhinweise und Beispiele dürfen zur Auswahl
384
+ beitragen; frühere Nachrichten außerhalb des aktuellen Telegram-Kanalblocks
385
+ zählen nicht als neue Absicht.
386
+
387
+ `ToolSearch` bleibt für unklare, unbekannte und mehrdeutige Anfragen vollständig
388
+ verfügbar. Eine automatische Auswahl wird nicht dauerhaft gespeichert und
389
+ vergrößert den residenten Werkzeugsatz nicht. Dadurch kann eine natürliche
390
+ Anweisung wie „Zeig mir den letzten Telegram-Verlauf“ das passende Werkzeug im
391
+ selben Modellschritt erhalten, während Begrüßungen und fachfremde Anfragen keine
392
+ zusätzlichen Schemata laden.
393
+
378
394
  Angehängte Bilder werden weiterhin mit `ReadMediaFile` gelesen. Bild-, Video-
379
395
  und Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
380
396
  nutzbar bleibt. `GenerateVideo` kann außerdem einen abgeschlossenen Bildauftrag
@@ -5,13 +5,20 @@ const TOOL_SCHEMA_MAX_TOKENS = 32_000;
5
5
  const DEFERRED_TOOL_LOADER_NAME = 'ToolSearch';
6
6
  const MAX_PERSISTENT_DEFERRED_TOOLS = 4;
7
7
  const MAX_TOOL_SEARCH_QUERY_CHARS = 1_000;
8
+ const MAX_AUTO_RANKED_TOOLS = 2;
9
+ const TOOL_RANK_STOP_WORDS = new Set([
10
+ 'aber', 'also', 'and', 'aus', 'bitte', 'can', 'das', 'den', 'der', 'des', 'die', 'dies', 'diese',
11
+ 'du', 'ein', 'eine', 'einer', 'eines', 'for', 'fuer', 'für', 'haben', 'ich', 'ist', 'kann', 'kannst',
12
+ 'mal', 'me', 'mein', 'meine', 'mir', 'mit', 'of', 'please', 'soll', 'the', 'und', 'uns', 'von',
13
+ 'was', 'wir', 'with', 'you', 'zeige', 'zeig', 'pruefe', 'prüfe', 'lies', 'read', 'show', 'get',
14
+ ]);
8
15
  const TOOL_SEARCH_INTENT_WORDS = new Set([
9
16
  'find', 'fetch', 'get', 'holen', 'list', 'read', 'search', 'show', 'anzeigen', 'lesen', 'suchen',
10
17
  ]);
11
18
  const TOOL_SEARCH_RELATED_TERM_GROUPS = Object.freeze([
12
19
  Object.freeze([
13
20
  'current', 'inbox', 'latest', 'message', 'messages', 'nachricht', 'nachrichten', 'queue', 'queued',
14
- 'recent', 'update', 'updates',
21
+ 'recent', 'letzte', 'letzten', 'letzter', 'vergangen', 'update', 'updates',
15
22
  ]),
16
23
  Object.freeze([
17
24
  'chat', 'conversation', 'history', 'log', 'logs', 'processed', 'protokoll', 'transcript', 'verlauf',
@@ -165,6 +172,96 @@ function relatedSearchTerms(token) {
165
172
  return group || [token];
166
173
  }
167
174
 
175
+ function rankingIntentTexts(value) {
176
+ const text = String(value || '');
177
+ const channelBodies = [...text.matchAll(
178
+ /(?:^|\r?\n)<channel\b[^>]*>\r?\n([\s\S]*?)\r?\n<\/channel>(?=\r?\n|$)/giu,
179
+ )].map((match) => match[1]);
180
+ return channelBodies.length > 0 ? channelBodies : [text];
181
+ }
182
+
183
+ function rankingTokens(value) {
184
+ return [...new Set(String(value || '')
185
+ .normalize('NFKC')
186
+ .toLowerCase()
187
+ .match(/[\p{L}\p{N}]{3,}/gu) || [])]
188
+ .filter((token) => !TOOL_RANK_STOP_WORDS.has(token));
189
+ }
190
+
191
+ function toolRankingDocument(tool) {
192
+ const name = String(tool?.name || '');
193
+ const leaf = name.split(/__|:/).at(-1) || name;
194
+ const nameTokens = rankingTokens(`${name.replaceAll('_', ' ')} ${leaf.replaceAll('_', ' ')}`);
195
+ const descriptionTokens = rankingTokens(tool?.description);
196
+ const parameterText = [];
197
+ const properties = tool?.parameters?.properties;
198
+ if (properties && typeof properties === 'object') {
199
+ for (const [parameterName, definition] of Object.entries(properties)) {
200
+ parameterText.push(parameterName, definition?.description || '');
201
+ }
202
+ }
203
+ const exampleText = Array.isArray(tool?.examples)
204
+ ? tool.examples.map((example) => example?.prompt || '').join(' ')
205
+ : '';
206
+ return {
207
+ name,
208
+ nameTokens: new Set(nameTokens),
209
+ descriptionTokens: new Set(descriptionTokens),
210
+ detailTokens: new Set(rankingTokens(`${parameterText.join(' ')} ${exampleText}`)),
211
+ };
212
+ }
213
+
214
+ function tokenMatchScore(document, token) {
215
+ if (document.nameTokens.has(token)) return 10;
216
+ if (document.descriptionTokens.has(token)) return 5;
217
+ if (document.detailTokens.has(token)) return 4;
218
+ const related = relatedSearchTerms(token).filter((term) => term !== token);
219
+ if (related.some((term) => document.nameTokens.has(term))) return 5;
220
+ if (related.some((term) => document.descriptionTokens.has(term))) return 3;
221
+ if (related.some((term) => document.detailTokens.has(term))) return 2;
222
+ return 0;
223
+ }
224
+
225
+ /**
226
+ * Select a tiny, high-confidence subset of deferred schemas for the current
227
+ * request. This mirrors AnythingLLM's tool-reranker boundary without adding an
228
+ * embedding runtime: King reuses the descriptions it already owns and keeps
229
+ * ToolSearch available whenever the lexical evidence is weak or ambiguous.
230
+ */
231
+ function rankedToolNamesForTurnText(tools, value, options = {}) {
232
+ const maxTools = Math.max(0, Math.min(
233
+ MAX_AUTO_RANKED_TOOLS,
234
+ Number.isInteger(options.maxTools) ? options.maxTools : MAX_AUTO_RANKED_TOOLS,
235
+ ));
236
+ if (maxTools === 0) return new Set();
237
+ const queryTokens = [...new Set(rankingIntentTexts(value).flatMap(rankingTokens))];
238
+ if (queryTokens.length === 0) return new Set();
239
+
240
+ const ranked = (Array.isArray(tools) ? tools : []).map((tool) => {
241
+ const document = toolRankingDocument(tool);
242
+ if (!document.name) return null;
243
+ let score = 0;
244
+ let matchedTokens = 0;
245
+ let exactNameMatches = 0;
246
+ for (const token of queryTokens) {
247
+ const tokenScore = tokenMatchScore(document, token);
248
+ if (tokenScore === 0) continue;
249
+ score += tokenScore;
250
+ matchedTokens += 1;
251
+ if (document.nameTokens.has(token)) exactNameMatches += 1;
252
+ }
253
+ const highConfidence = matchedTokens >= 2 || (exactNameMatches >= 1 && score >= 10);
254
+ return highConfidence ? { name: document.name, score, matchedTokens, exactNameMatches } : null;
255
+ }).filter(Boolean).sort((left, right) => (
256
+ right.matchedTokens - left.matchedTokens
257
+ || right.score - left.score
258
+ || right.exactNameMatches - left.exactNameMatches
259
+ || left.name.localeCompare(right.name)
260
+ ));
261
+
262
+ return new Set(ranked.slice(0, maxTools).map((entry) => entry.name));
263
+ }
264
+
168
265
  function searchRelatedDeferredTools(tools, query, options = {}) {
169
266
  const normalized = normalizeDeferredToolQuery(query).toLowerCase();
170
267
  if (!normalized || normalized.startsWith('select:')) return [];
@@ -351,6 +448,7 @@ module.exports = {
351
448
  mediaGenerationToolNamesForText,
352
449
  mediaToolNamesForTurnText,
353
450
  normalizeDeferredToolQuery,
451
+ rankedToolNamesForTurnText,
354
452
  rememberDeferredToolAfterNotFound,
355
453
  toolSchemaBudgetTokens,
356
454
  };
package/blun.mjs CHANGED
@@ -261514,7 +261514,7 @@ function toolResultText(result) {
261514
261514
  function abandonedToolResultOutput(ended) {
261515
261515
  return `Tool call did not complete: ${ended.reason === "cancelled" ? "the turn was cancelled" : ended.reason === "failed" ? `the turn failed${ended.error !== void 0 ? ` (${ended.error.message})` : ""}` : "the turn ended"} before its result was recorded. Do not assume the tool completed successfully.`;
261516
261516
  }
261517
- var BLUN_CORE_TOOL_NAMES, BLUN_LEAN_TOOL_NAMES, BLUN_ATTACHMENT_MARKER_RE, BLUN_TELEGRAM_OUTBOUND_TOOL_RE, BLUN_TELEGRAM_CHANNEL_RE, BLUN_TOOL_BUDGET_RATIO, createDeferredToolLoader, mediaToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens, projectRecurringCronHistory, TelegramDeliveryLedger, createRuntimeCognitiveTurnLifecycle, cognitiveFocusScopesForTurn, buildCognitiveWorkFocus, buildAttentionQueueItem, LLM_NOT_SET_MESSAGE, GOAL_CONTINUATION_ORIGIN, GOAL_COMPLETION_REMINDER_NAME, GOAL_BLOCKED_REMINDER_NAME, GOAL_RATE_LIMIT_PAUSE_REASON, GOAL_PROVIDER_CONNECTION_PAUSE_PREFIX, GOAL_PROVIDER_AUTH_PAUSE_PREFIX, GOAL_PROVIDER_API_PAUSE_PREFIX, GOAL_MODEL_CONFIG_PAUSE_PREFIX, GOAL_RUNTIME_PAUSE_PREFIX, GOAL_PROVIDER_FILTERED_PAUSE_REASON, GOAL_CONTINUATION_PROMPT, TurnFlow;
261517
+ var BLUN_CORE_TOOL_NAMES, BLUN_LEAN_TOOL_NAMES, BLUN_ATTACHMENT_MARKER_RE, BLUN_TELEGRAM_OUTBOUND_TOOL_RE, BLUN_TELEGRAM_CHANNEL_RE, BLUN_TOOL_BUDGET_RATIO, createDeferredToolLoader, mediaToolNamesForTurnText, rankedToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens, projectRecurringCronHistory, TelegramDeliveryLedger, createRuntimeCognitiveTurnLifecycle, cognitiveFocusScopesForTurn, buildCognitiveWorkFocus, buildAttentionQueueItem, LLM_NOT_SET_MESSAGE, GOAL_CONTINUATION_ORIGIN, GOAL_COMPLETION_REMINDER_NAME, GOAL_BLOCKED_REMINDER_NAME, GOAL_RATE_LIMIT_PAUSE_REASON, GOAL_PROVIDER_CONNECTION_PAUSE_PREFIX, GOAL_PROVIDER_AUTH_PAUSE_PREFIX, GOAL_PROVIDER_API_PAUSE_PREFIX, GOAL_MODEL_CONFIG_PAUSE_PREFIX, GOAL_RUNTIME_PAUSE_PREFIX, GOAL_PROVIDER_FILTERED_PAUSE_REASON, GOAL_CONTINUATION_PROMPT, TurnFlow;
261518
261518
  var init_turn = __esmMin((() => {
261519
261519
  init_dist$4();
261520
261520
  init_src$4();
@@ -261531,7 +261531,7 @@ var init_turn = __esmMin((() => {
261531
261531
  init_tool_result_budget();
261532
261532
  init_user_message_offload();
261533
261533
  init_assistant_message_offload();
261534
- ({ CORE_TOOL_NAMES: BLUN_CORE_TOOL_NAMES, createDeferredToolLoader, mediaToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens } = createRequire(import.meta.url)("./bin/turn-tool-performance-policy.cjs"));
261534
+ ({ CORE_TOOL_NAMES: BLUN_CORE_TOOL_NAMES, createDeferredToolLoader, mediaToolNamesForTurnText, rankedToolNamesForTurnText, rememberDeferredToolAfterNotFound, toolSchemaBudgetTokens } = createRequire(import.meta.url)("./bin/turn-tool-performance-policy.cjs"));
261535
261535
  ({ projectRecurringCronHistory } = createRequire(import.meta.url)("./bin/recurring-cron-history-policy.cjs"));
261536
261536
  ({ TelegramDeliveryLedger } = createRequire(import.meta.url)("./bin/repeated-user-message-projection.cjs"));
261537
261537
  ({ createRuntimeCognitiveTurnLifecycle } = createRequire(import.meta.url)("./bin/cognitive-turn-lifecycle.cjs"));
@@ -262265,12 +262265,13 @@ var init_turn = __esmMin((() => {
262265
262265
  variant: "cognitive_continuity"
262266
262266
  });
262267
262267
  this.setActiveSteerAcceptance(turnId, true);
262268
+ const turnText = blunExtractText(input);
262268
262269
  const turnNeedsTools = blunTurnNeedsTools(input, origin);
262269
262270
  const turnHasAttachment = blunTurnHasAttachment(input);
262270
262271
  const turnThinkingEffort = turnHasAttachment ? void 0 : selectThinkingEffortForTurn(blunExtractText(blunThinkingIntentInput(input)), origin.kind);
262271
262272
  const fastConversation = turnThinkingEffort === "off" || turnThinkingEffort === "low";
262272
262273
  const pendingMediaJobIds = this.agent.toolServices?.media?.pendingMediaJobIds?.() ?? [];
262273
- const requiredToolNames = mediaToolNamesForTurnText(blunExtractText(input));
262274
+ const requiredToolNames = mediaToolNamesForTurnText(turnText);
262274
262275
  const turnSystemPrompt = pendingMediaJobIds.length > 0 ? blunPendingMediaSystemPrompt(fastConversation ? this.agent.fastConversationSystemPrompt : this.agent.effectiveSystemPrompt, pendingMediaJobIds) : fastConversation ? this.agent.fastConversationSystemPrompt : void 0;
262275
262276
  const turnLLM = this.agent.llmForTurn(turnThinkingEffort, turnSystemPrompt);
262276
262277
  let previousStepToolOutcome = "initial";
@@ -262285,10 +262286,13 @@ var init_turn = __esmMin((() => {
262285
262286
  const originTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, this.agent.tools.loopTools), origin);
262286
262287
  const compactConversationTool = originTools.find((tool) => tool.name === "CompactConversation");
262287
262288
  const eligibleTools = originTools.filter((tool) => tool.name !== "CompactConversation" || this.agent.fullCompaction.isProactiveCompactionEligible());
262289
+ const turnRequiredToolNames = new Set(requiredToolNames);
262290
+ const rankedToolNames = rankedToolNamesForTurnText(eligibleTools, turnText);
262291
+ for (const name of rankedToolNames) turnRequiredToolNames.add(name);
262288
262292
  const toolSelection = fastConversation ? {
262289
- tools: [...blunFastConversationTools(eligibleTools, input, pendingMediaJobIds, requiredToolNames)],
262293
+ tools: [...blunFastConversationTools(eligibleTools, input, pendingMediaJobIds, turnRequiredToolNames)],
262290
262294
  deferredToolCount: 0
262291
- } : turnNeedsTools ? blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames, requiredToolNames) : {
262295
+ } : turnNeedsTools ? blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames, turnRequiredToolNames) : {
262292
262296
  tools: [],
262293
262297
  deferredToolCount: 0
262294
262298
  };
@@ -262356,7 +262360,9 @@ var init_turn = __esmMin((() => {
262356
262360
  const steerThinkingEffort = "low";
262357
262361
  const steerPendingMediaJobIds = this.agent.toolServices?.media?.pendingMediaJobIds?.() ?? [];
262358
262362
  const steerLLM = this.agent.llmForTurn(steerThinkingEffort, blunPendingMediaSystemPrompt(this.agent.fastConversationSystemPrompt, steerPendingMediaJobIds));
262359
- const steerRequiredToolNames = mediaToolNamesForTurnText(blunExtractText(steerInput));
262363
+ const steerText = blunExtractText(steerInput);
262364
+ const steerRequiredToolNames = mediaToolNamesForTurnText(steerText);
262365
+ for (const name of rankedToolNamesForTurnText(eligibleTools, steerText)) steerRequiredToolNames.add(name);
262360
262366
  const steerTools = [...blunFastConversationTools(eligibleTools, steerInput, steerPendingMediaJobIds, steerRequiredToolNames)];
262361
262367
  const steerHistory = () => blunFastConversationHistory(this.agent.context.history);
262362
262368
  const buildSteerMessages = () => this.agent.context.project(steerHistory(), { dropOrphanResults: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.395",
3
+ "version": "9.1.396",
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": {