@cydm/pie 2.7.0 → 2.7.1

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-W74NRSRV.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-M44CWVDZ.js";
90
+ import "./chunks/chunk-NJFPBU36.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-XUROMYTT.js";
110
116
  import {
111
117
  IsArray,
112
118
  IsBigInt,
@@ -52576,8 +52582,8 @@ function validateRemoteModel(profileId, model) {
52576
52582
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid input capabilities`);
52577
52583
  }
52578
52584
  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)) {
52585
+ assertOnlyKeys(model.cost, ["input", "output", "cacheRead", "cacheWrite", "cacheWriteLong"], `Remote profile "${profileId}" model "${model.id}" cost`);
52586
+ 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
52587
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid cost values`);
52582
52588
  }
52583
52589
  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 +52695,8 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52689
52695
  input: model.cost.input,
52690
52696
  output: model.cost.output,
52691
52697
  cacheRead: model.cost.cacheRead,
52692
- cacheWrite: model.cost.cacheWrite
52698
+ cacheWrite: model.cost.cacheWrite,
52699
+ ...model.cost.cacheWriteLong === void 0 ? {} : { cacheWriteLong: model.cost.cacheWriteLong }
52693
52700
  },
52694
52701
  contextWindow: model.contextWindow,
52695
52702
  maxTokens: model.maxTokens,
@@ -52756,8 +52763,8 @@ function validateModelOverrides(value) {
52756
52763
  if (override.maxTokens !== void 0 && (!Number.isInteger(override.maxTokens) || Number(override.maxTokens) < 1)) throw new Error(`models.json modelOverrides["${key}"].maxTokens is invalid`);
52757
52764
  if (override.cost !== void 0) {
52758
52765
  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`);
52766
+ for (const field of ["input", "output", "cacheRead", "cacheWrite", "cacheWriteLong"]) {
52767
+ 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
52768
  }
52762
52769
  }
52763
52770
  if (override.headers !== void 0) {
@@ -53856,6 +53863,16 @@ var SettingsManager = class {
53856
53863
  this.settings.autoCompactReserveTokens = tokens;
53857
53864
  this.writeSettings();
53858
53865
  }
53866
+ /** Undefined means "no CLI override": the provider default applies. */
53867
+ getPromptCacheRetention() {
53868
+ const retention = this.settings.promptCacheRetention;
53869
+ return retention === "none" || retention === "short" || retention === "long" ? retention : void 0;
53870
+ }
53871
+ setPromptCacheRetention(retention) {
53872
+ if (retention === void 0) delete this.settings.promptCacheRetention;
53873
+ else this.settings.promptCacheRetention = retention;
53874
+ this.writeSettings();
53875
+ }
53859
53876
  getWebSearchMode() {
53860
53877
  const mode = this.settings.tools?.webSearch?.mode;
53861
53878
  if (mode === "disabled" || mode === "cached" || mode === "live") {
@@ -55575,10 +55592,11 @@ async function forkStoredSession(params) {
55575
55592
  timestamp: entry.timestamp
55576
55593
  };
55577
55594
  if (copy.compactionData) {
55595
+ const selfAnchored = copy.compactionData.firstKeptEntryId === entry.id;
55578
55596
  const keptId = idMap.get(copy.compactionData.firstKeptEntryId);
55579
55597
  copy.compactionData = {
55580
55598
  ...copy.compactionData,
55581
- firstKeptEntryId: keptId ?? firstCopiedId ?? copy.id
55599
+ firstKeptEntryId: selfAnchored ? copy.id : keptId ?? firstCopiedId ?? copy.id
55582
55600
  };
55583
55601
  }
55584
55602
  idMap.set(summary.id, copy.id);
@@ -65073,6 +65091,23 @@ function buildCliFileAccessOptions(params) {
65073
65091
  };
65074
65092
  }
65075
65093
 
65094
+ // src/runtime/computer-use-owner.ts
65095
+ var COMPUTER_USE_OWNER_ENV = "PIE_COMPUTER_USE_OWNER_ID";
65096
+ var COMPUTER_USE_EXECUTION_ENV = "PIE_COMPUTER_USE_EXECUTION_ID";
65097
+ var COMPUTER_USE_STANDALONE_OWNER = "standalone";
65098
+ var OWNER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:@-]{0,255}$/;
65099
+ function normalizeComputerUseOwner(value) {
65100
+ if (typeof value !== "string") return COMPUTER_USE_STANDALONE_OWNER;
65101
+ const trimmed = value.trim();
65102
+ if (!trimmed) return COMPUTER_USE_STANDALONE_OWNER;
65103
+ return OWNER_PATTERN.test(trimmed) ? trimmed : COMPUTER_USE_STANDALONE_OWNER;
65104
+ }
65105
+ function computerUseOwnerEnv(ownerId, executionId) {
65106
+ const env = { [COMPUTER_USE_OWNER_ENV]: normalizeComputerUseOwner(ownerId) };
65107
+ if (executionId) env[COMPUTER_USE_EXECUTION_ENV] = executionId;
65108
+ return env;
65109
+ }
65110
+
65076
65111
  // src/theme/theme.ts
65077
65112
  import * as fs13 from "node:fs";
65078
65113
  import * as path12 from "node:path";
@@ -70270,14 +70305,18 @@ function computePromptPrefixHash(systemPrompt, tools) {
70270
70305
  function calculateCumulativeCacheHitRate(messages) {
70271
70306
  let input = 0;
70272
70307
  let cacheRead = 0;
70308
+ let cacheWrite = 0;
70309
+ let cacheWriteLong = 0;
70273
70310
  for (const message of messages) {
70274
70311
  if (message.role !== "assistant") continue;
70275
70312
  const usage = message.usage;
70276
70313
  if (!usage) continue;
70277
70314
  input += usage.input;
70278
70315
  cacheRead += usage.cacheRead;
70316
+ cacheWrite += usage.cacheWrite;
70317
+ cacheWriteLong += usage.cacheWriteLong ?? 0;
70279
70318
  }
70280
- const base = input + cacheRead;
70319
+ const base = input + cacheRead + cacheWrite + cacheWriteLong;
70281
70320
  return base > 0 ? cacheRead / base : null;
70282
70321
  }
70283
70322
  var signalMemories = /* @__PURE__ */ new WeakMap();
@@ -70534,6 +70573,11 @@ function formatReportTokens(count) {
70534
70573
  if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
70535
70574
  return `${Math.round(count / 1e6)}M`;
70536
70575
  }
70576
+ function formatMeasuredTime(timestamp) {
70577
+ const date = new Date(timestamp);
70578
+ const pad = (value) => String(value).padStart(2, "0");
70579
+ return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
70580
+ }
70537
70581
  function buildSystemPromptBlocks(systemPrompt, skillsSection, knowledgeSection) {
70538
70582
  const totalTokens = estimateStringTokens(systemPrompt);
70539
70583
  const skillsTokens = skillsSection && systemPrompt.includes(skillsSection.trim()) ? estimateStringTokens(skillsSection) : null;
@@ -70789,8 +70833,12 @@ ${file.content}`).join("\n\n");
70789
70833
  messagesTokens,
70790
70834
  dynamicContextTokens: 0,
70791
70835
  estimatedTotalTokens,
70792
- actualTokens: inputs.actualTokens,
70836
+ estimatedCurrentTokens: inputs.estimatedCurrentTokens,
70837
+ providerMeasuredTokens: inputs.providerMeasuredTokens,
70838
+ providerMeasuredAt: inputs.providerMeasuredAt ?? null,
70839
+ measurementMatchesCurrentContext: inputs.measurementMatchesCurrentContext,
70793
70840
  contextWindow: inputs.contextWindow,
70841
+ autoCompactEnabled: inputs.autoCompactEnabled,
70794
70842
  autoCompactLimitTokens: inputs.autoCompactLimitTokens,
70795
70843
  reserveTokens: inputs.reserveTokens,
70796
70844
  promptPrefixHash: inputs.promptPrefixHash,
@@ -70847,13 +70895,14 @@ function buildContextOverviewText(data, themeInput) {
70847
70895
  }
70848
70896
  }
