blun-king-cli 9.1.77 → 9.1.78

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
@@ -213,6 +213,21 @@ sodass auch kurze Schritte wie Rendern und Speichern in der Produktionskette
213
213
  sichtbar sind. Aktualisierte Vorschaubilder werden erneuert; Medienpfade und
214
214
  Webadressen sind als Terminalverweise anklickbar.
215
215
 
216
+ Wiederauffindbarer Verdichtungsverlauf
217
+ --------------------------------------
218
+
219
+ Bevor eine erfolgreiche Vollverdichtung ältere Nachrichten ersetzt, schreibt
220
+ King den verdrängten Verlauf in ein privates Markdown-Archiv unter
221
+ ~/.blun/conversation-history/. Die Verdichtungszusammenfassung enthält den
222
+ genauen history_path, sodass der Agent mit Read Einzelheiten wiederfinden kann,
223
+ die nicht in der Zusammenfassung stehen.
224
+
225
+ Eingebettete Bild-, Audio- und Videodaten werden im Archiv nicht doppelt
226
+ gespeichert. Der ursprüngliche Sitzungs-Wire bleibt maßgeblich. Kann das Archiv
227
+ nicht geschrieben werden, protokolliert King compaction_history_archive_failed
228
+ und lässt den normalen Verdichtungsweg verfügbar; ein Archivfehler löscht
229
+ niemals den gespeicherten Sitzungsverlauf.
230
+
216
231
  Aktualisieren
217
232
  -------------
218
233
 
package/README.md CHANGED
@@ -240,6 +240,12 @@ sodass auch kurze Schritte wie Rendern und Speichern in der Produktionskette
240
240
  sichtbar sind. Aktualisierte Vorschaubilder werden erneuert; Medienpfade und
241
241
  Webadressen sind als Terminalverweise anklickbar.
242
242
 
243
+ ## Wiederauffindbarer Verdichtungsverlauf
244
+
245
+ Bevor eine erfolgreiche Vollverdichtung ältere Nachrichten ersetzt, schreibt King den verdrängten Verlauf in ein privates Markdown-Archiv unter `~/.blun/conversation-history/`. Die Verdichtungszusammenfassung enthält den genauen `history_path`, sodass der Agent mit `Read` Einzelheiten wiederfinden kann, die nicht in der Zusammenfassung stehen.
246
+
247
+ Eingebettete Bild-, Audio- und Videodaten werden im Archiv nicht doppelt gespeichert. Der ursprüngliche Sitzungs-Wire bleibt maßgeblich. Kann das Archiv nicht geschrieben werden, protokolliert King `compaction_history_archive_failed` und lässt den normalen Verdichtungsweg verfügbar; ein Archivfehler löscht niemals den gespeicherten Sitzungsverlauf.
248
+
243
249
  ## Aktualisieren
244
250
 
245
251
  `blun update`, `king update` und die jeweilige Variante `upgrade` verwenden
@@ -0,0 +1,112 @@
1
+ 'use strict';
2
+
3
+ const { randomUUID } = require('node:crypto');
4
+ const { mkdir, writeFile } = require('node:fs/promises');
5
+ const path = require('node:path');
6
+
7
+ const COMPACTION_HISTORY_DIR = 'conversation-history';
8
+ const INLINE_MEDIA_NOTICE = '[inline media payload omitted; original remains in session wire]';
9
+
10
+ async function archiveCompactionHistory(options = {}) {
11
+ const homedir = requiredString(options.homedir, 'homedir');
12
+ const messages = Array.isArray(options.messages) ? options.messages : [];
13
+ const now = finiteNumber(options.now, Date.now());
14
+ const id = safeFilePart(options.id || randomUUID());
15
+ const dir = path.join(homedir, COMPACTION_HISTORY_DIR);
16
+ const timestamp = new Date(now).toISOString().replace(/[:.]/g, '-');
17
+ const outputPath = path.join(dir, `compaction-${timestamp}-${id}.md`);
18
+
19
+ await mkdir(dir, { recursive: true, mode: 0o700 });
20
+ await writeFile(outputPath, serializeCompactionHistory(messages, { now }), {
21
+ encoding: 'utf8',
22
+ flag: 'wx',
23
+ mode: 0o600,
24
+ });
25
+ return outputPath;
26
+ }
27
+
28
+ function serializeCompactionHistory(messages, options = {}) {
29
+ const now = finiteNumber(options.now, Date.now());
30
+ const lines = [
31
+ '# BLUN conversation history archive',
32
+ '',
33
+ `Archived at: ${new Date(now).toISOString()}`,
34
+ `Messages: ${messages.length}`,
35
+ '',
36
+ 'This file contains the messages displaced by a full context compaction.',
37
+ 'Inline media payloads are omitted here; the original session wire remains authoritative.',
38
+ ];
39
+
40
+ for (let index = 0; index < messages.length; index += 1) {
41
+ const message = sanitizeMessage(messages[index]);
42
+ lines.push(
43
+ '',
44
+ `## Message ${index + 1}: ${typeof message?.role === 'string' ? message.role : 'unknown'}`,
45
+ '',
46
+ '```json',
47
+ JSON.stringify(message, null, 2),
48
+ '```',
49
+ );
50
+ }
51
+ lines.push('');
52
+ return lines.join('\n');
53
+ }
54
+
55
+ function buildCompactionArchiveNotice(historyPath) {
56
+ return [
57
+ '<system-reminder>',
58
+ 'The complete messages displaced by this compaction were archived locally.',
59
+ `history_path: ${historyPath}`,
60
+ 'next_step: Use Read with history_path to recover exact details that the summary omitted.',
61
+ '</system-reminder>',
62
+ ].join('\n');
63
+ }
64
+
65
+ function sanitizeMessage(message) {
66
+ if (message === null || typeof message !== 'object') return message;
67
+ if (!Array.isArray(message.content)) return message;
68
+ return {
69
+ ...message,
70
+ content: message.content.map(sanitizeContentPart),
71
+ };
72
+ }
73
+
74
+ function sanitizeContentPart(part) {
75
+ if (part === null || typeof part !== 'object') return part;
76
+ if (part.type === 'image_url' && isInlineMediaUrl(part.imageUrl?.url)) {
77
+ return { type: 'text', text: INLINE_MEDIA_NOTICE };
78
+ }
79
+ if (part.type === 'audio_url' && isInlineMediaUrl(part.audioUrl?.url)) {
80
+ return { type: 'text', text: INLINE_MEDIA_NOTICE };
81
+ }
82
+ if (part.type === 'video_url' && isInlineMediaUrl(part.videoUrl?.url)) {
83
+ return { type: 'text', text: INLINE_MEDIA_NOTICE };
84
+ }
85
+ return part;
86
+ }
87
+
88
+ function isInlineMediaUrl(value) {
89
+ return typeof value === 'string' && /^data:(?:image|audio|video)\//i.test(value);
90
+ }
91
+
92
+ function requiredString(value, name) {
93
+ if (typeof value === 'string' && value.length > 0) return value;
94
+ throw new TypeError(`${name} must be a non-empty string`);
95
+ }
96
+
97
+ function safeFilePart(value) {
98
+ const cleaned = String(value).replace(/[^a-zA-Z0-9._-]+/g, '_').replace(/^_+|_+$/g, '').slice(0, 96);
99
+ return cleaned || randomUUID();
100
+ }
101
+
102
+ function finiteNumber(value, fallback) {
103
+ const number = Number(value);
104
+ return Number.isFinite(number) ? number : fallback;
105
+ }
106
+
107
+ module.exports = {
108
+ COMPACTION_HISTORY_DIR,
109
+ archiveCompactionHistory,
110
+ buildCompactionArchiveNotice,
111
+ serializeCompactionHistory,
112
+ };
package/blun.mjs CHANGED
@@ -75237,8 +75237,9 @@ function extractCompactionSummary(response) {
75237
75237
  if (summary.trim().length === 0) throw new APIEmptyResponseError("The compaction response did not contain a non-empty summary.");
75238
75238
  return summary;
75239
75239
  }
