@cydm/pie 2.7.10 → 2.7.12
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/README.md +2 -2
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +37 -12
- package/dist/builtin/extensions/subagent/index.js +10 -4
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/chunks/{chunk-JO6EDWHP.js → chunk-2NBUFVUQ.js} +48 -2
- package/dist/chunks/{chunk-C4LXD56O.js → chunk-AFPRX5XI.js} +715 -267
- package/dist/chunks/{chunk-2B6VS2NH.js → chunk-EN47TIBG.js} +105 -13
- package/dist/chunks/{chunk-5GUPRFNP.js → chunk-YI3CSQGD.js} +176 -3
- package/dist/chunks/{src-WFN4JWLE.js → src-XDNJ3PHL.js} +2 -2
- package/dist/chunks/{test-stream-GXZ7NT4H.js → test-stream-LUUGOJMP.js} +1 -1
- package/dist/cli.js +1417 -520
- package/package.json +2 -2
|
@@ -2,7 +2,7 @@ import { createRequire as __createRequire } from "node:module"; const require =
|
|
|
2
2
|
import {
|
|
3
3
|
Agent,
|
|
4
4
|
getToolCallerIdentity
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-2NBUFVUQ.js";
|
|
6
6
|
import {
|
|
7
7
|
AssistantMessageEventStream,
|
|
8
8
|
FileSystemGateway,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
getPlatformConfig,
|
|
13
13
|
hashText,
|
|
14
14
|
streamSimple
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-EN47TIBG.js";
|
|
16
16
|
import {
|
|
17
17
|
Type
|
|
18
18
|
} from "./chunk-XXGYUYXI.js";
|
|
@@ -1003,9 +1003,9 @@ var PLAN_POWERSHELL_READ_COMMANDS = new Set([
|
|
|
1003
1003
|
].map((name) => name.toLowerCase()));
|
|
1004
1004
|
var PLAN_POWERSHELL_READ_ALIASES = /* @__PURE__ */ new Set(["cat", "ls", "pwd", "echo", "sort"]);
|
|
1005
1005
|
var PLAN_POWERSHELL_SAFE_PARAMETERS = /^(?:-(?:Raw|TotalCount|Head|Tail|First|Last|Wait|LiteralPath|Path|Name|Pattern|Recurse|Filter|Include|Exclude|SimpleMatch|CaseSensitive|Quiet|Word|Line|Character|Encoding|ErrorAction|WarningAction))$/i;
|
|
1006
|
-
var PLAN_POWERSHELL_SEPARATORS = /[;\n\r\t]
|
|
1006
|
+
var PLAN_POWERSHELL_SEPARATORS = /[;\n\r\t&<>|]/;
|
|
1007
1007
|
var PLAN_POWERSHELL_NON_ASCII = /[^\x20-\x7E]/;
|
|
1008
|
-
var PLAN_POWERSHELL_UNSAFE_TOKENS = /\$\(|`|%\{|[(){}\[\]]|\bsource\b|\.\s+[/~]|\b(bash|sh|zsh|pwsh|powershell|cmd|npm|npx|node|python3
|
|
1008
|
+
var PLAN_POWERSHELL_UNSAFE_TOKENS = /\$\(|`|%\{|[(){}\[\]]|\bsource\b|\.\s+[/~]|\b(bash|sh|zsh|pwsh|powershell|cmd|npm|npx|node|python3?)\b/i;
|
|
1009
1009
|
function isPlanModeSafePowerShellCommand(command) {
|
|
1010
1010
|
const trimmed = command.trim();
|
|
1011
1011
|
if (!trimmed) return false;
|
|
@@ -1015,6 +1015,18 @@ function isPlanModeSafePowerShellCommand(command) {
|
|
|
1015
1015
|
const tokens = trimmed.split(/\s+/).filter(Boolean);
|
|
1016
1016
|
const head = tokens[0].toLowerCase();
|
|
1017
1017
|
if (!head) return false;
|
|
1018
|
+
if (head === "git" || head === "rg") {
|
|
1019
|
+
if (/["']/.test(trimmed)) return false;
|
|
1020
|
+
if (/[$@]/.test(trimmed)) return false;
|
|
1021
|
+
}
|
|
1022
|
+
if (head === "git") {
|
|
1023
|
+
const subcommand = tokens[1]?.toLowerCase() ?? "";
|
|
1024
|
+
if (!GIT_READ_SUBCOMMANDS.has(subcommand)) return false;
|
|
1025
|
+
return !tokens.slice(2).some((token) => UNSAFE_GIT_FLAGS.test(token) || token === "--no-pager" || token === "-c");
|
|
1026
|
+
}
|
|
1027
|
+
if (head === "rg") {
|
|
1028
|
+
return !tokens.slice(1).some((token) => UNSAFE_RG_FLAGS.test(token) || token === "--pre" || token === "--pre-glob");
|
|
1029
|
+
}
|
|
1018
1030
|
if (!PLAN_POWERSHELL_READ_COMMANDS.has(head) && !PLAN_POWERSHELL_READ_ALIASES.has(head)) return false;
|
|
1019
1031
|
for (const token of tokens.slice(1)) {
|
|
1020
1032
|
if (token.startsWith("-") && !PLAN_POWERSHELL_SAFE_PARAMETERS.test(token)) return false;
|
|
@@ -1319,7 +1331,9 @@ var DEFAULT_COMPACTION_CONFIG = {
|
|
|
1319
1331
|
summaryPrefix: SUMMARY_PREFIX
|
|
1320
1332
|
};
|
|
1321
1333
|
var APPROX_BYTES_PER_TOKEN = 4;
|
|
1322
|
-
var
|
|
1334
|
+
var IMAGE_MEDIA_BYTES_ESTIMATE = 7373;
|
|
1335
|
+
var AUDIO_MEDIA_BYTES_ESTIMATE = 6800;
|
|
1336
|
+
var VIDEO_MEDIA_BYTES_ESTIMATE = 16e3;
|
|
1323
1337
|
function estimateTokens(message) {
|
|
1324
1338
|
return approximateBytesAsTokens(estimateModelVisibleBytes(message));
|
|
1325
1339
|
}
|
|
@@ -1444,20 +1458,16 @@ function planRetainedTail(summaries, options = {}) {
|
|
|
1444
1458
|
if (boundaries.length === 0) {
|
|
1445
1459
|
return nonePlan("no-boundary");
|
|
1446
1460
|
}
|
|
1447
|
-
const prefix = new Array(summaries.length + 1).fill(0);
|
|
1448
|
-
for (let i = 0; i < summaries.length; i++) {
|
|
1449
|
-
prefix[i + 1] = prefix[i] + Math.max(0, Math.floor(summaries[i].estTokens || 0));
|
|
1450
|
-
}
|
|
1451
1461
|
const groupEnd = boundaries.map((_, index) => boundaries[index + 1] ?? summaries.length);
|
|
1452
1462
|
const planFrom = (startIndex, reason) => ({
|
|
1453
1463
|
retainedTail: { kind: "from-entry", entryId: summaries[startIndex].id },
|
|
1454
1464
|
entryIds: summaries.slice(startIndex).map((summary) => summary.id),
|
|
1455
|
-
tailTokens:
|
|
1465
|
+
tailTokens: estimateRetainedTailTokens(summaries, { kind: "from-entry", entryId: summaries[startIndex].id }),
|
|
1456
1466
|
reason
|
|
1457
1467
|
});
|
|
1458
1468
|
let accumulated = 0;
|
|
1459
1469
|
for (let k = boundaries.length - 1; k >= 0; k--) {
|
|
1460
|
-
const groupTokens =
|
|
1470
|
+
const groupTokens = summaries.slice(boundaries[k], groupEnd[k]).reduce((sum, summary) => sum + Math.max(0, Math.floor(summary.estTokens || 0)), 0);
|
|
1461
1471
|
if (groupTokens > budget) {
|
|
1462
1472
|
if (accumulated === 0) {
|
|
1463
1473
|
return nonePlan("single-group-over-budget");
|
|
@@ -1533,6 +1543,9 @@ function estimateCompactionGain(params) {
|
|
|
1533
1543
|
const tokensBefore = Math.max(0, params.tokensBefore);
|
|
1534
1544
|
const tokenSavings = Math.max(0, tokensBefore - projectedTokens);
|
|
1535
1545
|
const estimate = { summaryTokens, tailTokens, projectedTokens, tokenSavings };
|
|
1546
|
+
if (!Number.isFinite(projectedTokens)) {
|
|
1547
|
+
return { ...estimate, ok: false, reason: "retained context has unknown model-visible size; supply a compaction modelView" };
|
|
1548
|
+
}
|
|
1536
1549
|
if (params.budgetTokens !== void 0 && params.budgetTokens > 0 && projectedTokens > params.budgetTokens) {
|
|
1537
1550
|
return {
|
|
1538
1551
|
...estimate,
|
|
@@ -1813,7 +1826,7 @@ function normalizeUserContentBlock(block) {
|
|
|
1813
1826
|
return { type: "text", text: block.text };
|
|
1814
1827
|
}
|
|
1815
1828
|
if (block.type === "hostContext") {
|
|
1816
|
-
return { type: "
|
|
1829
|
+
return { type: "hostContext" };
|
|
1817
1830
|
}
|
|
1818
1831
|
if (block.type === "image") {
|
|
1819
1832
|
return { type: "image", mimeType: block.mimeType };
|
|
@@ -1845,22 +1858,16 @@ function estimateMessageMediaBytes(message) {
|
|
|
1845
1858
|
}
|
|
1846
1859
|
function estimateUserContentBlockMediaBytes(block) {
|
|
1847
1860
|
if (block.type === "image") {
|
|
1848
|
-
return
|
|
1861
|
+
return IMAGE_MEDIA_BYTES_ESTIMATE;
|
|
1849
1862
|
}
|
|
1850
1863
|
if (block.type === "audio") {
|
|
1851
|
-
return
|
|
1864
|
+
return AUDIO_MEDIA_BYTES_ESTIMATE;
|
|
1852
1865
|
}
|
|
1853
1866
|
if (block.type === "video") {
|
|
1854
|
-
return
|
|
1867
|
+
return VIDEO_MEDIA_BYTES_ESTIMATE;
|
|
1855
1868
|
}
|
|
1856
1869
|
return 0;
|
|
1857
1870
|
}
|
|
1858
|
-
function estimateImageContentBytes(block) {
|
|
1859
|
-
return Math.max(RESIZED_IMAGE_BYTES_ESTIMATE, estimateInlineDataBytes(block));
|
|
1860
|
-
}
|
|
1861
|
-
function estimateInlineDataBytes(block) {
|
|
1862
|
-
return utf8ByteLength(block.data || "");
|
|
1863
|
-
}
|
|
1864
1871
|
function approximateBytesAsTokens(bytes) {
|
|
1865
1872
|
return Math.ceil(bytes / APPROX_BYTES_PER_TOKEN);
|
|
1866
1873
|
}
|
|
@@ -4611,10 +4618,26 @@ function prepareSessionCompaction(params) {
|
|
|
4611
4618
|
const agentOnlyFailure = excludedFailure === null && requireExclusion ? agentTail ?? null : null;
|
|
4612
4619
|
const summaries = sessionManager.getActiveContextSummaries();
|
|
4613
4620
|
const entries = sessionManager.getResidentContextEntries();
|
|
4614
|
-
const
|
|
4621
|
+
const sourceSummaries = excludedFailure ? summaries.filter((summary) => summary.id !== excludedFailure.entryId) : summaries;
|
|
4615
4622
|
const effectiveEntries = excludedFailure ? entries.filter((entry) => entry.id !== excludedFailure.entryId) : entries;
|
|
4616
4623
|
const summaryInput = excludedFailure || agentOnlyFailure ? messages.slice(0, -1) : messages;
|
|
4617
|
-
const
|
|
4624
|
+
const estimates = /* @__PURE__ */ new Map();
|
|
4625
|
+
const estimate = (message) => {
|
|
4626
|
+
const cached = estimates.get(message);
|
|
4627
|
+
if (cached !== void 0) return cached;
|
|
4628
|
+
const visible = params.modelView ? params.modelView(message) : message;
|
|
4629
|
+
const unknown = visible.role === "user" && Array.isArray(visible.content) && visible.content.some((block) => block.type === "hostContext");
|
|
4630
|
+
const tokens = unknown ? Infinity : estimateTokens(visible);
|
|
4631
|
+
estimates.set(message, tokens);
|
|
4632
|
+
return tokens;
|
|
4633
|
+
};
|
|
4634
|
+
const bodies = new Map(effectiveEntries.map((entry) => [entry.id, entry]));
|
|
4635
|
+
const effectiveSummaries = sourceSummaries.map((summary) => {
|
|
4636
|
+
const entry = bodies.get(summary.id);
|
|
4637
|
+
return { ...summary, estTokens: entry?.message ? estimate(entry.message) : summary.type === "message" ? Infinity : summary.estTokens };
|
|
4638
|
+
});
|
|
4639
|
+
const inputEstimate = summaryInput.reduce((sum, message) => sum + estimate(message), 0);
|
|
4640
|
+
const inputTokens = Number.isFinite(inputEstimate) ? inputEstimate : calculateTotalTokens(summaryInput);
|
|
4618
4641
|
const reductionLimit = inputTokens >= 1e3 ? inputTokens - Math.ceil(inputTokens * 0.1) : Infinity;
|
|
4619
4642
|
const contextLimit = Math.min(
|
|
4620
4643
|
reductionLimit,
|
|
@@ -4628,7 +4651,7 @@ function prepareSessionCompaction(params) {
|
|
|
4628
4651
|
let planned = planRetainedTail(effectiveSummaries, { ...params.compactionConfig, keepRecentTokens: tailBudget });
|
|
4629
4652
|
if (planned.retainedTail.kind !== "none") {
|
|
4630
4653
|
const validation = validateRetainedTailEntries({ entries: effectiveEntries, retainedTail: planned.retainedTail });
|
|
4631
|
-
planned = validation.ok ? { ...planned, entryIds: validation.entryIds, tailTokens:
|
|
4654
|
+
planned = validation.ok ? { ...planned, entryIds: validation.entryIds, tailTokens: estimateRetainedTailTokens(effectiveSummaries, planned.retainedTail) } : { retainedTail: { kind: "none" }, entryIds: [], tailTokens: 0, reason: "pairing" };
|
|
4632
4655
|
}
|
|
4633
4656
|
return {
|
|
4634
4657
|
sessionId: sessionManager.getActiveSessionId(),
|
|
@@ -4684,7 +4707,7 @@ function verifyPreparedCompactionCommit(params) {
|
|
|
4684
4707
|
const gain = estimateCompactionGain({
|
|
4685
4708
|
summary: checkpointSummary,
|
|
4686
4709
|
tailTokens: estimateRetainedTailTokens(params.preparation.summaries, retainedTail),
|
|
4687
|
-
tokensBefore: params.
|
|
4710
|
+
tokensBefore: params.preparation.inputTokens,
|
|
4688
4711
|
budgetTokens: params.preparation.budgetTokens
|
|
4689
4712
|
});
|
|
4690
4713
|
if (!gain.ok) {
|
|
@@ -5164,7 +5187,13 @@ async function preparePromptMessages(params) {
|
|
|
5164
5187
|
|
|
5165
5188
|
// ../../packages/agent-framework/src/session/controller/pre-request-policy.ts
|
|
5166
5189
|
async function runPreRequestAutoCompact(params) {
|
|
5167
|
-
|
|
5190
|
+
const boundary = params.toolContinuation;
|
|
5191
|
+
const cancelled = () => params.isCurrent?.() === false || boundary?.signal.aborted === true;
|
|
5192
|
+
if (boundary) {
|
|
5193
|
+
await boundary.sync();
|
|
5194
|
+
if (cancelled()) return;
|
|
5195
|
+
}
|
|
5196
|
+
if (params.disposed || params.runtimeGuardTriggered || params.isStreaming && !boundary || params.recoveryActive() || params.agent.state.messages.length === 0) {
|
|
5168
5197
|
return;
|
|
5169
5198
|
}
|
|
5170
5199
|
const decision = params.getAutoCompactDecision?.({
|
|
@@ -5173,12 +5202,13 @@ async function runPreRequestAutoCompact(params) {
|
|
|
5173
5202
|
cwd: params.cwd,
|
|
5174
5203
|
messages: params.agent.state.messages.slice()
|
|
5175
5204
|
});
|
|
5176
|
-
if (decision?.reason !== "threshold") {
|
|
5205
|
+
if (cancelled() || decision?.reason !== "threshold") {
|
|
5177
5206
|
return;
|
|
5178
5207
|
}
|
|
5179
5208
|
try {
|
|
5180
5209
|
await params.runAutoCompact();
|
|
5181
|
-
} catch {
|
|
5210
|
+
} catch (error) {
|
|
5211
|
+
if (boundary) throw error;
|
|
5182
5212
|
if (params.isCurrent?.() !== false) params.setRecoveryState("none");
|
|
5183
5213
|
}
|
|
5184
5214
|
}
|
|
@@ -5432,12 +5462,7 @@ var AgentSessionQueueDispatcher = class {
|
|
|
5432
5462
|
return false;
|
|
5433
5463
|
}
|
|
5434
5464
|
this.options.emit({ type: "queued_turn_dispatched", queuedInput: cloneQueuedInput(dispatched) });
|
|
5435
|
-
if (dispatched.deliveredToAgent
|
|
5436
|
-
if (!dispatched.deliveredToAgent) {
|
|
5437
|
-
this.options.queue.deliverToAgent(dispatched);
|
|
5438
|
-
}
|
|
5439
|
-
await this.options.continueTurn();
|
|
5440
|
-
} else {
|
|
5465
|
+
if (!dispatched.deliveredToAgent) {
|
|
5441
5466
|
const prepared = await this.options.preparePromptMessages(dispatched.messages, {
|
|
5442
5467
|
source: dispatched.source,
|
|
5443
5468
|
streamingBehavior: dispatched.mode,
|
|
@@ -5447,13 +5472,20 @@ var AgentSessionQueueDispatcher = class {
|
|
|
5447
5472
|
visibility: dispatched.visibility
|
|
5448
5473
|
});
|
|
5449
5474
|
if (generation !== this.generation) return false;
|
|
5450
|
-
if (
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
}
|
|
5454
|
-
await this.options.agent.prompt(prepared.messages);
|
|
5455
|
-
await this.options.syncNewAgentMessages();
|
|
5475
|
+
if (prepared.handled) return true;
|
|
5476
|
+
if (prepared.systemPrompt !== void 0) {
|
|
5477
|
+
this.options.agent.setSystemPrompt(prepared.systemPrompt);
|
|
5456
5478
|
}
|
|
5479
|
+
dispatched.messages = prepared.messages;
|
|
5480
|
+
}
|
|
5481
|
+
if (dispatched.deliveredToAgent || this.options.agent.state.messages.at(-1)?.role === "assistant") {
|
|
5482
|
+
if (!dispatched.deliveredToAgent) {
|
|
5483
|
+
this.options.queue.deliverToAgent(dispatched);
|
|
5484
|
+
}
|
|
5485
|
+
await this.options.continueTurn();
|
|
5486
|
+
} else {
|
|
5487
|
+
await this.options.agent.prompt(dispatched.messages);
|
|
5488
|
+
await this.options.syncNewAgentMessages();
|
|
5457
5489
|
}
|
|
5458
5490
|
return true;
|
|
5459
5491
|
}
|
|
@@ -5574,6 +5606,95 @@ var ReplayStallCircuit = class {
|
|
|
5574
5606
|
}
|
|
5575
5607
|
};
|
|
5576
5608
|
|
|
5609
|
+
// ../../packages/agent-framework/src/session/controller/retry-decision.ts
|
|
5610
|
+
function getAgentSessionRetryLimit(retry, failure) {
|
|
5611
|
+
return Math.min(
|
|
5612
|
+
(failure !== void 0 ? retry?.maxRetriesForFailure?.(failure) : void 0) ?? retry?.maxRetries ?? 3,
|
|
5613
|
+
failure?.kind === "provider_confirmation" ? 1 : Infinity
|
|
5614
|
+
);
|
|
5615
|
+
}
|
|
5616
|
+
var AgentSessionRetryEpisode = class {
|
|
5617
|
+
constructor(retry) {
|
|
5618
|
+
this.retry = retry;
|
|
5619
|
+
this.replayCircuit = new ReplayStallCircuit(retry?.systemicReplayStallLimit);
|
|
5620
|
+
}
|
|
5621
|
+
retry;
|
|
5622
|
+
retryAttempts = /* @__PURE__ */ new Map();
|
|
5623
|
+
total = 0;
|
|
5624
|
+
replayCircuit;
|
|
5625
|
+
get totalRetryAttempts() {
|
|
5626
|
+
return this.total;
|
|
5627
|
+
}
|
|
5628
|
+
/** A healthy response ends the current episode and proves endpoint liveness. */
|
|
5629
|
+
succeed() {
|
|
5630
|
+
this.replayCircuit.recordHealthyTurn();
|
|
5631
|
+
const attempts = this.total;
|
|
5632
|
+
this.resetAttempts();
|
|
5633
|
+
return attempts;
|
|
5634
|
+
}
|
|
5635
|
+
/** Cancellation/invalidation resets counters, never the endpoint-health circuit. */
|
|
5636
|
+
resetAttempts() {
|
|
5637
|
+
this.retryAttempts.clear();
|
|
5638
|
+
this.total = 0;
|
|
5639
|
+
}
|
|
5640
|
+
next(failure) {
|
|
5641
|
+
if (!this.retry || !failure.retryable) {
|
|
5642
|
+
if (failure.kind !== "unknown" && failure.kind !== "internal_error") {
|
|
5643
|
+
this.replayCircuit.recordHealthyTurn();
|
|
5644
|
+
}
|
|
5645
|
+
const totalRetryAttempts2 = this.total;
|
|
5646
|
+
this.resetAttempts();
|
|
5647
|
+
return { action: "terminal", failure, totalRetryAttempts: totalRetryAttempts2 };
|
|
5648
|
+
}
|
|
5649
|
+
if (failure.replayExempt === true && this.replayCircuit.isOpen) {
|
|
5650
|
+
const openFailure = asTerminalFailure(
|
|
5651
|
+
failure,
|
|
5652
|
+
`${failure.message} (replay circuit open: ${this.replayCircuit.consecutiveTurnStalls} consecutive turns ended by timeout-replay exhaustion)`
|
|
5653
|
+
);
|
|
5654
|
+
const totalRetryAttempts2 = this.total;
|
|
5655
|
+
this.resetAttempts();
|
|
5656
|
+
return { action: "exhausted", failure: openFailure, maxRetries: 0, totalRetryAttempts: totalRetryAttempts2, reason: "circuit_open" };
|
|
5657
|
+
}
|
|
5658
|
+
const maxRetries = getAgentSessionRetryLimit(this.retry, failure);
|
|
5659
|
+
const kindAttempts = (this.retryAttempts.get(failure.kind) ?? 0) + 1;
|
|
5660
|
+
this.retryAttempts.set(failure.kind, kindAttempts);
|
|
5661
|
+
this.total += 1;
|
|
5662
|
+
const totalRetryAttempts = this.total;
|
|
5663
|
+
const totalBudget = this.retry.maxTotalRetries;
|
|
5664
|
+
const kindExhausted = kindAttempts > maxRetries;
|
|
5665
|
+
const totalExhausted = totalBudget !== void 0 && totalRetryAttempts > totalBudget;
|
|
5666
|
+
const delayMs = resolveRetryDelay(
|
|
5667
|
+
(this.retry.baseDelayMs ?? 2e3) * 2 ** (kindAttempts - 1),
|
|
5668
|
+
this.retry.maxDelayMs,
|
|
5669
|
+
failure.retryAfterMs
|
|
5670
|
+
);
|
|
5671
|
+
if (kindExhausted || totalExhausted || delayMs === void 0) {
|
|
5672
|
+
const terminalFailure = asTerminalFailure(
|
|
5673
|
+
failure,
|
|
5674
|
+
delayMs === void 0 ? `${failure.message} (Retry-After exceeds the allowed wait; automatic recovery paused)` : failure.message
|
|
5675
|
+
);
|
|
5676
|
+
const circuit = failure.replayExempt === true ? this.replayCircuit.recordReplayExhaustion() : void 0;
|
|
5677
|
+
this.resetAttempts();
|
|
5678
|
+
return {
|
|
5679
|
+
action: "exhausted",
|
|
5680
|
+
failure: terminalFailure,
|
|
5681
|
+
maxRetries: totalExhausted ? totalBudget : maxRetries,
|
|
5682
|
+
totalRetryAttempts,
|
|
5683
|
+
reason: delayMs === void 0 ? "retry_after" : totalExhausted ? "total_budget" : "kind_budget",
|
|
5684
|
+
...circuit ? { circuit } : {}
|
|
5685
|
+
};
|
|
5686
|
+
}
|
|
5687
|
+
return {
|
|
5688
|
+
action: "retry",
|
|
5689
|
+
failure,
|
|
5690
|
+
attempt: kindAttempts,
|
|
5691
|
+
maxRetries,
|
|
5692
|
+
delayMs,
|
|
5693
|
+
totalRetryAttempts
|
|
5694
|
+
};
|
|
5695
|
+
}
|
|
5696
|
+
};
|
|
5697
|
+
|
|
5577
5698
|
// ../../packages/agent-framework/src/session/controller/retry-policy.ts
|
|
5578
5699
|
function defaultSetTimeout4(callback, delay) {
|
|
5579
5700
|
return setTimeout(callback, delay);
|
|
@@ -5588,21 +5709,14 @@ var AgentSessionRetryController = class {
|
|
|
5588
5709
|
this.removeTrailingAssistantError = removeTrailingAssistantError2;
|
|
5589
5710
|
this.continueTurn = continueTurn;
|
|
5590
5711
|
this.setRecoveryState = setRecoveryState;
|
|
5591
|
-
this.
|
|
5712
|
+
this.episode = new AgentSessionRetryEpisode(retry);
|
|
5592
5713
|
}
|
|
5593
5714
|
retry;
|
|
5594
5715
|
emit;
|
|
5595
5716
|
removeTrailingAssistantError;
|
|
5596
5717
|
continueTurn;
|
|
5597
5718
|
setRecoveryState;
|
|
5598
|
-
|
|
5599
|
-
* 10-attempt network budget must not be eaten by interleaved provider
|
|
5600
|
-
* errors, and vice versa), so each kind counts its own attempts. */
|
|
5601
|
-
retryAttempts = /* @__PURE__ */ new Map();
|
|
5602
|
-
/** Total attempts across kinds in this recovery episode, for reporting. */
|
|
5603
|
-
totalRetryAttempts = 0;
|
|
5604
|
-
/** L2 systemic-stall circuit (see replay-stall-circuit.ts). */
|
|
5605
|
-
replayCircuit;
|
|
5719
|
+
episode;
|
|
5606
5720
|
retryTimer;
|
|
5607
5721
|
retryEpoch = 0;
|
|
5608
5722
|
pendingRetry;
|
|
@@ -5616,72 +5730,45 @@ var AgentSessionRetryController = class {
|
|
|
5616
5730
|
waitForRetry() {
|
|
5617
5731
|
return this.pendingRetry ?? Promise.resolve();
|
|
5618
5732
|
}
|
|
5619
|
-
/** L2 circuit: healthy turn (success or an actively-answered failure). */
|
|
5620
|
-
noteEndpointHealthy() {
|
|
5621
|
-
this.replayCircuit.recordHealthyTurn();
|
|
5622
|
-
}
|
|
5623
5733
|
async handleTurnEnd(message, failure) {
|
|
5624
5734
|
if (!this.retry || !message || message.role !== "assistant") {
|
|
5625
5735
|
this.settleStrandedRetry(failure);
|
|
5626
5736
|
return;
|
|
5627
5737
|
}
|
|
5628
5738
|
if (!failure) {
|
|
5629
|
-
this.
|
|
5630
|
-
if (
|
|
5631
|
-
this.emit({ type: "retry_succeeded", attempts
|
|
5632
|
-
this.emit({ type: "turn_retry_succeeded", attempts
|
|
5633
|
-
this.resetAttempts();
|
|
5739
|
+
const attempts = this.episode.succeed();
|
|
5740
|
+
if (attempts > 0) {
|
|
5741
|
+
this.emit({ type: "retry_succeeded", attempts });
|
|
5742
|
+
this.emit({ type: "turn_retry_succeeded", attempts });
|
|
5634
5743
|
this.setRecoveryState("none");
|
|
5635
5744
|
this.resolvePending();
|
|
5636
5745
|
}
|
|
5637
5746
|
return;
|
|
5638
5747
|
}
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
}
|
|
5643
|
-
if (this.totalRetryAttempts > 0) {
|
|
5644
|
-
this.resetAttempts();
|
|
5748
|
+
const decision = this.episode.next(failure);
|
|
5749
|
+
if (decision.action === "terminal") {
|
|
5750
|
+
if (decision.totalRetryAttempts > 0) {
|
|
5645
5751
|
this.setRecoveryState("none");
|
|
5646
5752
|
this.resolvePending();
|
|
5647
5753
|
}
|
|
5648
5754
|
return;
|
|
5649
5755
|
}
|
|
5650
|
-
if (
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
this.resolvePending();
|
|
5658
|
-
return;
|
|
5659
|
-
}
|
|
5660
|
-
const maxRetries = Math.min(this.retry.maxRetriesForFailure?.(failure) ?? this.retry.maxRetries ?? 3, failure.kind === "provider_confirmation" ? 1 : Infinity);
|
|
5661
|
-
const kindAttempts = (this.retryAttempts.get(failure.kind) ?? 0) + 1;
|
|
5662
|
-
this.retryAttempts.set(failure.kind, kindAttempts);
|
|
5663
|
-
this.totalRetryAttempts += 1;
|
|
5664
|
-
const totalBudget = this.retry.maxTotalRetries;
|
|
5665
|
-
const kindExhausted = kindAttempts > maxRetries;
|
|
5666
|
-
const totalExhausted = totalBudget !== void 0 && this.totalRetryAttempts > totalBudget;
|
|
5667
|
-
const delayMs = resolveRetryDelay((this.retry.baseDelayMs ?? 2e3) * 2 ** (kindAttempts - 1), this.retry.maxDelayMs, failure.retryAfterMs);
|
|
5668
|
-
if (kindExhausted || totalExhausted || delayMs === void 0) {
|
|
5669
|
-
const terminalFailure = asTerminalFailure(failure, delayMs === void 0 ? `${failure.message} (Retry-After exceeds the allowed wait; automatic recovery paused)` : failure.message);
|
|
5670
|
-
if (failure.replayExempt === true) {
|
|
5671
|
-
const outcome = this.replayCircuit.recordReplayExhaustion();
|
|
5672
|
-
if (outcome.opened) {
|
|
5673
|
-
this.emit({ type: "retry_exhausted", errorMessage: `systemic timeout-stall: ${outcome.consecutive} consecutive turns ended by timeout-replay exhaustion`, maxRetries: totalExhausted ? totalBudget : maxRetries });
|
|
5674
|
-
}
|
|
5756
|
+
if (decision.action === "exhausted") {
|
|
5757
|
+
if (decision.circuit?.opened) {
|
|
5758
|
+
this.emit({
|
|
5759
|
+
type: "retry_exhausted",
|
|
5760
|
+
errorMessage: `systemic timeout-stall: ${decision.circuit.consecutive} consecutive turns ended by timeout-replay exhaustion`,
|
|
5761
|
+
maxRetries: decision.maxRetries
|
|
5762
|
+
});
|
|
5675
5763
|
}
|
|
5676
|
-
this.emit({ type: "retry_exhausted", errorMessage:
|
|
5677
|
-
this.emit({ type: "turn_failed_terminal", failure:
|
|
5764
|
+
this.emit({ type: "retry_exhausted", errorMessage: decision.failure.message, maxRetries: decision.maxRetries });
|
|
5765
|
+
this.emit({ type: "turn_failed_terminal", failure: decision.failure, message });
|
|
5678
5766
|
this.emit({ type: "turn_settled" });
|
|
5679
|
-
this.resetAttempts();
|
|
5680
5767
|
this.setRecoveryState("terminal");
|
|
5681
5768
|
this.resolvePending();
|
|
5682
5769
|
return;
|
|
5683
5770
|
}
|
|
5684
|
-
const attempt =
|
|
5771
|
+
const { attempt, maxRetries, delayMs } = decision;
|
|
5685
5772
|
this.ensurePending();
|
|
5686
5773
|
const epoch = this.retryEpoch;
|
|
5687
5774
|
const pending = this.pendingRetry;
|
|
@@ -5708,7 +5795,7 @@ var AgentSessionRetryController = class {
|
|
|
5708
5795
|
this.emit({ type: "retry_exhausted", errorMessage, maxRetries });
|
|
5709
5796
|
this.emit({ type: "turn_failed_terminal", failure: terminalFailure, message });
|
|
5710
5797
|
this.emit({ type: "turn_settled" });
|
|
5711
|
-
this.resetAttempts();
|
|
5798
|
+
this.episode.resetAttempts();
|
|
5712
5799
|
this.setRecoveryState("terminal");
|
|
5713
5800
|
this.resolvePending(pending);
|
|
5714
5801
|
});
|
|
@@ -5733,24 +5820,20 @@ var AgentSessionRetryController = class {
|
|
|
5733
5820
|
userAbort: false,
|
|
5734
5821
|
terminal: true
|
|
5735
5822
|
});
|
|
5736
|
-
const maxRetries =
|
|
5823
|
+
const maxRetries = getAgentSessionRetryLimit(this.retry, failure);
|
|
5737
5824
|
this.emit({ type: "retry_exhausted", errorMessage: terminalFailure.message, maxRetries });
|
|
5738
5825
|
this.emit({ type: "turn_failed_terminal", failure: terminalFailure });
|
|
5739
5826
|
this.emit({ type: "turn_settled" });
|
|
5740
|
-
this.resetAttempts();
|
|
5827
|
+
this.episode.resetAttempts();
|
|
5741
5828
|
this.setRecoveryState("terminal");
|
|
5742
5829
|
this.resolvePending();
|
|
5743
5830
|
}
|
|
5744
5831
|
cancel() {
|
|
5745
5832
|
this.retryEpoch++;
|
|
5746
5833
|
this.cancelTimerOnly();
|
|
5747
|
-
this.resetAttempts();
|
|
5834
|
+
this.episode.resetAttempts();
|
|
5748
5835
|
this.resolvePending();
|
|
5749
5836
|
}
|
|
5750
|
-
resetAttempts() {
|
|
5751
|
-
this.retryAttempts.clear();
|
|
5752
|
-
this.totalRetryAttempts = 0;
|
|
5753
|
-
}
|
|
5754
5837
|
cancelTimerOnly() {
|
|
5755
5838
|
if (this.retryTimer === void 0) {
|
|
5756
5839
|
return;
|
|
@@ -6089,7 +6172,8 @@ async function runAgentSessionCompaction(params) {
|
|
|
6089
6172
|
const preparation = prepareSessionCompaction({
|
|
6090
6173
|
sessionManager: params.sessionManager,
|
|
6091
6174
|
messages: params.agent.state.messages,
|
|
6092
|
-
budgetTokens: params.compactionBudgetTokens?.()
|
|
6175
|
+
budgetTokens: params.compactionBudgetTokens?.(),
|
|
6176
|
+
modelView: params.compaction.modelView
|
|
6093
6177
|
});
|
|
6094
6178
|
let result = await params.compaction.compact({
|
|
6095
6179
|
reason: params.reason,
|
|
@@ -6368,6 +6452,8 @@ var AgentSessionController = class {
|
|
|
6368
6452
|
autoContinue;
|
|
6369
6453
|
unsubscribeAgent;
|
|
6370
6454
|
unsubscribeSemantic;
|
|
6455
|
+
unsubscribeToolContinuation;
|
|
6456
|
+
compactionOwner;
|
|
6371
6457
|
lastSyncedMessageCount = 0;
|
|
6372
6458
|
disposed = false;
|
|
6373
6459
|
transitioning = false;
|
|
@@ -6471,6 +6557,7 @@ var AgentSessionController = class {
|
|
|
6471
6557
|
});
|
|
6472
6558
|
this.syncFromActiveSession();
|
|
6473
6559
|
this.unsubscribeAgent = this.agent.subscribe((event) => this.handleAgentEvent(event));
|
|
6560
|
+
this.unsubscribeToolContinuation = this.agent.setBeforeToolContinuation((signal) => this.runPreRequestAutoCompactIfNeeded(signal));
|
|
6474
6561
|
this.unsubscribeSemantic = this.agent.subscribeSemantic((event) => {
|
|
6475
6562
|
this.emit({ type: "semantic_event", event });
|
|
6476
6563
|
});
|
|
@@ -6521,6 +6608,7 @@ var AgentSessionController = class {
|
|
|
6521
6608
|
this.transitioning = true;
|
|
6522
6609
|
try {
|
|
6523
6610
|
this.generation++;
|
|
6611
|
+
if (this.isStreaming) this.agent.resetProcessingState();
|
|
6524
6612
|
this.cancelScheduledWork();
|
|
6525
6613
|
this.recoveryState = "none";
|
|
6526
6614
|
this.pendingCompactionFailure = void 0;
|
|
@@ -6652,48 +6740,61 @@ var AgentSessionController = class {
|
|
|
6652
6740
|
const generation = this.generation;
|
|
6653
6741
|
if (this.disposed || this.transitioning) throw new Error("Controller disposed or transitioning");
|
|
6654
6742
|
const isCurrent = () => generation === this.generation && !this.disposed;
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
|
|
6677
|
-
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6743
|
+
if (this.compactionOwner) throw new Error("Compaction already running");
|
|
6744
|
+
const owner = this.compactionOwner = {};
|
|
6745
|
+
try {
|
|
6746
|
+
return await this.convergence.run(() => runAgentSessionControllerAutoCompact({
|
|
6747
|
+
isCurrent,
|
|
6748
|
+
reason,
|
|
6749
|
+
willRetry,
|
|
6750
|
+
signal: options?.signal,
|
|
6751
|
+
compaction: this.compaction,
|
|
6752
|
+
agent: this.agent,
|
|
6753
|
+
sessionManager: this.sessionManager,
|
|
6754
|
+
cwd: this.cwd,
|
|
6755
|
+
pendingFailure: this.pendingCompactionFailure,
|
|
6756
|
+
emit: (event) => {
|
|
6757
|
+
if (isCurrent()) this.emit(event);
|
|
6758
|
+
},
|
|
6759
|
+
setRecoveryState: (state) => {
|
|
6760
|
+
this.recoveryState = state;
|
|
6761
|
+
},
|
|
6762
|
+
getRecoveryState: () => this.recoveryState,
|
|
6763
|
+
getDisposed: () => this.disposed,
|
|
6764
|
+
cancelRetry: () => this.retryController.cancel(),
|
|
6765
|
+
clearPendingFailure: () => {
|
|
6766
|
+
this.pendingCompactionFailure = void 0;
|
|
6767
|
+
},
|
|
6768
|
+
getCompactionBudgetTokens: this.compactionSettings.getCompactionBudgetTokens,
|
|
6769
|
+
removeTrailingAssistantError: () => this.removeTrailingAssistantError(),
|
|
6770
|
+
replaceMessagesAndMarkSynced: (messages) => this.replaceMessagesAndMarkSynced(messages),
|
|
6771
|
+
markSyncedToAgentState: () => this.markSyncedToAgentState(),
|
|
6772
|
+
continueTurn: async () => {
|
|
6773
|
+
if (!isCurrent() || options?.signal?.aborted) return;
|
|
6774
|
+
if (this.compactionOwner === owner) this.compactionOwner = void 0;
|
|
6775
|
+
this.recoveryState = "none";
|
|
6776
|
+
await this.continue();
|
|
6777
|
+
},
|
|
6778
|
+
handleAgentEndHook: (messages) => this.handleAgentEndHook(messages),
|
|
6779
|
+
emitSettled: options?.emitSettled,
|
|
6780
|
+
runAfterAgentEndHook: options?.runAfterAgentEndHook,
|
|
6781
|
+
cancelOnAbort: options?.cancelOnAbort
|
|
6782
|
+
}));
|
|
6783
|
+
} finally {
|
|
6784
|
+
if (this.compactionOwner === owner) this.compactionOwner = void 0;
|
|
6785
|
+
}
|
|
6687
6786
|
}
|
|
6688
6787
|
dispose() {
|
|
6689
6788
|
if (this.disposed) return;
|
|
6690
6789
|
this.disposed = true;
|
|
6790
|
+
if (this.isStreaming) this.agent.resetProcessingState();
|
|
6691
6791
|
this.agent.releaseTransport?.();
|
|
6692
6792
|
this.generation++;
|
|
6693
6793
|
this.pendingEndOperations.clear();
|
|
6694
6794
|
this.cancelScheduledWork();
|
|
6695
6795
|
this.unsubscribeAgent?.();
|
|
6696
6796
|
this.unsubscribeSemantic?.();
|
|
6797
|
+
this.unsubscribeToolContinuation?.();
|
|
6697
6798
|
this.queue.clear();
|
|
6698
6799
|
this.convergence.invalidate("dispose");
|
|
6699
6800
|
this.listeners.clear();
|
|
@@ -6717,19 +6818,26 @@ var AgentSessionController = class {
|
|
|
6717
6818
|
});
|
|
6718
6819
|
}
|
|
6719
6820
|
}
|
|
6720
|
-
async runPreRequestAutoCompactIfNeeded() {
|
|
6821
|
+
async runPreRequestAutoCompactIfNeeded(signal) {
|
|
6721
6822
|
const generation = this.generation;
|
|
6722
6823
|
return runPreRequestAutoCompact({
|
|
6723
6824
|
isCurrent: () => generation === this.generation && !this.disposed,
|
|
6724
6825
|
disposed: this.disposed,
|
|
6725
6826
|
runtimeGuardTriggered: this.runtimeGuard.triggered,
|
|
6726
6827
|
isStreaming: this.isStreaming,
|
|
6727
|
-
recoveryActive: this.isRecoveryActive(),
|
|
6828
|
+
recoveryActive: () => this.isRecoveryActive(),
|
|
6728
6829
|
agent: this.agent,
|
|
6729
6830
|
sessionManager: this.sessionManager,
|
|
6730
6831
|
cwd: this.cwd,
|
|
6731
6832
|
getAutoCompactDecision: this.compactionSettings.getAutoCompactDecision,
|
|
6732
|
-
|
|
6833
|
+
toolContinuation: signal ? { signal, sync: async () => {
|
|
6834
|
+
await this.pendingEndOperations.wait();
|
|
6835
|
+
if (generation === this.generation && !this.disposed && !signal.aborted) await this.syncNewAgentMessages();
|
|
6836
|
+
} } : void 0,
|
|
6837
|
+
runAutoCompact: async () => {
|
|
6838
|
+
const result = await this.runAutoCompact("threshold", false, { signal, emitSettled: false, runAfterAgentEndHook: false, cancelOnAbort: false });
|
|
6839
|
+
if (signal && (!result || result.aborted)) throw new Error("Tool-continuation compaction was aborted");
|
|
6840
|
+
},
|
|
6733
6841
|
setRecoveryState: (state) => {
|
|
6734
6842
|
this.recoveryState = state;
|
|
6735
6843
|
}
|
|
@@ -6874,6 +6982,7 @@ var AgentSessionController = class {
|
|
|
6874
6982
|
return this.recoveryState !== "none" || this.retryController.hasPendingRetry || this.autoCompact.hasPendingAutoCompact;
|
|
6875
6983
|
}
|
|
6876
6984
|
cancelScheduledWork() {
|
|
6985
|
+
this.compactionOwner = void 0;
|
|
6877
6986
|
this.retryController.cancel();
|
|
6878
6987
|
this.autoCompact.cancel();
|
|
6879
6988
|
this.queueDispatcher.cancel();
|
|
@@ -8229,6 +8338,9 @@ function withSubagentRunContext(stream, hooks) {
|
|
|
8229
8338
|
${SUBAGENT_FINALIZATION_HINT}` } : context, options);
|
|
8230
8339
|
};
|
|
8231
8340
|
}
|
|
8341
|
+
function isRetryableSubagentFailure(message) {
|
|
8342
|
+
return isRetryableSubagentFailureFrom({ errorMessage: message });
|
|
8343
|
+
}
|
|
8232
8344
|
function isRetryableSubagentFailureFrom(facts) {
|
|
8233
8345
|
if (facts.retryable === false) return false;
|
|
8234
8346
|
if (facts.replayClass === "stream_lost" || facts.replayClass === "stream_lost_partial") return true;
|
|
@@ -8250,90 +8362,366 @@ function cancellableBackoff(ms, signal) {
|
|
|
8250
8362
|
if (signal?.aborted) finish();
|
|
8251
8363
|
});
|
|
8252
8364
|
}
|
|
8365
|
+
function factsFromMessage(message) {
|
|
8366
|
+
return {
|
|
8367
|
+
errorMessage: message.errorMessage,
|
|
8368
|
+
retryable: message.retryable,
|
|
8369
|
+
replayClass: message.replayClass,
|
|
8370
|
+
replayEligible: message.replayEligible,
|
|
8371
|
+
remoteFailure: message.remoteFailure,
|
|
8372
|
+
stopReason: message.stopReason
|
|
8373
|
+
};
|
|
8374
|
+
}
|
|
8253
8375
|
function factsOf(error) {
|
|
8254
8376
|
if (error instanceof Error) {
|
|
8255
8377
|
const carrier = error;
|
|
8256
|
-
return {
|
|
8378
|
+
return {
|
|
8379
|
+
errorMessage: carrier.subagentFailure?.errorMessage ?? error.message,
|
|
8380
|
+
retryable: carrier.subagentFailure?.retryable ?? carrier.retryable,
|
|
8381
|
+
replayClass: carrier.subagentFailure?.replayClass ?? carrier.replayClass,
|
|
8382
|
+
replayEligible: carrier.subagentFailure?.replayEligible ?? carrier.replayEligible,
|
|
8383
|
+
remoteFailure: carrier.subagentFailure?.remoteFailure ?? carrier.remoteFailure,
|
|
8384
|
+
stopReason: carrier.subagentFailure?.stopReason ?? carrier.stopReason ?? (carrier.subagentFailure?.classification?.userAbort ? "aborted" : "error")
|
|
8385
|
+
};
|
|
8257
8386
|
}
|
|
8258
|
-
return { errorMessage: String(error) };
|
|
8387
|
+
return { errorMessage: String(error), stopReason: "error" };
|
|
8259
8388
|
}
|
|
8260
|
-
function
|
|
8389
|
+
function assistantFromFacts(facts) {
|
|
8390
|
+
return {
|
|
8391
|
+
role: "assistant",
|
|
8392
|
+
api: "unknown",
|
|
8393
|
+
provider: "unknown",
|
|
8394
|
+
model: "unknown",
|
|
8395
|
+
content: [],
|
|
8396
|
+
timestamp: 0,
|
|
8397
|
+
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0, cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 } },
|
|
8398
|
+
stopReason: facts.stopReason ?? "error",
|
|
8399
|
+
errorMessage: facts.errorMessage,
|
|
8400
|
+
...facts.retryable !== void 0 ? { retryable: facts.retryable } : {},
|
|
8401
|
+
...facts.replayClass !== void 0 ? { replayClass: facts.replayClass } : {},
|
|
8402
|
+
...facts.replayEligible !== void 0 ? { replayEligible: facts.replayEligible } : {},
|
|
8403
|
+
...facts.remoteFailure !== void 0 ? { remoteFailure: facts.remoteFailure } : {}
|
|
8404
|
+
};
|
|
8405
|
+
}
|
|
8406
|
+
function boundedClassification(failure) {
|
|
8407
|
+
return { ...failure, message: failure.message.slice(0, 2e3) };
|
|
8408
|
+
}
|
|
8409
|
+
function failureDetails(facts, classification, recovery) {
|
|
8410
|
+
return {
|
|
8411
|
+
errorMessage: (classification.message || facts.errorMessage || "Subagent response failed").slice(0, 2e3),
|
|
8412
|
+
...facts.retryable !== void 0 ? { retryable: facts.retryable } : {},
|
|
8413
|
+
...facts.replayClass !== void 0 ? { replayClass: facts.replayClass } : {},
|
|
8414
|
+
...facts.replayEligible !== void 0 ? { replayEligible: facts.replayEligible } : {},
|
|
8415
|
+
...facts.remoteFailure !== void 0 ? {
|
|
8416
|
+
remoteFailure: {
|
|
8417
|
+
category: facts.remoteFailure.category,
|
|
8418
|
+
...typeof facts.remoteFailure.retryAfterMs === "number" && Number.isFinite(facts.remoteFailure.retryAfterMs) && facts.remoteFailure.retryAfterMs >= 0 ? { retryAfterMs: facts.remoteFailure.retryAfterMs } : {}
|
|
8419
|
+
}
|
|
8420
|
+
} : {},
|
|
8421
|
+
...facts.stopReason === "aborted" ? { stopReason: facts.stopReason } : {},
|
|
8422
|
+
classification: boundedClassification(classification),
|
|
8423
|
+
recovery
|
|
8424
|
+
};
|
|
8425
|
+
}
|
|
8426
|
+
function legacyRecovery(backoffMs) {
|
|
8427
|
+
return {
|
|
8428
|
+
policy: {
|
|
8429
|
+
isRetryableError: (message) => isRetryableSubagentFailure(message),
|
|
8430
|
+
maxRetries: 2,
|
|
8431
|
+
baseDelayMs: backoffMs
|
|
8432
|
+
},
|
|
8433
|
+
classifyFailure: (context) => {
|
|
8434
|
+
const facts = context.message?.role === "assistant" ? factsFromMessage(context.message) : { errorMessage: context.errorMessage, stopReason: "error" };
|
|
8435
|
+
const retryable = isRetryableSubagentFailureFrom({ ...facts, errorMessage: context.errorMessage });
|
|
8436
|
+
return {
|
|
8437
|
+
kind: retryable ? "provider_transient" : "unknown",
|
|
8438
|
+
retryable,
|
|
8439
|
+
compactable: false,
|
|
8440
|
+
userAbort: false,
|
|
8441
|
+
terminal: !retryable,
|
|
8442
|
+
message: context.errorMessage
|
|
8443
|
+
};
|
|
8444
|
+
}
|
|
8445
|
+
};
|
|
8446
|
+
}
|
|
8447
|
+
function resolveRecovery(options, backoffMs) {
|
|
8448
|
+
if (!options) return legacyRecovery(backoffMs);
|
|
8449
|
+
const fallback = legacyRecovery(backoffMs);
|
|
8450
|
+
return {
|
|
8451
|
+
policy: options.policy ?? fallback.policy,
|
|
8452
|
+
// An explicitly supplied policy without a classifier must reach its own
|
|
8453
|
+
// isRetryableError predicate, exactly as in the main controller. Only the
|
|
8454
|
+
// no-host compatible default installs the legacy prose classifier.
|
|
8455
|
+
classifyFailure: options.classifyFailure ?? (options.policy ? void 0 : fallback.classifyFailure)
|
|
8456
|
+
};
|
|
8457
|
+
}
|
|
8458
|
+
function classify(facts, recovery) {
|
|
8459
|
+
const message = assistantFromFacts(facts);
|
|
8460
|
+
return createFailureClassification({
|
|
8461
|
+
message,
|
|
8462
|
+
errorMessage: facts.errorMessage ?? "Subagent response failed",
|
|
8463
|
+
phase: "stream"
|
|
8464
|
+
}, recovery.policy, recovery.classifyFailure);
|
|
8465
|
+
}
|
|
8466
|
+
var DEFAULT_ITERATOR_CLEANUP_MS = 2e3;
|
|
8467
|
+
async function closeIterator(iterator, hooks, signal) {
|
|
8468
|
+
const cleanup = Promise.resolve().then(() => iterator.return?.()).then(() => void 0).catch(() => void 0);
|
|
8469
|
+
const remaining = hooks?.hardDeadlineAt === void 0 ? DEFAULT_ITERATOR_CLEANUP_MS : Math.max(0, hooks.hardDeadlineAt - Date.now());
|
|
8470
|
+
const waitMs = Math.min(DEFAULT_ITERATOR_CLEANUP_MS, remaining);
|
|
8471
|
+
if (waitMs <= 0 || signal?.aborted) return;
|
|
8472
|
+
let timer;
|
|
8473
|
+
let onAbort;
|
|
8474
|
+
try {
|
|
8475
|
+
await Promise.race([
|
|
8476
|
+
cleanup,
|
|
8477
|
+
new Promise((resolve2) => {
|
|
8478
|
+
timer = setTimeout(resolve2, waitMs);
|
|
8479
|
+
onAbort = () => resolve2();
|
|
8480
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
8481
|
+
})
|
|
8482
|
+
]);
|
|
8483
|
+
} finally {
|
|
8484
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
8485
|
+
if (onAbort) signal?.removeEventListener("abort", onAbort);
|
|
8486
|
+
}
|
|
8487
|
+
}
|
|
8488
|
+
function singleResponse(message) {
|
|
8489
|
+
const result = new AssistantMessageEventStream();
|
|
8490
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") {
|
|
8491
|
+
result.push({ type: "error", reason: message.stopReason, error: { ...message, content: [] } });
|
|
8492
|
+
} else {
|
|
8493
|
+
result.push({ type: "done", reason: message.stopReason, message });
|
|
8494
|
+
}
|
|
8495
|
+
result.end();
|
|
8496
|
+
return result;
|
|
8497
|
+
}
|
|
8498
|
+
function throwWithDetails(error, details) {
|
|
8499
|
+
const failure = new Error(details.classification?.message ?? (error instanceof Error ? error.message : String(error)));
|
|
8500
|
+
failure.cause = error;
|
|
8501
|
+
failure.subagentFailure = details;
|
|
8502
|
+
throw failure;
|
|
8503
|
+
}
|
|
8504
|
+
function withSubagentResponseRecovery(stream, backoffMs = 2e3, onRetry, onActivity, hooks, recoveryOptions) {
|
|
8505
|
+
const recovery = resolveRecovery(recoveryOptions, backoffMs);
|
|
8506
|
+
const episode = recoveryOptions?.episode ?? hooks?.recoveryEpisode ?? new AgentSessionRetryEpisode(recovery.policy);
|
|
8507
|
+
if (hooks) hooks.recoveryEpisode = episode;
|
|
8261
8508
|
return async (model, context, options) => {
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8509
|
+
let attempts = 0;
|
|
8510
|
+
let retries = 0;
|
|
8511
|
+
if (hooks) hooks.failure = void 0;
|
|
8512
|
+
try {
|
|
8513
|
+
for (; ; ) {
|
|
8514
|
+
if (hooks) {
|
|
8515
|
+
hooks.terminalText = void 0;
|
|
8516
|
+
hooks.terminalStopReason = void 0;
|
|
8517
|
+
}
|
|
8518
|
+
if (options?.signal?.aborted) {
|
|
8519
|
+
episode.resetAttempts();
|
|
8520
|
+
throw new Error("Subagent aborted by parent");
|
|
8521
|
+
}
|
|
8522
|
+
if (hooks?.hardDeadlineAt !== void 0 && Date.now() >= hooks.hardDeadlineAt) throw new Error("Subagent hard deadline reached");
|
|
8523
|
+
let message;
|
|
8524
|
+
const finishUsage = hooks?.usage ? beginSubagentUsage(hooks.usage, model) : void 0;
|
|
8525
|
+
attempts += 1;
|
|
8275
8526
|
try {
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8282
|
-
|
|
8283
|
-
|
|
8284
|
-
|
|
8285
|
-
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8294
|
-
|
|
8527
|
+
const response = await stream(model, context, options);
|
|
8528
|
+
let terminal;
|
|
8529
|
+
const iterator = response[Symbol.asyncIterator]();
|
|
8530
|
+
try {
|
|
8531
|
+
for (; ; ) {
|
|
8532
|
+
const next = await iterator.next();
|
|
8533
|
+
if (next.done) break;
|
|
8534
|
+
try {
|
|
8535
|
+
onActivity?.(next.value.type);
|
|
8536
|
+
} catch {
|
|
8537
|
+
}
|
|
8538
|
+
if (next.value.type === "done") {
|
|
8539
|
+
terminal = next.value.message;
|
|
8540
|
+
finishUsage?.(terminal);
|
|
8541
|
+
}
|
|
8542
|
+
if (next.value.type === "error") {
|
|
8543
|
+
terminal = next.value.error;
|
|
8544
|
+
finishUsage?.(terminal);
|
|
8545
|
+
}
|
|
8546
|
+
if (next.value.type === "done" || next.value.type === "error") {
|
|
8547
|
+
if (hooks && terminal) {
|
|
8548
|
+
const reportTerminal = ["stop", "length", "error", "aborted"].includes(terminal.stopReason) && !terminal.content.some((block) => block.type === "toolCall");
|
|
8549
|
+
hooks.terminalStopReason = reportTerminal ? terminal.stopReason : void 0;
|
|
8550
|
+
hooks.terminalText = reportTerminal ? terminal.content.filter((block) => block.type === "text").map((block) => block.text).join("\n").slice(0, 24001) : void 0;
|
|
8551
|
+
}
|
|
8552
|
+
break;
|
|
8553
|
+
}
|
|
8295
8554
|
}
|
|
8555
|
+
} finally {
|
|
8556
|
+
await closeIterator(iterator, hooks, options?.signal);
|
|
8296
8557
|
}
|
|
8297
|
-
|
|
8298
|
-
|
|
8299
|
-
|
|
8300
|
-
|
|
8558
|
+
if (!terminal) throw new Error("Provider protocol error: response ended without a terminal event");
|
|
8559
|
+
message = terminal;
|
|
8560
|
+
} catch (error) {
|
|
8561
|
+
finishUsage?.();
|
|
8562
|
+
if (options?.signal?.aborted) {
|
|
8563
|
+
episode.resetAttempts();
|
|
8564
|
+
throw error;
|
|
8301
8565
|
}
|
|
8302
|
-
|
|
8303
|
-
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8310
|
-
|
|
8566
|
+
const facts2 = factsOf(error);
|
|
8567
|
+
if (facts2.stopReason === "aborted") {
|
|
8568
|
+
episode.resetAttempts();
|
|
8569
|
+
const classification3 = {
|
|
8570
|
+
kind: "user_abort",
|
|
8571
|
+
retryable: false,
|
|
8572
|
+
compactable: false,
|
|
8573
|
+
userAbort: true,
|
|
8574
|
+
terminal: true,
|
|
8575
|
+
message: facts2.errorMessage ?? "Subagent aborted"
|
|
8576
|
+
};
|
|
8577
|
+
const details = failureDetails(facts2, classification3, { outcome: "terminal", attempts, retries, reason: "cancelled" });
|
|
8578
|
+
if (hooks) hooks.failure = details;
|
|
8579
|
+
throwWithDetails(error, details);
|
|
8311
8580
|
}
|
|
8312
|
-
|
|
8313
|
-
|
|
8581
|
+
const classification2 = classify(facts2, recovery);
|
|
8582
|
+
const failure2 = await resolveFailure({
|
|
8583
|
+
facts: facts2,
|
|
8584
|
+
classification: classification2,
|
|
8585
|
+
attempts,
|
|
8586
|
+
retries,
|
|
8587
|
+
episode,
|
|
8588
|
+
hooks,
|
|
8589
|
+
onRetry,
|
|
8590
|
+
options
|
|
8591
|
+
});
|
|
8592
|
+
if (failure2.retry) {
|
|
8593
|
+
retries += 1;
|
|
8594
|
+
continue;
|
|
8595
|
+
}
|
|
8596
|
+
throwWithDetails(error, failure2.details);
|
|
8314
8597
|
}
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8318
|
-
if (message.stopReason === "error" && attempt < 2 && isRetryableSubagentFailureFrom({ errorMessage: message.errorMessage, retryable: message.retryable, replayClass: message.replayClass })) {
|
|
8319
|
-
try {
|
|
8320
|
-
onRetry?.();
|
|
8321
|
-
} catch {
|
|
8598
|
+
if (options?.signal?.aborted) {
|
|
8599
|
+
episode.resetAttempts();
|
|
8600
|
+
throw new Error("Subagent aborted by parent");
|
|
8322
8601
|
}
|
|
8323
|
-
|
|
8324
|
-
|
|
8325
|
-
|
|
8326
|
-
|
|
8327
|
-
|
|
8328
|
-
|
|
8329
|
-
|
|
8330
|
-
|
|
8602
|
+
if (hooks?.hardDeadlineAt !== void 0 && Date.now() >= hooks.hardDeadlineAt) {
|
|
8603
|
+
episode.resetAttempts();
|
|
8604
|
+
throw new Error("Subagent hard deadline reached");
|
|
8605
|
+
}
|
|
8606
|
+
if (message.stopReason !== "error" && message.stopReason !== "aborted") {
|
|
8607
|
+
episode.succeed();
|
|
8608
|
+
if (hooks) hooks.failure = void 0;
|
|
8609
|
+
return singleResponse(message);
|
|
8610
|
+
}
|
|
8611
|
+
if (message.stopReason === "aborted") {
|
|
8612
|
+
episode.resetAttempts();
|
|
8613
|
+
if (hooks) hooks.failure = void 0;
|
|
8614
|
+
return singleResponse(message);
|
|
8615
|
+
}
|
|
8616
|
+
const facts = factsFromMessage(message);
|
|
8617
|
+
const classification = classify(facts, recovery);
|
|
8618
|
+
const failure = await resolveFailure({
|
|
8619
|
+
facts,
|
|
8620
|
+
classification,
|
|
8621
|
+
attempts,
|
|
8622
|
+
retries,
|
|
8623
|
+
episode,
|
|
8624
|
+
hooks,
|
|
8625
|
+
onRetry,
|
|
8626
|
+
options
|
|
8627
|
+
});
|
|
8628
|
+
if (failure.retry) {
|
|
8629
|
+
retries += 1;
|
|
8630
|
+
continue;
|
|
8631
|
+
}
|
|
8632
|
+
const finalMessage = failure.details.classification?.message && failure.details.classification.message !== message.errorMessage ? { ...message, errorMessage: failure.details.classification.message } : message;
|
|
8633
|
+
return singleResponse(finalMessage);
|
|
8331
8634
|
}
|
|
8332
|
-
|
|
8333
|
-
|
|
8635
|
+
} finally {
|
|
8636
|
+
episode.resetAttempts();
|
|
8334
8637
|
}
|
|
8335
8638
|
};
|
|
8336
8639
|
}
|
|
8640
|
+
async function resolveFailure(params) {
|
|
8641
|
+
const { facts, classification, attempts, retries, episode, hooks, onRetry, options } = params;
|
|
8642
|
+
if (options?.signal?.aborted) {
|
|
8643
|
+
episode.resetAttempts();
|
|
8644
|
+
throw new Error("Subagent aborted by parent");
|
|
8645
|
+
}
|
|
8646
|
+
if (classification.userAbort) {
|
|
8647
|
+
episode.resetAttempts();
|
|
8648
|
+
const cancellation = {
|
|
8649
|
+
...classification,
|
|
8650
|
+
kind: "user_abort",
|
|
8651
|
+
retryable: false,
|
|
8652
|
+
compactable: false,
|
|
8653
|
+
userAbort: true,
|
|
8654
|
+
terminal: true
|
|
8655
|
+
};
|
|
8656
|
+
const details2 = failureDetails({ ...facts, stopReason: "aborted" }, cancellation, {
|
|
8657
|
+
outcome: "terminal",
|
|
8658
|
+
attempts,
|
|
8659
|
+
retries,
|
|
8660
|
+
reason: "cancelled"
|
|
8661
|
+
});
|
|
8662
|
+
if (hooks) hooks.failure = details2;
|
|
8663
|
+
return { retry: false, details: details2 };
|
|
8664
|
+
}
|
|
8665
|
+
if (classification.compactable) {
|
|
8666
|
+
const details2 = failureDetails(facts, classification, {
|
|
8667
|
+
outcome: "overflow",
|
|
8668
|
+
attempts,
|
|
8669
|
+
retries,
|
|
8670
|
+
reason: "compaction_unavailable"
|
|
8671
|
+
});
|
|
8672
|
+
if (hooks) hooks.failure = details2;
|
|
8673
|
+
return { retry: false, details: details2 };
|
|
8674
|
+
}
|
|
8675
|
+
let decision = episode.next(classification);
|
|
8676
|
+
if (decision.action === "retry") {
|
|
8677
|
+
const remaining = hooks?.hardDeadlineAt === void 0 ? Infinity : hooks.hardDeadlineAt - Date.now();
|
|
8678
|
+
if (remaining <= 0) {
|
|
8679
|
+
episode.resetAttempts();
|
|
8680
|
+
throw new Error("Subagent hard deadline reached");
|
|
8681
|
+
}
|
|
8682
|
+
if (decision.delayMs > remaining) {
|
|
8683
|
+
const deadlineClassification = {
|
|
8684
|
+
...classification,
|
|
8685
|
+
retryable: false,
|
|
8686
|
+
terminal: true,
|
|
8687
|
+
message: `${classification.message} (recovery delay ${decision.delayMs}ms exceeds the remaining subagent deadline ${Math.max(0, Math.round(remaining))}ms)`
|
|
8688
|
+
};
|
|
8689
|
+
const details2 = failureDetails(facts, deadlineClassification, {
|
|
8690
|
+
outcome: "exhausted",
|
|
8691
|
+
attempts,
|
|
8692
|
+
retries,
|
|
8693
|
+
maxRetries: decision.maxRetries,
|
|
8694
|
+
reason: "deadline"
|
|
8695
|
+
});
|
|
8696
|
+
if (hooks) hooks.failure = details2;
|
|
8697
|
+
return { retry: false, details: details2 };
|
|
8698
|
+
}
|
|
8699
|
+
try {
|
|
8700
|
+
onRetry?.();
|
|
8701
|
+
} catch {
|
|
8702
|
+
}
|
|
8703
|
+
await cancellableBackoff(decision.delayMs, options?.signal);
|
|
8704
|
+
if (options?.signal?.aborted) {
|
|
8705
|
+
episode.resetAttempts();
|
|
8706
|
+
throw new Error("Subagent aborted by parent");
|
|
8707
|
+
}
|
|
8708
|
+
if (hooks?.hardDeadlineAt !== void 0 && Date.now() >= hooks.hardDeadlineAt) {
|
|
8709
|
+
episode.resetAttempts();
|
|
8710
|
+
throw new Error("Subagent hard deadline reached");
|
|
8711
|
+
}
|
|
8712
|
+
return { retry: true };
|
|
8713
|
+
}
|
|
8714
|
+
const finalClassification = decision.action === "exhausted" ? decision.failure : classification;
|
|
8715
|
+
const details = failureDetails(facts, finalClassification, {
|
|
8716
|
+
outcome: decision.action === "exhausted" ? "exhausted" : "terminal",
|
|
8717
|
+
attempts,
|
|
8718
|
+
retries,
|
|
8719
|
+
...decision.action !== "terminal" ? { maxRetries: decision.maxRetries } : {},
|
|
8720
|
+
...decision.action === "exhausted" ? { reason: decision.reason } : {}
|
|
8721
|
+
});
|
|
8722
|
+
if (hooks) hooks.failure = details;
|
|
8723
|
+
return { retry: false, details };
|
|
8724
|
+
}
|
|
8337
8725
|
|
|
8338
8726
|
// ../../packages/shared-headless-capabilities/src/subagent-sessions.ts
|
|
8339
8727
|
var SUBAGENT_SESSION_RECORD_VERSION = 1;
|
|
@@ -8467,8 +8855,11 @@ var POWERSHELL_DESTRUCTIVE_PATTERNS = [
|
|
|
8467
8855
|
// PowerShell resolves these aliases before execution. The classifier must
|
|
8468
8856
|
// treat the aliases exactly like their canonical cmdlets; otherwise a
|
|
8469
8857
|
// guarded shell can run `ri -Recurse -Force` without the confirmation that
|
|
8470
|
-
// `Remove-Item -Recurse -Force` receives.
|
|
8471
|
-
|
|
8858
|
+
// `Remove-Item -Recurse -Force` receives. PowerShell parameters are
|
|
8859
|
+
// prefix-abbreviatable (`-r` == `-Recurse`, `-fo` == `-Force`), so the
|
|
8860
|
+
// parameter matcher below accepts any unambiguous-style abbreviation,
|
|
8861
|
+
// not just the full spellings.
|
|
8862
|
+
{ pattern: buildPowerShellRemoveItemPattern(), warning: "may recursively or forcefully remove files", commandClass: "filesystem_write" },
|
|
8472
8863
|
{ pattern: /\b(?:Remove-Item|ri|rm|rmdir|del|erase|rd)\b[^;\n|]*(?:HKLM:|HKCU:|Registry::)/i, warning: "may remove registry entries", commandClass: "infra_destructive" },
|
|
8473
8864
|
// These write aliases are not all destructive in the recursive-delete sense,
|
|
8474
8865
|
// but they can overwrite or create arbitrary files. Keep them behind the
|
|
@@ -8482,7 +8873,10 @@ var POWERSHELL_DESTRUCTIVE_PATTERNS = [
|
|
|
8482
8873
|
];
|
|
8483
8874
|
var CMD_DESTRUCTIVE_PATTERNS = [
|
|
8484
8875
|
{ pattern: /\b(del|erase)\b[^&|\n]*(\/s|\/q)/i, warning: "may recursively or quietly delete files", commandClass: "filesystem_write" },
|
|
8485
|
-
|
|
8876
|
+
// `rd` is cmd's documented synonym for rmdir and accepts the same /s /q
|
|
8877
|
+
// switches; classifying only `rmdir` let `rd /s /q C:\\anywhere` run as an
|
|
8878
|
+
// undetected recursive deletion.
|
|
8879
|
+
{ pattern: /\b(?:rmdir|rd)\b[^&|\n]*(\/s|\/q)/i, warning: "may recursively remove directories", commandClass: "filesystem_write" },
|
|
8486
8880
|
{ pattern: /\bformat\s+[a-z]:/i, warning: "may format a drive", commandClass: "infra_destructive" },
|
|
8487
8881
|
{ pattern: /\b(takeown|icacls)\b[^&|\n]*(\/grant|\/reset|\/remove)/i, warning: "may alter ownership or permissions", commandClass: "privilege_escalation" },
|
|
8488
8882
|
{ pattern: /\bgit\s+reset\s+--hard\b/i, warning: "may discard uncommitted changes", commandClass: "git_destructive" },
|
|
@@ -8494,18 +8888,27 @@ function splitCommand(command) {
|
|
|
8494
8888
|
function firstWord(command) {
|
|
8495
8889
|
return command.trim().split(/\s+/)[0] || "";
|
|
8496
8890
|
}
|
|
8891
|
+
function buildPowerShellRemoveItemPattern() {
|
|
8892
|
+
const removeVerb = "(?:Remove-Item|ri|rm|rmdir|del|erase|rd)";
|
|
8893
|
+
const recurseg = "-(?:r(?:e(?:c(?:u(?:r(?:s(?:e?)?)?)?)?)?)?)";
|
|
8894
|
+
const force = "-(?:f(?:o(?:r(?:c(?:e?)?)?)?)?)";
|
|
8895
|
+
return new RegExp(
|
|
8896
|
+
`\\b${removeVerb}\\b[^;\\n|]*(?:${recurseg}|${force})\\b`,
|
|
8897
|
+
"i"
|
|
8898
|
+
);
|
|
8899
|
+
}
|
|
8497
8900
|
function lastPipelineCommand(command) {
|
|
8498
8901
|
const segments = command.split("|").map((part) => part.trim()).filter(Boolean);
|
|
8499
8902
|
return segments[segments.length - 1] || command;
|
|
8500
8903
|
}
|
|
8501
|
-
function splitShellSegments(command) {
|
|
8904
|
+
function splitShellSegments(command, shell = "bash") {
|
|
8502
8905
|
const segments = [];
|
|
8503
8906
|
let current = "";
|
|
8504
8907
|
let quote;
|
|
8505
8908
|
for (let index = 0; index < command.length; index += 1) {
|
|
8506
8909
|
const char = command[index];
|
|
8507
8910
|
if (quote) {
|
|
8508
|
-
if (quote === '"' && char === "\\" && index + 1 < command.length) {
|
|
8911
|
+
if (quote === '"' && char === (shell === "powershell" ? "`" : "\\") && index + 1 < command.length) {
|
|
8509
8912
|
current += char;
|
|
8510
8913
|
current += command[index + 1];
|
|
8511
8914
|
index += 1;
|
|
@@ -8520,13 +8923,13 @@ function splitShellSegments(command) {
|
|
|
8520
8923
|
current += char;
|
|
8521
8924
|
continue;
|
|
8522
8925
|
}
|
|
8523
|
-
if (char === "\\" && index + 1 < command.length) {
|
|
8926
|
+
if (char === (shell === "powershell" ? "`" : "\\") && index + 1 < command.length) {
|
|
8524
8927
|
current += char;
|
|
8525
8928
|
current += command[index + 1];
|
|
8526
8929
|
index += 1;
|
|
8527
8930
|
continue;
|
|
8528
8931
|
}
|
|
8529
|
-
if (char === "&" || char === "|" || char === ";" || char === "\n") {
|
|
8932
|
+
if (char === "&" || char === "|" || char === ";" || char === "\n" || shell === "powershell" && /[(){}]/.test(char)) {
|
|
8530
8933
|
if ((char === "&" || char === "|") && command[index + 1] === char) {
|
|
8531
8934
|
index += 1;
|
|
8532
8935
|
}
|
|
@@ -8539,7 +8942,7 @@ function splitShellSegments(command) {
|
|
|
8539
8942
|
segments.push(current);
|
|
8540
8943
|
return segments.map((part) => part.trim()).filter(Boolean);
|
|
8541
8944
|
}
|
|
8542
|
-
function splitShellWords(segment) {
|
|
8945
|
+
function splitShellWords(segment, shell = "bash") {
|
|
8543
8946
|
const words = [];
|
|
8544
8947
|
let current = "";
|
|
8545
8948
|
let quote;
|
|
@@ -8548,7 +8951,7 @@ function splitShellWords(segment) {
|
|
|
8548
8951
|
if (quote) {
|
|
8549
8952
|
if (char === quote) {
|
|
8550
8953
|
quote = void 0;
|
|
8551
|
-
} else if (quote === '"' && char === "\\" && index + 1 < segment.length) {
|
|
8954
|
+
} else if (quote === '"' && char === (shell === "powershell" ? "`" : "\\") && index + 1 < segment.length) {
|
|
8552
8955
|
current += segment[index + 1];
|
|
8553
8956
|
index += 1;
|
|
8554
8957
|
} else {
|
|
@@ -8560,7 +8963,7 @@ function splitShellWords(segment) {
|
|
|
8560
8963
|
quote = char;
|
|
8561
8964
|
continue;
|
|
8562
8965
|
}
|
|
8563
|
-
if (char === "\\") {
|
|
8966
|
+
if (char === (shell === "powershell" ? "`" : "\\")) {
|
|
8564
8967
|
if (index + 1 >= segment.length) return void 0;
|
|
8565
8968
|
current += segment[index + 1];
|
|
8566
8969
|
index += 1;
|
|
@@ -8769,7 +9172,7 @@ function classifyShellCommand(command, shell = "bash", options) {
|
|
|
8769
9172
|
const warnings = [];
|
|
8770
9173
|
let commandClass = "unknown_shell";
|
|
8771
9174
|
let destructive = false;
|
|
8772
|
-
const destructivePatterns = shell === "
|
|
9175
|
+
const destructivePatterns = shell === "cmd" ? [...CMD_DESTRUCTIVE_PATTERNS, ...DESTRUCTIVE_PATTERNS] : DESTRUCTIVE_PATTERNS;
|
|
8773
9176
|
const removeWarnings = recursiveRemoveWarnings(command);
|
|
8774
9177
|
if (removeWarnings.length > 0) {
|
|
8775
9178
|
if (shell === "bash" && isStaticallyScopedRecursiveForceRemove(command, options)) {
|
|
@@ -8786,6 +9189,37 @@ function classifyShellCommand(command, shell = "bash", options) {
|
|
|
8786
9189
|
commandClass = "unknown_shell";
|
|
8787
9190
|
destructive = true;
|
|
8788
9191
|
}
|
|
9192
|
+
if (shell === "powershell") {
|
|
9193
|
+
const segments = splitShellSegments(command, shell);
|
|
9194
|
+
const simple = !/[$`#@{}()[\]=<>]/.test(command) && segments.every((segment) => {
|
|
9195
|
+
const words2 = splitShellWords(segment, shell);
|
|
9196
|
+
if (!words2?.length) return false;
|
|
9197
|
+
const head = words2[0] === "." ? words2[1] : words2[0];
|
|
9198
|
+
return !!head && /^[A-Za-z0-9_.\\/][A-Za-z0-9_.\\/:-]*$/.test(head) && !/^(?:return|throw|exit|if|elseif|else|switch|foreach|for|while|do|until|try|catch|finally|function|filter|class|begin|process|end|trap|data|using|workflow|configuration|break|continue|inlinescript|parallel|sequence)$/i.test(head);
|
|
9199
|
+
});
|
|
9200
|
+
if (!simple) {
|
|
9201
|
+
for (const entry of POWERSHELL_DESTRUCTIVE_PATTERNS) {
|
|
9202
|
+
if (!entry.pattern.test(command)) continue;
|
|
9203
|
+
warnings.push(entry.warning);
|
|
9204
|
+
commandClass = entry.commandClass;
|
|
9205
|
+
destructive = true;
|
|
9206
|
+
}
|
|
9207
|
+
}
|
|
9208
|
+
for (const segment of segments) {
|
|
9209
|
+
const words2 = splitShellWords(segment, shell) ?? segment.split(/\s+/);
|
|
9210
|
+
if (words2[0] === ".") words2.shift();
|
|
9211
|
+
if (!words2[0]) continue;
|
|
9212
|
+
words2[0] = executableName(words2[0]);
|
|
9213
|
+
for (const entry of POWERSHELL_DESTRUCTIVE_PATTERNS) {
|
|
9214
|
+
const pattern = new RegExp(`^(?:${entry.pattern.source})`, entry.pattern.flags);
|
|
9215
|
+
if (pattern.test(words2.join(" "))) {
|
|
9216
|
+
warnings.push(entry.warning);
|
|
9217
|
+
commandClass = entry.commandClass;
|
|
9218
|
+
destructive = true;
|
|
9219
|
+
}
|
|
9220
|
+
}
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
8789
9223
|
for (const entry of destructivePatterns) {
|
|
8790
9224
|
if (entry.pattern.test(command)) {
|
|
8791
9225
|
warnings.push(entry.warning);
|
|
@@ -8801,8 +9235,6 @@ function classifyShellCommand(command, shell = "bash", options) {
|
|
|
8801
9235
|
const primary = words[0] || firstWord(normalized);
|
|
8802
9236
|
if (shell === "powershell" && /\b(invoke-webrequest|iwr|invoke-restmethod|irm)\b/.test(normalized)) {
|
|
8803
9237
|
commandClass = "network";
|
|
8804
|
-
} else if (shell === "powershell" && /\b(new-item|set-content|add-content|copy-item|move-item|rename-item)\b/.test(normalized)) {
|
|
8805
|
-
commandClass = "filesystem_write";
|
|
8806
9238
|
} else if (shell === "cmd" && /\b(curl|bitsadmin|certutil)\b/.test(normalized)) {
|
|
8807
9239
|
commandClass = "network";
|
|
8808
9240
|
} else if (shell === "cmd" && /\b(copy|xcopy|robocopy|move|mkdir|md|ren|rename)\b/.test(normalized)) {
|
|
@@ -9104,7 +9536,7 @@ function createAgentInstance(deps, systemPrompt, tools, model, apiKey, thinkingL
|
|
|
9104
9536
|
hooks.observation.lastWorkAt = Date.now();
|
|
9105
9537
|
hooks.observation.lastActivity = "response_event";
|
|
9106
9538
|
}
|
|
9107
|
-
}, hooks);
|
|
9539
|
+
}, hooks, deps.recovery);
|
|
9108
9540
|
if (deps.createAgent) {
|
|
9109
9541
|
const agent2 = deps.createAgent({
|
|
9110
9542
|
streamFn,
|
|
@@ -9578,10 +10010,21 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
|
|
|
9578
10010
|
throw new Error(`Subagent output token limit reached (stopReason=length): the provider cut the final response before the report was produced${messages.some(messageHasOwnText) ? " (earlier-turn narration discarded)" : ", no text response"}. The output/thinking budget was too small for this task at its reasoning level. Rerun with a narrower task or a lower thinkingLevel.`);
|
|
9579
10011
|
}
|
|
9580
10012
|
const detail = terminalError ? `: ${terminalError.slice(0, 200)}` : "";
|
|
9581
|
-
const
|
|
10013
|
+
const hooks2 = agentRunHooks.get(subagent);
|
|
10014
|
+
const fallbackFailure = lastAssistant && (lastAssistant.retryable !== void 0 || lastAssistant.replayClass !== void 0 || lastAssistant.replayEligible !== void 0 || lastAssistant.remoteFailure !== void 0) ? {
|
|
10015
|
+
errorMessage: terminalError,
|
|
10016
|
+
retryable: lastAssistant.retryable,
|
|
10017
|
+
replayClass: lastAssistant.replayClass,
|
|
10018
|
+
replayEligible: lastAssistant.replayEligible,
|
|
10019
|
+
remoteFailure: lastAssistant.remoteFailure
|
|
10020
|
+
} : void 0;
|
|
10021
|
+
const subagentFailure = hooks2?.failure ?? fallbackFailure;
|
|
10022
|
+
const overflow = subagentFailure?.recovery?.outcome === "overflow";
|
|
10023
|
+
const failure = new Error(overflow ? `Subagent context overflow requires compaction, but no subagent compactor is configured${detail}` : `Subagent response failed (provider/transport failure before the report was produced, stopReason=${terminalStopReason ?? "none"}${collectedResponse ? ", partial transcript discarded" : ", no text response"})${detail}`);
|
|
9582
10024
|
const carrier = failure;
|
|
9583
|
-
carrier.retryable =
|
|
9584
|
-
carrier.replayClass =
|
|
10025
|
+
carrier.retryable = subagentFailure?.retryable;
|
|
10026
|
+
carrier.replayClass = subagentFailure?.replayClass;
|
|
10027
|
+
carrier.subagentFailure = subagentFailure;
|
|
9585
10028
|
throw failure;
|
|
9586
10029
|
}
|
|
9587
10030
|
let response;
|
|
@@ -9628,8 +10071,15 @@ ${DELEGATION_REPORT_INSTRUCTION}`;
|
|
|
9628
10071
|
phaseProgress(task.observation.phase);
|
|
9629
10072
|
task.error = errorMessage.slice(0, 2e3);
|
|
9630
10073
|
const failureCarrier = error;
|
|
9631
|
-
if (failureCarrier
|
|
9632
|
-
task.failure =
|
|
10074
|
+
if (failureCarrier?.subagentFailure) {
|
|
10075
|
+
task.failure = failureCarrier.subagentFailure;
|
|
10076
|
+
} else if (failureCarrier && (failureCarrier.retryable !== void 0 || failureCarrier.replayClass !== void 0 || failureCarrier.replayEligible !== void 0 || failureCarrier.remoteFailure !== void 0)) {
|
|
10077
|
+
task.failure = {
|
|
10078
|
+
...failureCarrier.retryable !== void 0 ? { retryable: failureCarrier.retryable } : {},
|
|
10079
|
+
...failureCarrier.replayClass !== void 0 ? { replayClass: failureCarrier.replayClass } : {},
|
|
10080
|
+
...failureCarrier.replayEligible !== void 0 ? { replayEligible: failureCarrier.replayEligible } : {},
|
|
10081
|
+
...failureCarrier.remoteFailure !== void 0 ? { remoteFailure: failureCarrier.remoteFailure } : {}
|
|
10082
|
+
};
|
|
9633
10083
|
} else {
|
|
9634
10084
|
task.failure = void 0;
|
|
9635
10085
|
}
|
|
@@ -9714,6 +10164,11 @@ function buildParallelResult(tasks, totalDuration) {
|
|
|
9714
10164
|
`;
|
|
9715
10165
|
if (task.observation) resultText += `Tools: ${task.observation.toolStarts} started, ${task.observation.toolEnds} ended, ${task.observation.toolErrors} errors
|
|
9716
10166
|
`;
|
|
10167
|
+
if (task.failure?.recovery) {
|
|
10168
|
+
const recovery = task.failure.recovery;
|
|
10169
|
+
resultText += `Recovery: ${task.failure.classification?.kind ?? "unclassified"}; outcome=${recovery.outcome}${recovery.reason ? ` (${recovery.reason})` : ""}; attempts=${recovery.attempts}; retries=${recovery.retries}${recovery.maxRetries !== void 0 ? `; budget=${recovery.maxRetries}` : ""}
|
|
10170
|
+
`;
|
|
10171
|
+
}
|
|
9717
10172
|
if (task.status === "completed" && task.result) {
|
|
9718
10173
|
resultText += `${task.result}
|
|
9719
10174
|
`;
|
|
@@ -9965,40 +10420,8 @@ ${SPAWN_USAGE_HINT}` }],
|
|
|
9965
10420
|
toolContext.log?.(`Spawning ${tasks.length} parallel subagents: ${tasks.map((task) => task.name).join(", ")}`);
|
|
9966
10421
|
toolContext.reportProgress?.({ message: `Launching ${tasks.length} parallel subagents...`, increment: 5 });
|
|
9967
10422
|
const startTime = Date.now();
|
|
9968
|
-
const runTask = async (task) => {
|
|
9969
|
-
await run(task, toolContext.reportProgress, signal);
|
|
9970
|
-
if (task.wasRecall || task.mode !== "read" || task.status !== "failed" || !task.error) return;
|
|
9971
|
-
if (signal?.aborted) return;
|
|
9972
|
-
if (!isRetryableSubagentFailureFrom({ errorMessage: task.error, retryable: task.failure?.retryable, replayClass: task.failure?.replayClass })) return;
|
|
9973
|
-
observeDelegationPhase(task.observation, "task_replay");
|
|
9974
|
-
const settleMs = Math.max(0, (deps.transientRetryBackoffMs ?? 2e3) * 10);
|
|
9975
|
-
toolContext.reportProgress?.({ message: `[${task.name}] Provider/transport failure \u2014 replaying the read task once after ${Math.round(settleMs / 1e3)}s`, increment: 0 });
|
|
9976
|
-
await cancellableBackoff(settleMs, signal);
|
|
9977
|
-
if (signal.aborted) {
|
|
9978
|
-
task.status = "cancelled";
|
|
9979
|
-
task.error = "Subagent cancelled during replay backoff";
|
|
9980
|
-
task.failure = void 0;
|
|
9981
|
-
observeDelegationPhase(task.observation, "cancelled");
|
|
9982
|
-
task.completedAt = Date.now();
|
|
9983
|
-
return;
|
|
9984
|
-
}
|
|
9985
|
-
task.evidence = [];
|
|
9986
|
-
task.confidenceSignals = [];
|
|
9987
|
-
task.runtimeEvidence = [];
|
|
9988
|
-
task.reportedEvidence = [];
|
|
9989
|
-
task.error = void 0;
|
|
9990
|
-
task.failure = void 0;
|
|
9991
|
-
task.report = void 0;
|
|
9992
|
-
task.result = void 0;
|
|
9993
|
-
task.details = void 0;
|
|
9994
|
-
task.parentHandoffRequired = false;
|
|
9995
|
-
task.sessionId = void 0;
|
|
9996
|
-
task.requestedMode = void 0;
|
|
9997
|
-
task.wasRecall = void 0;
|
|
9998
|
-
await run(task, toolContext.reportProgress, signal);
|
|
9999
|
-
};
|
|
10000
10423
|
const batch = Promise.resolve().then(() => runWithConcurrencyLimit(
|
|
10001
|
-
tasks.map((task) => () =>
|
|
10424
|
+
tasks.map((task) => () => run(task, toolContext.reportProgress, signal)),
|
|
10002
10425
|
Math.max(1, deps.maxConcurrentSubagents ?? DEFAULT_MAX_CONCURRENT_SUBAGENTS)
|
|
10003
10426
|
));
|
|
10004
10427
|
pending.add(batch);
|
|
@@ -10041,9 +10464,8 @@ ${SPAWN_USAGE_HINT}` }],
|
|
|
10041
10464
|
observation: task.observation,
|
|
10042
10465
|
parentHandoffRequired: task.parentHandoffRequired,
|
|
10043
10466
|
failureReason: task.error,
|
|
10044
|
-
// Structured provider
|
|
10045
|
-
//
|
|
10046
|
-
// provider vetoes (auth/quota/config — not recoverable here).
|
|
10467
|
+
// Structured provider facts plus the actual recovery outcome. Failed-
|
|
10468
|
+
// response tool calls never execute; do not infer their absence.
|
|
10047
10469
|
failure: task.failure,
|
|
10048
10470
|
details: task.details
|
|
10049
10471
|
})),
|
|
@@ -10266,7 +10688,11 @@ function executeManageTodoList(args, currentTodos) {
|
|
|
10266
10688
|
};
|
|
10267
10689
|
}
|
|
10268
10690
|
default:
|
|
10269
|
-
return rejectTodoAction(
|
|
10691
|
+
return rejectTodoAction(
|
|
10692
|
+
"read",
|
|
10693
|
+
`unknown action: ${args.action}. Valid actions: create, read, complete_current, clear.`,
|
|
10694
|
+
safeCurrentTodos
|
|
10695
|
+
);
|
|
10270
10696
|
}
|
|
10271
10697
|
}
|
|
10272
10698
|
|
|
@@ -11115,6 +11541,7 @@ var resolveToCwd = resolveInSandbox;
|
|
|
11115
11541
|
var resolveReadPath = resolveInSandbox;
|
|
11116
11542
|
|
|
11117
11543
|
// ../../packages/shared-headless-capabilities/src/builtin/fs/schema-utils.ts
|
|
11544
|
+
var SCOPE_BASELINE_SENTENCE = "Absolute paths outside the sandbox are accepted only within configured access roots or when the current runtime mode's allowlist permits them; the effective scope is stated in the runtime-mode context, not in this description.";
|
|
11118
11545
|
function formatPrefixes(prefixes) {
|
|
11119
11546
|
if (!prefixes || prefixes.length === 0) {
|
|
11120
11547
|
return "";
|
|
@@ -11124,6 +11551,11 @@ function formatPrefixes(prefixes) {
|
|
|
11124
11551
|
return trimmed.endsWith("/") ? trimmed : `${trimmed}/`;
|
|
11125
11552
|
}).join(", ");
|
|
11126
11553
|
}
|
|
11554
|
+
function arraysEqual(a, b) {
|
|
11555
|
+
if (a === b) return true;
|
|
11556
|
+
if (a.length !== b.length) return false;
|
|
11557
|
+
return a.every((value, index) => value === b[index]);
|
|
11558
|
+
}
|
|
11127
11559
|
function buildRootParameterSchema(sandboxRoot, options) {
|
|
11128
11560
|
const roots = getAvailableRoots(options);
|
|
11129
11561
|
if (roots.length === 0) {
|
|
@@ -11147,8 +11579,11 @@ function buildRootParameterSchema(sandboxRoot, options) {
|
|
|
11147
11579
|
function buildPathResolutionDescription(sandboxRoot, options) {
|
|
11148
11580
|
const roots = getAvailableRoots(options);
|
|
11149
11581
|
const allowlistedDirs = options?.allowlistedDirs ?? [];
|
|
11150
|
-
const
|
|
11151
|
-
const
|
|
11582
|
+
const descriptionOverride = options?.descriptionAllowlistedDirs;
|
|
11583
|
+
const describedAllowlist = descriptionOverride ?? allowlistedDirs;
|
|
11584
|
+
const viewsEqual = arraysEqual(describedAllowlist, allowlistedDirs);
|
|
11585
|
+
const hasGlobalAllowlist = describedAllowlist.some((dir) => dir.trim() === "/") && viewsEqual;
|
|
11586
|
+
const allowlistSentence = hasGlobalAllowlist ? "Absolute paths anywhere on the local filesystem are accepted in the current runtime mode." : viewsEqual ? describedAllowlist.length > 0 ? `Absolute paths inside allowlisted directories are accepted: ${describedAllowlist.join(", ")}.` : "" : SCOPE_BASELINE_SENTENCE;
|
|
11152
11587
|
if (roots.length === 0) {
|
|
11153
11588
|
return ["Relative paths resolve against the workspace root.", allowlistSentence].filter(Boolean).join(" ");
|
|
11154
11589
|
}
|
|
@@ -11673,9 +12108,10 @@ Focus: ${question.trim()}` : ""}` },
|
|
|
11673
12108
|
return;
|
|
11674
12109
|
}
|
|
11675
12110
|
if (magicInfo?.kind === "image") {
|
|
12111
|
+
const inlineSupported = INLINE_IMAGE_MIME_TYPES.has(magicInfo.mimeType);
|
|
11676
12112
|
resolve2({
|
|
11677
|
-
content: [{ type: "text", text: `Cannot attach image file ${path3}: unsupported image payload ${magicInfo.mimeType}.` }],
|
|
11678
|
-
details: { understanding: { kind: "image", mimeType: magicInfo.mimeType, fallbackReason: "unsupported inline image format" } },
|
|
12113
|
+
content: [{ type: "text", text: inlineSupported ? `Cannot attach image file ${path3}: the host filesystem bridge returned no usable image data for ${magicInfo.mimeType} (see logs for readImageInfo diagnostics).` : `Cannot attach image file ${path3}: unsupported image payload ${magicInfo.mimeType}.` }],
|
|
12114
|
+
details: { understanding: { kind: "image", mimeType: magicInfo.mimeType, fallbackReason: inlineSupported ? "image bridge read failed" : "unsupported inline image format" } },
|
|
11679
12115
|
isError: true
|
|
11680
12116
|
});
|
|
11681
12117
|
return;
|
|
@@ -12082,11 +12518,10 @@ function detectTextOnlyBinary(input, filePath, prefixMayBeTruncated) {
|
|
|
12082
12518
|
async function readImageInfo(path3, fs2) {
|
|
12083
12519
|
try {
|
|
12084
12520
|
let base64 = "";
|
|
12085
|
-
const size = (await fs2.stat(path3).catch(() => void 0))?.size ?? 0;
|
|
12086
12521
|
if (typeof fs2.readFile === "function") {
|
|
12087
12522
|
base64 = await fs2.readFile(path3, "base64").catch(() => "");
|
|
12088
12523
|
}
|
|
12089
|
-
if (!base64 &&
|
|
12524
|
+
if (!base64 && typeof fs2.readFileSync === "function") {
|
|
12090
12525
|
base64 = fs2.readFileSync(path3, "base64");
|
|
12091
12526
|
}
|
|
12092
12527
|
if (!base64) {
|
|
@@ -12114,6 +12549,8 @@ async function readImageInfo(path3, fs2) {
|
|
|
12114
12549
|
if (bytes.length >= 12 && asciiAt(bytes, 0, 4) === "RIFF" && asciiAt(bytes, 8, 12 - 8) === "WEBP") {
|
|
12115
12550
|
return { mimeType: "image/webp", base64, ...parseWebpDimensions(bytes) };
|
|
12116
12551
|
}
|
|
12552
|
+
console.error(`[pie] readImageInfo decoded ${bytes.length} bytes for ${path3} but no known image signature matched`);
|
|
12553
|
+
return void 0;
|
|
12117
12554
|
} catch (error) {
|
|
12118
12555
|
console.error(`[pie] readImageInfo failed for ${path3}: ${error instanceof Error ? error.message : String(error)}`);
|
|
12119
12556
|
return void 0;
|
|
@@ -12121,7 +12558,7 @@ async function readImageInfo(path3, fs2) {
|
|
|
12121
12558
|
return void 0;
|
|
12122
12559
|
}
|
|
12123
12560
|
function decodeBase64ToBytes(base64) {
|
|
12124
|
-
if (
|
|
12561
|
+
if (hasUsableNodeBuffer()) {
|
|
12125
12562
|
return Buffer.from(base64, "base64");
|
|
12126
12563
|
}
|
|
12127
12564
|
const clean = base64.replace(/[^A-Za-z0-9+/=]/g, "");
|
|
@@ -12140,6 +12577,17 @@ function decodeBase64ToBytes(base64) {
|
|
|
12140
12577
|
}
|
|
12141
12578
|
return bytes.subarray(0, at);
|
|
12142
12579
|
}
|
|
12580
|
+
function hasUsableNodeBuffer() {
|
|
12581
|
+
if (typeof Buffer === "undefined" || typeof Buffer.from !== "function") {
|
|
12582
|
+
return false;
|
|
12583
|
+
}
|
|
12584
|
+
try {
|
|
12585
|
+
const probe = Buffer.from("aGk=", "base64");
|
|
12586
|
+
return probe.length === 2 && probe[0] === 104;
|
|
12587
|
+
} catch {
|
|
12588
|
+
return false;
|
|
12589
|
+
}
|
|
12590
|
+
}
|
|
12143
12591
|
function readUIntBE(bytes, offset, length) {
|
|
12144
12592
|
let value = 0;
|
|
12145
12593
|
for (let i = 0; i < length; i += 1) {
|