blun-king-cli 9.1.612 → 9.1.614

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,6 +1,17 @@
1
1
  # BLUN King Changelog
2
2
 
3
- ## 9.1.612
3
+ ## 9.1.614
4
+
5
+ - Zu große AGENTS.md- und AGENTS.override.md-Dateien stoppen einen Zug nicht mehr allein wegen ihrer Größe. Außerhalb der King-Konsole liest King insgesamt bis zu 32 KiB aus Anweisungsdateien und nennt dir einmal je Sitzung die Anzahl der ausgelassenen Bytes. In der King-Konsole bleiben die Dateien vollständig, solange die Anfragegrenze das zulässt. Verknüpfungen und unsichere Pfade bleiben gesperrt.
6
+
7
+ ## 9.1.613
8
+
9
+ - 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.
10
+ - Die Nutzungsanzeige übernimmt gesondert gemeldete Denktoken, sofern der Anbieter sie liefert. Eine kürzere Modellwartezeit oder geringere Kosten sind damit nicht belegt.
11
+
12
+ ## 9.1.612 (Nicht veröffentlicht)
13
+
14
+ 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.
4
15
 
5
16
  - 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.
6
17
  - 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.
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.612
29
+ npm install -g blun-king-cli@9.1.614
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.612
12
+ npm install -g blun-king-cli@9.1.614
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) {
@@ -86,7 +86,7 @@ export async function indexExplicitDocuments(entries) {
86
86
  if (!metadata.isFile() || metadata.isSymbolicLink()) throw new Error(`source is not a regular non-symlink file: ${entry.id}`);
87
87
  buffer = await readFile(path);
88
88
  }
89
- if (buffer.byteLength > (entry.maxBytes || 4 * 1024 * 1024)) throw new Error(`source exceeds its byte limit: ${entry.id}`);
89
+ if (buffer.byteLength > (entry.maxBytes || 8 * 1024 * 1024)) throw new Error(`source exceeds its byte limit: ${entry.id}`);
90
90
  const classification = classify(entry.id);
91
91
  const document = {
92
92
  path,
@@ -44,6 +44,7 @@ return null;
44
44
 
45
45
  export function hostContextLimit(preflight) {
46
46
  if (preflight?.receipt?.instructionHost === "codex") return 32768 + Buffer.byteLength(JSON.stringify(preflight.briefing || {}));
47
+ if (preflight?.briefing?.instructions?.length) return 32768 + Buffer.byteLength(JSON.stringify(preflight.briefing));
47
48
  const mode = preflight?.receipt?.instructionBudget?.mode;
48
49
  if (mode === "claude-required-overflow") return MAX_CLAUDE_OVERFLOW_CONTEXT_BYTES;
49
50
  return STANDARD_HOST_CONTEXT_BYTES;
@@ -4,8 +4,7 @@ const KING_TOTAL = 8388608;
4
4
  export const isKingHost = (env = process.env) => !!(env.BLUN_PLUGIN_ROOT && env.BLUN_HOME);
5
5
 
6
6
  export function instructionBudget(host, usedBytes = 0, env = process.env) {
7
- const hardLimitBytes = host === "codex" && isKingHost(env) ? KING_TOTAL
8
- : host === "claude" ? 16384 : host === "codex" ? 32768 : STANDARD;
7
+ const hardLimitBytes = KING_TOTAL;
9
8
  return { mode: usedBytes > STANDARD ? `${host}-required-overflow` : "standard",
10
9
  standardBytes: STANDARD, hardLimitBytes, usedBytes, overflowBytes: Math.max(0, usedBytes - STANDARD) };
11
10
  }
@@ -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,19 @@ 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);
500
+ const contentBudget = kingInstructions ? 900000 : 32768;
495
501
  for (const document of documents) {
496
502
  const allowedRoot = document.sourceScope === "user" ? resolvedSources.hostHome : resolvedSources.projectRoot;
497
- const snapshot = await safeReadRequired(document.path, allowedRoot, maximumInstructionBytes, fileHooks);
503
+ const snapshot = await safeReadRequired(document.path, allowedRoot, maximumInstructionBytes, fileHooks, Math.max(0, contentBudget - instructionBytes));
498
504
  if (snapshot.sha256 !== document.sha256 || snapshot.bytes !== document.bytes) throw new Error(`required instruction changed after source resolution: ${document.relativePath}`);
499
- instructionBytes += snapshot.bytes;
505
+ instructionBytes += snapshot.readBytes;
500
506
  if (instructionBytes > maximumInstructionBytes) {
501
507
  throw new Error(`required host instructions total ${instructionBytes} bytes; mandatory limit is ${maximumInstructionBytes} bytes`);
502
508
  }
503
509
  requiredInstructions.push({ path: document.path, displayPath: document.relativePath, scope: document.sourceScope,
504
- bytes: snapshot.bytes, sha256: snapshot.sha256, identity: snapshot.identity, content: snapshot.content });
510
+ bytes: snapshot.bytes, readBytes: snapshot.readBytes, omittedBytes: snapshot.omittedBytes,
511
+ sha256: snapshot.sha256, identity: snapshot.identity, content: snapshot.content });
505
512
  }
