blun-king-cli 9.1.102 → 9.1.104
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 +3 -1
- package/README.md +3 -1
- package/bin/foreground-output-capture-policy.cjs +41 -0
- package/bin/subagent-timeout-policy.cjs +16 -1
- package/blun.mjs +65 -20
- package/package.json +1 -1
package/LIESMICH.txt
CHANGED
|
@@ -91,6 +91,8 @@ 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
|
+
Version 9.1.104 lässt Shell-Befehle im Vordergrund auch bei sehr großen Ausgaben bis zum regulären Ende laufen. King schreibt bis zu 16 MiB fortlaufend in das private Aufgabenprotokoll, verwirft darüber hinausgehende Ausgaben und meldet den tatsächlichen Exit-Code, statt den Prozess wegen der Ausgabemenge zu beenden. Kleine Ausgaben, Hintergrundaufgaben, Zeitgrenzen und manuelle Abbrüche bleiben unverändert.
|
|
95
|
+
|
|
94
96
|
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
97
|
|
|
96
98
|
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.
|
|
@@ -103,7 +105,7 @@ Das Telemetrieereignis `tool_result_batch_offloaded` meldet ausschließlich die
|
|
|
103
105
|
|
|
104
106
|
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.
|
|
105
107
|
|
|
106
|
-
Verliert ein delegierter Einzelagent die Provider-Verbindung, erhält
|
|
108
|
+
Verliert ein delegierter Einzelagent oder ein Mitglied eines Agentenschwarms die Provider-Verbindung, erhält es eine leere Provider-Antwort, bricht sein Stream wegen Leerlaufs ab oder meldet der Server HTTP 408/500/502/503/504, setzt BLUN King denselben Agenten anhand seines dauerhaften Verlaufs fort. Bei Einzelagenten sowie bei Schwarm-Unterbrechungen außerhalb von HTTP 429 wartet die erste Fortsetzung zwei Sekunden und die zweite fünf Sekunden; danach bleibt der ursprüngliche Fehler sichtbar. HTTP 429 behält im Schwarm seine getrennte, längere Überlastungsregel. Authentifizierungs-, Zahlungs- oder Kontingent-, Richtlinien-, Kontext-, Werkzeug- und Codefehler sowie manuelle Abbrüche lösen keine automatische Fortsetzung aus. Jeder Fortsetzungsversuch verwendet die bestehende Agenten-ID, damit bereits abgeschlossene Arbeit nicht wiederholt wird.
|
|
107
109
|
|
|
108
110
|
Rein lesende Sitzungsdiagnose
|
|
109
111
|
--------------------------------
|
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.104
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Reproduzierbares Staging und Packen
|
|
@@ -111,6 +111,8 @@ 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
|
+
Version 9.1.104 lässt Shell-Befehle im Vordergrund auch bei sehr großen Ausgaben bis zum regulären Ende laufen. King schreibt bis zu 16 MiB fortlaufend in das private Aufgabenprotokoll, verwirft darüber hinausgehende Ausgaben und meldet den tatsächlichen Exit-Code, statt den Prozess wegen der Ausgabemenge zu beenden. Kleine Ausgaben, Hintergrundaufgaben, Zeitgrenzen und manuelle Abbrüche bleiben unverändert.
|
|
115
|
+
|
|
114
116
|
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
117
|
|
|
116
118
|
Ab BLUN King 9.1.100 beendet eine begrenzte Grep-Inhaltssuche ripgrep, sobald der Versatz, die angeforderten Zeilen und eine zusätzliche Vorschauzeile vollständig vorliegen. Die Vorschauzeile belegt, ob eine weitere Seite existiert, ohne vorher bis zu 10 MB einzulesen. Unbegrenzte Suchen, Trefferzählungen und nach Änderungszeit sortierte Dateilisten laufen weiterhin vollständig durch.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MAX_FOREGROUND_CAPTURE_BYTES = 16 * 1024 * 1024;
|
|
4
|
+
|
|
5
|
+
function utf8PrefixWithinBytes(text, maxBytes) {
|
|
6
|
+
if (maxBytes <= 0 || text.length === 0) return '';
|
|
7
|
+
if (Buffer.byteLength(text, 'utf8') <= maxBytes) return text;
|
|
8
|
+
|
|
9
|
+
let low = 0;
|
|
10
|
+
let high = text.length;
|
|
11
|
+
while (low < high) {
|
|
12
|
+
const middle = Math.ceil((low + high) / 2);
|
|
13
|
+
if (Buffer.byteLength(text.slice(0, middle), 'utf8') <= maxBytes) low = middle;
|
|
14
|
+
else high = middle - 1;
|
|
15
|
+
}
|
|
16
|
+
if (low > 0 && low < text.length) {
|
|
17
|
+
const last = text.charCodeAt(low - 1);
|
|
18
|
+
if (last >= 0xD800 && last <= 0xDBFF) low -= 1;
|
|
19
|
+
}
|
|
20
|
+
return text.slice(0, low);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function captureForegroundOutputChunk(text, capturedBytes, maxBytes = MAX_FOREGROUND_CAPTURE_BYTES) {
|
|
24
|
+
const observedBytes = Buffer.byteLength(text, 'utf8');
|
|
25
|
+
const remainingBytes = Math.max(0, maxBytes - Math.max(0, capturedBytes));
|
|
26
|
+
const capturedText = utf8PrefixWithinBytes(text, remainingBytes);
|
|
27
|
+
const newCapturedBytes = Buffer.byteLength(capturedText, 'utf8');
|
|
28
|
+
return {
|
|
29
|
+
capturedText,
|
|
30
|
+
observedBytes,
|
|
31
|
+
capturedBytes: newCapturedBytes,
|
|
32
|
+
droppedBytes: observedBytes - newCapturedBytes,
|
|
33
|
+
capped: newCapturedBytes < observedBytes,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
MAX_FOREGROUND_CAPTURE_BYTES,
|
|
39
|
+
captureForegroundOutputChunk,
|
|
40
|
+
utf8PrefixWithinBytes,
|
|
41
|
+
};
|
|
@@ -74,6 +74,18 @@ function isRetryableSubagentFailure(error) {
|
|
|
74
74
|
return RETRYABLE_MESSAGE_RE.test(message);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
function transientSubagentRetryDelayMs(retryCount) {
|
|
78
|
+
if (!Number.isInteger(retryCount) || retryCount < 1) {
|
|
79
|
+
throw new Error("retryCount must be a positive integer.");
|
|
80
|
+
}
|
|
81
|
+
return DEFAULT_TRANSIENT_RETRY_DELAYS_MS[retryCount - 1]
|
|
82
|
+
?? DEFAULT_TRANSIENT_RETRY_DELAYS_MS.at(-1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function shouldStopTransientSubagentContinuation(retryCount) {
|
|
86
|
+
return retryCount >= MAX_TRANSIENT_SUBAGENT_CONTINUATIONS;
|
|
87
|
+
}
|
|
88
|
+
|
|
77
89
|
function waitForRetryDelay(delayMs, signal) {
|
|
78
90
|
if (delayMs <= 0) return Promise.resolve();
|
|
79
91
|
if (signal.aborted) return Promise.reject(abortError(signal.reason));
|
|
@@ -134,7 +146,8 @@ async function runAgentWithTimeoutContinuation({
|
|
|
134
146
|
throw result.error;
|
|
135
147
|
}
|
|
136
148
|
const attempt = transientContinuations + 1;
|
|
137
|
-
const delayMs = retryDelaysMs[transientContinuations]
|
|
149
|
+
const delayMs = retryDelaysMs[transientContinuations]
|
|
150
|
+
?? transientSubagentRetryDelayMs(attempt);
|
|
138
151
|
transientContinuations = attempt;
|
|
139
152
|
await waitForRetryDelay(delayMs, signal);
|
|
140
153
|
currentHandle = await retrySameAgent(currentHandle.agentId, {
|
|
@@ -164,4 +177,6 @@ module.exports = {
|
|
|
164
177
|
isRetryableSubagentFailure,
|
|
165
178
|
resolveSubagentTimeoutMs,
|
|
166
179
|
runAgentWithTimeoutContinuation,
|
|
180
|
+
shouldStopTransientSubagentContinuation,
|
|
181
|
+
transientSubagentRetryDelayMs,
|
|
167
182
|
};
|
package/blun.mjs
CHANGED
|
@@ -29316,9 +29316,6 @@ function isBackgroundTaskTerminal(status) {
|
|
|
29316
29316
|
return TERMINAL_STATUSES.has(status);
|
|
29317
29317
|
}
|
|
29318
29318
|
/** Terminal `stopReason` recorded when a foreground command trips the output ceiling. */
|
|
29319
|
-
function foregroundOutputLimitReason() {
|
|
29320
|
-
return `Output limit exceeded: the command produced more than ${Math.floor(MAX_FOREGROUND_OUTPUT_BYTES / (1024 * 1024))} MiB and was terminated. Redirect large output to a file (e.g. \`command > out.txt\`) and inspect it in slices instead.`;
|
|
29321
|
-
}
|
|
29322
29319
|
/**
|
|
29323
29320
|
* Generate `{prefix}-{8 base36 chars}`.
|
|
29324
29321
|
*
|
|
@@ -29378,7 +29375,7 @@ function buildBackgroundTaskNotificationBody(info) {
|
|
|
29378
29375
|
"The subagent retains its full prior context across the restart, but any in-flight tool call lost its result and may need to be redone."
|
|
29379
29376
|
].join("\n")}`;
|
|
29380
29377
|
}
|
|
29381
|
-
var MAX_OUTPUT_BYTES$2, NOTIFICATION_FALLBACK_PREVIEW_BYTES,
|
|
29378
|
+
var MAX_OUTPUT_BYTES$2, NOTIFICATION_FALLBACK_PREVIEW_BYTES, MAX_FOREGROUND_CAPTURE_BYTES, captureForegroundOutputChunk, SIGTERM_GRACE_MS$1, USER_INTERRUPT_REASON$1, _ALPHABET, BackgroundManager;
|
|
29382
29379
|
var init_background = __esmMin((() => {
|
|
29383
29380
|
init_dist$4();
|
|
29384
29381
|
init_errors$4();
|
|
@@ -29392,7 +29389,7 @@ var init_background = __esmMin((() => {
|
|
|
29392
29389
|
init_question_task();
|
|
29393
29390
|
MAX_OUTPUT_BYTES$2 = 1024 * 1024;
|
|
29394
29391
|
NOTIFICATION_FALLBACK_PREVIEW_BYTES = 3e3;
|
|
29395
|
-
|
|
29392
|
+
({ MAX_FOREGROUND_CAPTURE_BYTES, captureForegroundOutputChunk } = createRequire(import.meta.url)("./bin/foreground-output-capture-policy.cjs"));
|
|
29396
29393
|
SIGTERM_GRACE_MS$1 = 5e3;
|
|
29397
29394
|
USER_INTERRUPT_REASON$1 = "Interrupted by user";
|
|
29398
29395
|
_ALPHABET = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
@@ -29470,7 +29467,8 @@ var init_background = __esmMin((() => {
|
|
|
29470
29467
|
outputChunks: [],
|
|
29471
29468
|
outputRingChars: 0,
|
|
29472
29469
|
outputSizeBytes: 0,
|
|
29473
|
-
|
|
29470
|
+
outputCapturedBytes: 0,
|
|
29471
|
+
outputCaptureCapped: false,
|
|
29474
29472
|
status: "running",
|
|
29475
29473
|
options: entryOptions,
|
|
29476
29474
|
startedAt: Date.now(),
|
|
@@ -29708,27 +29706,32 @@ var init_background = __esmMin((() => {
|
|
|
29708
29706
|
return entry.persistWriteQueue;
|
|
29709
29707
|
}
|
|
29710
29708
|
appendOutput(entry, chunk) {
|
|
29711
|
-
|
|
29712
|
-
|
|
29713
|
-
|
|
29709
|
+
const capture = this.isDetached(entry) ? {
|
|
29710
|
+
capturedText: chunk,
|
|
29711
|
+
observedBytes: Buffer.byteLength(chunk, "utf-8"),
|
|
29712
|
+
capturedBytes: Buffer.byteLength(chunk, "utf-8"),
|
|
29713
|
+
droppedBytes: 0,
|
|
29714
|
+
capped: false
|
|
29715
|
+
} : captureForegroundOutputChunk(chunk, entry.outputCapturedBytes);
|
|
29716
|
+
entry.outputSizeBytes += capture.observedBytes;
|
|
29717
|
+
entry.outputCapturedBytes += capture.capturedBytes;
|
|
29718
|
+
if (capture.capped) entry.outputCaptureCapped = true;
|
|
29719
|
+
if (capture.capturedText.length === 0) return;
|
|
29720
|
+
entry.outputChunks.push(capture.capturedText);
|
|
29721
|
+
entry.outputRingChars += capture.capturedText.length;
|
|
29714
29722
|
while (entry.outputRingChars > MAX_OUTPUT_BYTES$2 && entry.outputChunks.length > 1) {
|
|
29715
29723
|
const removed = entry.outputChunks.shift();
|
|
29716
29724
|
if (removed === void 0) break;
|
|
29717
29725
|
entry.outputRingChars -= removed.length;
|
|
29718
29726
|
}
|
|
29719
|
-
if (!entry.outputLimitTripped && !this.isDetached(entry) && entry.outputSizeBytes > MAX_FOREGROUND_OUTPUT_BYTES) {
|
|
29720
|
-
entry.outputLimitTripped = true;
|
|
29721
|
-
this.stop(entry.taskId, foregroundOutputLimitReason());
|
|
29722
|
-
}
|
|
29723
|
-
if (entry.outputLimitTripped) return;
|
|
29724
29727
|
if (this.persistence === void 0) return;
|
|
29725
29728
|
if (!entry.outputPersistStarted) {
|
|
29726
|
-
entry.pendingOutput.push(
|
|
29727
|
-
entry.pendingOutputBytes +=
|
|
29729
|
+
entry.pendingOutput.push(capture.capturedText);
|
|
29730
|
+
entry.pendingOutputBytes += capture.capturedBytes;
|
|
29728
29731
|
if (entry.pendingOutputBytes > MAX_OUTPUT_BYTES$2) this.startOutputPersist(entry);
|
|
29729
29732
|
return;
|
|
29730
29733
|
}
|
|
29731
|
-
this.appendTaskOutput(entry,
|
|
29734
|
+
this.appendTaskOutput(entry, capture.capturedText);
|
|
29732
29735
|
}
|
|
29733
29736
|
/** Enqueue an `output.log` append, serialized per task. No-op when detached managers omit persistence. */
|
|
29734
29737
|
appendTaskOutput(entry, chunk) {
|
|
@@ -29925,6 +29928,7 @@ var init_background = __esmMin((() => {
|
|
|
29925
29928
|
endedAt: entry.endedAt,
|
|
29926
29929
|
stopReason: entry.stopReason,
|
|
29927
29930
|
terminalNotificationSuppressed: entry.terminalNotificationSuppressed,
|
|
29931
|
+
outputCaptureCapped: entry.outputCaptureCapped,
|
|
29928
29932
|
timeoutMs: entry.options.timeoutMs
|
|
29929
29933
|
};
|
|
29930
29934
|
return entry.task.toInfo(base);
|
|
@@ -251353,18 +251357,20 @@ function resolveSwarmMaxConcurrency(env = process.env) {
|
|
|
251353
251357
|
if (!Number.isInteger(value) || value <= 0) throw new Error(`${AGENT_SWARM_MAX_CONCURRENCY_ENV} must be a positive integer, got ${JSON.stringify(raw)}.`);
|
|
251354
251358
|
return value;
|
|
251355
251359
|
}
|
|
251356
|
-
var import_retry, INITIAL_LAUNCH_LIMIT, INITIAL_LAUNCH_INTERVAL_MS, RATE_LIMIT_RETRY_BASE_MS, RATE_LIMIT_RETRY_FACTOR, RATE_LIMIT_CAPACITY_SHRINK_INTERVAL_MS, RATE_LIMIT_CAPACITY_RECOVERY_INTERVAL_MS, RATE_LIMIT_SUSPENDED_REASON, AGENT_SWARM_MAX_CONCURRENCY_ENV, MAX_AUTOMATIC_RATE_LIMIT_RESUMES, rateLimitRetryDelayMs, shouldCompactBeforeRateLimitResume, shouldStopAutomaticRateLimitResume, SubagentBatch;
|
|
251360
|
+
var import_retry, INITIAL_LAUNCH_LIMIT, INITIAL_LAUNCH_INTERVAL_MS, RATE_LIMIT_RETRY_BASE_MS, RATE_LIMIT_RETRY_FACTOR, RATE_LIMIT_CAPACITY_SHRINK_INTERVAL_MS, RATE_LIMIT_CAPACITY_RECOVERY_INTERVAL_MS, RATE_LIMIT_SUSPENDED_REASON, TRANSIENT_PROVIDER_SUSPENDED_REASON, AGENT_SWARM_MAX_CONCURRENCY_ENV, MAX_AUTOMATIC_RATE_LIMIT_RESUMES, MAX_TRANSIENT_SUBAGENT_CONTINUATIONS, isRetryableSubagentFailure, rateLimitRetryDelayMs, shouldCompactBeforeRateLimitResume, shouldStopAutomaticRateLimitResume, shouldStopTransientSubagentContinuation, transientSubagentRetryDelayMs, SubagentBatch;
|
|
251357
251361
|
var init_subagent_batch = __esmMin((() => {
|
|
251358
251362
|
init_src$4();
|
|
251359
251363
|
import_retry = /* @__PURE__ */ __toESM(require_retry$1(), 1);
|
|
251360
251364
|
init_abort();
|
|
251361
251365
|
({ MAX_AUTOMATIC_RATE_LIMIT_RESUMES, RATE_LIMIT_RETRY_BASE_MS, rateLimitRetryDelayMs, shouldCompactBeforeRateLimitResume, shouldStopAutomaticRateLimitResume } = createRequire(import.meta.url)("./bin/rate-limit-recovery-policy.cjs"));
|
|
251366
|
+
({ MAX_TRANSIENT_SUBAGENT_CONTINUATIONS, isRetryableSubagentFailure, shouldStopTransientSubagentContinuation, transientSubagentRetryDelayMs } = createRequire(import.meta.url)("./bin/subagent-timeout-policy.cjs"));
|
|
251362
251367
|
INITIAL_LAUNCH_LIMIT = 5;
|
|
251363
251368
|
INITIAL_LAUNCH_INTERVAL_MS = 700;
|
|
251364
251369
|
RATE_LIMIT_RETRY_FACTOR = 2;
|
|
251365
251370
|
RATE_LIMIT_CAPACITY_SHRINK_INTERVAL_MS = 2e3;
|
|
251366
251371
|
RATE_LIMIT_CAPACITY_RECOVERY_INTERVAL_MS = 180 * 1e3;
|
|
251367
251372
|
RATE_LIMIT_SUSPENDED_REASON = "Provider rate limit; subagent requeued for retry.";
|
|
251373
|
+
TRANSIENT_PROVIDER_SUSPENDED_REASON = "Transient provider interruption; subagent requeued for continuation.";
|
|
251368
251374
|
AGENT_SWARM_MAX_CONCURRENCY_ENV = "BLUN_AGENT_SWARM_MAX_CONCURRENCY";
|
|
251369
251375
|
SubagentBatch = class {
|
|
251370
251376
|
launcher;
|
|
@@ -251379,6 +251385,7 @@ var init_subagent_batch = __esmMin((() => {
|
|
|
251379
251385
|
normalLaunchCount = 0;
|
|
251380
251386
|
normalLaunchTimer;
|
|
251381
251387
|
rateLimitLaunchTimer;
|
|
251388
|
+
transientRetryTimers = /* @__PURE__ */ new Set();
|
|
251382
251389
|
resolve;
|
|
251383
251390
|
reject;
|
|
251384
251391
|
finished = false;
|
|
@@ -251398,6 +251405,7 @@ var init_subagent_batch = __esmMin((() => {
|
|
|
251398
251405
|
index,
|
|
251399
251406
|
task,
|
|
251400
251407
|
retryCount: 0,
|
|
251408
|
+
transientRetryCount: 0,
|
|
251401
251409
|
retryReadyAt: 0,
|
|
251402
251410
|
started: false
|
|
251403
251411
|
}));
|
|
@@ -251540,6 +251548,11 @@ var init_subagent_batch = __esmMin((() => {
|
|
|
251540
251548
|
agentId: handle.agentId,
|
|
251541
251549
|
error: this.attemptErrorMessage(attempt, error, "failed")
|
|
251542
251550
|
};
|
|
251551
|
+
if (isRetryableSubagentFailure(error)) return {
|
|
251552
|
+
type: "transient_provider",
|
|
251553
|
+
agentId: handle.agentId,
|
|
251554
|
+
error: this.attemptErrorMessage(attempt, error, "failed")
|
|
251555
|
+
};
|
|
251543
251556
|
return this.failedAttemptOutcome(attempt, error);
|
|
251544
251557
|
}
|
|
251545
251558
|
}
|
|
@@ -251571,6 +251584,14 @@ var init_subagent_batch = __esmMin((() => {
|
|
|
251571
251584
|
this.requeueTimedOut(attempt, outcome.agentId);
|
|
251572
251585
|
}
|
|
251573
251586
|
else if ("status" in outcome) this.results[attempt.state.index] = outcome;
|
|
251587
|
+
else if (outcome.type === "transient_provider" && shouldStopTransientSubagentContinuation(attempt.state.transientRetryCount)) this.results[attempt.state.index] = {
|
|
251588
|
+
task: attempt.state.task,
|
|
251589
|
+
agentId: outcome.agentId,
|
|
251590
|
+
status: "failed",
|
|
251591
|
+
state: "started",
|
|
251592
|
+
error: `${outcome.error} Automatic continuation stopped after ${String(MAX_TRANSIENT_SUBAGENT_CONTINUATIONS)} attempts; agent ${outcome.agentId} remains resumable.`
|
|
251593
|
+
};
|
|
251594
|
+
else if (outcome.type === "transient_provider") this.requeueTransientProvider(attempt, outcome.agentId);
|
|
251574
251595
|
else if (shouldStopAutomaticRateLimitResume(attempt.state.retryCount)) this.results[attempt.state.index] = {
|
|
251575
251596
|
task: attempt.state.task,
|
|
251576
251597
|
agentId: outcome.agentId,
|
|
@@ -251604,6 +251625,26 @@ var init_subagent_batch = __esmMin((() => {
|
|
|
251604
251625
|
attempt.cleanup();
|
|
251605
251626
|
return true;
|
|
251606
251627
|
}
|
|
251628
|
+
requeueTransientProvider(attempt, agentId) {
|
|
251629
|
+
const state = attempt.state;
|
|
251630
|
+
state.agentId = agentId;
|
|
251631
|
+
state.retryAgentId = agentId;
|
|
251632
|
+
state.transientRetryCount += 1;
|
|
251633
|
+
this.launcher.suspended?.({
|
|
251634
|
+
task: state.task,
|
|
251635
|
+
agentId,
|
|
251636
|
+
reason: TRANSIENT_PROVIDER_SUSPENDED_REASON
|
|
251637
|
+
});
|
|
251638
|
+
const retryDelay = transientSubagentRetryDelayMs(state.transientRetryCount);
|
|
251639
|
+
const timer = setTimeout(() => {
|
|
251640
|
+
this.transientRetryTimers.delete(timer);
|
|
251641
|
+
if (this.finished || this.controller.signal.aborted) return;
|
|
251642
|
+
state.retryReadyAt = 0;
|
|
251643
|
+
this.pending.unshift(state);
|
|
251644
|
+
this.schedule();
|
|
251645
|
+
}, retryDelay);
|
|
251646
|
+
this.transientRetryTimers.add(timer);
|
|
251647
|
+
}
|
|
251607
251648
|
requeueRateLimited(attempt, agentId) {
|
|
251608
251649
|
const state = attempt.state;
|
|
251609
251650
|
state.agentId = agentId;
|
|
@@ -251714,6 +251755,8 @@ var init_subagent_batch = __esmMin((() => {
|
|
|
251714
251755
|
this.batchSignal?.removeEventListener("abort", this.batchAbortListener);
|
|
251715
251756
|
this.clearNormalTimer();
|
|
251716
251757
|
this.clearRateLimitTimer();
|
|
251758
|
+
for (const timer of this.transientRetryTimers) clearTimeout(timer);
|
|
251759
|
+
this.transientRetryTimers.clear();
|
|
251717
251760
|
for (const attempt of this.active.values()) attempt.cleanup();
|
|
251718
251761
|
this.active.clear();
|
|
251719
251762
|
}
|
|
@@ -262103,7 +262146,7 @@ var init_bash = __esmMin((() => {
|
|
|
262103
262146
|
MAX_TIMEOUT_S,
|
|
262104
262147
|
MAX_BACKGROUND_TIMEOUT_S
|
|
262105
262148
|
};
|
|
262106
|
-
bash_default = "Execute a `{{ SHELL_NAME }}` command for pipes, environment variables, processes, git, package managers, build or test runners, and other genuine shell semantics.\n\nPrefer the dedicated tools when they fit: `Read` for a known file, `Write` to create or replace a file, `Edit` for exact changes, `Glob` to locate files, and `Grep` to search contents. Their output is bounded and their permissions are clearer.\n\nStdout and stderr are combined. Long output
|
|
262149
|
+
bash_default = "Execute a `{{ SHELL_NAME }}` command for pipes, environment variables, processes, git, package managers, build or test runners, and other genuine shell semantics.\n\nPrefer the dedicated tools when they fit: `Read` for a known file, `Write` to create or replace a file, `Edit` for exact changes, `Glob` to locate files, and `Grep` to search contents. Their output is bounded and their permissions are clearer.\n\nStdout and stderr are combined. Long foreground output is streamed to a private file; capture stops at a safe size while the command continues to its real exit code. A non-zero command ends with its exit code; timeouts and user interrupts are reported separately.\n\nEach call runs in a fresh shell environment. Variables, `cd`, and shell history do not carry into later calls; pass `cwd` or use absolute paths. Foreground commands default to {{ DEFAULT_TIMEOUT_S }}s and allow up to {{ MAX_TIMEOUT_S }}s through `timeout`. Do not run interactive commands or commands that may run forever in the foreground.\n\nWith `run_in_background=true`, provide a short `description`; the tool returns a task ID and notifies you on completion. Background tasks default to {{ DEFAULT_BACKGROUND_TIMEOUT_S }}s and allow up to {{ MAX_BACKGROUND_TIMEOUT_S }}s. Use `disable_timeout=true` only when no deadline is appropriate. Use `TaskOutput` for a status or output snapshot and `TaskStop` only to cancel. After launch, normally return control instead of immediately waiting.\n\nQuote paths containing spaces. Chain dependent commands with `&&`; issue independent read-only checks as parallel tool calls. Use `;`, `||`, pipes, redirects, and shell control flow when their semantics are required.\n\nStay inside the working directory unless the user explicitly requests otherwise. Avoid `..`, and never require superuser privileges without explicit instruction. Command availability depends on the host; verify unfamiliar commands before relying on them.";
|
|
262107
262150
|
BashTool = class {
|
|
262108
262151
|
kaos;
|
|
262109
262152
|
cwd;
|
|
@@ -262269,8 +262312,10 @@ var init_bash = __esmMin((() => {
|
|
|
262269
262312
|
if (!result.truncated) return result;
|
|
262270
262313
|
const output = await this.backgroundManager.getOutputSnapshot(taskId, 0);
|
|
262271
262314
|
if (!output.fullOutputAvailable || output.outputPath === void 0) return result;
|
|
262315
|
+
const captureCapped = this.backgroundManager.getTask(taskId)?.outputCaptureCapped === true;
|
|
262272
262316
|
const taskOutputHint = this.allowBackground ? `, or TaskOutput(task_id="${taskId}", block=false)` : "";
|
|
262273
|
-
const
|
|
262317
|
+
const captureNotice = captureCapped ? `\n[Capture capped at ${String(MAX_FOREGROUND_CAPTURE_BYTES)} bytes. Additional output was discarded, but the command continued to its real exit code.]` : "";
|
|
262318
|
+
const reference = `\n\n[Full output saved]\ntask_id: ${taskId}\noutput_path: ${output.outputPath}\noutput_size_bytes: ${String(output.outputSizeBytes)}${captureNotice}\nnext_step: Use Read with output_path to page through the full log${taskOutputHint}.`;
|
|
262274
262319
|
return {
|
|
262275
262320
|
...result,
|
|
262276
262321
|
output: `${result.output}${reference}`
|