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 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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.119",
3
+ "version": "9.1.120",
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": {