75240
- var DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS, COMPACTION_THINKING_EFFORT, COMPACTION_SUMMARY_RESERVE_RATIO, MAX_HIERARCHICAL_COMPACTION_PASSES, HIERARCHICAL_COMPACTION_PREFIX, CompactionTruncatedError, CompactionStallError, COMPACTION_STALL_MEASUREMENT_MULTIPLIER, REBUILT_AFTER_COMPACTION_INJECTION_VARIANTS, FullCompaction, MAX_COMPACTION_OVERFLOW_SHRINK_ATTEMPTS, COMPACTION_OVERFLOW_SHRINK_RATIOS;
75240
+ var archiveCompactionHistory, buildCompactionArchiveNotice, DEFAULT_COMPACTION_MAX_COMPLETION_TOKENS, COMPACTION_THINKING_EFFORT, COMPACTION_SUMMARY_RESERVE_RATIO, MAX_HIERARCHICAL_COMPACTION_PASSES, HIERARCHICAL_COMPACTION_PREFIX, CompactionTruncatedError, CompactionStallError, COMPACTION_STALL_MEASUREMENT_MULTIPLIER, REBUILT_AFTER_COMPACTION_INJECTION_VARIANTS, FullCompaction, MAX_COMPACTION_OVERFLOW_SHRINK_ATTEMPTS, COMPACTION_OVERFLOW_SHRINK_RATIOS;
75241
75241
  var init_full = __esmMin((() => {
75242
+ ({ archiveCompactionHistory, buildCompactionArchiveNotice } = createRequire(import.meta.url)("./bin/compaction-history-archive.cjs"));
75242
75243
  init_errors$8();
75243
75244
  init_src$4();
75244
75245
  init_errors$4();
@@ -75773,7 +75774,24 @@ var init_full = __esmMin((() => {
75773
75774
  return;
75774
75775
  }
75775
75776
  const rawSummary = this.postProcessSummary(summary ?? "");
75776
- const contextSummary = buildCompactionSummaryText(rawSummary);
75777
+ let contextSummary = buildCompactionSummaryText(rawSummary);
75778
+ let historyPath;
75779
+ try {
75780
+ historyPath = await archiveCompactionHistory({
75781
+ homedir: this.agent.blunHomeDir,
75782
+ messages: originalHistory
75783
+ });
75784
+ contextSummary = `${contextSummary}\n\n${buildCompactionArchiveNotice(historyPath)}`;
75785
+ this.agent.telemetry.track("compaction_history_archived", {
75786
+ history_path: historyPath,
75787
+ message_count: originalHistory.length
75788
+ });
75789
+ } catch (error) {
75790
+ this.agent.telemetry.track("compaction_history_archive_failed", {
75791
+ message_count: originalHistory.length,
75792
+ error_type: error instanceof Error ? error.name : "Unknown"
75793
+ });
75794
+ }
75777
75795
  const result = this.agent.context.applyCompaction({
75778
75796
  summary: rawSummary,
75779
75797
  contextSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "9.1.77",
3
+ "version": "9.1.78",
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": {