70849
70897
  });
70850
- const autoCompact = data.autoCompactLimitTokens ? `triggers > ~${formatReportTokens(data.autoCompactLimitTokens)} (reserve ${formatReportTokens(data.reserveTokens)})` : "disabled";
70898
+ 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
70899
  lines.push(
70852
70900
  `${padEndVisible(` ${branch(sections.length)} auto-compact`, labelWidth)}${theme2.fg("dim", autoCompact)}`
70853
70901
  );
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";
70902
+ const measuredAt = data.providerMeasuredAt !== null ? ` \xB7 measured ${formatMeasuredTime(data.providerMeasuredAt)}` : "";
70903
+ 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(
70904
+ Math.abs(data.estimatedCurrentTokens - data.providerMeasuredTokens)
70905
+ )}${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
70906
  lines.push(` ${theme2.fg("dim", reconciliation)}`);
70858
70907
  lines.push(theme2.fg("dim", " /debugs/context \xB7 Enter to drill into a section, Esc to go back"));
70859
70908
  return lines.join("\n");
@@ -70872,23 +70921,37 @@ function buildWindowRows(data) {
70872
70921
  label: "estimated total (prompt + tools + messages)",
70873
70922
  estTokens: data.estimatedTotalTokens,
70874
70923
  note: `prompt ~${formatReportTokens(data.systemPromptTokens)} \xB7 tools ~${formatReportTokens(data.toolsTokens)} \xB7 messages ~${formatReportTokens(data.messagesTokens)}`
70924
+ },
70925
+ {
70926
+ id: "window:estimated-current",
70927
+ label: "estimated current context (local estimate + tail)",
70928
+ estTokens: data.estimatedCurrentTokens,
70929
+ note: "local estimate \u2014 not a provider measurement"
70875
70930
  }
70876
70931
  ];
70877
- if (data.actualTokens !== null) {
70932
+ if (data.providerMeasuredTokens !== null) {
70933
+ const measuredAt = data.providerMeasuredAt !== null ? formatMeasuredTime(data.providerMeasuredAt) : "unknown time";
70878
70934
  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))}`
70935
+ id: "window:provider-measured",
70936
+ label: "provider-measured (last response)",
70937
+ estTokens: data.providerMeasuredTokens,
70938
+ valueText: formatReportTokens(data.providerMeasuredTokens),
70939
+ 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
70940
  });
70885
70941
  }
70886
70942
  rows.push({
70887
70943
  id: "window:auto-compact",
70944
+ label: "auto-compact",
70945
+ estTokens: 0,
70946
+ valueText: data.autoCompactEnabled ? "On" : "Off",
70947
+ note: data.autoCompactEnabled ? "enabled in settings" : "disabled in settings"
70948
+ });
70949
+ rows.push({
70950
+ id: "window:auto-compact-threshold",
70888
70951
  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"
70952
+ estTokens: data.autoCompactEnabled ? data.autoCompactLimitTokens ?? 0 : 0,
70953
+ valueText: data.autoCompactEnabled ? data.autoCompactLimitTokens ? `>${formatReportTokens(data.autoCompactLimitTokens)}` : "unknown" : "\u2014",
70954
+ note: data.autoCompactEnabled ? data.autoCompactLimitTokens ? `reserve ${formatReportTokens(data.reserveTokens)}` : "context window unavailable" : "not applied while Off"
70892
70955
  });
70893
70956
  if (data.promptPrefixHash) {
70894
70957
  rows.push({
@@ -72781,7 +72844,9 @@ function calculateSessionUsageTotals(messages) {
72781
72844
  let output = 0;
72782
72845
  let cacheRead = 0;
72783
72846
  let cacheWrite = 0;
72847
+ let cacheWriteLong = 0;
72784
72848
  let cost = 0;
72849
+ let costEstimated = false;
72785
72850
  for (const message of messages) {
72786
72851
  if (message.role !== "assistant") continue;
72787
72852
  const usage = message.usage;
@@ -72790,9 +72855,11 @@ function calculateSessionUsageTotals(messages) {
72790
72855
  output += usage.output;
72791
72856
  cacheRead += usage.cacheRead;
72792
72857
  cacheWrite += usage.cacheWrite;
72858
+ cacheWriteLong += usage.cacheWriteLong ?? 0;
72793
72859
  cost += usage.cost?.total ?? 0;
72860
+ if (usage.cost?.cacheWriteLongEstimated === true) costEstimated = true;
72794
72861
  }
72795
- return { input, output, cacheRead, cacheWrite, cost };
72862
+ return { input, output, cacheRead, cacheWrite, cacheWriteLong, cacheWriteTotal: cacheWrite + cacheWriteLong, cost, costEstimated };
72796
72863
  }
72797
72864
  function formatTokensCompact(count) {
72798
72865
  if (count < 1e3) return String(count);
@@ -72838,16 +72905,18 @@ ${options.theme.bold("Usage (active context)")}
72838
72905
  info += `${options.theme.fg("dim", "Cache Read:")} ${totals.cacheRead.toLocaleString()}
72839
72906
  `;
72840
72907
  }
72841
- if (totals.cacheWrite > 0) {
72842
- info += `${options.theme.fg("dim", "Cache Write:")} ${totals.cacheWrite.toLocaleString()}
72908
+ if (totals.cacheWriteTotal > 0) {
72909
+ const split = totals.cacheWriteLong > 0 ? options.theme.fg("dim", ` (short ${totals.cacheWrite.toLocaleString()} \xB7 long ${totals.cacheWriteLong.toLocaleString()})`) : "";
72910
+ info += `${options.theme.fg("dim", "Cache Write:")} ${totals.cacheWriteTotal.toLocaleString()}${split}
72843
72911
  `;
72844
72912
  }
