blun-king-cli 9.1.97 → 9.1.99
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 +7 -1
- package/README.md +7 -1
- package/bin/proactive-compaction-policy.cjs +25 -0
- package/bin/tool-result-offload-policy.cjs +38 -0
- package/bin/turn-tool-performance-policy.cjs +1 -0
- package/blun.mjs +196 -7
- package/package.json +1 -1
package/LIESMICH.txt
CHANGED
|
@@ -9,7 +9,7 @@ Installation
|
|
|
9
9
|
------------
|
|
10
10
|
Die geprüfte Version exakt global installieren:
|
|
11
11
|
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.99
|
|
13
13
|
|
|
14
14
|
Start
|
|
15
15
|
-----
|
|
@@ -85,14 +85,20 @@ King bewahrt den vollständigen Verlauf weiterhin im Sitzungs-Wire auf. Erreicht
|
|
|
85
85
|
|
|
86
86
|
Dabei werden keine gespeicherten Nachrichten geändert oder gelöscht. Fortsetzen, Exportieren und die sichtbare Historie behalten die ursprünglichen Werkzeugergebnisse und Werkzeugargumente. Das Telemetrieereignis `micro_compaction_finished` nennt den Auslöser, den Schnitt und die geschätzte Tokenzahl vor und nach der Entlastung. Außerdem zählt es getrennt, wie viele Werkzeugergebnisse und Werkzeugargumente entlastet wurden. Der Sitzungsinspektor summiert zusätzlich die eingesparten Argument-Token, ohne Inhalte offenzulegen. Beispiel: Ein früherer `Write`-Aufruf mit einem vollständigen Dateiinhalt bleibt im Wire erhalten; die Modellprojektion trägt nur noch einen Platzhalter, sobald für diesen `Write`-Aufruf ein zugehöriges Ergebnis vorliegt.
|
|
87
87
|
|
|
88
|
+
Ab BLUN King 9.1.98 kann King einen abgeschlossenen Arbeitsabschnitt verdichten, bevor die harte automatische Grenze erreicht ist. Unterhalb der halben Vollverdichtungsgrenze bleibt `CompactConversation` vollständig aus dem Modellprompt. Ab 128.000 geschätzten Token im Standardmodellfenster wird es für den nächsten Modellschritt verfügbar. Die Verdichtung beginnt erst nach Abschluss des aktuellen Werkzeugschritts, öffnet keinen konkurrierenden Zug und lässt den ursprünglichen Verlauf bei einem Fehlschlag unverändert.
|
|
89
|
+
|
|
88
90
|
## Große Werkzeugausgaben und isolierte Teilagenten
|
|
89
91
|
|
|
90
92
|
Große textbasierte Werkzeugergebnisse bleiben nicht mehr vollständig im Modellkontext. Ab 12.001 Zeichen speichert BLUN King das vollständige Ergebnis in einer privaten Datei im Sitzungsordner `tool-results`. Im Modellkontext verbleiben die ersten 1.000 und die letzten 1.000 Zeichen, die genaue Zahl der ausgelassenen Zeichen und der `output_path`. Der Agent kann das vollständige Ergebnis anschließend mit `Read` seitenweise über diesen Pfad lesen. Ergebnisse bis einschließlich 12.000 Zeichen, gemischte Medienergebnisse und bereits gekürzte Ergebnisse bleiben unverändert. Auch eine spätere Mikroverdichtung bewahrt den Dateiverweis. Beispiel: Bei einem Suchergebnis mit 30.000 Zeichen sehen folgende Modellanfragen den Anfang und das abschließende Ergebnis oder den Fehler; der vollständige Text bleibt lokal verfügbar.
|
|
91
93
|
|
|
94
|
+
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.
|
|
95
|
+
|
|
92
96
|
Große Dateien werden weiterhin seitenweise gelesen. Erreicht `Read` seine interne Grenze von 1.000 Zeilen und enthält die Datei weitere Zeilen, nennt das Ergebnis jetzt den exakten nächsten `line_offset`. Beispiel: Ein Lesevorgang ab Zeile 2001 wird mit `line_offset=3001` fortgesetzt. Am Dateiende und bei einem bewusst kleineren Leseausschnitt erscheint kein Fortsetzungshinweis. Dadurch zieht der Agent keine Schlüsse aus einem unvollständigen Ausschnitt und liest die Datei nicht erneut ab der ersten Zeile.
|
|
93
97
|
|
|
94
98
|
Das Telemetrieereignis `tool_result_offloaded` enthält ausschließlich `tool_name`, `output_size_chars`, `output_size_bytes` und `preview_size_chars`. Es enthält weder den Inhalt noch den Speicherpfad. Beispiel: Eine Ausgabe mit 80.000 Zeichen erzeugt eine Vorschau mit 2.000 Zeichen; die Telemetrie zeigt die Größenersparnis, ohne Nutzdaten zu protokollieren.
|
|
95
99
|
|
|
100
|
+
Das Telemetrieereignis `tool_result_batch_offloaded` meldet ausschließlich die Zahl der Ergebnisse sowie die Zeichenzahlen vor und nach der Entlastung und die eingesparte Zeichenzahl. Es enthält weder Ergebnisinhalte noch Speicherpfade.
|
|
101
|
+
|
|
96
102
|
Reguläre `Agent`-Teilaufgaben verwenden eine eigene `ContextMemory` und eine eigene `wire.jsonl` in einem getrennten Agentenverzeichnis. Der Hauptagent erhält nur die Ergebniszusammenfassung, sodass lange Teilaufgaben nicht den Hauptverlauf füllen. `TodoList` speichert Pläne weiterhin maschinenlesbar im Sitzungs-Wire; `blun handoff` übergibt sie zusammen mit prüfbaren Hashes zwischen CLI, Desktop und Web.
|
|
97
103
|
|
|
98
104
|
Rein lesende Sitzungsdiagnose
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ Voraussetzung ist Node.js 24.15 oder neuer. Die geprüfte Version wird exakt
|
|
|
9
9
|
installiert:
|
|
10
10
|
|
|
11
11
|
```powershell
|
|
12
|
-
npm install -g blun-king-cli@9.1.
|
|
12
|
+
npm install -g blun-king-cli@9.1.99
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Reproduzierbares Staging und Packen
|
|
@@ -105,14 +105,20 @@ King bewahrt den vollständigen Verlauf weiterhin im Sitzungs-Wire auf. Bei 75 P
|
|
|
105
105
|
|
|
106
106
|
Dabei werden keine gespeicherten Nachrichten geändert oder gelöscht. Fortsetzen, Exportieren und die sichtbare Historie behalten die ursprünglichen Werkzeugergebnisse und Werkzeugargumente. Das Telemetrieereignis `micro_compaction_finished` nennt den Auslöser, den Schnitt, die verwendete Druckgrenze, das Modellfenster und die geschätzte Tokenzahl vor und nach der Entlastung. Außerdem zählt es getrennt, wie viele Werkzeugergebnisse und Werkzeugargumente entlastet wurden. Der Sitzungsinspektor summiert zusätzlich die eingesparten Argument-Token, ohne Inhalte offenzulegen. Beispiel: Ein früherer `Write`-Aufruf mit einem vollständigen Dateiinhalt bleibt im Wire erhalten; die Modellprojektion trägt nur noch einen Platzhalter, sobald für diesen `Write`-Aufruf ein zugehöriges Ergebnis vorliegt.
|
|
107
107
|
|
|
108
|
+
Ab BLUN King 9.1.98 kann King einen abgeschlossenen Arbeitsabschnitt verdichten, bevor die harte automatische Grenze erreicht ist. Unterhalb der halben Vollverdichtungsgrenze bleibt `CompactConversation` vollständig aus dem Modellprompt. Ab 128.000 geschätzten Token im Standardmodellfenster wird es für den nächsten Modellschritt verfügbar. Die Verdichtung beginnt erst nach Abschluss des aktuellen Werkzeugschritts, öffnet keinen konkurrierenden Zug und lässt den ursprünglichen Verlauf bei einem Fehlschlag unverändert.
|
|
109
|
+
|
|
108
110
|
## Große Werkzeugausgaben und isolierte Teilagenten
|
|
109
111
|
|
|
110
112
|
Große textbasierte Werkzeugergebnisse bleiben nicht mehr vollständig im Modellkontext. Ab 12.001 Zeichen speichert BLUN King das vollständige Ergebnis in einer privaten Datei im Sitzungsordner `tool-results`. Im Modellkontext verbleiben die ersten 1.000 und die letzten 1.000 Zeichen, die genaue Zahl der ausgelassenen Zeichen und der `output_path`. Der Agent kann das vollständige Ergebnis anschließend mit `Read` seitenweise über diesen Pfad lesen. Ergebnisse bis einschließlich 12.000 Zeichen, gemischte Medienergebnisse und bereits gekürzte Ergebnisse bleiben unverändert. Auch eine spätere Mikroverdichtung bewahrt den Dateiverweis. Beispiel: Bei einem Suchergebnis mit 30.000 Zeichen sehen folgende Modellanfragen den Anfang und das abschließende Ergebnis oder den Fehler; der vollständige Text bleibt lokal verfügbar.
|
|
111
113
|
|
|
114
|
+
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.
|
|
115
|
+
|
|
112
116
|
Große Dateien werden weiterhin seitenweise gelesen. Erreicht `Read` seine interne Grenze von 1.000 Zeilen und enthält die Datei weitere Zeilen, nennt das Ergebnis jetzt den exakten nächsten `line_offset`. Beispiel: Ein Lesevorgang ab Zeile 2001 wird mit `line_offset=3001` fortgesetzt. Am Dateiende und bei einem bewusst kleineren Leseausschnitt erscheint kein Fortsetzungshinweis. Dadurch zieht der Agent keine Schlüsse aus einem unvollständigen Ausschnitt und liest die Datei nicht erneut ab der ersten Zeile.
|
|
113
117
|
|
|
114
118
|
Das Telemetrieereignis `tool_result_offloaded` enthält ausschließlich `tool_name`, `output_size_chars`, `output_size_bytes` und `preview_size_chars`. Es enthält weder den Inhalt noch den Speicherpfad. Beispiel: Eine Ausgabe mit 80.000 Zeichen erzeugt eine Vorschau mit 2.000 Zeichen; die Telemetrie zeigt die Größenersparnis, ohne Nutzdaten zu protokollieren.
|
|
115
119
|
|
|
120
|
+
Das Telemetrieereignis `tool_result_batch_offloaded` meldet ausschließlich die Zahl der Ergebnisse sowie die Zeichenzahlen vor und nach der Entlastung und die eingesparte Zeichenzahl. Es enthält weder Ergebnisinhalte noch Speicherpfade.
|
|
121
|
+
|
|
116
122
|
Reguläre `Agent`-Teilaufgaben verwenden eine eigene `ContextMemory` und eine eigene `wire.jsonl` in einem getrennten Agentenverzeichnis. Der Hauptagent erhält nur die Ergebniszusammenfassung, sodass lange Teilaufgaben nicht den Hauptverlauf füllen. `TodoList` speichert Pläne weiterhin maschinenlesbar im Sitzungs-Wire; `blun handoff` übergibt sie zusammen mit prüfbaren Hashes zwischen CLI, Desktop und Web.
|
|
117
123
|
|
|
118
124
|
## Rein lesende Sitzungsdiagnose
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const PROACTIVE_COMPACTION_TRIGGER_FRACTION = 0.5;
|
|
4
|
+
|
|
5
|
+
function proactiveCompactionEligibility({ usedTokens, budgetTokens, hasShrinkableHistory, isCompacting }) {
|
|
6
|
+
const budget = Number(budgetTokens);
|
|
7
|
+
const thresholdTokens = Number.isFinite(budget) && budget > 0
|
|
8
|
+
? Math.max(1, Math.floor(budget * PROACTIVE_COMPACTION_TRIGGER_FRACTION))
|
|
9
|
+
: undefined;
|
|
10
|
+
|
|
11
|
+
if (isCompacting === true) return { eligible: false, thresholdTokens, reason: 'already_compacting' };
|
|
12
|
+
if (thresholdTokens === undefined) return { eligible: false, thresholdTokens, reason: 'no_budget' };
|
|
13
|
+
if (!Number.isFinite(usedTokens) || usedTokens < thresholdTokens) {
|
|
14
|
+
return { eligible: false, thresholdTokens, reason: 'below_threshold' };
|
|
15
|
+
}
|
|
16
|
+
if (hasShrinkableHistory !== true) {
|
|
17
|
+
return { eligible: false, thresholdTokens, reason: 'nothing_to_compact' };
|
|
18
|
+
}
|
|
19
|
+
return { eligible: true, thresholdTokens, reason: 'eligible' };
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = {
|
|
23
|
+
PROACTIVE_COMPACTION_TRIGGER_FRACTION,
|
|
24
|
+
proactiveCompactionEligibility,
|
|
25
|
+
};
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
const TOOL_RESULT_MAX_CHARS = 12_000;
|
|
4
4
|
const TOOL_RESULT_PREVIEW_CHARS = 2_000;
|
|
5
5
|
const TOOL_RESULT_OFFLOAD_MARKER = '[Tool result offloaded]';
|
|
6
|
+
const TOOL_RESULT_BATCH_MAX_CHARS = TOOL_RESULT_MAX_CHARS;
|
|
7
|
+
const TOOL_RESULT_BATCH_MIN_ITEM_CHARS = 3_000;
|
|
8
|
+
const TOOL_RESULT_BATCH_REPLACEMENT_BUDGET_CHARS = 2_500;
|
|
6
9
|
|
|
7
10
|
function shouldOffloadToolResult(textLength) {
|
|
8
11
|
return Number.isFinite(textLength) && textLength > TOOL_RESULT_MAX_CHARS;
|
|
@@ -28,11 +31,46 @@ function isPersistedToolResultReference(content) {
|
|
|
28
31
|
});
|
|
29
32
|
}
|
|
30
33
|
|
|
34
|
+
function selectToolResultBatchOffloads(textLengths) {
|
|
35
|
+
if (!Array.isArray(textLengths) || textLengths.length < 2) return [];
|
|
36
|
+
|
|
37
|
+
const normalized = textLengths.map((length) => (
|
|
38
|
+
Number.isFinite(length) && length > 0 ? Math.floor(length) : 0
|
|
39
|
+
));
|
|
40
|
+
const totalChars = normalized.reduce((total, length) => total + length, 0);
|
|
41
|
+
if (totalChars <= TOOL_RESULT_BATCH_MAX_CHARS) return [];
|
|
42
|
+
|
|
43
|
+
let projectedChars = totalChars;
|
|
44
|
+
const selected = [];
|
|
45
|
+
const candidates = normalized
|
|
46
|
+
.map((length, index) => ({ index, length }))
|
|
47
|
+
.filter(({ length }) => (
|
|
48
|
+
length >= TOOL_RESULT_BATCH_MIN_ITEM_CHARS
|
|
49
|
+
&& length <= TOOL_RESULT_MAX_CHARS
|
|
50
|
+
&& length > TOOL_RESULT_BATCH_REPLACEMENT_BUDGET_CHARS
|
|
51
|
+
))
|
|
52
|
+
.sort((left, right) => right.length - left.length || left.index - right.index);
|
|
53
|
+
|
|
54
|
+
for (const candidate of candidates) {
|
|
55
|
+
if (projectedChars <= TOOL_RESULT_BATCH_MAX_CHARS) break;
|
|
56
|
+
projectedChars -= candidate.length - TOOL_RESULT_BATCH_REPLACEMENT_BUDGET_CHARS;
|
|
57
|
+
selected.push(candidate.index);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return projectedChars <= TOOL_RESULT_BATCH_MAX_CHARS
|
|
61
|
+
? selected.sort((left, right) => left - right)
|
|
62
|
+
: [];
|
|
63
|
+
}
|
|
64
|
+
|
|
31
65
|
module.exports = {
|
|
66
|
+
TOOL_RESULT_BATCH_MAX_CHARS,
|
|
67
|
+
TOOL_RESULT_BATCH_MIN_ITEM_CHARS,
|
|
68
|
+
TOOL_RESULT_BATCH_REPLACEMENT_BUDGET_CHARS,
|
|
32
69
|
TOOL_RESULT_MAX_CHARS,
|
|
33
70
|
TOOL_RESULT_PREVIEW_CHARS,
|
|
34
71
|
TOOL_RESULT_OFFLOAD_MARKER,
|
|
35
72
|
createToolResultPreview,
|
|
36
73
|
isPersistedToolResultReference,
|
|
74
|
+
selectToolResultBatchOffloads,
|
|
37
75
|
shouldOffloadToolResult,
|
|
38
76
|
};
|
package/blun.mjs
CHANGED
|
@@ -28485,6 +28485,7 @@ var init_load = __esmMin((() => {
|
|
|
28485
28485
|
var agent_default$1;
|
|
28486
28486
|
var init_agent$3 = __esmMin((() => {
|
|
28487
28487
|
agent_default$1 = "name: agent\ndescription: Default BLUN King agent\n\nsystemPromptPath: ./system.md\npromptVars:\n roleAdditional: ''\n\ntools:\n - Read\n - Write\n - Edit\n - Grep\n - Glob\n - Bash\n - TaskList\n - TaskOutput\n - TaskStop\n - CronCreate\n - CronList\n - CronDelete\n - ReadMediaFile\n - TodoList\n - Skill\n - WebSearch\n - Agent\n - AgentSwarm\n - FetchURL\n - GenerateImage\n - GenerateVideo\n - GenerateSpeech\n - GetMedia\n - AskUserQuestion\n - MistakeRecord\n - CodebaseSearch\n - EnterPlanMode\n - ExitPlanMode\n - CreateGoal\n - GetGoal\n - SetGoalBudget\n - UpdateGoal\n - mcp__*\n\nsubagents:\n coder:\n description: General software engineering agent — the only subagent type with file-editing tools; use it for any delegated task that must modify code.\n explore:\n description: Fast codebase exploration with prompt-enforced read-only behavior.\n plan:\n description: Read-only implementation planning and architecture design.\n";
|
|
28488
|
+
agent_default$1 = agent_default$1.replace(" - TodoList\n", " - TodoList\n - CompactConversation\n");
|
|
28488
28489
|
}));
|
|
28489
28490
|
//#endregion
|
|
28490
28491
|
//#region ../../packages/agent-core/src/profile/default/coder.yaml?raw
|
|
@@ -75241,10 +75242,11 @@ function extractCompactionSummary(response) {
|
|
|
75241
75242
|
if (summary.trim().length === 0) throw new APIEmptyResponseError("The compaction response did not contain a non-empty summary.");
|
|
75242
75243
|
return summary;
|
|
75243
75244
|
}
|
|
75244
|
-
var archiveCompactionHistory, buildCompactionArchiveNotice, capCompactionStageTarget, 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;
|
|
75245
|
+
var archiveCompactionHistory, buildCompactionArchiveNotice, capCompactionStageTarget, proactiveCompactionEligibility, 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;
|
|
75245
75246
|
var init_full = __esmMin((() => {
|
|
75246
75247
|
({ archiveCompactionHistory, buildCompactionArchiveNotice } = createRequire(import.meta.url)("./bin/compaction-history-archive.cjs"));
|
|
75247
75248
|
({ capCompactionStageTarget } = createRequire(import.meta.url)("./bin/compaction-stage-policy.cjs"));
|
|
75249
|
+
({ proactiveCompactionEligibility } = createRequire(import.meta.url)("./bin/proactive-compaction-policy.cjs"));
|
|
75248
75250
|
init_errors$8();
|
|
75249
75251
|
init_src$4();
|
|
75250
75252
|
init_errors$4();
|
|
@@ -75300,6 +75302,7 @@ var init_full = __esmMin((() => {
|
|
|
75300
75302
|
compactionTimingByProviderModel = /* @__PURE__ */ new Map();
|
|
75301
75303
|
lastCompactedState = null;
|
|
75302
75304
|
consecutiveOverflowCompactions = 0;
|
|
75305
|
+
proactiveCompactionRequested = false;
|
|
75303
75306
|
strategy;
|
|
75304
75307
|
requiresShrinkableHistory;
|
|
75305
75308
|
constructor(agent, strategy) {
|
|
@@ -75329,6 +75332,27 @@ var init_full = __esmMin((() => {
|
|
|
75329
75332
|
const budget = this.strategy.compactionBudgetTokens;
|
|
75330
75333
|
return budget !== void 0 && Number.isFinite(budget) && budget > 0 ? Math.floor(budget) : void 0;
|
|
75331
75334
|
}
|
|
75335
|
+
proactiveCompactionStatus() {
|
|
75336
|
+
return proactiveCompactionEligibility({
|
|
75337
|
+
usedTokens: this.estimateCurrentRequestTokens(),
|
|
75338
|
+
budgetTokens: this.getCompactionBudgetTokens(),
|
|
75339
|
+
hasShrinkableHistory: this.hasShrinkableHistory(this.agent.context.history),
|
|
75340
|
+
isCompacting: this.isCompacting
|
|
75341
|
+
});
|
|
75342
|
+
}
|
|
75343
|
+
isProactiveCompactionEligible() {
|
|
75344
|
+
return this.proactiveCompactionStatus().eligible;
|
|
75345
|
+
}
|
|
75346
|
+
requestProactiveCompaction() {
|
|
75347
|
+
const status = this.proactiveCompactionStatus();
|
|
75348
|
+
if (!status.eligible) return status;
|
|
75349
|
+
this.proactiveCompactionRequested = true;
|
|
75350
|
+
this.agent.telemetry.track("proactive_compaction_requested", {
|
|
75351
|
+
used_tokens: this.estimateCurrentRequestTokens(),
|
|
75352
|
+
threshold_tokens: status.thresholdTokens
|
|
75353
|
+
});
|
|
75354
|
+
return status;
|
|
75355
|
+
}
|
|
75332
75356
|
estimateCurrentRequestTokens(context = {}) {
|
|
75333
75357
|
const estimated = this.estimateProjectedRequestTokens(context);
|
|
75334
75358
|
return Math.max(this.tokenCountWithPending, estimated);
|
|
@@ -75406,6 +75430,7 @@ var init_full = __esmMin((() => {
|
|
|
75406
75430
|
this.compactionCountInTurn = 0;
|
|
75407
75431
|
this.lastCompactedState = null;
|
|
75408
75432
|
this.consecutiveOverflowCompactions = 0;
|
|
75433
|
+
this.proactiveCompactionRequested = false;
|
|
75409
75434
|
}
|
|
75410
75435
|
async handleOverflowError(signal, error) {
|
|
75411
75436
|
this.consecutiveOverflowCompactions += 1;
|
|
@@ -75435,8 +75460,14 @@ var init_full = __esmMin((() => {
|
|
|
75435
75460
|
} });
|
|
75436
75461
|
return compacted;
|
|
75437
75462
|
}
|
|
75438
|
-
async afterStep() {
|
|
75463
|
+
async afterStep(signal) {
|
|
75439
75464
|
this.consecutiveOverflowCompactions = 0;
|
|
75465
|
+
if (this.proactiveCompactionRequested) {
|
|
75466
|
+
this.proactiveCompactionRequested = false;
|
|
75467
|
+
if (!this.compacting) this.beginAutoCompaction();
|
|
75468
|
+
await this.block(signal);
|
|
75469
|
+
return;
|
|
75470
|
+
}
|
|
75440
75471
|
if (this.strategy.checkAfterStep) this.checkAutoCompaction(false);
|
|
75441
75472
|
}
|
|
75442
75473
|
checkAutoCompaction(throwOnLimit = true, usedSize = this.tokenCountWithPending, messages = this.agent.context.history) {
|
|
@@ -78726,6 +78757,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78726
78757
|
if (removedMessages.size === 0) return 0;
|
|
78727
78758
|
this.agent.replayBuilder.removeLastMessages(removedMessages);
|
|
78728
78759
|
this.agent.microCompaction.reset(this._history.length);
|
|
78760
|
+
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78729
78761
|
this.agent.emitStatusUpdated();
|
|
78730
78762
|
return removedMessages.size;
|
|
78731
78763
|
}
|
|
@@ -78739,6 +78771,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78739
78771
|
this.deferredMessages = [];
|
|
78740
78772
|
this._lastAssistantAt = null;
|
|
78741
78773
|
this.agent.microCompaction.reset();
|
|
78774
|
+
this.agent.toolResultBatchOffload.clear();
|
|
78742
78775
|
this.agent.injection.onContextClear();
|
|
78743
78776
|
this.agent.emitStatusUpdated();
|
|
78744
78777
|
}
|
|
@@ -78777,6 +78810,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78777
78810
|
this.pendingToolResultIds.clear();
|
|
78778
78811
|
this.deferredMessages = [];
|
|
78779
78812
|
this.agent.microCompaction.reset(this._history.length);
|
|
78813
|
+
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78780
78814
|
this.agent.emitStatusUpdated();
|
|
78781
78815
|
if (!this.agent.records.restoring && (stoppedAtBoundary || removedUserCount < count)) throw new BlunError(ErrorCodes.REQUEST_INVALID, formatUndoUnavailableMessage(count, removedUserCount, stoppedAtBoundary), { details: {
|
|
78782
78816
|
reason: "undo_limit",
|
|
@@ -78855,6 +78889,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78855
78889
|
this._tokenCount = result.tokensAfter;
|
|
78856
78890
|
this.tokenCountCoveredMessageCount = this._history.length;
|
|
78857
78891
|
this.agent.microCompaction.reset();
|
|
78892
|
+
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78858
78893
|
this.agent.injection.onContextCompacted();
|
|
78859
78894
|
this.agent.emitStatusUpdated();
|
|
78860
78895
|
return result;
|
|
@@ -78877,7 +78912,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78877
78912
|
}
|
|
78878
78913
|
project(messages, options) {
|
|
78879
78914
|
const anomalies = [];
|
|
78880
|
-
const result = project(this.agent.microCompaction.compact(messages), {
|
|
78915
|
+
const result = project(this.agent.microCompaction.compact(this.agent.toolResultBatchOffload.compact(messages)), {
|
|
78881
78916
|
...options,
|
|
78882
78917
|
onAnomaly: (anomaly) => {
|
|
78883
78918
|
anomalies.push(anomaly);
|
|
@@ -234797,6 +234832,9 @@ function restoreAgentRecord(agent, input) {
|
|
|
234797
234832
|
case "micro_compaction.apply":
|
|
234798
234833
|
agent.microCompaction.apply(input.cutoff);
|
|
234799
234834
|
return;
|
|
234835
|
+
case "tool_result_batch_offload.apply":
|
|
234836
|
+
agent.toolResultBatchOffload.apply(input.replacements);
|
|
234837
|
+
return;
|
|
234800
234838
|
case "plan_mode.enter":
|
|
234801
234839
|
agent.planMode.restoreEnter(input);
|
|
234802
234840
|
return;
|
|
@@ -259984,6 +260022,11 @@ var init_tool_dedup = __esmMin((() => {
|
|
|
259984
260022
|
async function budgetToolResultForModel(options) {
|
|
259985
260023
|
const text = persistableToolResultText(options.result.output);
|
|
259986
260024
|
if (text === void 0 || !shouldOffloadToolResult(text.length)) return options.result;
|
|
260025
|
+
return persistToolResultForModel(options, text);
|
|
260026
|
+
}
|
|
260027
|
+
async function persistToolResultForModel(options, knownText) {
|
|
260028
|
+
const text = knownText ?? persistableToolResultText(options.result.output);
|
|
260029
|
+
if (text === void 0) return options.result;
|
|
259987
260030
|
if (options.result.truncated === true) return options.result;
|
|
259988
260031
|
if (options.homedir === void 0) return options.result;
|
|
259989
260032
|
const outputPath = await saveToolResult({
|
|
@@ -260047,7 +260090,7 @@ function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
|
|
|
260047
260090
|
function safeToolResultFileStem(toolName, toolCallId) {
|
|
260048
260091
|
return `${toolName}-${toolCallId}`.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "tool-result";
|
|
260049
260092
|
}
|
|
260050
|
-
var TOOL_RESULT_MAX_CHARS, TOOL_RESULT_PREVIEW_CHARS, TOOL_RESULT_OFFLOAD_MARKER, shouldOffloadToolResult, createToolResultPreview, buildToolResultOffloadTelemetry;
|
|
260093
|
+
var TOOL_RESULT_MAX_CHARS, TOOL_RESULT_PREVIEW_CHARS, TOOL_RESULT_OFFLOAD_MARKER, shouldOffloadToolResult, createToolResultPreview, selectToolResultBatchOffloads, buildToolResultOffloadTelemetry;
|
|
260051
260094
|
var init_tool_result_budget = __esmMin((() => {
|
|
260052
260095
|
init_dist$6();
|
|
260053
260096
|
const toolResultOffloadPolicy = createRequire(import.meta.url)("./bin/tool-result-offload-policy.cjs");
|
|
@@ -260057,7 +260100,110 @@ var init_tool_result_budget = __esmMin((() => {
|
|
|
260057
260100
|
TOOL_RESULT_OFFLOAD_MARKER = toolResultOffloadPolicy.TOOL_RESULT_OFFLOAD_MARKER;
|
|
260058
260101
|
shouldOffloadToolResult = toolResultOffloadPolicy.shouldOffloadToolResult;
|
|
260059
260102
|
createToolResultPreview = toolResultOffloadPolicy.createToolResultPreview;
|
|
260060
|
-
|
|
260103
|
+
selectToolResultBatchOffloads = toolResultOffloadPolicy.selectToolResultBatchOffloads;
|
|
260104
|
+
}));
|
|
260105
|
+
//#endregion
|
|
260106
|
+
//#region ../../packages/agent-core/src/agent/turn/tool-result-batch-offload.ts
|
|
260107
|
+
var ToolResultBatchOffload = class {
|
|
260108
|
+
agent;
|
|
260109
|
+
replacements = /* @__PURE__ */ new Map();
|
|
260110
|
+
constructor(agent) {
|
|
260111
|
+
this.agent = agent;
|
|
260112
|
+
}
|
|
260113
|
+
async detect() {
|
|
260114
|
+
const history = this.agent.context.history;
|
|
260115
|
+
const tail = [];
|
|
260116
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
260117
|
+
const message = history[index];
|
|
260118
|
+
if (message?.role !== "tool") break;
|
|
260119
|
+
tail.unshift(message);
|
|
260120
|
+
}
|
|
260121
|
+
const candidates = tail.map((message) => {
|
|
260122
|
+
if (message.toolCallId === void 0 || this.replacements.has(message.toolCallId) || isPersistedToolResultReference(message.content)) return;
|
|
260123
|
+
const text = persistableToolResultText(message.content);
|
|
260124
|
+
if (text === void 0) return;
|
|
260125
|
+
return {
|
|
260126
|
+
message,
|
|
260127
|
+
text,
|
|
260128
|
+
toolName: this.toolNameFor(history, message.toolCallId)
|
|
260129
|
+
};
|
|
260130
|
+
}).filter((candidate) => candidate !== void 0);
|
|
260131
|
+
const selected = selectToolResultBatchOffloads(candidates.map((candidate) => candidate.text.length));
|
|
260132
|
+
if (selected.length === 0) return 0;
|
|
260133
|
+
const replacements = [];
|
|
260134
|
+
let charsBefore = 0;
|
|
260135
|
+
let charsAfter = 0;
|
|
260136
|
+
for (const index of selected) {
|
|
260137
|
+
const candidate = candidates[index];
|
|
260138
|
+
if (candidate === void 0) continue;
|
|
260139
|
+
const original = {
|
|
260140
|
+
output: candidate.message.content,
|
|
260141
|
+
isError: candidate.message.isError
|
|
260142
|
+
};
|
|
260143
|
+
const persisted = await persistToolResultForModel({
|
|
260144
|
+
homedir: this.agent.homedir,
|
|
260145
|
+
toolName: candidate.toolName,
|
|
260146
|
+
toolCallId: candidate.message.toolCallId,
|
|
260147
|
+
result: original,
|
|
260148
|
+
telemetry: this.agent.telemetry
|
|
260149
|
+
}, candidate.text);
|
|
260150
|
+
if (persisted === original || typeof persisted.output !== "string" || persisted.output.length >= candidate.text.length) continue;
|
|
260151
|
+
const content = [{
|
|
260152
|
+
type: "text",
|
|
260153
|
+
text: persisted.output
|
|
260154
|
+
}];
|
|
260155
|
+
replacements.push({
|
|
260156
|
+
toolCallId: candidate.message.toolCallId,
|
|
260157
|
+
content
|
|
260158
|
+
});
|
|
260159
|
+
charsBefore += candidate.text.length;
|
|
260160
|
+
charsAfter += persisted.output.length;
|
|
260161
|
+
}
|
|
260162
|
+
if (replacements.length === 0) return 0;
|
|
260163
|
+
this.apply(replacements);
|
|
260164
|
+
this.agent.telemetry.track("tool_result_batch_offloaded", {
|
|
260165
|
+
result_count: replacements.length,
|
|
260166
|
+
chars_before: charsBefore,
|
|
260167
|
+
chars_after: charsAfter,
|
|
260168
|
+
chars_saved: charsBefore - charsAfter
|
|
260169
|
+
});
|
|
260170
|
+
return replacements.length;
|
|
260171
|
+
}
|
|
260172
|
+
apply(replacements) {
|
|
260173
|
+
for (const replacement of replacements) this.replacements.set(replacement.toolCallId, replacement.content);
|
|
260174
|
+
this.agent.records.logRecord({
|
|
260175
|
+
type: "tool_result_batch_offload.apply",
|
|
260176
|
+
replacements
|
|
260177
|
+
});
|
|
260178
|
+
}
|
|
260179
|
+
compact(messages) {
|
|
260180
|
+
if (this.replacements.size === 0) return messages;
|
|
260181
|
+
return messages.map((message) => {
|
|
260182
|
+
if (message.role !== "tool" || message.toolCallId === void 0) return message;
|
|
260183
|
+
const content = this.replacements.get(message.toolCallId);
|
|
260184
|
+
return content === void 0 ? message : {
|
|
260185
|
+
...message,
|
|
260186
|
+
content
|
|
260187
|
+
};
|
|
260188
|
+
});
|
|
260189
|
+
}
|
|
260190
|
+
reset(history = this.agent.context?.history ?? []) {
|
|
260191
|
+
const activeIds = new Set(history.filter((message) => message.role === "tool" && message.toolCallId !== void 0).map((message) => message.toolCallId));
|
|
260192
|
+
for (const toolCallId of this.replacements.keys()) if (!activeIds.has(toolCallId)) this.replacements.delete(toolCallId);
|
|
260193
|
+
}
|
|
260194
|
+
clear() {
|
|
260195
|
+
this.replacements.clear();
|
|
260196
|
+
}
|
|
260197
|
+
toolNameFor(history, toolCallId) {
|
|
260198
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
260199
|
+
const message = history[index];
|
|
260200
|
+
if (message?.role !== "assistant" || !Array.isArray(message.toolCalls)) continue;
|
|
260201
|
+
const call = message.toolCalls.find((candidate) => candidate.id === toolCallId);
|
|
260202
|
+
if (call !== void 0) return call.name;
|
|
260203
|
+
}
|
|
260204
|
+
return "Tool";
|
|
260205
|
+
}
|
|
260206
|
+
};
|
|
260061
260207
|
//#endregion
|
|
260062
260208
|
//#region ../../packages/agent-core/src/agent/turn/index.ts
|
|
260063
260209
|
function blunExtractText(content) {
|
|
@@ -260979,7 +261125,9 @@ var init_turn = __esmMin((() => {
|
|
|
260979
261125
|
const loopControl = this.agent.blunConfig?.loopControl;
|
|
260980
261126
|
let stopForGoalBudget = false;
|
|
260981
261127
|
try {
|
|
260982
|
-
const
|
|
261128
|
+
const originTools = blunToolsForOrigin(this.agent.injection.filterPersonalMemoryToolsForTurn(turnId, input, origin, this.agent.tools.loopTools), origin);
|
|
261129
|
+
const compactConversationTool = originTools.find((tool) => tool.name === "CompactConversation");
|
|
261130
|
+
const eligibleTools = originTools.filter((tool) => tool.name !== "CompactConversation" || this.agent.fullCompaction.isProactiveCompactionEligible());
|
|
260983
261131
|
const toolSelection = fastConversation ? {
|
|
260984
261132
|
tools: [...blunFastConversationTools(eligibleTools, input, pendingMediaJobIds)],
|
|
260985
261133
|
deferredToolCount: 0
|
|
@@ -261077,7 +261225,9 @@ var init_turn = __esmMin((() => {
|
|
|
261077
261225
|
previousStepToolOutcome = currentStepHadTool ? currentStepHadFailure ? "failure" : "success" : "none";
|
|
261078
261226
|
this.agent.usage.record(this.agent.activeResponderModel ?? model, usage, "turn");
|
|
261079
261227
|
if (stopForGoalBudget) this.setActiveSteerAcceptance(turnId, false);
|
|
261080
|
-
await this.agent.
|
|
261228
|
+
await this.agent.toolResultBatchOffload.detect();
|
|
261229
|
+
await this.agent.fullCompaction.afterStep(signal);
|
|
261230
|
+
if (compactConversationTool !== void 0 && this.agent.fullCompaction.isProactiveCompactionEligible() && !selectedTools.includes(compactConversationTool)) selectedTools.push(compactConversationTool);
|
|
261081
261231
|
deduper.endStep();
|
|
261082
261232
|
return stopForGoalBudget ? { stopTurn: true } : void 0;
|
|
261083
261233
|
},
|
|
@@ -261434,6 +261584,41 @@ var init_update_goal = __esmMin((() => {
|
|
|
261434
261584
|
};
|
|
261435
261585
|
}));
|
|
261436
261586
|
//#endregion
|
|
261587
|
+
//#region ../../packages/agent-core/src/tools/builtin/compact-conversation.ts
|
|
261588
|
+
var CompactConversationTool;
|
|
261589
|
+
var init_compact_conversation_tool = __esmMin((() => {
|
|
261590
|
+
CompactConversationTool = class {
|
|
261591
|
+
agent;
|
|
261592
|
+
name = "CompactConversation";
|
|
261593
|
+
description = "Compact older conversation messages into a concise continuation summary. Use after finishing a substantial phase, before switching to an unrelated task, or when a long conversation is becoming costly. The tool appears only after the context reaches the safe proactive threshold and takes no arguments.";
|
|
261594
|
+
parameters = {
|
|
261595
|
+
type: "object",
|
|
261596
|
+
properties: {},
|
|
261597
|
+
additionalProperties: false
|
|
261598
|
+
};
|
|
261599
|
+
constructor(agent) {
|
|
261600
|
+
this.agent = agent;
|
|
261601
|
+
}
|
|
261602
|
+
resolveExecution(_args) {
|
|
261603
|
+
return {
|
|
261604
|
+
description: "Scheduling conversation compaction",
|
|
261605
|
+
approvalRule: this.name,
|
|
261606
|
+
execute: async () => {
|
|
261607
|
+
const status = this.agent.fullCompaction.requestProactiveCompaction();
|
|
261608
|
+
if (!status.eligible) return {
|
|
261609
|
+
isError: false,
|
|
261610
|
+
output: status.reason === "already_compacting" ? "Conversation compaction is already running." : "Nothing to compact yet; the conversation is within the safe proactive budget."
|
|
261611
|
+
};
|
|
261612
|
+
return {
|
|
261613
|
+
isError: false,
|
|
261614
|
+
output: `Conversation compaction scheduled at the end of this step (threshold: ${String(status.thresholdTokens)} estimated tokens).`
|
|
261615
|
+
};
|
|
261616
|
+
}
|
|
261617
|
+
};
|
|
261618
|
+
}
|
|
261619
|
+
};
|
|
261620
|
+
}));
|
|
261621
|
+
//#endregion
|
|
261437
261622
|
//#region ../../packages/agent-core/src/tools/builtin/planning/enter-plan-mode.md?raw
|
|
261438
261623
|
var enter_plan_mode_default;
|
|
261439
261624
|
var init_enter_plan_mode$1 = __esmMin((() => {
|
|
@@ -262870,6 +263055,7 @@ var init_tool$1 = __esmMin((() => {
|
|
|
262870
263055
|
init_profile();
|
|
262871
263056
|
init_skill$4();
|
|
262872
263057
|
init_builtin();
|
|
263058
|
+
init_compact_conversation_tool();
|
|
262873
263059
|
init_types$3();
|
|
262874
263060
|
SHELL_FOREGROUND_TIMEOUT_S = 120;
|
|
262875
263061
|
ToolManager = class {
|
|
@@ -263263,6 +263449,7 @@ var init_tool$1 = __esmMin((() => {
|
|
|
263263
263449
|
goalToolsEnabled && new UpdateGoalTool(this.agent),
|
|
263264
263450
|
this.agent.rpc?.requestQuestion && new AskUserQuestionTool(this.agent),
|
|
263265
263451
|
new TodoListTool(this.toolStore),
|
|
263452
|
+
new CompactConversationTool(this.agent),
|
|
263266
263453
|
new TaskListTool(background),
|
|
263267
263454
|
new TaskOutputTool(background),
|
|
263268
263455
|
new TaskStopTool(background),
|
|
@@ -263736,6 +263923,7 @@ var init_agent = __esmMin((() => {
|
|
|
263736
263923
|
records;
|
|
263737
263924
|
fullCompaction;
|
|
263738
263925
|
microCompaction;
|
|
263926
|
+
toolResultBatchOffload;
|
|
263739
263927
|
context;
|
|
263740
263928
|
config;
|
|
263741
263929
|
turn;
|
|
@@ -263793,6 +263981,7 @@ var init_agent = __esmMin((() => {
|
|
|
263793
263981
|
}) : void 0));
|
|
263794
263982
|
this.fullCompaction = new FullCompaction(this, options.compactionStrategy);
|
|
263795
263983
|
this.microCompaction = new MicroCompaction(this, options.microCompaction);
|
|
263984
|
+
this.toolResultBatchOffload = new ToolResultBatchOffload(this);
|
|
263796
263985
|
this.context = new ContextMemory(this);
|
|
263797
263986
|
this.config = new ConfigState(this);
|
|
263798
263987
|
this.turn = new TurnFlow(this);
|