@cydm/pie 1.0.14 → 1.0.16

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.
@@ -1,6 +1,8 @@
1
1
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
2
2
  import {
3
- Agent,
3
+ Agent
4
+ } from "./chunk-VE2HDCNB.js";
5
+ import {
4
6
  FileSystemGateway,
5
7
  Type,
6
8
  completeSimple,
@@ -8,7 +10,7 @@ import {
8
10
  getFileSystem,
9
11
  getPlatformConfig,
10
12
  streamSimple
11
- } from "./chunk-D7NAXU7F.js";
13
+ } from "./chunk-5DA2D3K2.js";
12
14
  import {
13
15
  __require
14
16
  } from "./chunk-TG2EQLX2.js";
@@ -1469,36 +1471,66 @@ var DEFAULT_COMPACTION_CONFIG = {
1469
1471
  compactPrompt: COMPACT_PROMPT,
1470
1472
  summaryPrefix: SUMMARY_PREFIX
1471
1473
  };
1474
+ var APPROX_BYTES_PER_TOKEN = 4;
1475
+ var RESIZED_IMAGE_BYTES_ESTIMATE = 7373;
1472
1476
  function estimateTokens(message) {
1473
- if (message.role === "assistant") {
1474
- let chars = 0;
1475
- for (const block of message.content) {
1476
- if (block.type === "text") {
1477
- chars += block.text.length;
1478
- } else if (block.type === "thinking") {
1479
- chars += block.thinking.length;
1480
- } else if (block.type === "toolCall") {
1481
- chars += block.name.length + JSON.stringify(block.arguments).length;
1482
- }
1483
- }
1484
- 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;
1485
1488
  }
1486
- if (message.role === "toolResult") {
1487
- let chars = 0;
1488
- for (const block of message.content) {
1489
- if (block.type === "text") {
1490
- chars += block.text.length;
1491
- } else if (block.type === "image") {
1492
- chars += 4800;
1493
- }
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;
1494
1503
  }
1495
- return Math.ceil(chars / 4);
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
+ };
1496
1515
  }
1497
- const text = getMessageText(message);
1498
- return Math.ceil(text.length / 4);
1516
+ return {
1517
+ tokens: localTokens,
1518
+ usageTokens: 0,
1519
+ providerTokens: 0,
1520
+ localTokens,
1521
+ trailingTokens: localTokens,
1522
+ lastUsageIndex: null
1523
+ };
1499
1524
  }
1500
- function calculateTotalTokens(messages) {
1501
- return messages.reduce((sum, m) => sum + estimateTokens(m), 0);
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;
1502
1534
  }
