@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/cli.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  getSettingsPath,
15
15
  getThemesDir,
16
16
  migrateConfigFromAgentDir
17
- } from "./chunks/chunk-55RUSXEA.js";
17
+ } from "./chunks/chunk-4MIOCDBV.js";
18
18
  import {
19
19
  AGENTS_CONTEXT_FILE_NAME,
20
20
  AgentSessionController,
@@ -37,11 +37,14 @@ import {
37
37
  createSharedFileSystemTools,
38
38
  createSharedWebSearchTool,
39
39
  createTodoWidgetView,
40
+ estimateContextTokens,
41
+ estimateTokens,
40
42
  evaluateTodoClosureAfterAbort,
41
43
  evaluateTodoClosureAfterCompletedTurn,
42
44
  evaluateTodoClosureAfterFailedTurn,
43
45
  findFirstKeptEntryId,
44
46
  formatSkillSummariesForPrompt,
47
+ getAutoCompactTokenLimit,
45
48
  hasInteractionHandler,
46
49
  maybeAdvanceTodoExecutionState,
47
50
  modalityForReadFileUnderstandingKind,
@@ -52,7 +55,7 @@ import {
52
55
  selectToolsForRuntimePolicy,
53
56
  shouldPreserveExecutionStateForUserText,
54
57
  supersedeExecutionState
55
- } from "./chunks/chunk-R5LYKDKA.js";
58
+ } from "./chunks/chunk-B4VUX6SD.js";
56
59
  import "./chunks/chunk-VE2HDCNB.js";
57
60
  import {
58
61
  Deref,
@@ -59547,6 +59550,26 @@ ${theme.fg("muted", "Receiving edit parameters...")}`;
59547
59550
 
59548
59551
  ${theme.fg("error", errorText)}`;
59549
59552
  }
59553
+ } else if (this.result?.details?.diff && rawPath) {
59554
+ const diff = this.result.details.diff;
59555
+ const firstChangedLine = this.result.details.firstChangedLine;
59556
+ if (firstChangedLine) {
59557
+ text = `${theme.fg("toolTitle", theme.bold("edit_file"))} ${theme.fg("accent", path27 || rawPath)}${theme.fg("warning", `:${firstChangedLine}`)}`;
59558
+ }
59559
+ const lines = diff.split("\n");
59560
+ const maxLines = this.expanded ? lines.length : 15;
59561
+ const displayLines = lines.slice(0, maxLines);
59562
+ const remaining = lines.length - maxLines;
59563
+ text += "\n\n" + displayLines.map((line) => {
59564
+ if (line.startsWith("+")) return theme.fg("success", line);
59565
+ if (line.startsWith("-")) return theme.fg("error", line);
59566
+ if (line.startsWith("@@")) return theme.fg("warning", line);
59567
+ return theme.fg("toolOutput", line);
59568
+ }).join("\n");
59569
+ if (remaining > 0) {
59570
+ text += theme.fg("muted", `
59571
+ ... (${remaining} more lines, press Ctrl+O to expand)`);
59572
+ }
59550
59573
  } else if (hasOldText && hasNewText && rawPath) {
59551
59574
  const diff = generateEditDiff(oldText, newText, rawPath);
59552
59575
  const firstChangedLine = findFirstChangedLine(oldText, newText);
@@ -61388,7 +61411,7 @@ function bindInteractiveRuntimeEvents(host) {
61388
61411
  lastStopReason: host.agent.state.messages.at(-1)?.role === "assistant" ? host.agent.state.messages.at(-1).stopReason : void 0
61389
61412
  });
61390
61413
  const lastMessage = host.agent.state.messages[host.agent.state.messages.length - 1];
