blun-king-cli 9.1.119 → 9.1.120
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 +2 -0
- package/README.md +2 -0
- package/bin/assistant-message-offload-policy.cjs +51 -0
- package/blun.mjs +135 -1
- package/package.json +1 -1
package/LIESMICH.txt
CHANGED
|
@@ -117,6 +117,8 @@ Ab BLUN King 9.1.118 bleibt der Sitzungszeitstempel im Systemprompt während ein
|
|
|
117
117
|
|
|
118
118
|
Ab BLUN King 9.1.119 enthält der bei jedem Modellschritt erneut gesendete Prompt nur noch eine kleine Auswahl häufig benötigter Skills. Alle übrigen registrierten Skills bleiben verfügbar und lassen sich über das Skill-Werkzeug anhand ihres Namens oder Zwecks suchen; der ausgewählte Skill wird anschließend bei Bedarf geladen. Dadurch sinkt die wiederholt übertragene Promptlast, ohne Skills zu löschen oder die vollständige Ansicht unter `/skills` einzuschränken.
|
|
119
119
|
|
|
120
|
+
Ab BLUN King 9.1.120 lagert die Konsole auch große ältere Assistentenantworten aus dem aktiven Modellkontext in private Sitzungsdateien aus. Der vollständige Gesprächsverlauf bleibt erhalten; im Modellkontext verbleiben eine begrenzte Vorschau und der Dateipfad. Die 20 neuesten Nachrichten sowie Assistentenantworten mit Werkzeugaufrufen bleiben unverändert, damit laufende Arbeit und Aufrufketten vollständig verfügbar sind.
|
|
121
|
+
|
|
120
122
|
Verliert ein delegierter Einzelagent oder ein Mitglied eines Agentenschwarms die Provider-Verbindung, erhält es eine leere Provider-Antwort, bricht sein Stream wegen Leerlaufs ab oder meldet der Server HTTP 408/500/502/503/504, setzt BLUN King denselben Agenten anhand seines dauerhaften Verlaufs fort. Bei Einzelagenten sowie bei Schwarm-Unterbrechungen außerhalb von HTTP 429 wartet die erste Fortsetzung zwei Sekunden und die zweite fünf Sekunden; danach bleibt der ursprüngliche Fehler sichtbar. HTTP 429 behält im Schwarm seine getrennte, längere Überlastungsregel. Authentifizierungs-, Zahlungs- oder Kontingent-, Richtlinien-, Kontext-, Werkzeug- und Codefehler sowie manuelle Abbrüche lösen keine automatische Fortsetzung aus. Jeder Fortsetzungsversuch verwendet die bestehende Agenten-ID, damit bereits abgeschlossene Arbeit nicht wiederholt wird.
|
|
121
123
|
|
|
122
124
|
Rein lesende Sitzungsdiagnose
|
package/README.md
CHANGED
|
@@ -123,6 +123,8 @@ Ab BLUN King 9.1.118 bleibt der Sitzungszeitstempel im Systemprompt während ein
|
|
|
123
123
|
|
|
124
124
|
Ab BLUN King 9.1.119 enthält der bei jedem Modellschritt erneut gesendete Prompt nur noch eine kleine Auswahl häufig benötigter Skills. Alle übrigen registrierten Skills bleiben verfügbar und lassen sich über das Skill-Werkzeug anhand ihres Namens oder Zwecks suchen; der ausgewählte Skill wird anschließend bei Bedarf geladen. Dadurch sinkt die wiederholt übertragene Promptlast, ohne Skills zu löschen oder die vollständige Ansicht unter `/skills` einzuschränken.
|
|
125
125
|
|
|
126
|
+
Ab BLUN King 9.1.120 lagert die Konsole auch große ältere Assistentenantworten aus dem aktiven Modellkontext in private Sitzungsdateien aus. Der vollständige Gesprächsverlauf bleibt erhalten; im Modellkontext verbleiben eine begrenzte Vorschau und der Dateipfad. Die 20 neuesten Nachrichten sowie Assistentenantworten mit Werkzeugaufrufen bleiben unverändert, damit laufende Arbeit und Aufrufketten vollständig verfügbar sind.
|
|
127
|
+
|
|
126
128
|
Ab BLUN King 9.1.99 werden auch direkt aufeinanderfolgende reine Textergebnisse als Stapel betrachtet. Enthalten sie zusammen mehr als 12.000 Zeichen, obwohl kein einzelnes Ergebnis diese Grenze überschreitet, speichert King so viele der größten geeigneten Ergebnisse wie nötig in privaten Dateien. In der Modellprojektion verbleiben lesbare Verweise. Der unveränderte Sitzungsrohverlauf bleibt vollständig erhalten. Gemischte Medienergebnisse, einzelne Ergebnisse unter 3.000 Zeichen und Stapel bis einschließlich 12.000 Zeichen bleiben unverändert. Beispiel: Bei zwei Suchergebnissen mit 7.000 und 6.000 Zeichen wird das größere Ergebnis privat gespeichert; Anfang, Ende, ausgelassene Zeichenzahl und `output_path` bleiben für den Agenten sichtbar.
|
|
127
129
|
|
|
128
130
|
Ab BLUN King 9.1.100 beendet eine begrenzte Grep-Inhaltssuche ripgrep, sobald der Versatz, die angeforderten Zeilen und eine zusätzliche Vorschauzeile vollständig vorliegen. Die Vorschauzeile belegt, ob eine weitere Seite existiert, ohne vorher bis zu 10 MB einzulesen. Unbegrenzte Suchen, Trefferzählungen und nach Änderungszeit sortierte Dateilisten laufen weiterhin vollständig durch.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ASSISTANT_MESSAGE_MAX_CHARS = 4_000;
|
|
4
|
+
const ASSISTANT_MESSAGE_KEEP_RECENT_MESSAGES = 20;
|
|
5
|
+
const ASSISTANT_MESSAGE_PREVIEW_LINES = 5;
|
|
6
|
+
const ASSISTANT_MESSAGE_PREVIEW_LINE_CHARS = 1_000;
|
|
7
|
+
const ASSISTANT_MESSAGE_OFFLOAD_MARKER = '[Assistant message offloaded]';
|
|
8
|
+
|
|
9
|
+
function shouldOffloadHistoricalAssistantMessage(options = {}) {
|
|
10
|
+
const historyIndex = Number(options.historyIndex);
|
|
11
|
+
const historyLength = Number(options.historyLength);
|
|
12
|
+
const textChars = Number(options.textChars);
|
|
13
|
+
if (
|
|
14
|
+
!Number.isSafeInteger(historyIndex)
|
|
15
|
+
|| !Number.isSafeInteger(historyLength)
|
|
16
|
+
|| !Number.isSafeInteger(textChars)
|
|
17
|
+
|| historyIndex < 0
|
|
18
|
+
|| historyLength < 0
|
|
19
|
+
|| historyIndex >= historyLength
|
|
20
|
+
) return false;
|
|
21
|
+
const cutoff = Math.max(0, historyLength - ASSISTANT_MESSAGE_KEEP_RECENT_MESSAGES);
|
|
22
|
+
return historyIndex < cutoff && textChars > ASSISTANT_MESSAGE_MAX_CHARS;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function createAssistantMessagePreview(text) {
|
|
26
|
+
const lines = String(text).split(/\r?\n/u);
|
|
27
|
+
const render = (line, index) => `${index}: ${line.slice(0, ASSISTANT_MESSAGE_PREVIEW_LINE_CHARS)}`;
|
|
28
|
+
if (lines.length <= ASSISTANT_MESSAGE_PREVIEW_LINES * 2) {
|
|
29
|
+
return lines.map((line, index) => render(line, index + 1)).join('\n');
|
|
30
|
+
}
|
|
31
|
+
const head = lines.slice(0, ASSISTANT_MESSAGE_PREVIEW_LINES)
|
|
32
|
+
.map((line, index) => render(line, index + 1));
|
|
33
|
+
const tailStart = lines.length - ASSISTANT_MESSAGE_PREVIEW_LINES;
|
|
34
|
+
const tail = lines.slice(tailStart)
|
|
35
|
+
.map((line, index) => render(line, tailStart + index + 1));
|
|
36
|
+
return [
|
|
37
|
+
...head,
|
|
38
|
+
`... [${lines.length - ASSISTANT_MESSAGE_PREVIEW_LINES * 2} lines omitted] ...`,
|
|
39
|
+
...tail,
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
module.exports = {
|
|
44
|
+
ASSISTANT_MESSAGE_KEEP_RECENT_MESSAGES,
|
|
45
|
+
ASSISTANT_MESSAGE_MAX_CHARS,
|
|
46
|
+
ASSISTANT_MESSAGE_OFFLOAD_MARKER,
|
|
47
|
+
ASSISTANT_MESSAGE_PREVIEW_LINES,
|
|
48
|
+
ASSISTANT_MESSAGE_PREVIEW_LINE_CHARS,
|
|
49
|
+
createAssistantMessagePreview,
|
|
50
|
+
shouldOffloadHistoricalAssistantMessage,
|
|
51
|
+
};
|
package/blun.mjs
CHANGED
|
@@ -78799,6 +78799,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78799
78799
|
this.agent.microCompaction.reset(this._history.length);
|
|
78800
78800
|
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78801
78801
|
this.agent.userMessageOffload.reset(this._history);
|
|
78802
|
+
this.agent.assistantMessageOffload.reset(this._history);
|
|
78802
78803
|
this.agent.emitStatusUpdated();
|
|
78803
78804
|
return removedMessages.size;
|
|
78804
78805
|
}
|
|
@@ -78814,6 +78815,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78814
78815
|
this.agent.microCompaction.reset();
|
|
78815
78816
|
this.agent.toolResultBatchOffload.clear();
|
|
78816
78817
|
this.agent.userMessageOffload.clear();
|
|
78818
|
+
this.agent.assistantMessageOffload.clear();
|
|
78817
78819
|
this.agent.injection.onContextClear();
|
|
78818
78820
|
this.agent.emitStatusUpdated();
|
|
78819
78821
|
}
|
|
@@ -78854,6 +78856,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78854
78856
|
this.agent.microCompaction.reset(this._history.length);
|
|
78855
78857
|
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78856
78858
|
this.agent.userMessageOffload.reset(this._history);
|
|
78859
|
+
this.agent.assistantMessageOffload.reset(this._history);
|
|
78857
78860
|
this.agent.emitStatusUpdated();
|
|
78858
78861
|
if (!this.agent.records.restoring && (stoppedAtBoundary || removedUserCount < count)) throw new BlunError(ErrorCodes.REQUEST_INVALID, formatUndoUnavailableMessage(count, removedUserCount, stoppedAtBoundary), { details: {
|
|
78859
78862
|
reason: "undo_limit",
|
|
@@ -78934,6 +78937,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78934
78937
|
this.agent.microCompaction.reset();
|
|
78935
78938
|
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78936
78939
|
this.agent.userMessageOffload.reset(this._history);
|
|
78940
|
+
this.agent.assistantMessageOffload.reset(this._history);
|
|
78937
78941
|
this.agent.injection.onContextCompacted();
|
|
78938
78942
|
this.agent.emitStatusUpdated();
|
|
78939
78943
|
return result;
|
|
@@ -78956,7 +78960,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78956
78960
|
}
|
|
78957
78961
|
project(messages, options) {
|
|
78958
78962
|
const anomalies = [];
|
|
78959
|
-
const result = project(this.agent.userMessageOffload.compact(this.agent.microCompaction.compact(this.agent.toolResultBatchOffload.compact(dedupeRepeatedInjections(messages)))), {
|
|
78963
|
+
const result = project(this.agent.assistantMessageOffload.compact(this.agent.userMessageOffload.compact(this.agent.microCompaction.compact(this.agent.toolResultBatchOffload.compact(dedupeRepeatedInjections(messages))))), {
|
|
78960
78964
|
...options,
|
|
78961
78965
|
onAnomaly: (anomaly) => {
|
|
78962
78966
|
anomalies.push(anomaly);
|
|
@@ -234883,6 +234887,9 @@ function restoreAgentRecord(agent, input) {
|
|
|
234883
234887
|
case "user_message_offload.apply":
|
|
234884
234888
|
agent.userMessageOffload.apply(input.replacement);
|
|
234885
234889
|
return;
|
|
234890
|
+
case "assistant_message_offload.apply":
|
|
234891
|
+
agent.assistantMessageOffload.apply(input.replacement);
|
|
234892
|
+
return;
|
|
234886
234893
|
case "plan_mode.enter":
|
|
234887
234894
|
agent.planMode.restoreEnter(input);
|
|
234888
234895
|
return;
|
|
@@ -260584,6 +260591,129 @@ var UserMessageOffload = class {
|
|
|
260584
260591
|
}
|
|
260585
260592
|
};
|
|
260586
260593
|
//#endregion
|
|
260594
|
+
//#region ../../packages/agent-core/src/agent/turn/assistant-message-offload.ts
|
|
260595
|
+
function persistableAssistantMessageText(message) {
|
|
260596
|
+
if (message?.role !== "assistant" || message.toolCalls.length > 0) return;
|
|
260597
|
+
const textParts = message.content.filter((part) => part.type === "text");
|
|
260598
|
+
if (textParts.length === 0) return;
|
|
260599
|
+
return textParts.map((part) => part.text).join("\n");
|
|
260600
|
+
}
|
|
260601
|
+
function assistantMessageOffloadHash(message, text) {
|
|
260602
|
+
return createHash("sha256").update(JSON.stringify({ text })).digest("hex");
|
|
260603
|
+
}
|
|
260604
|
+
async function saveAssistantMessage(homedir, text) {
|
|
260605
|
+
try {
|
|
260606
|
+
const dir = join$4(homedir, "conversation-history");
|
|
260607
|
+
await mkdir(dir, {
|
|
260608
|
+
recursive: true,
|
|
260609
|
+
mode: 448
|
|
260610
|
+
});
|
|
260611
|
+
const outputPath = join$4(dir, `assistant-message-${randomUUID()}.txt`);
|
|
260612
|
+
await writeFile(outputPath, text, {
|
|
260613
|
+
encoding: "utf8",
|
|
260614
|
+
flag: "wx",
|
|
260615
|
+
mode: 384
|
|
260616
|
+
});
|
|
260617
|
+
return outputPath;
|
|
260618
|
+
} catch {
|
|
260619
|
+
return;
|
|
260620
|
+
}
|
|
260621
|
+
}
|
|
260622
|
+
function renderPersistedAssistantMessage(text, outputPath) {
|
|
260623
|
+
return [
|
|
260624
|
+
ASSISTANT_MESSAGE_OFFLOAD_MARKER,
|
|
260625
|
+
"The complete original was archived to keep the active context fast and remains available on disk.",
|
|
260626
|
+
`message_size_chars: ${String(text.length)}`,
|
|
260627
|
+
`message_size_bytes: ${String(Buffer.byteLength(text, "utf8"))}`,
|
|
260628
|
+
`output_path: ${outputPath}`,
|
|
260629
|
+
"next_step: Use Read with output_path to inspect the complete message in pages.",
|
|
260630
|
+
"",
|
|
260631
|
+
"[preview: head and tail]",
|
|
260632
|
+
createAssistantMessagePreview(text)
|
|
260633
|
+
].join("\n");
|
|
260634
|
+
}
|
|
260635
|
+
var ASSISTANT_MESSAGE_MAX_CHARS, ASSISTANT_MESSAGE_OFFLOAD_MARKER, shouldOffloadHistoricalAssistantMessage, createAssistantMessagePreview;
|
|
260636
|
+
var init_assistant_message_offload = __esmMin((() => {
|
|
260637
|
+
const policy = createRequire(import.meta.url)("./bin/assistant-message-offload-policy.cjs");
|
|
260638
|
+
ASSISTANT_MESSAGE_MAX_CHARS = policy.ASSISTANT_MESSAGE_MAX_CHARS;
|
|
260639
|
+
ASSISTANT_MESSAGE_OFFLOAD_MARKER = policy.ASSISTANT_MESSAGE_OFFLOAD_MARKER;
|
|
260640
|
+
shouldOffloadHistoricalAssistantMessage = policy.shouldOffloadHistoricalAssistantMessage;
|
|
260641
|
+
createAssistantMessagePreview = policy.createAssistantMessagePreview;
|
|
260642
|
+
}));
|
|
260643
|
+
var AssistantMessageOffload = class {
|
|
260644
|
+
agent;
|
|
260645
|
+
replacements = /* @__PURE__ */ new Map();
|
|
260646
|
+
constructor(agent) {
|
|
260647
|
+
this.agent = agent;
|
|
260648
|
+
}
|
|
260649
|
+
async detect() {
|
|
260650
|
+
const history = this.agent.context.history;
|
|
260651
|
+
if (this.agent.homedir === void 0) return 0;
|
|
260652
|
+
let offloaded = 0;
|
|
260653
|
+
for (let historyIndex = 0; historyIndex < history.length; historyIndex++) {
|
|
260654
|
+
const message = history[historyIndex];
|
|
260655
|
+
const text = persistableAssistantMessageText(message);
|
|
260656
|
+
if (text === void 0 || !shouldOffloadHistoricalAssistantMessage({
|
|
260657
|
+
historyIndex,
|
|
260658
|
+
historyLength: history.length,
|
|
260659
|
+
textChars: text.length
|
|
260660
|
+
})) continue;
|
|
260661
|
+
const messageHash = assistantMessageOffloadHash(message, text);
|
|
260662
|
+
if (this.replacements.has(messageHash)) continue;
|
|
260663
|
+
const outputPath = await saveAssistantMessage(this.agent.homedir, text);
|
|
260664
|
+
if (outputPath === void 0) continue;
|
|
260665
|
+
const replacementText = renderPersistedAssistantMessage(text, outputPath);
|
|
260666
|
+
const content = [{
|
|
260667
|
+
type: "text",
|
|
260668
|
+
text: replacementText
|
|
260669
|
+
}, ...message.content.filter((part) => part.type !== "text")];
|
|
260670
|
+
this.apply({
|
|
260671
|
+
messageHash,
|
|
260672
|
+
content,
|
|
260673
|
+
outputPath
|
|
260674
|
+
});
|
|
260675
|
+
this.agent.telemetry.track("assistant_message_offloaded", {
|
|
260676
|
+
chars_before: text.length,
|
|
260677
|
+
chars_after: replacementText.length,
|
|
260678
|
+
chars_saved: text.length - replacementText.length,
|
|
260679
|
+
non_text_part_count: content.length - 1,
|
|
260680
|
+
reason: "historical_oversize"
|
|
260681
|
+
});
|
|
260682
|
+
offloaded++;
|
|
260683
|
+
}
|
|
260684
|
+
return offloaded;
|
|
260685
|
+
}
|
|
260686
|
+
apply(replacement) {
|
|
260687
|
+
this.replacements.set(replacement.messageHash, replacement);
|
|
260688
|
+
this.agent.records.logRecord({
|
|
260689
|
+
type: "assistant_message_offload.apply",
|
|
260690
|
+
replacement
|
|
260691
|
+
});
|
|
260692
|
+
}
|
|
260693
|
+
compact(messages) {
|
|
260694
|
+
if (this.replacements.size === 0) return messages;
|
|
260695
|
+
return messages.map((message) => {
|
|
260696
|
+
const text = persistableAssistantMessageText(message);
|
|
260697
|
+
if (text === void 0) return message;
|
|
260698
|
+
const replacement = this.replacements.get(assistantMessageOffloadHash(message, text));
|
|
260699
|
+
return replacement === void 0 ? message : {
|
|
260700
|
+
...message,
|
|
260701
|
+
content: replacement.content
|
|
260702
|
+
};
|
|
260703
|
+
});
|
|
260704
|
+
}
|
|
260705
|
+
reset(history = this.agent.context?.history ?? []) {
|
|
260706
|
+
const activeHashes = new Set(history.map((message) => {
|
|
260707
|
+
const text = persistableAssistantMessageText(message);
|
|
260708
|
+
return text === void 0 ? void 0 : assistantMessageOffloadHash(message, text);
|
|
260709
|
+
}).filter((hash) => hash !== void 0));
|
|
260710
|
+
for (const messageHash of this.replacements.keys()) if (!activeHashes.has(messageHash)) this.replacements.delete(messageHash);
|
|
260711
|
+
}
|
|
260712
|
+
clear() {
|
|
260713
|
+
this.replacements.clear();
|
|
260714
|
+
}
|
|
260715
|
+
};
|
|
260716
|
+
//#endregion
|
|
260587
260717
|
//#region ../../packages/agent-core/src/agent/turn/index.ts
|
|
260588
260718
|
function blunExtractText(content) {
|
|
260589
260719
|
if (typeof content === "string") return content;
|
|
@@ -260920,6 +261050,7 @@ var init_turn = __esmMin((() => {
|
|
|
260920
261050
|
init_tool_dedup();
|
|
260921
261051
|
init_tool_result_budget();
|
|
260922
261052
|
init_user_message_offload();
|
|
261053
|
+
init_assistant_message_offload();
|
|
260923
261054
|
({ CORE_TOOL_NAMES: BLUN_CORE_TOOL_NAMES, createDeferredToolLoader, toolSchemaBudgetTokens } = createRequire(import.meta.url)("./bin/turn-tool-performance-policy.cjs"));
|
|
260924
261055
|
BLUN_LEAN_TOOL_NAMES = new Set(BLUN_CORE_TOOL_NAMES);
|
|
260925
261056
|
BLUN_ATTACHMENT_MARKER_RE = /\b(?:attachment_file_id|telegram-anhang|telegram attachment)\b/i;
|
|
@@ -261555,6 +261686,7 @@ var init_turn = __esmMin((() => {
|
|
|
261555
261686
|
await this.agent.records.flush();
|
|
261556
261687
|
this.agent.microCompaction.detect();
|
|
261557
261688
|
await this.agent.userMessageOffload.detect();
|
|
261689
|
+
await this.agent.assistantMessageOffload.detect();
|
|
261558
261690
|
await this.agent.injection.inject();
|
|
261559
261691
|
stepSignal.throwIfAborted();
|
|
261560
261692
|
deduper.beginStep();
|
|
@@ -264332,6 +264464,7 @@ var init_agent = __esmMin((() => {
|
|
|
264332
264464
|
fullCompaction;
|
|
264333
264465
|
microCompaction;
|
|
264334
264466
|
toolResultBatchOffload;
|
|
264467
|
+
assistantMessageOffload;
|
|
264335
264468
|
context;
|
|
264336
264469
|
config;
|
|
264337
264470
|
turn;
|
|
@@ -264392,6 +264525,7 @@ var init_agent = __esmMin((() => {
|
|
|
264392
264525
|
this.microCompaction = new MicroCompaction(this, options.microCompaction);
|
|
264393
264526
|
this.toolResultBatchOffload = new ToolResultBatchOffload(this);
|
|
264394
264527
|
this.userMessageOffload = new UserMessageOffload(this);
|
|
264528
|
+
this.assistantMessageOffload = new AssistantMessageOffload(this);
|
|
264395
264529
|
this.context = new ContextMemory(this);
|
|
264396
264530
|
this.config = new ConfigState(this);
|
|
264397
264531
|
this.turn = new TurnFlow(this);
|