blun-king-cli 9.1.77 → 9.1.79
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 +22 -0
- package/README.md +12 -0
- package/bin/compaction-history-archive.cjs +112 -0
- package/blun.mjs +22 -2
- package/package.json +1 -1
package/LIESMICH.txt
CHANGED
|
@@ -213,6 +213,28 @@ 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
|
+
|
|
231
|
+
Dauerhafte Checkpoints zwischen Arbeitsschritten
|
|
232
|
+
-------------------------------------------------
|
|
233
|
+
|
|
234
|
+
Bevor King den nächsten Modellschritt beginnt, schreibt er alle vorangegangenen Wire-Einträge dauerhaft auf den Datenträger. Nach dem Ende eines Zuges erfolgt eine weitere vollständige Sicherung, bevor der zugehörige Worker abgeschlossen wird. Das schließt beendete Modell- und Werkzeugschritte ein. Eine fortgesetzte Sitzung beginnt dadurch beim letzten abgeschlossenen Schritt und ist nicht auf noch ungesicherte Einträge im Arbeitsspeicher angewiesen.
|
|
235
|
+
|
|
236
|
+
Beispiel: Wenn der Anbieter oder das Terminal nach einem abgeschlossenen Werkzeugaufruf, aber vor der nächsten Modellantwort ausfällt, kann die Sitzung neu gestartet oder fortgesetzt werden. Der vorangegangene Werkzeugeintrag wurde vor der nächsten Anfrage an den Anbieter in wire.jsonl synchronisiert und wird beim Fortsetzen wieder eingelesen. Der Checkpoint verkürzt keine Prompts und ersetzt nicht die wiederauffindbaren Verdichtungsarchive; er schützt die Übergabe zwischen zwei Arbeitsschritten.
|
|
237
|
+
|
|
216
238
|
Aktualisieren
|
|
217
239
|
-------------
|
|
218
240
|
|
package/README.md
CHANGED
|
@@ -240,6 +240,18 @@ 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
|
+
|
|
249
|
+
## Dauerhafte Checkpoints zwischen Arbeitsschritten
|
|
250
|
+
|
|
251
|
+
Bevor King den nächsten Modellschritt beginnt, schreibt er alle vorangegangenen Wire-Einträge dauerhaft auf den Datenträger. Nach dem Ende eines Zuges erfolgt eine weitere vollständige Sicherung, bevor der zugehörige Worker abgeschlossen wird. Das schließt beendete Modell- und Werkzeugschritte ein. Eine fortgesetzte Sitzung beginnt dadurch beim letzten abgeschlossenen Schritt und ist nicht auf noch ungesicherte Einträge im Arbeitsspeicher angewiesen.
|
|
252
|
+
|
|
253
|
+
Beispiel: Wenn der Anbieter oder das Terminal nach einem abgeschlossenen Werkzeugaufruf, aber vor der nächsten Modellantwort ausfällt, kann die Sitzung neu gestartet oder fortgesetzt werden. Der vorangegangene Werkzeugeintrag wurde vor der nächsten Anfrage an den Anbieter in `wire.jsonl` synchronisiert und wird beim Fortsetzen wieder eingelesen. Der Checkpoint verkürzt keine Prompts und ersetzt nicht die wiederauffindbaren Verdichtungsarchive; er schützt die Übergabe zwischen zwei Arbeitsschritten.
|
|
254
|
+
|
|
243
255
|
## Aktualisieren
|
|
244
256
|
|
|
245
257
|
`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
|
-
|
|
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,
|
|
@@ -260766,6 +260784,7 @@ var init_turn = __esmMin((() => {
|
|
|
260766
260784
|
this.currentStepByTurn.delete(turnId);
|
|
260767
260785
|
this.interruptedTelemetryTurnIds.delete(turnId);
|
|
260768
260786
|
this.stepFailureByTurn.delete(turnId);
|
|
260787
|
+
await this.agent.records.flush();
|
|
260769
260788
|
return {
|
|
260770
260789
|
event: ended,
|
|
260771
260790
|
stopReason: completedStopReason,
|
|
@@ -260898,6 +260917,7 @@ var init_turn = __esmMin((() => {
|
|
|
260898
260917
|
},
|
|
260899
260918
|
hooks: {
|
|
260900
260919
|
beforeStep: async ({ signal: stepSignal, stepNumber }) => {
|
|
260920
|
+
await this.agent.records.flush();
|
|
260901
260921
|
this.agent.microCompaction.detect();
|
|
260902
260922
|
await this.agent.injection.inject();
|
|
260903
260923
|
stepSignal.throwIfAborted();
|