506
513
  const appliedInstructionBudget = instructionBudget(instructionHost, instructionBytes, env);
507
514
  const memoryState = validateMemories(await readJson(paths.memories, MAX_STATE_BYTES, emptyMemories));
@@ -548,6 +555,7 @@ export async function runPreflight({ input, scope, resolvedSources, prompt, now
548
555
  const key = await signingKey(paths); const bodyDigest = digestObject(receiptBody);
549
556
  const receipt = { id: `preflight:${bodyDigest.slice(0, 32)}`, ...receiptBody, bodyDigest,
550
557
  signature: createHmac("sha256", key).update(bodyDigest).digest("hex") };
558
+ let instructionNotice = null;
551
559
  await withLock(paths, async () => {
552
560
  const state = await readVerifiedState(paths);
553
561
  state.receipts = state.receipts.filter((item) => new Date(item.receipt?.expiresAt || 0).getTime() >= new Date(createdAt).getTime() - RECEIPT_TTL_MS);
@@ -557,6 +565,25 @@ export async function runPreflight({ input, scope, resolvedSources, prompt, now
557
565
  const envelope = { receipt, consumedAt: null, invalidatedAt: null, stateSignature: "" };
558
566
  envelope.stateSignature = createHmac("sha256", key).update(envelopeBody(envelope)).digest("hex");
559
567
  state.receipts.push(envelope); state.revision += 1;
568
+ if (requiredInstructions.length > 0) {
569
+ const notices = [];
570
+ for (const item of requiredInstructions.filter((instruction) => instruction.bytes > 8192)) {
571
+ const sessionDigest = sha256(`${exactScope.sessionId}\0${item.path}`);
572
+ if (state.history.some((event) => event.event === "instruction-notice" && event.sessionDigest === sessionDigest)) continue;
573
+ state.history.push({ event: "instruction-notice", sessionDigest, at: createdAt, authority: "diagnostic-only" });
574
+ notices.push(`${item.displayPath}: ${item.bytes} bytes; ${item.readBytes} read; ${item.omittedBytes} omitted.`);
575
+ }
576
+ const totalBytes = requiredInstructions.reduce((sum, item) => sum + item.bytes, 0);
577
+ const omittedBytes = totalBytes - instructionBytes;
578
+ if (omittedBytes > 0) {
579
+ const totalDigest = sha256(`${exactScope.sessionId}\0instruction-total`);
580
+ if (!state.history.some((event) => event.event === "instruction-notice" && event.sessionDigest === totalDigest)) {
581
+ state.history.push({ event: "instruction-notice", sessionDigest: totalDigest, at: createdAt, authority: "diagnostic-only" });
582
+ notices.push(`Instruction total: ${totalBytes} bytes; ${instructionBytes} read; ${omittedBytes} omitted.`);
583
+ }
584
+ }
585
+ instructionNotice = notices.join("\n") || null;
586
+ }
560
587
  state.lastTurn = { status: "ready", receiptId: receipt.id, at: createdAt, host: exactScope.host,
561
588
  instructionHost, instructionFiles: receipt.instructionFiles.length, recall: receipt.providerQueries.map((item) => ({
562
589
  providerId: item.providerId, status: item.status, rejected: item.rejected
@@ -564,7 +591,7 @@ export async function runPreflight({ input, scope, resolvedSources, prompt, now
564
591
  state.history.push({ event: "ready", receiptId: receipt.id, at: createdAt, authority: "preflight-proof-only" });
565
592
  await writeJson(paths.state, state);
566
593
  });
567
- return { receipt, briefing, policy: { configured: Boolean(profile), requiredProviderIds } };
594
+ return { receipt, briefing, instructionNotice, policy: { configured: Boolean(profile), requiredProviderIds } };
568
595
  }
569
596
 
570
597
  export async function verifyPreflightReceipt({ receipt, input, scope, resolvedSources, prompt, now = new Date(), env = process.env, consume = false }) {
@@ -607,12 +634,14 @@ export async function verifyPreflightReceipt({ receipt, input, scope, resolvedSo
607
634
  || currentDocuments.some((document, index) => document.path !== receipt.instructionFiles[index]?.path)) return false;
608
635
  let instructionBytes = 0;
609
636
  const maximumInstructionBytes = instructionBudget(receipt.instructionHost, 0, env).hardLimitBytes;
637
+ const kingInstructions = receipt.instructionHost === "codex" && isKingHost(env);
638
+ const contentBudget = kingInstructions ? 900000 : 32768;
610
639
  for (const instruction of receipt.instructionFiles) {
611
640
  const allowedRoot = instruction.scope === "user" ? freshSources.hostHome : freshSources.projectRoot;
612
- const currentSnapshot = await safeReadRequired(instruction.path, allowedRoot, maximumInstructionBytes);
641
+ const currentSnapshot = await safeReadRequired(instruction.path, allowedRoot, maximumInstructionBytes, null, Math.max(0, contentBudget - instructionBytes));
613
642
  if (currentSnapshot.sha256 !== instruction.sha256 || currentSnapshot.bytes !== instruction.bytes
614
643
  || currentSnapshot.identity !== instruction.identity) return false;
615
- instructionBytes += currentSnapshot.bytes;
644
+ instructionBytes += currentSnapshot.readBytes;
616
645
  }
617
646
  if (instructionBytes > maximumInstructionBytes
618
647
  || canonical(receipt.instructionBudget) !== canonical(instructionBudget(receipt.instructionHost, instructionBytes, env))) return false;
@@ -376,7 +376,7 @@ export async function resolveHostSourceCatalog({ host, cwd = process.cwd(), inpu
376
376
  const codexHome = isKingHost(env) ? env.BLUN_HOME : env.CODEX_HOME || env.BLUN_HOME || join(homedir(), ".codex");
377
377
  hostHome = await existingDirectory(resolve(codexHome)) || resolve(codexHome);
378
378
  const config = await codexConfig(hostHome);
379
- if (isKingHost(env)) config.maxBytes = KING_SOURCE_BYTES;
379
+ config.maxBytes = KING_SOURCE_BYTES;
380
380
  if (env.AGENTSPINE_ROOT) projectRoot = await canonicalPath(env.AGENTSPINE_ROOT);
381
381
  else ({ root: projectRoot, resolution: rootResolution } = await findRoot(canonicalCwd, config.rootMarkers));
382
382
  sources = await codexSources({ cwd: canonicalCwd, projectRoot, codexHome: hostHome, config });