blun-king-cli 9.1.49 → 9.1.50

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
@@ -9,7 +9,7 @@ Installation
9
9
  ------------
10
10
  Die geprüfte Version exakt global installieren:
11
11
 
12
- npm install -g blun-king-cli@9.1.49
12
+ npm install -g blun-king-cli@9.1.50
13
13
 
14
14
  Start
15
15
  -----
package/README.md CHANGED
@@ -9,7 +9,7 @@ Voraussetzung ist Node.js 24.15 oder neuer. Die geprüfte Version wird exakt
9
9
  installiert:
10
10
 
11
11
  ```powershell
12
- npm install -g blun-king-cli@9.1.49
12
+ npm install -g blun-king-cli@9.1.50
13
13
  ```
14
14
 
15
15
  ## Reproduzierbares Staging und Packen
package/blun.mjs CHANGED
@@ -30172,7 +30172,8 @@ function parseEnvBudget(raw) {
30172
30172
  */
30173
30173
  function computeCompletionBudgetCap(args) {
30174
30174
  const maxCtx = args.capability?.max_context_tokens ?? 0;
30175
- const cap = args.budget.hardCap ?? (maxCtx > 0 ? maxCtx : args.budget.fallback ?? DEFAULT_UNKNOWN_CONTEXT_FALLBACK);
30175
+ const fallback = args.budget.fallback ?? DEFAULT_UNKNOWN_CONTEXT_FALLBACK;
30176
+ const cap = args.budget.hardCap ?? (maxCtx > 0 ? Math.min(maxCtx, fallback) : fallback);
30176
30177
  return Math.max(MIN_FLOOR, cap);
30177
30178
  }
30178
30179
  /**
@@ -30197,7 +30198,7 @@ function applyCompletionBudgetWithDetails(args) {
30197
30198
  });
30198
30199
  if (args.retry !== void 0) cap = Math.max(args.retry.minimumCompletionTokens, Math.ceil(cap * args.retry.multiplier));
30199
30200
  const maxContextTokens = args.capability?.max_context_tokens;
30200
- if (args.usedContextTokens !== void 0 && maxContextTokens !== void 0 && maxContextTokens > 0) cap = Math.max(MIN_FLOOR, Math.min(cap, maxContextTokens - args.usedContextTokens));
30201
+ if (args.usedContextTokens !== void 0 && maxContextTokens !== void 0 && maxContextTokens > 0) cap = Math.max(MIN_FLOOR, Math.min(cap, maxContextTokens - args.usedContextTokens - COMPLETION_CONTEXT_SAFETY_MARGIN));
30201
30202
  return {
30202
30203
  provider: args.provider.withMaxCompletionTokens(cap, {
30203
30204
  usedContextTokens: args.usedContextTokens,
@@ -30206,11 +30207,12 @@ function applyCompletionBudgetWithDetails(args) {
30206
30207
  maxCompletionTokens: cap
30207
30208
  };
30208
30209
  }
30209
- var MIN_FLOOR, DEFAULT_UNKNOWN_CONTEXT_FALLBACK, MIN_THINKING_COMPLETION_TOKENS;
30210
+ var MIN_FLOOR, DEFAULT_UNKNOWN_CONTEXT_FALLBACK, MIN_THINKING_COMPLETION_TOKENS, COMPLETION_CONTEXT_SAFETY_MARGIN;
30210
30211
  var init_completion_budget = __esmMin((() => {
30211
30212
  MIN_FLOOR = 1;
30212
30213
  DEFAULT_UNKNOWN_CONTEXT_FALLBACK = 32e3;
30213
30214
  MIN_THINKING_COMPLETION_TOKENS = 1024;
30215
+ COMPLETION_CONTEXT_SAFETY_MARGIN = 1e4;
30214
30216
  }));
30215
30217
  //#endregion
30216
30218
  //#region ../../packages/agent-core/src/loop/retry.ts
@@ -262017,8 +262019,9 @@ var init_blun_media$1 = __esmMin((() => {
262017
262019
  GenerateImageInputSchema = object({ prompt: PromptSchema.describe("A complete visual description of the image to generate.") });
262018
262020
  GenerateVideoInputSchema = object({
262019
262021
  prompt: PromptSchema.describe("A complete visual description of the video or motion to generate."),
262020
- image_id: MediaIdSchema.optional().describe("An optional completed PNG or JPEG media job id to animate into the video.")
262021
- });
262022
+ image_id: MediaIdSchema.optional().describe("An optional completed PNG or JPEG media job id to animate into the video."),
262023
+ path: string().trim().min(1).optional().describe("An optional path to a local PNG or JPEG file to upload and animate. Relative paths resolve against the working directory.")
262024
+ }).refine((args) => !(args.image_id && args.path), { message: "Provide either image_id or path, not both." });
262022
262025
  GenerateSpeechInputSchema = object({ input: PromptSchema.describe("The exact text to synthesize as speech.") });
262023
262026
  GetMediaInputSchema = object({ id: MediaIdSchema.describe("The media job id returned by a generation tool.") });
262024
262027
  UnderstandImageInputSchema = object({
@@ -262092,11 +262095,79 @@ var init_blun_media$1 = __esmMin((() => {
262092
262095
  }
262093
262096
  };
262094
262097
  GenerateVideoTool = class extends MediaGenerationTool {
262098
+ kaos;
262099
+ workspace;
262095
262100
  name = "GenerateVideo";
262096
- description = "Generate a new video from text or animate an existing completed image job with BLUN media models. Pass image_id for image-to-video requests. Do not use this for understanding an existing video. This starts a billed asynchronous media job.";
262101
+ description = "Generate a new video from text, animate an existing completed image job, or animate a local PNG/JPEG file with BLUN media models. Pass image_id for an existing media job or path for a local or Telegram-provided image. Do not use this for understanding an existing video. This starts a billed asynchronous media job.";
262097
262102
  parameters = toInputJsonSchema(GenerateVideoInputSchema);
262103
+ constructor(provider, kaos, workspace) {
262104
+ super(provider);
262105
+ this.kaos = kaos;
262106
+ this.workspace = workspace;
262107
+ }
262098
262108
  subject(args) {
262099
- return args.image_id ? `${args.image_id}: ${args.prompt}` : args.prompt;
262109
+ return args.path ? `${args.path}: ${args.prompt}` : args.image_id ? `${args.image_id}: ${args.prompt}` : args.prompt;
262110
+ }
262111
+ resolveExecution(args) {
262112
+ if (!args.path) return super.resolveExecution(args);
262113
+ const path = resolvePathAccessPath(args.path, {
262114
+ kaos: this.kaos,
262115
+ workspace: this.workspace,
262116
+ operation: "read"
262117
+ });
262118
+ return {
262119
+ accesses: ToolAccesses.readFile(path),
262120
+ description: `${this.name}: ${args.path}`,
262121
+ display: {
262122
+ kind: "file_io",
262123
+ operation: "read",
262124
+ path
262125
+ },
262126
+ approvalRule: literalRulePattern(this.name, path),
262127
+ matchesRule: (ruleArgs) => matchesPathRuleSubject(ruleArgs, path, {
262128
+ cwd: this.workspace.workspaceDir,
262129
+ pathClass: this.kaos.pathClass(),
262130
+ homeDir: this.kaos.gethome()
262131
+ }),
262132
+ execute: (ctx) => this.executionFromPath(args, path, ctx)
262133
+ };
262134
+ }
262135
+ async executionFromPath(args, safePath, ctx) {
262136
+ emitMediaProgress(ctx, this.name, "uploading");
262137
+ try {
262138
+ const fileType = detectFileType(safePath, await this.kaos.readBytes(safePath, 512), "media");
262139
+ if (fileType.mimeType !== "image/png" && fileType.mimeType !== "image/jpeg") return {
262140
+ isError: true,
262141
+ output: "Image-to-video accepts only PNG or JPEG files."
262142
+ };
262143
+ const data = await this.kaos.readBytes(safePath);
262144
+ if (data.byteLength === 0) return {
262145
+ isError: true,
262146
+ output: `"${args.path}" is empty.`
262147
+ };
262148
+ const options = {
262149
+ signal: ctx.signal,
262150
+ toolCallId: ctx.toolCallId
262151
+ };
262152
+ const upload = await this.provider.uploadMedia(data, fileType.mimeType, options);
262153
+ emitMediaProgress(ctx, this.name, "submitting", { sourceId: upload.id });
262154
+ const job = await this.provider.generateVideo(args.prompt, options, upload.id);
262155
+ emitMediaProgress(ctx, this.name, job.status, {
262156
+ id: job.id,
262157
+ sourceId: upload.id,
262158
+ ...job.progress
262159
+ });
262160
+ return {
262161
+ output: `Media job ${job.id} accepted with status ${job.status}. ${this.requestNote}`,
262162
+ isError: false
262163
+ };
262164
+ } catch (error) {
262165
+ emitMediaProgress(ctx, this.name, "failed", { error: errorMessage$10(error) });
262166
+ return {
262167
+ isError: true,
262168
+ output: `Media request failed: ${errorMessage$10(error)}`
262169
+ };
262170
+ }
262100
262171
  }
262101
262172
  submit(args, options) {
262102
262173
  return this.provider.generateVideo(args.prompt, options, args.image_id);
@@ -262764,7 +262835,7 @@ var init_tool$1 = __esmMin((() => {
262764
262835
  toolServices?.webSearcher && new WebSearchTool(toolServices.webSearcher),
262765
262836
  toolServices?.urlFetcher && new FetchURLTool(toolServices.urlFetcher),
262766
262837
  toolServices?.media && new GenerateImageTool(toolServices.media),
262767
- toolServices?.media && new GenerateVideoTool(toolServices.media),
262838
+ toolServices?.media && new GenerateVideoTool(toolServices.media, kaos, workspace),
262768
262839
  toolServices?.media && new GenerateSpeechTool(toolServices.media),
262769
262840
  toolServices?.media && new UnderstandImageTool(toolServices.media, kaos, workspace),
262770
262841
  toolServices?.media && new UnderstandVideoTool(toolServices.media),
@@ -410038,6 +410109,16 @@ function trimPartialClosingFences(tokens) {
410038
410109
  if (!marker || !lastLine || lastLine.length >= marker.length || lastLine !== marker[0]?.repeat(lastLine.length)) return;
410039
410110
  token.text = token.text.slice(0, -lastLine.length).replace(/\n$/, "");
410040
410111
  }
410112
+ function terminalHyperlinkTarget(value) {
410113
+ const candidate = value.trim();
410114
+ if (/^https?:\/\//iu.test(candidate) || /^file:\/\//iu.test(candidate)) try {
410115
+ const parsed = new URL(candidate);
410116
+ if (["http:", "https:", "file:"].includes(parsed.protocol)) return parsed.href;
410117
+ } catch {
410118
+ return;
410119
+ }
410120
+ if (/^[A-Za-z]:[\\/]/u.test(candidate)) return pathToFileURL(candidate).href;
410121
+ }
410041
410122
  const markdownParser = new q();
410042
410123
  markdownParser.setOptions({ tokenizer: new StrictStrikethroughTokenizer() });
410043
410124
  var Markdown = class {
@@ -410291,7 +410372,11 @@ var Markdown = class {
410291
410372
  break;
410292
410373
  }
410293
410374
  case "codespan":
410294
- result += this.theme.code(token.text) + stylePrefix;
410375
+ {
410376
+ const styledCode = this.theme.code(token.text);
410377
+ const linkTarget = terminalHyperlinkTarget(token.text);
410378
+ result += (linkTarget !== void 0 && getCapabilities().hyperlinks ? hyperlink(styledCode, linkTarget) : styledCode) + stylePrefix;
410379
+ }
410295
410380
  break;
410296
410381
  case "link": {
410297
410382
  const linkText = this.renderInlineTokens(token.tokens || [], resolvedStyleContext);
@@ -498182,11 +498267,18 @@ function mediaActivityPhaseSequence(mediaKind) {
498182
498267
  }
498183
498268
  }
498184
498269
  function mediaActivityChainStates(job) {
498185
- const current = mediaActivityCanonicalPhase(job.phaseLabel ?? job.phase);
498186
- const history = new Set((job.phaseHistory ?? []).map((phase) => mediaActivityCanonicalPhase(phase)));
498187
- return mediaActivityPhaseSequence(job.mediaKind).map((phase) => ({
498270
+ const sequence = mediaActivityPhaseSequence(job.mediaKind);
498271
+ const normalize = (value) => {
498272
+ const phase = mediaActivityCanonicalPhase(value);
498273
+ if (phase === "processing" && !sequence.includes(phase)) return sequence[1] ?? phase;
498274
+ return phase;
498275
+ };
498276
+ const current = normalize(job.phaseLabel ?? job.phase);
498277
+ const currentIndex = sequence.indexOf(current);
498278
+ const history = new Set((job.phaseHistory ?? []).map(normalize));
498279
+ return sequence.map((phase, index) => ({
498188
498280
  phase,
498189
- state: phase === current ? "active" : history.has(phase) ? "done" : "pending"
498281
+ state: phase === current ? "active" : history.has(phase) || currentIndex > index ? "done" : "pending"
498190
498282
  }));
498191
498283
  }
498192
498284
  function renderMediaWaveform(samples, width) {
@@ -498340,16 +498432,19 @@ var MediaActivityComponent = class {
498340
498432
  const percent = Math.min(100, Math.max(0, job.percent));
498341
498433
  progress = includeBar ? ` · ${renderProgressBar(percent / 100, 12)} · ${String(Math.round(percent))} %` : ` · ${String(Math.round(percent))} %`;
498342
498434
  }
498343
- const line = `${currentTheme.boldFg("primary", kind)} · ${phase}${details.length === 0 ? "" : ` · ${details.join(" · ")}`}${progress}`;
498435
+ const line = `${currentTheme.boldFg("primary", "●")} ${currentTheme.boldFg("primary", kind)} · ${phase}${details.length === 0 ? "" : ` · ${details.join(" · ")}`}${progress}`;
498344
498436
  return truncateToWidth(line, Math.max(1, width), "…");
498345
498437
  }
498346
498438
  chainLine(job, width) {
498347
498439
  const chain = mediaActivityChainStates(job).map(({ phase, state }) => {
498348
498440
  const symbol = state === "done" ? "[x]" : state === "active" ? "[>]" : "[ ]";
498349
498441
  const label = mediaActivityLabel("media.phase", phase, phase);
498350
- return `${symbol} ${label}`;
498442
+ const text = `${symbol} ${label}`;
498443
+ if (state === "done") return currentTheme.fg("success", text);
498444
+ if (state === "active") return currentTheme.boldFg("primary", text);
498445
+ return currentTheme.fg("textDim", text);
498351
498446
  });
498352
- return truncateToWidth(` ${mediaUiText("media.activity.chain")} · ${chain.join(" · ")}`, Math.max(1, width), "…");
498447
+ return truncateToWidth(` ${currentTheme.fg("textDim", mediaUiText("media.activity.chain"))} · ${chain.join(currentTheme.fg("textDim", " · "))}`, Math.max(1, width), "…");
498353
498448
  }
498354
498449
  previewImage(job) {
498355
498450
  if (typeof job.previewDataBase64 !== "string" || job.previewDataBase64.length === 0 || job.previewDataBase64.length > 2 * 1024 * 1024 || !/^[A-Za-z0-9+/=\r\n]+$/u.test(job.previewDataBase64)) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.49",
3
+ "version": "9.1.50",
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": {