@cydm/pie 2.7.0 → 2.7.2

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
@@ -6,6 +6,8 @@ import {
6
6
  createBashTool,
7
7
  createCliHostCapabilities,
8
8
  createCliWebResearchHealthStore,
9
+ createTraceTransportSink,
10
+ createTransportDiagnosticsStreamFn,
9
11
  formatSize,
10
12
  getAgentDir,
11
13
  getBuiltinDir,
@@ -18,9 +20,10 @@ import {
18
20
  getSessionsDir,
19
21
  getSettingsPath,
20
22
  getThemesDir,
23
+ logTransportLifecycle,
21
24
  migrateConfigFromAgentDir,
22
25
  truncateHeadTail
23
- } from "./chunks/chunk-IPNAFMOJ.js";
26
+ } from "./chunks/chunk-GECAIKZL.js";
24
27
  import {
25
28
  resolveCliProjectRoot
26
29
  } from "./chunks/chunk-WLFOBECN.js";
@@ -29,6 +32,7 @@ import {
29
32
  AgentSessionController,
30
33
  AttentionCoordinator,
31
34
  BUILTIN_TOOL_CAPABILITY_METADATA,
35
+ DEFAULT_COMPACTION_CONFIG,
32
36
  ExtensionResourceScope,
33
37
  FileSessionStore,
34
38
  SessionTraceSnapshotWriter,
@@ -72,6 +76,7 @@ import {
72
76
  maybeAdvanceTodoExecutionState,
73
77
  modalityForReadFileUnderstandingKind,
74
78
  pauseExecutionState,
79
+ prepareSessionCompaction,
75
80
  registerInteractionHandler,
76
81
  requestInteraction,
77
82
  resolveExtensionIdentity,
@@ -79,9 +84,10 @@ import {
79
84
  setEnablementInMap,
80
85
  setExtensionEnabledInSettings,
81
86
  shouldPreserveExecutionStateForUserText,
82
- supersedeExecutionState
83
- } from "./chunks/chunk-6RBSTII7.js";
84
- import "./chunks/chunk-TWILEFLD.js";
87
+ supersedeExecutionState,
88
+ verifyPreparedCompactionCommit
89
+ } from "./chunks/chunk-65B6P5J5.js";
90
+ import "./chunks/chunk-GZDAIW6F.js";
85
91
  import {
86
92
  Deref,
87
93
  EMPTY_ASSISTANT_CONTENT_ERROR,
@@ -106,7 +112,7 @@ import {
106
112
  sortToolModelCandidatesByCapability,
107
113
  streamSimple,
108
114
  type_exports
109
- } from "./chunks/chunk-R5I2QLF2.js";
115
+ } from "./chunks/chunk-BRFP5WZ6.js";
110
116
  import {
111
117
  IsArray,
112
118
  IsBigInt,
@@ -52509,6 +52515,9 @@ function validateCompat(value, label) {
52509
52515
  "store",
52510
52516
  "supportsEncryptedReasoning",
52511
52517
  "responsesContinuation",
52518
+ "supportsReasoningSummary",
52519
+ "reasoningSummary",
52520
+ "timeoutReplay",
52512
52521
  "supportsStore",
52513
52522
  "supportsDeveloperRole",
52514
52523
  "supportsReasoningEffort",
@@ -52525,10 +52534,16 @@ function validateCompat(value, label) {
52525
52534
  if (value.forcedReasoningEffort !== void 0 && value.forcedReasoningEffort !== "none") {
52526
52535
  throw new Error(`${label}.forcedReasoningEffort must be "none"`);
52527
52536
  }
52537
+ if (value.reasoningSummary !== void 0 && !["auto", "concise", "detailed", "none"].includes(String(value.reasoningSummary))) {
52538
+ throw new Error(`${label}.reasoningSummary must be auto | concise | detailed | none`);
52539
+ }
52528
52540
  for (const field of [
52529
52541
  "store",
52530
52542
  "supportsEncryptedReasoning",
52531
52543
  "responsesContinuation",
52544
+ "supportsReasoningSummary",
52545
+ "reasoningSummary",
52546
+ "timeoutReplay",
52532
52547
  "supportsStore",
52533
52548
  "supportsDeveloperRole",
52534
52549
  "supportsReasoningEffort",
@@ -52576,8 +52591,8 @@ function validateRemoteModel(profileId, model) {
52576
52591
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid input capabilities`);
52577
52592
  }
52578
52593
  if (!isPlainRecord(model.cost)) throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid cost values`);
52579
- assertOnlyKeys(model.cost, ["input", "output", "cacheRead", "cacheWrite"], `Remote profile "${profileId}" model "${model.id}" cost`);
52580
- if ([model.cost.input, model.cost.output, model.cost.cacheRead, model.cost.cacheWrite].some((value) => typeof value !== "number" || !Number.isFinite(value) || value < 0)) {
52594
+ assertOnlyKeys(model.cost, ["input", "output", "cacheRead", "cacheWrite", "cacheWriteLong"], `Remote profile "${profileId}" model "${model.id}" cost`);
52595
+ if ([model.cost.input, model.cost.output, model.cost.cacheRead, model.cost.cacheWrite, model.cost.cacheWriteLong].some((value) => value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < 0))) {
52581
52596
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid cost values`);
52582
52597
  }
52583
52598
  if (!Number.isSafeInteger(model.contextWindow) || Number(model.contextWindow) <= 0 || !Number.isSafeInteger(model.maxTokens) || Number(model.maxTokens) <= 0 || Number(model.maxTokens) > Number(model.contextWindow)) {
@@ -52689,7 +52704,8 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52689
52704
  input: model.cost.input,
52690
52705
  output: model.cost.output,
52691
52706
  cacheRead: model.cost.cacheRead,
52692
- cacheWrite: model.cost.cacheWrite
52707
+ cacheWrite: model.cost.cacheWrite,
52708
+ ...model.cost.cacheWriteLong === void 0 ? {} : { cacheWriteLong: model.cost.cacheWriteLong }
52693
52709
  },
52694
52710
  contextWindow: model.contextWindow,
52695
52711
  maxTokens: model.maxTokens,
@@ -52756,8 +52772,8 @@ function validateModelOverrides(value) {
52756
52772
  if (override.maxTokens !== void 0 && (!Number.isInteger(override.maxTokens) || Number(override.maxTokens) < 1)) throw new Error(`models.json modelOverrides["${key}"].maxTokens is invalid`);
52757
52773
  if (override.cost !== void 0) {
52758
52774
  if (!isPlainRecord(override.cost)) throw new Error(`models.json modelOverrides["${key}"].cost must be an object`);
52759
- for (const field of ["input", "output", "cacheRead", "cacheWrite"]) {
52760
- if (override.cost[field] !== void 0 && (typeof override.cost[field] !== "number" || Number.isNaN(override.cost[field]))) throw new Error(`models.json modelOverrides["${key}"].cost.${field} must be a number`);
52775
+ for (const field of ["input", "output", "cacheRead", "cacheWrite", "cacheWriteLong"]) {
52776
+ if (override.cost[field] !== void 0 && (typeof override.cost[field] !== "number" || !Number.isFinite(override.cost[field]) || override.cost[field] < 0)) throw new Error(`models.json modelOverrides["${key}"].cost.${field} must be a non-negative finite number`);
52761
52777
  }
52762
52778
  }
52763
52779
  if (override.headers !== void 0) {
@@ -53856,6 +53872,16 @@ var SettingsManager = class {
53856
53872
  this.settings.autoCompactReserveTokens = tokens;
53857
53873
  this.writeSettings();
53858
53874
  }
53875
+ /** Undefined means "no CLI override": the provider default applies. */
53876
+ getPromptCacheRetention() {
53877
+ const retention = this.settings.promptCacheRetention;
53878
+ return retention === "none" || retention === "short" || retention === "long" ? retention : void 0;
53879
+ }
53880
+ setPromptCacheRetention(retention) {
53881
+ if (retention === void 0) delete this.settings.promptCacheRetention;
53882
+ else this.settings.promptCacheRetention = retention;
53883
+ this.writeSettings();
53884
+ }
53859
53885
  getWebSearchMode() {
53860
53886
  const mode = this.settings.tools?.webSearch?.mode;
53861
53887
  if (mode === "disabled" || mode === "cached" || mode === "live") {
@@ -55575,10 +55601,11 @@ async function forkStoredSession(params) {
55575
55601
  timestamp: entry.timestamp
55576
55602
  };
55577
55603
  if (copy.compactionData) {
55604
+ const selfAnchored = copy.compactionData.firstKeptEntryId === entry.id;
55578
55605
  const keptId = idMap.get(copy.compactionData.firstKeptEntryId);
55579
55606
  copy.compactionData = {
55580
55607
  ...copy.compactionData,
55581
- firstKeptEntryId: keptId ?? firstCopiedId ?? copy.id
55608
+ firstKeptEntryId: selfAnchored ? copy.id : keptId ?? firstCopiedId ?? copy.id
55582
55609
  };
55583
55610
  }
55584
55611
  idMap.set(summary.id, copy.id);
@@ -65073,6 +65100,23 @@ function buildCliFileAccessOptions(params) {
65073
65100
  };
65074
65101
  }
65075
65102
 
65103
+ // src/runtime/computer-use-owner.ts
65104
+ var COMPUTER_USE_OWNER_ENV = "PIE_COMPUTER_USE_OWNER_ID";
65105
+ var COMPUTER_USE_EXECUTION_ENV = "PIE_COMPUTER_USE_EXECUTION_ID";
65106
+ var COMPUTER_USE_STANDALONE_OWNER = "standalone";
65107
+ var OWNER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,255}$/;
65108
+ function normalizeComputerUseOwner(value) {
65109
+ if (typeof value !== "string") return COMPUTER_USE_STANDALONE_OWNER;
65110
+ const trimmed = value.trim();
65111
+ if (!trimmed) return COMPUTER_USE_STANDALONE_OWNER;
65112
+ return OWNER_PATTERN.test(trimmed) ? trimmed : COMPUTER_USE_STANDALONE_OWNER;
65113
+ }
65114
+ function computerUseOwnerEnv(ownerId, executionId) {
65115
+ const env = { [COMPUTER_USE_OWNER_ENV]: normalizeComputerUseOwner(ownerId) };
65116
+ if (executionId) env[COMPUTER_USE_EXECUTION_ENV] = executionId;
65117
+ return env;
65118
+ }
65119
+
65076
65120
  // src/theme/theme.ts
65077
65121
  import * as fs13 from "node:fs";
65078
65122
  import * as path12 from "node:path";
@@ -70243,19 +70287,25 @@ var SessionTraceWriter = class extends SessionTraceSnapshotWriter {
70243
70287
  import { createHash as createHash4 } from "crypto";
70244
70288
  function applyContextSignalsToFooter(target) {
70245
70289
  if (!target.agent?.state) return;
70290
+ const state = target.agent.state;
70291
+ const route = {
70292
+ model: state.model ? `${state.model.provider ?? ""}/${state.model.id ?? ""}` : void 0,
70293
+ thinkingLevel: state.thinkingLevel
70294
+ };
70246
70295
  const signals = computeContextSignals(
70247
70296
  target,
70248
- target.agent.state.systemPrompt ?? "",
70249
- target.agent.state.tools,
70250
- target.sessionManager.getMessages()
70297
+ state.systemPrompt ?? "",
70298
+ state.tools,
70299
+ target.sessionManager.getMessages(),
70300
+ route
70251
70301
  );
70252
70302
  target.footer.setState({
70253
70303
  prefixHash: signals.prefixHash,
70254
70304
  prefixHashChanged: signals.prefixHashChanged,
70255
- cacheHitRate: signals.cacheHitRate
70305
+ cacheHitRate: signals.recentCacheHitRate ?? signals.cacheHitRate
70256
70306
  });
70257
70307
  }
70258
- function computePromptPrefixHash(systemPrompt, tools) {
70308
+ function computePromptPrefixHash(systemPrompt, tools, route) {
70259
70309
  return createHash4("sha256").update(
70260
70310
  JSON.stringify({
70261
70311
  systemPrompt,
@@ -70263,21 +70313,31 @@ function computePromptPrefixHash(systemPrompt, tools) {
70263
70313
  name: tool.name,
70264
70314
  description: tool.description ?? "",
70265
70315
  parameters: tool.parameters ?? null
70266
- }))
70316
+ })),
70317
+ route: { model: route?.model ?? "", thinkingLevel: route?.thinkingLevel ?? "" }
70267
70318
  })
70268
70319
  ).digest("hex").slice(0, 8);
70269
70320
  }
70270
- function calculateCumulativeCacheHitRate(messages) {
70271
- let input = 0;
70272
- let cacheRead = 0;
70321
+ function calculateCumulativeCacheHitRate(messages, recentTurns) {
70322
+ const usages = [];
70273
70323
  for (const message of messages) {
70274
70324
  if (message.role !== "assistant") continue;
70275
70325
  const usage = message.usage;
70276
70326
  if (!usage) continue;
70327
+ usages.push(usage);
70328
+ }
70329
+ const windowed = recentTurns !== void 0 && usages.length > recentTurns ? usages.slice(-recentTurns) : usages;
70330
+ let input = 0;
70331
+ let cacheRead = 0;
70332
+ let cacheWrite = 0;
70333
+ let cacheWriteLong = 0;
70334
+ for (const usage of windowed) {
70277
70335
  input += usage.input;
70278
70336
  cacheRead += usage.cacheRead;
70337
+ cacheWrite += usage.cacheWrite;
70338
+ cacheWriteLong += usage.cacheWriteLong ?? 0;
70279
70339
  }
70280
- const base = input + cacheRead;
70340
+ const base = input + cacheRead + cacheWrite + cacheWriteLong;
70281
70341
  return base > 0 ? cacheRead / base : null;
70282
70342
  }
70283
70343
  var signalMemories = /* @__PURE__ */ new WeakMap();
@@ -70285,14 +70345,16 @@ var lastSignals = /* @__PURE__ */ new WeakMap();
70285
70345
  function getLastContextSignals(hostKey) {
70286
70346
  return lastSignals.get(hostKey) ?? null;
70287
70347
  }
70288
- function computeContextSignals(hostKey, systemPrompt, tools, messages) {
70289
- const prefixHash = computePromptPrefixHash(systemPrompt, tools);
70348
+ var RECENT_HIT_RATE_TURNS = 5;
70349
+ function computeContextSignals(hostKey, systemPrompt, tools, messages, route) {
70350
+ const routeKey = `${route?.model ?? ""}|${route?.thinkingLevel ?? ""}`;
70351
+ const prefixHash = computePromptPrefixHash(systemPrompt, tools, route);
70290
70352
  const systemHash = computePromptPrefixHash(systemPrompt, []);
70291
70353
  const toolsHash = computePromptPrefixHash("", tools);
70292
70354
  const previous = signalMemories.get(hostKey);
70293
70355
  const prefixHashChanged = previous !== void 0 && previous.hash !== prefixHash;
70294
70356
  if (previous?.hash !== prefixHash) {
70295
- signalMemories.set(hostKey, { hash: prefixHash, systemHash, toolsHash });
70357
+ signalMemories.set(hostKey, { hash: prefixHash, systemHash, toolsHash, routeKey });
70296
70358
  }
70297
70359
  const signals = {
70298
70360
  prefixHash,
@@ -70300,7 +70362,9 @@ function computeContextSignals(hostKey, systemPrompt, tools, messages) {
70300
70362
  ...prefixHashChanged && previous ? { previousPrefixHash: previous.hash } : {},
70301
70363
  systemPromptChanged: prefixHashChanged && previous !== void 0 && previous.systemHash !== systemHash,
70302
70364
  toolsChanged: prefixHashChanged && previous !== void 0 && previous.toolsHash !== toolsHash,
70303
- cacheHitRate: calculateCumulativeCacheHitRate(messages)
70365
+ routeChanged: prefixHashChanged && previous !== void 0 && previous.routeKey !== routeKey,
70366
+ cacheHitRate: calculateCumulativeCacheHitRate(messages),
70367
+ recentCacheHitRate: calculateCumulativeCacheHitRate(messages, RECENT_HIT_RATE_TURNS)
70304
70368
  };
70305
70369
  lastSignals.set(hostKey, signals);
70306
70370
  return signals;
@@ -70310,10 +70374,11 @@ function computeContextSignals(hostKey, systemPrompt, tools, messages) {
70310
70374
  function noteCacheInvalidationInChat(host, signals) {
70311
70375
  if (!signals || !signals.prefixHashChanged) return;
70312
70376
  const parts = [];
70377
+ if (signals.routeChanged) parts.push(signals.systemPromptChanged || signals.toolsChanged ? "route + prompt rebuilt" : "model/thinking route changed");
70313
70378
  if (signals.systemPromptChanged && signals.toolsChanged) parts.push("system prompt + tools rebuilt");
70314
70379
  else if (signals.toolsChanged) parts.push("tools rebuilt");
70315
70380
  else if (signals.systemPromptChanged) parts.push("system prompt rebuilt");
70316
- else parts.push("prefix rebuilt");
70381
+ if (parts.length === 0) parts.push("prefix rebuilt");
70317
70382
  const from = signals.previousPrefixHash ?? "?";
70318
70383
  const line = `\u26A1 Prompt cache invalidated: \u2301${from} \u2192 \u2301${signals.prefixHash} (${parts.join(", ")})`;
70319
70384
  if (host.chatContainer && host.ui) {
@@ -70325,7 +70390,8 @@ function noteCacheInvalidationInChat(host, signals) {
70325
70390
  from,
70326
70391
  to: signals.prefixHash,
70327
70392
  systemPromptChanged: signals.systemPromptChanged,
70328
- toolsChanged: signals.toolsChanged
70393
+ toolsChanged: signals.toolsChanged,
70394
+ routeChanged: signals.routeChanged
70329
70395
  });
70330
70396
  }
70331
70397
  function applyContextSignalsWithTrace(host) {
@@ -70534,6 +70600,11 @@ function formatReportTokens(count) {
70534
70600
  if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
70535
70601
  return `${Math.round(count / 1e6)}M`;
70536
70602
  }
70603
+ function formatMeasuredTime(timestamp) {
70604
+ const date = new Date(timestamp);
70605
+ const pad = (value) => String(value).padStart(2, "0");
70606
+ return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
70607
+ }
70537
70608
  function buildSystemPromptBlocks(systemPrompt, skillsSection, knowledgeSection) {
70538
70609
  const totalTokens = estimateStringTokens(systemPrompt);
70539
70610
  const skillsTokens = skillsSection && systemPrompt.includes(skillsSection.trim()) ? estimateStringTokens(skillsSection) : null;
@@ -70789,8 +70860,12 @@ ${file.content}`).join("\n\n");
70789
70860
  messagesTokens,
70790
70861
  dynamicContextTokens: 0,
70791
70862
  estimatedTotalTokens,
70792
- actualTokens: inputs.actualTokens,
70863
+ estimatedCurrentTokens: inputs.estimatedCurrentTokens,
70864
+ providerMeasuredTokens: inputs.providerMeasuredTokens,
70865
+ providerMeasuredAt: inputs.providerMeasuredAt ?? null,
70866
+ measurementMatchesCurrentContext: inputs.measurementMatchesCurrentContext,
70793
70867
  contextWindow: inputs.contextWindow,
70868
+ autoCompactEnabled: inputs.autoCompactEnabled,
70794
70869
  autoCompactLimitTokens: inputs.autoCompactLimitTokens,
70795
70870
  reserveTokens: inputs.reserveTokens,
70796
70871
  promptPrefixHash: inputs.promptPrefixHash,
@@ -70847,13 +70922,14 @@ function buildContextOverviewText(data, themeInput) {
70847
70922
  }
70848
70923
  }
70849
70924
  });
70850
- const autoCompact = data.autoCompactLimitTokens ? `triggers > ~${formatReportTokens(data.autoCompactLimitTokens)} (reserve ${formatReportTokens(data.reserveTokens)})` : "disabled";
70925
+ const autoCompact = !data.autoCompactEnabled ? "Off (disabled in settings)" : data.autoCompactLimitTokens ? `On \xB7 triggers > ~${formatReportTokens(data.autoCompactLimitTokens)} (reserve ${formatReportTokens(data.reserveTokens)})` : "On \xB7 threshold unknown (context window unavailable)";
70851
70926
  lines.push(
70852
70927
  `${padEndVisible(` ${branch(sections.length)} auto-compact`, labelWidth)}${theme2.fg("dim", autoCompact)}`
70853
70928
  );
70854
- const reconciliation = data.actualTokens !== null ? `actual ${formatReportTokens(data.actualTokens)} (last response) \xB7 est\u2212actual \u2248 ${formatReportTokens(
70855
- Math.abs(data.estimatedTotalTokens - data.actualTokens)
70856
- )}${data.estimatedTotalTokens >= data.actualTokens ? " overhead" : " undercount"}` : "actual unknown until the next model response";
70929
+ const measuredAt = data.providerMeasuredAt !== null ? ` \xB7 measured ${formatMeasuredTime(data.providerMeasuredAt)}` : "";
70930
+ const reconciliation = data.providerMeasuredTokens !== null ? data.measurementMatchesCurrentContext ? `est current ~${formatReportTokens(data.estimatedCurrentTokens)} \xB7 provider-measured ${formatReportTokens(data.providerMeasuredTokens)} (last response${measuredAt}) \xB7 est\u2212measured \u2248 ${formatReportTokens(
70931
+ Math.abs(data.estimatedCurrentTokens - data.providerMeasuredTokens)
70932
+ )}${data.estimatedCurrentTokens >= data.providerMeasuredTokens ? " overhead" : " undercount"}` : `est current ~${formatReportTokens(data.estimatedCurrentTokens)} \xB7 provider-measured ${formatReportTokens(data.providerMeasuredTokens)} (last response${measuredAt}) \xB7 not comparable \u2014 context changed since measurement` : `est current ~${formatReportTokens(data.estimatedCurrentTokens)} \xB7 provider usage not reported yet`;
70857
70933
  lines.push(` ${theme2.fg("dim", reconciliation)}`);
70858
70934
  lines.push(theme2.fg("dim", " /debugs/context \xB7 Enter to drill into a section, Esc to go back"));
70859
70935
  return lines.join("\n");
@@ -70872,23 +70948,37 @@ function buildWindowRows(data) {
70872
70948
  label: "estimated total (prompt + tools + messages)",
70873
70949
  estTokens: data.estimatedTotalTokens,
70874
70950
  note: `prompt ~${formatReportTokens(data.systemPromptTokens)} \xB7 tools ~${formatReportTokens(data.toolsTokens)} \xB7 messages ~${formatReportTokens(data.messagesTokens)}`
70951
+ },
70952
+ {
70953
+ id: "window:estimated-current",
70954
+ label: "estimated current context (local estimate + tail)",
70955
+ estTokens: data.estimatedCurrentTokens,
70956
+ note: "local estimate \u2014 not a provider measurement"
70875
70957
  }
70876
70958
  ];
70877
- if (data.actualTokens !== null) {
70959
+ if (data.providerMeasuredTokens !== null) {
70960
+ const measuredAt = data.providerMeasuredAt !== null ? formatMeasuredTime(data.providerMeasuredAt) : "unknown time";
70878
70961
  rows.push({
70879
- id: "window:actual",
70880
- label: "actual (last model response)",
70881
- estTokens: data.actualTokens,
70882
- valueText: formatReportTokens(data.actualTokens),
70883
- note: `gap \u2248 ${formatReportTokens(Math.abs(data.estimatedTotalTokens - data.actualTokens))}`
70962
+ id: "window:provider-measured",
70963
+ label: "provider-measured (last response)",
70964
+ estTokens: data.providerMeasuredTokens,
70965
+ valueText: formatReportTokens(data.providerMeasuredTokens),
70966
+ note: data.measurementMatchesCurrentContext ? `est\u2212measured \u2248 ${formatReportTokens(Math.abs(data.estimatedCurrentTokens - data.providerMeasuredTokens))} \xB7 ${measuredAt}` : `not comparable \u2014 context changed since measurement \xB7 ${measuredAt}`
70884
70967
  });
70885
70968
  }
70886
70969
  rows.push({
70887
70970
  id: "window:auto-compact",
70971
+ label: "auto-compact",
70972
+ estTokens: 0,
70973
+ valueText: data.autoCompactEnabled ? "On" : "Off",
70974
+ note: data.autoCompactEnabled ? "enabled in settings" : "disabled in settings"
70975
+ });
70976
+ rows.push({
70977
+ id: "window:auto-compact-threshold",
70888
70978
  label: "auto-compact threshold",
70889
- estTokens: data.autoCompactLimitTokens ?? 0,
70890
- valueText: data.autoCompactLimitTokens ? `>${formatReportTokens(data.autoCompactLimitTokens)}` : "off",
70891
- note: data.autoCompactLimitTokens ? `reserve ${formatReportTokens(data.reserveTokens)}` : "disabled"
70979
+ estTokens: data.autoCompactEnabled ? data.autoCompactLimitTokens ?? 0 : 0,
70980
+ valueText: data.autoCompactEnabled ? data.autoCompactLimitTokens ? `>${formatReportTokens(data.autoCompactLimitTokens)}` : "unknown" : "\u2014",
70981
+ note: data.autoCompactEnabled ? data.autoCompactLimitTokens ? `reserve ${formatReportTokens(data.reserveTokens)}` : "context window unavailable" : "not applied while Off"
70892
70982
  });
70893
70983
  if (data.promptPrefixHash) {
70894
70984
  rows.push({
@@ -71300,11 +71390,20 @@ var TreeSelectorComponent = class {
71300
71390
  this.statusMessage = null;
71301
71391
  }
71302
71392
  }
71393
+ /** Cursor on the resident entry with the highest seq (the newest), not the last rendered row. */
71394
+ selectNewestResidentRow(message) {
71395
+ if (this.filteredRows.length === 0) return;
71396
+ let newestIndex = 0;
71397
+ for (let i = 1; i < this.filteredRows.length; i++) {
71398
+ if (this.filteredRows[i].summary.seq > this.filteredRows[newestIndex].summary.seq) newestIndex = i;
71399
+ }
71400
+ this.selectedIndex = newestIndex;
71401
+ if (message !== null) this.statusMessage = message;
71402
+ }
71303
71403
  /** Jump to the newest page. Seqs are 1..total (append-only journal), so the last window starts at total - pageSize. */
71304
71404
  jumpToLastPage() {
71305
71405
  if (this.pageSummaries.length > 0 && !this.hasNext) {
71306
- this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71307
- this.statusMessage = "latest page - at newest entry";
71406
+ this.selectNewestResidentRow("latest page - at newest entry");
71308
71407
  return;
71309
71408
  }
71310
71409
  let anchor = Math.max(0, this.totalEntries - this.pageSizeValue);
@@ -71314,8 +71413,7 @@ var TreeSelectorComponent = class {
71314
71413
  anchor = this.pageSummaries[this.pageSummaries.length - 1].seq;
71315
71414
  continue;
71316
71415
  }
71317
- this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71318
- this.statusMessage = null;
71416
+ this.selectNewestResidentRow(null);
71319
71417
  return;
71320
71418
  }
71321
71419
  if (anchor === 0 || !this.hasNext) break;
@@ -72781,7 +72879,9 @@ function calculateSessionUsageTotals(messages) {
72781
72879
  let output = 0;
72782
72880
  let cacheRead = 0;
72783
72881
  let cacheWrite = 0;
72882
+ let cacheWriteLong = 0;
72784
72883
  let cost = 0;
72884
+ let costEstimated = false;
72785
72885
  for (const message of messages) {
72786
72886
  if (message.role !== "assistant") continue;
72787
72887
  const usage = message.usage;
@@ -72790,9 +72890,11 @@ function calculateSessionUsageTotals(messages) {
72790
72890
  output += usage.output;
72791
72891
  cacheRead += usage.cacheRead;
72792
72892
  cacheWrite += usage.cacheWrite;
72893
+ cacheWriteLong += usage.cacheWriteLong ?? 0;
72793
72894
  cost += usage.cost?.total ?? 0;
72895
+ if (usage.cost?.cacheWriteLongEstimated === true) costEstimated = true;
72794
72896
  }
72795
- return { input, output, cacheRead, cacheWrite, cost };
72897
+ return { input, output, cacheRead, cacheWrite, cacheWriteLong, cacheWriteTotal: cacheWrite + cacheWriteLong, cost, costEstimated };
72796
72898
  }
72797
72899
  function formatTokensCompact(count) {
72798
72900
  if (count < 1e3) return String(count);
@@ -72838,16 +72940,18 @@ ${options.theme.bold("Usage (active context)")}
72838
72940
  info += `${options.theme.fg("dim", "Cache Read:")} ${totals.cacheRead.toLocaleString()}
72839
72941
  `;
72840
72942
  }
72841
- if (totals.cacheWrite > 0) {
72842
- info += `${options.theme.fg("dim", "Cache Write:")} ${totals.cacheWrite.toLocaleString()}
72943
+ if (totals.cacheWriteTotal > 0) {
72944
+ const split = totals.cacheWriteLong > 0 ? options.theme.fg("dim", ` (short ${totals.cacheWrite.toLocaleString()} \xB7 long ${totals.cacheWriteLong.toLocaleString()})`) : "";
72945
+ info += `${options.theme.fg("dim", "Cache Write:")} ${totals.cacheWriteTotal.toLocaleString()}${split}
72843
72946
  `;
72844
72947
  }
72845
72948
  if (totals.cost > 0) {
72846
- info += `${options.theme.fg("dim", "Cost:")} $${totals.cost.toFixed(4)}
72949
+ const estimateNote = totals.costEstimated ? " (long-TTL estimate)" : "";
72950
+ info += `${options.theme.fg("dim", "Cost:")} $${totals.cost.toFixed(4)}${estimateNote}
72847
72951
  `;
72848
72952
  }
72849
- const hitRateBase = totals.input + totals.cacheRead;
72850
- if (hitRateBase > 0 && totals.cacheRead > 0) {
72953
+ const hitRateBase = totals.input + totals.cacheRead + totals.cacheWriteTotal;
72954
+ if (hitRateBase > 0) {
72851
72955
  const hitRate = (totals.cacheRead / hitRateBase * 100).toFixed(1);
72852
72956
  info += `${options.theme.fg("dim", "Cache Hit Rate:")} ${hitRate}% (~${formatTokensCompact(hitRateBase)} served)
72853
72957
  `;
@@ -72981,7 +73085,9 @@ async function loadSession(host, sessionId) {
72981
73085
  outputTokens: contextUsage.output,
72982
73086
  cacheRead: contextUsage.cacheRead,
72983
73087
  cacheWrite: contextUsage.cacheWrite,
73088
+ cacheWriteLong: contextUsage.cacheWriteLong,
72984
73089
  cost: contextUsage.cost,
73090
+ costEstimated: contextUsage.costEstimated,
72985
73091
  contextPercent,
72986
73092
  contextWindow
72987
73093
  });
@@ -74019,7 +74125,9 @@ function bindInteractiveRuntimeEvents(host) {
74019
74125
  outputTokens: contextUsage.output,
74020
74126
  cacheRead: contextUsage.cacheRead,
74021
74127
  cacheWrite: contextUsage.cacheWrite,
74128
+ cacheWriteLong: contextUsage.cacheWriteLong,
74022
74129
  cost: contextUsage.cost,
74130
+ costEstimated: contextUsage.costEstimated,
74023
74131
  contextPercent,
74024
74132
  contextWindow
74025
74133
  });
@@ -74200,6 +74308,9 @@ function isControllerRecovering(host) {
74200
74308
  function handleMessageStart(host, event) {
74201
74309
  if (event.message?.role === "assistant") {
74202
74310
  host.clearContinuationTimeout("assistant_message_start");
74311
+ for (const [id, component] of host.pendingTools) {
74312
+ if (!component.calm) host.pendingTools.delete(id);
74313
+ }
74203
74314
  host.streamingComponent = new AssistantMessageComponent(void 0, host.thinkingLevel === "off");
74204
74315
  host.streamingComponent.setCalmMode(host.calmMode);
74205
74316
  host.streamingMessageHadToolCalls = false;
@@ -74771,7 +74882,7 @@ function listDiscoveredSkills(host) {
74771
74882
  function applyReloadedSkills(host, result) {
74772
74883
  host.skills = result.skills.filter((skill) => host.options.settingsManager.isSkillEnabled(skill.name));
74773
74884
  host.skillsSection = formatSkillsForPrompt(host.skills);
74774
- host.applyRuntimeToolMode(host.allTools.length > 0 ? host.allTools : host.agent.state.tools);
74885
+ host.applyRuntimeToolMode();
74775
74886
  host.updateSystemPromptTools();
74776
74887
  for (const skill of host.skills) {
74777
74888
  const skillPath = ["skills", `skill:${skill.name}`];
@@ -75199,6 +75310,23 @@ function handleToolsCommand(host, args) {
75199
75310
  var RESET2 = "\x1B[0m";
75200
75311
  var INVERSE2 = "\x1B[7m";
75201
75312
  var ROLE_FILTER_CYCLE = ["all", "user", "assistant", "toolResult", "compaction"];
75313
+ function buildWrappedLines(rawLines, contentWidth) {
75314
+ const width = Math.max(1, contentWidth);
75315
+ const lines = [];
75316
+ const sourceStart = [];
75317
+ for (const rawLine of rawLines) {
75318
+ sourceStart.push(lines.length);
75319
+ lines.push(...wrapTextWithAnsi(rawLine, width));
75320
+ }
75321
+ return { lines: lines.length > 0 ? lines : [""], sourceStart };
75322
+ }
75323
+ function anchorFor(scroll, sourceStart, sourceCount) {
75324
+ if (sourceStart.length === 0) return { source: 0, offset: 0 };
75325
+ const target = Math.max(0, Math.min(scroll, sourceCount - 1));
75326
+ let source = sourceStart.length - 1;
75327
+ while (source > 0 && sourceStart[source] > target) source -= 1;
75328
+ return { source, offset: target - sourceStart[source] };
75329
+ }
75202
75330
  function createLine2(content, width, isSelected) {
75203
75331
  const normalized = content.replace(/[\r\n]+/g, " ");
75204
75332
  const body = isSelected ? INVERSE2 + normalized : normalized;
@@ -75339,12 +75467,33 @@ var ContextReportSelectorComponent = class {
75339
75467
  this.views.push({
75340
75468
  kind: "text",
75341
75469
  title,
75342
- lines: text.replace(/\r\n/g, "\n").split("\n"),
75470
+ rawLines: text.replace(/\r\n/g, "\n").split("\n"),
75471
+ lines: [],
75472
+ sourceStart: [],
75473
+ wrapWidth: 0,
75343
75474
  scroll: 0
75344
75475
  });
75345
75476
  this.selectedIndex = 0;
75346
75477
  this.statusMessage = null;
75347
75478
  }
75479
+ /** Re-wrap a text view for the current terminal width, preserving the anchor. */
75480
+ ensureTextWrapped(view, width) {
75481
+ const contentWidth = Math.max(1, width - 2);
75482
+ if (view.wrapWidth === contentWidth) return;
75483
+ const previousCount = view.lines.length;
75484
+ const anchor = view.wrapWidth > 0 && previousCount > 0 ? anchorFor(view.scroll, view.sourceStart, previousCount) : { source: 0, offset: 0 };
75485
+ const rebuilt = buildWrappedLines(view.rawLines, contentWidth);
75486
+ view.lines = rebuilt.lines;
75487
+ view.sourceStart = rebuilt.sourceStart;
75488
+ view.wrapWidth = contentWidth;
75489
+ const source = Math.min(anchor.source, Math.max(0, rebuilt.sourceStart.length - 1));
75490
+ const nextSourceStart = rebuilt.sourceStart[source + 1] ?? rebuilt.lines.length;
75491
+ const sourceLineCount = Math.max(1, nextSourceStart - rebuilt.sourceStart[source]);
75492
+ view.scroll = Math.min(
75493
+ Math.max(0, rebuilt.lines.length - 1),
75494
+ rebuilt.sourceStart[source] + Math.min(anchor.offset, sourceLineCount - 1)
75495
+ );
75496
+ }
75348
75497
  drillIntoSelected() {
75349
75498
  const view = this.currentView;
75350
75499
  if (view.kind !== "list") return;
@@ -75526,6 +75675,8 @@ var ContextReportSelectorComponent = class {
75526
75675
  )
75527
75676
  );
75528
75677
  } else {
75678
+ this.ensureTextWrapped(view, width);
75679
+ view.scroll = Math.max(0, Math.min(view.scroll, view.lines.length - 1));
75529
75680
  const total = view.lines.length;
75530
75681
  const start = Math.min(view.scroll, Math.max(0, total - 1));
75531
75682
  const end = Math.min(start + this.maxVisibleLines, total);
@@ -75690,14 +75841,20 @@ function handleContextReportCommand(host) {
75690
75841
  const usage = host.calculateContextUsage();
75691
75842
  const contextWindow = host.options.model?.contextWindow ?? 0;
75692
75843
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
75844
+ const autoCompactEnabled = host.options.settingsManager.getAutoCompactEnabled();
75693
75845
  const autoCompactLimit = contextWindow > 0 ? getAutoCompactTokenLimit(contextWindow, reserveTokens) ?? null : null;
75846
+ const state = host.agent.state;
75694
75847
  const prefixHash = computePromptPrefixHash(
75695
75848
  systemPrompt,
75696
- host.agent.state.tools.map((tool) => ({
75849
+ state.tools.map((tool) => ({
75697
75850
  name: tool.name,
75698
75851
  description: tool.description,
75699
75852
  parameters: tool.parameters
75700
- }))
75853
+ })),
75854
+ {
75855
+ model: state.model ? `${state.model.provider ?? ""}/${state.model.id ?? ""}` : void 0,
75856
+ thinkingLevel: state.thinkingLevel
75857
+ }
75701
75858
  );
75702
75859
  const lastSignals2 = getLastContextSignals(host);
75703
75860
  const promptPrefixChanged = lastSignals2 !== null && lastSignals2.prefixHash !== prefixHash;
@@ -75721,7 +75878,11 @@ function handleContextReportCommand(host) {
75721
75878
  entries,
75722
75879
  compactionTokensBefore: (entryId) => host.sessionManager.getEntry(entryId)?.compactionData?.tokensBefore,
75723
75880
  contextWindow,
75724
- actualTokens: usage.tokens,
75881
+ estimatedCurrentTokens: usage.estimatedCurrentTokens,
75882
+ providerMeasuredTokens: usage.providerMeasuredTokens,
75883
+ providerMeasuredAt: usage.providerMeasuredAt,
75884
+ measurementMatchesCurrentContext: usage.measurementMatchesCurrentContext,
75885
+ autoCompactEnabled,
75725
75886
  autoCompactLimitTokens: autoCompactLimit,
75726
75887
  reserveTokens,
75727
75888
  promptPrefixHash: prefixHash,
@@ -76553,7 +76714,9 @@ async function handleClearCommand(host) {
76553
76714
  outputTokens: contextUsage.output,
76554
76715
  cacheRead: contextUsage.cacheRead,
76555
76716
  cacheWrite: contextUsage.cacheWrite,
76717
+ cacheWriteLong: contextUsage.cacheWriteLong,
76556
76718
  cost: contextUsage.cost,
76719
+ costEstimated: contextUsage.costEstimated,
76557
76720
  contextPercent: contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0,
76558
76721
  contextWindow
76559
76722
  });
@@ -76916,11 +77079,16 @@ async function handleCompactCommand(host) {
76916
77079
  host.showStatus("Nothing to compact yet");
76917
77080
  return;
76918
77081
  }
76919
- const firstKeptEntryId = findFirstKeptEntrySummaryId(host.sessionManager.getActiveContextSummaries());
76920
- if (!firstKeptEntryId) {
77082
+ const preparation = prepareSessionCompaction({
77083
+ sessionManager: host.sessionManager,
77084
+ messages: existingMessages,
77085
+ budgetTokens: host.options.model?.contextWindow
77086
+ });
77087
+ if (preparation.entries.length === 0) {
76921
77088
  host.showStatus("Need at least one real user message before compacting");
76922
77089
  return;
76923
77090
  }
77091
+ const tokensBefore = calculateTotalTokens(preparation.messages);
76924
77092
  const compactOptions = {
76925
77093
  model: host.options.model,
76926
77094
  apiKey: host.options.apiKey,
@@ -76939,10 +77107,27 @@ async function handleCompactCommand(host) {
76939
77107
  if (beforeCompact?.summary) {
76940
77108
  summary = beforeCompact.summary;
76941
77109
  } else {
76942
- summary = await createCompactionSummary(existingMessages, compactOptions);
77110
+ summary = await createCompactionSummary(preparation.messages, compactOptions);
77111
+ }
77112
+ const verified = verifyPreparedCompactionCommit({
77113
+ preparation,
77114
+ sessionManager: host.sessionManager,
77115
+ messages: host.agent.state.messages,
77116
+ result: { summary, retainedTail: preparation.retainedTail.retainedTail, tokensBefore }
77117
+ });
77118
+ if (verified.ok === false) {
77119
+ if (verified.kind === "stale") {
77120
+ host.showStatus("Session changed during compaction; nothing was published");
77121
+ return;
77122
+ }
77123
+ throw new Error(`Compaction abandoned: ${verified.reason}`);
76943
77124
  }
76944
- const tokensBefore = calculateTotalTokens(existingMessages);
76945
- host.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore);
77125
+ host.sessionManager.appendCompactionCheckpoint({
77126
+ summary,
77127
+ retainedTail: verified.retainedTail,
77128
+ tokensBefore,
77129
+ excludeFailure: preparation.excludedFailure
77130
+ });
76946
77131
  const compactedMessages = host.sessionManager.getMessages();
76947
77132
  host.replaceAgentMessagesAndMarkSynced(compactedMessages);
76948
77133
  host.resetFileObservationState();
@@ -76967,7 +77152,9 @@ async function handleCompactCommand(host) {
76967
77152
  outputTokens: contextUsage.output,
76968
77153
  cacheRead: contextUsage.cacheRead,
76969
77154
  cacheWrite: contextUsage.cacheWrite,
77155
+ cacheWriteLong: contextUsage.cacheWriteLong,
76970
77156
  cost: contextUsage.cost,
77157
+ costEstimated: contextUsage.costEstimated,
76971
77158
  contextPercent,
76972
77159
  contextWindow
76973
77160
  });
@@ -79350,8 +79537,12 @@ var FooterComponent = class {
79350
79537
  if (state.inputTokens) statsParts.push(`\u2191${formatTokens(state.inputTokens)}`);
79351
79538
  if (state.outputTokens) statsParts.push(`\u2193${formatTokens(state.outputTokens)}`);
79352
79539
  if (state.cacheRead) statsParts.push(`R${formatTokens(state.cacheRead)}`);
79353
- if (state.cacheWrite) statsParts.push(`W${formatTokens(state.cacheWrite)}`);
79354
- if (state.cost) statsParts.push(`$${state.cost.toFixed(3)}`);
79540
+ const cacheWriteShort = state.cacheWrite ?? 0;
79541
+ const cacheWriteLong = state.cacheWriteLong ?? 0;
79542
+ const cacheWriteTotal = cacheWriteShort + cacheWriteLong;
79543
+ if (cacheWriteTotal) statsParts.push(`W${formatTokens(cacheWriteTotal)}`);
79544
+ if (cacheWriteLong) statsParts.push(`W1h${formatTokens(cacheWriteLong)}`);
79545
+ if (state.cost) statsParts.push(`${state.costEstimated ? "~" : ""}$${state.cost.toFixed(3)}`);
79355
79546
  if (state.cacheHitRate != null) {
79356
79547
  statsParts.push(theme.fg("dim", `ca ${(state.cacheHitRate * 100).toFixed(0)}%`));
79357
79548
  }
@@ -79501,13 +79692,23 @@ function calculateCliContextUsage(params) {
79501
79692
  const estimate = estimateContextTokens(messages);
79502
79693
  const usageMessage = estimate.lastUsageIndex !== null ? messages[estimate.lastUsageIndex] : void 0;
79503
79694
  const usage = usageMessage?.role === "assistant" ? usageMessage.usage : void 0;
79695
+ const hasProviderMeasurement = usage !== void 0 && estimate.lastUsageIndex !== null;
79504
79696
  return {
79505
79697
  tokens: estimate.tokens,
79698
+ estimatedCurrentTokens: estimate.tokens,
79699
+ providerMeasuredTokens: hasProviderMeasurement ? estimate.providerTokens : null,
79700
+ providerMeasuredAt: hasProviderMeasurement && usageMessage ? usageMessage.timestamp : null,
79701
+ // A provider total describes the context AT that response. Any trailing
79702
+ // tool/user message (or a compaction reset, which zeroes stale usage)
79703
+ // makes the two figures different snapshots, so no error may be shown.
79704
+ measurementMatchesCurrentContext: hasProviderMeasurement && estimate.lastUsageIndex === messages.length - 1,
79506
79705
  input: usage?.input ?? 0,
79507
79706
  output: usage?.output ?? 0,
79508
79707
  cacheRead: usage?.cacheRead ?? 0,
79509
79708
  cacheWrite: usage?.cacheWrite ?? 0,
79510
- cost: usage?.cost.total ?? 0
79709
+ cacheWriteLong: usage?.cacheWriteLong ?? 0,
79710
+ cost: usage?.cost.total ?? 0,
79711
+ costEstimated: usage?.cost?.cacheWriteLongEstimated === true
79511
79712
  };
79512
79713
  }
79513
79714
  function createCliAutoCompactDecision(params) {
@@ -79538,6 +79739,11 @@ function shouldCliAutoCompactTokens(params) {
79538
79739
  const tokenLimit = getAutoCompactTokenLimit(params.contextWindow, params.reserveTokens);
79539
79740
  return tokenLimit !== void 0 && params.tokens > tokenLimit;
79540
79741
  }
79742
+ function createCliCompactionBudget(params) {
79743
+ const contextWindow = params.contextWindow ?? 0;
79744
+ const reserveTokens = params.reserveTokens ?? DEFAULT_COMPACTION_CONFIG.reserveTokens;
79745
+ return () => getAutoCompactTokenLimit(contextWindow, reserveTokens);
79746
+ }
79541
79747
  async function createCliAutoCompactionResult(params) {
79542
79748
  if (params.context.signal?.aborted) {
79543
79749
  return { aborted: true };
@@ -79546,8 +79752,9 @@ async function createCliAutoCompactionResult(params) {
79546
79752
  if (!activeEntryId) {
79547
79753
  return { aborted: true };
79548
79754
  }
79549
- const firstKeptEntryId = findFirstKeptEntrySummaryId(params.context.sessionManager.getActiveContextSummaries());
79550
- if (!firstKeptEntryId) {
79755
+ const legacyCutId = params.context.preparation ? null : findFirstKeptEntrySummaryId(params.context.sessionManager.getActiveContextSummaries());
79756
+ const retainedTail = params.context.preparation ? params.context.preparation.retainedTail.retainedTail : legacyCutId ? { kind: "from-entry", entryId: legacyCutId } : void 0;
79757
+ if (!retainedTail) {
79551
79758
  return { aborted: true };
79552
79759
  }
79553
79760
  const compactOptions = {
@@ -79562,7 +79769,8 @@ async function createCliAutoCompactionResult(params) {
79562
79769
  }
79563
79770
  return {
79564
79771
  summary,
79565
- firstKeptEntryId,
79772
+ retainedTail,
79773
+ ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {},
79566
79774
  tokensBefore: calculateTotalTokens(params.context.messages)
79567
79775
  };
79568
79776
  }
@@ -79714,16 +79922,44 @@ function createCliRetryPolicy(isRetryableErrorOverride) {
79714
79922
  maxRetries: CLI_RETRY_MAX_RETRIES,
79715
79923
  baseDelayMs: getCliRetryBaseDelayMs(),
79716
79924
  maxDelayMs: getCliRetryMaxDelayMs(),
79717
- maxRetriesForFailure: (failure) => failure.kind === "transient_network" ? getCliTransientNetworkMaxRetries() : CLI_RETRY_MAX_RETRIES,
79718
- maxTotalRetries: getCliRetryTotalMaxRetries()
79925
+ maxRetriesForFailure: (failure) => failure.kind === "transient_network" ? getCliTransientNetworkMaxRetries() : failure.replayExempt === true ? getCliTimeoutReplayMaxRetries() : CLI_RETRY_MAX_RETRIES,
79926
+ maxTotalRetries: getCliRetryTotalMaxRetries(),
79927
+ systemicReplayStallLimit: getCliSystemicReplayStallLimit()
79719
79928
  };
79720
79929
  }
79930
+ var CLI_TIMEOUT_REPLAY_MAX_RETRIES = 5;
79931
+ function getCliTimeoutReplayMaxRetries() {
79932
+ const override = process.env.PIE_TIMEOUT_REPLAY_MAX_RETRIES;
79933
+ if (!override) return CLI_TIMEOUT_REPLAY_MAX_RETRIES;
79934
+ const parsed = Number(override);
79935
+ return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : CLI_TIMEOUT_REPLAY_MAX_RETRIES;
79936
+ }
79937
+ var CLI_SYSTEMIC_REPLAY_STALL_LIMIT = 3;
79938
+ function getCliSystemicReplayStallLimit() {
79939
+ const override = process.env.PIE_SYSTEMIC_REPLAY_STALL_LIMIT;
79940
+ if (!override) return CLI_SYSTEMIC_REPLAY_STALL_LIMIT;
79941
+ const parsed = Number(override);
79942
+ return Number.isFinite(parsed) && parsed >= 1 ? Math.floor(parsed) : CLI_SYSTEMIC_REPLAY_STALL_LIMIT;
79943
+ }
79721
79944
 
79722
79945
  // src/runtime/interactive-context-ui.ts
79723
79946
  function calculateContextUsage(host) {
79724
79947
  const messages = host.agent?.state.messages ?? [];
79725
79948
  if (messages.length === 0) {
79726
- return { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
79949
+ return {
79950
+ tokens: 0,
79951
+ estimatedCurrentTokens: 0,
79952
+ providerMeasuredTokens: null,
79953
+ providerMeasuredAt: null,
79954
+ measurementMatchesCurrentContext: false,
79955
+ input: 0,
79956
+ output: 0,
79957
+ cacheRead: 0,
79958
+ cacheWrite: 0,
79959
+ cacheWriteLong: 0,
79960
+ cost: 0,
79961
+ costEstimated: false
79962
+ };
79727
79963
  }
79728
79964
  return calculateCliContextUsage({
79729
79965
  messages,
@@ -80474,6 +80710,9 @@ function createInteractiveAgentSessionController(host) {
80474
80710
  }
80475
80711
  return void 0;
80476
80712
  },
80713
+ // Same auto-compact limit the threshold decision uses; a checkpoint whose
80714
+ // summary + retained tail would still exceed it is refused.
80715
+ getCompactionBudgetTokens: createCliCompactionBudget({ contextWindow: host.options.model?.contextWindow }),
80477
80716
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
80478
80717
  classifyFailure: (context) => classifyCliFailure(context),
80479
80718
  retry: createCliRetryPolicy(
@@ -80491,9 +80730,16 @@ function createInteractiveAgentSessionController(host) {
80491
80730
  return { aborted: true };
80492
80731
  }
80493
80732
  if (beforeCompact?.summary) {
80494
- const firstKeptEntryId = findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries());
80495
- if (!firstKeptEntryId) return { aborted: true };
80496
- return { summary: beforeCompact.summary, firstKeptEntryId };
80733
+ const retainedTail = context.preparation?.retainedTail.retainedTail ?? (() => {
80734
+ const legacyCutId = findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries());
80735
+ return legacyCutId ? { kind: "from-entry", entryId: legacyCutId } : void 0;
80736
+ })();
80737
+ if (!retainedTail) return { aborted: true };
80738
+ return {
80739
+ summary: beforeCompact.summary,
80740
+ retainedTail,
80741
+ ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {}
80742
+ };
80497
80743
  }
80498
80744
  autoCompactBeforeStats = summarizeActiveContext(context.sessionManager.getActiveContextSummaries());
80499
80745
  return createCliAutoCompactionResult({
@@ -80655,6 +80901,13 @@ function createInteractiveAgentSessionController(host) {
80655
80901
  host.isProcessing = true;
80656
80902
  host.footer.setState({ status: "processing", lifecycleEvent: "auto_compact" });
80657
80903
  host.showStatus(event.reason === "overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context...");
80904
+ } else if (event.type === "compaction_committed") {
80905
+ host.resetFileObservationState();
80906
+ host.resetRuntimeModeBlocks?.();
80907
+ host.sessionTrace.noteRuntimeEvent("compaction_committed", {
80908
+ reason: event.reason,
80909
+ willRetry: event.willRetry
80910
+ });
80658
80911
  } else if (event.type === "auto_compaction_end") {
80659
80912
  host.isCompacting = false;
80660
80913
  host.isProcessing = false;
@@ -80667,8 +80920,6 @@ function createInteractiveAgentSessionController(host) {
80667
80920
  autoCompactBeforeStats = null;
80668
80921
  return;
80669
80922
  }
80670
- host.resetFileObservationState();
80671
- host.resetRuntimeModeBlocks?.();
80672
80923
  if (!event.result?.aborted) {
80673
80924
  host.streamingComponent = void 0;
80674
80925
  host.pendingTools.clear();
@@ -80687,7 +80938,9 @@ function createInteractiveAgentSessionController(host) {
80687
80938
  outputTokens: contextUsage.output,
80688
80939
  cacheRead: contextUsage.cacheRead,
80689
80940
  cacheWrite: contextUsage.cacheWrite,
80941
+ cacheWriteLong: contextUsage.cacheWriteLong,
80690
80942
  cost: contextUsage.cost,
80943
+ costEstimated: contextUsage.costEstimated,
80691
80944
  contextPercent,
80692
80945
  contextWindow
80693
80946
  });
@@ -80843,20 +81096,80 @@ function currentDateInZone(timeZone) {
80843
81096
  const part = (type) => parts.find((p) => p.type === type)?.value ?? "00";
80844
81097
  return `${part("year")}-${part("month")}-${part("day")}`;
80845
81098
  }
80846
- var RUNTIME_MODE_BLOCK_PATTERN = /^[\t ]*<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>[\t ]*(?:\r?\n){0,2}/;
81099
+ var RUNTIME_MODE_BLOCK_START_MARKER = "<runtime-mode-context>";
81100
+ function leadingRuntimeBlock(text) {
81101
+ const withoutLeadingWhitespace = text.replace(/^[\t ]+/, "");
81102
+ if (!withoutLeadingWhitespace.startsWith(RUNTIME_MODE_BLOCK_START_MARKER)) return null;
81103
+ const markerStart = text.length - withoutLeadingWhitespace.length;
81104
+ const endIndex = text.indexOf(RUNTIME_MODE_CONTEXT_END_MARKER, markerStart);
81105
+ if (endIndex < 0) return null;
81106
+ const block = text.slice(markerStart, endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length);
81107
+ const rest = text.slice(endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length).replace(/^[\t ]*(?:\r?\n){0,2}/, "");
81108
+ return { block, rest };
81109
+ }
81110
+ function stripLeadingRuntimeBlock(text) {
81111
+ const leading = leadingRuntimeBlock(text);
81112
+ return leading ? { rest: leading.rest } : null;
81113
+ }
81114
+ function leadingRuntimeBlockOfMessage(message) {
81115
+ if (message.role !== "user") return null;
81116
+ const content = message.content;
81117
+ if (typeof content === "string") return leadingRuntimeBlock(content)?.block ?? null;
81118
+ if (!Array.isArray(content)) return null;
81119
+ const first = content[0];
81120
+ if (first?.type !== "text" || typeof first.text !== "string") return null;
81121
+ return leadingRuntimeBlock(first.text)?.block ?? null;
81122
+ }
81123
+ function lastUserMessageCarriesRuntimeBlock(messages, block) {
81124
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
81125
+ const message = messages[index];
81126
+ if (message?.role !== "user") continue;
81127
+ return leadingRuntimeBlockOfMessage(message) === block;
81128
+ }
81129
+ return false;
81130
+ }
81131
+ function applyRuntimeBlockToText(text, block) {
81132
+ const split = stripLeadingRuntimeBlock(text);
81133
+ const rest = split ? split.rest : text;
81134
+ const next = rest.length > 0 ? `${block}
81135
+
81136
+ ${rest}` : block;
81137
+ return next === text ? text : next;
81138
+ }
81139
+ function applyRuntimeBlockToContentArray(content, block) {
81140
+ const first = content[0];
81141
+ if (first && typeof first === "object" && first.type === "text" && typeof first.text === "string") {
81142
+ const text = first.text;
81143
+ if (stripLeadingRuntimeBlock(text)) {
81144
+ const nextText = applyRuntimeBlockToText(text, block);
81145
+ if (nextText === text) return content;
81146
+ const copy = [...content];
81147
+ copy[0] = { ...first, text: nextText };
81148
+ return copy;
81149
+ }
81150
+ }
81151
+ return [{ type: "text", text: block }, ...content];
81152
+ }
80847
81153
  function refreshRuntimeModeBlockOnLastUserMessage(messages, block) {
80848
81154
  for (let i = messages.length - 1; i >= 0; i--) {
80849
81155
  const message = messages[i];
80850
81156
  if (message?.role !== "user") continue;
80851
- const content = typeof message.content === "string" ? message.content : "";
80852
- const stripped = content.replace(RUNTIME_MODE_BLOCK_PATTERN, "");
80853
- const refreshed = stripped === content && content.includes("<runtime-mode-context>") ? content : `${block}
80854
-
80855
- ${stripped}`;
80856
- if (refreshed === content) return messages;
80857
- const next = [...messages];
80858
- next[i] = { ...message, content: refreshed };
80859
- return next;
81157
+ const content = message.content;
81158
+ if (typeof content === "string") {
81159
+ const next = applyRuntimeBlockToText(content, block);
81160
+ if (next === content) return messages;
81161
+ const copy = [...messages];
81162
+ copy[i] = { ...message, content: next };
81163
+ return copy;
81164
+ }
81165
+ if (Array.isArray(content)) {
81166
+ const next = applyRuntimeBlockToContentArray(content, block);
81167
+ if (next === content) return messages;
81168
+ const copy = [...messages];
81169
+ copy[i] = { ...message, content: next };
81170
+ return copy;
81171
+ }
81172
+ return messages;
80860
81173
  }
80861
81174
  return messages;
80862
81175
  }
@@ -80866,28 +81179,54 @@ var RuntimeModeBlockInjector = class {
80866
81179
  }
80867
81180
  options;
80868
81181
  lastKey = null;
80869
- pendingRetryRefresh = false;
81182
+ /**
81183
+ * Request-view overlay. While active, the CURRENT block is re-derived on
81184
+ * every request from the live message snapshot and applied to the latest
81185
+ * user message. It is not cached into history and it ends only when a real
81186
+ * user message freezes the current block (see `nextBlock` + the transform),
81187
+ * so consecutive tool rounds after a compaction retry keep the fresh state
81188
+ * instead of losing it after one request.
81189
+ */
81190
+ overlayActive = false;
80870
81191
  reset() {
80871
81192
  this.lastKey = null;
80872
- this.pendingRetryRefresh = true;
81193
+ this.overlayActive = true;
81194
+ }
81195
+ /**
81196
+ * Arm the request-view overlay without resetting the frozen-block key. Used
81197
+ * when the runtime shape changed while a turn is still running (compaction
81198
+ * checkpoint committed, plan approval, YOLO toggle inside a tool turn): the
81199
+ * next and subsequent model requests must see the current state even though
81200
+ * no new user message has been accepted yet.
81201
+ */
81202
+ armRefresh() {
81203
+ this.overlayActive = true;
80873
81204
  }
80874
- /** Block to prepend to the next user message, or null when unchanged. */
81205
+ /**
81206
+ * Block to prepend to the next user message, or null when unchanged. Does
81207
+ * NOT end the overlay: the block is only proof once the message carrying it
81208
+ * reaches the request transform.
81209
+ */
80875
81210
  nextBlock() {
80876
- const block = this.freshBlock();
80877
- this.pendingRetryRefresh = false;
80878
- return block;
81211
+ return this.freshBlock();
80879
81212
  }
80880
81213
  /**
80881
- * Latest block for a request that reuses existing history (overflow retry
80882
- * after compaction, manual compaction continue): the last user message may
80883
- * carry a stale or evicted block, so the request-level transform refreshes
80884
- * it. Consumed once per reset; null when nothing is pending.
81214
+ * Current block for a request that reuses existing history (overflow retry
81215
+ * after compaction, manual-compact continue, or the tool rounds that follow
81216
+ * a mid-turn mode change). Returns the same block for every request until
81217
+ * the latest user message itself carries that block; then the overlay is
81218
+ * retired and null is returned. Derivation is per request from the given
81219
+ * snapshot; no message array is cached.
80885
81220
  */
80886
- consumeRetryRefreshBlock() {
80887
- if (!this.pendingRetryRefresh) return null;
80888
- this.pendingRetryRefresh = false;
81221
+ consumeRetryRefreshBlock(messages) {
81222
+ if (!this.overlayActive) return null;
80889
81223
  const state = this.options.getState();
80890
- return buildRuntimeModeContext(state);
81224
+ const block = buildRuntimeModeContext(state);
81225
+ if (messages && lastUserMessageCarriesRuntimeBlock(messages, block)) {
81226
+ this.overlayActive = false;
81227
+ return null;
81228
+ }
81229
+ return block;
80891
81230
  }
80892
81231
  freshBlock() {
80893
81232
  const state = this.options.getState();
@@ -80900,14 +81239,17 @@ var RuntimeModeBlockInjector = class {
80900
81239
  };
80901
81240
 
80902
81241
  // src/runtime/cache-retention.ts
80903
- function createCacheRetentionStreamFn(configured) {
81242
+ function createCacheRetentionStreamFn(configured, getConfiguredRetention) {
80904
81243
  const base = configured ?? ((model, context, options) => streamSimple(model, context, options));
80905
- return (model, context, options) => base(model, context, {
80906
- ...options,
80907
- // Default only: explicit none/short requests (tests, debug commands) win.
80908
- // Note: cost estimates still price cache writes at the short-TTL rate.
80909
- cacheRetention: model.api === "anthropic-messages" && options?.cacheRetention === void 0 ? "long" : options?.cacheRetention
80910
- });
81244
+ return (model, context, options) => {
81245
+ const requested = options?.cacheRetention;
81246
+ const configuredRetention = model.api === "anthropic-messages" ? getConfiguredRetention?.() : void 0;
81247
+ const cacheRetention = requested ?? configuredRetention;
81248
+ return base(model, context, {
81249
+ ...options,
81250
+ ...cacheRetention === void 0 ? {} : { cacheRetention }
81251
+ });
81252
+ };
80911
81253
  }
80912
81254
 
80913
81255
  // src/runtime/guard-advisory-context.ts
@@ -80931,7 +81273,7 @@ function findLatestUserIndex(messages) {
80931
81273
  function applyRuntimeRequestTransform(messages, options) {
80932
81274
  const advisory = options.takeAdvisory();
80933
81275
  let next = advisory ? injectGuardAdvisory(messages, advisory) : stripGuardAdvisories(messages);
80934
- const retryBlock = options.takeRetryBlock();
81276
+ const retryBlock = options.takeRetryBlock(next);
80935
81277
  if (retryBlock) next = refreshRuntimeModeBlockOnLastUserMessage(next, retryBlock);
80936
81278
  return next;
80937
81279
  }
@@ -81814,9 +82156,12 @@ async function loadInteractiveExtensions(host) {
81814
82156
  const activeTools = [...host.agent.state.tools, ...extensionTools];
81815
82157
  host.agent.state.tools = wrapToolsWithExtensions(activeTools, host.extensionRunner);
81816
82158
  const availableTools = [
81817
- ...host.allTools.length > 0 ? host.allTools : host.originalTools.length > 0 ? host.originalTools : host.agent.state.tools,
82159
+ // Prefer the pristine (extension-unwrapped) assembly; re-deriving from
82160
+ // already extension-wrapped `allTools` would nest interceptors.
82161
+ ...host.pristineTools && host.pristineTools.length > 0 ? host.pristineTools : host.allTools.length > 0 ? host.allTools : host.originalTools.length > 0 ? host.originalTools : host.agent.state.tools,
81818
82162
  ...extensionTools
81819
82163
  ];
82164
+ host.pristineTools = [...availableTools];
81820
82165
  host.allTools = wrapToolsWithExtensions(availableTools, host.extensionRunner);
81821
82166
  host.originalTools = [...host.allTools];
81822
82167
  if (host.options.compositionTrace) {
@@ -81899,6 +82244,8 @@ var InteractiveMode = class {
81899
82244
  mentionPickerOptions = [];
81900
82245
  attachmentResolver;
81901
82246
  yoloMode = false;
82247
+ pristineTools = [];
82248
+ // Pristine (extension-unwrapped) tool assembly: the mode-rebuild source.
81902
82249
  /** Calm mode: render only questions and answers (display-only; persisted). */
81903
82250
  calmMode = false;
81904
82251
  originalTools = [];
@@ -82064,7 +82411,7 @@ var InteractiveMode = class {
82064
82411
  const preservedTools = sourceTools.filter(
82065
82412
  (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash"
82066
82413
  );
82067
- const bashTool = createBashTool(this.options.cwd, { isYoloMode: () => this.yoloMode });
82414
+ const bashTool = createBashTool(this.options.cwd, { isYoloMode: () => this.yoloMode, getEnv: () => computerUseOwnerEnv(this.sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, String(process.pid)) });
82068
82415
  const fileSystemToolOptions = (allowlistedDirs) => ({
82069
82416
  ...buildCliFileAccessOptions({
82070
82417
  cwd: this.options.cwd,
@@ -82134,7 +82481,7 @@ var InteractiveMode = class {
82134
82481
  }
82135
82482
  applyRuntimeToolMode(sourceTools) {
82136
82483
  if (!this.agent) return;
82137
- const completeToolSet = sourceTools ?? (this.allTools.length > 0 ? this.allTools : void 0) ?? (this.originalTools.length > 0 ? this.originalTools : void 0) ?? this.agent.state.tools;
82484
+ const completeToolSet = sourceTools ?? ((this.pristineTools?.length ?? 0) > 0 ? this.pristineTools : this.allTools) ?? this.agent.state.tools;
82138
82485
  const rebuilt = this.rebuildSkillAwareTools(completeToolSet);
82139
82486
  const currentTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.currentTools, this.extensionRunner) : rebuilt.currentTools;
82140
82487
  const originalTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.originalTools, this.extensionRunner) : rebuilt.originalTools;
@@ -82156,6 +82503,7 @@ var InteractiveMode = class {
82156
82503
  });
82157
82504
  }
82158
82505
  noteRuntimeModeChanged(trigger) {
82506
+ this.runtimeModeBlocks?.armRefresh();
82159
82507
  const state = this.getRuntimeModeState();
82160
82508
  this.sessionTrace.noteRuntimeEvent("runtime_mode_changed", {
82161
82509
  trigger,
@@ -82214,7 +82562,7 @@ var InteractiveMode = class {
82214
82562
  async init() {
82215
82563
  const activeSession = this.sessionManager.getActiveSession();
82216
82564
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
82217
- const { Agent } = await import("./chunks/src-E7NCXMU5.js");
82565
+ const { Agent } = await import("./chunks/src-PRKTGADX.js");
82218
82566
  this.agent = new Agent({
82219
82567
  initialState: {
82220
82568
  systemPrompt: this.buildSystemPrompt(),
@@ -82224,14 +82572,14 @@ var InteractiveMode = class {
82224
82572
  },
82225
82573
  apiKey: this.options.apiKey,
82226
82574
  sessionId: activeSession?.id,
82227
- streamFn: createCacheRetentionStreamFn(this.options.streamFn),
82575
+ streamFn: createTransportDiagnosticsStreamFn(createCacheRetentionStreamFn(this.options.streamFn, () => this.options.settingsManager.getPromptCacheRetention()), createTraceTransportSink((type, meta) => this.sessionTrace.noteRuntimeEvent(type, meta))),
82228
82576
  transformContext: async (messages) => applyRuntimeRequestTransform(messages, {
82229
82577
  takeAdvisory: () => {
82230
82578
  const advisory = this.pendingRuntimeGuardAdvisory;
82231
82579
  this.pendingRuntimeGuardAdvisory = null;
82232
82580
  return advisory;
82233
82581
  },
82234
- takeRetryBlock: () => this.runtimeModeBlocks.consumeRetryRefreshBlock()
82582
+ takeRetryBlock: (snapshot) => this.runtimeModeBlocks.consumeRetryRefreshBlock(snapshot)
82235
82583
  })
82236
82584
  });
82237
82585
  this.agentSessionController = createInteractiveAgentSessionController(this.controllerSetupHost);
@@ -82324,6 +82672,7 @@ var InteractiveMode = class {
82324
82672
  }
82325
82673
  this.allTools = [...this.options.allTools];
82326
82674
  this.originalTools = [...this.options.allTools];
82675
+ this.pristineTools = [...this.options.allTools];
82327
82676
  this.agent.state.tools = [...this.options.allTools];
82328
82677
  await this.extensionRunner?.dispose("reload");
82329
82678
  this.extensionWidgets?.clearAll();
@@ -82620,6 +82969,7 @@ var InteractiveMode = class {
82620
82969
 
82621
82970
  // src/app/runtime-composition.ts
82622
82971
  import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
82972
+ import { randomUUID } from "crypto";
82623
82973
  import * as path31 from "path";
82624
82974
  var CliMissingApiKeyError = class extends Error {
82625
82975
  constructor(message) {
@@ -82749,6 +83099,7 @@ async function composeCliRuntime(params) {
82749
83099
  await sessionManager.save();
82750
83100
  }
82751
83101
  const sessionId = sessionManager.getActiveSession()?.id;
83102
+ const computerUseExecutionId = randomUUID();
82752
83103
  const fileObservationState = createFileObservationState();
82753
83104
  if (sessionId) {
82754
83105
  if (process.env.PIE_DEBUG && !fileLogger.isEnabled()) {
@@ -82777,7 +83128,8 @@ async function composeCliRuntime(params) {
82777
83128
  getWebSearchMode: () => settingsManager.getWebSearchMode(),
82778
83129
  resolveToolModel: (purpose) => modelRegistry.resolveToolModel(purpose, getCurrentModel()),
82779
83130
  resolveToolModelCandidates: (purpose) => modelRegistry.resolveToolModelCandidates(purpose, getCurrentModel()),
82780
- configDir: paths.configDir
83131
+ configDir: paths.configDir,
83132
+ getBashEnv: () => computerUseOwnerEnv(sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, computerUseExecutionId)
82781
83133
  });
82782
83134
  const fileSystemToolOptions = {
82783
83135
  ...buildCliFileAccessOptions({ cwd, configDir: paths.configDir, skills, allowlistedDirs: [paths.configDir] }),
@@ -82905,6 +83257,38 @@ function createPrintModeRetryStatusReporter() {
82905
83257
  };
82906
83258
  }
82907
83259
 
83260
+ // src/app/print-mode-compaction.ts
83261
+ function createPrintModeCompactionHandler(params) {
83262
+ return {
83263
+ compact: async (context) => {
83264
+ const beforeCompact = await params.runner.emitSessionBeforeCompact({
83265
+ type: "session:before_compact",
83266
+ reason: context.reason === "overflow" ? "overflow" : "threshold",
83267
+ messageCount: context.messages.length
83268
+ });
83269
+ if (beforeCompact.cancel) return { aborted: true };
83270
+ if (beforeCompact.summary) {
83271
+ const retainedTail = context.preparation?.retainedTail.retainedTail ?? (() => {
83272
+ const legacyCutId = findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries());
83273
+ return legacyCutId ? { kind: "from-entry", entryId: legacyCutId } : void 0;
83274
+ })();
83275
+ if (!retainedTail) return { aborted: true };
83276
+ return {
83277
+ summary: beforeCompact.summary,
83278
+ retainedTail,
83279
+ ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {}
83280
+ };
83281
+ }
83282
+ return createCliAutoCompactionResult({
83283
+ context,
83284
+ model: params.model,
83285
+ apiKey: params.apiKey,
83286
+ systemPrompt: params.systemPrompt
83287
+ });
83288
+ }
83289
+ };
83290
+ }
83291
+
82908
83292
  // src/app/print-mode.ts
82909
83293
  function createNoOpExtensionUIContext() {
82910
83294
  return {
@@ -83036,7 +83420,7 @@ async function runPrintMode(params) {
83036
83420
  `);
83037
83421
  console.log("Assistant: ");
83038
83422
  }
83039
- const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
83423
+ const { Agent: AgentClass } = await import("./chunks/src-PRKTGADX.js");
83040
83424
  let agent;
83041
83425
  const printModeExtensions = await loadPrintModeExtensions({
83042
83426
  cwd: runtime.cwd,
@@ -83080,7 +83464,8 @@ async function runPrintMode(params) {
83080
83464
  thinkingLevel: printThinkingLevel
83081
83465
  },
83082
83466
  apiKey: runtime.apiKey,
83083
- streamFn: params.streamFn
83467
+ // Body-free transport timeline for the main (print-mode) agent.
83468
+ streamFn: createTransportDiagnosticsStreamFn(params.streamFn ?? streamSimple, logTransportLifecycle)
83084
83469
  });
83085
83470
  const sessionTrace = new SessionTraceWriter(runtime.paths.sessionsDir, runtime.sessionManager);
83086
83471
  const reportRetryStatus = createPrintModeRetryStatusReporter();
@@ -83114,28 +83499,16 @@ async function runPrintMode(params) {
83114
83499
  latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
83115
83500
  isContextOverflowError: isCliContextOverflowError
83116
83501
  }),
83117
- compaction: {
83118
- compact: async (context) => {
83119
- const beforeCompact = await printModeExtensions.runner.emitSessionBeforeCompact({
83120
- type: "session:before_compact",
83121
- reason: context.reason === "overflow" ? "overflow" : "threshold",
83122
- messageCount: context.messages.length
83123
- });
83124
- if (beforeCompact.cancel) return { aborted: true };
83125
- if (beforeCompact.summary) {
83126
- const activeEntryId = context.sessionManager.getActiveEntryId();
83127
- const firstKeptEntryId = activeEntryId ? findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries()) : void 0;
83128
- if (!firstKeptEntryId) return { aborted: true };
83129
- return { summary: beforeCompact.summary, firstKeptEntryId };
83130
- }
83131
- return createCliAutoCompactionResult({
83132
- context,
83133
- model: runtime.model,
83134
- apiKey: runtime.apiKey,
83135
- systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt
83136
- });
83137
- }
83138
- },
83502
+ getCompactionBudgetTokens: createCliCompactionBudget({
83503
+ contextWindow: runtime.model.contextWindow,
83504
+ reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
83505
+ }),
83506
+ compaction: createPrintModeCompactionHandler({
83507
+ runner: printModeExtensions.runner,
83508
+ model: runtime.model,
83509
+ apiKey: runtime.apiKey,
83510
+ systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt
83511
+ }),
83139
83512
  classifyFailure: (context) => classifyCliFailure(context),
83140
83513
  onEvent: (event) => {
83141
83514
  sessionTrace.noteRuntimeEvent(event.type, event);
@@ -83143,7 +83516,7 @@ async function runPrintMode(params) {
83143
83516
  console.error(event.failure.kind === "context_overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context before retry...");
83144
83517
  } else if (event.type === "auto_compaction_start") {
83145
83518
  console.error(event.reason === "overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context...");
83146
- } else if (event.type === "auto_compaction_end" && !event.errorMessage) {
83519
+ } else if (event.type === "compaction_committed") {
83147
83520
  runtime.fileObservationState.clear();
83148
83521
  } else {
83149
83522
  reportRetryStatus(event);
@@ -83497,6 +83870,9 @@ async function loadJsonModeExtensions(params) {
83497
83870
  extensionCount: extensions.length
83498
83871
  };
83499
83872
  }
83873
+ function createJsonEventsTransportStreamFn(streamFn) {
83874
+ return createTransportDiagnosticsStreamFn(streamFn ?? streamSimple, logTransportLifecycle);
83875
+ }
83500
83876
  async function runJsonEventsMode(params) {
83501
83877
  const eventDetail = params.eventDetail === FULL_DETAIL ? FULL_DETAIL : COMPACT_DETAIL;
83502
83878
  const eventStringCap = eventDetail === FULL_DETAIL ? MAX_EVENT_STRING_LENGTH : COMPACT_EVENT_STRING_LENGTH;
@@ -83897,7 +84273,7 @@ async function runJsonEventsMode(params) {
83897
84273
  return;
83898
84274
  }
83899
84275
  const parent = agent;
83900
- const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
84276
+ const { Agent: AgentClass } = await import("./chunks/src-PRKTGADX.js");
83901
84277
  const sideAgent = new AgentClass({
83902
84278
  initialState: {
83903
84279
  systemPrompt: parent.state.systemPrompt,
@@ -83907,7 +84283,11 @@ async function runJsonEventsMode(params) {
83907
84283
  messages: structuredClone(parent.state.messages)
83908
84284
  },
83909
84285
  apiKey: runtime.apiKey,
83910
- streamFn: params.streamFn
84286
+ // Same body-free transport timeline as the main agent: a sidechat is
84287
+ // an independent Responses request and previously had no record at all
84288
+ // when it stalled. `streamSimple` matches the host default when the
84289
+ // caller injected no stream function.
84290
+ streamFn: createJsonEventsTransportStreamFn(params.streamFn)
83911
84291
  });
83912
84292
  sidechatController.signal.addEventListener("abort", () => sideAgent.abort(), { once: true });
83913
84293
  const timer = setTimeout(() => {
@@ -84065,7 +84445,7 @@ async function runJsonEventsMode(params) {
84065
84445
  }
84066
84446
  function handleControllerEvent(event) {
84067
84447
  trace?.noteRuntimeEvent(event.type, event);
84068
- if (event.type === "auto_compaction_end" && !event.errorMessage) {
84448
+ if (event.type === "compaction_committed") {
84069
84449
  runtime?.fileObservationState.clear();
84070
84450
  }
84071
84451
  if (event.type === "turn_settled" && activeTurn) {
@@ -84241,7 +84621,7 @@ async function runJsonEventsMode(params) {
84241
84621
  if (runtime.initialModel.unconfigured || controller) {
84242
84622
  return;
84243
84623
  }
84244
- const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
84624
+ const { Agent: AgentClass } = await import("./chunks/src-PRKTGADX.js");
84245
84625
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
84246
84626
  extensionRunner = jsonModeExtensions.runner;
84247
84627
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
@@ -84273,7 +84653,8 @@ async function runJsonEventsMode(params) {
84273
84653
  thinkingLevel
84274
84654
  },
84275
84655
  apiKey: runtime.apiKey,
84276
- streamFn: params.streamFn
84656
+ // Body-free transport timeline for the main (JSON-events) agent.
84657
+ streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84277
84658
  });
84278
84659
  controller = new AgentSessionController({
84279
84660
  agent,
@@ -84302,6 +84683,10 @@ async function runJsonEventsMode(params) {
84302
84683
  latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
84303
84684
  isContextOverflowError: isCliContextOverflowError
84304
84685
  }),
84686
+ getCompactionBudgetTokens: createCliCompactionBudget({
84687
+ contextWindow: runtime.model.contextWindow,
84688
+ reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
84689
+ }),
84305
84690
  compaction: {
84306
84691
  compact: async (context) => createCliAutoCompactionResult({
84307
84692
  context,
@@ -84835,7 +85220,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
84835
85220
  }
84836
85221
  async function createCliTestStreamFnFromEnv() {
84837
85222
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
84838
- return (await import("./chunks/test-stream-BWBJTFNI.js")).createTestStreamFnFromSequenceEnv();
85223
+ return (await import("./chunks/test-stream-BGAEVPRX.js")).createTestStreamFnFromSequenceEnv();
84839
85224
  }
84840
85225
  function resolveJsonEventsDetailFlag() {
84841
85226
  for (let index = 0; index < process.argv.length; index += 1) {