blun-king-cli 9.1.611 → 9.1.613

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # BLUN King Changelog
2
2
 
3
+ ## 9.1.613
4
+
5
+ - King schätzt vor dem Senden nun die Größe der gesamten Modellanfrage, einschließlich Anweisungen, Verlauf, Werkzeugdefinitionen und Bilddaten. Nähert sie sich der Byte-Grenze des Servers, beginnt eine stufenweise Verdichtung. Lässt sich eine Anfrage nicht sicher aufteilen, bricht King ab, ohne den gespeicherten Verlauf stillschweigend zu verwerfen.
6
+ - Die Nutzungsanzeige übernimmt gesondert gemeldete Denktoken, sofern der Anbieter sie liefert. Eine kürzere Modellwartezeit oder geringere Kosten sind damit nicht belegt.
7
+
8
+ ## 9.1.612 (Nicht veröffentlicht)
9
+
10
+ 9.1.612 wurde nicht als Kundenupdate freigegeben. Der frühe Byte-Schutz griff bei einer Anfrage über 900 KB nicht, wenn der Verlauf allein unter 600 KB blieb. 9.1.613 ersetzt diesen Stand.
11
+
12
+ - King speichert abgeschlossene Werkzeugaufrufe lokal als Metadaten: Werkzeugname, Zeitpunkt, Status und bei Dateiwerkzeugen gegebenenfalls den Pfad. Eingaben, Ausgaben, Befehle und Schlüssel werden nicht gespeichert. Mit `/memory tools` kannst du die Einträge deines Profils durchsuchen.
13
+ - Die Kontextdiagnose zeigt zusätzliche Zahlen zur Zusammensetzung von Anfragen. Eine zuschaltbare Phasenmessung erfasst die Zeit bis zum Modellaufruf, ohne Inhalte zu protokollieren. Diese Diagnose beschleunigt Modellantworten für sich genommen nicht.
14
+
3
15
  ## 9.1.611
4
16
 
5
17
  - Große Verdichtungsanfragen teilt King jetzt nach Anfragegröße in chronologische Schritte auf. Passt eine einzelne Nachricht nicht unter die Byte-Grenze, bleibt der gespeicherte Verlauf unverändert. So überschreitet die Verdichtungsanfrage die Servergrenze nicht mehr, obwohl im Tokenfenster noch Platz ist. Eine kürzere Modellwartezeit oder geringerer Tokenverbrauch ist damit nicht belegt.
package/LIESMICH.txt CHANGED
@@ -26,7 +26,7 @@ Installation
26
26
  ------------
27
27
  Die geprüfte Version exakt global installieren:
28
28
 
29
- npm install -g blun-king-cli@9.1.611
29
+ npm install -g blun-king-cli@9.1.613
30
30
 
31
31
  Start
