blun-king-cli 9.1.74 → 9.1.76

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
@@ -43,6 +43,11 @@ einen aktiven Zug ab, ohne den geschriebenen Entwurf zu löschen. Das gilt auch
43
43
  bei Autovervollständigung, Geistervorschlägen, Bash-Eingabe und einer offenen
44
44
  Mehrzeileneingabe.
45
45
 
46
+ Telegram-Nachrichten, die bei bereits aktivem Kernzug eintreffen, werden ohne
47
+ konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
48
+ FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
49
+ `turn.agent_busy`-Fehler.
50
+
46
51
  Zuverlässiger King-Start
47
52
  -----------------------
48
53
  Der Windows-Hilfsprozess für private Pfade übernimmt TEMP und TMP aus der
@@ -175,6 +180,11 @@ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Seku
175
180
  genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
176
181
  GetMedia abrufen.
177
182
 
183
+ Ein angenommener Medienauftrag bleibt über Verdichtungen und schnelle
184
+ Folgeturns hinweg gespeichert. Solange er offen ist, bleibt `GetMedia`
185
+ verfügbar; King prüft den tatsächlichen Status, statt fälschlich zu behaupten,
186
+ die Medienerzeugung sei nicht verfügbar.
187
+
178
188
  Angehängte Bilder werden weiterhin mit ReadMediaFile gelesen. Bild-, Video- und
179
189
  Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
180
190
  nutzbar bleibt. GenerateVideo kann außerdem einen abgeschlossenen Bildauftrag
package/README.md CHANGED
@@ -62,6 +62,10 @@ blun tools enable context7
62
62
  Telegram-Nachrichten werden in Eingangsreihenfolge verarbeitet. Trifft eine
63
63
  Nachricht im Leerlauf ein, startet die Warteschlange selbstständig; während
64
64
  eines Werkzeuglaufs wird sie am nächsten geschützten Übergabepunkt eingefügt.
65
+ Telegram-Nachrichten, die bei bereits aktivem Kernzug eintreffen, werden ohne
66
+ konkurrierenden Start zurückgewiesen und bleiben an der Spitze der
67
+ FIFO-Warteschlange. Für diesen normalen Wartestatus erscheint kein
68
+ `turn.agent_busy`-Fehler.
65
69
  `Strg+C` und `Esc` brechen einen aktiven Zug zuverlässig ab, ohne den bereits
66
70
  geschriebenen Entwurf zu löschen. Das gilt auch bei Autovervollständigung,
67
71
  Geistervorschlägen, Bash-Eingabe und einer noch offenen Mehrzeileneingabe.
@@ -203,6 +207,11 @@ Marmor“ oder „Animiere das letzte Bild als ruhige Kamerafahrt von sechs Seku
203
207
  genügt. King wählt den passenden Medienweg und kann das asynchrone Ergebnis mit
204
208
  `GetMedia` abrufen.
205
209
 
210
+ Ein angenommener Medienauftrag bleibt über Verdichtungen und schnelle
211
+ Folgeturns hinweg gespeichert. Solange er offen ist, bleibt `GetMedia`
212
+ verfügbar; King prüft den tatsächlichen Status, statt fälschlich zu behaupten,
213
+ die Medienerzeugung sei nicht verfügbar.
214
+
206
215
  Angehängte Bilder werden weiterhin mit `ReadMediaFile` gelesen. Bild-, Video-
207
216
  und Spracherzeugung laufen asynchron, sodass die Konsole während der Verarbeitung
208
217
  nutzbar bleibt. `GenerateVideo` kann außerdem einen abgeschlossenen Bildauftrag