1503
1535
  function isCompactSummaryMessage(message, summaryPrefix = SUMMARY_PREFIX) {
1504
1536
  return message.startsWith(`${summaryPrefix}
@@ -1690,6 +1722,138 @@ function appendFileOperationsToSummary(summary, messages) {
1690
1722
  const { readFiles, modifiedFiles } = computeFileLists(fileOps);
1691
1723
  return `${summary}${formatFileOperations(readFiles, modifiedFiles)}`;
1692
1724
  }
1725
+ function estimateModelVisibleBytes(message) {
1726
+ const visibleMessage = normalizeModelVisibleMessage(message);
1727
+ const wrapperBytes = utf8ByteLength(safeStringify(visibleMessage));
1728
+ return wrapperBytes + estimateMessageMediaBytes(message);
1729
+ }
1730
+ function normalizeModelVisibleMessage(message) {
1731
+ if (message.role === "user") {
1732
+ return {
1733
+ role: "user",
1734
+ content: typeof message.content === "string" ? message.content : message.content.map(normalizeUserContentBlock)
1735
+ };
1736
+ }
1737
+ if (message.role === "assistant") {
1738
+ return {
1739
+ role: "assistant",
1740
+ content: message.content.map((block) => {
1741
+ if (block.type === "text") {
1742
+ return {
1743
+ type: "text",
1744
+ text: block.text,
1745
+ ...block.textSignature ? { textSignature: block.textSignature } : {}
1746
+ };
1747
+ }
1748
+ if (block.type === "thinking") {
1749
+ return {
1750
+ type: "thinking",
1751
+ thinking: block.thinking,
1752
+ ...block.thinkingSignature ? { thinkingSignature: block.thinkingSignature } : {}
1753
+ };
1754
+ }
1755
+ if (block.type === "toolCall") {
1756
+ return {
1757
+ type: "toolCall",
1758
+ id: block.id,
1759
+ name: block.name,
1760
+ arguments: block.arguments ?? {},
1761
+ ...block.thoughtSignature ? { thoughtSignature: block.thoughtSignature } : {}
1762
+ };
1763
+ }
1764
+ return block;
1765
+ })
1766
+ };
1767
+ }
1768
+ if (message.role === "toolResult") {
1769
+ return {
1770
+ role: "toolResult",
1771
+ toolCallId: message.toolCallId,
1772
+ toolName: message.toolName,
1773
+ isError: message.isError,
1774
+ content: message.content.map(normalizeUserContentBlock)
1775
+ };
1776
+ }
1777
+ return {
1778
+ role: String(message.role ?? "unknown"),
1779
+ content: getMessageText(message)
1780
+ };
1781
+ }
1782
+ function normalizeUserContentBlock(block) {
1783
+ if (block.type === "text") {
1784
+ return { type: "text", text: block.text };
1785
+ }
1786
+ if (block.type === "image") {
1787
+ return { type: "image", mimeType: block.mimeType };
1788
+ }
1789
+ if (block.type === "audio") {
1790
+ return { type: "audio", mimeType: block.mimeType };
1791
+ }
1792
+ if (block.type === "video") {
1793
+ return { type: "video", mimeType: block.mimeType };
1794
+ }
1795
+ if (block.type === "fileRef") {
1796
+ return {
1797
+ type: "fileRef",
1798
+ url: block.url,
1799
+ mimeType: block.mimeType,
1800
+ modality: block.modality
1801
+ };
1802
+ }
1803
+ return block;
1804
+ }
1805
+ function estimateMessageMediaBytes(message) {
1806
+ if (message.role === "user" && Array.isArray(message.content)) {
1807
+ return message.content.reduce((sum, block) => sum + estimateUserContentBlockMediaBytes(block), 0);
1808
+ }
1809
+ if (message.role === "toolResult") {
1810
+ return message.content.reduce((sum, block) => sum + estimateUserContentBlockMediaBytes(block), 0);
1811
+ }
1812
+ return 0;
1813
+ }
1814
+ function estimateUserContentBlockMediaBytes(block) {
1815
+ if (block.type === "image") {
1816
+ return estimateImageContentBytes(block);
1817
+ }
1818
+ if (block.type === "audio") {
1819
+ return estimateInlineDataBytes(block);
1820
+ }
1821
+ if (block.type === "video") {
1822
+ return estimateInlineDataBytes(block);
1823
+ }
1824
+ return 0;
1825
+ }
1826
+ function estimateImageContentBytes(block) {
1827
+ return Math.max(RESIZED_IMAGE_BYTES_ESTIMATE, estimateInlineDataBytes(block));
1828
+ }
1829
+ function estimateInlineDataBytes(block) {
1830
+ return utf8ByteLength(block.data || "");
1831
+ }
1832
+ function approximateBytesAsTokens(bytes) {
1833
+ return Math.ceil(bytes / APPROX_BYTES_PER_TOKEN);
1834
+ }
1835
+ function safeStringify(value) {
1836
+ try {
1837
+ return JSON.stringify(value) ?? "";
1838
+ } catch {
1839
+ return String(value ?? "");
1840
+ }
1841
+ }
1842
+ var utf8Encoder = typeof globalThis.TextEncoder === "function" ? new globalThis.TextEncoder() : void 0;
1843
+ function utf8ByteLength(text) {
1844
+ if (utf8Encoder) {
1845
+ return utf8Encoder.encode(text).length;
1846
+ }
1847
+ let bytes = 0;
1848
+ for (const char of text) {
1849
+ const codePoint = char.codePointAt(0) ?? 0;
1850
+ if (codePoint <= 127) bytes += 1;
1851
+ else if (codePoint <= 2047) bytes += 2;
1852
+ else if (codePoint <= 65535) bytes += 3;
1853
+ else bytes += 4;
1854
+ }
1855
+ return bytes;
1856
+ }
1693
1857
  function isSyntheticUserEnvelope(text) {
1694
1858
  const trimmed = text.trimStart();
1695
1859
  return trimmed.startsWith("<environment_context>") || trimmed.startsWith("<turn_aborted>") || trimmed.startsWith("# AGENTS.md instructions") || trimmed.startsWith("## Project Context\nProject-specific instructions from AGENTS.md");
@@ -2805,13 +2969,20 @@ var AgentSessionAutoCompactScheduler = class {
2805
2969
  options;
2806
2970
  autoCompactTimer;
2807
2971
  pendingAutoCompact;
2972
+ runningAutoCompact;
2973
+ pendingWait;
2974
+ resolvePendingWait;
2808
2975
  get hasPendingAutoCompact() {
2809
- return this.autoCompactTimer !== void 0;
2976
+ return this.autoCompactTimer !== void 0 || this.pendingAutoCompact !== void 0 || this.runningAutoCompact !== void 0;
2977
+ }
2978
+ waitForAutoCompact() {
2979
+ return this.pendingWait ?? Promise.resolve();
2810
2980
  }
2811
2981
  schedule(decision, delayMs = 0) {
2812
2982
  if (this.options.getRuntimeGuardTriggered()) {
2813
2983
  return;
2814
2984
  }
2985
+ this.ensurePendingWait();
2815
2986
  this.pendingAutoCompact = decision;
2816
2987
  if (this.autoCompactTimer !== void 0) {
2817
2988
  return;
@@ -2822,6 +2993,7 @@ var AgentSessionAutoCompactScheduler = class {
2822
2993
  const pending = this.pendingAutoCompact;
2823
2994
  this.pendingAutoCompact = void 0;
2824
2995
  if (!pending || this.options.getDisposed()) {
2996
+ this.resolvePending();
2825
2997
  return;
2826
2998
  }
2827
2999
  if (this.options.isStreaming()) {
@@ -2829,17 +3001,33 @@ var AgentSessionAutoCompactScheduler = class {
2829
3001
  this.schedule(pending, 100);
2830
3002
  return;
2831
3003
  }
2832
- void this.options.runAutoCompact(pending);
3004
+ this.runningAutoCompact = this.options.runAutoCompact(pending).catch(() => {
3005
+ }).finally(() => {
3006
+ this.runningAutoCompact = void 0;
3007
+ this.resolvePending();
3008
+ });
2833
3009
  }, delayMs);
2834
3010
  }
2835
3011
  cancel() {
2836
- if (this.autoCompactTimer === void 0) {
2837
- return;
3012
+ if (this.autoCompactTimer !== void 0) {
3013
+ const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout;
3014
+ clearTimer(this.autoCompactTimer);
3015
+ this.autoCompactTimer = void 0;
2838
3016
  }
2839
- const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout;
2840
- clearTimer(this.autoCompactTimer);
2841
- this.autoCompactTimer = void 0;
2842
3017
  this.pendingAutoCompact = void 0;
3018
+ this.resolvePending();
3019
+ }
3020
+ ensurePendingWait() {
3021
+ if (this.pendingWait) return;
3022
+ this.pendingWait = new Promise((resolve2) => {
3023
+ this.resolvePendingWait = resolve2;
3024
+ });
3025
+ }
3026
+ resolvePending() {
3027
+ const resolve2 = this.resolvePendingWait;
3028
+ this.pendingWait = void 0;
3029
+ this.resolvePendingWait = void 0;
3030
+ resolve2?.();
2843
3031
  }
2844
3032
  };
2845
3033
 
@@ -2856,15 +3044,24 @@ var AgentSessionAutoContinueController = class {
2856
3044
  }
2857
3045
  options;
2858
3046
  autoContinueTimer;
3047
+ pendingAutoContinue;
3048
+ resolvePendingAutoContinue;
3049
+ get hasPendingAutoContinue() {
3050
+ return this.autoContinueTimer !== void 0 || this.pendingAutoContinue !== void 0;
3051
+ }
3052
+ waitForAutoContinue() {
3053
+ return this.pendingAutoContinue ?? Promise.resolve();
3054
+ }
2859
3055
  schedule(messages) {
2860
3056
  if (!this.options.getAutoContinueMessage || this.options.getDisposed() || this.autoContinueTimer !== void 0) {
2861
3057
  return;
2862
3058
  }
3059
+ this.ensurePending();
2863
3060
  const setTimer = this.options.retry?.setTimeout ?? defaultSetTimeout2;
2864
3061
  const snapshot = messages.slice();
2865
3062
  this.autoContinueTimer = setTimer(() => {
2866
3063
  this.autoContinueTimer = void 0;
2867
- void this.maybeScheduleAutoContinue(snapshot);
3064
+ void this.maybeScheduleAutoContinue(snapshot).finally(() => this.resolvePending());
2868
3065
  }, 0);
2869
3066
  }
2870
3067
  cancel() {
@@ -2874,9 +3071,10 @@ var AgentSessionAutoContinueController = class {
2874
3071
  const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout2;
2875
3072
  clearTimer(this.autoContinueTimer);
2876
3073
  this.autoContinueTimer = void 0;
3074
+ this.resolvePending();
2877
3075
  }
2878
3076
  async maybeScheduleAutoContinue(messages) {
2879
- if (!this.options.getAutoContinueMessage || this.options.getDisposed() || this.options.getRuntimeGuardTriggered() || this.options.hasQueuedMessages() || this.options.isStreaming()) {
3077
+ if (!this.options.getAutoContinueMessage || this.options.getDisposed() || this.options.getRuntimeGuardTriggered() || this.options.isBlockedByRuntimePolicy() || this.options.hasQueuedMessages() || this.options.isStreaming()) {
2880
3078
  if (this.options.hasQueuedMessages()) {
2881
3079
  this.options.emit({ type: "auto_continue_skipped", reason: "queued_messages" });
2882
3080
  }
@@ -2900,21 +3098,36 @@ var AgentSessionAutoContinueController = class {
2900
3098
  this.options.emit({ type: "auto_continue_skipped", reason: reason ?? "no_message" });
2901
3099
  return;
2902
3100
  }
2903
- if (this.options.hasQueuedMessages() || this.options.isStreaming()) {
3101
+ if (this.options.hasQueuedMessages() || this.options.isStreaming() || this.options.isBlockedByRuntimePolicy()) {
2904
3102
  this.options.emit({ type: "auto_continue_skipped", reason: "runtime_busy" });
2905
3103
  return;
2906
3104
  }
2907
3105
  this.options.emit({ type: "auto_continue_scheduled", reason });
2908
3106
  this.options.agent.followUp(message);
2909
3107
  const setTimer = this.options.retry?.setTimeout ?? defaultSetTimeout2;
2910
- this.autoContinueTimer = setTimer(() => {
2911
- this.autoContinueTimer = void 0;
2912
- if (this.options.getDisposed()) {
2913
- return;
2914
- }
2915
- void this.options.continueTurn().catch(() => {
2916
- });
2917
- }, 0);
3108
+ await new Promise((resolve2) => {
3109
+ this.autoContinueTimer = setTimer(() => {
3110
+ this.autoContinueTimer = void 0;
3111
+ if (this.options.getDisposed()) {
3112
+ resolve2();
3113
+ return;
3114
+ }
3115
+ void this.options.continueTurn().catch(() => {
3116
+ }).finally(resolve2);
3117
+ }, 0);
3118
+ });
3119
+ }
3120
+ ensurePending() {
3121
+ if (this.pendingAutoContinue) return;
3122
+ this.pendingAutoContinue = new Promise((resolve2) => {
3123
+ this.resolvePendingAutoContinue = resolve2;
3124
+ });
3125
+ }
3126
+ resolvePending() {
3127
+ const resolve2 = this.resolvePendingAutoContinue;
3128
+ this.pendingAutoContinue = void 0;
3129
+ this.resolvePendingAutoContinue = void 0;
3130
+ resolve2?.();
2918
3131
  }
2919
3132
  };
2920
3133
 
@@ -2932,7 +3145,7 @@ async function runAgentSessionCompaction(params) {
2932
3145
  }
2933
3146
  params.emit({ type: "auto_compaction_start", reason: params.reason, willRetry: params.willRetry });
2934
3147
  try {
2935
- if (params.reason === "overflow") {
3148
+ if (params.willRetry) {
2936
3149
  params.removeTrailingAssistantError();
2937
3150
  }
2938
3151
  const result = await params.compaction.compact({
@@ -3026,6 +3239,27 @@ async function preparePromptMessages(params) {
3026
3239
  };
3027
3240
  }
3028
3241
 
3242
+ // ../../packages/agent-framework/src/session/controller/pre-request-policy.ts
3243
+ async function runPreRequestAutoCompact(params) {
3244
+ if (params.disposed || params.runtimeGuardTriggered || params.isStreaming || params.recoveryActive || params.agent.state.messages.length === 0) {
3245
+ return;
3246
+ }
3247
+ const decision = params.getAutoCompactDecision?.({
3248
+ agent: params.agent,
3249
+ sessionManager: params.sessionManager,
3250
+ cwd: params.cwd,
3251
+ messages: params.agent.state.messages.slice()
3252
+ });
3253
+ if (decision?.reason !== "threshold") {
3254
+ return;
3255
+ }
3256
+ try {
3257
+ await params.runAutoCompact();
3258
+ } catch {
3259
+ params.setRecoveryState("none");
3260
+ }
3261
+ }
3262
+
3029
3263
  // ../../packages/agent-framework/src/session/controller/queue.ts
3030
3264
  function cloneQueuedInput(input) {
3031
3265
  return {
@@ -3184,14 +3418,23 @@ var AgentSessionQueueDispatcher = class {
3184
3418
  }
3185
3419
  options;
3186
3420
  dispatchTimer;
3421
+ pendingDispatch;
3422
+ resolvePendingDispatch;
3423
+ get hasPendingDispatch() {
3424
+ return this.dispatchTimer !== void 0 || this.pendingDispatch !== void 0;
3425
+ }
3426
+ waitForDispatch() {
3427
+ return this.pendingDispatch ?? Promise.resolve();
3428
+ }
3187
3429
  schedule(delayMs = 0) {
3188
3430
  if (this.options.getDisposed() || this.options.getRuntimeGuardTriggered() || this.dispatchTimer !== void 0) {
3189
3431
  return;
3190
3432
  }
3433
+ this.ensurePending();
3191
3434
  const setTimer = this.options.retry?.setTimeout ?? defaultSetTimeout3;
3192
3435
  this.dispatchTimer = setTimer(() => {
3193
3436
  this.dispatchTimer = void 0;
3194
- void this.dispatchNext();
3437
+ void this.dispatchNext().finally(() => this.resolvePending());
3195
3438
  }, delayMs);
3196
3439
  }
3197
3440
  cancel() {
@@ -3201,6 +3444,7 @@ var AgentSessionQueueDispatcher = class {
3201
3444
  const clearTimer = this.options.retry?.clearTimeout ?? defaultClearTimeout3;
3202
3445
  clearTimer(this.dispatchTimer);
3203
3446
  this.dispatchTimer = void 0;
3447
+ this.resolvePending();
3204
3448
  }
3205
3449
  async dispatchNext() {
3206
3450
  if (this.options.getDisposed() || this.options.isStreaming() || this.options.isBlockedByRuntimePolicy()) {
@@ -3228,6 +3472,11 @@ var AgentSessionQueueDispatcher = class {
3228
3472
  }
3229
3473
  return false;
3230
3474
  }
3475
+ await this.options.runPreRequestAutoCompact();
3476
+ if (this.options.getDisposed() || this.options.isStreaming() || this.options.isBlockedByRuntimePolicy()) {
3477
+ this.schedule(100);
3478
+ return false;
3479
+ }
3231
3480
  const dispatched = this.options.queue.shift();
3232
3481
  if (!dispatched) {
3233
3482
  return false;
@@ -3257,8 +3506,56 @@ var AgentSessionQueueDispatcher = class {
3257
3506
  }
3258
3507
  return true;
3259
3508
  }
3509
+ ensurePending() {
3510
+ if (this.pendingDispatch) return;
3511
+ this.pendingDispatch = new Promise((resolve2) => {
3512
+ this.resolvePendingDispatch = resolve2;
3513
+ });
3514
+ }
3515
+ resolvePending() {
3516
+ const resolve2 = this.resolvePendingDispatch;
3517
+ this.pendingDispatch = void 0;
3518
+ this.resolvePendingDispatch = void 0;
3519
+ resolve2?.();
3520
+ }
3260
3521
  };
3261
3522
 
3523
+ // ../../packages/agent-framework/src/session/controller/failure-classification.ts
3524
+ function isAssistantFailureMessage(message) {
3525
+ return message?.role === "assistant" && (message.stopReason === "error" || message.stopReason === "aborted");
3526
+ }
3527
+ function isAssistantTerminalCompletion(message) {
3528
+ if (message?.role !== "assistant") return false;
3529
+ if (message.stopReason === "error" || message.stopReason === "aborted") return false;
3530
+ if (message.stopReason === "toolUse") return false;
3531
+ return !message.content.some((block) => block.type === "toolCall");
3532
+ }
3533
+ function createFailureClassification(context, retry, classifyFailure) {
3534
+ const hostClassification = classifyFailure?.(context);
3535
+ if (hostClassification) {
3536
+ return hostClassification;
3537
+ }
3538
+ const retryable = retry?.isRetryableError(context.errorMessage) === true;
3539
+ const userAbort = context.message?.role === "assistant" && context.message.stopReason === "aborted";
3540
+ return {
3541
+ kind: userAbort ? "user_abort" : retryable ? "provider_transient" : "unknown",
3542
+ retryable: retryable && !userAbort,
3543
+ compactable: false,
3544
+ userAbort,
3545
+ terminal: !retryable || userAbort,
3546
+ message: context.errorMessage
3547
+ };
3548
+ }
3549
+ function asTerminalFailure(failure, message = failure.message) {
3550
+ return {
3551
+ ...failure,
3552
+ retryable: false,
3553
+ compactable: false,
3554
+ terminal: true,
3555
+ message
3556
+ };
3557
+ }
3558
+
3262
3559
  // ../../packages/agent-framework/src/session/controller/retry-policy.ts
3263
3560
  function defaultSetTimeout4(callback, delay) {
3264
3561
  return setTimeout(callback, delay);
@@ -3267,59 +3564,99 @@ function defaultClearTimeout4(handle) {
3267
3564
  clearTimeout(handle);
3268
3565
  }
3269
3566
  var AgentSessionRetryController = class {
3270
- constructor(retry, emit, removeTrailingAssistantError2, continueTurn) {
3567
+ constructor(retry, emit, removeTrailingAssistantError2, continueTurn, setRecoveryState) {
3271
3568
  this.retry = retry;
3272
3569
  this.emit = emit;
3273
3570
  this.removeTrailingAssistantError = removeTrailingAssistantError2;
3274
3571
  this.continueTurn = continueTurn;
3572
+ this.setRecoveryState = setRecoveryState;
3275
3573
  }
3276
3574
  retry;
3277
3575
  emit;
3278
3576
  removeTrailingAssistantError;
3279
3577
  continueTurn;
3578
+ setRecoveryState;
3280
3579
  retryAttempt = 0;
3281
3580
  retryTimer;
3581
+ pendingRetry;
3582
+ resolvePendingRetry;
3583
+ get policy() {
3584
+ return this.retry;
3585
+ }
3282
3586
  get hasPendingRetry() {
3283
- return this.retryTimer !== void 0;
3587
+ return this.retryTimer !== void 0 || this.pendingRetry !== void 0;
3284
3588
  }
3285
- async handleTurnEnd(message) {
3589
+ waitForRetry() {
3590
+ return this.pendingRetry ?? Promise.resolve();
3591
+ }
3592
+ async handleTurnEnd(message, failure) {
3286
3593
  if (!this.retry || !message || message.role !== "assistant") {
3287
3594
  return;
3288
3595
  }
3289
- const errorMessage = message.stopReason === "error" ? message.errorMessage : void 0;
3290
- if (!errorMessage) {
3596
+ if (!failure) {
3291
3597
  if (this.retryAttempt > 0) {
3292
3598
  this.emit({ type: "retry_succeeded", attempts: this.retryAttempt });
3599
+ this.emit({ type: "turn_retry_succeeded", attempts: this.retryAttempt });
3293
3600
  this.retryAttempt = 0;
3601
+ this.setRecoveryState("none");
3602
+ this.resolvePending();
3294
3603
  }
3295
3604
  return;
3296
3605
  }
3297
- if (!this.retry.isRetryableError(errorMessage)) {
3606
+ if (!failure.retryable) {
3607
+ if (this.retryAttempt > 0) {
3608
+ this.retryAttempt = 0;
3609
+ this.setRecoveryState("none");
3610
+ this.resolvePending();
3611
+ }
3298
3612
  return;
3299
3613
  }
3300
3614
  const maxRetries = this.retry.maxRetries ?? 3;
3301
3615
  this.retryAttempt += 1;
3302
3616
  if (this.retryAttempt > maxRetries) {
3303
- this.emit({ type: "retry_exhausted", errorMessage, maxRetries });
3617
+ const terminalFailure = asTerminalFailure(failure);
3618
+ this.emit({ type: "retry_exhausted", errorMessage: failure.message, maxRetries });
3619
+ this.emit({ type: "turn_failed_terminal", failure: terminalFailure, message });
3620
+ this.emit({ type: "turn_settled" });
3304
3621
  this.retryAttempt = 0;
3622
+ this.setRecoveryState("terminal");
3623
+ this.resolvePending();
3305
3624
  return;
3306
3625
  }
3307
3626
  const attempt = this.retryAttempt;
3308
3627
  const delayMs = (this.retry.baseDelayMs ?? 2e3) * 2 ** (attempt - 1);
3309
- this.emit({ type: "retry_scheduled", errorMessage, attempt, maxRetries, delayMs });
3628
+ this.ensurePending();
3629
+ this.setRecoveryState("retry_pending");
3630
+ this.emit({ type: "turn_recovery_pending", recovery: "retry", failure, attempt, maxRetries, delayMs });
3631
+ this.emit({ type: "retry_scheduled", errorMessage: failure.message, attempt, maxRetries, delayMs });
3310
3632
  this.cancelTimerOnly();
3311
3633
  const setTimer = this.retry.setTimeout ?? defaultSetTimeout4;
3312
3634
  this.retryTimer = setTimer(() => {
3313
3635
  this.retryTimer = void 0;
3636
+ this.setRecoveryState("retrying");
3314
3637
  this.emit({ type: "retry_start", attempt, maxRetries });
3638
+ this.emit({ type: "turn_retry_started", attempt, maxRetries, failure });
3315
3639
  this.removeTrailingAssistantError();
3316
- void this.continueTurn().catch(() => {
3640
+ void this.continueTurn().catch((error) => {
3641
+ const errorMessage = error instanceof Error ? error.message : String(error);
3642
+ const terminalFailure = asTerminalFailure(failure, errorMessage);
3643
+ this.emit({
3644
+ type: "retry_exhausted",
3645
+ errorMessage,
3646
+ maxRetries
3647
+ });
3648
+ this.emit({ type: "turn_failed_terminal", failure: terminalFailure, message });
3649
+ this.emit({ type: "turn_settled" });
3650
+ this.retryAttempt = 0;
3651
+ this.setRecoveryState("terminal");
3652
+ this.resolvePending();
3317
3653
  });
3318
3654
  }, delayMs);
3319
3655
  }
3320
3656
  cancel() {
3321
3657
  this.cancelTimerOnly();
3322
3658
  this.retryAttempt = 0;
3659
+ this.resolvePending();
3323
3660
  }
3324
3661
  cancelTimerOnly() {
3325
3662
  if (this.retryTimer === void 0) {
@@ -3329,6 +3666,20 @@ var AgentSessionRetryController = class {
3329
3666
  clearTimer(this.retryTimer);
3330
3667
  this.retryTimer = void 0;
3331
3668
  }
3669
+ ensurePending() {
3670
+ if (this.pendingRetry) {
3671
+ return;
3672
+ }
3673
+ this.pendingRetry = new Promise((resolve2) => {
3674
+ this.resolvePendingRetry = resolve2;
3675
+ });
3676
+ }
3677
+ resolvePending() {
3678
+ const resolve2 = this.resolvePendingRetry;
3679
+ this.pendingRetry = void 0;
3680
+ this.resolvePendingRetry = void 0;
3681
+ resolve2?.();
3682
+ }
3332
3683
  };
3333
3684
 
3334
3685
  // ../../packages/agent-framework/src/session/controller/runtime-guard.ts
@@ -3428,6 +3779,33 @@ var AgentSessionRuntimeGuard = class {
3428
3779
  }
3429
3780
  };
3430
3781
 
3782
+ // ../../packages/agent-framework/src/session/controller/runtime-phase-policy.ts
3783
+ function classifyControllerRuntimeError(params) {
3784
+ const errorMessage = params.error instanceof Error ? params.error.message : String(params.error);
3785
+ return createFailureClassification({ errorMessage, phase: params.phase }, params.retry, params.classifyFailure);
3786
+ }
3787
+ async function runAgentSessionRuntimePhase(params) {
3788
+ try {
3789
+ return await params.action();
3790
+ } catch (error) {
3791
+ const failure = classifyControllerRuntimeError({
3792
+ error,
3793
+ phase: params.phase,
3794
+ retry: params.retry,
3795
+ classifyFailure: params.classifyFailure
3796
+ });
3797
+ if (failure.userAbort) {
3798
+ params.setRecoveryState("cancelled");
3799
+ params.emit({ type: "turn_cancelled", reason: failure.message });
3800
+ } else {
3801
+ params.setRecoveryState("terminal");
3802
+ params.emit({ type: "turn_failed_terminal", failure: asTerminalFailure(failure) });
3803
+ }
3804
+ params.emit({ type: "turn_settled" });
3805
+ throw error;
3806
+ }
3807
+ }
3808
+
3431
3809
  // ../../packages/agent-framework/src/session/controller/session-sync.ts
3432
3810
  function getConversationMessages(messages) {
3433
3811
  return messages.filter(
@@ -3532,6 +3910,161 @@ function reduceAgentSessionLifecycle(state, input) {
3532
3910
  }
3533
3911
  }
3534
3912
 
3913
+ // ../../packages/agent-framework/src/session/controller/turn-lifecycle-policy.ts
3914
+ function isAgentSessionRecoveryActive(state) {
3915
+ return state === "retry_pending" || state === "retrying" || state === "compacting";
3916
+ }
3917
+ function createRuntimeGuardFailure(reason) {
3918
+ return {
3919
+ kind: "runtime_guard",
3920
+ retryable: false,
3921
+ compactable: false,
3922
+ userAbort: false,
3923
+ terminal: true,
3924
+ message: reason
3925
+ };
3926
+ }
3927
+ function classifyControllerTurnFailure(params) {
3928
+ if (!isAssistantFailureMessage(params.message)) {
3929
+ return void 0;
3930
+ }
3931
+ const errorMessage = params.message.errorMessage || (params.message.stopReason === "aborted" ? "Request was aborted" : "assistant response failed");
3932
+ return createFailureClassification(
3933
+ { errorMessage, message: params.message, phase: "stream" },
3934
+ params.retry,
3935
+ params.classifyFailure
3936
+ );
3937
+ }
3938
+ async function handleAgentSessionTurnEndRuntimePolicies(params) {
3939
+ if (params.disposed || params.runtimeGuardTriggered) {
3940
+ return;
3941
+ }
3942
+ const failure = classifyControllerTurnFailure({
3943
+ message: params.message,
3944
+ retry: params.retry,
3945
+ classifyFailure: params.classifyFailure
3946
+ });
3947
+ if (failure?.userAbort) {
3948
+ params.cancelRetry();
3949
+ params.setRecoveryState("cancelled");
3950
+ params.emit({ type: "turn_cancelled", reason: failure.message, message: params.message });
3951
+ params.emit({ type: "turn_settled" });
3952
+ return;
3953
+ }
3954
+ const compactDecision = params.getAutoCompactDecision?.({
3955
+ agent: params.agent,
3956
+ sessionManager: params.sessionManager,
3957
+ cwd: params.cwd,
3958
+ message: params.message,
3959
+ messages: params.agent.state.messages.slice()
3960
+ });
3961
+ if (failure?.compactable) {
3962
+ params.scheduleAutoCompact(compactDecision ?? { reason: "overflow", willRetry: true }, failure);
3963
+ return;
3964
+ }
3965
+ if (failure && compactDecision?.reason === "overflow") {
3966
+ params.scheduleAutoCompact(compactDecision, failure);
3967
+ return;
3968
+ }
3969
+ if (failure?.retryable && compactDecision?.reason === "threshold") {
3970
+ params.scheduleAutoCompact({ reason: "threshold", willRetry: true }, failure);
3971
+ return;
3972
+ }
3973
+ if (failure) {
3974
+ if (failure.retryable) {
3975
+ await params.handleRetryTurnEnd(params.message, failure);
3976
+ return;
3977
+ }
3978
+ await params.handleRetryTurnEnd(params.message, failure);
3979
+ params.setRecoveryState("terminal");
3980
+ params.emit({ type: "turn_failed_terminal", failure: asTerminalFailure(failure), message: params.message });
3981
+ params.emit({ type: "turn_settled" });
3982
+ return;
3983
+ }
3984
+ await params.handleRetryTurnEnd(params.message);
3985
+ if (isAssistantTerminalCompletion(params.message)) {
3986
+ params.emit({ type: "turn_completed", message: params.message, toolResults: params.toolResults });
3987
+ const postTurnCompactDecision = compactDecision ?? params.getAutoCompactDecision?.({
3988
+ agent: params.agent,
3989
+ sessionManager: params.sessionManager,
3990
+ cwd: params.cwd,
3991
+ message: params.message,
3992
+ messages: params.agent.state.messages.slice()
3993
+ });
3994
+ if (postTurnCompactDecision?.reason === "threshold") {
3995
+ params.scheduleAutoCompact(postTurnCompactDecision);
3996
+ return;
3997
+ }
3998
+ params.setRecoveryState("none");
3999
+ params.emit({ type: "turn_settled" });
4000
+ }
4001
+ }
4002
+ async function runAgentSessionControllerAutoCompact(params) {
4003
+ const emitSettled = params.emitSettled ?? true;
4004
+ const runAfterAgentEndHook = params.runAfterAgentEndHook ?? true;
4005
+ const cancelOnAbort = params.cancelOnAbort ?? true;
4006
+ params.setRecoveryState("compacting");
4007
+ params.emit({ type: "turn_compaction_started", reason: params.reason, willRetry: params.willRetry });
4008
+ try {
4009
+ const result = await runAgentSessionCompaction({
4010
+ reason: params.reason,
4011
+ willRetry: params.willRetry,
4012
+ compaction: params.compaction,
4013
+ agent: params.agent,
4014
+ sessionManager: params.sessionManager,
4015
+ cwd: params.cwd,
4016
+ emit: params.emit,
4017
+ removeTrailingAssistantError: params.removeTrailingAssistantError,
4018
+ replaceMessagesAndMarkSynced: params.replaceMessagesAndMarkSynced,
4019
+ markSyncedToAgentState: params.markSyncedToAgentState,
4020
+ continueTurn: params.continueTurn
4021
+ });
4022
+ if (!params.willRetry || result?.aborted) {
4023
+ params.setRecoveryState(result?.aborted && cancelOnAbort ? "cancelled" : "none");
4024
+ if (result?.aborted) {
4025
+ params.cancelRetry();
4026
+ if (cancelOnAbort) {
4027
+ params.emit({ type: "turn_cancelled", reason: "compaction_aborted" });
4028
+ }
4029
+ }
4030
+ if (emitSettled) {
4031
+ params.emit({ type: "turn_settled" });
4032
+ }
4033
+ if (!result?.aborted && runAfterAgentEndHook) {
4034
+ void params.handleAgentEndHook(params.agent.state.messages.slice());
4035
+ }
4036
+ }
4037
+ return result;
4038
+ } catch (error) {
4039
+ const errorMessage = error instanceof Error ? error.message : String(error);
4040
+ if (!params.willRetry && params.reason !== "overflow") {
4041
+ params.setRecoveryState("none");
4042
+ if (emitSettled) {
4043
+ params.emit({ type: "turn_settled" });
4044
+ }
4045
+ if (runAfterAgentEndHook) {
4046
+ void params.handleAgentEndHook(params.agent.state.messages.slice());
4047
+ }
4048
+ throw error;
4049
+ }
4050
+ const failure = asTerminalFailure(params.pendingFailure ?? {
4051
+ kind: "internal_error",
4052
+ retryable: false,
4053
+ compactable: false,
4054
+ userAbort: false,
4055
+ terminal: true,
4056
+ message: errorMessage
4057
+ }, errorMessage);
4058
+ params.cancelRetry();
4059
+ params.setRecoveryState("terminal");
4060
+ params.emit({ type: "turn_failed_terminal", failure });
4061
+ params.emit({ type: "turn_settled" });
4062
+ throw error;
4063
+ } finally {
4064
+ params.clearPendingFailure();
4065
+ }
4066
+ }
4067
+
3535
4068
  // ../../packages/agent-framework/src/session/controller.ts
3536
4069
  var AgentSessionController = class {
3537
4070
  agent;
@@ -3553,6 +4086,9 @@ var AgentSessionController = class {
3553
4086
  prepareTurn;
3554
4087
  afterAgentEnd;
3555
4088
  getAutoCompactDecision;
4089
+ classifyFailure;
4090
+ recoveryState = "none";
4091
+ pendingCompactionFailure;
3556
4092
  lifecycleState = createInitialAgentSessionLifecycleState();
3557
4093
  constructor(options) {
3558
4094
  this.agent = options.agent;
@@ -3563,6 +4099,7 @@ var AgentSessionController = class {
3563
4099
  this.prepareTurn = options.prepareTurn;
3564
4100
  this.afterAgentEnd = options.afterAgentEnd;
3565
4101
  this.getAutoCompactDecision = options.getAutoCompactDecision;
4102
+ this.classifyFailure = options.classifyFailure;
3566
4103
  if (options.onEvent) {
3567
4104
  this.listeners.add(options.onEvent);
3568
4105
  }
@@ -3578,7 +4115,10 @@ var AgentSessionController = class {
3578
4115
  options.retry,
3579
4116
  (event) => this.emit(event),
3580
4117
  () => this.removeTrailingAssistantError(),
3581
- () => this.continue()
4118
+ () => this.continue(),
4119
+ (state) => {
4120
+ this.recoveryState = state;
4121
+ }
3582
4122
  );
3583
4123
  this.autoCompact = new AgentSessionAutoCompactScheduler({
3584
4124
  retry: options.retry,
@@ -3598,6 +4138,7 @@ var AgentSessionController = class {
3598
4138
  emit: (event) => this.emit(event),
3599
4139
  getDisposed: () => this.disposed,
3600
4140
  getRuntimeGuardTriggered: () => this.runtimeGuard.triggered,
4141
+ isBlockedByRuntimePolicy: () => this.isBlockedByRuntimePolicy(),
3601
4142
  hasQueuedMessages: () => this.hasQueuedMessages,
3602
4143
  isStreaming: () => this.isStreaming,
3603
4144
  continueTurn: () => this.continue()
@@ -3613,7 +4154,8 @@ var AgentSessionController = class {
3613
4154
  getDisposed: () => this.disposed,
3614
4155
  getRuntimeGuardTriggered: () => this.runtimeGuard.triggered,
3615
4156
  isStreaming: () => this.isStreaming,
3616
- isBlockedByRuntimePolicy: () => this.retryController.hasPendingRetry || this.autoCompact.hasPendingAutoCompact,
4157
+ isBlockedByRuntimePolicy: () => this.isBlockedByRuntimePolicy(),
4158
+ runPreRequestAutoCompact: () => this.runPreRequestAutoCompactIfNeeded(),
3617
4159
  preparePromptMessages: (messages, promptOptions) => this.preparePromptMessages(messages, promptOptions),
3618
4160
  continueTurn: () => this.continue(),
3619
4161
  syncNewAgentMessages: () => this.syncNewAgentMessages()
@@ -3636,6 +4178,9 @@ var AgentSessionController = class {
3636
4178
  get queuedInputsSnapshot() {
3637
4179
  return this.queue.snapshot;
3638
4180
  }
4181
+ get turnRecoveryState() {
4182
+ return this.recoveryState;
4183
+ }
3639
4184
  get activeSessionMetadata() {
3640
4185
  const metadata = this.sessionManager.getActiveSession()?.metadata;
3641
4186
  return metadata ? { ...metadata } : void 0;
@@ -3644,10 +4189,7 @@ var AgentSessionController = class {
3644
4189
  return this.lastSyncedMessageCount;
3645
4190
  }
3646
4191
  get runtimeContext() {
3647
- return {
3648
- systemPrompt: this.agent.state.systemPrompt,
3649
- tools: this.agent.state.tools.slice()
3650
- };
4192
+ return { systemPrompt: this.agent.state.systemPrompt, tools: this.agent.state.tools.slice() };
3651
4193
  }
3652
4194
  getRuntimeSnapshot() {
3653
4195
  return {
@@ -3694,6 +4236,17 @@ var AgentSessionController = class {
3694
4236
  }
3695
4237
  async prompt(input, options) {
3696
4238
  const messages = normalizePromptInput(input);
4239
+ if (this.isRecoveryActive()) {
4240
+ this.queue.enqueue(messages, options?.streamingBehavior ?? "followUp", {
4241
+ ...options,
4242
+ streamingBehavior: options?.streamingBehavior ?? "followUp"
4243
+ });
4244
+ this.recordLifecycle({ type: "queued_input_added" });
4245
+ return;
4246
+ }
4247
+ if (this.recoveryState === "terminal" || this.recoveryState === "cancelled") {
4248
+ this.recoveryState = "none";
4249
+ }
3697
4250
  if (options?.streamingBehavior) {
3698
4251
  this.queue.enqueue(messages, options.streamingBehavior, options);
3699
4252
  this.recordLifecycle({ type: "queued_input_added" });
@@ -3705,7 +4258,8 @@ var AgentSessionController = class {
3705
4258
  if (this.isStreaming) {
3706
4259
  throw new Error("Agent is already processing. Specify streamingBehavior ('steer' or 'followUp') to queue the message.");
3707
4260
  }
3708
- const prepared = await this.preparePromptMessages(messages, options);
4261
+ await this.runPreRequestAutoCompactIfNeeded();
4262
+ const prepared = await this.runRuntimePhase("prepare", () => this.preparePromptMessages(messages, options));
3709
4263
  if (prepared.handled) {
3710
4264
  return;
3711
4265
  }
@@ -3713,41 +4267,69 @@ var AgentSessionController = class {
3713
4267
  if (prepared.systemPrompt !== void 0) {
3714
4268
  this.agent.setSystemPrompt(prepared.systemPrompt);
3715
4269
  }
3716
- await this.agent.prompt(prepared.messages);
3717
- await this.syncNewAgentMessages();
4270
+ await this.runRuntimePhase("request", () => this.agent.prompt(prepared.messages));
4271
+ await this.runRuntimePhase("post_turn", () => this.syncNewAgentMessages());
3718
4272
  }
3719
4273
  async continue() {
3720
4274
  if (this.isStreaming) {
3721
4275
  throw new Error("Agent is already processing. Wait for completion before continuing.");
3722
4276
  }
4277
+ await this.runPreRequestAutoCompactIfNeeded();
3723
4278
  this.recordLifecycle({ type: "prompt_started" });
3724
- await this.agent.continue();
3725
- await this.syncNewAgentMessages();
4279
+ await this.runRuntimePhase("request", () => this.agent.continue());
4280
+ await this.runRuntimePhase("post_turn", () => this.syncNewAgentMessages());
3726
4281
  }
3727
4282
  abort() {
3728
4283
  this.recordLifecycle({ type: "abort" });
4284
+ const cancelPendingRecovery = this.isRecoveryActive() && !this.isStreaming;
3729
4285
  this.retryController.cancel();
3730
4286
  this.autoCompact.cancel();
3731
4287
  this.queueDispatcher.cancel();
3732
4288
  this.autoContinue.cancel();
4289
+ if (cancelPendingRecovery) {
4290
+ this.recoveryState = "cancelled";
4291
+ this.emit({ type: "turn_cancelled", reason: "user_abort" });
4292
+ this.emit({ type: "turn_settled" });
4293
+ }
3733
4294
  this.agent.abort();
3734
4295
  }
3735
4296
  waitForIdle() {
3736
4297
  return this.agent.waitForIdle();
3737
4298
  }
3738
- async runAutoCompact(reason, willRetry = false) {
3739
- return runAgentSessionCompaction({
4299
+ async waitForSettled() {
4300
+ while (true) {
4301
+ await this.agent.waitForIdle();
4302
+ if (!this.hasPendingRuntimePolicy()) {
4303
+ return;
4304
+ }
4305
+ await Promise.all([this.retryController.waitForRetry(), this.autoCompact.waitForAutoCompact(), this.autoContinue.waitForAutoContinue(), this.queueDispatcher.waitForDispatch()]);
4306
+ }
4307
+ }
4308
+ async runAutoCompact(reason, willRetry = false, options) {
4309
+ return runAgentSessionControllerAutoCompact({
3740
4310
  reason,
3741
4311
  willRetry,
3742
4312
  compaction: this.compaction,
3743
4313
  agent: this.agent,
3744
4314
  sessionManager: this.sessionManager,
3745
4315
  cwd: this.cwd,
4316
+ pendingFailure: this.pendingCompactionFailure,
3746
4317
  emit: (event) => this.emit(event),
4318
+ setRecoveryState: (state) => {
4319
+ this.recoveryState = state;
4320
+ },
4321
+ cancelRetry: () => this.retryController.cancel(),
4322
+ clearPendingFailure: () => {
4323
+ this.pendingCompactionFailure = void 0;
4324
+ },
3747
4325
  removeTrailingAssistantError: () => this.removeTrailingAssistantError(),
3748
- replaceMessagesAndMarkSynced: (messages, options) => this.replaceMessagesAndMarkSynced(messages, options),
4326
+ replaceMessagesAndMarkSynced: (messages, options2) => this.replaceMessagesAndMarkSynced(messages, options2),
3749
4327
  markSyncedToAgentState: () => this.markSyncedToAgentState(),
3750
- continueTurn: () => this.continue()
4328
+ continueTurn: () => this.continue(),
4329
+ handleAgentEndHook: (messages) => this.handleAgentEndHook(messages),
4330
+ emitSettled: options?.emitSettled,
4331
+ runAfterAgentEndHook: options?.runAfterAgentEndHook,
4332
+ cancelOnAbort: options?.cancelOnAbort
3751
4333
  });
3752
4334
  }
3753
4335
  dispose() {
@@ -3769,9 +4351,23 @@ var AgentSessionController = class {
3769
4351
  this.queue.replaceText(search, replacement);
3770
4352
  }
3771
4353
  requestQueueDispatch() {
3772
- if (this.queue.length > 0) {
3773
- this.queueDispatcher.schedule();
3774
- }
4354
+ if (this.queue.length > 0) this.queueDispatcher.schedule();
4355
+ }
4356
+ async runPreRequestAutoCompactIfNeeded() {
4357
+ return runPreRequestAutoCompact({
4358
+ disposed: this.disposed,
4359
+ runtimeGuardTriggered: this.runtimeGuard.triggered,
4360
+ isStreaming: this.isStreaming,
4361
+ recoveryActive: this.isRecoveryActive(),
4362
+ agent: this.agent,
4363
+ sessionManager: this.sessionManager,
4364
+ cwd: this.cwd,
4365
+ getAutoCompactDecision: this.getAutoCompactDecision,
4366
+ runAutoCompact: () => this.runAutoCompact("threshold", false, { emitSettled: false, runAfterAgentEndHook: false, cancelOnAbort: false }).then(() => void 0),
4367
+ setRecoveryState: (state) => {
4368
+ this.recoveryState = state;
4369
+ }
4370
+ });
3775
4371
  }
3776
4372
  handleAgentEvent(event) {
3777
4373
  this.emit({ type: "agent_event", event });
@@ -3792,7 +4388,7 @@ var AgentSessionController = class {
3792
4388
  if (event.type === "turn_end") {
3793
4389
  void this.syncNewAgentMessages();
3794
4390
  this.recordLifecycle({ type: "turn_end_success" });
3795
- void this.handleTurnEndRuntimePolicies(event.message);
4391
+ void this.handleTurnEndRuntimePolicies(event.message, event.toolResults);
3796
4392
  }
3797
4393
  if (event.type === "agent_end") {
3798
4394
  this.recordLifecycle({ type: "agent_end" });
@@ -3829,25 +4425,29 @@ var AgentSessionController = class {
3829
4425
  replaceMessagesAndMarkSynced: (messages, options) => this.replaceMessagesAndMarkSynced(messages, options)
3830
4426
  });
3831
4427
  }
3832
- async handleTurnEndRuntimePolicies(message) {
3833
- if (this.disposed || this.runtimeGuard.triggered) {
3834
- return;
3835
- }
3836
- const compactDecision = this.getAutoCompactDecision?.({
4428
+ async handleTurnEndRuntimePolicies(message, toolResults = []) {
4429
+ return handleAgentSessionTurnEndRuntimePolicies({
4430
+ disposed: this.disposed,
4431
+ runtimeGuardTriggered: this.runtimeGuard.triggered,
4432
+ message,
4433
+ toolResults,
3837
4434
  agent: this.agent,
3838
4435
  sessionManager: this.sessionManager,
3839
4436
  cwd: this.cwd,
3840
- message,
3841
- messages: this.agent.state.messages.slice()
4437
+ getAutoCompactDecision: this.getAutoCompactDecision,
4438
+ classifyFailure: this.classifyFailure,
4439
+ retry: this.retryController.policy,
4440
+ emit: (event) => this.emit(event),
4441
+ setRecoveryState: (state) => {
4442
+ this.recoveryState = state;
4443
+ },
4444
+ handleRetryTurnEnd: (turnMessage, failure) => this.retryController.handleTurnEnd(turnMessage, failure),
4445
+ cancelRetry: () => this.retryController.cancel(),
4446
+ scheduleAutoCompact: (decision, failure) => this.scheduleAutoCompact(decision, failure)
3842
4447
  });
3843
- if (compactDecision) {
3844
- this.scheduleAutoCompact(compactDecision);
3845
- return;
3846
- }
3847
- await this.retryController.handleTurnEnd(message);
3848
4448
  }
3849
4449
  async handleAgentEndHook(messages) {
3850
- if (this.disposed || this.runtimeGuard.triggered) {
4450
+ if (this.disposed || this.runtimeGuard.triggered || this.isBlockedByRuntimePolicy()) {
3851
4451
  return;
3852
4452
  }
3853
4453
  if (this.afterAgentEnd) {
@@ -3882,8 +4482,36 @@ var AgentSessionController = class {
3882
4482
  recordToolExecutionEndForGuard(event) {
3883
4483
  this.runtimeGuard.recordToolExecutionEnd(event);
3884
4484
  }
3885
- scheduleAutoCompact(decision) {
4485
+ async runRuntimePhase(phase, action) {
4486
+ return runAgentSessionRuntimePhase({
4487
+ phase,
4488
+ action,
4489
+ retry: this.retryController.policy,
4490
+ classifyFailure: this.classifyFailure,
4491
+ emit: (event) => this.emit(event),
4492
+ setRecoveryState: (state) => {
4493
+ this.recoveryState = state;
4494
+ }
4495
+ });
4496
+ }
4497
+ isRecoveryActive() {
4498
+ return isAgentSessionRecoveryActive(this.recoveryState);
4499
+ }
4500
+ isBlockedByRuntimePolicy() {
4501
+ return this.recoveryState !== "none" || this.retryController.hasPendingRetry || this.autoCompact.hasPendingAutoCompact;
4502
+ }
4503
+ hasPendingRuntimePolicy() {
4504
+ return this.retryController.hasPendingRetry || this.autoCompact.hasPendingAutoCompact || this.autoContinue.hasPendingAutoContinue || this.queueDispatcher.hasPendingDispatch;
4505
+ }
4506
+ scheduleAutoCompact(decision, failure) {
3886
4507
  this.recordLifecycle({ type: "auto_compact_scheduled" });
4508
+ if (failure) {
4509
+ this.pendingCompactionFailure = failure;
4510
+ this.recoveryState = "compacting";
4511
+ this.emit({ type: "turn_recovery_pending", recovery: "compaction", failure });
4512
+ } else if (decision.reason === "threshold") {
4513
+ this.recoveryState = "compacting";
4514
+ }
3887
4515
  this.autoCompact.schedule(decision);
3888
4516
  }
3889
4517
  triggerRuntimeGuard(toolName, repeatCount, reason) {
@@ -3895,7 +4523,13 @@ var AgentSessionController = class {
3895
4523
  this.autoCompact.cancel();
3896
4524
  this.queueDispatcher.cancel();
3897
4525
  this.autoContinue.cancel();
4526
+ this.recoveryState = "terminal";
3898
4527
  this.emit({ type: "runtime_guard_triggered", toolName, repeatCount, reason });
4528
+ this.emit({
4529
+ type: "turn_failed_terminal",
4530
+ failure: createRuntimeGuardFailure(reason)
4531
+ });
4532
+ this.emit({ type: "turn_settled" });
3899
4533
  this.agent.abort();
3900
4534
  }
3901
4535
  emit(event) {
@@ -8972,7 +9606,10 @@ export {
8972
9606
  ManageTodoListParamsSchema,
8973
9607
  executeManageTodoList,
8974
9608
  maybeAdvanceTodoExecutionState,
9609
+ estimateTokens,
8975
9610
  calculateTotalTokens,
9611
+ estimateContextTokens,
9612
+ getAutoCompactTokenLimit,
8976
9613
  findFirstKeptEntryId,
8977
9614
  createCompactionSummary,
8978
9615
  createSessionManager,