blun-king-cli 9.1.98 → 9.1.100
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/grep-output-limit-policy.cjs +39 -0
- package/bin/tool-result-offload-policy.cjs +38 -0
- package/blun.mjs +169 -19
- 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.100
|
|
13
13
|
|
|
14
14
|
Start
|
|
15
15
|
-----
|
|
@@ -91,10 +91,16 @@ Ab BLUN King 9.1.98 kann King einen abgeschlossenen Arbeitsabschnitt verdichten,
|
|
|
91
91
|
|
|
92
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.
|
|
93
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
|
+
|
|
96
|
+
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.
|
|
97
|
+
|
|
94
98
|
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.
|
|
95
99
|
|
|
96
100
|
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.
|
|
97
101
|
|
|
102
|
+
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.
|
|
103
|
+
|
|
98
104
|
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.
|
|
99
105
|
|
|
100
106
|
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.100
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Reproduzierbares Staging und Packen
|
|
@@ -111,10 +111,16 @@ Ab BLUN King 9.1.98 kann King einen abgeschlossenen Arbeitsabschnitt verdichten,
|
|
|
111
111
|
|
|
112
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.
|
|
113
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
|
+
|
|
116
|
+
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.
|
|
117
|
+
|
|
114
118
|
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.
|
|
115
119
|
|
|
116
120
|
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.
|
|
117
121
|
|
|
122
|
+
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.
|
|
123
|
+
|
|
118
124
|
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.
|
|
119
125
|
|
|
120
126
|
## Rein lesende Sitzungsdiagnose
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function grepContentRecordLimit(args = {}) {
|
|
4
|
+
const mode = args.output_mode ?? "files_with_matches";
|
|
5
|
+
const headLimit = args.head_limit ?? 250;
|
|
6
|
+
const offset = args.offset ?? 0;
|
|
7
|
+
if (mode !== "content" || !Number.isSafeInteger(headLimit) || headLimit <= 0) return undefined;
|
|
8
|
+
if (!Number.isSafeInteger(offset) || offset < 0) return undefined;
|
|
9
|
+
const limit = offset + headLimit + 1;
|
|
10
|
+
return Number.isSafeInteger(limit) ? limit : undefined;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function grepPaginationNotice({ headLimit, offset, afterOffsetLength, earlyStopped = false }) {
|
|
14
|
+
const nextOffset = offset + headLimit;
|
|
15
|
+
const observedTotal = afterOffsetLength + offset;
|
|
16
|
+
const total = earlyStopped ? `at least ${String(observedTotal)}` : String(observedTotal);
|
|
17
|
+
return `Results truncated to ${String(headLimit)} lines (total: ${total}). Use offset=${String(nextOffset)} to see more.`;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function sliceThroughNewlineLimit(buffer, remainingNewlines) {
|
|
21
|
+
if (!Number.isSafeInteger(remainingNewlines) || remainingNewlines <= 0) {
|
|
22
|
+
return { accepted: buffer, newlineCount: 0, stopped: false };
|
|
23
|
+
}
|
|
24
|
+
let newlineCount = 0;
|
|
25
|
+
for (let index = 0; index < buffer.length; index += 1) {
|
|
26
|
+
if (buffer[index] !== 10) continue;
|
|
27
|
+
newlineCount += 1;
|
|
28
|
+
if (newlineCount === remainingNewlines) {
|
|
29
|
+
return { accepted: buffer.subarray(0, index + 1), newlineCount, stopped: true };
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { accepted: buffer, newlineCount, stopped: false };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
module.exports = {
|
|
36
|
+
grepContentRecordLimit,
|
|
37
|
+
grepPaginationNotice,
|
|
38
|
+
sliceThroughNewlineLimit,
|
|
39
|
+
};
|
|
@@ -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
|
@@ -78757,6 +78757,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78757
78757
|
if (removedMessages.size === 0) return 0;
|
|
78758
78758
|
this.agent.replayBuilder.removeLastMessages(removedMessages);
|
|
78759
78759
|
this.agent.microCompaction.reset(this._history.length);
|
|
78760
|
+
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78760
78761
|
this.agent.emitStatusUpdated();
|
|
78761
78762
|
return removedMessages.size;
|
|
78762
78763
|
}
|
|
@@ -78770,6 +78771,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78770
78771
|
this.deferredMessages = [];
|
|
78771
78772
|
this._lastAssistantAt = null;
|
|
78772
78773
|
this.agent.microCompaction.reset();
|
|
78774
|
+
this.agent.toolResultBatchOffload.clear();
|
|
78773
78775
|
this.agent.injection.onContextClear();
|
|
78774
78776
|
this.agent.emitStatusUpdated();
|
|
78775
78777
|
}
|
|
@@ -78808,6 +78810,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78808
78810
|
this.pendingToolResultIds.clear();
|
|
78809
78811
|
this.deferredMessages = [];
|
|
78810
78812
|
this.agent.microCompaction.reset(this._history.length);
|
|
78813
|
+
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78811
78814
|
this.agent.emitStatusUpdated();
|
|
78812
78815
|
if (!this.agent.records.restoring && (stoppedAtBoundary || removedUserCount < count)) throw new BlunError(ErrorCodes.REQUEST_INVALID, formatUndoUnavailableMessage(count, removedUserCount, stoppedAtBoundary), { details: {
|
|
78813
78816
|
reason: "undo_limit",
|
|
@@ -78886,6 +78889,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78886
78889
|
this._tokenCount = result.tokensAfter;
|
|
78887
78890
|
this.tokenCountCoveredMessageCount = this._history.length;
|
|
78888
78891
|
this.agent.microCompaction.reset();
|
|
78892
|
+
this.agent.toolResultBatchOffload.reset(this._history);
|
|
78889
78893
|
this.agent.injection.onContextCompacted();
|
|
78890
78894
|
this.agent.emitStatusUpdated();
|
|
78891
78895
|
return result;
|
|
@@ -78908,7 +78912,7 @@ var init_context$2 = __esmMin((() => {
|
|
|
78908
78912
|
}
|
|
78909
78913
|
project(messages, options) {
|
|
78910
78914
|
const anomalies = [];
|
|
78911
|
-
const result = project(this.agent.microCompaction.compact(messages), {
|
|
78915
|
+
const result = project(this.agent.microCompaction.compact(this.agent.toolResultBatchOffload.compact(messages)), {
|
|
78912
78916
|
...options,
|
|
78913
78917
|
onAnomaly: (anomaly) => {
|
|
78914
78918
|
anomalies.push(anomaly);
|
|
@@ -234828,6 +234832,9 @@ function restoreAgentRecord(agent, input) {
|
|
|
234828
234832
|
case "micro_compaction.apply":
|
|
234829
234833
|
agent.microCompaction.apply(input.cutoff);
|
|
234830
234834
|
return;
|
|
234835
|
+
case "tool_result_batch_offload.apply":
|
|
234836
|
+
agent.toolResultBatchOffload.apply(input.replacements);
|
|
234837
|
+
return;
|
|
234831
234838
|
case "plan_mode.enter":
|
|
234832
234839
|
agent.planMode.restoreEnter(input);
|
|
234833
234840
|
return;
|
|
@@ -257658,10 +257665,17 @@ async function runRipgrepOnce(kaos, rgArgs, signal, options = {}) {
|
|
|
257658
257665
|
let stderrText = "";
|
|
257659
257666
|
let bufferTruncated = false;
|
|
257660
257667
|
let stderrTruncated = false;
|
|
257668
|
+
let earlyStopped = false;
|
|
257661
257669
|
try {
|
|
257662
|
-
const isTerminating = () => timedOut || aborted || killed;
|
|
257670
|
+
const isTerminating = () => timedOut || aborted || killed || earlyStopped;
|
|
257663
257671
|
const [stdoutResult, stderrResult, code] = await Promise.all([
|
|
257664
|
-
readStreamWithCap(proc.stdout, MAX_OUTPUT_BYTES$1, isTerminating
|
|
257672
|
+
readStreamWithCap(proc.stdout, MAX_OUTPUT_BYTES$1, isTerminating, {
|
|
257673
|
+
stopAfterDelimiterCount: options.stdoutRecordLimit,
|
|
257674
|
+
onStop: () => {
|
|
257675
|
+
earlyStopped = true;
|
|
257676
|
+
void killProc();
|
|
257677
|
+
}
|
|
257678
|
+
}),
|
|
257665
257679
|
readStreamWithCap(proc.stderr, MAX_OUTPUT_BYTES$1, isTerminating),
|
|
257666
257680
|
proc.wait()
|
|
257667
257681
|
]);
|
|
@@ -257697,7 +257711,8 @@ async function runRipgrepOnce(kaos, rgArgs, signal, options = {}) {
|
|
|
257697
257711
|
stderrText,
|
|
257698
257712
|
bufferTruncated,
|
|
257699
257713
|
stderrTruncated,
|
|
257700
|
-
timedOut
|
|
257714
|
+
timedOut,
|
|
257715
|
+
earlyStopped
|
|
257701
257716
|
};
|
|
257702
257717
|
}
|
|
257703
257718
|
function shouldRetryRipgrepEagain(result) {
|
|
@@ -257706,23 +257721,37 @@ function shouldRetryRipgrepEagain(result) {
|
|
|
257706
257721
|
function isEagainRipgrepError(stderr) {
|
|
257707
257722
|
return stderr.includes("os error 11") || stderr.includes("Resource temporarily unavailable");
|
|
257708
257723
|
}
|
|
257709
|
-
async function readStreamWithCap(stream, maxBytes, suppressPrematureClose) {
|
|
257724
|
+
async function readStreamWithCap(stream, maxBytes, suppressPrematureClose, options = {}) {
|
|
257710
257725
|
const chunks = [];
|
|
257711
257726
|
let total = 0;
|
|
257712
257727
|
let truncated = false;
|
|
257728
|
+
let delimiterCount = 0;
|
|
257713
257729
|
try {
|
|
257714
257730
|
for await (const chunk of stream) {
|
|
257715
257731
|
const buf = typeof chunk === "string" ? Buffer.from(chunk, "utf8") : chunk;
|
|
257716
257732
|
if (truncated) continue;
|
|
257717
|
-
|
|
257733
|
+
let accepted = buf;
|
|
257734
|
+
let stoppedAtRecordLimit = false;
|
|
257735
|
+
const delimiterLimit = options.stopAfterDelimiterCount;
|
|
257736
|
+
if (Number.isSafeInteger(delimiterLimit) && delimiterLimit > 0) {
|
|
257737
|
+
const slice = sliceThroughNewlineLimit(buf, delimiterLimit - delimiterCount);
|
|
257738
|
+
accepted = slice.accepted;
|
|
257739
|
+
delimiterCount += slice.newlineCount;
|
|
257740
|
+
stoppedAtRecordLimit = slice.stopped;
|
|
257741
|
+
}
|
|
257742
|
+
if (total + accepted.length > maxBytes) {
|
|
257718
257743
|
const remaining = maxBytes - total;
|
|
257719
|
-
if (remaining > 0) chunks.push(
|
|
257744
|
+
if (remaining > 0) chunks.push(accepted.subarray(0, remaining));
|
|
257720
257745
|
total = maxBytes;
|
|
257721
257746
|
truncated = true;
|
|
257722
257747
|
continue;
|
|
257723
257748
|
}
|
|
257724
|
-
chunks.push(
|
|
257725
|
-
total +=
|
|
257749
|
+
chunks.push(accepted);
|
|
257750
|
+
total += accepted.length;
|
|
257751
|
+
if (stoppedAtRecordLimit) {
|
|
257752
|
+
options.onStop?.();
|
|
257753
|
+
break;
|
|
257754
|
+
}
|
|
257726
257755
|
}
|
|
257727
257756
|
} catch (error) {
|
|
257728
257757
|
if (!isPrematureCloseError(error) || suppressPrematureClose?.() !== true) throw error;
|
|
@@ -258351,7 +258380,7 @@ function countPayloadFromLegacyLine(line) {
|
|
|
258351
258380
|
const idx = line.lastIndexOf(":");
|
|
258352
258381
|
return idx > 0 ? line.slice(idx + 1) : void 0;
|
|
258353
258382
|
}
|
|
258354
|
-
var GrepInputSchema, RG_MAX_COLUMNS, DEFAULT_HEAD_LIMIT, MTIME_STAT_CONCURRENCY, CONTENT_LINE_RE, GrepTool, GrepAbortedError;
|
|
258383
|
+
var GrepInputSchema, RG_MAX_COLUMNS, DEFAULT_HEAD_LIMIT, MTIME_STAT_CONCURRENCY, CONTENT_LINE_RE, GrepTool, GrepAbortedError, grepContentRecordLimit, grepPaginationNotice, sliceThroughNewlineLimit;
|
|
258355
258384
|
var init_grep = __esmMin((() => {
|
|
258356
258385
|
init_dist$6();
|
|
258357
258386
|
init_zod$1();
|
|
@@ -258366,6 +258395,7 @@ var init_grep = __esmMin((() => {
|
|
|
258366
258395
|
init_result_builder();
|
|
258367
258396
|
init_run_rg();
|
|
258368
258397
|
init_grep$1();
|
|
258398
|
+
({ grepContentRecordLimit, grepPaginationNotice, sliceThroughNewlineLimit } = createRequire(import.meta.url)("./bin/grep-output-limit-policy.cjs"));
|
|
258369
258399
|
GrepInputSchema = object({
|
|
258370
258400
|
pattern: string().describe("Regular expression to search for."),
|
|
258371
258401
|
path: string().optional().describe("File or directory to search. Accepts an absolute path, or a path relative to the current working directory. Omit to search the current working directory. Use Read instead when you already know a concrete file path and need its contents."),
|
|
@@ -258466,15 +258496,21 @@ var init_grep = __esmMin((() => {
|
|
|
258466
258496
|
output: rgUnavailableMessage(error)
|
|
258467
258497
|
};
|
|
258468
258498
|
}
|
|
258469
|
-
let runResult = await runRipgrepOnce(this.kaos, buildRgArgs(rgPath, args, searchPaths), signal, {
|
|
258499
|
+
let runResult = await runRipgrepOnce(this.kaos, buildRgArgs(rgPath, args, searchPaths), signal, {
|
|
258500
|
+
abortedMessage: "Grep aborted",
|
|
258501
|
+
stdoutRecordLimit: grepContentRecordLimit(args)
|
|
258502
|
+
});
|
|
258470
258503
|
if (runResult.kind === "tool-error") return runResult.result;
|
|
258471
258504
|
if (shouldRetryRipgrepEagain(runResult)) {
|
|
258472
|
-
runResult = await runRipgrepOnce(this.kaos, buildRgArgs(rgPath, args, searchPaths, true), signal, {
|
|
258505
|
+
runResult = await runRipgrepOnce(this.kaos, buildRgArgs(rgPath, args, searchPaths, true), signal, {
|
|
258506
|
+
abortedMessage: "Grep aborted",
|
|
258507
|
+
stdoutRecordLimit: grepContentRecordLimit(args)
|
|
258508
|
+
});
|
|
258473
258509
|
if (runResult.kind === "tool-error") return runResult.result;
|
|
258474
258510
|
}
|
|
258475
|
-
const { exitCode, stderrText, bufferTruncated, stderrTruncated, timedOut } = runResult;
|
|
258511
|
+
const { exitCode, stderrText, bufferTruncated, stderrTruncated, timedOut, earlyStopped } = runResult;
|
|
258476
258512
|
let { stdoutText } = runResult;
|
|
258477
|
-
if (exitCode !== 0 && exitCode !== 1 && !timedOut) return {
|
|
258513
|
+
if (exitCode !== 0 && exitCode !== 1 && !timedOut && !earlyStopped) return {
|
|
258478
258514
|
isError: true,
|
|
258479
258515
|
output: formatRipgrepError(exitCode, stderrText, stderrTruncated)
|
|
258480
258516
|
};
|
|
@@ -258515,9 +258551,12 @@ var init_grep = __esmMin((() => {
|
|
|
258515
258551
|
}
|
|
258516
258552
|
if (mode === "count_matches" && orderedLines.length > 0) headerLines.push(formatCountSummary(orderedLines, filteredSensitive.size > 0));
|
|
258517
258553
|
if (paginationTruncated) {
|
|
258518
|
-
const
|
|
258519
|
-
|
|
258520
|
-
|
|
258554
|
+
const paginationNotice = grepPaginationNotice({
|
|
258555
|
+
headLimit,
|
|
258556
|
+
offset,
|
|
258557
|
+
afterOffsetLength: afterOffset.length,
|
|
258558
|
+
earlyStopped
|
|
258559
|
+
});
|
|
258521
258560
|
if (mode === "count_matches") headerLines.push(paginationNotice);
|
|
258522
258561
|
else messages.push(paginationNotice);
|
|
258523
258562
|
}
|
|
@@ -260015,6 +260054,11 @@ var init_tool_dedup = __esmMin((() => {
|
|
|
260015
260054
|
async function budgetToolResultForModel(options) {
|
|
260016
260055
|
const text = persistableToolResultText(options.result.output);
|
|
260017
260056
|
if (text === void 0 || !shouldOffloadToolResult(text.length)) return options.result;
|
|
260057
|
+
return persistToolResultForModel(options, text);
|
|
260058
|
+
}
|
|
260059
|
+
async function persistToolResultForModel(options, knownText) {
|
|
260060
|
+
const text = knownText ?? persistableToolResultText(options.result.output);
|
|
260061
|
+
if (text === void 0) return options.result;
|
|
260018
260062
|
if (options.result.truncated === true) return options.result;
|
|
260019
260063
|
if (options.homedir === void 0) return options.result;
|
|
260020
260064
|
const outputPath = await saveToolResult({
|
|
@@ -260078,7 +260122,7 @@ function renderPersistedToolResult(toolName, toolCallId, text, outputPath) {
|
|
|
260078
260122
|
function safeToolResultFileStem(toolName, toolCallId) {
|
|
260079
260123
|
return `${toolName}-${toolCallId}`.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 80) || "tool-result";
|
|
260080
260124
|
}
|
|
260081
|
-
var TOOL_RESULT_MAX_CHARS, TOOL_RESULT_PREVIEW_CHARS, TOOL_RESULT_OFFLOAD_MARKER, shouldOffloadToolResult, createToolResultPreview, buildToolResultOffloadTelemetry;
|
|
260125
|
+
var TOOL_RESULT_MAX_CHARS, TOOL_RESULT_PREVIEW_CHARS, TOOL_RESULT_OFFLOAD_MARKER, shouldOffloadToolResult, createToolResultPreview, selectToolResultBatchOffloads, buildToolResultOffloadTelemetry;
|
|
260082
260126
|
var init_tool_result_budget = __esmMin((() => {
|
|
260083
260127
|
init_dist$6();
|
|
260084
260128
|
const toolResultOffloadPolicy = createRequire(import.meta.url)("./bin/tool-result-offload-policy.cjs");
|
|
@@ -260088,7 +260132,110 @@ var init_tool_result_budget = __esmMin((() => {
|
|
|
260088
260132
|
TOOL_RESULT_OFFLOAD_MARKER = toolResultOffloadPolicy.TOOL_RESULT_OFFLOAD_MARKER;
|
|
260089
260133
|
shouldOffloadToolResult = toolResultOffloadPolicy.shouldOffloadToolResult;
|
|
260090
260134
|
createToolResultPreview = toolResultOffloadPolicy.createToolResultPreview;
|
|
260091
|
-
|
|
260135
|
+
selectToolResultBatchOffloads = toolResultOffloadPolicy.selectToolResultBatchOffloads;
|
|
260136
|
+
}));
|
|
260137
|
+
//#endregion
|
|
260138
|
+
//#region ../../packages/agent-core/src/agent/turn/tool-result-batch-offload.ts
|
|
260139
|
+
var ToolResultBatchOffload = class {
|
|
260140
|
+
agent;
|
|
260141
|
+
replacements = /* @__PURE__ */ new Map();
|
|
260142
|
+
constructor(agent) {
|
|
260143
|
+
this.agent = agent;
|
|
260144
|
+
}
|
|
260145
|
+
async detect() {
|
|
260146
|
+
const history = this.agent.context.history;
|
|
260147
|
+
const tail = [];
|
|
260148
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
260149
|
+
const message = history[index];
|
|
260150
|
+
if (message?.role !== "tool") break;
|
|
260151
|
+
tail.unshift(message);
|
|
260152
|
+
}
|
|
260153
|
+
const candidates = tail.map((message) => {
|
|
260154
|
+
if (message.toolCallId === void 0 || this.replacements.has(message.toolCallId) || isPersistedToolResultReference(message.content)) return;
|
|
260155
|
+
const text = persistableToolResultText(message.content);
|
|
260156
|
+
if (text === void 0) return;
|
|
260157
|
+
return {
|
|
260158
|
+
message,
|
|
260159
|
+
text,
|
|
260160
|
+
toolName: this.toolNameFor(history, message.toolCallId)
|
|
260161
|
+
};
|
|
260162
|
+
}).filter((candidate) => candidate !== void 0);
|
|
260163
|
+
const selected = selectToolResultBatchOffloads(candidates.map((candidate) => candidate.text.length));
|
|
260164
|
+
if (selected.length === 0) return 0;
|
|
260165
|
+
const replacements = [];
|
|
260166
|
+
let charsBefore = 0;
|
|
260167
|
+
let charsAfter = 0;
|
|
260168
|
+
for (const index of selected) {
|
|
260169
|
+
const candidate = candidates[index];
|
|
260170
|
+
if (candidate === void 0) continue;
|
|
260171
|
+
const original = {
|
|
260172
|
+
output: candidate.message.content,
|
|
260173
|
+
isError: candidate.message.isError
|
|
260174
|
+
};
|
|
260175
|
+
const persisted = await persistToolResultForModel({
|
|
260176
|
+
homedir: this.agent.homedir,
|
|
260177
|
+
toolName: candidate.toolName,
|
|
260178
|
+
toolCallId: candidate.message.toolCallId,
|
|
260179
|
+
result: original,
|
|
260180
|
+
telemetry: this.agent.telemetry
|
|
260181
|
+
}, candidate.text);
|
|
260182
|
+
if (persisted === original || typeof persisted.output !== "string" || persisted.output.length >= candidate.text.length) continue;
|
|
260183
|
+
const content = [{
|
|
260184
|
+
type: "text",
|
|
260185
|
+
text: persisted.output
|
|
260186
|
+
}];
|
|
260187
|
+
replacements.push({
|
|
260188
|
+
toolCallId: candidate.message.toolCallId,
|
|
260189
|
+
content
|
|
260190
|
+
});
|
|
260191
|
+
charsBefore += candidate.text.length;
|
|
260192
|
+
charsAfter += persisted.output.length;
|
|
260193
|
+
}
|
|
260194
|
+
if (replacements.length === 0) return 0;
|
|
260195
|
+
this.apply(replacements);
|
|
260196
|
+
this.agent.telemetry.track("tool_result_batch_offloaded", {
|
|
260197
|
+
result_count: replacements.length,
|
|
260198
|
+
chars_before: charsBefore,
|
|
260199
|
+
chars_after: charsAfter,
|
|
260200
|
+
chars_saved: charsBefore - charsAfter
|
|
260201
|
+
});
|
|
260202
|
+
return replacements.length;
|
|
260203
|
+
}
|
|
260204
|
+
apply(replacements) {
|
|
260205
|
+
for (const replacement of replacements) this.replacements.set(replacement.toolCallId, replacement.content);
|
|
260206
|
+
this.agent.records.logRecord({
|
|
260207
|
+
type: "tool_result_batch_offload.apply",
|
|
260208
|
+
replacements
|
|
260209
|
+
});
|
|
260210
|
+
}
|
|
260211
|
+
compact(messages) {
|
|
260212
|
+
if (this.replacements.size === 0) return messages;
|
|
260213
|
+
return messages.map((message) => {
|
|
260214
|
+
if (message.role !== "tool" || message.toolCallId === void 0) return message;
|
|
260215
|
+
const content = this.replacements.get(message.toolCallId);
|
|
260216
|
+
return content === void 0 ? message : {
|
|
260217
|
+
...message,
|
|
260218
|
+
content
|
|
260219
|
+
};
|
|
260220
|
+
});
|
|
260221
|
+
}
|
|
260222
|
+
reset(history = this.agent.context?.history ?? []) {
|
|
260223
|
+
const activeIds = new Set(history.filter((message) => message.role === "tool" && message.toolCallId !== void 0).map((message) => message.toolCallId));
|
|
260224
|
+
for (const toolCallId of this.replacements.keys()) if (!activeIds.has(toolCallId)) this.replacements.delete(toolCallId);
|
|
260225
|
+
}
|
|
260226
|
+
clear() {
|
|
260227
|
+
this.replacements.clear();
|
|
260228
|
+
}
|
|
260229
|
+
toolNameFor(history, toolCallId) {
|
|
260230
|
+
for (let index = history.length - 1; index >= 0; index -= 1) {
|
|
260231
|
+
const message = history[index];
|
|
260232
|
+
if (message?.role !== "assistant" || !Array.isArray(message.toolCalls)) continue;
|
|
260233
|
+
const call = message.toolCalls.find((candidate) => candidate.id === toolCallId);
|
|
260234
|
+
if (call !== void 0) return call.name;
|
|
260235
|
+
}
|
|
260236
|
+
return "Tool";
|
|
260237
|
+
}
|
|
260238
|
+
};
|
|
260092
260239
|
//#endregion
|
|
260093
260240
|
//#region ../../packages/agent-core/src/agent/turn/index.ts
|
|
260094
260241
|
function blunExtractText(content) {
|
|
@@ -261110,6 +261257,7 @@ var init_turn = __esmMin((() => {
|
|
|
261110
261257
|
previousStepToolOutcome = currentStepHadTool ? currentStepHadFailure ? "failure" : "success" : "none";
|
|
261111
261258
|
this.agent.usage.record(this.agent.activeResponderModel ?? model, usage, "turn");
|
|
261112
261259
|
if (stopForGoalBudget) this.setActiveSteerAcceptance(turnId, false);
|
|
261260
|
+
await this.agent.toolResultBatchOffload.detect();
|
|
261113
261261
|
await this.agent.fullCompaction.afterStep(signal);
|
|
261114
261262
|
if (compactConversationTool !== void 0 && this.agent.fullCompaction.isProactiveCompactionEligible() && !selectedTools.includes(compactConversationTool)) selectedTools.push(compactConversationTool);
|
|
261115
261263
|
deduper.endStep();
|
|
@@ -263807,6 +263955,7 @@ var init_agent = __esmMin((() => {
|
|
|
263807
263955
|
records;
|
|
263808
263956
|
fullCompaction;
|
|
263809
263957
|
microCompaction;
|
|
263958
|
+
toolResultBatchOffload;
|
|
263810
263959
|
context;
|
|
263811
263960
|
config;
|
|
263812
263961
|
turn;
|
|
@@ -263864,6 +264013,7 @@ var init_agent = __esmMin((() => {
|
|
|
263864
264013
|
}) : void 0));
|
|
263865
264014
|
this.fullCompaction = new FullCompaction(this, options.compactionStrategy);
|
|
263866
264015
|
this.microCompaction = new MicroCompaction(this, options.microCompaction);
|
|
264016
|
+
this.toolResultBatchOffload = new ToolResultBatchOffload(this);
|
|
263867
264017
|
this.context = new ContextMemory(this);
|
|
263868
264018
|
this.config = new ConfigState(this);
|
|
263869
264019
|
this.turn = new TurnFlow(this);
|