@@ -0,0 +1,43 @@
1
+ 'use strict';
2
+
3
+ const TERMINAL_STATUSES = new Set([
4
+ 'blocked',
5
+ 'cancelled',
6
+ 'canceled',
7
+ 'complete',
8
+ 'completed',
9
+ 'expired',
10
+ 'failed',
11
+ 'succeeded',
12
+ ]);
13
+
14
+ function validMediaId(value) {
15
+ return typeof value === 'string' && value.length > 0 && value.length <= 200
16
+ && /^[A-Za-z0-9_-]+$/.test(value);
17
+ }
18
+
19
+ function terminalStatus(value) {
20
+ return typeof value === 'string' && TERMINAL_STATUSES.has(value.trim().toLowerCase());
21
+ }
22
+
23
+ function createPendingMediaTracker() {
24
+ const pending = new Set();
25
+
26
+ return {
27
+ observeSubmission(job) {
28
+ if (!validMediaId(job?.id)) return;
29
+ if (terminalStatus(job?.status)) pending.delete(job.id);
30
+ else pending.add(job.id);
31
+ },
32
+ observeLookup(result) {
33
+ if (!validMediaId(result?.id)) return;
34
+ if (result.kind !== 'status' || terminalStatus(result.status)) pending.delete(result.id);
35
+ else pending.add(result.id);
36
+ },
37
+ ids() {
38
+ return [...pending];
39
+ },
40
+ };
41
+ }
42
+
43
+ module.exports = { createPendingMediaTracker };
package/blun.mjs CHANGED
@@ -259910,17 +259910,23 @@ function blunMessageTextChars(message) {
259910
259910
  function blunFastConversationHistory(history) {
259911
259911
  const selected = [];
259912
259912
  let textChars = 0;
259913
+ let expectsAssistant = false;
259913
259914
  for (let index = history.length - 1; index >= 0; index -= 1) {
259914
259915
  const message = history[index];
259915
259916
  if (message === void 0) continue;
259916
259917
  if (message.role === "user" && message.origin?.kind !== "user") continue;
259917
- if (message.role === "assistant" && message.toolCalls.length > 0) continue;
259918
259918
  if (message.role !== "user" && message.role !== "assistant") continue;
259919
+ if (message.role === "user" && selected.length > 0 && expectsAssistant) break;
259920
+ if (message.role === "assistant") {
259921
+ if (!expectsAssistant) continue;
259922
+ if (message.toolCalls.length > 0) break;
259923
+ }
259919
259924
  const messageTextChars = blunMessageTextChars(message);
259920
259925
  if (messageTextChars === 0) continue;
259921
- if (textChars + messageTextChars > 12e3) continue;
259926
+ if (textChars + messageTextChars > 12e3) break;
259922
259927
  selected.push(message);
259923
259928
  textChars += messageTextChars;
259929
+ expectsAssistant = message.role === "user";
259924
259930
  if (selected.length >= 6) break;
259925
259931
  }
259926
259932
  return selected.toReversed();
@@ -259936,9 +259942,14 @@ function blunToolsForOrigin(tools, origin) {
259936
259942
  if (origin.kind !== "system_trigger" && origin.kind !== "injection") return tools;
259937
259943
  return tools.filter((tool) => !BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name));
259938
259944
  }
259939
- function blunFastConversationTools(tools, input) {
259940
- if (!BLUN_TELEGRAM_CHANNEL_RE.test(blunExtractText(input))) return [];
259941
- return tools.filter((tool) => BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name));
259945
+ function blunPendingMediaSystemPrompt(systemPrompt, pendingMediaJobIds) {
259946
+ if (pendingMediaJobIds.length === 0) return systemPrompt;
259947
+ return `${systemPrompt}\n\n## Pending BLUN media\n\nThe following media jobs were already accepted and are still pending: ${pendingMediaJobIds.join(", ")}. Call GetMedia exactly once for each listed id before replying. If a job is still processing, report that status truthfully and wait for a later turn; do not claim that media tools are unavailable.`;
259948
+ }
259949
+ function blunFastConversationTools(tools, input, pendingMediaJobIds = []) {
259950
+ const telegramTurn = BLUN_TELEGRAM_CHANNEL_RE.test(blunExtractText(input));
259951
+ const mediaPending = pendingMediaJobIds.length > 0;
259952
+ return tools.filter((tool) => telegramTurn && BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name) || mediaPending && tool.name === "GetMedia");
259942
259953
  }