72845
72913
  if (totals.cost > 0) {
72846
- info += `${options.theme.fg("dim", "Cost:")} $${totals.cost.toFixed(4)}
72914
+ const estimateNote = totals.costEstimated ? " (long-TTL estimate)" : "";
72915
+ info += `${options.theme.fg("dim", "Cost:")} $${totals.cost.toFixed(4)}${estimateNote}
72847
72916
  `;
72848
72917
  }
72849
- const hitRateBase = totals.input + totals.cacheRead;
72850
- if (hitRateBase > 0 && totals.cacheRead > 0) {
72918
+ const hitRateBase = totals.input + totals.cacheRead + totals.cacheWriteTotal;
72919
+ if (hitRateBase > 0) {
72851
72920
  const hitRate = (totals.cacheRead / hitRateBase * 100).toFixed(1);
72852
72921
  info += `${options.theme.fg("dim", "Cache Hit Rate:")} ${hitRate}% (~${formatTokensCompact(hitRateBase)} served)
72853
72922
  `;
@@ -72981,7 +73050,9 @@ async function loadSession(host, sessionId) {
72981
73050
  outputTokens: contextUsage.output,
72982
73051
  cacheRead: contextUsage.cacheRead,
72983
73052
  cacheWrite: contextUsage.cacheWrite,
73053
+ cacheWriteLong: contextUsage.cacheWriteLong,
72984
73054
  cost: contextUsage.cost,
73055
+ costEstimated: contextUsage.costEstimated,
72985
73056
  contextPercent,
72986
73057
  contextWindow
72987
73058
  });
@@ -74019,7 +74090,9 @@ function bindInteractiveRuntimeEvents(host) {
74019
74090
  outputTokens: contextUsage.output,
74020
74091
  cacheRead: contextUsage.cacheRead,
74021
74092
  cacheWrite: contextUsage.cacheWrite,
74093
+ cacheWriteLong: contextUsage.cacheWriteLong,
74022
74094
  cost: contextUsage.cost,
74095
+ costEstimated: contextUsage.costEstimated,
74023
74096
  contextPercent,
74024
74097
  contextWindow
74025
74098
  });
@@ -74771,7 +74844,7 @@ function listDiscoveredSkills(host) {
74771
74844
  function applyReloadedSkills(host, result) {
74772
74845
  host.skills = result.skills.filter((skill) => host.options.settingsManager.isSkillEnabled(skill.name));
74773
74846
  host.skillsSection = formatSkillsForPrompt(host.skills);
74774
- host.applyRuntimeToolMode(host.allTools.length > 0 ? host.allTools : host.agent.state.tools);
74847
+ host.applyRuntimeToolMode();
74775
74848
  host.updateSystemPromptTools();
74776
74849
  for (const skill of host.skills) {
74777
74850
  const skillPath = ["skills", `skill:${skill.name}`];
@@ -75199,6 +75272,23 @@ function handleToolsCommand(host, args) {
75199
75272
  var RESET2 = "\x1B[0m";
75200
75273
  var INVERSE2 = "\x1B[7m";
75201
75274
  var ROLE_FILTER_CYCLE = ["all", "user", "assistant", "toolResult", "compaction"];
75275
+ function buildWrappedLines(rawLines, contentWidth) {
75276
+ const width = Math.max(1, contentWidth);
75277
+ const lines = [];
75278
+ const sourceStart = [];
75279
+ for (const rawLine of rawLines) {
75280
+ sourceStart.push(lines.length);
75281
+ lines.push(...wrapTextWithAnsi(rawLine, width));
75282
+ }
75283
+ return { lines: lines.length > 0 ? lines : [""], sourceStart };
75284
+ }
75285
+ function anchorFor(scroll, sourceStart, sourceCount) {
75286
+ if (sourceStart.length === 0) return { source: 0, offset: 0 };
75287
+ const target = Math.max(0, Math.min(scroll, sourceCount - 1));
75288
+ let source = sourceStart.length - 1;
75289
+ while (source > 0 && sourceStart[source] > target) source -= 1;
75290
+ return { source, offset: target - sourceStart[source] };
75291
+ }
75202
75292
  function createLine2(content, width, isSelected) {
75203
75293
  const normalized = content.replace(/[\r\n]+/g, " ");
75204
75294
  const body = isSelected ? INVERSE2 + normalized : normalized;
@@ -75339,12 +75429,33 @@ var ContextReportSelectorComponent = class {
75339
75429
  this.views.push({
75340
75430
  kind: "text",
75341
75431
  title,
75342
- lines: text.replace(/\r\n/g, "\n").split("\n"),
75432
+ rawLines: text.replace(/\r\n/g, "\n").split("\n"),
75433
+ lines: [],
75434
+ sourceStart: [],
75435
+ wrapWidth: 0,
75343
75436
  scroll: 0
75344
75437
  });
75345
75438
  this.selectedIndex = 0;
75346
75439
  this.statusMessage = null;
75347
75440
  }
75441
+ /** Re-wrap a text view for the current terminal width, preserving the anchor. */
75442
+ ensureTextWrapped(view, width) {
75443
+ const contentWidth = Math.max(1, width - 2);
75444
+ if (view.wrapWidth === contentWidth) return;
75445
+ const previousCount = view.lines.length;
75446
+ const anchor = view.wrapWidth > 0 && previousCount > 0 ? anchorFor(view.scroll, view.sourceStart, previousCount) : { source: 0, offset: 0 };
75447
+ const rebuilt = buildWrappedLines(view.rawLines, contentWidth);
75448
+ view.lines = rebuilt.lines;
75449
+ view.sourceStart = rebuilt.sourceStart;
75450
+ view.wrapWidth = contentWidth;
75451
+ const source = Math.min(anchor.source, Math.max(0, rebuilt.sourceStart.length - 1));
75452
+ const nextSourceStart = rebuilt.sourceStart[source + 1] ?? rebuilt.lines.length;
75453
+ const sourceLineCount = Math.max(1, nextSourceStart - rebuilt.sourceStart[source]);
75454
+ view.scroll = Math.min(
75455
+ Math.max(0, rebuilt.lines.length - 1),
75456
+ rebuilt.sourceStart[source] + Math.min(anchor.offset, sourceLineCount - 1)
75457
+ );
75458
+ }
75348
75459
  drillIntoSelected() {
75349
75460
  const view = this.currentView;
75350
75461
  if (view.kind !== "list") return;
@@ -75526,6 +75637,8 @@ var ContextReportSelectorComponent = class {
75526
75637
  )
75527
75638
  );
75528
75639
  } else {
75640
+ this.ensureTextWrapped(view, width);
75641
+ view.scroll = Math.max(0, Math.min(view.scroll, view.lines.length - 1));
75529
75642
  const total = view.lines.length;
75530
75643
  const start = Math.min(view.scroll, Math.max(0, total - 1));
75531
75644
  const end = Math.min(start + this.maxVisibleLines, total);
@@ -75690,6 +75803,7 @@ function handleContextReportCommand(host) {
75690
75803
  const usage = host.calculateContextUsage();
75691
75804
  const contextWindow = host.options.model?.contextWindow ?? 0;
75692
75805
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
75806
+ const autoCompactEnabled = host.options.settingsManager.getAutoCompactEnabled();
75693
75807
  const autoCompactLimit = contextWindow > 0 ? getAutoCompactTokenLimit(contextWindow, reserveTokens) ?? null : null;
75694
75808
  const prefixHash = computePromptPrefixHash(
75695
75809
  systemPrompt,
@@ -75721,7 +75835,11 @@ function handleContextReportCommand(host) {
75721
75835
  entries,
75722
75836
  compactionTokensBefore: (entryId) => host.sessionManager.getEntry(entryId)?.compactionData?.tokensBefore,
75723
75837
  contextWindow,
75724
- actualTokens: usage.tokens,
75838
+ estimatedCurrentTokens: usage.estimatedCurrentTokens,
75839
+ providerMeasuredTokens: usage.providerMeasuredTokens,
75840
+ providerMeasuredAt: usage.providerMeasuredAt,
75841
+ measurementMatchesCurrentContext: usage.measurementMatchesCurrentContext,
75842
+ autoCompactEnabled,
75725
75843
  autoCompactLimitTokens: autoCompactLimit,
75726
75844
  reserveTokens,
75727
75845
  promptPrefixHash: prefixHash,
@@ -76553,7 +76671,9 @@ async function handleClearCommand(host) {
76553
76671
  outputTokens: contextUsage.output,
76554
76672
  cacheRead: contextUsage.cacheRead,
76555
76673
  cacheWrite: contextUsage.cacheWrite,
76674
+ cacheWriteLong: contextUsage.cacheWriteLong,
76556
76675
  cost: contextUsage.cost,
76676
+ costEstimated: contextUsage.costEstimated,
76557
76677
  contextPercent: contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0,
76558
76678
  contextWindow
76559
76679
  });
@@ -76916,11 +77036,16 @@ async function handleCompactCommand(host) {
76916
77036
  host.showStatus("Nothing to compact yet");
76917
77037
  return;
76918
77038
  }
76919
- const firstKeptEntryId = findFirstKeptEntrySummaryId(host.sessionManager.getActiveContextSummaries());
76920
- if (!firstKeptEntryId) {
77039
+ const preparation = prepareSessionCompaction({
77040
+ sessionManager: host.sessionManager,
77041
+ messages: existingMessages,
77042
+ budgetTokens: host.options.model?.contextWindow
77043
+ });
77044
+ if (preparation.entries.length === 0) {
76921
77045
  host.showStatus("Need at least one real user message before compacting");
76922
77046
  return;
76923
77047
  }
77048
+ const tokensBefore = calculateTotalTokens(preparation.messages);
76924
77049
  const compactOptions = {
76925
77050
  model: host.options.model,
76926
77051
  apiKey: host.options.apiKey,
@@ -76939,10 +77064,27 @@ async function handleCompactCommand(host) {
76939
77064
  if (beforeCompact?.summary) {
76940
77065
  summary = beforeCompact.summary;
76941
77066
  } else {
76942
- summary = await createCompactionSummary(existingMessages, compactOptions);
77067
+ summary = await createCompactionSummary(preparation.messages, compactOptions);
77068
+ }
77069
+ const verified = verifyPreparedCompactionCommit({
77070
+ preparation,
77071
+ sessionManager: host.sessionManager,
77072
+ messages: host.agent.state.messages,
77073
+ result: { summary, retainedTail: preparation.retainedTail.retainedTail, tokensBefore }
77074
+ });
77075
+ if (verified.ok === false) {
77076
+ if (verified.kind === "stale") {
77077
+ host.showStatus("Session changed during compaction; nothing was published");
77078
+ return;
77079
+ }
77080
+ throw new Error(`Compaction abandoned: ${verified.reason}`);
76943
77081
  }
76944
- const tokensBefore = calculateTotalTokens(existingMessages);
76945
- host.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore);
77082
+ host.sessionManager.appendCompactionCheckpoint({
77083
+ summary,
77084
+ retainedTail: verified.retainedTail,
77085
+ tokensBefore,
77086
+ excludeFailure: preparation.excludedFailure
77087
+ });
76946
77088
  const compactedMessages = host.sessionManager.getMessages();
76947
77089
  host.replaceAgentMessagesAndMarkSynced(compactedMessages);
76948
77090
  host.resetFileObservationState();
@@ -76967,7 +77109,9 @@ async function handleCompactCommand(host) {
76967
77109
  outputTokens: contextUsage.output,
76968
77110
  cacheRead: contextUsage.cacheRead,
76969
77111
  cacheWrite: contextUsage.cacheWrite,
77112
+ cacheWriteLong: contextUsage.cacheWriteLong,
76970
77113
  cost: contextUsage.cost,
77114
+ costEstimated: contextUsage.costEstimated,
76971
77115
  contextPercent,
76972
77116
  contextWindow
76973
77117
  });
@@ -79350,8 +79494,12 @@ var FooterComponent = class {
79350
79494
  if (state.inputTokens) statsParts.push(`\u2191${formatTokens(state.inputTokens)}`);
79351
79495
  if (state.outputTokens) statsParts.push(`\u2193${formatTokens(state.outputTokens)}`);
79352
79496
  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)}`);
