@cydm/pie 1.0.15 → 1.0.17
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/dist/builtin/extensions/ask-user/index.js +1 -1
- package/dist/builtin/extensions/plan-mode/index.js +1 -1
- package/dist/builtin/extensions/subagent/index.js +2 -2
- package/dist/builtin/extensions/todo/index.js +1 -1
- package/dist/chunks/{chunk-55RUSXEA.js → chunk-4MIOCDBV.js} +1 -1
- package/dist/chunks/{chunk-R5LYKDKA.js → chunk-B4VUX6SD.js} +1027 -158
- package/dist/cli.js +444 -141
- package/package.json +2 -2
|
@@ -1471,36 +1471,66 @@ var DEFAULT_COMPACTION_CONFIG = {
|
|
|
1471
1471
|
compactPrompt: COMPACT_PROMPT,
|
|
1472
1472
|
summaryPrefix: SUMMARY_PREFIX
|
|
1473
1473
|
};
|
|
1474
|
+
var APPROX_BYTES_PER_TOKEN = 4;
|
|
1475
|
+
var RESIZED_IMAGE_BYTES_ESTIMATE = 7373;
|
|
1474
1476
|
function estimateTokens(message) {
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
return Math.ceil(chars / 4);
|
|
1477
|
+
return approximateBytesAsTokens(estimateModelVisibleBytes(message));
|
|
1478
|
+
}
|
|
1479
|
+
function calculateTotalTokens(messages) {
|
|
1480
|
+
return messages.reduce((sum, m) => sum + estimateTokens(m), 0);
|
|
1481
|
+
}
|
|
1482
|
+
function calculateContextTokens(usage) {
|
|
1483
|
+
return usage.totalTokens || usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
|
|
1484
|
+
}
|
|
1485
|
+
function getAssistantUsage(message) {
|
|
1486
|
+
if (message.role !== "assistant") {
|
|
1487
|
+
return void 0;
|
|
1487
1488
|
}
|
|
1488
|
-
if (message.
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1489
|
+
if (message.stopReason === "aborted" || message.stopReason === "error") {
|
|
1490
|
+
return void 0;
|
|
1491
|
+
}
|
|
1492
|
+
if (calculateContextTokens(message.usage) <= 0) {
|
|
1493
|
+
return void 0;
|
|
1494
|
+
}
|
|
1495
|
+
return message.usage;
|
|
1496
|
+
}
|
|
1497
|
+
function estimateContextTokens(messages) {
|
|
1498
|
+
const localTokens = calculateTotalTokens(messages);
|
|
1499
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
1500
|
+
const usage = getAssistantUsage(messages[i]);
|
|
1501
|
+
if (!usage) {
|
|
1502
|
+
continue;
|
|
1496
1503
|
}
|
|
1497
|
-
|
|
1504
|
+
const providerTokens = calculateContextTokens(usage);
|
|
1505
|
+
const trailingTokens = calculateTotalTokens(messages.slice(i + 1));
|
|
1506
|
+
const usageTokens = providerTokens;
|
|
1507
|
+
return {
|
|
1508
|
+
tokens: Math.max(providerTokens + trailingTokens, localTokens),
|
|
1509
|
+
usageTokens,
|
|
1510
|
+
providerTokens,
|
|
1511
|
+
localTokens,
|
|
1512
|
+
trailingTokens,
|
|
1513
|
+
lastUsageIndex: i
|
|
1514
|
+
};
|
|
1498
1515
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1516
|
+
return {
|
|
1517
|
+
tokens: localTokens,
|
|
1518
|
+
usageTokens: 0,
|
|
1519
|
+
providerTokens: 0,
|
|
1520
|
+
localTokens,
|
|
1521
|
+
trailingTokens: localTokens,
|
|
1522
|
+
lastUsageIndex: null
|
|
1523
|
+
};
|
|
1501
1524
|
}
|
|
1502
|
-
function
|
|
1503
|
-
|
|
1525
|
+
function getAutoCompactTokenLimit(contextWindow, reserveTokens, autoCompactTokenLimit) {
|
|
1526
|
+
const explicitLimit = autoCompactTokenLimit !== void 0 && autoCompactTokenLimit > 0 ? Math.floor(autoCompactTokenLimit) : void 0;
|
|
1527
|
+
if (!contextWindow || contextWindow <= 0) {
|
|
1528
|
+
return explicitLimit;
|
|
1529
|
+
}
|
|
1530
|
+
const reserveLimit = reserveTokens >= contextWindow ? Math.floor(contextWindow * 0.8) : contextWindow - reserveTokens;
|
|
1531
|
+
const windowLimit = Math.floor(contextWindow * 0.9);
|
|
1532
|
+
const defaultLimit = Math.max(1, Math.min(reserveLimit, windowLimit));
|
|
1533
|
+
return explicitLimit !== void 0 ? Math.min(defaultLimit, explicitLimit) : defaultLimit;
|
|
1504
1534
|
}
|
|
1505
1535
|
function isCompactSummaryMessage(message, summaryPrefix = SUMMARY_PREFIX) {
|
|
1506
1536
|
return message.startsWith(`${summaryPrefix}
|
|
@@ -1603,10 +1633,17 @@ ${sections.join("\n\n")}` : "";
|
|
|
1603
1633
|
}
|
|
1604
1634
|
async function createCompactionSummary(messages, options = {}) {
|
|
1605
1635
|
const { model, apiKey, systemPrompt, compactPrompt, signal } = options;
|
|
1636
|
+
const throwIfAborted = () => {
|
|
1637
|
+
if (signal?.aborted) {
|
|
1638
|
+
throw new Error("Compaction aborted");
|
|
1639
|
+
}
|
|
1640
|
+
};
|
|
1606
1641
|
if (!model) {
|
|
1642
|
+
throwIfAborted();
|
|
1607
1643
|
return appendFileOperationsToSummary(createSimpleSummary(messages), messages);
|
|
1608
1644
|
}
|
|
1609
1645
|
try {
|
|
1646
|
+
throwIfAborted();
|
|
1610
1647
|
const response = await streamSimple(
|
|
1611
1648
|
model,
|
|
1612
1649
|
{
|
|
@@ -1625,8 +1662,10 @@ async function createCompactionSummary(messages, options = {}) {
|
|
|
1625
1662
|
signal
|
|
1626
1663
|
}
|
|
1627
1664
|
).result();
|
|
1665
|
+
throwIfAborted();
|
|
1628
1666
|
return appendFileOperationsToSummary(getAssistantText(response) || createSimpleSummary(messages), messages);
|
|
1629
1667
|
} catch (error) {
|
|
1668
|
+
throwIfAborted();
|
|
1630
1669
|
console.warn("[Compaction] Failed to create Codex-style summary:", error);
|
|
1631
1670
|
return appendFileOperationsToSummary(createSimpleSummary(messages), messages);
|
|
1632
1671
|
}
|
|
@@ -1692,6 +1731,138 @@ function appendFileOperationsToSummary(summary, messages) {
|
|
|
1692
1731
|
const { readFiles, modifiedFiles } = computeFileLists(fileOps);
|
|
1693
1732
|
return `${summary}${formatFileOperations(readFiles, modifiedFiles)}`;
|
|
1694
1733
|
}
|
|
1734
|
+
function estimateModelVisibleBytes(message) {
|
|
1735
|
+
const visibleMessage = normalizeModelVisibleMessage(message);
|
|
1736
|
+
const wrapperBytes = utf8ByteLength(safeStringify(visibleMessage));
|
|
1737
|
+
return wrapperBytes + estimateMessageMediaBytes(message);
|
|
1738
|
+
}
|
|
1739
|
+
function normalizeModelVisibleMessage(message) {
|
|
1740
|
+
if (message.role === "user") {
|
|
1741
|
+
return {
|
|
1742
|
+
role: "user",
|
|
1743
|
+
content: typeof message.content === "string" ? message.content : message.content.map(normalizeUserContentBlock)
|
|
1744
|
+
};
|
|
1745
|
+
}
|
|
1746
|
+
if (message.role === "assistant") {
|
|
1747
|
+
return {
|
|
1748
|
+
role: "assistant",
|
|
1749
|
+
content: message.content.map((block) => {
|
|
1750
|
+
if (block.type === "text") {
|
|
1751
|
+
return {
|
|
1752
|
+
type: "text",
|
|
1753
|
+
text: block.text,
|
|
1754
|
+
...block.textSignature ? { textSignature: block.textSignature } : {}
|
|
1755
|
+
};
|
|
1756
|
+
}
|
|
1757
|
+
if (block.type === "thinking") {
|
|
1758
|
+
return {
|
|
1759
|
+
type: "thinking",
|
|
1760
|
+
thinking: block.thinking,
|
|
1761
|
+
...block.thinkingSignature ? { thinkingSignature: block.thinkingSignature } : {}
|
|
1762
|
+
};
|
|
1763
|
+
}
|
|
1764
|
+
if (block.type === "toolCall") {
|
|
1765
|
+
return {
|
|
1766
|
+
type: "toolCall",
|
|
1767
|
+
id: block.id,
|
|
1768
|
+
name: block.name,
|
|
1769
|
+
arguments: block.arguments ?? {},
|
|
1770
|
+
...block.thoughtSignature ? { thoughtSignature: block.thoughtSignature } : {}
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
return block;
|
|
1774
|
+
})
|
|
1775
|
+
};
|
|
1776
|
+
}
|
|
1777
|
+
if (message.role === "toolResult") {
|
|
1778
|
+
return {
|
|
1779
|
+
role: "toolResult",
|
|
1780
|
+
toolCallId: message.toolCallId,
|
|
1781
|
+
toolName: message.toolName,
|
|
1782
|
+
isError: message.isError,
|
|
1783
|
+
content: message.content.map(normalizeUserContentBlock)
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
return {
|
|
1787
|
+
role: String(message.role ?? "unknown"),
|
|
1788
|
+
content: getMessageText(message)
|
|
1789
|
+
};
|
|
1790
|
+
}
|
|
1791
|
+
function normalizeUserContentBlock(block) {
|
|
1792
|
+
if (block.type === "text") {
|
|
1793
|
+
return { type: "text", text: block.text };
|
|
1794
|
+
}
|
|
1795
|
+
if (block.type === "image") {
|
|
1796
|
+
return { type: "image", mimeType: block.mimeType };
|
|
1797
|
+
}
|
|
1798
|
+
if (block.type === "audio") {
|
|
1799
|
+
return { type: "audio", mimeType: block.mimeType };
|
|
1800
|
+
}
|
|
1801
|
+
if (block.type === "video") {
|
|
1802
|
+
return { type: "video", mimeType: block.mimeType };
|
|
1803
|
+
}
|
|
1804
|
+
if (block.type === "fileRef") {
|
|
1805
|
+
return {
|
|
1806
|
+
type: "fileRef",
|
|
1807
|
+
url: block.url,
|
|
1808
|
+
mimeType: block.mimeType,
|
|
1809
|
+
modality: block.modality
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
return block;
|
|
1813
|
+
}
|
|
1814
|
+
function estimateMessageMediaBytes(message) {
|
|
1815
|
+
if (message.role === "user" && Array.isArray(message.content)) {
|
|
1816
|
+
return message.content.reduce((sum, block) => sum + estimateUserContentBlockMediaBytes(block), 0);
|
|
1817
|
+
}
|
|
1818
|
+
if (message.role === "toolResult") {
|
|
1819
|
+
return message.content.reduce((sum, block) => sum + estimateUserContentBlockMediaBytes(block), 0);
|
|
1820
|
+
}
|
|
1821
|
+
return 0;
|
|
1822
|
+
}
|
|
1823
|
+
function estimateUserContentBlockMediaBytes(block) {
|
|
1824
|
+
if (block.type === "image") {
|
|
1825
|
+
return estimateImageContentBytes(block);
|
|
1826
|
+
}
|
|
1827
|
+
if (block.type === "audio") {
|
|
1828
|
+
return estimateInlineDataBytes(block);
|
|
1829
|
+
}
|
|
1830
|
+
if (block.type === "video") {
|
|
1831
|
+
return estimateInlineDataBytes(block);
|
|
1832
|
+
}
|
|
1833
|
+
return 0;
|
|
1834
|
+
}
|
|
1835
|
+
function estimateImageContentBytes(block) {
|
|
1836
|
+
return Math.max(RESIZED_IMAGE_BYTES_ESTIMATE, estimateInlineDataBytes(block));
|
|
1837
|
+
}
|
|
1838
|
+
function estimateInlineDataBytes(block) {
|
|
1839
|
+
return utf8ByteLength(block.data || "");
|
|
1840
|
+
}
|
|
1841
|
+
function approximateBytesAsTokens(bytes) {
|
|
1842
|
+
return Math.ceil(bytes / APPROX_BYTES_PER_TOKEN);
|
|
1843
|
+
}
|
|
1844
|
+
function safeStringify(value) {
|
|
1845
|
+
try {
|
|
1846
|
+
return JSON.stringify(value) ?? "";
|
|
1847
|
+
} catch {
|
|
1848
|
+
return String(value ?? "");
|
|
1849
|
+
}
|
|
1850
|
+
}
|
|
1851
|
+
var utf8Encoder = typeof globalThis.TextEncoder === "function" ? new globalThis.TextEncoder() : void 0;
|
|
1852
|
+
function utf8ByteLength(text) {
|
|
1853
|
+
if (utf8Encoder) {
|
|
1854
|
+
return utf8Encoder.encode(text).length;
|
|
1855
|
+
}
|
|
1856
|
+
let bytes = 0;
|
|
1857
|
+
for (const char of text) {
|
|
1858
|
+
const codePoint = char.codePointAt(0) ?? 0;
|
|
1859
|
+
if (codePoint <= 127) bytes += 1;
|
|
1860
|
+
else if (codePoint <= 2047) bytes += 2;
|
|
1861
|
+
else if (codePoint <= 65535) bytes += 3;
|
|
1862
|
+
else bytes += 4;
|
|
1863
|
+
}
|
|
1864
|
+
return bytes;
|
|
1865
|
+
}
|
|
1695
1866
|
function isSyntheticUserEnvelope(text) {
|
|
1696
1867
|
const trimmed = text.trimStart();
|
|
1697
1868
|
return trimmed.startsWith("<environment_context>") || trimmed.startsWith("<turn_aborted>") || trimmed.startsWith("# AGENTS.md instructions") || trimmed.startsWith("## Project Context\nProject-specific instructions from AGENTS.md");
|
|
@@ -2807,13 +2978,21 @@ var AgentSessionAutoCompactScheduler = class {
|
|
|
2807
2978
|
options;
|
|
2808
2979
|
autoCompactTimer;
|
|
2809
2980
|
pendingAutoCompact;
|
|
2981
|
+
runningAutoCompact;
|
|
2982
|
+
runningAbortController;
|
|
2983
|
+
pendingWait;
|
|
2984
|
+
resolvePendingWait;
|
|
2810
2985
|
get hasPendingAutoCompact() {
|
|
2811
|
-
return this.autoCompactTimer !== void 0;
|
|
2986
|
+
return this.autoCompactTimer !== void 0 || this.pendingAutoCompact !== void 0 || this.runningAutoCompact !== void 0;
|
|
2987
|
+
}
|
|
2988
|
+
waitForAutoCompact() {
|
|
2989
|
+
return this.pendingWait ?? Promise.resolve();
|
|
2812
2990
|
}
|
|
2813
2991
|
schedule(decision, delayMs = 0) {
|
|
2814
2992
|
if (this.options.getRuntimeGuardTriggered()) {
|
|
2815
2993
|
return;
|
|
2816
2994
|
}
|
|
2995
|
+
this.ensurePendingWait();
|
|
2817
2996
|
this.pendingAutoCompact = decision;
|
|
2818
2997
|
if (this.autoCompactTimer !== void 0) {
|
|
2819
2998
|
return;
|
|
@@ -2824,6 +3003,7 @@ var AgentSessionAutoCompactScheduler = class {
|
|
|
2824
3003
|
const pending = this.pendingAutoCompact;
|
|
2825
3004
|
this.pendingAutoCompact = void 0;
|
|
2826
3005
|
if (!pending || this.options.getDisposed()) {
|
|
3006
|
+
this.resolvePending();
|
|
2827
3007
|
return;
|
|
2828
3008
|
}
|
|
2829
3009
|
if (this.options.isStreaming()) {
|
|
@@ -2831,17 +3011,42 @@ var AgentSessionAutoCompactScheduler = class {
|
|
|
2831
3011
|
this.schedule(pending, 100);
|
|
2832
3012
|
return;
|
|
2833
3013
|
}
|
|
2834
|
-
|
|
3014
|
+
const abortController = new AbortController();
|
|
3015
|
+
this.runningAbortController = abortController;
|
|
3016
|
+
const running = this.options.runAutoCompact(pending, abortController.signal).catch(() => {
|
|
3017
|
+
}).finally(() => {
|
|
3018
|
+
if (this.runningAutoCompact === running) {
|
|
3019
|
+
this.runningAutoCompact = void 0;
|
|
3020
|
+
this.runningAbortController = void 0;
|
|
3021
|
+
this.resolvePending();
|
|
3022
|
+
}
|
|
3023
|
+
});
|
|
3024
|
+
this.runningAutoCompact = running;
|
|
2835
3025
|
}, delayMs);
|
|
2836
3026
|
}
|
|
2837
3027
|
cancel() {
|
|
2838
|
-
if (this.autoCompactTimer
|
|
2839
|
-
|
|
3028
|
+
if (this.autoCompactTimer !== void 0) {
|
|
3029
|
+
const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout;
|
|
3030
|
+
clearTimer(this.autoCompactTimer);
|
|
3031
|
+
this.autoCompactTimer = void 0;
|
|
2840
3032
|
}
|
|
2841
|
-
const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout;
|
|
2842
|
-
clearTimer(this.autoCompactTimer);
|
|
2843
|
-
this.autoCompactTimer = void 0;
|
|
2844
3033
|
this.pendingAutoCompact = void 0;
|
|
3034
|
+
this.runningAbortController?.abort();
|
|
3035
|
+
this.runningAbortController = void 0;
|
|
3036
|
+
this.runningAutoCompact = void 0;
|
|
3037
|
+
this.resolvePending();
|
|
3038
|
+
}
|
|
3039
|
+
ensurePendingWait() {
|
|
3040
|
+
if (this.pendingWait) return;
|
|
3041
|
+
this.pendingWait = new Promise((resolve2) => {
|
|
3042
|
+
this.resolvePendingWait = resolve2;
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
3045
|
+
resolvePending() {
|
|
3046
|
+
const resolve2 = this.resolvePendingWait;
|
|
3047
|
+
this.pendingWait = void 0;
|
|
3048
|
+
this.resolvePendingWait = void 0;
|
|
3049
|
+
resolve2?.();
|
|
2845
3050
|
}
|
|
2846
3051
|
};
|
|
2847
3052
|
|
|
@@ -2858,15 +3063,24 @@ var AgentSessionAutoContinueController = class {
|
|
|
2858
3063
|
}
|
|
2859
3064
|
options;
|
|
2860
3065
|
autoContinueTimer;
|
|
3066
|
+
pendingAutoContinue;
|
|
3067
|
+
resolvePendingAutoContinue;
|
|
3068
|
+
get hasPendingAutoContinue() {
|
|
3069
|
+
return this.autoContinueTimer !== void 0 || this.pendingAutoContinue !== void 0;
|
|
3070
|
+
}
|
|
3071
|
+
waitForAutoContinue() {
|
|
3072
|
+
return this.pendingAutoContinue ?? Promise.resolve();
|
|
3073
|
+
}
|
|
2861
3074
|
schedule(messages) {
|
|
2862
3075
|
if (!this.options.getAutoContinueMessage || this.options.getDisposed() || this.autoContinueTimer !== void 0) {
|
|
2863
3076
|
return;
|
|
2864
3077
|
}
|
|
3078
|
+
this.ensurePending();
|
|
2865
3079
|
const setTimer = this.options.retry?.setTimeout ?? defaultSetTimeout2;
|
|
2866
3080
|
const snapshot = messages.slice();
|
|
2867
3081
|
this.autoContinueTimer = setTimer(() => {
|
|
2868
3082
|
this.autoContinueTimer = void 0;
|
|
2869
|
-
void this.maybeScheduleAutoContinue(snapshot);
|
|
3083
|
+
void this.maybeScheduleAutoContinue(snapshot).finally(() => this.resolvePending());
|
|
2870
3084
|
}, 0);
|
|
2871
3085
|
}
|
|
2872
3086
|
cancel() {
|
|
@@ -2876,9 +3090,10 @@ var AgentSessionAutoContinueController = class {
|
|
|
2876
3090
|
const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout2;
|
|
2877
3091
|
clearTimer(this.autoContinueTimer);
|
|
2878
3092
|
this.autoContinueTimer = void 0;
|
|
3093
|
+
this.resolvePending();
|
|
2879
3094
|
}
|
|
2880
3095
|
async maybeScheduleAutoContinue(messages) {
|
|
2881
|
-
if (!this.options.getAutoContinueMessage || this.options.getDisposed() || this.options.getRuntimeGuardTriggered() || this.options.hasQueuedMessages() || this.options.isStreaming()) {
|
|
3096
|
+
if (!this.options.getAutoContinueMessage || this.options.getDisposed() || this.options.getRuntimeGuardTriggered() || this.options.isBlockedByRuntimePolicy() || this.options.hasQueuedMessages() || this.options.isStreaming()) {
|
|
2882
3097
|
if (this.options.hasQueuedMessages()) {
|
|
2883
3098
|
this.options.emit({ type: "auto_continue_skipped", reason: "queued_messages" });
|
|
2884
3099
|
}
|
|
@@ -2902,21 +3117,36 @@ var AgentSessionAutoContinueController = class {
|
|
|
2902
3117
|
this.options.emit({ type: "auto_continue_skipped", reason: reason ?? "no_message" });
|
|
2903
3118
|
return;
|
|
2904
3119
|
}
|
|
2905
|
-
if (this.options.hasQueuedMessages() || this.options.isStreaming()) {
|
|
3120
|
+
if (this.options.hasQueuedMessages() || this.options.isStreaming() || this.options.isBlockedByRuntimePolicy()) {
|
|
2906
3121
|
this.options.emit({ type: "auto_continue_skipped", reason: "runtime_busy" });
|
|
2907
3122
|
return;
|
|
2908
3123
|
}
|
|
2909
3124
|
this.options.emit({ type: "auto_continue_scheduled", reason });
|
|
2910
3125
|
this.options.agent.followUp(message);
|
|
2911
3126
|
const setTimer = this.options.retry?.setTimeout ?? defaultSetTimeout2;
|
|
2912
|
-
|
|
2913
|
-
this.autoContinueTimer =
|
|
2914
|
-
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3127
|
+
await new Promise((resolve2) => {
|
|
3128
|
+
this.autoContinueTimer = setTimer(() => {
|
|
3129
|
+
this.autoContinueTimer = void 0;
|
|
3130
|
+
if (this.options.getDisposed()) {
|
|
3131
|
+
resolve2();
|
|
3132
|
+
return;
|
|
3133
|
+
}
|
|
3134
|
+
void this.options.continueTurn().catch(() => {
|
|
3135
|
+
}).finally(resolve2);
|
|
3136
|
+
}, 0);
|
|
3137
|
+
});
|
|
3138
|
+
}
|
|
3139
|
+
ensurePending() {
|
|
3140
|
+
if (this.pendingAutoContinue) return;
|
|
3141
|
+
this.pendingAutoContinue = new Promise((resolve2) => {
|
|
3142
|
+
this.resolvePendingAutoContinue = resolve2;
|
|
3143
|
+
});
|
|
3144
|
+
}
|
|
3145
|
+
resolvePending() {
|
|
3146
|
+
const resolve2 = this.resolvePendingAutoContinue;
|
|
3147
|
+
this.pendingAutoContinue = void 0;
|
|
3148
|
+
this.resolvePendingAutoContinue = void 0;
|
|
3149
|
+
resolve2?.();
|
|
2920
3150
|
}
|
|
2921
3151
|
};
|
|
2922
3152
|
|
|
@@ -2932,9 +3162,16 @@ async function runAgentSessionCompaction(params) {
|
|
|
2932
3162
|
if (!params.compaction) {
|
|
2933
3163
|
throw new Error("No compaction handler configured");
|
|
2934
3164
|
}
|
|
3165
|
+
const abortedResult = () => ({ aborted: true });
|
|
3166
|
+
const isAborted = () => params.signal?.aborted === true;
|
|
2935
3167
|
params.emit({ type: "auto_compaction_start", reason: params.reason, willRetry: params.willRetry });
|
|
2936
3168
|
try {
|
|
2937
|
-
if (
|
|
3169
|
+
if (isAborted()) {
|
|
3170
|
+
const result2 = abortedResult();
|
|
3171
|
+
params.emit({ type: "auto_compaction_end", reason: params.reason, willRetry: params.willRetry, result: result2 });
|
|
3172
|
+
return result2;
|
|
3173
|
+
}
|
|
3174
|
+
if (params.willRetry) {
|
|
2938
3175
|
params.removeTrailingAssistantError();
|
|
2939
3176
|
}
|
|
2940
3177
|
const result = await params.compaction.compact({
|
|
@@ -2943,22 +3180,43 @@ async function runAgentSessionCompaction(params) {
|
|
|
2943
3180
|
agent: params.agent,
|
|
2944
3181
|
sessionManager: params.sessionManager,
|
|
2945
3182
|
messages: params.agent.state.messages.slice(),
|
|
2946
|
-
cwd: params.cwd
|
|
3183
|
+
cwd: params.cwd,
|
|
3184
|
+
signal: params.signal
|
|
2947
3185
|
});
|
|
3186
|
+
if (isAborted() || result?.aborted) {
|
|
3187
|
+
const aborted = { ...result, aborted: true };
|
|
3188
|
+
params.emit({ type: "auto_compaction_end", reason: params.reason, willRetry: params.willRetry, result: aborted });
|
|
3189
|
+
return aborted;
|
|
3190
|
+
}
|
|
2948
3191
|
if (result?.summary && result.firstKeptEntryId) {
|
|
2949
3192
|
params.sessionManager.appendCompaction(result.summary, result.firstKeptEntryId, result.tokensBefore);
|
|
2950
3193
|
params.replaceMessagesAndMarkSynced(params.sessionManager.getMessages());
|
|
2951
3194
|
} else if (result?.messages) {
|
|
2952
3195
|
params.replaceMessagesAndMarkSynced(result.messages, { updateSession: true });
|
|
2953
3196
|
}
|
|
3197
|
+
if (isAborted()) {
|
|
3198
|
+
const aborted = { ...result, aborted: true };
|
|
3199
|
+
params.emit({ type: "auto_compaction_end", reason: params.reason, willRetry: params.willRetry, result: aborted });
|
|
3200
|
+
return aborted;
|
|
3201
|
+
}
|
|
2954
3202
|
params.markSyncedToAgentState();
|
|
2955
3203
|
await params.sessionManager.save();
|
|
3204
|
+
if (isAborted()) {
|
|
3205
|
+
const aborted = { ...result, aborted: true };
|
|
3206
|
+
params.emit({ type: "auto_compaction_end", reason: params.reason, willRetry: params.willRetry, result: aborted });
|
|
3207
|
+
return aborted;
|
|
3208
|
+
}
|
|
2956
3209
|
if (params.willRetry && !result?.aborted) {
|
|
2957
3210
|
await params.continueTurn();
|
|
2958
3211
|
}
|
|
2959
3212
|
params.emit({ type: "auto_compaction_end", reason: params.reason, willRetry: params.willRetry, result });
|
|
2960
3213
|
return result;
|
|
2961
3214
|
} catch (error) {
|
|
3215
|
+
if (isAborted()) {
|
|
3216
|
+
const result = abortedResult();
|
|
3217
|
+
params.emit({ type: "auto_compaction_end", reason: params.reason, willRetry: params.willRetry, result });
|
|
3218
|
+
return result;
|
|
3219
|
+
}
|
|
2962
3220
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2963
3221
|
params.emit({ type: "auto_compaction_end", reason: params.reason, willRetry: params.willRetry, errorMessage });
|
|
2964
3222
|
throw error;
|
|
@@ -3028,6 +3286,27 @@ async function preparePromptMessages(params) {
|
|
|
3028
3286
|
};
|
|
3029
3287
|
}
|
|
3030
3288
|
|
|
3289
|
+
// ../../packages/agent-framework/src/session/controller/pre-request-policy.ts
|
|
3290
|
+
async function runPreRequestAutoCompact(params) {
|
|
3291
|
+
if (params.disposed || params.runtimeGuardTriggered || params.isStreaming || params.recoveryActive || params.agent.state.messages.length === 0) {
|
|
3292
|
+
return;
|
|
3293
|
+
}
|
|
3294
|
+
const decision = params.getAutoCompactDecision?.({
|
|
3295
|
+
agent: params.agent,
|
|
3296
|
+
sessionManager: params.sessionManager,
|
|
3297
|
+
cwd: params.cwd,
|
|
3298
|
+
messages: params.agent.state.messages.slice()
|
|
3299
|
+
});
|
|
3300
|
+
if (decision?.reason !== "threshold") {
|
|
3301
|
+
return;
|
|
3302
|
+
}
|
|
3303
|
+
try {
|
|
3304
|
+
await params.runAutoCompact();
|
|
3305
|
+
} catch {
|
|
3306
|
+
params.setRecoveryState("none");
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
|
|
3031
3310
|
// ../../packages/agent-framework/src/session/controller/queue.ts
|
|
3032
3311
|
function cloneQueuedInput(input) {
|
|
3033
3312
|
return {
|
|
@@ -3186,14 +3465,23 @@ var AgentSessionQueueDispatcher = class {
|
|
|
3186
3465
|
}
|
|
3187
3466
|
options;
|
|
3188
3467
|
dispatchTimer;
|
|
3468
|
+
pendingDispatch;
|
|
3469
|
+
resolvePendingDispatch;
|
|
3470
|
+
get hasPendingDispatch() {
|
|
3471
|
+
return this.dispatchTimer !== void 0 || this.pendingDispatch !== void 0;
|
|
3472
|
+
}
|
|
3473
|
+
waitForDispatch() {
|
|
3474
|
+
return this.pendingDispatch ?? Promise.resolve();
|
|
3475
|
+
}
|
|
3189
3476
|
schedule(delayMs = 0) {
|
|
3190
3477
|
if (this.options.getDisposed() || this.options.getRuntimeGuardTriggered() || this.dispatchTimer !== void 0) {
|
|
3191
3478
|
return;
|
|
3192
3479
|
}
|
|
3480
|
+
this.ensurePending();
|
|
3193
3481
|
const setTimer = this.options.retry?.setTimeout ?? defaultSetTimeout3;
|
|
3194
3482
|
this.dispatchTimer = setTimer(() => {
|
|
3195
3483
|
this.dispatchTimer = void 0;
|
|
3196
|
-
void this.dispatchNext();
|
|
3484
|
+
void this.dispatchNext().finally(() => this.resolvePending());
|
|
3197
3485
|
}, delayMs);
|
|
3198
3486
|
}
|
|
3199
3487
|
cancel() {
|
|
@@ -3203,6 +3491,7 @@ var AgentSessionQueueDispatcher = class {
|
|
|
3203
3491
|
const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout3;
|
|
3204
3492
|
clearTimer(this.dispatchTimer);
|
|
3205
3493
|
this.dispatchTimer = void 0;
|
|
3494
|
+
this.resolvePending();
|
|
3206
3495
|
}
|
|
3207
3496
|
async dispatchNext() {
|
|
3208
3497
|
if (this.options.getDisposed() || this.options.isStreaming() || this.options.isBlockedByRuntimePolicy()) {
|
|
@@ -3230,6 +3519,11 @@ var AgentSessionQueueDispatcher = class {
|
|
|
3230
3519
|
}
|
|
3231
3520
|
return false;
|
|
3232
3521
|
}
|
|
3522
|
+
await this.options.runPreRequestAutoCompact();
|
|
3523
|
+
if (this.options.getDisposed() || this.options.isStreaming() || this.options.isBlockedByRuntimePolicy()) {
|
|
3524
|
+
this.schedule(100);
|
|
3525
|
+
return false;
|
|
3526
|
+
}
|
|
3233
3527
|
const dispatched = this.options.queue.shift();
|
|
3234
3528
|
if (!dispatched) {
|
|
3235
3529
|
return false;
|
|
@@ -3259,8 +3553,56 @@ var AgentSessionQueueDispatcher = class {
|
|
|
3259
3553
|
}
|
|
3260
3554
|
return true;
|
|
3261
3555
|
}
|
|
3556
|
+
ensurePending() {
|
|
3557
|
+
if (this.pendingDispatch) return;
|
|
3558
|
+
this.pendingDispatch = new Promise((resolve2) => {
|
|
3559
|
+
this.resolvePendingDispatch = resolve2;
|
|
3560
|
+
});
|
|
3561
|
+
}
|
|
3562
|
+
resolvePending() {
|
|
3563
|
+
const resolve2 = this.resolvePendingDispatch;
|
|
3564
|
+
this.pendingDispatch = void 0;
|
|
3565
|
+
this.resolvePendingDispatch = void 0;
|
|
3566
|
+
resolve2?.();
|
|
3567
|
+
}
|
|
3262
3568
|
};
|
|
3263
3569
|
|
|
3570
|
+
// ../../packages/agent-framework/src/session/controller/failure-classification.ts
|
|
3571
|
+
function isAssistantFailureMessage(message) {
|
|
3572
|
+
return message?.role === "assistant" && (message.stopReason === "error" || message.stopReason === "aborted");
|
|
3573
|
+
}
|
|
3574
|
+
function isAssistantTerminalCompletion(message) {
|
|
3575
|
+
if (message?.role !== "assistant") return false;
|
|
3576
|
+
if (message.stopReason === "error" || message.stopReason === "aborted") return false;
|
|
3577
|
+
if (message.stopReason === "toolUse") return false;
|
|
3578
|
+
return !message.content.some((block) => block.type === "toolCall");
|
|
3579
|
+
}
|
|
3580
|
+
function createFailureClassification(context, retry, classifyFailure) {
|
|
3581
|
+
const hostClassification = classifyFailure?.(context);
|
|
3582
|
+
if (hostClassification) {
|
|
3583
|
+
return hostClassification;
|
|
3584
|
+
}
|
|
3585
|
+
const retryable = retry?.isRetryableError(context.errorMessage) === true;
|
|
3586
|
+
const userAbort = context.message?.role === "assistant" && context.message.stopReason === "aborted";
|
|
3587
|
+
return {
|
|
3588
|
+
kind: userAbort ? "user_abort" : retryable ? "provider_transient" : "unknown",
|
|
3589
|
+
retryable: retryable && !userAbort,
|
|
3590
|
+
compactable: false,
|
|
3591
|
+
userAbort,
|
|
3592
|
+
terminal: !retryable || userAbort,
|
|
3593
|
+
message: context.errorMessage
|
|
3594
|
+
};
|
|
3595
|
+
}
|
|
3596
|
+
function asTerminalFailure(failure, message = failure.message) {
|
|
3597
|
+
return {
|
|
3598
|
+
...failure,
|
|
3599
|
+
retryable: false,
|
|
3600
|
+
compactable: false,
|
|
3601
|
+
terminal: true,
|
|
3602
|
+
message
|
|
3603
|
+
};
|
|
3604
|
+
}
|
|
3605
|
+
|
|
3264
3606
|
// ../../packages/agent-framework/src/session/controller/retry-policy.ts
|
|
3265
3607
|
function defaultSetTimeout4(callback, delay) {
|
|
3266
3608
|
return setTimeout(callback, delay);
|
|
@@ -3269,42 +3611,49 @@ function defaultClearTimeout4(handle) {
|
|
|
3269
3611
|
clearTimeout(handle);
|
|
3270
3612
|
}
|
|
3271
3613
|
var AgentSessionRetryController = class {
|
|
3272
|
-
constructor(retry, emit, removeTrailingAssistantError2, continueTurn) {
|
|
3614
|
+
constructor(retry, emit, removeTrailingAssistantError2, continueTurn, setRecoveryState) {
|
|
3273
3615
|
this.retry = retry;
|
|
3274
3616
|
this.emit = emit;
|
|
3275
3617
|
this.removeTrailingAssistantError = removeTrailingAssistantError2;
|
|
3276
3618
|
this.continueTurn = continueTurn;
|
|
3619
|
+
this.setRecoveryState = setRecoveryState;
|
|
3277
3620
|
}
|
|
3278
3621
|
retry;
|
|
3279
3622
|
emit;
|
|
3280
3623
|
removeTrailingAssistantError;
|
|
3281
3624
|
continueTurn;
|
|
3625
|
+
setRecoveryState;
|
|
3282
3626
|
retryAttempt = 0;
|
|
3283
3627
|
retryTimer;
|
|
3284
3628
|
pendingRetry;
|
|
3285
3629
|
resolvePendingRetry;
|
|
3630
|
+
get policy() {
|
|
3631
|
+
return this.retry;
|
|
3632
|
+
}
|
|
3286
3633
|
get hasPendingRetry() {
|
|
3287
3634
|
return this.retryTimer !== void 0 || this.pendingRetry !== void 0;
|
|
3288
3635
|
}
|
|
3289
3636
|
waitForRetry() {
|
|
3290
3637
|
return this.pendingRetry ?? Promise.resolve();
|
|
3291
3638
|
}
|
|
3292
|
-
async handleTurnEnd(message) {
|
|
3639
|
+
async handleTurnEnd(message, failure) {
|
|
3293
3640
|
if (!this.retry || !message || message.role !== "assistant") {
|
|
3294
3641
|
return;
|
|
3295
3642
|
}
|
|
3296
|
-
|
|
3297
|
-
if (!errorMessage) {
|
|
3643
|
+
if (!failure) {
|
|
3298
3644
|
if (this.retryAttempt > 0) {
|
|
3299
3645
|
this.emit({ type: "retry_succeeded", attempts: this.retryAttempt });
|
|
3646
|
+
this.emit({ type: "turn_retry_succeeded", attempts: this.retryAttempt });
|
|
3300
3647
|
this.retryAttempt = 0;
|
|
3648
|
+
this.setRecoveryState("none");
|
|
3301
3649
|
this.resolvePending();
|
|
3302
3650
|
}
|
|
3303
3651
|
return;
|
|
3304
3652
|
}
|
|
3305
|
-
if (!
|
|
3653
|
+
if (!failure.retryable) {
|
|
3306
3654
|
if (this.retryAttempt > 0) {
|
|
3307
3655
|
this.retryAttempt = 0;
|
|
3656
|
+
this.setRecoveryState("none");
|
|
3308
3657
|
this.resolvePending();
|
|
3309
3658
|
}
|
|
3310
3659
|
return;
|
|
@@ -3312,28 +3661,41 @@ var AgentSessionRetryController = class {
|
|
|
3312
3661
|
const maxRetries = this.retry.maxRetries ?? 3;
|
|
3313
3662
|
this.retryAttempt += 1;
|
|
3314
3663
|
if (this.retryAttempt > maxRetries) {
|
|
3315
|
-
|
|
3664
|
+
const terminalFailure = asTerminalFailure(failure);
|
|
3665
|
+
this.emit({ type: "retry_exhausted", errorMessage: failure.message, maxRetries });
|
|
3666
|
+
this.emit({ type: "turn_failed_terminal", failure: terminalFailure, message });
|
|
3667
|
+
this.emit({ type: "turn_settled" });
|
|
3316
3668
|
this.retryAttempt = 0;
|
|
3669
|
+
this.setRecoveryState("terminal");
|
|
3317
3670
|
this.resolvePending();
|
|
3318
3671
|
return;
|
|
3319
3672
|
}
|
|
3320
3673
|
const attempt = this.retryAttempt;
|
|
3321
3674
|
const delayMs = (this.retry.baseDelayMs ?? 2e3) * 2 ** (attempt - 1);
|
|
3322
3675
|
this.ensurePending();
|
|
3323
|
-
this.
|
|
3676
|
+
this.setRecoveryState("retry_pending");
|
|
3677
|
+
this.emit({ type: "turn_recovery_pending", recovery: "retry", failure, attempt, maxRetries, delayMs });
|
|
3678
|
+
this.emit({ type: "retry_scheduled", errorMessage: failure.message, attempt, maxRetries, delayMs });
|
|
3324
3679
|
this.cancelTimerOnly();
|
|
3325
3680
|
const setTimer = this.retry.setTimeout ?? defaultSetTimeout4;
|
|
3326
3681
|
this.retryTimer = setTimer(() => {
|
|
3327
3682
|
this.retryTimer = void 0;
|
|
3683
|
+
this.setRecoveryState("retrying");
|
|
3328
3684
|
this.emit({ type: "retry_start", attempt, maxRetries });
|
|
3685
|
+
this.emit({ type: "turn_retry_started", attempt, maxRetries, failure });
|
|
3329
3686
|
this.removeTrailingAssistantError();
|
|
3330
3687
|
void this.continueTurn().catch((error) => {
|
|
3688
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
3689
|
+
const terminalFailure = asTerminalFailure(failure, errorMessage);
|
|
3331
3690
|
this.emit({
|
|
3332
3691
|
type: "retry_exhausted",
|
|
3333
|
-
errorMessage
|
|
3692
|
+
errorMessage,
|
|
3334
3693
|
maxRetries
|
|
3335
3694
|
});
|
|
3695
|
+
this.emit({ type: "turn_failed_terminal", failure: terminalFailure, message });
|
|
3696
|
+
this.emit({ type: "turn_settled" });
|
|
3336
3697
|
this.retryAttempt = 0;
|
|
3698
|
+
this.setRecoveryState("terminal");
|
|
3337
3699
|
this.resolvePending();
|
|
3338
3700
|
});
|
|
3339
3701
|
}, delayMs);
|
|
@@ -3370,6 +3732,9 @@ var AgentSessionRetryController = class {
|
|
|
3370
3732
|
// ../../packages/agent-framework/src/session/controller/runtime-guard.ts
|
|
3371
3733
|
var RUNAWAY_TOOLCALL_WHITESPACE_DELTA_CHARS = 16384;
|
|
3372
3734
|
var RUNAWAY_TOOLCALL_WHITESPACE_DELTA_EVENTS = 40;
|
|
3735
|
+
var TOOL_FAILURE_SIGNATURE_CHARS = 1e3;
|
|
3736
|
+
var TOOL_FAILURE_SIGNATURE_CONTEXT_LINES = 5;
|
|
3737
|
+
var FAILURE_DIAGNOSTIC_LINE_PATTERN = /\b(E2E_FAILED|AssertionError|TimeoutError|SyntaxError|ReferenceError|TypeError|Error):|(^|\b)(failed|failure|timed out|timeout|exit code|command failed|npm ERR!)/i;
|
|
3373
3738
|
function stableStringify(value) {
|
|
3374
3739
|
if (value === null || typeof value !== "object") {
|
|
3375
3740
|
return JSON.stringify(value) ?? String(value);
|
|
@@ -3388,6 +3753,20 @@ function summarizeToolResultForGuard(result) {
|
|
|
3388
3753
|
const text = content.filter((block) => !!block && typeof block === "object" && "type" in block).filter((block) => block.type === "text").map((block) => block.text || "").join("\n").trim();
|
|
3389
3754
|
return text || stableStringify(result);
|
|
3390
3755
|
}
|
|
3756
|
+
function summarizeToolFailureSignature(result) {
|
|
3757
|
+
const summary = summarizeToolResultForGuard(result).replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim();
|
|
3758
|
+
if (!summary) {
|
|
3759
|
+
return "";
|
|
3760
|
+
}
|
|
3761
|
+
const lines = summary.split("\n");
|
|
3762
|
+
for (let index = lines.length - 1; index >= 0; index--) {
|
|
3763
|
+
if (!FAILURE_DIAGNOSTIC_LINE_PATTERN.test(lines[index] ?? "")) {
|
|
3764
|
+
continue;
|
|
3765
|
+
}
|
|
3766
|
+
return lines.slice(index, Math.min(lines.length, index + TOOL_FAILURE_SIGNATURE_CONTEXT_LINES)).join("\n").slice(0, TOOL_FAILURE_SIGNATURE_CHARS);
|
|
3767
|
+
}
|
|
3768
|
+
return summary.slice(-TOOL_FAILURE_SIGNATURE_CHARS);
|
|
3769
|
+
}
|
|
3391
3770
|
var AgentSessionRuntimeGuard = class {
|
|
3392
3771
|
constructor(trigger) {
|
|
3393
3772
|
this.trigger = trigger;
|
|
@@ -3452,7 +3831,7 @@ var AgentSessionRuntimeGuard = class {
|
|
|
3452
3831
|
}
|
|
3453
3832
|
return;
|
|
3454
3833
|
}
|
|
3455
|
-
const resultSummary =
|
|
3834
|
+
const resultSummary = summarizeToolFailureSignature(event.result);
|
|
3456
3835
|
const key = `${event.toolName}:${stableStringify(event.args)}:${resultSummary}`;
|
|
3457
3836
|
const repeatCount = (this.repeatedToolFailures.get(key) ?? 0) + 1;
|
|
3458
3837
|
this.repeatedToolFailures.set(key, repeatCount);
|
|
@@ -3464,6 +3843,33 @@ var AgentSessionRuntimeGuard = class {
|
|
|
3464
3843
|
}
|
|
3465
3844
|
};
|
|
3466
3845
|
|
|
3846
|
+
// ../../packages/agent-framework/src/session/controller/runtime-phase-policy.ts
|
|
3847
|
+
function classifyControllerRuntimeError(params) {
|
|
3848
|
+
const errorMessage = params.error instanceof Error ? params.error.message : String(params.error);
|
|
3849
|
+
return createFailureClassification({ errorMessage, phase: params.phase }, params.retry, params.classifyFailure);
|
|
3850
|
+
}
|
|
3851
|
+
async function runAgentSessionRuntimePhase(params) {
|
|
3852
|
+
try {
|
|
3853
|
+
return await params.action();
|
|
3854
|
+
} catch (error) {
|
|
3855
|
+
const failure = classifyControllerRuntimeError({
|
|
3856
|
+
error,
|
|
3857
|
+
phase: params.phase,
|
|
3858
|
+
retry: params.retry,
|
|
3859
|
+
classifyFailure: params.classifyFailure
|
|
3860
|
+
});
|
|
3861
|
+
if (failure.userAbort) {
|
|
3862
|
+
params.setRecoveryState("cancelled");
|
|
3863
|
+
params.emit({ type: "turn_cancelled", reason: failure.message });
|
|
3864
|
+
} else {
|
|
3865
|
+
params.setRecoveryState("terminal");
|
|
3866
|
+
params.emit({ type: "turn_failed_terminal", failure: asTerminalFailure(failure) });
|
|
3867
|
+
}
|
|
3868
|
+
params.emit({ type: "turn_settled" });
|
|
3869
|
+
throw error;
|
|
3870
|
+
}
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3467
3873
|
// ../../packages/agent-framework/src/session/controller/session-sync.ts
|
|
3468
3874
|
function getConversationMessages(messages) {
|
|
3469
3875
|
return messages.filter(
|
|
@@ -3568,6 +3974,163 @@ function reduceAgentSessionLifecycle(state, input) {
|
|
|
3568
3974
|
}
|
|
3569
3975
|
}
|
|
3570
3976
|
|
|
3977
|
+
// ../../packages/agent-framework/src/session/controller/turn-lifecycle-policy.ts
|
|
3978
|
+
function isAgentSessionRecoveryActive(state) {
|
|
3979
|
+
return state === "retry_pending" || state === "retrying" || state === "compacting";
|
|
3980
|
+
}
|
|
3981
|
+
function createRuntimeGuardFailure(reason) {
|
|
3982
|
+
return {
|
|
3983
|
+
kind: "runtime_guard",
|
|
3984
|
+
retryable: false,
|
|
3985
|
+
compactable: false,
|
|
3986
|
+
userAbort: false,
|
|
3987
|
+
terminal: true,
|
|
3988
|
+
message: reason
|
|
3989
|
+
};
|
|
3990
|
+
}
|
|
3991
|
+
function classifyControllerTurnFailure(params) {
|
|
3992
|
+
if (!isAssistantFailureMessage(params.message)) {
|
|
3993
|
+
return void 0;
|
|
3994
|
+
}
|
|
3995
|
+
const errorMessage = params.message.errorMessage || (params.message.stopReason === "aborted" ? "Request was aborted" : "assistant response failed");
|
|
3996
|
+
return createFailureClassification(
|
|
3997
|
+
{ errorMessage, message: params.message, phase: "stream" },
|
|
3998
|
+
params.retry,
|
|
3999
|
+
params.classifyFailure
|
|
4000
|
+
);
|
|
4001
|
+
}
|
|
4002
|
+
async function handleAgentSessionTurnEndRuntimePolicies(params) {
|
|
4003
|
+
if (params.disposed || params.runtimeGuardTriggered) {
|
|
4004
|
+
return;
|
|
4005
|
+
}
|
|
4006
|
+
const failure = classifyControllerTurnFailure({
|
|
4007
|
+
message: params.message,
|
|
4008
|
+
retry: params.retry,
|
|
4009
|
+
classifyFailure: params.classifyFailure
|
|
4010
|
+
});
|
|
4011
|
+
if (failure?.userAbort) {
|
|
4012
|
+
params.cancelRetry();
|
|
4013
|
+
params.setRecoveryState("cancelled");
|
|
4014
|
+
params.emit({ type: "turn_cancelled", reason: failure.message, message: params.message });
|
|
4015
|
+
params.emit({ type: "turn_settled" });
|
|
4016
|
+
return;
|
|
4017
|
+
}
|
|
4018
|
+
const compactDecision = params.getAutoCompactDecision?.({
|
|
4019
|
+
agent: params.agent,
|
|
4020
|
+
sessionManager: params.sessionManager,
|
|
4021
|
+
cwd: params.cwd,
|
|
4022
|
+
message: params.message,
|
|
4023
|
+
messages: params.agent.state.messages.slice()
|
|
4024
|
+
});
|
|
4025
|
+
if (failure?.compactable) {
|
|
4026
|
+
params.scheduleAutoCompact(compactDecision ?? { reason: "overflow", willRetry: true }, failure);
|
|
4027
|
+
return;
|
|
4028
|
+
}
|
|
4029
|
+
if (failure && compactDecision?.reason === "overflow") {
|
|
4030
|
+
params.scheduleAutoCompact(compactDecision, failure);
|
|
4031
|
+
return;
|
|
4032
|
+
}
|
|
4033
|
+
if (failure?.retryable && compactDecision?.reason === "threshold") {
|
|
4034
|
+
params.scheduleAutoCompact({ reason: "threshold", willRetry: true }, failure);
|
|
4035
|
+
return;
|
|
4036
|
+
}
|
|
4037
|
+
if (failure) {
|
|
4038
|
+
if (failure.retryable) {
|
|
4039
|
+
await params.handleRetryTurnEnd(params.message, failure);
|
|
4040
|
+
return;
|
|
4041
|
+
}
|
|
4042
|
+
await params.handleRetryTurnEnd(params.message, failure);
|
|
4043
|
+
params.setRecoveryState("terminal");
|
|
4044
|
+
params.emit({ type: "turn_failed_terminal", failure: asTerminalFailure(failure), message: params.message });
|
|
4045
|
+
params.emit({ type: "turn_settled" });
|
|
4046
|
+
return;
|
|
4047
|
+
}
|
|
4048
|
+
await params.handleRetryTurnEnd(params.message);
|
|
4049
|
+
if (isAssistantTerminalCompletion(params.message)) {
|
|
4050
|
+
params.emit({ type: "turn_completed", message: params.message, toolResults: params.toolResults });
|
|
4051
|
+
const postTurnCompactDecision = compactDecision ?? params.getAutoCompactDecision?.({
|
|
4052
|
+
agent: params.agent,
|
|
4053
|
+
sessionManager: params.sessionManager,
|
|
4054
|
+
cwd: params.cwd,
|
|
4055
|
+
message: params.message,
|
|
4056
|
+
messages: params.agent.state.messages.slice()
|
|
4057
|
+
});
|
|
4058
|
+
if (postTurnCompactDecision?.reason === "threshold") {
|
|
4059
|
+
params.scheduleAutoCompact(postTurnCompactDecision);
|
|
4060
|
+
return;
|
|
4061
|
+
}
|
|
4062
|
+
params.setRecoveryState("none");
|
|
4063
|
+
params.emit({ type: "turn_settled" });
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
async function runAgentSessionControllerAutoCompact(params) {
|
|
4067
|
+
const emitSettled = params.emitSettled ?? true;
|
|
4068
|
+
const runAfterAgentEndHook = params.runAfterAgentEndHook ?? true;
|
|
4069
|
+
const cancelOnAbort = params.cancelOnAbort ?? true;
|
|
4070
|
+
params.setRecoveryState("compacting");
|
|
4071
|
+
params.emit({ type: "turn_compaction_started", reason: params.reason, willRetry: params.willRetry });
|
|
4072
|
+
try {
|
|
4073
|
+
const result = await runAgentSessionCompaction({
|
|
4074
|
+
reason: params.reason,
|
|
4075
|
+
willRetry: params.willRetry,
|
|
4076
|
+
signal: params.signal,
|
|
4077
|
+
compaction: params.compaction,
|
|
4078
|
+
agent: params.agent,
|
|
4079
|
+
sessionManager: params.sessionManager,
|
|
4080
|
+
cwd: params.cwd,
|
|
4081
|
+
emit: params.emit,
|
|
4082
|
+
removeTrailingAssistantError: params.removeTrailingAssistantError,
|
|
4083
|
+
replaceMessagesAndMarkSynced: params.replaceMessagesAndMarkSynced,
|
|
4084
|
+
markSyncedToAgentState: params.markSyncedToAgentState,
|
|
4085
|
+
continueTurn: params.continueTurn
|
|
4086
|
+
});
|
|
4087
|
+
if (!params.willRetry || result?.aborted) {
|
|
4088
|
+
const alreadyCancelled = params.getRecoveryState?.() === "cancelled" || params.getDisposed?.() === true;
|
|
4089
|
+
params.setRecoveryState(result?.aborted && cancelOnAbort ? "cancelled" : "none");
|
|
4090
|
+
if (result?.aborted) {
|
|
4091
|
+
params.cancelRetry();
|
|
4092
|
+
if (cancelOnAbort && !alreadyCancelled) {
|
|
4093
|
+
params.emit({ type: "turn_cancelled", reason: "compaction_aborted" });
|
|
4094
|
+
}
|
|
4095
|
+
}
|
|
4096
|
+
if (emitSettled && !alreadyCancelled) {
|
|
4097
|
+
params.emit({ type: "turn_settled" });
|
|
4098
|
+
}
|
|
4099
|
+
if (!result?.aborted && runAfterAgentEndHook) {
|
|
4100
|
+
void params.handleAgentEndHook(params.agent.state.messages.slice());
|
|
4101
|
+
}
|
|
4102
|
+
}
|
|
4103
|
+
return result;
|
|
4104
|
+
} catch (error) {
|
|
4105
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4106
|
+
if (!params.willRetry && params.reason !== "overflow") {
|
|
4107
|
+
params.setRecoveryState("none");
|
|
4108
|
+
if (emitSettled) {
|
|
4109
|
+
params.emit({ type: "turn_settled" });
|
|
4110
|
+
}
|
|
4111
|
+
if (runAfterAgentEndHook) {
|
|
4112
|
+
void params.handleAgentEndHook(params.agent.state.messages.slice());
|
|
4113
|
+
}
|
|
4114
|
+
throw error;
|
|
4115
|
+
}
|
|
4116
|
+
const failure = asTerminalFailure(params.pendingFailure ?? {
|
|
4117
|
+
kind: "internal_error",
|
|
4118
|
+
retryable: false,
|
|
4119
|
+
compactable: false,
|
|
4120
|
+
userAbort: false,
|
|
4121
|
+
terminal: true,
|
|
4122
|
+
message: errorMessage
|
|
4123
|
+
}, errorMessage);
|
|
4124
|
+
params.cancelRetry();
|
|
4125
|
+
params.setRecoveryState("terminal");
|
|
4126
|
+
params.emit({ type: "turn_failed_terminal", failure });
|
|
4127
|
+
params.emit({ type: "turn_settled" });
|
|
4128
|
+
throw error;
|
|
4129
|
+
} finally {
|
|
4130
|
+
params.clearPendingFailure();
|
|
4131
|
+
}
|
|
4132
|
+
}
|
|
4133
|
+
|
|
3571
4134
|
// ../../packages/agent-framework/src/session/controller.ts
|
|
3572
4135
|
var AgentSessionController = class {
|
|
3573
4136
|
agent;
|
|
@@ -3589,6 +4152,9 @@ var AgentSessionController = class {
|
|
|
3589
4152
|
prepareTurn;
|
|
3590
4153
|
afterAgentEnd;
|
|
3591
4154
|
getAutoCompactDecision;
|
|
4155
|
+
classifyFailure;
|
|
4156
|
+
recoveryState = "none";
|
|
4157
|
+
pendingCompactionFailure;
|
|
3592
4158
|
lifecycleState = createInitialAgentSessionLifecycleState();
|
|
3593
4159
|
constructor(options) {
|
|
3594
4160
|
this.agent = options.agent;
|
|
@@ -3599,6 +4165,7 @@ var AgentSessionController = class {
|
|
|
3599
4165
|
this.prepareTurn = options.prepareTurn;
|
|
3600
4166
|
this.afterAgentEnd = options.afterAgentEnd;
|
|
3601
4167
|
this.getAutoCompactDecision = options.getAutoCompactDecision;
|
|
4168
|
+
this.classifyFailure = options.classifyFailure;
|
|
3602
4169
|
if (options.onEvent) {
|
|
3603
4170
|
this.listeners.add(options.onEvent);
|
|
3604
4171
|
}
|
|
@@ -3614,15 +4181,18 @@ var AgentSessionController = class {
|
|
|
3614
4181
|
options.retry,
|
|
3615
4182
|
(event) => this.emit(event),
|
|
3616
4183
|
() => this.removeTrailingAssistantError(),
|
|
3617
|
-
() => this.continue()
|
|
4184
|
+
() => this.continue(),
|
|
4185
|
+
(state) => {
|
|
4186
|
+
this.recoveryState = state;
|
|
4187
|
+
}
|
|
3618
4188
|
);
|
|
3619
4189
|
this.autoCompact = new AgentSessionAutoCompactScheduler({
|
|
3620
4190
|
retry: options.retry,
|
|
3621
4191
|
getDisposed: () => this.disposed,
|
|
3622
4192
|
getRuntimeGuardTriggered: () => this.runtimeGuard.triggered,
|
|
3623
4193
|
isStreaming: () => this.isStreaming,
|
|
3624
|
-
runAutoCompact: async (decision) => {
|
|
3625
|
-
await this.runAutoCompact(decision.reason, decision.willRetry);
|
|
4194
|
+
runAutoCompact: async (decision, signal) => {
|
|
4195
|
+
await this.runAutoCompact(decision.reason, decision.willRetry, { signal });
|
|
3626
4196
|
}
|
|
3627
4197
|
});
|
|
3628
4198
|
this.autoContinue = new AgentSessionAutoContinueController({
|
|
@@ -3634,6 +4204,7 @@ var AgentSessionController = class {
|
|
|
3634
4204
|
emit: (event) => this.emit(event),
|
|
3635
4205
|
getDisposed: () => this.disposed,
|
|
3636
4206
|
getRuntimeGuardTriggered: () => this.runtimeGuard.triggered,
|
|
4207
|
+
isBlockedByRuntimePolicy: () => this.isBlockedByRuntimePolicy(),
|
|
3637
4208
|
hasQueuedMessages: () => this.hasQueuedMessages,
|
|
3638
4209
|
isStreaming: () => this.isStreaming,
|
|
3639
4210
|
continueTurn: () => this.continue()
|
|
@@ -3649,7 +4220,8 @@ var AgentSessionController = class {
|
|
|
3649
4220
|
getDisposed: () => this.disposed,
|
|
3650
4221
|
getRuntimeGuardTriggered: () => this.runtimeGuard.triggered,
|
|
3651
4222
|
isStreaming: () => this.isStreaming,
|
|
3652
|
-
isBlockedByRuntimePolicy: () => this.
|
|
4223
|
+
isBlockedByRuntimePolicy: () => this.isBlockedByRuntimePolicy(),
|
|
4224
|
+
runPreRequestAutoCompact: () => this.runPreRequestAutoCompactIfNeeded(),
|
|
3653
4225
|
preparePromptMessages: (messages, promptOptions) => this.preparePromptMessages(messages, promptOptions),
|
|
3654
4226
|
continueTurn: () => this.continue(),
|
|
3655
4227
|
syncNewAgentMessages: () => this.syncNewAgentMessages()
|
|
@@ -3672,6 +4244,9 @@ var AgentSessionController = class {
|
|
|
3672
4244
|
get queuedInputsSnapshot() {
|
|
3673
4245
|
return this.queue.snapshot;
|
|
3674
4246
|
}
|
|
4247
|
+
get turnRecoveryState() {
|
|
4248
|
+
return this.recoveryState;
|
|
4249
|
+
}
|
|
3675
4250
|
get activeSessionMetadata() {
|
|
3676
4251
|
const metadata = this.sessionManager.getActiveSession()?.metadata;
|
|
3677
4252
|
return metadata ? { ...metadata } : void 0;
|
|
@@ -3680,10 +4255,7 @@ var AgentSessionController = class {
|
|
|
3680
4255
|
return this.lastSyncedMessageCount;
|
|
3681
4256
|
}
|
|
3682
4257
|
get runtimeContext() {
|
|
3683
|
-
return {
|
|
3684
|
-
systemPrompt: this.agent.state.systemPrompt,
|
|
3685
|
-
tools: this.agent.state.tools.slice()
|
|
3686
|
-
};
|
|
4258
|
+
return { systemPrompt: this.agent.state.systemPrompt, tools: this.agent.state.tools.slice() };
|
|
3687
4259
|
}
|
|
3688
4260
|
getRuntimeSnapshot() {
|
|
3689
4261
|
return {
|
|
@@ -3730,6 +4302,17 @@ var AgentSessionController = class {
|
|
|
3730
4302
|
}
|
|
3731
4303
|
async prompt(input, options) {
|
|
3732
4304
|
const messages = normalizePromptInput(input);
|
|
4305
|
+
if (this.isRecoveryActive()) {
|
|
4306
|
+
this.queue.enqueue(messages, options?.streamingBehavior ?? "followUp", {
|
|
4307
|
+
...options,
|
|
4308
|
+
streamingBehavior: options?.streamingBehavior ?? "followUp"
|
|
4309
|
+
});
|
|
4310
|
+
this.recordLifecycle({ type: "queued_input_added" });
|
|
4311
|
+
return;
|
|
4312
|
+
}
|
|
4313
|
+
if (this.recoveryState === "terminal" || this.recoveryState === "cancelled") {
|
|
4314
|
+
this.recoveryState = "none";
|
|
4315
|
+
}
|
|
3733
4316
|
if (options?.streamingBehavior) {
|
|
3734
4317
|
this.queue.enqueue(messages, options.streamingBehavior, options);
|
|
3735
4318
|
this.recordLifecycle({ type: "queued_input_added" });
|
|
@@ -3741,7 +4324,8 @@ var AgentSessionController = class {
|
|
|
3741
4324
|
if (this.isStreaming) {
|
|
3742
4325
|
throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
|
|
3743
4326
|
}
|
|
3744
|
-
|
|
4327
|
+
await this.runPreRequestAutoCompactIfNeeded();
|
|
4328
|
+
const prepared = await this.runRuntimePhase("prepare", () => this.preparePromptMessages(messages, options));
|
|
3745
4329
|
if (prepared.handled) {
|
|
3746
4330
|
return;
|
|
3747
4331
|
}
|
|
@@ -3749,23 +4333,30 @@ var AgentSessionController = class {
|
|
|
3749
4333
|
if (prepared.systemPrompt !== void 0) {
|
|
3750
4334
|
this.agent.setSystemPrompt(prepared.systemPrompt);
|
|
3751
4335
|
}
|
|
3752
|
-
await this.agent.prompt(prepared.messages);
|
|
3753
|
-
await this.syncNewAgentMessages();
|
|
4336
|
+
await this.runRuntimePhase("request", () => this.agent.prompt(prepared.messages));
|
|
4337
|
+
await this.runRuntimePhase("post_turn", () => this.syncNewAgentMessages());
|
|
3754
4338
|
}
|
|
3755
4339
|
async continue() {
|
|
3756
4340
|
if (this.isStreaming) {
|
|
3757
4341
|
throw new Error("Agent is already processing. Wait for completion before continuing.");
|
|
3758
4342
|
}
|
|
4343
|
+
await this.runPreRequestAutoCompactIfNeeded();
|
|
3759
4344
|
this.recordLifecycle({ type: "prompt_started" });
|
|
3760
|
-
await this.agent.continue();
|
|
3761
|
-
await this.syncNewAgentMessages();
|
|
4345
|
+
await this.runRuntimePhase("request", () => this.agent.continue());
|
|
4346
|
+
await this.runRuntimePhase("post_turn", () => this.syncNewAgentMessages());
|
|
3762
4347
|
}
|
|
3763
4348
|
abort() {
|
|
3764
4349
|
this.recordLifecycle({ type: "abort" });
|
|
4350
|
+
const cancelPendingRecovery = this.isRecoveryActive() && !this.isStreaming;
|
|
3765
4351
|
this.retryController.cancel();
|
|
3766
4352
|
this.autoCompact.cancel();
|
|
3767
4353
|
this.queueDispatcher.cancel();
|
|
3768
4354
|
this.autoContinue.cancel();
|
|
4355
|
+
if (cancelPendingRecovery) {
|
|
4356
|
+
this.recoveryState = "cancelled";
|
|
4357
|
+
this.emit({ type: "turn_cancelled", reason: "user_abort" });
|
|
4358
|
+
this.emit({ type: "turn_settled" });
|
|
4359
|
+
}
|
|
3769
4360
|
this.agent.abort();
|
|
3770
4361
|
}
|
|
3771
4362
|
waitForIdle() {
|
|
@@ -3774,25 +4365,40 @@ var AgentSessionController = class {
|
|
|
3774
4365
|
async waitForSettled() {
|
|
3775
4366
|
while (true) {
|
|
3776
4367
|
await this.agent.waitForIdle();
|
|
3777
|
-
if (!this.
|
|
4368
|
+
if (!this.hasPendingRuntimePolicy()) {
|
|
3778
4369
|
return;
|
|
3779
4370
|
}
|
|
3780
|
-
await this.retryController.waitForRetry();
|
|
4371
|
+
await Promise.all([this.retryController.waitForRetry(), this.autoCompact.waitForAutoCompact(), this.autoContinue.waitForAutoContinue(), this.queueDispatcher.waitForDispatch()]);
|
|
3781
4372
|
}
|
|
3782
4373
|
}
|
|
3783
|
-
async runAutoCompact(reason, willRetry = false) {
|
|
3784
|
-
return
|
|
4374
|
+
async runAutoCompact(reason, willRetry = false, options) {
|
|
4375
|
+
return runAgentSessionControllerAutoCompact({
|
|
3785
4376
|
reason,
|
|
3786
4377
|
willRetry,
|
|
4378
|
+
signal: options?.signal,
|
|
3787
4379
|
compaction: this.compaction,
|
|
3788
4380
|
agent: this.agent,
|
|
3789
4381
|
sessionManager: this.sessionManager,
|
|
3790
4382
|
cwd: this.cwd,
|
|
4383
|
+
pendingFailure: this.pendingCompactionFailure,
|
|
3791
4384
|
emit: (event) => this.emit(event),
|
|
4385
|
+
setRecoveryState: (state) => {
|
|
4386
|
+
this.recoveryState = state;
|
|
4387
|
+
},
|
|
4388
|
+
getRecoveryState: () => this.recoveryState,
|
|
4389
|
+
getDisposed: () => this.disposed,
|
|
4390
|
+
cancelRetry: () => this.retryController.cancel(),
|
|
4391
|
+
clearPendingFailure: () => {
|
|
4392
|
+
this.pendingCompactionFailure = void 0;
|
|
4393
|
+
},
|
|
3792
4394
|
removeTrailingAssistantError: () => this.removeTrailingAssistantError(),
|
|
3793
|
-
replaceMessagesAndMarkSynced: (messages,
|
|
4395
|
+
replaceMessagesAndMarkSynced: (messages, options2) => this.replaceMessagesAndMarkSynced(messages, options2),
|
|
3794
4396
|
markSyncedToAgentState: () => this.markSyncedToAgentState(),
|
|
3795
|
-
continueTurn: () => this.continue()
|
|
4397
|
+
continueTurn: () => this.continue(),
|
|
4398
|
+
handleAgentEndHook: (messages) => this.handleAgentEndHook(messages),
|
|
4399
|
+
emitSettled: options?.emitSettled,
|
|
4400
|
+
runAfterAgentEndHook: options?.runAfterAgentEndHook,
|
|
4401
|
+
cancelOnAbort: options?.cancelOnAbort
|
|
3796
4402
|
});
|
|
3797
4403
|
}
|
|
3798
4404
|
dispose() {
|
|
@@ -3814,9 +4420,23 @@ var AgentSessionController = class {
|
|
|
3814
4420
|
this.queue.replaceText(search, replacement);
|
|
3815
4421
|
}
|
|
3816
4422
|
requestQueueDispatch() {
|
|
3817
|
-
if (this.queue.length > 0)
|
|
3818
|
-
|
|
3819
|
-
|
|
4423
|
+
if (this.queue.length > 0) this.queueDispatcher.schedule();
|
|
4424
|
+
}
|
|
4425
|
+
async runPreRequestAutoCompactIfNeeded() {
|
|
4426
|
+
return runPreRequestAutoCompact({
|
|
4427
|
+
disposed: this.disposed,
|
|
4428
|
+
runtimeGuardTriggered: this.runtimeGuard.triggered,
|
|
4429
|
+
isStreaming: this.isStreaming,
|
|
4430
|
+
recoveryActive: this.isRecoveryActive(),
|
|
4431
|
+
agent: this.agent,
|
|
4432
|
+
sessionManager: this.sessionManager,
|
|
4433
|
+
cwd: this.cwd,
|
|
4434
|
+
getAutoCompactDecision: this.getAutoCompactDecision,
|
|
4435
|
+
runAutoCompact: () => this.runAutoCompact("threshold", false, { emitSettled: false, runAfterAgentEndHook: false, cancelOnAbort: false }).then(() => void 0),
|
|
4436
|
+
setRecoveryState: (state) => {
|
|
4437
|
+
this.recoveryState = state;
|
|
4438
|
+
}
|
|
4439
|
+
});
|
|
3820
4440
|
}
|
|
3821
4441
|
handleAgentEvent(event) {
|
|
3822
4442
|
this.emit({ type: "agent_event", event });
|
|
@@ -3837,7 +4457,7 @@ var AgentSessionController = class {
|
|
|
3837
4457
|
if (event.type === "turn_end") {
|
|
3838
4458
|
void this.syncNewAgentMessages();
|
|
3839
4459
|
this.recordLifecycle({ type: "turn_end_success" });
|
|
3840
|
-
void this.handleTurnEndRuntimePolicies(event.message);
|
|
4460
|
+
void this.handleTurnEndRuntimePolicies(event.message, event.toolResults);
|
|
3841
4461
|
}
|
|
3842
4462
|
if (event.type === "agent_end") {
|
|
3843
4463
|
this.recordLifecycle({ type: "agent_end" });
|
|
@@ -3874,31 +4494,29 @@ var AgentSessionController = class {
|
|
|
3874
4494
|
replaceMessagesAndMarkSynced: (messages, options) => this.replaceMessagesAndMarkSynced(messages, options)
|
|
3875
4495
|
});
|
|
3876
4496
|
}
|
|
3877
|
-
async handleTurnEndRuntimePolicies(message) {
|
|
3878
|
-
|
|
3879
|
-
|
|
3880
|
-
|
|
3881
|
-
|
|
3882
|
-
|
|
3883
|
-
await this.retryController.handleTurnEnd(message);
|
|
3884
|
-
}
|
|
3885
|
-
const compactDecision = this.getAutoCompactDecision?.({
|
|
4497
|
+
async handleTurnEndRuntimePolicies(message, toolResults = []) {
|
|
4498
|
+
return handleAgentSessionTurnEndRuntimePolicies({
|
|
4499
|
+
disposed: this.disposed,
|
|
4500
|
+
runtimeGuardTriggered: this.runtimeGuard.triggered,
|
|
4501
|
+
message,
|
|
4502
|
+
toolResults,
|
|
3886
4503
|
agent: this.agent,
|
|
3887
4504
|
sessionManager: this.sessionManager,
|
|
3888
4505
|
cwd: this.cwd,
|
|
3889
|
-
|
|
3890
|
-
|
|
4506
|
+
getAutoCompactDecision: this.getAutoCompactDecision,
|
|
4507
|
+
classifyFailure: this.classifyFailure,
|
|
4508
|
+
retry: this.retryController.policy,
|
|
4509
|
+
emit: (event) => this.emit(event),
|
|
4510
|
+
setRecoveryState: (state) => {
|
|
4511
|
+
this.recoveryState = state;
|
|
4512
|
+
},
|
|
4513
|
+
handleRetryTurnEnd: (turnMessage, failure) => this.retryController.handleTurnEnd(turnMessage, failure),
|
|
4514
|
+
cancelRetry: () => this.retryController.cancel(),
|
|
4515
|
+
scheduleAutoCompact: (decision, failure) => this.scheduleAutoCompact(decision, failure)
|
|
3891
4516
|
});
|
|
3892
|
-
if (compactDecision) {
|
|
3893
|
-
this.scheduleAutoCompact(compactDecision);
|
|
3894
|
-
return;
|
|
3895
|
-
}
|
|
3896
|
-
if (assistantError) {
|
|
3897
|
-
await this.retryController.handleTurnEnd(message);
|
|
3898
|
-
}
|
|
3899
4517
|
}
|
|
3900
4518
|
async handleAgentEndHook(messages) {
|
|
3901
|
-
if (this.disposed || this.runtimeGuard.triggered) {
|
|
4519
|
+
if (this.disposed || this.runtimeGuard.triggered || this.isBlockedByRuntimePolicy()) {
|
|
3902
4520
|
return;
|
|
3903
4521
|
}
|
|
3904
4522
|
if (this.afterAgentEnd) {
|
|
@@ -3933,8 +4551,36 @@ var AgentSessionController = class {
|
|
|
3933
4551
|
recordToolExecutionEndForGuard(event) {
|
|
3934
4552
|
this.runtimeGuard.recordToolExecutionEnd(event);
|
|
3935
4553
|
}
|
|
3936
|
-
|
|
4554
|
+
async runRuntimePhase(phase, action) {
|
|
4555
|
+
return runAgentSessionRuntimePhase({
|
|
4556
|
+
phase,
|
|
4557
|
+
action,
|
|
4558
|
+
retry: this.retryController.policy,
|
|
4559
|
+
classifyFailure: this.classifyFailure,
|
|
4560
|
+
emit: (event) => this.emit(event),
|
|
4561
|
+
setRecoveryState: (state) => {
|
|
4562
|
+
this.recoveryState = state;
|
|
4563
|
+
}
|
|
4564
|
+
});
|
|
4565
|
+
}
|
|
4566
|
+
isRecoveryActive() {
|
|
4567
|
+
return isAgentSessionRecoveryActive(this.recoveryState);
|
|
4568
|
+
}
|
|
4569
|
+
isBlockedByRuntimePolicy() {
|
|
4570
|
+
return this.recoveryState !== "none" || this.retryController.hasPendingRetry || this.autoCompact.hasPendingAutoCompact;
|
|
4571
|
+
}
|
|
4572
|
+
hasPendingRuntimePolicy() {
|
|
4573
|
+
return this.retryController.hasPendingRetry || this.autoCompact.hasPendingAutoCompact || this.autoContinue.hasPendingAutoContinue || this.queueDispatcher.hasPendingDispatch;
|
|
4574
|
+
}
|
|
4575
|
+
scheduleAutoCompact(decision, failure) {
|
|
3937
4576
|
this.recordLifecycle({ type: "auto_compact_scheduled" });
|
|
4577
|
+
if (failure) {
|
|
4578
|
+
this.pendingCompactionFailure = failure;
|
|
4579
|
+
this.recoveryState = "compacting";
|
|
4580
|
+
this.emit({ type: "turn_recovery_pending", recovery: "compaction", failure });
|
|
4581
|
+
} else if (decision.reason === "threshold") {
|
|
4582
|
+
this.recoveryState = "compacting";
|
|
4583
|
+
}
|
|
3938
4584
|
this.autoCompact.schedule(decision);
|
|
3939
4585
|
}
|
|
3940
4586
|
triggerRuntimeGuard(toolName, repeatCount, reason) {
|
|
@@ -3946,7 +4592,13 @@ var AgentSessionController = class {
|
|
|
3946
4592
|
this.autoCompact.cancel();
|
|
3947
4593
|
this.queueDispatcher.cancel();
|
|
3948
4594
|
this.autoContinue.cancel();
|
|
4595
|
+
this.recoveryState = "terminal";
|
|
3949
4596
|
this.emit({ type: "runtime_guard_triggered", toolName, repeatCount, reason });
|
|
4597
|
+
this.emit({
|
|
4598
|
+
type: "turn_failed_terminal",
|
|
4599
|
+
failure: createRuntimeGuardFailure(reason)
|
|
4600
|
+
});
|
|
4601
|
+
this.emit({ type: "turn_settled" });
|
|
3950
4602
|
this.agent.abort();
|
|
3951
4603
|
}
|
|
3952
4604
|
emit(event) {
|
|
@@ -5155,7 +5807,49 @@ function restoreLineEndings(text, ending) {
|
|
|
5155
5807
|
return ending === "\r\n" ? text.replace(/\n/g, "\r\n") : text;
|
|
5156
5808
|
}
|
|
5157
5809
|
function normalizeForFuzzyMatch(text) {
|
|
5158
|
-
return text
|
|
5810
|
+
return buildFuzzyIndexMap(text).normalized;
|
|
5811
|
+
}
|
|
5812
|
+
function normalizeCharForFuzzyMatch(char) {
|
|
5813
|
+
if (/[\u2018\u2019\u201A\u201B]/.test(char)) return "'";
|
|
5814
|
+
if (/[\u201C\u201D\u201E\u201F]/.test(char)) return '"';
|
|
5815
|
+
if (/[\u2010\u2011\u2012\u2013\u2014\u2015\u2212]/.test(char)) return "-";
|
|
5816
|
+
if (/[\u00A0\u2002-\u200A\u202F\u205F\u3000]/.test(char)) return " ";
|
|
5817
|
+
return char;
|
|
5818
|
+
}
|
|
5819
|
+
function buildFuzzyIndexMap(text) {
|
|
5820
|
+
const lines = text.split("\n");
|
|
5821
|
+
let normalized = "";
|
|
5822
|
+
const originalStartByNormalizedIndex = [];
|
|
5823
|
+
const originalEndByNormalizedIndex = [];
|
|
5824
|
+
let originalLineStart = 0;
|
|
5825
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
5826
|
+
const line = lines[lineIndex] ?? "";
|
|
5827
|
+
const trimmedLine = line.trimEnd();
|
|
5828
|
+
for (let index = 0; index < trimmedLine.length; index++) {
|
|
5829
|
+
normalized += normalizeCharForFuzzyMatch(trimmedLine[index]);
|
|
5830
|
+
originalStartByNormalizedIndex.push(originalLineStart + index);
|
|
5831
|
+
originalEndByNormalizedIndex.push(originalLineStart + index + 1);
|
|
5832
|
+
}
|
|
5833
|
+
if (lineIndex < lines.length - 1) {
|
|
5834
|
+
normalized += "\n";
|
|
5835
|
+
originalStartByNormalizedIndex.push(originalLineStart + line.length);
|
|
5836
|
+
originalEndByNormalizedIndex.push(originalLineStart + line.length + 1);
|
|
5837
|
+
}
|
|
5838
|
+
originalLineStart += line.length + (lineIndex < lines.length - 1 ? 1 : 0);
|
|
5839
|
+
}
|
|
5840
|
+
return { normalized, originalStartByNormalizedIndex, originalEndByNormalizedIndex };
|
|
5841
|
+
}
|
|
5842
|
+
function countOccurrences(content, needle) {
|
|
5843
|
+
if (needle.length === 0) return 0;
|
|
5844
|
+
let count = 0;
|
|
5845
|
+
let index = 0;
|
|
5846
|
+
while (true) {
|
|
5847
|
+
const nextIndex = content.indexOf(needle, index);
|
|
5848
|
+
if (nextIndex === -1) break;
|
|
5849
|
+
count++;
|
|
5850
|
+
index = nextIndex + needle.length;
|
|
5851
|
+
}
|
|
5852
|
+
return count;
|
|
5159
5853
|
}
|
|
5160
5854
|
function fuzzyFindText(content, oldText) {
|
|
5161
5855
|
const exactIndex = content.indexOf(oldText);
|
|
@@ -5165,11 +5859,25 @@ function fuzzyFindText(content, oldText) {
|
|
|
5165
5859
|
index: exactIndex,
|
|
5166
5860
|
matchLength: oldText.length,
|
|
5167
5861
|
usedFuzzyMatch: false,
|
|
5168
|
-
contentForReplacement: content
|
|
5862
|
+
contentForReplacement: content,
|
|
5863
|
+
occurrences: countOccurrences(content, oldText),
|
|
5864
|
+
matchKind: "exact"
|
|
5169
5865
|
};
|
|
5170
5866
|
}
|
|
5171
|
-
const
|
|
5867
|
+
const fuzzyMap = buildFuzzyIndexMap(content);
|
|
5868
|
+
const fuzzyContent = fuzzyMap.normalized;
|
|
5172
5869
|
const fuzzyOldText = normalizeForFuzzyMatch(oldText);
|
|
5870
|
+
if (fuzzyOldText.length === 0) {
|
|
5871
|
+
return {
|
|
5872
|
+
found: false,
|
|
5873
|
+
index: -1,
|
|
5874
|
+
matchLength: 0,
|
|
5875
|
+
usedFuzzyMatch: false,
|
|
5876
|
+
contentForReplacement: content,
|
|
5877
|
+
occurrences: 0,
|
|
5878
|
+
matchKind: "none"
|
|
5879
|
+
};
|
|
5880
|
+
}
|
|
5173
5881
|
const fuzzyIndex = fuzzyContent.indexOf(fuzzyOldText);
|
|
5174
5882
|
if (fuzzyIndex === -1) {
|
|
5175
5883
|
return {
|
|
@@ -5177,35 +5885,65 @@ function fuzzyFindText(content, oldText) {
|
|
|
5177
5885
|
index: -1,
|
|
5178
5886
|
matchLength: 0,
|
|
5179
5887
|
usedFuzzyMatch: false,
|
|
5180
|
-
contentForReplacement: content
|
|
5888
|
+
contentForReplacement: content,
|
|
5889
|
+
occurrences: 0,
|
|
5890
|
+
matchKind: "none"
|
|
5181
5891
|
};
|
|
5182
5892
|
}
|
|
5893
|
+
const endFuzzyIndex = fuzzyIndex + fuzzyOldText.length - 1;
|
|
5894
|
+
const originalStart = fuzzyMap.originalStartByNormalizedIndex[fuzzyIndex];
|
|
5895
|
+
const originalEnd = fuzzyMap.originalEndByNormalizedIndex[endFuzzyIndex];
|
|
5183
5896
|
return {
|
|
5184
5897
|
found: true,
|
|
5185
|
-
index:
|
|
5186
|
-
matchLength:
|
|
5898
|
+
index: originalStart,
|
|
5899
|
+
matchLength: originalEnd - originalStart,
|
|
5187
5900
|
usedFuzzyMatch: true,
|
|
5188
|
-
contentForReplacement:
|
|
5901
|
+
contentForReplacement: content,
|
|
5902
|
+
occurrences: countOccurrences(fuzzyContent, fuzzyOldText),
|
|
5903
|
+
matchKind: "fuzzy"
|
|
5189
5904
|
};
|
|
5190
5905
|
}
|
|
5191
5906
|
function stripBom(content) {
|
|
5192
5907
|
return content.startsWith("\uFEFF") ? { bom: "\uFEFF", text: content.slice(1) } : { bom: "", text: content };
|
|
5193
5908
|
}
|
|
5194
|
-
function generateDiffString(oldContent, newContent,
|
|
5195
|
-
const oldLines = oldContent.split("\n");
|
|
5196
|
-
const newLines = newContent.split("\n");
|
|
5197
|
-
let
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
}
|
|
5203
|
-
}
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5909
|
+
function generateDiffString(oldContent, newContent, contextLines = 4) {
|
|
5910
|
+
const oldLines = normalizeToLF(stripBom(oldContent).text).split("\n");
|
|
5911
|
+
const newLines = normalizeToLF(stripBom(newContent).text).split("\n");
|
|
5912
|
+
let prefixLength = 0;
|
|
5913
|
+
while (prefixLength < oldLines.length && prefixLength < newLines.length && oldLines[prefixLength] === newLines[prefixLength]) {
|
|
5914
|
+
prefixLength++;
|
|
5915
|
+
}
|
|
5916
|
+
if (prefixLength === oldLines.length && prefixLength === newLines.length) {
|
|
5917
|
+
return { diff: "--- old\n+++ new\n", firstChangedLine: void 0 };
|
|
5918
|
+
}
|
|
5919
|
+
let oldSuffixIndex = oldLines.length - 1;
|
|
5920
|
+
let newSuffixIndex = newLines.length - 1;
|
|
5921
|
+
while (oldSuffixIndex >= prefixLength && newSuffixIndex >= prefixLength && oldLines[oldSuffixIndex] === newLines[newSuffixIndex]) {
|
|
5922
|
+
oldSuffixIndex--;
|
|
5923
|
+
newSuffixIndex--;
|
|
5924
|
+
}
|
|
5925
|
+
const beforeContext = Math.min(contextLines, prefixLength);
|
|
5926
|
+
const removedCount = Math.max(0, oldSuffixIndex - prefixLength + 1);
|
|
5927
|
+
const addedCount = Math.max(0, newSuffixIndex - prefixLength + 1);
|
|
5928
|
+
const afterContext = Math.min(contextLines, oldLines.length - oldSuffixIndex - 1, newLines.length - newSuffixIndex - 1);
|
|
5929
|
+
const hunkOldStart = Math.max(0, prefixLength - beforeContext);
|
|
5930
|
+
const hunkNewStart = Math.max(0, prefixLength - beforeContext);
|
|
5931
|
+
const hunkOldCount = beforeContext + removedCount + afterContext;
|
|
5932
|
+
const hunkNewCount = beforeContext + addedCount + afterContext;
|
|
5933
|
+
const lines = ["--- old", "+++ new", `@@ -${hunkOldStart + 1},${hunkOldCount} +${hunkNewStart + 1},${hunkNewCount} @@`];
|
|
5934
|
+
for (let index = prefixLength - beforeContext; index < prefixLength; index++) {
|
|
5935
|
+
lines.push(` ${oldLines[index] ?? ""}`);
|
|
5936
|
+
}
|
|
5937
|
+
for (let index = prefixLength; index <= oldSuffixIndex; index++) {
|
|
5938
|
+
lines.push(`-${oldLines[index] ?? ""}`);
|
|
5939
|
+
}
|
|
5940
|
+
for (let index = prefixLength; index <= newSuffixIndex; index++) {
|
|
5941
|
+
lines.push(`+${newLines[index] ?? ""}`);
|
|
5942
|
+
}
|
|
5943
|
+
for (let offset = 1; offset <= afterContext; offset++) {
|
|
5944
|
+
lines.push(` ${oldLines[oldSuffixIndex + offset] ?? ""}`);
|
|
5945
|
+
}
|
|
5946
|
+
return { diff: lines.join("\n"), firstChangedLine: prefixLength + 1 };
|
|
5209
5947
|
}
|
|
5210
5948
|
|
|
5211
5949
|
// ../../packages/shared-headless-capabilities/src/builtin/fs/edit.ts
|
|
@@ -5227,7 +5965,7 @@ function validateEditArgs(path3, oldText, newText) {
|
|
|
5227
5965
|
throw new Error("Invalid edit_file arguments: path, oldText, and newText must be real edit values, not tool-call markup.");
|
|
5228
5966
|
}
|
|
5229
5967
|
}
|
|
5230
|
-
function
|
|
5968
|
+
function countOccurrences2(content, needle) {
|
|
5231
5969
|
if (needle.length === 0) return 0;
|
|
5232
5970
|
let count = 0;
|
|
5233
5971
|
let index = 0;
|
|
@@ -5239,6 +5977,29 @@ function countOccurrences(content, needle) {
|
|
|
5239
5977
|
}
|
|
5240
5978
|
return count;
|
|
5241
5979
|
}
|
|
5980
|
+
function createEditErrorResult(params) {
|
|
5981
|
+
const detailLines = [
|
|
5982
|
+
params.message,
|
|
5983
|
+
params.recoveryHint,
|
|
5984
|
+
params.alreadyAppliedCandidate ? "Diagnostic: newText already appears in the current file, but edit_file did not treat that as success because it may be an unrelated occurrence." : ""
|
|
5985
|
+
].filter(Boolean);
|
|
5986
|
+
return {
|
|
5987
|
+
content: [{ type: "text", text: detailLines.join("\n") }],
|
|
5988
|
+
details: {
|
|
5989
|
+
status: "error",
|
|
5990
|
+
matchKind: params.matchKind,
|
|
5991
|
+
changed: false,
|
|
5992
|
+
diff: "",
|
|
5993
|
+
oldTextOccurrences: params.oldTextOccurrences,
|
|
5994
|
+
newTextOccurrences: params.newTextOccurrences,
|
|
5995
|
+
errorReason: params.errorReason,
|
|
5996
|
+
recoveryHint: params.recoveryHint,
|
|
5997
|
+
staleBaseLikely: params.staleBaseLikely,
|
|
5998
|
+
alreadyAppliedCandidate: params.alreadyAppliedCandidate
|
|
5999
|
+
},
|
|
6000
|
+
isError: true
|
|
6001
|
+
};
|
|
6002
|
+
}
|
|
5242
6003
|
function createEditTool(cwd, options) {
|
|
5243
6004
|
const fs2 = getFileSystem();
|
|
5244
6005
|
const rootSchema = buildRootParameterSchema(cwd, options);
|
|
@@ -5288,37 +6049,53 @@ function createEditTool(cwd, options) {
|
|
|
5288
6049
|
const normalizedContent = normalizeToLF(content);
|
|
5289
6050
|
const normalizedOldText = normalizeToLF(oldText);
|
|
5290
6051
|
const normalizedNewText = normalizeToLF(newText);
|
|
6052
|
+
const newTextOccurrences = countOccurrences2(normalizedContent, normalizedNewText);
|
|
5291
6053
|
const matchResult = fuzzyFindText(normalizedContent, normalizedOldText);
|
|
5292
6054
|
if (!matchResult.found) {
|
|
5293
6055
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
`Could not find
|
|
5297
|
-
|
|
6056
|
+
resolve2(
|
|
6057
|
+
createEditErrorResult({
|
|
6058
|
+
message: `Could not find oldText in ${path3}.`,
|
|
6059
|
+
matchKind: "none",
|
|
6060
|
+
errorReason: "old_text_not_found",
|
|
6061
|
+
oldTextOccurrences: 0,
|
|
6062
|
+
newTextOccurrences,
|
|
6063
|
+
staleBaseLikely: newTextOccurrences > 0,
|
|
6064
|
+
alreadyAppliedCandidate: newTextOccurrences > 0,
|
|
6065
|
+
recoveryHint: newTextOccurrences > 0 ? "The replacement text already appears in the current file. Read the current file before retrying; this may be a stale oldText from an earlier edit in the same turn." : "Read the current file and retry with a larger exact block copied from the latest file contents."
|
|
6066
|
+
})
|
|
5298
6067
|
);
|
|
5299
6068
|
return;
|
|
5300
6069
|
}
|
|
5301
|
-
|
|
5302
|
-
const fuzzyOldText = normalizeForFuzzyMatch(normalizedOldText);
|
|
5303
|
-
const occurrences = countOccurrences(fuzzyContent, fuzzyOldText);
|
|
5304
|
-
if (occurrences > 1) {
|
|
6070
|
+
if (matchResult.occurrences > 1) {
|
|
5305
6071
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
`Found ${occurrences} occurrences of
|
|
5309
|
-
|
|
6072
|
+
resolve2(
|
|
6073
|
+
createEditErrorResult({
|
|
6074
|
+
message: `Found ${matchResult.occurrences} occurrences of oldText in ${path3}. The text must be unique.`,
|
|
6075
|
+
matchKind: matchResult.matchKind,
|
|
6076
|
+
errorReason: "old_text_not_unique",
|
|
6077
|
+
oldTextOccurrences: matchResult.occurrences,
|
|
6078
|
+
newTextOccurrences,
|
|
6079
|
+
recoveryHint: "Read the current file and retry with a larger exact block that uniquely identifies the intended location."
|
|
6080
|
+
})
|
|
5310
6081
|
);
|
|
5311
6082
|
return;
|
|
5312
6083
|
}
|
|
5313
6084
|
if (aborted) return;
|
|
5314
|
-
const baseContent =
|
|
6085
|
+
const baseContent = normalizedContent;
|
|
5315
6086
|
const newContent = baseContent.substring(0, matchResult.index) + normalizedNewText + baseContent.substring(matchResult.index + matchResult.matchLength);
|
|
5316
6087
|
if (baseContent === newContent) {
|
|
5317
6088
|
if (signal) signal.removeEventListener("abort", onAbort);
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
`No changes made to ${path3}. The replacement produced identical content
|
|
5321
|
-
|
|
6089
|
+
resolve2(
|
|
6090
|
+
createEditErrorResult({
|
|
6091
|
+
message: `No changes made to ${path3}. The replacement produced identical content.`,
|
|
6092
|
+
matchKind: matchResult.matchKind,
|
|
6093
|
+
errorReason: "replacement_no_change",
|
|
6094
|
+
oldTextOccurrences: matchResult.occurrences,
|
|
6095
|
+
newTextOccurrences,
|
|
6096
|
+
recoveryHint: "Read the current file and verify whether the intended edit is already present or whether oldText/newText need a larger exact block.",
|
|
6097
|
+
alreadyAppliedCandidate: newTextOccurrences > 0
|
|
6098
|
+
})
|
|
5322
6099
|
);
|
|
5323
6100
|
return;
|
|
5324
6101
|
}
|
|
@@ -5328,16 +6105,24 @@ function createEditTool(cwd, options) {
|
|
|
5328
6105
|
if (signal) {
|
|
5329
6106
|
signal.removeEventListener("abort", onAbort);
|
|
5330
6107
|
}
|
|
5331
|
-
const diffResult = generateDiffString(content,
|
|
6108
|
+
const diffResult = generateDiffString(content, restoreLineEndings(newContent, originalEnding));
|
|
5332
6109
|
resolve2({
|
|
5333
6110
|
content: [
|
|
5334
6111
|
{
|
|
5335
6112
|
type: "text",
|
|
5336
|
-
text: `Successfully replaced text in ${path3}.`
|
|
6113
|
+
text: `Successfully replaced text in ${path3} using ${matchResult.matchKind} match.`
|
|
5337
6114
|
},
|
|
5338
6115
|
{ type: "text", text: diffResult.diff }
|
|
5339
6116
|
],
|
|
5340
|
-
details: {
|
|
6117
|
+
details: {
|
|
6118
|
+
status: "success",
|
|
6119
|
+
matchKind: matchResult.matchKind,
|
|
6120
|
+
changed: true,
|
|
6121
|
+
diff: diffResult.diff,
|
|
6122
|
+
firstChangedLine: diffResult.firstChangedLine,
|
|
6123
|
+
oldTextOccurrences: matchResult.occurrences,
|
|
6124
|
+
newTextOccurrences
|
|
6125
|
+
}
|
|
5341
6126
|
});
|
|
5342
6127
|
} catch (error) {
|
|
5343
6128
|
if (signal) {
|
|
@@ -6391,6 +7176,81 @@ function createEmptyRuntimeSummary() {
|
|
|
6391
7176
|
toolErrors: 0
|
|
6392
7177
|
};
|
|
6393
7178
|
}
|
|
7179
|
+
function summarizeString(value, maxLength = 300) {
|
|
7180
|
+
return value.length > maxLength ? `${value.slice(0, maxLength)}...` : value;
|
|
7181
|
+
}
|
|
7182
|
+
function summarizeMessage(message) {
|
|
7183
|
+
if (!isRecord(message)) return typeof message;
|
|
7184
|
+
if ("contentBlocks" in message && !("content" in message)) return message;
|
|
7185
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
7186
|
+
const blockTypes = content.filter(isRecord).map((block) => String(block.type || "unknown")).slice(0, 10);
|
|
7187
|
+
const text = getTextFromMessageContent(content);
|
|
7188
|
+
return {
|
|
7189
|
+
role: message.role,
|
|
7190
|
+
stopReason: message.stopReason,
|
|
7191
|
+
errorMessage: typeof message.errorMessage === "string" ? summarizeString(message.errorMessage) : void 0,
|
|
7192
|
+
contentBlocks: content.length,
|
|
7193
|
+
blockTypes,
|
|
7194
|
+
textLength: text.length
|
|
7195
|
+
};
|
|
7196
|
+
}
|
|
7197
|
+
function summarizeToolResults(value) {
|
|
7198
|
+
if (isRecord(value) && typeof value.count === "number" && !Array.isArray(value)) return value;
|
|
7199
|
+
if (!Array.isArray(value)) return typeof value;
|
|
7200
|
+
let errors = 0;
|
|
7201
|
+
const toolNames = [];
|
|
7202
|
+
for (const result of value) {
|
|
7203
|
+
if (!isRecord(result)) continue;
|
|
7204
|
+
if (result.isError === true) errors++;
|
|
7205
|
+
const toolName = typeof result.toolName === "string" ? result.toolName : typeof result.name === "string" ? result.name : void 0;
|
|
7206
|
+
if (toolName && toolNames.length < 10) toolNames.push(toolName);
|
|
7207
|
+
}
|
|
7208
|
+
return { count: value.length, errors, toolNames };
|
|
7209
|
+
}
|
|
7210
|
+
function summarizeFailure(value) {
|
|
7211
|
+
if (!isRecord(value)) return typeof value;
|
|
7212
|
+
return {
|
|
7213
|
+
kind: value.kind,
|
|
7214
|
+
retryable: value.retryable,
|
|
7215
|
+
compactable: value.compactable,
|
|
7216
|
+
userAbort: value.userAbort,
|
|
7217
|
+
terminal: value.terminal,
|
|
7218
|
+
message: typeof value.message === "string" ? summarizeString(value.message) : void 0
|
|
7219
|
+
};
|
|
7220
|
+
}
|
|
7221
|
+
function summarizeRuntimeEvent(value) {
|
|
7222
|
+
if (!isRecord(value)) return typeof value;
|
|
7223
|
+
const summary = {
|
|
7224
|
+
type: value.type
|
|
7225
|
+
};
|
|
7226
|
+
if ("toolCallId" in value) summary.toolCallId = value.toolCallId;
|
|
7227
|
+
if ("toolName" in value) summary.toolName = value.toolName;
|
|
7228
|
+
if ("turnIndex" in value) summary.turnIndex = value.turnIndex;
|
|
7229
|
+
if ("isError" in value) summary.isError = value.isError;
|
|
7230
|
+
if ("errorMessage" in value && typeof value.errorMessage === "string") {
|
|
7231
|
+
summary.errorMessage = summarizeString(value.errorMessage);
|
|
7232
|
+
}
|
|
7233
|
+
if ("message" in value) summary.message = summarizeMessage(value.message);
|
|
7234
|
+
if ("toolResults" in value) summary.toolResults = summarizeToolResults(value.toolResults);
|
|
7235
|
+
return summary;
|
|
7236
|
+
}
|
|
7237
|
+
function shouldRecordRuntimeEvent(type) {
|
|
7238
|
+
const normalizedType = type.startsWith("runtime:") ? type.slice("runtime:".length) : type;
|
|
7239
|
+
return normalizedType !== "agent_event" && normalizedType !== "semantic_event";
|
|
7240
|
+
}
|
|
7241
|
+
function recomputeRuntimeSummaryFromTimeline(events) {
|
|
7242
|
+
const summary = createEmptyRuntimeSummary();
|
|
7243
|
+
for (const event of events ?? []) {
|
|
7244
|
+
if (/queue|queued_turn/.test(event.type)) summary.queuedTurnEvents++;
|
|
7245
|
+
if (/retry/.test(event.type)) summary.retryEvents++;
|
|
7246
|
+
if (/auto_compaction|auto_compact|compaction/.test(event.type)) summary.autoCompactionEvents++;
|
|
7247
|
+
if (/runtime_guard|repeated_tool_error/.test(event.type)) summary.runtimeGuardEvents++;
|
|
7248
|
+
if (/subagent/.test(event.type)) summary.subagentEvents++;
|
|
7249
|
+
if (event.type === "tool_end" || event.type === "tool_error") summary.toolResults++;
|
|
7250
|
+
if (event.type === "tool_error") summary.toolErrors++;
|
|
7251
|
+
}
|
|
7252
|
+
return summary;
|
|
7253
|
+
}
|
|
6394
7254
|
function summarizeMeta(meta) {
|
|
6395
7255
|
if (!isRecord(meta)) return meta;
|
|
6396
7256
|
const summary = {};
|
|
@@ -6399,6 +7259,22 @@ function summarizeMeta(meta) {
|
|
|
6399
7259
|
summary[key] = Array.isArray(value) ? { count: value.length } : typeof value;
|
|
6400
7260
|
continue;
|
|
6401
7261
|
}
|
|
7262
|
+
if (key === "message") {
|
|
7263
|
+
summary[key] = summarizeMessage(value);
|
|
7264
|
+
continue;
|
|
7265
|
+
}
|
|
7266
|
+
if (key === "toolResults") {
|
|
7267
|
+
summary[key] = summarizeToolResults(value);
|
|
7268
|
+
continue;
|
|
7269
|
+
}
|
|
7270
|
+
if (key === "failure") {
|
|
7271
|
+
summary[key] = summarizeFailure(value);
|
|
7272
|
+
continue;
|
|
7273
|
+
}
|
|
7274
|
+
if (key === "event") {
|
|
7275
|
+
summary[key] = summarizeRuntimeEvent(value);
|
|
7276
|
+
continue;
|
|
7277
|
+
}
|
|
6402
7278
|
if (key === "queuedInput" && isRecord(value)) {
|
|
6403
7279
|
summary[key] = {
|
|
6404
7280
|
mode: value.mode,
|
|
@@ -6408,7 +7284,7 @@ function summarizeMeta(meta) {
|
|
|
6408
7284
|
};
|
|
6409
7285
|
continue;
|
|
6410
7286
|
}
|
|
6411
|
-
summary[key] = typeof value === "string"
|
|
7287
|
+
summary[key] = typeof value === "string" ? summarizeString(value) : value;
|
|
6412
7288
|
}
|
|
6413
7289
|
return summary;
|
|
6414
7290
|
}
|
|
@@ -6522,9 +7398,7 @@ function timelineEventFromTraceEvent(type, meta) {
|
|
|
6522
7398
|
failure: typeof data.failureCategory === "string" && data.failureCategory.length > 0
|
|
6523
7399
|
};
|
|
6524
7400
|
}
|
|
6525
|
-
if (normalizedType === "subagent_summary")
|
|
6526
|
-
return { type: "subagent_child_end", summary: `subagent summary count=${String(data.count ?? "?")}`, details: detail };
|
|
6527
|
-
}
|
|
7401
|
+
if (normalizedType === "subagent_summary") return null;
|
|
6528
7402
|
return null;
|
|
6529
7403
|
}
|
|
6530
7404
|
function cloneTurn(turn) {
|
|
@@ -6565,6 +7439,9 @@ var SessionTraceController = class {
|
|
|
6565
7439
|
if (!Array.isArray(this.trace.recentTimeline)) {
|
|
6566
7440
|
this.trace.recentTimeline = [];
|
|
6567
7441
|
}
|
|
7442
|
+
this.trace.recentEvents = this.trace.recentEvents.slice(-this.recentEventsLimit);
|
|
7443
|
+
this.trace.recentTimeline = this.trace.recentTimeline.slice(-this.recentEventsLimit);
|
|
7444
|
+
this.trace.runtimeSummary = recomputeRuntimeSummaryFromTimeline(this.trace.recentTimeline);
|
|
6568
7445
|
}
|
|
6569
7446
|
trace;
|
|
6570
7447
|
recentTurnsLimit;
|
|
@@ -6579,7 +7456,6 @@ var SessionTraceController = class {
|
|
|
6579
7456
|
if (this.trace.recentEvents.length > this.recentEventsLimit) {
|
|
6580
7457
|
this.trace.recentEvents = this.trace.recentEvents.slice(-this.recentEventsLimit);
|
|
6581
7458
|
}
|
|
6582
|
-
this.updateRuntimeSummary(type, meta);
|
|
6583
7459
|
const timelineEvent = timelineEventFromTraceEvent(type, meta);
|
|
6584
7460
|
if (timelineEvent) {
|
|
6585
7461
|
this.pushTimeline(timelineEvent);
|
|
@@ -6594,17 +7470,7 @@ var SessionTraceController = class {
|
|
|
6594
7470
|
if (this.trace.recentTimeline.length > this.recentEventsLimit) {
|
|
6595
7471
|
this.trace.recentTimeline = this.trace.recentTimeline.slice(-this.recentEventsLimit);
|
|
6596
7472
|
}
|
|
6597
|
-
|
|
6598
|
-
updateRuntimeSummary(type, meta) {
|
|
6599
|
-
const summary = this.trace.runtimeSummary ?? createEmptyRuntimeSummary();
|
|
6600
|
-
if (/queue|queued_turn/.test(type)) summary.queuedTurnEvents++;
|
|
6601
|
-
if (/retry/.test(type)) summary.retryEvents++;
|
|
6602
|
-
if (/auto_compaction|auto_compact|compaction/.test(type)) summary.autoCompactionEvents++;
|
|
6603
|
-
if (/runtime_guard|repeated_tool_error/.test(type)) summary.runtimeGuardEvents++;
|
|
6604
|
-
if (/subagent/.test(type)) summary.subagentEvents++;
|
|
6605
|
-
if (type === "tool_end") summary.toolResults++;
|
|
6606
|
-
if (type === "tool_end" && isRecord(meta) && meta.isError) summary.toolErrors++;
|
|
6607
|
-
this.trace.runtimeSummary = summary;
|
|
7473
|
+
this.trace.runtimeSummary = recomputeRuntimeSummaryFromTimeline(this.trace.recentTimeline);
|
|
6608
7474
|
}
|
|
6609
7475
|
nextTurnIndex() {
|
|
6610
7476
|
if (this.trace.activeTurn) {
|
|
@@ -6676,6 +7542,7 @@ var SessionTraceController = class {
|
|
|
6676
7542
|
this.touch();
|
|
6677
7543
|
}
|
|
6678
7544
|
noteRuntimeEvent(type, meta) {
|
|
7545
|
+
if (!shouldRecordRuntimeEvent(type)) return;
|
|
6679
7546
|
this.pushEvent(`runtime:${type}`, summarizeMeta(meta));
|
|
6680
7547
|
this.touch();
|
|
6681
7548
|
}
|
|
@@ -6700,7 +7567,6 @@ var SessionTraceController = class {
|
|
|
6700
7567
|
if (!message || message.role !== "assistant" || !this.trace.activeTurn) return;
|
|
6701
7568
|
this.trace.activeTurn.state = this.trace.activeTurn.toolCalls.some((tool) => tool.status === "running") ? "tool_running" : "streaming";
|
|
6702
7569
|
this.trace.activeTurn.assistantText = getTextFromMessageContent(message.content);
|
|
6703
|
-
this.pushEvent("assistant_update");
|
|
6704
7570
|
this.touch();
|
|
6705
7571
|
}
|
|
6706
7572
|
startTool(toolCallId, toolName, args) {
|
|
@@ -9023,7 +9889,10 @@ export {
|
|
|
9023
9889
|
ManageTodoListParamsSchema,
|
|
9024
9890
|
executeManageTodoList,
|
|
9025
9891
|
maybeAdvanceTodoExecutionState,
|
|
9892
|
+
estimateTokens,
|
|
9026
9893
|
calculateTotalTokens,
|
|
9894
|
+
estimateContextTokens,
|
|
9895
|
+
getAutoCompactTokenLimit,
|
|
9027
9896
|
findFirstKeptEntryId,
|
|
9028
9897
|
createCompactionSummary,
|
|
9029
9898
|
createSessionManager,
|