259943
259954
  /**
259944
259955
  * Keep frequently used native tools resident and expose every other tool by
@@ -260275,11 +260286,15 @@ var init_turn = __esmMin((() => {
260275
260286
  input,
260276
260287
  origin
260277
260288
  });
260278
- const blocked = this.activeTurn !== null;
260279
- const buffered = !blocked && this.agent.fullCompaction.isCompacting;
260289
+ if (this.activeTurn !== null) return {
260290
+ accepted: false,
260291
+ buffered: false,
260292
+ turnId: null
260293
+ };
260294
+ const buffered = this.agent.fullCompaction.isCompacting;
260280
260295
  const turnId = this.launch(input, origin);
260281
260296
  return {
260282
- accepted: !blocked,
260297
+ accepted: true,
260283
260298
  buffered,
260284
260299
  turnId
260285
260300
  };
@@ -260767,7 +260782,9 @@ var init_turn = __esmMin((() => {
260767
260782
  const turnHasAttachment = blunTurnHasAttachment(input);
260768
260783
  const turnThinkingEffort = turnHasAttachment ? void 0 : selectThinkingEffortForTurn(blunExtractText(blunThinkingIntentInput(input)), origin.kind);
260769
260784
  const fastConversation = turnThinkingEffort === "off" || turnThinkingEffort === "low";
260770
- const turnLLM = this.agent.llmForTurn(turnThinkingEffort, fastConversation ? this.agent.fastConversationSystemPrompt : void 0);
260785
+ const pendingMediaJobIds = this.agent.toolServices?.media?.pendingMediaJobIds?.() ?? [];
260786
+ const turnSystemPrompt = pendingMediaJobIds.length > 0 ? blunPendingMediaSystemPrompt(fastConversation ? this.agent.fastConversationSystemPrompt : this.agent.effectiveSystemPrompt, pendingMediaJobIds) : fastConversation ? this.agent.fastConversationSystemPrompt : void 0;
260787
+ const turnLLM = this.agent.llmForTurn(turnThinkingEffort, turnSystemPrompt);
260771
260788
  let previousStepToolOutcome = "initial";
260772
260789
  let currentStepHadTool = false;
260773
260790
  let currentStepHadFailure = false;
@@ -260779,7 +260796,7 @@ var init_turn = __esmMin((() => {
260779
260796
  try {
260780
260797
  const eligibleTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, this.agent.tools.loopTools), origin);
260781
260798
  const toolSelection = fastConversation ? {
260782
- tools: [...blunFastConversationTools(eligibleTools, input)],
260799
+ tools: [...blunFastConversationTools(eligibleTools, input, pendingMediaJobIds)],
260783
260800
  deferredToolCount: 0
260784
260801
  } : turnNeedsTools ? blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames) : {
260785
260802
  tools: [],
@@ -260843,8 +260860,9 @@ var init_turn = __esmMin((() => {
260843
260860
  if (steerEfforts.some((effort) => effort === void 0)) return;
260844
260861
  const steerInput = pendingSteers.flatMap((steer) => [...steer.input]);
260845
260862
  const steerThinkingEffort = "low";
260846
- const steerLLM = this.agent.llmForTurn(steerThinkingEffort, this.agent.fastConversationSystemPrompt);
260847
- const steerTools = [...blunFastConversationTools(eligibleTools, steerInput)];
260863
+ const steerPendingMediaJobIds = this.agent.toolServices?.media?.pendingMediaJobIds?.() ?? [];
260864
+ const steerLLM = this.agent.llmForTurn(steerThinkingEffort, blunPendingMediaSystemPrompt(this.agent.fastConversationSystemPrompt, steerPendingMediaJobIds));
260865
+ const steerTools = [...blunFastConversationTools(eligibleTools, steerInput, steerPendingMediaJobIds)];
260848
260866
  const steerHistory = () => blunFastConversationHistory(this.agent.context.history);
260849
260867
  const buildSteerMessages = () => this.agent.context.project(steerHistory(), { dropOrphanResults: true });
260850
260868
  const buildSteerMessagesStrict = () => this.agent.context.project(steerHistory(), {
@@ -311236,8 +311254,9 @@ async function assertSuccess(response, operation) {
311236
311254
  } catch {}
311237
311255
  throw new Error(`${operation} failed: HTTP ${String(response.status)}${detail ? `: ${detail}` : ""}`);
311238
311256
  }
311239
- var BlunMediaService;
311257
+ var createPendingMediaTracker, BlunMediaService;
311240
311258
  var init_blun_media = __esmMin((() => {
311259
+ ({ createPendingMediaTracker } = createRequire(import.meta.url)("./bin/pending-media-policy.cjs"));
311241
311260
  BlunMediaService = class {
311242
311261
  tokenProvider;
311243
311262
  apiKey;
@@ -311245,6 +311264,7 @@ var init_blun_media = __esmMin((() => {
311245
311264
  defaultHeaders;
311246
311265
  customHeaders;
311247
311266
  fetchImpl;
311267
+ pendingMedia = createPendingMediaTracker();
311248
311268
  constructor(options) {
311249
311269
  this.tokenProvider = options.tokenProvider;
311250
311270
  this.apiKey = options.apiKey;
@@ -311297,28 +311317,28 @@ var init_blun_media = __esmMin((() => {
311297
311317
  }
311298
311318
  async getMedia(id, options) {
311299
311319
  const response = await this.request(`/media/${encodeURIComponent(id)}`, { method: "GET" }, options);
311300
- if (response.status === 409) return parseBlockedStatus(response, id);
311301
- if (response.status === 410) return {
311320
+ if (response.status === 409) return this.trackMediaLookup(parseBlockedStatus(response, id));
311321
+ if (response.status === 410) return this.trackMediaLookup({
311302
311322
  kind: "status",
311303
311323
  id,
311304
311324
  status: "expired"
311305
- };
311325
+ });
311306
311326
  await assertSuccess(response, "Media lookup");
311307
311327
  const mimeType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() ?? "";
311308
- if (mimeType === "application/json" || mimeType.endsWith("+json")) return parseStatus(await response.json(), id);
311309
- if (mimeType === "text/plain") return {
311328
+ if (mimeType === "application/json" || mimeType.endsWith("+json")) return this.trackMediaLookup(parseStatus(await response.json(), id));
311329
+ if (mimeType === "text/plain") return this.trackMediaLookup({
311310
311330
  kind: "text",
311311
311331
  id,
311312
311332
  mimeType,
311313
311333
  text: await response.text()
311314
- };
311334
+ });
311315
311335
  if (!mimeType.startsWith("image/") && !mimeType.startsWith("audio/") && !mimeType.startsWith("video/")) throw new Error(`Media lookup returned unsupported content type ${mimeType || "(missing)"}`);
311316
- return {
311336
+ return this.trackMediaLookup({
311317
311337
  kind: "file",
311318
311338
  id,
311319
311339
  mimeType,
311320
311340
  data: new Uint8Array(await response.arrayBuffer())
311321
- };
311341
+ });
311322
311342
  }
311323
311343
  async submit(path, body, options) {
311324
311344
  const response = await this.request(path, {
@@ -311327,7 +311347,16 @@ var init_blun_media = __esmMin((() => {
311327
311347
  }, options);
311328
311348
  if (response.status === 409) throw await blockedSubmissionError(response);
311329
311349
  await assertSuccess(response, "Media request");
311330
- return parseJob(await response.json());
311350
+ const job = parseJob(await response.json());
311351
+ this.pendingMedia.observeSubmission(job);
311352
+ return job;
311353
+ }
311354
+ trackMediaLookup(result) {
311355
+ this.pendingMedia.observeLookup(result);
311356
+ return result;
311357
+ }
311358
+ pendingMediaJobIds() {
311359
+ return this.pendingMedia.ids();
311331
311360
  }
311332
311361
  async request(path, init, options) {
311333
311362
  const firstToken = await this.resolveToken(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.74",
3
+ "version": "9.1.76",
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": {