79497
+ const cacheWriteShort = state.cacheWrite ?? 0;
79498
+ const cacheWriteLong = state.cacheWriteLong ?? 0;
79499
+ const cacheWriteTotal = cacheWriteShort + cacheWriteLong;
79500
+ if (cacheWriteTotal) statsParts.push(`W${formatTokens(cacheWriteTotal)}`);
79501
+ if (cacheWriteLong) statsParts.push(`W1h${formatTokens(cacheWriteLong)}`);
79502
+ if (state.cost) statsParts.push(`${state.costEstimated ? "~" : ""}$${state.cost.toFixed(3)}`);
79355
79503
  if (state.cacheHitRate != null) {
79356
79504
  statsParts.push(theme.fg("dim", `ca ${(state.cacheHitRate * 100).toFixed(0)}%`));
79357
79505
  }
@@ -79501,13 +79649,23 @@ function calculateCliContextUsage(params) {
79501
79649
  const estimate = estimateContextTokens(messages);
79502
79650
  const usageMessage = estimate.lastUsageIndex !== null ? messages[estimate.lastUsageIndex] : void 0;
79503
79651
  const usage = usageMessage?.role === "assistant" ? usageMessage.usage : void 0;
79652
+ const hasProviderMeasurement = usage !== void 0 && estimate.lastUsageIndex !== null;
79504
79653
  return {
79505
79654
  tokens: estimate.tokens,
79655
+ estimatedCurrentTokens: estimate.tokens,
79656
+ providerMeasuredTokens: hasProviderMeasurement ? estimate.providerTokens : null,
79657
+ providerMeasuredAt: hasProviderMeasurement && usageMessage ? usageMessage.timestamp : null,
79658
+ // A provider total describes the context AT that response. Any trailing
79659
+ // tool/user message (or a compaction reset, which zeroes stale usage)
79660
+ // makes the two figures different snapshots, so no error may be shown.
79661
+ measurementMatchesCurrentContext: hasProviderMeasurement && estimate.lastUsageIndex === messages.length - 1,
79506
79662
  input: usage?.input ?? 0,
79507
79663
  output: usage?.output ?? 0,
79508
79664
  cacheRead: usage?.cacheRead ?? 0,
79509
79665
  cacheWrite: usage?.cacheWrite ?? 0,
79510
- cost: usage?.cost.total ?? 0
79666
+ cacheWriteLong: usage?.cacheWriteLong ?? 0,
79667
+ cost: usage?.cost.total ?? 0,
79668
+ costEstimated: usage?.cost?.cacheWriteLongEstimated === true
79511
79669
  };
79512
79670
  }