32
32
  -----
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.611
12
+ npm install -g blun-king-cli@9.1.613
13
13
  ```
14
14
 
15
15
  ### Windows: Node.js, npm und Git Bash
@@ -431,6 +431,10 @@ async function runHookCore(input, payload, options) {
431
431
  }
432
432
  if (payload) return { blocked: false, context, briefing, preflight, signal, attentionEvent, channelEvent, lessonRecall, catalogPath };
433
433
  const output = hookOutput(event, context, process.env, sourceWarning);
434
+ if (preflight?.instructionNotice) {
435
+ output.hookSpecificOutput.message = [output.hookSpecificOutput.message, preflight.instructionNotice]
436
+ .filter(Boolean).join("\n");
437
+ }
434
438
  const handoff = await writeContextOutput(output, process.env.BLUN_PLUGIN_ROOT ? "king"
435
439
  : process.env.PLUGIN_ROOT ? "codex" : process.env.CLAUDE_PLUGIN_ROOT ? "claude" : scope.host);
436
440
  if (event === "UserPromptSubmit" && briefingOrigin) {
@@ -1,13 +1,14 @@
1
1
  import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
2
2
  import { constants as fsConstants } from "node:fs";
3
3
  import { spawn } from "node:child_process";
4
+ import { StringDecoder } from "node:string_decoder";
4
5
  import { lstat, mkdir, open, readFile, realpath, stat, unlink, writeFile } from "node:fs/promises";
5
6
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
6
7
  import { isFileLockContention, replaceFileWithRetry } from "./filesystem-retry.js";
7
8
  import { ancestorsBetween, isInside, stateRoot } from "./paths.js";
8
9
  import { preflightDeliveryId } from "./preflight-delivery-id.js";
9
10
  import { resolveHostSourceCatalog } from "./source-roots.js";
10
- import { instructionBudget } from "./host-instruction-budget.js";
11
+ import { instructionBudget, isKingHost } from "./host-instruction-budget.js";
11
12
 
12
13
  export const PREFLIGHT_SCHEMA = "agentspine.preflight/v2";
13
14
  export const PREFLIGHT_POLICY_SCHEMA = "agentspine.preflight-policy/v1";
@@ -326,7 +327,7 @@ async function assertNoSymlinkParents(path, allowedRoot) {
326
327
  }
327
328
  return { root, target };
328
329
  }
329
- async function safeReadRequired(path, allowedRoot, maximum = STANDARD_REQUIRED_INSTRUCTIONS_BYTES, fileHooks = null) {
330
+ async function safeReadRequired(path, allowedRoot, maximum = STANDARD_REQUIRED_INSTRUCTIONS_BYTES, fileHooks = null, contentLimit = null) {
330
331
  const checked = await assertNoSymlinkParents(path, allowedRoot);
331
332
  const flags = fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW || 0);
332
333
  const handle = await open(checked.target, flags);
@@ -353,7 +354,10 @@ async function safeReadRequired(path, allowedRoot, maximum = STANDARD_REQUIRED_I
353
354
  || current.size !== before.size || current.mtimeMs !== before.mtimeMs || current.ctimeMs !== before.ctimeMs) {
354
355
  throw new Error(`required instruction ${basename(path)} was replaced during preflight`);
355
356
  }
356
- return { content: buffer.toString("utf8"), bytes: buffer.length, sha256: sha256(buffer),
357
+ const decoder = new StringDecoder("utf8");
358
+ const content = contentLimit === null ? buffer.toString("utf8") : decoder.write(buffer.subarray(0, contentLimit));
359
+ const readBytes = Buffer.byteLength(content);
360
+ return { content, bytes: buffer.length, readBytes, omittedBytes: buffer.length - readBytes, sha256: sha256(buffer),
357
361
  identity: sha256(`${before.dev}\0${before.ino}\0${before.size}\0${before.mtimeMs}\0${before.ctimeMs}`) };
358
362
  } finally { await handle.close(); }
359
363
  }
@@ -492,16 +496,18 @@ export async function runPreflight({ input, scope, resolvedSources, prompt, now
492
496
  const requiredInstructions = [];
493
497
  let instructionBytes = 0;
494
498
  const maximumInstructionBytes = instructionBudget(instructionHost, 0, env).hardLimitBytes;
499
+ const kingInstructions = instructionHost === "codex" && isKingHost(env);
495
500
  for (const document of documents) {
496
501
  const allowedRoot = document.sourceScope === "user" ? resolvedSources.hostHome : resolvedSources.projectRoot;
497
- const snapshot = await safeReadRequired(document.path, allowedRoot, maximumInstructionBytes, fileHooks);
502
+ const snapshot = await safeReadRequired(document.path, allowedRoot, maximumInstructionBytes, fileHooks, kingInstructions ? 32768 : null);
498
503
  if (snapshot.sha256 !== document.sha256 || snapshot.bytes !== document.bytes) throw new Error(`required instruction changed after source resolution: ${document.relativePath}`);
499
- instructionBytes += snapshot.bytes;
504
+ instructionBytes += kingInstructions ? snapshot.readBytes : snapshot.bytes;
500
505
  if (instructionBytes > maximumInstructionBytes) {
501
506
  throw new Error(`required host instructions total ${instructionBytes} bytes; mandatory limit is ${maximumInstructionBytes} bytes`);
502
507
  }
503
508
  requiredInstructions.push({ path: document.path, displayPath: document.relativePath, scope: document.sourceScope,
504
- bytes: snapshot.bytes, sha256: snapshot.sha256, identity: snapshot.identity, content: snapshot.content });
509
+ bytes: snapshot.bytes, readBytes: snapshot.readBytes, omittedBytes: snapshot.omittedBytes,
510
+ sha256: snapshot.sha256, identity: snapshot.identity, content: snapshot.content });
505
511
  }
506
512
  const appliedInstructionBudget = instructionBudget(instructionHost, instructionBytes, env);
507
513
  const memoryState = validateMemories(await readJson(paths.memories, MAX_STATE_BYTES, emptyMemories));
@@ -548,6 +554,7 @@ export async function runPreflight({ input, scope, resolvedSources, prompt, now
548
554
  const key = await signingKey(paths); const bodyDigest = digestObject(receiptBody);
549
555
  const receipt = { id: `preflight:${bodyDigest.slice(0, 32)}`, ...receiptBody, bodyDigest,
550
556
  signature: createHmac("sha256", key).update(bodyDigest).digest("hex") };
557
+ let instructionNotice = null;
551
558
  await withLock(paths, async () => {
552
559
  const state = await readVerifiedState(paths);
553
560
  state.receipts = state.receipts.filter((item) => new Date(item.receipt?.expiresAt || 0).getTime() >= new Date(createdAt).getTime() - RECEIPT_TTL_MS);
@@ -557,6 +564,16 @@ export async function runPreflight({ input, scope, resolvedSources, prompt, now
557
564
  const envelope = { receipt, consumedAt: null, invalidatedAt: null, stateSignature: "" };
558
565
  envelope.stateSignature = createHmac("sha256", key).update(envelopeBody(envelope)).digest("hex");
559
566
  state.receipts.push(envelope); state.revision += 1;
567
+ if (kingInstructions) {
568
+ const notices = [];
569
+ for (const item of requiredInstructions.filter((instruction) => instruction.bytes > 8192)) {
570
+ const sessionDigest = sha256(`${exactScope.sessionId}\0${item.path}`);
571
+ if (state.history.some((event) => event.event === "instruction-notice" && event.sessionDigest === sessionDigest)) continue;
572
+ state.history.push({ event: "instruction-notice", sessionDigest, at: createdAt, authority: "diagnostic-only" });
573
+ notices.push(`${item.displayPath}: ${item.bytes} bytes; ${item.readBytes} read; ${item.omittedBytes} omitted.`);
574
+ }
575
+ instructionNotice = notices.join("\n") || null;
576
+ }
560
577
  state.lastTurn = { status: "ready", receiptId: receipt.id, at: createdAt, host: exactScope.host,
561
578
  instructionHost, instructionFiles: receipt.instructionFiles.length, recall: receipt.providerQueries.map((item) => ({
562
579
  providerId: item.providerId, status: item.status, rejected: item.rejected
@@ -564,7 +581,7 @@ export async function runPreflight({ input, scope, resolvedSources, prompt, now
564
581
  state.history.push({ event: "ready", receiptId: receipt.id, at: createdAt, authority: "preflight-proof-only" });
565
582
  await writeJson(paths.state, state);
566
583
  });
567
- return { receipt, briefing, policy: { configured: Boolean(profile), requiredProviderIds } };
584
+ return { receipt, briefing, instructionNotice, policy: { configured: Boolean(profile), requiredProviderIds } };
568
585
  }
569
586
 
570
587
  export async function verifyPreflightReceipt({ receipt, input, scope, resolvedSources, prompt, now = new Date(), env = process.env, consume = false }) {
@@ -607,12 +624,13 @@ export async function verifyPreflightReceipt({ receipt, input, scope, resolvedSo
607
624
  || currentDocuments.some((document, index) => document.path !== receipt.instructionFiles[index]?.path)) return false;
608
625
  let instructionBytes = 0;
609
626
  const maximumInstructionBytes = instructionBudget(receipt.instructionHost, 0, env).hardLimitBytes;
627
+ const kingInstructions = receipt.instructionHost === "codex" && isKingHost(env);
610
628
  for (const instruction of receipt.instructionFiles) {
611
629
  const allowedRoot = instruction.scope === "user" ? freshSources.hostHome : freshSources.projectRoot;
612
- const currentSnapshot = await safeReadRequired(instruction.path, allowedRoot, maximumInstructionBytes);
630
+ const currentSnapshot = await safeReadRequired(instruction.path, allowedRoot, maximumInstructionBytes, null, kingInstructions ? 32768 : null);
613
631
  if (currentSnapshot.sha256 !== instruction.sha256 || currentSnapshot.bytes !== instruction.bytes
614
632
  || currentSnapshot.identity !== instruction.identity) return false;
615
- instructionBytes += currentSnapshot.bytes;
633
+ instructionBytes += kingInstructions ? currentSnapshot.readBytes : currentSnapshot.bytes;
616
634
  }
617
635
  if (instructionBytes > maximumInstructionBytes
618
636
  || canonical(receipt.instructionBudget) !== canonical(instructionBudget(receipt.instructionHost, instructionBytes, env))) return false;