61391
- if (lastMessage?.role === "assistant" && lastMessage?.stopReason === "error") {
61414
+ if (!hasControllerTurnAuthority(host) && lastMessage?.role === "assistant" && lastMessage?.stopReason === "error") {
61392
61415
  host.sessionTrace.abortTurn(String(lastMessage?.errorMessage || "agent_end"));
61393
61416
  }
61394
61417
  host.isProcessing = false;
@@ -61433,6 +61456,12 @@ function handleSemanticAgentEvent(host, event) {
61433
61456
  break;
61434
61457
  }
61435
61458
  case "turn_completed": {
61459
+ if (hasControllerTurnAuthority(host)) {
61460
+ host.logDebug("semantic:turn_completed_ignored_controller_authority", {
61461
+ turnIndex: host._semanticTurnIndex
61462
+ });
61463
+ break;
61464
+ }
61436
61465
  host.clearContinuationTimeout("turn_completed");
61437
61466
  host.emitExtensionEvent({
61438
61467
  type: "turn:completed",
@@ -61460,6 +61489,13 @@ function handleSemanticAgentEvent(host, event) {
61460
61489
  break;
61461
61490
  }
61462
61491
  case "turn_failed": {
61492
+ if (hasControllerTurnAuthority(host)) {
61493
+ host.logDebug("semantic:turn_failed_ignored_controller_authority", {
61494
+ turnIndex: host._semanticTurnIndex,
61495
+ recoveryState: host.agentSessionController?.turnRecoveryState
61496
+ });
61497
+ break;
61498
+ }
61463
61499
  host.clearContinuationTimeout("turn_failed");
61464
61500
  host.emitExtensionEvent({
61465
61501
  type: "turn:failed",
@@ -61488,6 +61524,9 @@ function handleSemanticAgentEvent(host, event) {
61488
61524
  }
61489
61525
  case "status_snapshot_changed": {
61490
61526
  const snapshot = event.snapshot;
61527
+ if (isControllerRecovering(host)) {
61528
+ break;
61529
+ }
61491
61530
  if (snapshot.phase === "idle" && !snapshot.hasPendingToolContinuation) {
61492
61531
  host.clearContinuationTimeout("status_idle");
61493
61532
  const outcome = host.executionState.markAwaitingContinuationStalled();
@@ -61509,6 +61548,13 @@ function handleSemanticAgentEvent(host, event) {
61509
61548
  }
61510
61549
  }
61511
61550
  }
61551
+ function hasControllerTurnAuthority(host) {
61552
+ return host.agentSessionController?.turnRecoveryState !== void 0;
61553
+ }
61554
+ function isControllerRecovering(host) {
61555
+ const state = host.agentSessionController?.turnRecoveryState;
61556
+ return state === "retry_pending" || state === "retrying" || state === "compacting";
61557
+ }
61512
61558
  function handleMessageStart(host, event) {
61513
61559
  if (event.message?.role === "assistant") {
61514
61560
  host.clearContinuationTimeout("assistant_message_start");
@@ -64044,9 +64090,110 @@ function buildCliSystemPrompt(options) {
64044
64090
  ].filter(Boolean).join("\n\n");
64045
64091
  }
64046
64092
 
64093
+ // src/runtime/cli-auto-compaction.ts
64094
+ function calculateCliContextUsage(params) {
64095
+ const messages = stripStaleUsageBeforeCompaction(
64096
+ params.messages,
64097
+ params.latestCompactionTimestamp ?? null
64098
+ );
64099
+ const estimate = estimateContextTokens(messages);
64100
+ const usageMessage = estimate.lastUsageIndex !== null ? messages[estimate.lastUsageIndex] : void 0;
64101
+ const usage = usageMessage?.role === "assistant" ? usageMessage.usage : void 0;
64102
+ return {
64103
+ tokens: estimate.tokens,
64104
+ input: usage?.input ?? 0,
64105
+ output: usage?.output ?? 0,
64106
+ cacheRead: usage?.cacheRead ?? 0,
64107
+ cacheWrite: usage?.cacheWrite ?? 0,
64108
+ cost: usage?.cost.total ?? 0
64109
+ };
64110
+ }
64111
+ function createCliAutoCompactDecision(params) {
64112
+ const errorMessage3 = params.message?.role === "assistant" ? params.message.errorMessage : void 0;
64113
+ if (errorMessage3 && params.isContextOverflowError(errorMessage3)) {
64114
+ return { reason: "overflow", willRetry: true };
64115
+ }
64116
+ if (!params.autoCompactEnabled || params.contextWindow <= 0) {
64117
+ return void 0;
64118
+ }
64119
+ const usage = calculateCliContextUsage({
64120
+ messages: params.messages,
64121
+ latestCompactionTimestamp: params.latestCompactionTimestamp
64122
+ });
64123
+ if (!usage.tokens) {
64124
+ return void 0;
64125
+ }
64126
+ const tokenLimit = getAutoCompactTokenLimit(params.contextWindow, params.reserveTokens);
64127
+ if (tokenLimit === void 0) {
64128
+ return void 0;
64129
+ }
64130
+ return usage.tokens > tokenLimit ? { reason: "threshold", willRetry: false } : void 0;
64131
+ }
64132
+ function shouldCliAutoCompactTokens(params) {
64133
+ if (!params.tokens || params.contextWindow <= 0) {
64134
+ return false;
64135
+ }
64136
+ const tokenLimit = getAutoCompactTokenLimit(params.contextWindow, params.reserveTokens);
64137
+ return tokenLimit !== void 0 && params.tokens > tokenLimit;
64138
+ }
64139
+ async function createCliAutoCompactionResult(params) {
64140
+ if (params.context.signal?.aborted) {
64141
+ return { aborted: true };
64142
+ }
64143
+ const activeEntryId = params.context.sessionManager.getActiveEntryId();
64144
+ if (!activeEntryId) {
64145
+ return { aborted: true };
64146
+ }
64147
+ const pathEntries = params.context.sessionManager.getPathToEntry(activeEntryId);
64148
+ const firstKeptEntryId = findFirstKeptEntryId(pathEntries);
64149
+ if (!firstKeptEntryId) {
64150
+ return { aborted: true };
64151
+ }
64152
+ const compactOptions = {
64153
+ model: params.model,
64154
+ apiKey: params.apiKey,
64155
+ systemPrompt: params.systemPrompt,
64156
+ signal: params.context.signal
64157
+ };
64158
+ const summary = await createCompactionSummary(params.context.messages, compactOptions);
64159
+ if (params.context.signal?.aborted) {
64160
+ return { aborted: true };
64161
+ }
64162
+ return {
64163
+ summary,
64164
+ firstKeptEntryId,
64165
+ tokensBefore: calculateTotalTokens(params.context.messages)
64166
+ };
64167
+ }
64168
+ function stripStaleUsageBeforeCompaction(messages, latestCompactionTimestamp) {
64169
+ if (latestCompactionTimestamp === null) {
64170
+ return messages;
64171
+ }
64172
+ return messages.map((message) => {
64173
+ if (message.role !== "assistant" || message.timestamp > latestCompactionTimestamp) {
64174
+ return message;
64175
+ }
64176
+ return {
64177
+ ...message,
64178
+ usage: {
64179
+ input: 0,
64180
+ output: 0,
64181
+ cacheRead: 0,
64182
+ cacheWrite: 0,
64183
+ totalTokens: 0,
64184
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 }
64185
+ }
64186
+ };
64187
+ });
64188
+ }
64189
+
64047
64190
  // src/runtime/retry-policy.ts
64048
- var TRANSIENT_MODEL_ERROR_PATTERN = /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated|retry delay|sse stream aborted|sse stream closed before end|sse stream ended before response completed/i;
64049
- var USER_ABORT_PATTERN = /request was aborted/i;
64191
+ var TRANSIENT_NETWORK_ERROR_PATTERN = /sse stream aborted|sse stream closed before end|sse stream ended before response completed|(?:read\s+)?ECONNRESET|ETIMEDOUT|EAI_AGAIN|ENOTFOUND|ECONNREFUSED|socket hang up|request timeout|client network socket disconnected before secure TLS connection was established|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers|terminated/i;
64192
+ var PROVIDER_TRANSIENT_ERROR_PATTERN = /overloaded|rate.?limit|too many requests|\b429\b|\b500\b|\b502\b|\b503\b|\b504\b|service.?unavailable|server error|internal error|retry delay/i;
64193
+ var USER_ABORT_PATTERN = /request was aborted|local abort signal|user abort|user_interrupt|interrupted by user|esc pressed|ctrl\+c/i;
64194
+ var AUTH_CONFIG_PATTERN = /missing api key|invalid api key|api key.*invalid|unauthorized|\b401\b|\b403\b|forbidden|authentication failed|no configured model|no models? are configured/i;
64195
+ var INVALID_FILE_REFERENCE_PATTERN = /not the owner of this file|owner of this file|invalid_authentication_error/i;
64196
+ var INVALID_REQUEST_PATTERN = /bad request|\b400\b|invalid request|invalid schema|schema validation|invalid parameter|unsupported parameter|malformed/i;
64050
64197
  var CLI_RETRY_MAX_RETRIES = 3;
64051
64198
  var CLI_RETRY_BASE_DELAY_MS = 2e3;
64052
64199
  function isCliContextOverflowError(errorMessage3) {
@@ -64054,14 +64201,56 @@ function isCliContextOverflowError(errorMessage3) {
64054
64201
  errorMessage3
64055
64202
  );
64056
64203
  }
64057
- function isCliRetryableError(errorMessage3) {
64204
+ function createClassification(kind, message, overrides) {
64205
+ return {
64206
+ kind,
64207
+ message,
64208
+ retryable: false,
64209
+ compactable: false,
64210
+ userAbort: false,
64211
+ terminal: true,
64212
+ ...overrides
64213
+ };
64214
+ }
64215
+ function classifyCliFailure(context) {
64216
+ const errorMessage3 = typeof context === "string" ? context : context.errorMessage;
64058
64217
  if (isCliContextOverflowError(errorMessage3)) {
64059
- return false;
64218
+ return createClassification("context_overflow", errorMessage3, {
64219
+ compactable: true,
64220
+ terminal: false
64221
+ });
64060
64222
  }
64061
64223
  if (USER_ABORT_PATTERN.test(errorMessage3)) {
64062
- return false;
64224
+ return createClassification("user_abort", errorMessage3, {
64225
+ userAbort: true,
64226
+ terminal: false
64227
+ });
64228
+ }
64229
+ if (INVALID_FILE_REFERENCE_PATTERN.test(errorMessage3)) {
64230
+ return createClassification("invalid_file_reference", errorMessage3);
64231
+ }
64232
+ if (AUTH_CONFIG_PATTERN.test(errorMessage3)) {
64233
+ return createClassification("auth_config", errorMessage3);
64063
64234
  }
64064
- return TRANSIENT_MODEL_ERROR_PATTERN.test(errorMessage3);
64235
+ if (TRANSIENT_NETWORK_ERROR_PATTERN.test(errorMessage3)) {
64236
+ return createClassification("transient_network", errorMessage3, {
64237
+ retryable: true,
64238
+ terminal: false
64239
+ });
64240
+ }
64241
+ if (PROVIDER_TRANSIENT_ERROR_PATTERN.test(errorMessage3)) {
64242
+ return createClassification("provider_transient", errorMessage3, {
64243
+ retryable: true,
64244
+ terminal: false
64245
+ });
64246
+ }
64247
+ if (INVALID_REQUEST_PATTERN.test(errorMessage3)) {
64248
+ return createClassification("invalid_request", errorMessage3);
64249
+ }
64250
+ return createClassification("unknown", errorMessage3);
64251
+ }
64252
+ function isCliRetryableError(errorMessage3) {
64253
+ return classifyCliFailure(errorMessage3).retryable;
64065
64254
  }
64066
64255
  function getCliRetryBaseDelayMs() {
64067
64256
  const override = process.env.PIE_TEST_CLI_RETRY_BASE_DELAY_MS;
@@ -64078,78 +64267,13 @@ function calculateContextUsage(host) {
64078
64267
  if (messages.length === 0) {
64079
64268
  return { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
64080
64269
  }
64081
- let lastAssistantIndex = -1;
64082
- let lastUsage = null;
64083
- const latestCompactionTimestamp = host.sessionManager.getLatestCompactionTimestamp();
64084
- for (let i = messages.length - 1; i >= 0; i--) {
64085
- const msg = messages[i];
64086
- if (msg.role === "assistant") {
64087
- const assistantMsg = msg;
64088
- if (latestCompactionTimestamp !== null && assistantMsg.timestamp <= latestCompactionTimestamp) {
64089
- continue;
64090
- }
64091
- if (assistantMsg.usage && assistantMsg.stopReason !== "aborted" && assistantMsg.stopReason !== "error") {
64092
- lastAssistantIndex = i;
64093
- lastUsage = assistantMsg.usage;
64094
- break;
64095
- }
64096
- }
64097
- }
64098
- if (!lastUsage || lastAssistantIndex < 0) {
64099
- const estimated = messages.reduce((sum, msg) => sum + estimateMessageTokens(msg), 0);
64100
- return { tokens: estimated, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
64101
- }
64102
- const usageTokens = lastUsage.totalTokens || lastUsage.input + lastUsage.output + lastUsage.cacheRead + lastUsage.cacheWrite;
64103
- if (usageTokens <= 0) {
64104
- const estimated = messages.reduce((sum, msg) => sum + estimateMessageTokens(msg), 0);
64105
- return {
64106
- tokens: estimated,
64107
- input: lastUsage.input,
64108
- output: lastUsage.output,
64109
- cacheRead: lastUsage.cacheRead,
64110
- cacheWrite: lastUsage.cacheWrite,
64111
- cost: lastUsage.cost.total
64112
- };
64113
- }
64114
- let trailingTokens = 0;
64115
- for (let i = lastAssistantIndex + 1; i < messages.length; i++) {
64116
- trailingTokens += estimateMessageTokens(messages[i]);
64117
- }
64118
- return {
64119
- tokens: usageTokens + trailingTokens,
64120
- input: lastUsage.input,
64121
- output: lastUsage.output,
64122
- cacheRead: lastUsage.cacheRead,
64123
- cacheWrite: lastUsage.cacheWrite,
64124
- cost: lastUsage.cost.total
64125
- };
64270
+ return calculateCliContextUsage({
64271
+ messages,
64272
+ latestCompactionTimestamp: host.sessionManager.getLatestCompactionTimestamp()
64273
+ });
64126
64274
  }
64127
64275
  function estimateMessageTokens(msg) {
64128
- let chars = 0;
64129
- if (msg.role === "user") {
64130
- const content = msg.content;
64131
- if (typeof content === "string") {
64132
- chars = content.length;
64133
- } else if (Array.isArray(content)) {
64134
- for (const block of content) {
64135
- if (block.type === "text" && block.text) {
64136
- chars += block.text.length;
64137
- }
64138
- }
64139
- }
64140
- } else if (msg.role === "assistant") {
64141
- const assistant = msg;
64142
- for (const block of assistant.content) {
64143
- if (block.type === "text") {
64144
- chars += block.text.length;
64145
- } else if (block.type === "thinking") {
64146
- chars += block.thinking.length;
64147
- } else if (block.type === "toolCall") {
64148
- chars += block.name.length + JSON.stringify(block.arguments).length;
64149
- }
64150
- }
64151
- }
64152
- return Math.ceil(chars / 4);
64276
+ return estimateTokens(msg);
64153
64277
  }
64154
64278
  function buildSystemPrompt(host) {
64155
64279
  const tools = (host.agent?.state?.tools ?? host.options.tools ?? []).filter((tool) => typeof tool.name === "string").map((tool) => ({
@@ -64299,9 +64423,8 @@ function isContextOverflowError(errorMessage3) {
64299
64423
  }
64300
64424
  function shouldAutoCompact(host, contextTokens, contextWindow) {
64301
64425
  if (!host.options.settingsManager.getAutoCompactEnabled()) return false;
64302
- if (!contextTokens || contextWindow <= 0) return false;
64303
64426
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
64304
- return contextTokens > contextWindow - reserveTokens;
64427
+ return shouldCliAutoCompactTokens({ tokens: contextTokens, contextWindow, reserveTokens });
64305
64428
  }
64306
64429
  function isRetryableError(errorMessage3) {
64307
64430
  return isCliRetryableError(errorMessage3);
@@ -64746,28 +64869,6 @@ async function prepareAgentTurn(host, context) {
64746
64869
  messages: result.messages?.length ? [...context.messages, ...result.messages] : context.messages
64747
64870
  };
64748
64871
  }
64749
- async function createAutoCompactionResult(host, messages) {
64750
- const activeEntryId = host.sessionManager.getActiveEntryId();
64751
- if (!activeEntryId) {
64752
- return { aborted: true };
64753
- }
64754
- const pathEntries = host.sessionManager.getPathToEntry(activeEntryId);
64755
- const firstKeptEntryId = findFirstKeptEntryId(pathEntries);
64756
- if (!firstKeptEntryId) {
64757
- return { aborted: true };
64758
- }
64759
- const compactOptions = {
64760
- model: host.options.model,
64761
- apiKey: host.options.apiKey,
64762
- systemPrompt: host.agent.state.systemPrompt
64763
- };
64764
- const summary = await createCompactionSummary(messages, compactOptions);
64765
- return {
64766
- summary,
64767
- firstKeptEntryId,
64768
- tokensBefore: calculateTotalTokens(messages)
64769
- };
64770
- }
64771
64872
  function createInteractiveAgentSessionController(host) {
64772
64873
  return new AgentSessionController({
64773
64874
  agent: host.agent,
@@ -64787,14 +64888,16 @@ function createInteractiveAgentSessionController(host) {
64787
64888
  }
64788
64889
  return { allowed: true };
64789
64890
  },
64790
- getAutoCompactDecision: ({ message }) => {
64891
+ getAutoCompactDecision: ({ message, messages }) => {
64791
64892
  if (message?.role === "assistant" && message.stopReason === "error" && message.errorMessage) {
64792
64893
  if (host._isContextOverflowError(message.errorMessage)) {
64793
64894
  return { reason: "overflow", willRetry: true };
64794
64895
  }
64795
- return void 0;
64796
64896
  }
64797
- const contextUsage = host.calculateContextUsage();
64897
+ const contextUsage = calculateCliContextUsage({
64898
+ messages,
64899
+ latestCompactionTimestamp: host.sessionManager.getLatestCompactionTimestamp()
64900
+ });
64798
64901
  const contextWindow = host.options.model?.contextWindow ?? 0;
64799
64902
  if (host._shouldAutoCompact(contextUsage.tokens, contextWindow)) {
64800
64903
  return { reason: "threshold", willRetry: false };
@@ -64802,13 +64905,19 @@ function createInteractiveAgentSessionController(host) {
64802
64905
  return void 0;
64803
64906
  },
64804
64907
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
64908
+ classifyFailure: (context) => classifyCliFailure(context),
64805
64909
  retry: {
64806
64910
  isRetryableError: (errorMessage3) => !host._isContextOverflowError(errorMessage3) && isCliRetryableError(errorMessage3),
64807
64911
  maxRetries: CLI_RETRY_MAX_RETRIES,
64808
64912
  baseDelayMs: getCliRetryBaseDelayMs()
64809
64913
  },
64810
64914
  compaction: {
64811
- compact: async ({ messages }) => createAutoCompactionResult(host, messages)
64915
+ compact: async (context) => createCliAutoCompactionResult({
64916
+ context,
64917
+ model: host.options.model,
64918
+ apiKey: host.options.apiKey,
64919
+ systemPrompt: host.agent.state.systemPrompt
64920
+ })
64812
64921
  },
64813
64922
  onEvent: (event) => {
64814
64923
  host.sessionTrace.noteRuntimeEvent(event.type, event);
@@ -64831,11 +64940,15 @@ function createInteractiveAgentSessionController(host) {
64831
64940
  reason: event.reason,
64832
64941
  queueLength: host.agentSessionController?.pendingMessageCount ?? 0
64833
64942
  });
64834
- } else if (event.type === "retry_scheduled") {
64835
- host.showStatus(`Rate limited. Retrying in ${event.delayMs / 1e3}s... (attempt ${event.attempt}/${event.maxRetries})`);
64836
- } else if (event.type === "retry_start") {
64943
+ } else if (event.type === "turn_recovery_pending") {
64944
+ if (event.recovery === "retry") {
64945
+ host.showStatus(`Transient request error. Retrying in ${(event.delayMs ?? 0) / 1e3}s... (attempt ${event.attempt}/${event.maxRetries})`);
64946
+ } else {
64947
+ host.showStatus(event.failure.kind === "context_overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context before retry...");
64948
+ }
64949
+ } else if (event.type === "turn_retry_started") {
64837
64950
  host.showStatus(`Retrying request (attempt ${event.attempt}/${event.maxRetries})...`);
64838
- } else if (event.type === "retry_succeeded") {
64951
+ } else if (event.type === "turn_retry_succeeded") {
64839
64952
  const outcome = host.executionState.resumePausedTodo();
64840
64953
  if (outcome.changed) {
64841
64954
  host.sessionTrace.noteRuntimeEvent("todo_resumed_after_retry", {
@@ -64846,12 +64959,80 @@ function createInteractiveAgentSessionController(host) {
64846
64959
  void host.refreshTodoWidget();
64847
64960
  }
64848
64961
  host.showStatus("Retry succeeded");
64849
- } else if (event.type === "retry_exhausted") {
64962
+ } else if (event.type === "turn_completed") {
64963
+ host.clearContinuationTimeout("turn_completed");
64964
+ host.emitExtensionEvent({
64965
+ type: "turn:completed",
64966
+ turnIndex: host._semanticTurnIndex,
64967
+ message: event.message,
64968
+ toolResults: event.toolResults
64969
+ });
64970
+ const outcome = host.executionState.advanceFromCompletedTurn(event.message, event.toolResults || []);
64971
+ host.logDebug("execution:turn_completed", {
64972
+ turnIndex: host._semanticTurnIndex,
64973
+ changed: outcome.changed,
64974
+ currentStepId: host.executionState.read().currentStepId,
64975
+ lifecycle: host.executionState.read().lifecycle,
64976
+ reason: outcome.cleared ? `cleared_${outcome.terminalStatus}` : outcome.changed ? "completed_turn" : "unchanged"
64977
+ });
64978
+ if (outcome.changed) {
64979
+ host.persistExecutionState();
64980
+ void host.refreshTodoWidget();
64981
+ }
64982
+ if (outcome.terminalSummary) {
64983
+ host.appendWarningSummary(outcome.terminalSummary);
64984
+ }
64985
+ host.sessionTrace.completeTurn(event.message, false);
64986
+ host._semanticTurnIndex++;
64987
+ } else if (event.type === "turn_failed_terminal") {
64988
+ host.clearContinuationTimeout("turn_failed_terminal");
64989
+ if (event.message) {
64990
+ host.emitExtensionEvent({
64991
+ type: "turn:failed",
64992
+ turnIndex: host._semanticTurnIndex,
64993
+ message: event.message,
64994
+ toolResults: []
64995
+ });
64996
+ }
64997
+ const outcome = host.executionState.handleTurnFailed();
64998
+ host.logDebug("execution:turn_failed_terminal", {
64999
+ turnIndex: host._semanticTurnIndex,
65000
+ changed: outcome.changed,
65001
+ currentStepId: host.executionState.read().currentStepId,
65002
+ lifecycle: host.executionState.read().lifecycle,
65003
+ kind: event.failure.kind
65004
+ });
65005
+ if (outcome.changed) {
65006
+ host.persistExecutionState();
65007
+ void host.refreshTodoWidget();
65008
+ }
65009
+ if (outcome.terminalSummary) {
65010
+ host.appendWarningSummary(outcome.terminalSummary);
65011
+ }
65012
+ if (event.message) {
65013
+ host.sessionTrace.completeTurn(event.message, true);
65014
+ }
65015
+ host._semanticTurnIndex++;
64850
65016
  const state = host.executionState.read();
64851
65017
  if (state.mode === "todo" && state.lifecycle === "paused" && state.steps.length > 0) {
64852
65018
  host.showStatus("Todo list preserved. Send 'continue' when you want to resume it.");
64853
65019
  }
64854
- host.showError(`Request failed after ${event.maxRetries} retries: ${event.errorMessage}`);
65020
+ host.showError(event.failure.message);
65021
+ } else if (event.type === "turn_cancelled") {
65022
+ const outcome = host.executionState.abort();
65023
+ host.logDebug("execution:turn_cancelled", {
65024
+ turnIndex: host._semanticTurnIndex,
65025
+ cleared: outcome.cleared,
65026
+ lifecycle: host.executionState.read().lifecycle,
65027
+ reason: event.reason
65028
+ });
65029
+ if (outcome.cleared || outcome.terminalSummary) {
65030
+ host.persistExecutionState();
65031
+ void host.refreshTodoWidget();
65032
+ }
65033
+ if (outcome.terminalSummary) {
65034
+ host.appendWarningSummary(outcome.terminalSummary);
65035
+ }
64855
65036
  } else if (event.type === "runtime_guard_triggered") {
64856
65037
  host.sessionTrace.noteStalled("runtime_guard_triggered", {
64857
65038
  toolName: event.toolName,
@@ -67497,16 +67678,38 @@ async function runPrintMode(params) {
67497
67678
  maxRetries: CLI_RETRY_MAX_RETRIES,
67498
67679
  baseDelayMs: getCliRetryBaseDelayMs()
67499
67680
  },
67681
+ getAutoCompactDecision: ({ message, messages }) => createCliAutoCompactDecision({
67682
+ message,
67683
+ messages,
67684
+ contextWindow: runtime.model.contextWindow ?? 0,
67685
+ autoCompactEnabled: runtime.settingsManager.getAutoCompactEnabled(),
67686
+ reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens(),
67687
+ latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
67688
+ isContextOverflowError: isCliContextOverflowError
67689
+ }),
67690
+ compaction: {
67691
+ compact: async (context) => createCliAutoCompactionResult({
67692
+ context,
67693
+ model: runtime.model,
67694
+ apiKey: runtime.apiKey,
67695
+ systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt
67696
+ })
67697
+ },
67698
+ classifyFailure: (context) => classifyCliFailure(context),
67500
67699
  onEvent: (event) => {
67501
67700
  sessionTrace.noteRuntimeEvent(event.type, event);
67502
- if (event.type === "retry_scheduled") {
67503
- console.error(`Transient request error: ${event.errorMessage}. Retrying in ${event.delayMs / 1e3}s (${event.attempt}/${event.maxRetries}).`);
67504
- } else if (event.type === "retry_start") {
67701
+ if (event.type === "turn_recovery_pending" && event.recovery === "retry") {
67702
+ console.error(`Transient request error: ${event.failure.message}. Retrying in ${(event.delayMs ?? 0) / 1e3}s (${event.attempt}/${event.maxRetries}).`);
67703
+ } else if (event.type === "turn_recovery_pending" && event.recovery === "compaction") {
67704
+ console.error(event.failure.kind === "context_overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context before retry...");
67705
+ } else if (event.type === "auto_compaction_start") {
67706
+ console.error(event.reason === "overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context...");
67707
+ } else if (event.type === "turn_retry_started") {
67505
67708
  console.error(`Retrying request (${event.attempt}/${event.maxRetries})...`);
67506
- } else if (event.type === "retry_succeeded") {
67709
+ } else if (event.type === "turn_retry_succeeded") {
67507
67710
  console.error(`Retry succeeded after ${event.attempts} attempt${event.attempts === 1 ? "" : "s"}.`);
67508
- } else if (event.type === "retry_exhausted") {
67509
- console.error(`Request failed after ${event.maxRetries} retries: ${event.errorMessage}`);
67711
+ } else if (event.type === "turn_failed_terminal" && (event.failure.kind === "transient_network" || event.failure.kind === "provider_transient")) {
67712
+ console.error(`Request failed after ${CLI_RETRY_MAX_RETRIES} retries: ${event.failure.message}`);
67510
67713
  }
67511
67714
  }
67512
67715
  });
@@ -67665,8 +67868,12 @@ function assistantTextFromAgent(agent) {
67665
67868
  const messages = agent?.state.messages ?? [];
67666
67869
  const lastMessage = messages[messages.length - 1];
67667
67870
  if (lastMessage?.role !== "assistant") return "";
67871
+ return assistantTextFromMessage(lastMessage);
67872
+ }
67873
+ function assistantTextFromMessage(message) {
67874
+ if (message.role !== "assistant") return "";
67668
67875
  let text = "";
67669
- for (const content of lastMessage.content) {
67876
+ for (const content of message.content) {
67670
67877
  if (content.type === "text") {
67671
67878
  text += content.text;
67672
67879
  }
@@ -67818,6 +68025,24 @@ async function runJsonEventsMode(params) {
67818
68025
  chunk
67819
68026
  });
67820
68027
  }
68028
+ function bufferAssistantOutput(chunk) {
68029
+ if (!activeTurn || !chunk) return;
68030
+ activeTurn.pendingAssistantOutput += chunk;
68031
+ touch();
68032
+ }
68033
+ function clearPendingAssistantOutput() {
68034
+ if (activeTurn) {
68035
+ activeTurn.pendingAssistantOutput = "";
68036
+ }
68037
+ }
68038
+ function flushAssistantOutput(fallbackText = "") {
68039
+ if (!activeTurn) return;
68040
+ const chunk = activeTurn.pendingAssistantOutput || fallbackText;
68041
+ activeTurn.pendingAssistantOutput = "";
68042
+ if (chunk) {
68043
+ display(chunk);
68044
+ }
68045
+ }
67821
68046
  function finalize(kind, detail) {
67822
68047
  const turn = activeTurn;
67823
68048
  if (!turn || turn.finalized) return;
@@ -67837,11 +68062,23 @@ async function runJsonEventsMode(params) {
67837
68062
  break;
67838
68063
  }
67839
68064
  }
68065
+ function rememberTerminal(kind, detail, options) {
68066
+ if (!activeTurn || activeTurn.finalized) return;
68067
+ if (activeTurn.pendingTerminal && options?.overwrite === false) return;
68068
+ activeTurn.pendingTerminal = { kind, detail };
68069
+ touch();
68070
+ }
68071
+ function finalizePendingTerminal() {
68072
+ if (!activeTurn || activeTurn.finalized) return;
68073
+ const pending = activeTurn.pendingTerminal;
68074
+ if (!pending) return;
68075
+ finalize(pending.kind, pending.detail);
68076
+ }
67840
68077
  function handleSemanticEvent(event) {
67841
68078
  if (!activeTurn || activeTurn.finalized) return;
67842
68079
  switch (event.type) {
67843
68080
  case "assistant_response_delta":
67844
- display(event.delta);
68081
+ bufferAssistantOutput(event.delta);
67845
68082
  break;
67846
68083
  case "tool_started":
67847
68084
  touch();
@@ -67883,26 +68120,48 @@ async function runJsonEventsMode(params) {
67883
68120
  }
67884
68121
  function handleControllerEvent(event) {
67885
68122
  trace?.noteRuntimeEvent(event.type, event);
68123
+ if (event.type === "turn_settled" && activeTurn) {
68124
+ finalizePendingTerminal();
68125
+ writeEvent({
68126
+ type: "turn.settled",
68127
+ sessionId: activeTurn.sessionId,
68128
+ turnId: activeTurn.turnId
68129
+ });
68130
+ return;
68131
+ }
67886
68132
  if (!activeTurn || activeTurn.finalized) return;
67887
- if (event.type === "retry_scheduled") {
68133
+ if (event.type === "turn_recovery_pending" && event.recovery === "retry") {
67888
68134
  lastRetryEvent = {
67889
- attempt: event.attempt,
67890
- maxRetries: event.maxRetries,
68135
+ attempt: event.attempt ?? 0,
68136
+ maxRetries: event.maxRetries ?? CLI_RETRY_MAX_RETRIES,
67891
68137
  delayMs: event.delayMs,
67892
- error: event.errorMessage
68138
+ error: event.failure.message
67893
68139
  };
67894
68140
  touch();
67895
68141
  writeEvent({
67896
- type: "turn.retry.scheduled",
68142
+ type: "turn.recovery.pending",
67897
68143
  sessionId: activeTurn.sessionId,
67898
68144
  turnId: activeTurn.turnId,
68145
+ recovery: event.recovery,
67899
68146
  attempt: event.attempt,
67900
68147
  maxRetries: event.maxRetries,
67901
68148
  delayMs: event.delayMs,
67902
- error: event.errorMessage
68149
+ error: event.failure.message
67903
68150
  });
67904
- } else if (event.type === "retry_start") {
68151
+ } else if (event.type === "turn_recovery_pending" && event.recovery === "compaction") {
67905
68152
  activeTurn.summary = "";
68153
+ clearPendingAssistantOutput();
68154
+ touch();
68155
+ writeEvent({
68156
+ type: "turn.recovery.pending",
68157
+ sessionId: activeTurn.sessionId,
68158
+ turnId: activeTurn.turnId,
68159
+ recovery: event.recovery,
68160
+ error: event.failure.message
68161
+ });
68162
+ } else if (event.type === "turn_retry_started") {
68163
+ activeTurn.summary = "";
68164
+ clearPendingAssistantOutput();
67906
68165
  lastRetryEvent = {
67907
68166
  attempt: event.attempt,
67908
68167
  maxRetries: event.maxRetries,
@@ -67919,7 +68178,7 @@ async function runJsonEventsMode(params) {
67919
68178
  delayMs: lastRetryEvent.delayMs,
67920
68179
  error: lastRetryEvent.error
67921
68180
  });
67922
- } else if (event.type === "retry_succeeded") {
68181
+ } else if (event.type === "turn_retry_succeeded") {
67923
68182
  touch();
67924
68183
  writeEvent({
67925
68184
  type: "turn.retry.succeeded",
@@ -67931,18 +68190,40 @@ async function runJsonEventsMode(params) {
67931
68190
  error: lastRetryEvent?.error
67932
68191
  });
67933
68192
  lastRetryEvent = void 0;
67934
- } else if (event.type === "retry_exhausted") {
68193
+ } else if (event.type === "turn_compaction_started") {
68194
+ if (event.willRetry) {
68195
+ activeTurn.summary = "";
68196
+ clearPendingAssistantOutput();
68197
+ }
67935
68198
  touch();
67936
68199
  writeEvent({
67937
- type: "turn.retry.exhausted",
68200
+ type: "turn.compaction.started",
67938
68201
  sessionId: activeTurn.sessionId,
67939
68202
  turnId: activeTurn.turnId,
67940
- attempt: lastRetryEvent?.attempt ?? event.maxRetries,
67941
- maxRetries: event.maxRetries,
67942
- delayMs: lastRetryEvent?.delayMs,
67943
- error: event.errorMessage
68203
+ reason: event.reason,
68204
+ willRetry: event.willRetry
67944
68205
  });
67945
- lastRetryEvent = void 0;
68206
+ } else if (event.type === "turn_completed") {
68207
+ const text = assistantTextFromMessage(event.message);
68208
+ flushAssistantOutput(text);
68209
+ rememberTerminal("completed", text || activeTurn.summary.trim());
68210
+ } else if (event.type === "turn_failed_terminal") {
68211
+ touch();
68212
+ if (lastRetryEvent) {
68213
+ writeEvent({
68214
+ type: "turn.retry.exhausted",
68215
+ sessionId: activeTurn.sessionId,
68216
+ turnId: activeTurn.turnId,
68217
+ attempt: lastRetryEvent.attempt,
68218
+ maxRetries: lastRetryEvent.maxRetries,
68219
+ delayMs: lastRetryEvent.delayMs,
68220
+ error: event.failure.message
68221
+ });
68222
+ lastRetryEvent = void 0;
68223
+ }
68224
+ rememberTerminal("failed", event.failure.message);
68225
+ } else if (event.type === "turn_cancelled") {
68226
+ rememberTerminal("cancelled", event.reason, { overwrite: false });
67946
68227
  }
67947
68228
  }
67948
68229
  async function ensureRuntime() {
@@ -68006,6 +68287,24 @@ async function runJsonEventsMode(params) {
68006
68287
  maxRetries: CLI_RETRY_MAX_RETRIES,
68007
68288
  baseDelayMs: getCliRetryBaseDelayMs()
68008
68289
  },
68290
+ getAutoCompactDecision: ({ message, messages }) => createCliAutoCompactDecision({
68291
+ message,
68292
+ messages,
68293
+ contextWindow: runtime.model.contextWindow ?? 0,
68294
+ autoCompactEnabled: runtime.settingsManager.getAutoCompactEnabled(),
68295
+ reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens(),
68296
+ latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
68297
+ isContextOverflowError: isCliContextOverflowError
68298
+ }),
68299
+ compaction: {
68300
+ compact: async (context) => createCliAutoCompactionResult({
68301
+ context,
68302
+ model: runtime.model,
68303
+ apiKey: runtime.apiKey,
68304
+ systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt
68305
+ })
68306
+ },
68307
+ classifyFailure: (context) => classifyCliFailure(context),
68009
68308
  onEvent: handleControllerEvent
68010
68309
  });
68011
68310
  trace = new SessionTraceWriter(runtime.paths.sessionsDir, runtime.sessionManager);
@@ -68042,7 +68341,8 @@ async function runJsonEventsMode(params) {
68042
68341
  interrupted: false,
68043
68342
  stalled: false,
68044
68343
  finalized: false,
68045
- summary: ""
68344
+ summary: "",
68345
+ pendingAssistantOutput: ""
68046
68346
  };
68047
68347
  const turn = activeTurn;
68048
68348
  writeEvent({ type: "turn.started", sessionId: activeTurn.sessionId, turnId: activeTurn.turnId });
@@ -68099,6 +68399,7 @@ async function runJsonEventsMode(params) {
68099
68399
  trace?.noteWaitForIdleStart({ mode: "json-events" });
68100
68400
  await controller.waitForSettled();
68101
68401
  trace?.noteWaitForIdleSettled({ mode: "json-events" });
68402
+ finalizePendingTerminal();
68102
68403
  if (!activeTurn?.finalized) {
68103
68404
  const finalError = assistantErrorFromAgent(agent);
68104
68405
  if (activeTurn?.interrupted) {
@@ -68110,9 +68411,7 @@ async function runJsonEventsMode(params) {
68110
68411
  return;
68111
68412
  }
68112
68413
  const text = assistantTextFromAgent(agent);
68113
- if (!activeTurn?.summary && text) {
68114
- display(text);
68115
- }
68414
+ flushAssistantOutput(text);
68116
68415
  finalize("completed", text || activeTurn?.summary.trim());
68117
68416
  }
68118
68417
  } catch (error) {
@@ -68176,7 +68475,11 @@ async function runJsonEventsMode(params) {
68176
68475
  if (activeTurn && !activeTurn.finalized) {
68177
68476
  activeTurn.interrupted = true;
68178
68477
  controller?.abort();
68179
- finalize("cancelled", message.reason || "shutdown");
68478
+ if (!controller) {
68479
+ finalize("cancelled", message.reason || "shutdown");
68480
+ } else {
68481
+ rememberTerminal("cancelled", message.reason || "shutdown");
68482
+ }
68180
68483
  }
68181
68484
  break;
68182
68485
  }