blun-king-cli 9.1.612 → 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 +8 -1
- package/LIESMICH.txt +1 -1
- package/README.md +1 -1
- package/agent-spine-plugin/src/hook.js +4 -0
- package/agent-spine-plugin/src/lib/preflight.js +27 -9
- package/blun.mjs +1547 -330
- package/bundled-agent-sources.json +2 -2
- package/package.json +1 -1
- package/worker-host.mjs +194 -25
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# BLUN King Changelog
|
|
2
2
|
|
|
3
|
-
## 9.1.
|
|
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.
|
|
4
11
|
|
|
5
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.
|
|
6
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.
|
package/LIESMICH.txt
CHANGED
package/README.md
CHANGED
|
@@ -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
|
-
|
|
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,
|
|
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;
|