79513
79671
  function createCliAutoCompactDecision(params) {
@@ -79538,6 +79696,11 @@ function shouldCliAutoCompactTokens(params) {
79538
79696
  const tokenLimit = getAutoCompactTokenLimit(params.contextWindow, params.reserveTokens);
79539
79697
  return tokenLimit !== void 0 && params.tokens > tokenLimit;
79540
79698
  }
79699
+ function createCliCompactionBudget(params) {
79700
+ const contextWindow = params.contextWindow ?? 0;
79701
+ const reserveTokens = params.reserveTokens ?? DEFAULT_COMPACTION_CONFIG.reserveTokens;
79702
+ return () => getAutoCompactTokenLimit(contextWindow, reserveTokens);
79703
+ }
79541
79704
  async function createCliAutoCompactionResult(params) {
79542
79705
  if (params.context.signal?.aborted) {
79543
79706
  return { aborted: true };
@@ -79546,8 +79709,9 @@ async function createCliAutoCompactionResult(params) {
79546
79709
  if (!activeEntryId) {
79547
79710
  return { aborted: true };
79548
79711
  }
79549
- const firstKeptEntryId = findFirstKeptEntrySummaryId(params.context.sessionManager.getActiveContextSummaries());
79550
- if (!firstKeptEntryId) {
79712
+ const legacyCutId = params.context.preparation ? null : findFirstKeptEntrySummaryId(params.context.sessionManager.getActiveContextSummaries());
79713
+ const retainedTail = params.context.preparation ? params.context.preparation.retainedTail.retainedTail : legacyCutId ? { kind: "from-entry", entryId: legacyCutId } : void 0;
79714
+ if (!retainedTail) {
79551
79715
  return { aborted: true };
79552
79716
  }
79553
79717
  const compactOptions = {
@@ -79562,7 +79726,8 @@ async function createCliAutoCompactionResult(params) {
79562
79726
  }
79563
79727
  return {
79564
79728
  summary,
79565
- firstKeptEntryId,
79729
+ retainedTail,
79730
+ ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {},
79566
79731
  tokensBefore: calculateTotalTokens(params.context.messages)
79567
79732
  };
79568
79733
  }
@@ -79723,7 +79888,20 @@ function createCliRetryPolicy(isRetryableErrorOverride) {
79723
79888
  function calculateContextUsage(host) {
79724
79889
  const messages = host.agent?.state.messages ?? [];
79725
79890
  if (messages.length === 0) {
79726
- return { tokens: 0, input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0 };
79891
+ return {
79892
+ tokens: 0,
79893
+ estimatedCurrentTokens: 0,
79894
+ providerMeasuredTokens: null,
79895
+ providerMeasuredAt: null,
79896
+ measurementMatchesCurrentContext: false,
79897
+ input: 0,
79898
+ output: 0,
79899
+ cacheRead: 0,
79900
+ cacheWrite: 0,
79901
+ cacheWriteLong: 0,
79902
+ cost: 0,
79903
+ costEstimated: false
79904
+ };
79727
79905
  }
79728
79906
  return calculateCliContextUsage({
79729
79907
  messages,
@@ -80474,6 +80652,9 @@ function createInteractiveAgentSessionController(host) {
80474
80652
  }
80475
80653
  return void 0;
80476
80654
  },
80655
+ // Same auto-compact limit the threshold decision uses; a checkpoint whose
80656
+ // summary + retained tail would still exceed it is refused.
80657
+ getCompactionBudgetTokens: createCliCompactionBudget({ contextWindow: host.options.model?.contextWindow }),
80477
80658
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
80478
80659
  classifyFailure: (context) => classifyCliFailure(context),
80479
80660
  retry: createCliRetryPolicy(
@@ -80491,9 +80672,16 @@ function createInteractiveAgentSessionController(host) {
80491
80672
  return { aborted: true };
80492
80673
  }
80493
80674
  if (beforeCompact?.summary) {
80494
- const firstKeptEntryId = findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries());
80495
- if (!firstKeptEntryId) return { aborted: true };
80496
- return { summary: beforeCompact.summary, firstKeptEntryId };
80675
+ const retainedTail = context.preparation?.retainedTail.retainedTail ?? (() => {
80676
+ const legacyCutId = findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries());
80677
+ return legacyCutId ? { kind: "from-entry", entryId: legacyCutId } : void 0;
80678
+ })();
80679
+ if (!retainedTail) return { aborted: true };
80680
+ return {
80681
+ summary: beforeCompact.summary,
80682
+ retainedTail,
80683
+ ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {}
80684
+ };
80497
80685
  }
80498
80686
  autoCompactBeforeStats = summarizeActiveContext(context.sessionManager.getActiveContextSummaries());
80499
80687
  return createCliAutoCompactionResult({
@@ -80655,6 +80843,13 @@ function createInteractiveAgentSessionController(host) {
80655
80843
  host.isProcessing = true;
80656
80844
  host.footer.setState({ status: "processing", lifecycleEvent: "auto_compact" });
80657
80845
  host.showStatus(event.reason === "overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context...");
80846
+ } else if (event.type === "compaction_committed") {
80847
+ host.resetFileObservationState();
80848
+ host.resetRuntimeModeBlocks?.();
80849
+ host.sessionTrace.noteRuntimeEvent("compaction_committed", {
80850
+ reason: event.reason,
80851
+ willRetry: event.willRetry
80852
+ });
80658
80853
  } else if (event.type === "auto_compaction_end") {
80659
80854
  host.isCompacting = false;
80660
80855
  host.isProcessing = false;
@@ -80667,8 +80862,6 @@ function createInteractiveAgentSessionController(host) {
80667
80862
  autoCompactBeforeStats = null;
80668
80863
  return;
80669
80864
  }
80670
- host.resetFileObservationState();
80671
- host.resetRuntimeModeBlocks?.();
80672
80865
  if (!event.result?.aborted) {
80673
80866
  host.streamingComponent = void 0;
80674
80867
  host.pendingTools.clear();
@@ -80687,7 +80880,9 @@ function createInteractiveAgentSessionController(host) {
80687
80880
  outputTokens: contextUsage.output,
80688
80881
  cacheRead: contextUsage.cacheRead,
80689
80882
  cacheWrite: contextUsage.cacheWrite,
80883
+ cacheWriteLong: contextUsage.cacheWriteLong,
80690
80884
  cost: contextUsage.cost,
80885
+ costEstimated: contextUsage.costEstimated,
80691
80886
  contextPercent,
80692
80887
  contextWindow
80693
80888
  });
@@ -80843,20 +81038,80 @@ function currentDateInZone(timeZone) {
80843
81038
  const part = (type) => parts.find((p) => p.type === type)?.value ?? "00";
80844
81039
  return `${part("year")}-${part("month")}-${part("day")}`;
80845
81040
  }
80846
- var RUNTIME_MODE_BLOCK_PATTERN = /^[\t ]*<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>[\t ]*(?:\r?\n){0,2}/;
81041
+ var RUNTIME_MODE_BLOCK_START_MARKER = "<runtime-mode-context>";
81042
+ function leadingRuntimeBlock(text) {
81043
+ const withoutLeadingWhitespace = text.replace(/^[\t ]+/, "");
81044
+ if (!withoutLeadingWhitespace.startsWith(RUNTIME_MODE_BLOCK_START_MARKER)) return null;
81045
+ const markerStart = text.length - withoutLeadingWhitespace.length;
81046
+ const endIndex = text.indexOf(RUNTIME_MODE_CONTEXT_END_MARKER, markerStart);
81047
+ if (endIndex < 0) return null;
81048
+ const block = text.slice(markerStart, endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length);
81049
+ const rest = text.slice(endIndex + RUNTIME_MODE_CONTEXT_END_MARKER.length).replace(/^[\t ]*(?:\r?\n){0,2}/, "");
81050
+ return { block, rest };
81051
+ }
81052
+ function stripLeadingRuntimeBlock(text) {
81053
+ const leading = leadingRuntimeBlock(text);
81054
+ return leading ? { rest: leading.rest } : null;
81055
+ }
81056
+ function leadingRuntimeBlockOfMessage(message) {
81057
+ if (message.role !== "user") return null;
81058
+ const content = message.content;
81059
+ if (typeof content === "string") return leadingRuntimeBlock(content)?.block ?? null;
81060
+ if (!Array.isArray(content)) return null;
81061
+ const first = content[0];
81062
+ if (first?.type !== "text" || typeof first.text !== "string") return null;
81063
+ return leadingRuntimeBlock(first.text)?.block ?? null;
81064
+ }
81065
+ function lastUserMessageCarriesRuntimeBlock(messages, block) {
81066
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
81067
+ const message = messages[index];
81068
+ if (message?.role !== "user") continue;
81069
+ return leadingRuntimeBlockOfMessage(message) === block;
81070
+ }
81071
+ return false;
81072
+ }
81073
+ function applyRuntimeBlockToText(text, block) {
81074
+ const split = stripLeadingRuntimeBlock(text);
81075
+ const rest = split ? split.rest : text;
81076
+ const next = rest.length > 0 ? `${block}
81077
+
81078
+ ${rest}` : block;
81079
+ return next === text ? text : next;
81080
+ }
81081
+ function applyRuntimeBlockToContentArray(content, block) {
81082
+ const first = content[0];
81083
+ if (first && typeof first === "object" && first.type === "text" && typeof first.text === "string") {
81084
+ const text = first.text;
81085
+ if (stripLeadingRuntimeBlock(text)) {
81086
+ const nextText = applyRuntimeBlockToText(text, block);
81087
+ if (nextText === text) return content;
81088
+ const copy = [...content];
81089
+ copy[0] = { ...first, text: nextText };
81090
+ return copy;
81091
+ }
81092
+ }
81093
+ return [{ type: "text", text: block }, ...content];
81094
+ }
80847
81095
  function refreshRuntimeModeBlockOnLastUserMessage(messages, block) {
80848
81096
  for (let i = messages.length - 1; i >= 0; i--) {
80849
81097
  const message = messages[i];
80850
81098
  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;
81099
+ const content = message.content;
81100
+ if (typeof content === "string") {
81101
+ const next = applyRuntimeBlockToText(content, block);
81102
+ if (next === content) return messages;
81103
+ const copy = [...messages];
81104
+ copy[i] = { ...message, content: next };
81105
+ return copy;
81106
+ }
81107
+ if (Array.isArray(content)) {
81108
+ const next = applyRuntimeBlockToContentArray(content, block);
81109
+ if (next === content) return messages;
81110
+ const copy = [...messages];
81111
+ copy[i] = { ...message, content: next };
81112
+ return copy;
81113
+ }
81114
+ return messages;
80860
81115
  }
80861
81116
  return messages;
80862
81117
  }
@@ -80866,28 +81121,54 @@ var RuntimeModeBlockInjector = class {
80866
81121
  }
80867
81122
  options;
80868
81123
  lastKey = null;
80869
- pendingRetryRefresh = false;
81124
+ /**
81125
+ * Request-view overlay. While active, the CURRENT block is re-derived on
81126
+ * every request from the live message snapshot and applied to the latest
81127
+ * user message. It is not cached into history and it ends only when a real
81128
+ * user message freezes the current block (see `nextBlock` + the transform),
81129
+ * so consecutive tool rounds after a compaction retry keep the fresh state
81130
+ * instead of losing it after one request.
81131
+ */
81132
+ overlayActive = false;
80870
81133
  reset() {
80871
81134
  this.lastKey = null;
80872
- this.pendingRetryRefresh = true;
81135
+ this.overlayActive = true;
80873
81136
  }
80874
- /** Block to prepend to the next user message, or null when unchanged. */
81137
+ /**
81138
+ * Arm the request-view overlay without resetting the frozen-block key. Used
81139
+ * when the runtime shape changed while a turn is still running (compaction
81140
+ * checkpoint committed, plan approval, YOLO toggle inside a tool turn): the
81141
+ * next and subsequent model requests must see the current state even though
81142
+ * no new user message has been accepted yet.
81143
+ */
81144
+ armRefresh() {
81145
+ this.overlayActive = true;
81146
+ }
81147
+ /**
81148
+ * Block to prepend to the next user message, or null when unchanged. Does
81149
+ * NOT end the overlay: the block is only proof once the message carrying it
81150
+ * reaches the request transform.
81151
+ */
80875
81152
  nextBlock() {
80876
- const block = this.freshBlock();
80877
- this.pendingRetryRefresh = false;
80878
- return block;
81153
+ return this.freshBlock();
80879
81154
  }
80880
81155
  /**
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.
81156
+ * Current block for a request that reuses existing history (overflow retry
81157
+ * after compaction, manual-compact continue, or the tool rounds that follow
81158
+ * a mid-turn mode change). Returns the same block for every request until
81159
+ * the latest user message itself carries that block; then the overlay is
81160
+ * retired and null is returned. Derivation is per request from the given
81161
+ * snapshot; no message array is cached.
80885
81162
  */
80886
- consumeRetryRefreshBlock() {
80887
- if (!this.pendingRetryRefresh) return null;
80888
- this.pendingRetryRefresh = false;
81163
+ consumeRetryRefreshBlock(messages) {
81164
+ if (!this.overlayActive) return null;
80889
81165
  const state = this.options.getState();
80890
- return buildRuntimeModeContext(state);
81166
+ const block = buildRuntimeModeContext(state);
81167
+ if (messages && lastUserMessageCarriesRuntimeBlock(messages, block)) {
81168
+ this.overlayActive = false;
81169
+ return null;
81170
+ }
81171
+ return block;
80891
81172
  }
80892
81173
  freshBlock() {
80893
81174
  const state = this.options.getState();
@@ -80900,14 +81181,17 @@ var RuntimeModeBlockInjector = class {
80900
81181
  };
80901
81182
 
80902
81183
  // src/runtime/cache-retention.ts
80903
- function createCacheRetentionStreamFn(configured) {
81184
+ function createCacheRetentionStreamFn(configured, getConfiguredRetention) {
80904
81185
  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
- });
81186
+ return (model, context, options) => {
81187
+ const requested = options?.cacheRetention;
81188
+ const configuredRetention = model.api === "anthropic-messages" ? getConfiguredRetention?.() : void 0;
81189
+ const cacheRetention = requested ?? configuredRetention;
81190
+ return base(model, context, {
81191
+ ...options,
81192
+ ...cacheRetention === void 0 ? {} : { cacheRetention }
81193
+ });
81194
+ };
80911
81195
  }
80912
81196
 
80913
81197
  // src/runtime/guard-advisory-context.ts
@@ -80931,7 +81215,7 @@ function findLatestUserIndex(messages) {
80931
81215
  function applyRuntimeRequestTransform(messages, options) {
80932
81216
  const advisory = options.takeAdvisory();
80933
81217
  let next = advisory ? injectGuardAdvisory(messages, advisory) : stripGuardAdvisories(messages);
80934
- const retryBlock = options.takeRetryBlock();
81218
+ const retryBlock = options.takeRetryBlock(next);
80935
81219
  if (retryBlock) next = refreshRuntimeModeBlockOnLastUserMessage(next, retryBlock);
80936
81220
  return next;
80937
81221
  }
@@ -81814,9 +82098,12 @@ async function loadInteractiveExtensions(host) {
81814
82098
  const activeTools = [...host.agent.state.tools, ...extensionTools];
81815
82099
  host.agent.state.tools = wrapToolsWithExtensions(activeTools, host.extensionRunner);
81816
82100
  const availableTools = [
81817
- ...host.allTools.length > 0 ? host.allTools : host.originalTools.length > 0 ? host.originalTools : host.agent.state.tools,
82101
+ // Prefer the pristine (extension-unwrapped) assembly; re-deriving from
82102
+ // already extension-wrapped `allTools` would nest interceptors.
82103
+ ...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
82104
  ...extensionTools
81819
82105
  ];
82106
+ host.pristineTools = [...availableTools];
81820
82107
  host.allTools = wrapToolsWithExtensions(availableTools, host.extensionRunner);
81821
82108
  host.originalTools = [...host.allTools];
81822
82109
  if (host.options.compositionTrace) {
@@ -81899,6 +82186,8 @@ var InteractiveMode = class {
81899
82186
  mentionPickerOptions = [];
81900
82187
  attachmentResolver;
81901
82188
  yoloMode = false;
82189
+ pristineTools = [];
82190
+ // Pristine (extension-unwrapped) tool assembly: the mode-rebuild source.
81902
82191
  /** Calm mode: render only questions and answers (display-only; persisted). */
81903
82192
  calmMode = false;
81904
82193
  originalTools = [];
@@ -82064,7 +82353,7 @@ var InteractiveMode = class {
82064
82353
  const preservedTools = sourceTools.filter(
82065
82354
  (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash"
82066
82355
  );
82067
- const bashTool = createBashTool(this.options.cwd, { isYoloMode: () => this.yoloMode });
82356
+ const bashTool = createBashTool(this.options.cwd, { isYoloMode: () => this.yoloMode, getEnv: () => computerUseOwnerEnv(this.sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, String(process.pid)) });
82068
82357
  const fileSystemToolOptions = (allowlistedDirs) => ({
82069
82358
  ...buildCliFileAccessOptions({
82070
82359
  cwd: this.options.cwd,
@@ -82134,7 +82423,7 @@ var InteractiveMode = class {
82134
82423
  }
82135
82424
  applyRuntimeToolMode(sourceTools) {
82136
82425
  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;
82426
+ const completeToolSet = sourceTools ?? ((this.pristineTools?.length ?? 0) > 0 ? this.pristineTools : this.allTools) ?? this.agent.state.tools;
82138
82427
  const rebuilt = this.rebuildSkillAwareTools(completeToolSet);
82139
82428
  const currentTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.currentTools, this.extensionRunner) : rebuilt.currentTools;
82140
82429
  const originalTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.originalTools, this.extensionRunner) : rebuilt.originalTools;
@@ -82156,6 +82445,7 @@ var InteractiveMode = class {
82156
82445
  });
82157
82446
  }
82158
82447
  noteRuntimeModeChanged(trigger) {
82448
+ this.runtimeModeBlocks?.armRefresh();
82159
82449
  const state = this.getRuntimeModeState();
82160
82450
  this.sessionTrace.noteRuntimeEvent("runtime_mode_changed", {
82161
82451
  trigger,
@@ -82214,7 +82504,7 @@ var InteractiveMode = class {
82214
82504
  async init() {
82215
82505
  const activeSession = this.sessionManager.getActiveSession();
82216
82506
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
82217
- const { Agent } = await import("./chunks/src-E7NCXMU5.js");
82507
+ const { Agent } = await import("./chunks/src-C4KA4G75.js");
82218
82508
  this.agent = new Agent({
82219
82509
  initialState: {
82220
82510
  systemPrompt: this.buildSystemPrompt(),
@@ -82224,14 +82514,14 @@ var InteractiveMode = class {
82224
82514
  },
82225
82515
  apiKey: this.options.apiKey,
82226
82516
  sessionId: activeSession?.id,
82227
- streamFn: createCacheRetentionStreamFn(this.options.streamFn),
82517
+ streamFn: createTransportDiagnosticsStreamFn(createCacheRetentionStreamFn(this.options.streamFn, () => this.options.settingsManager.getPromptCacheRetention()), createTraceTransportSink((type, meta) => this.sessionTrace.noteRuntimeEvent(type, meta))),
82228
82518
  transformContext: async (messages) => applyRuntimeRequestTransform(messages, {
82229
82519
  takeAdvisory: () => {
82230
82520
  const advisory = this.pendingRuntimeGuardAdvisory;
82231
82521
  this.pendingRuntimeGuardAdvisory = null;
82232
82522
  return advisory;
82233
82523
  },
82234
- takeRetryBlock: () => this.runtimeModeBlocks.consumeRetryRefreshBlock()
82524
+ takeRetryBlock: (snapshot) => this.runtimeModeBlocks.consumeRetryRefreshBlock(snapshot)
82235
82525
  })
82236
82526
  });
82237
82527
  this.agentSessionController = createInteractiveAgentSessionController(this.controllerSetupHost);
@@ -82324,6 +82614,7 @@ var InteractiveMode = class {
82324
82614
  }
82325
82615
  this.allTools = [...this.options.allTools];
82326
82616
  this.originalTools = [...this.options.allTools];
82617
+ this.pristineTools = [...this.options.allTools];
82327
82618
  this.agent.state.tools = [...this.options.allTools];
82328
82619
  await this.extensionRunner?.dispose("reload");
82329
82620
  this.extensionWidgets?.clearAll();
@@ -82620,6 +82911,7 @@ var InteractiveMode = class {
82620
82911
 
82621
82912
  // src/app/runtime-composition.ts
82622
82913
  import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
82914
+ import { randomUUID } from "crypto";
82623
82915
  import * as path31 from "path";
82624
82916
  var CliMissingApiKeyError = class extends Error {
82625
82917
  constructor(message) {
@@ -82749,6 +83041,7 @@ async function composeCliRuntime(params) {
82749
83041
  await sessionManager.save();
82750
83042
  }
82751
83043
  const sessionId = sessionManager.getActiveSession()?.id;
83044
+ const computerUseExecutionId = randomUUID();
82752
83045
  const fileObservationState = createFileObservationState();
82753
83046
  if (sessionId) {
82754
83047
  if (process.env.PIE_DEBUG && !fileLogger.isEnabled()) {
@@ -82777,7 +83070,8 @@ async function composeCliRuntime(params) {
82777
83070
  getWebSearchMode: () => settingsManager.getWebSearchMode(),
82778
83071
  resolveToolModel: (purpose) => modelRegistry.resolveToolModel(purpose, getCurrentModel()),
82779
83072
  resolveToolModelCandidates: (purpose) => modelRegistry.resolveToolModelCandidates(purpose, getCurrentModel()),
82780
- configDir: paths.configDir
83073
+ configDir: paths.configDir,
83074
+ getBashEnv: () => computerUseOwnerEnv(sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, computerUseExecutionId)
82781
83075
  });
82782
83076
  const fileSystemToolOptions = {
82783
83077
  ...buildCliFileAccessOptions({ cwd, configDir: paths.configDir, skills, allowlistedDirs: [paths.configDir] }),
@@ -82905,6 +83199,38 @@ function createPrintModeRetryStatusReporter() {
82905
83199
  };
82906
83200
  }
82907
83201
 
83202
+ // src/app/print-mode-compaction.ts
83203
+ function createPrintModeCompactionHandler(params) {
83204
+ return {
83205
+ compact: async (context) => {
83206
+ const beforeCompact = await params.runner.emitSessionBeforeCompact({
83207
+ type: "session:before_compact",
83208
+ reason: context.reason === "overflow" ? "overflow" : "threshold",
83209
+ messageCount: context.messages.length
83210
+ });
83211
+ if (beforeCompact.cancel) return { aborted: true };
83212
+ if (beforeCompact.summary) {
83213
+ const retainedTail = context.preparation?.retainedTail.retainedTail ?? (() => {
83214
+ const legacyCutId = findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries());
83215
+ return legacyCutId ? { kind: "from-entry", entryId: legacyCutId } : void 0;
83216
+ })();
83217
+ if (!retainedTail) return { aborted: true };
83218
+ return {
83219
+ summary: beforeCompact.summary,
83220
+ retainedTail,
83221
+ ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {}
83222
+ };
83223
+ }
83224
+ return createCliAutoCompactionResult({
83225
+ context,
83226
+ model: params.model,
83227
+ apiKey: params.apiKey,
83228
+ systemPrompt: params.systemPrompt
83229
+ });
83230
+ }
83231
+ };
83232
+ }
83233
+
82908
83234
  // src/app/print-mode.ts
82909
83235
  function createNoOpExtensionUIContext() {
82910
83236
  return {
@@ -83036,7 +83362,7 @@ async function runPrintMode(params) {
83036
83362
  `);
83037
83363
  console.log("Assistant: ");
83038
83364
  }
83039
- const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
83365
+ const { Agent: AgentClass } = await import("./chunks/src-C4KA4G75.js");
83040
83366
  let agent;
83041
83367
  const printModeExtensions = await loadPrintModeExtensions({
83042
83368
  cwd: runtime.cwd,
@@ -83080,7 +83406,8 @@ async function runPrintMode(params) {
83080
83406
  thinkingLevel: printThinkingLevel
83081
83407
  },
83082
83408
  apiKey: runtime.apiKey,
83083
- streamFn: params.streamFn
83409
+ // Body-free transport timeline for the main (print-mode) agent.
83410
+ streamFn: createTransportDiagnosticsStreamFn(params.streamFn ?? streamSimple, logTransportLifecycle)
83084
83411
  });
83085
83412
  const sessionTrace = new SessionTraceWriter(runtime.paths.sessionsDir, runtime.sessionManager);
83086
83413
  const reportRetryStatus = createPrintModeRetryStatusReporter();
@@ -83114,28 +83441,16 @@ async function runPrintMode(params) {
83114
83441
  latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
83115
83442
  isContextOverflowError: isCliContextOverflowError
83116
83443
  }),
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
- },
83444
+ getCompactionBudgetTokens: createCliCompactionBudget({
83445
+ contextWindow: runtime.model.contextWindow,
83446
+ reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
83447
+ }),
83448
+ compaction: createPrintModeCompactionHandler({
83449
+ runner: printModeExtensions.runner,
83450
+ model: runtime.model,
83451
+ apiKey: runtime.apiKey,
83452
+ systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt
83453
+ }),
83139
83454
  classifyFailure: (context) => classifyCliFailure(context),
83140
83455
  onEvent: (event) => {
83141
83456
  sessionTrace.noteRuntimeEvent(event.type, event);
@@ -83143,7 +83458,7 @@ async function runPrintMode(params) {
83143
83458
  console.error(event.failure.kind === "context_overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context before retry...");
83144
83459
  } else if (event.type === "auto_compaction_start") {
83145
83460
  console.error(event.reason === "overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context...");
83146
- } else if (event.type === "auto_compaction_end" && !event.errorMessage) {
83461
+ } else if (event.type === "compaction_committed") {
83147
83462
  runtime.fileObservationState.clear();
83148
83463
  } else {
83149
83464
  reportRetryStatus(event);
@@ -83497,6 +83812,9 @@ async function loadJsonModeExtensions(params) {
83497
83812
  extensionCount: extensions.length
83498
83813
  };
83499
83814
  }
83815
+ function createJsonEventsTransportStreamFn(streamFn) {
83816
+ return createTransportDiagnosticsStreamFn(streamFn ?? streamSimple, logTransportLifecycle);
83817
+ }
83500
83818
  async function runJsonEventsMode(params) {
83501
83819
  const eventDetail = params.eventDetail === FULL_DETAIL ? FULL_DETAIL : COMPACT_DETAIL;
83502
83820
  const eventStringCap = eventDetail === FULL_DETAIL ? MAX_EVENT_STRING_LENGTH : COMPACT_EVENT_STRING_LENGTH;
@@ -83897,7 +84215,7 @@ async function runJsonEventsMode(params) {
83897
84215
  return;
83898
84216
  }
83899
84217
  const parent = agent;
83900
- const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
84218
+ const { Agent: AgentClass } = await import("./chunks/src-C4KA4G75.js");
83901
84219
  const sideAgent = new AgentClass({
83902
84220
  initialState: {
83903
84221
  systemPrompt: parent.state.systemPrompt,
@@ -83907,7 +84225,11 @@ async function runJsonEventsMode(params) {
83907
84225
  messages: structuredClone(parent.state.messages)
83908
84226
  },
83909
84227
  apiKey: runtime.apiKey,
83910
- streamFn: params.streamFn
84228
+ // Same body-free transport timeline as the main agent: a sidechat is
84229
+ // an independent Responses request and previously had no record at all
84230
+ // when it stalled. `streamSimple` matches the host default when the
84231
+ // caller injected no stream function.
84232
+ streamFn: createJsonEventsTransportStreamFn(params.streamFn)
83911
84233
  });
83912
84234
  sidechatController.signal.addEventListener("abort", () => sideAgent.abort(), { once: true });
83913
84235
  const timer = setTimeout(() => {
@@ -84065,7 +84387,7 @@ async function runJsonEventsMode(params) {
84065
84387
  }
84066
84388
  function handleControllerEvent(event) {
84067
84389
  trace?.noteRuntimeEvent(event.type, event);
84068
- if (event.type === "auto_compaction_end" && !event.errorMessage) {
84390
+ if (event.type === "compaction_committed") {
84069
84391
  runtime?.fileObservationState.clear();
84070
84392
  }
84071
84393
  if (event.type === "turn_settled" && activeTurn) {
@@ -84241,7 +84563,7 @@ async function runJsonEventsMode(params) {
84241
84563
  if (runtime.initialModel.unconfigured || controller) {
84242
84564
  return;
84243
84565
  }
84244
- const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
84566
+ const { Agent: AgentClass } = await import("./chunks/src-C4KA4G75.js");
84245
84567
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
84246
84568
  extensionRunner = jsonModeExtensions.runner;
84247
84569
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
@@ -84273,7 +84595,8 @@ async function runJsonEventsMode(params) {
84273
84595
  thinkingLevel
84274
84596
  },
84275
84597
  apiKey: runtime.apiKey,
84276
- streamFn: params.streamFn
84598
+ // Body-free transport timeline for the main (JSON-events) agent.
84599
+ streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84277
84600
  });
84278
84601
  controller = new AgentSessionController({
84279
84602
  agent,
@@ -84302,6 +84625,10 @@ async function runJsonEventsMode(params) {
84302
84625
  latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
84303
84626
  isContextOverflowError: isCliContextOverflowError
84304
84627
  }),
84628
+ getCompactionBudgetTokens: createCliCompactionBudget({
84629
+ contextWindow: runtime.model.contextWindow,
84630
+ reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
84631
+ }),
84305
84632
  compaction: {
84306
84633
  compact: async (context) => createCliAutoCompactionResult({
84307
84634
  context,
@@ -84835,7 +85162,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
84835
85162
  }
84836
85163
  async function createCliTestStreamFnFromEnv() {
84837
85164
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
84838
- return (await import("./chunks/test-stream-BWBJTFNI.js")).createTestStreamFnFromSequenceEnv();
85165
+ return (await import("./chunks/test-stream-A5ZELKNG.js")).createTestStreamFnFromSequenceEnv();
84839
85166
  }
84840
85167
  function resolveJsonEventsDetailFlag() {
84841
85168
  for (let index = 0; index < process.argv.length; index += 1) {