blun-king-cli 9.1.75 → 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
@@ -259942,9 +259942,14 @@ function blunToolsForOrigin(tools, origin) {
259942
259942
  if (origin.kind !== "system_trigger" && origin.kind !== "injection") return tools;
259943
259943
  return tools.filter((tool) => !BLUN_TELEGRAM_OUTBOUND_TOOL_RE.test(tool.name));
259944
259944
  }
259945
- function blunFastConversationTools(tools, input) {
259946
- if (!BLUN_TELEGRAM_CHANNEL_RE.test(blunExtractText(input))) return [];
259947
- 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");
259948
259953
  }
259949
259954
  /**
259950
259955
  * Keep frequently used native tools resident and expose every other tool by
@@ -260281,11 +260286,15 @@ var init_turn = __esmMin((() => {
260281
260286
  input,
260282
260287
  origin
260283
260288
  });
260284
- const blocked = this.activeTurn !== null;
260285
- 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;
260286
260295
  const turnId = this.launch(input, origin);
260287
260296
  return {
260288
- accepted: !blocked,
260297
+ accepted: true,
260289
260298
  buffered,
260290
260299
  turnId
260291
260300
  };
@@ -260773,7 +260782,9 @@ var init_turn = __esmMin((() => {
260773
260782
  const turnHasAttachment = blunTurnHasAttachment(input);
260774
260783
  const turnThinkingEffort = turnHasAttachment ? void 0 : selectThinkingEffortForTurn(blunExtractText(blunThinkingIntentInput(input)), origin.kind);
260775
260784
  const fastConversation = turnThinkingEffort === "off" || turnThinkingEffort === "low";
260776
- 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);
260777
260788
  let previousStepToolOutcome = "initial";
260778
260789
  let currentStepHadTool = false;
260779
260790
  let currentStepHadFailure = false;
@@ -260785,7 +260796,7 @@ var init_turn = __esmMin((() => {
260785
260796
  try {
260786
260797
  const eligibleTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, this.agent.tools.loopTools), origin);
260787
260798
  const toolSelection = fastConversation ? {
260788
- tools: [...blunFastConversationTools(eligibleTools, input)],
260799
+ tools: [...blunFastConversationTools(eligibleTools, input, pendingMediaJobIds)],
260789
260800
  deferredToolCount: 0
260790
260801
  } : turnNeedsTools ? blunSelectTurnTools(eligibleTools, this.agent.config.modelCapabilities?.max_context_tokens, this.loadedToolNames) : {
260791
260802
  tools: [],
@@ -260849,8 +260860,9 @@ var init_turn = __esmMin((() => {
260849
260860
  if (steerEfforts.some((effort) => effort === void 0)) return;
260850
260861
  const steerInput = pendingSteers.flatMap((steer) => [...steer.input]);
260851
260862
  const steerThinkingEffort = "low";
260852
- const steerLLM = this.agent.llmForTurn(steerThinkingEffort, this.agent.fastConversationSystemPrompt);
260853
- 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)];
260854
260866
  const steerHistory = () => blunFastConversationHistory(this.agent.context.history);
260855
260867
  const buildSteerMessages = () => this.agent.context.project(steerHistory(), { dropOrphanResults: true });
260856
260868
  const buildSteerMessagesStrict = () => this.agent.context.project(steerHistory(), {
@@ -311242,8 +311254,9 @@ async function assertSuccess(response, operation) {
311242
311254
  } catch {}
311243
311255
  throw new Error(`${operation} failed: HTTP ${String(response.status)}${detail ? `: ${detail}` : ""}`);
311244
311256
  }
311245
- var BlunMediaService;
311257
+ var createPendingMediaTracker, BlunMediaService;
311246
311258
  var init_blun_media = __esmMin((() => {
311259
+ ({ createPendingMediaTracker } = createRequire(import.meta.url)("./bin/pending-media-policy.cjs"));
311247
311260
  BlunMediaService = class {
311248
311261
  tokenProvider;
311249
311262
  apiKey;
@@ -311251,6 +311264,7 @@ var init_blun_media = __esmMin((() => {
311251
311264
  defaultHeaders;
311252
311265
  customHeaders;
311253
311266
  fetchImpl;
311267
+ pendingMedia = createPendingMediaTracker();
311254
311268
  constructor(options) {
311255
311269
  this.tokenProvider = options.tokenProvider;
311256
311270
  this.apiKey = options.apiKey;
@@ -311303,28 +311317,28 @@ var init_blun_media = __esmMin((() => {
311303
311317
  }
311304
311318
  async getMedia(id, options) {
311305
311319
  const response = await this.request(`/media/${encodeURIComponent(id)}`, { method: "GET" }, options);
311306
- if (response.status === 409) return parseBlockedStatus(response, id);
311307
- if (response.status === 410) return {
311320
+ if (response.status === 409) return this.trackMediaLookup(parseBlockedStatus(response, id));
311321
+ if (response.status === 410) return this.trackMediaLookup({
311308
311322
  kind: "status",
311309
311323
  id,
311310
311324
  status: "expired"
311311
- };
311325
+ });
311312
311326
  await assertSuccess(response, "Media lookup");
311313
311327
  const mimeType = response.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() ?? "";
311314
- if (mimeType === "application/json" || mimeType.endsWith("+json")) return parseStatus(await response.json(), id);
311315
- 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({
311316
311330
  kind: "text",
311317
311331
  id,
311318
311332
  mimeType,
311319
311333
  text: await response.text()
311320
- };
311334
+ });
311321
311335
  if (!mimeType.startsWith("image/") && !mimeType.startsWith("audio/") && !mimeType.startsWith("video/")) throw new Error(`Media lookup returned unsupported content type ${mimeType || "(missing)"}`);
311322
- return {
311336
+ return this.trackMediaLookup({
311323
311337
  kind: "file",
311324
311338
  id,
311325
311339
  mimeType,
311326
311340
  data: new Uint8Array(await response.arrayBuffer())
311327
- };
311341
+ });
311328
311342
  }
311329
311343
  async submit(path, body, options) {
311330
311344
  const response = await this.request(path, {
@@ -311333,7 +311347,16 @@ var init_blun_media = __esmMin((() => {
311333
311347
  }, options);
311334
311348
  if (response.status === 409) throw await blockedSubmissionError(response);
311335
311349
  await assertSuccess(response, "Media request");
311336
- 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();
311337
311360
  }
311338
311361
  async request(path, init, options) {
311339
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.75",
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": {