@cydm/pie 2.6.12 → 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.
Files changed (35) hide show
  1. package/README.md +85 -3
  2. package/dist/builtin/extensions/ask-user/index.js +3 -3
  3. package/dist/builtin/extensions/plan-mode/index.js +130 -21
  4. package/dist/builtin/extensions/subagent/index.js +15 -6
  5. package/dist/builtin/extensions/todo/index.js +3 -3
  6. package/dist/builtin/skills/computer-use-axi/SKILL.md +172 -0
  7. package/dist/builtin/{extensions/computer-use/index.js → skills/computer-use-axi/computer-use-axi-broker.js} +2122 -708
  8. package/dist/builtin/skills/computer-use-axi/computer-use-axi-cli.js +37 -0
  9. package/dist/builtin/skills/computer-use-axi/computer-use-axi-runtime.js +728 -0
  10. package/dist/builtin/skills/computer-use-axi/package.json +11 -0
  11. package/dist/chunks/{chunk-PPEWRUMQ.js → chunk-M44CWVDZ.js} +1249 -752
  12. package/dist/chunks/{chunk-HHNOENEH.js → chunk-NJFPBU36.js} +14 -1
  13. package/dist/chunks/{chunk-AAEBH6KX.js → chunk-W74NRSRV.js} +239 -231
  14. package/dist/chunks/{chunk-7B4IIBVS.js → chunk-XUROMYTT.js} +1747 -14
  15. package/dist/chunks/{src-OWQI5M6W.js → src-C4KA4G75.js} +2 -2
  16. package/dist/chunks/{test-stream-Y7WXELAJ.js → test-stream-A5ZELKNG.js} +1 -1
  17. package/dist/cli.js +863 -305
  18. package/models.schema.json +57 -0
  19. package/package.json +3 -3
  20. package/dist/builtin/extensions/computer-use/package.json +0 -14
  21. package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +0 -278
  22. package/dist/builtin/extensions/computer-use/scripts/test-native-retention.mjs +0 -204
  23. package/dist/chunks/chunk-5353DEEC.js +0 -130
  24. package/dist/chunks/webapi-MBCKO7ZV.js +0 -2481
  25. package/dist/code-intel-worker.cjs +0 -211227
  26. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/arm64/linux-bridge +0 -0
  27. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/x64/linux-bridge +0 -0
  28. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/arm64/pie-computer-use +0 -0
  29. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/Info.plist +0 -0
  30. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
  31. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/_CodeSignature/CodeResources +0 -0
  32. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/x64/pie-computer-use +0 -0
  33. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/windows/windows-bridge.exe +0 -0
  34. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/helper-path.mjs +0 -0
  35. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/setup-helper.mjs +0 -0
package/dist/cli.js CHANGED
@@ -1,18 +1,38 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
3
3
  import {
4
+ VERSION,
4
5
  cleanupBashOutputArtifacts,
5
6
  createBashTool,
6
7
  createCliHostCapabilities,
7
8
  createCliWebResearchHealthStore,
9
+ createTraceTransportSink,
10
+ createTransportDiagnosticsStreamFn,
8
11
  formatSize,
12
+ getAgentDir,
13
+ getBuiltinDir,
14
+ getConfigDir,
15
+ getCustomThemesDir,
16
+ getLogsDir,
17
+ getPackageDir,
18
+ getProjectSettingsPath,
19
+ getRuntimeLogPath,
20
+ getSessionsDir,
21
+ getSettingsPath,
22
+ getThemesDir,
23
+ logTransportLifecycle,
24
+ migrateConfigFromAgentDir,
9
25
  truncateHeadTail
10
- } from "./chunks/chunk-AAEBH6KX.js";
26
+ } from "./chunks/chunk-W74NRSRV.js";
27
+ import {
28
+ resolveCliProjectRoot
29
+ } from "./chunks/chunk-WLFOBECN.js";
11
30
  import {
12
31
  AGENTS_CONTEXT_FILE_NAME,
13
32
  AgentSessionController,
14
33
  AttentionCoordinator,
15
34
  BUILTIN_TOOL_CAPABILITY_METADATA,
35
+ DEFAULT_COMPACTION_CONFIG,
16
36
  ExtensionResourceScope,
17
37
  FileSessionStore,
18
38
  SessionTraceSnapshotWriter,
@@ -30,6 +50,7 @@ import {
30
50
  createFileObservationState,
31
51
  createPolicyEnforcedTools,
32
52
  createReadFileUnderstandingHandler,
53
+ createSearchContextTool,
33
54
  createSessionManager,
34
55
  createSharedFileSystemTools,
35
56
  createSharedWebSearchTool,
@@ -51,20 +72,22 @@ import {
51
72
  interactionAttentionResolution,
52
73
  isCompactSummaryMessage,
53
74
  isExtensionEnabled,
75
+ isToolAllowedInPlanMode,
54
76
  maybeAdvanceTodoExecutionState,
55
77
  modalityForReadFileUnderstandingKind,
56
78
  pauseExecutionState,
79
+ prepareSessionCompaction,
57
80
  registerInteractionHandler,
58
81
  requestInteraction,
59
82
  resolveExtensionIdentity,
60
83
  restoreExecutionState,
61
- selectToolsForRuntimePolicy,
62
84
  setEnablementInMap,
63
85
  setExtensionEnabledInSettings,
64
86
  shouldPreserveExecutionStateForUserText,
65
- supersedeExecutionState
66
- } from "./chunks/chunk-PPEWRUMQ.js";
67
- import "./chunks/chunk-HHNOENEH.js";
87
+ supersedeExecutionState,
88
+ verifyPreparedCompactionCommit
89
+ } from "./chunks/chunk-M44CWVDZ.js";
90
+ import "./chunks/chunk-NJFPBU36.js";
68
91
  import {
69
92
  Deref,
70
93
  EMPTY_ASSISTANT_CONTENT_ERROR,
@@ -87,26 +110,9 @@ import {
87
110
  requiredCapabilityForToolPurpose,
88
111
  setLogger,
89
112
  sortToolModelCandidatesByCapability,
113
+ streamSimple,
90
114
  type_exports
91
- } from "./chunks/chunk-7B4IIBVS.js";
92
- import {
93
- VERSION,
94
- getAgentDir,
95
- getBuiltinDir,
96
- getConfigDir,
97
- getCustomThemesDir,
98
- getLogsDir,
99
- getPackageDir,
100
- getProjectSettingsPath,
101
- getRuntimeLogPath,
102
- getSessionsDir,
103
- getSettingsPath,
104
- getThemesDir,
105
- migrateConfigFromAgentDir
106
- } from "./chunks/chunk-5353DEEC.js";
107
- import {
108
- resolveCliProjectRoot
109
- } from "./chunks/chunk-WLFOBECN.js";
115
+ } from "./chunks/chunk-XUROMYTT.js";
110
116
  import {
111
117
  IsArray,
112
118
  IsBigInt,
@@ -51971,6 +51977,7 @@ var MODEL_OVERRIDE_KEYS = [
51971
51977
  "cost",
51972
51978
  "contextWindow",
51973
51979
  "maxTokens",
51980
+ "transport",
51974
51981
  "headers",
51975
51982
  "compat",
51976
51983
  "cacheCapability",
@@ -51988,9 +51995,11 @@ function validateExtensionProviderConfig(provider, config) {
51988
51995
  throw new Error(`registerProvider("${provider}"): baseUrl must be an absolute URL`);
51989
51996
  }
51990
51997
  assertHeadersShape(config.headers, `registerProvider("${provider}")`);
51998
+ validateTransport(config.transport, `registerProvider("${provider}")`);
51991
51999
  for (const model of config.models ?? []) {
51992
52000
  if (!model || typeof model.id !== "string" || !model.id.trim() || /\s/.test(model.id)) throw new Error(`registerProvider("${provider}"): invalid model id`);
51993
52001
  assertHeadersShape(model.headers, `registerProvider("${provider}") model "${model.id}"`);
52002
+ validateTransport(model.transport, `Model "${model.id}"`);
51994
52003
  validateCompat(model.compat, `registerProvider("${provider}") model "${model.id}" compat`);
51995
52004
  validateCacheCapability(model.cacheCapability, `registerProvider("${provider}") model "${model.id}" cacheCapability`);
51996
52005
  validateWebSearchCapability(model.webSearch, `registerProvider("${provider}") model "${model.id}" webSearch`);
@@ -51998,7 +52007,7 @@ function validateExtensionProviderConfig(provider, config) {
51998
52007
  }
51999
52008
  }
52000
52009
  var REMOTE_CATALOG_CACHE_VERSION = 4;
52001
- var SUPPORTED_REMOTE_APIS = /* @__PURE__ */ new Set(["openai-completions", "anthropic-messages"]);
52010
+ var SUPPORTED_REMOTE_APIS = /* @__PURE__ */ new Set(["openai-completions", "anthropic-messages", "openai-responses"]);
52002
52011
  var MAX_REMOTE_CATALOG_BYTES = 2 * 1024 * 1024;
52003
52012
  var OAUTH_RESTORE_LEEWAY_MS = 6e4;
52004
52013
  var OAUTH_REFRESH_TIMEOUT_MS = 3e4;
@@ -52495,11 +52504,17 @@ function validateFileCapabilities(value, label) {
52495
52504
  }
52496
52505
  }
52497
52506
  }
52507
+ function validateTransport(value, label) {
52508
+ if (value !== void 0 && value !== "sse" && value !== "websocket" && value !== "auto") throw new Error(`${label}.transport is invalid`);
52509
+ }
52498
52510
  function validateCompat(value, label) {
52499
52511
  if (value === void 0) return;
52500
52512
  if (!isPlainRecord(value)) throw new Error(`${label} is invalid`);
52501
52513
  assertOnlyKeys(value, [
52502
52514
  "forcedReasoningEffort",
52515
+ "store",
52516
+ "supportsEncryptedReasoning",
52517
+ "responsesContinuation",
52503
52518
  "supportsStore",
52504
52519
  "supportsDeveloperRole",
52505
52520
  "supportsReasoningEffort",
@@ -52517,6 +52532,9 @@ function validateCompat(value, label) {
52517
52532
  throw new Error(`${label}.forcedReasoningEffort must be "none"`);
52518
52533
  }
52519
52534
  for (const field of [
52535
+ "store",
52536
+ "supportsEncryptedReasoning",
52537
+ "responsesContinuation",
52520
52538
  "supportsStore",
52521
52539
  "supportsDeveloperRole",
52522
52540
  "supportsReasoningEffort",
@@ -52537,12 +52555,14 @@ function validateRemoteModel(profileId, model) {
52537
52555
  if (!isPlainRecord(model) || typeof model.id !== "string" || !model.id.trim() || model.id !== model.id.trim() || /\s/.test(model.id)) throw new Error(`Remote profile "${profileId}" contains an invalid model id`);
52538
52556
  assertOnlyKeys(model, [
52539
52557
  "id",
52558
+ "api",
52540
52559
  "name",
52541
52560
  "reasoning",
52542
52561
  "input",
52543
52562
  "cost",
52544
52563
  "contextWindow",
52545
52564
  "maxTokens",
52565
+ "transport",
52546
52566
  "compat",
52547
52567
  "cacheCapability",
52548
52568
  "webSearch",
@@ -52556,19 +52576,21 @@ function validateRemoteModel(profileId, model) {
52556
52576
  for (const field of ["name", "displayName", "filePlatform", "attachmentPlatform", "apiKeyEnv"]) {
52557
52577
  assertOptionalString(model[field], `Remote profile "${profileId}" model "${model.id}" ${field}`);
52558
52578
  }
52579
+ if (model.api !== void 0 && !SUPPORTED_REMOTE_APIS.has(model.api)) throw new Error(`Model "${model.id}" has unsupported api ${model.api}`);
52559
52580
  assertOptionalBoolean(model.reasoning, `Remote profile "${profileId}" model "${model.id}" reasoning`);
52560
52581
  if (!Array.isArray(model.input) || model.input.length === 0 || model.input.some((item) => !["text", "image", "audio", "video"].includes(item))) {
52561
52582
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid input capabilities`);
52562
52583
  }
52563
52584
  if (!isPlainRecord(model.cost)) throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid cost values`);
52564
- assertOnlyKeys(model.cost, ["input", "output", "cacheRead", "cacheWrite"], `Remote profile "${profileId}" model "${model.id}" cost`);
52565
- 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))) {
52566
52587
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid cost values`);
52567
52588
  }
52568
52589
  if (!Number.isSafeInteger(model.contextWindow) || Number(model.contextWindow) <= 0 || !Number.isSafeInteger(model.maxTokens) || Number(model.maxTokens) <= 0 || Number(model.maxTokens) > Number(model.contextWindow)) {
52569
52590
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid token limits`);
52570
52591
  }
52571
52592
  if (new Set(model.input).size !== model.input.length) throw new Error(`Remote profile "${profileId}" model "${model.id}" has duplicate input capabilities`);
52593
+ validateTransport(model.transport, `Model "${model.id}"`);
52572
52594
  validateCompat(model.compat, `Remote profile "${profileId}" model "${model.id}" compat`);
52573
52595
  validateCacheCapability(model.cacheCapability, `Remote profile "${profileId}" model "${model.id}" cacheCapability`);
52574
52596
  validateWebSearchCapability(model.webSearch, `Remote profile "${profileId}" model "${model.id}" webSearch`);
@@ -52604,6 +52626,7 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52604
52626
  if (!provider || provider !== provider.trim() || /\s/.test(provider) || !isPlainRecord(source)) throw new Error("catalog contains an invalid profile");
52605
52627
  assertOnlyKeys(source, [
52606
52628
  "baseUrl",
52629
+ "endpoints",
52607
52630
  "api",
52608
52631
  "apiKey",
52609
52632
  "apiKeyEnv",
@@ -52611,6 +52634,7 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52611
52634
  "displayName",
52612
52635
  "filePlatform",
52613
52636
  "attachmentPlatform",
52637
+ "transport",
52614
52638
  "compat",
52615
52639
  "cacheCapability",
52616
52640
  "webSearch",
@@ -52626,8 +52650,13 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52626
52650
  assertOptionalString(source[field], `Remote profile "${provider}" ${field}`);
52627
52651
  }
52628
52652
  validateRemoteEndpoint(source.baseUrl, catalogUrl, `Remote profile "${provider}"`);
52653
+ validateProfileEndpoints(source, `Remote profile "${provider}"`);
52654
+ for (const [api, endpoint] of Object.entries(source.endpoints ?? {})) {
52655
+ validateRemoteEndpoint(endpoint, catalogUrl, `Remote profile "${provider}" endpoints.${api}`);
52656
+ }
52629
52657
  if (!Array.isArray(source.models) || source.models.length === 0) throw new Error(`Remote profile "${provider}" has no models`);
52630
52658
  assertRemoteHeadersSafe(source.headers, `Remote profile "${provider}"`);
52659
+ validateTransport(source.transport, `Remote profile "${provider}"`);
52631
52660
  validateCompat(source.compat, `Remote profile "${provider}" compat`);
52632
52661
  validateCacheCapability(source.cacheCapability, `Remote profile "${provider}" cacheCapability`);
52633
52662
  validateWebSearchCapability(source.webSearch, `Remote profile "${provider}" webSearch`);
@@ -52644,7 +52673,9 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52644
52673
  }
52645
52674
  const sanitized = {
52646
52675
  baseUrl: source.baseUrl,
52676
+ ...source.endpoints === void 0 ? {} : { endpoints: { ...source.endpoints } },
52647
52677
  api: source.api,
52678
+ transport: source.transport,
52648
52679
  apiKeyEnv: remote.apiKeyEnv,
52649
52680
  ...source.headers === void 0 ? {} : { headers: { ...source.headers } },
52650
52681
  ...source.displayName === void 0 ? {} : { displayName: source.displayName },
@@ -52656,6 +52687,7 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52656
52687
  ...source.fileCapabilities === void 0 ? {} : { fileCapabilities: { ...source.fileCapabilities } },
52657
52688
  models: source.models.map((model) => ({
52658
52689
  id: model.id,
52690
+ ...model.api === void 0 ? {} : { api: model.api },
52659
52691
  ...model.name === void 0 ? {} : { name: model.name },
52660
52692
  ...model.reasoning === void 0 ? {} : { reasoning: model.reasoning },
52661
52693
  input: [...model.input],
@@ -52663,10 +52695,12 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52663
52695
  input: model.cost.input,
52664
52696
  output: model.cost.output,
52665
52697
  cacheRead: model.cost.cacheRead,
52666
- cacheWrite: model.cost.cacheWrite
52698
+ cacheWrite: model.cost.cacheWrite,
52699
+ ...model.cost.cacheWriteLong === void 0 ? {} : { cacheWriteLong: model.cost.cacheWriteLong }
52667
52700
  },
52668
52701
  contextWindow: model.contextWindow,
52669
52702
  maxTokens: model.maxTokens,
52703
+ ...model.transport === void 0 ? {} : { transport: model.transport },
52670
52704
  ...model.compat === void 0 ? {} : { compat: { ...model.compat } },
52671
52705
  ...model.cacheCapability === void 0 ? {} : { cacheCapability: { ...model.cacheCapability } },
52672
52706
  ...model.webSearch === void 0 ? {} : { webSearch: { ...model.webSearch } },
@@ -52729,8 +52763,8 @@ function validateModelOverrides(value) {
52729
52763
  if (override.maxTokens !== void 0 && (!Number.isInteger(override.maxTokens) || Number(override.maxTokens) < 1)) throw new Error(`models.json modelOverrides["${key}"].maxTokens is invalid`);
52730
52764
  if (override.cost !== void 0) {
52731
52765
  if (!isPlainRecord(override.cost)) throw new Error(`models.json modelOverrides["${key}"].cost must be an object`);
52732
- for (const field of ["input", "output", "cacheRead", "cacheWrite"]) {
52733
- 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`);
52734
52768
  }
52735
52769
  }
52736
52770
  if (override.headers !== void 0) {
@@ -52739,16 +52773,41 @@ function validateModelOverrides(value) {
52739
52773
  if (typeof headerValue !== "string") throw new Error(`models.json modelOverrides["${key}"].headers.${headerName} must be a string`);
52740
52774
  }
52741
52775
  }
52776
+ validateTransport(override.transport, `Model override "${key}"`);
52742
52777
  validateCompat(override.compat, `models.json modelOverrides["${key}"].compat`);
52743
52778
  validateCacheCapability(override.cacheCapability, `models.json modelOverrides["${key}"].cacheCapability`);
52744
52779
  validateWebSearchCapability(override.webSearch, `models.json modelOverrides["${key}"].webSearch`);
52745
52780
  validateFileCapabilities(override.fileCapabilities, `models.json modelOverrides["${key}"].fileCapabilities`);
52746
52781
  }
52747
52782
  }
52783
+ function validateProfileEndpoints(profile, label) {
52784
+ if (profile.endpoints === void 0) return;
52785
+ if (!isPlainRecord(profile.endpoints)) throw new Error(`${label} endpoints must be an object`);
52786
+ for (const [api, value] of Object.entries(profile.endpoints)) {
52787
+ if (!SUPPORTED_REMOTE_APIS.has(api)) throw new Error(`${label} endpoints has unsupported api ${api}`);
52788
+ try {
52789
+ if (typeof value !== "string") throw new Error("invalid URL");
52790
+ const endpoint = new URL(value);
52791
+ if (endpoint.protocol !== "http:" && endpoint.protocol !== "https:") throw new Error("unsupported protocol");
52792
+ } catch {
52793
+ throw new Error(`${label} endpoints.${api} must be an absolute HTTP(S) URL`);
52794
+ }
52795
+ }
52796
+ }
52797
+ function resolveProfileRoute(profile, model) {
52798
+ const api = model.api ?? profile.api;
52799
+ if (!SUPPORTED_REMOTE_APIS.has(api)) throw new Error(`Model "${model.id}" has unsupported api ${api}`);
52800
+ const endpoint = profile.endpoints?.[api];
52801
+ if (model.api !== void 0 && endpoint === void 0 && api !== profile.api) {
52802
+ throw new Error(`Model "${model.id}" api ${api} requires profile endpoints.${api} (otherwise api must equal profile api)`);
52803
+ }
52804
+ return { api, baseUrl: endpoint ?? profile.baseUrl };
52805
+ }
52748
52806
  function validateProfileConfig(profileId, profileConfig) {
52749
52807
  if (!isPlainRecord(profileConfig)) throw new Error(`Profile "${profileId}" must be an object.`);
52750
52808
  assertOnlyKeys(profileConfig, [
52751
52809
  "baseUrl",
52810
+ "endpoints",
52752
52811
  "api",
52753
52812
  "apiKey",
52754
52813
  "apiKeyEnv",
@@ -52756,6 +52815,7 @@ function validateProfileConfig(profileId, profileConfig) {
52756
52815
  "displayName",
52757
52816
  "filePlatform",
52758
52817
  "attachmentPlatform",
52818
+ "transport",
52759
52819
  "compat",
52760
52820
  "cacheCapability",
52761
52821
  "webSearch",
@@ -52787,7 +52847,9 @@ function validateProfileConfig(profileId, profileConfig) {
52787
52847
  } catch {
52788
52848
  throw new Error(`Profile "${profileId}" baseUrl must be an absolute HTTP(S) URL`);
52789
52849
  }
52850
+ validateProfileEndpoints(profileConfig, `Profile "${profileId}"`);
52790
52851
  assertHeadersShape(profileConfig.headers, `Profile "${profileId}"`);
52852
+ validateTransport(profileConfig.transport, `Profile "${profileId}"`);
52791
52853
  validateCompat(profileConfig.compat, `Profile "${profileId}" compat`);
52792
52854
  validateCacheCapability(profileConfig.cacheCapability, `Profile "${profileId}" cacheCapability`);
52793
52855
  validateWebSearchCapability(profileConfig.webSearch, `Profile "${profileId}" webSearch`);
@@ -52797,12 +52859,14 @@ function validateProfileConfig(profileId, profileConfig) {
52797
52859
  if (!isPlainRecord(modelConfig)) throw new Error(`Profile "${profileId}" model #${index} must be an object.`);
52798
52860
  assertOnlyKeys(modelConfig, [
52799
52861
  "id",
52862
+ "api",
52800
52863
  "name",
52801
52864
  "reasoning",
52802
52865
  "input",
52803
52866
  "cost",
52804
52867
  "contextWindow",
52805
52868
  "maxTokens",
52869
+ "transport",
52806
52870
  "compat",
52807
52871
  "cacheCapability",
52808
52872
  "webSearch",
@@ -52826,6 +52890,7 @@ function validateProfileConfig(profileId, profileConfig) {
52826
52890
  );
52827
52891
  }
52828
52892
  validateRemoteModel(profileId, modelConfig);
52893
+ resolveProfileRoute(profileConfig, modelConfig);
52829
52894
  assertHeadersShape(modelConfig.headers, `Profile "${profileId}" model "${modelConfig.id}"`);
52830
52895
  assertOptionalEnvName(modelConfig.apiKeyEnv, `Profile "${profileId}" model "${modelConfig.id}" apiKeyEnv`);
52831
52896
  if (seenModelIds.has(modelConfig.id)) throw new Error(`Profile "${profileId}" contains duplicate model "${modelConfig.id}".`);
@@ -52916,12 +52981,13 @@ var ModelRegistry = class {
52916
52981
  );
52917
52982
  }
52918
52983
  const existing = this.models.get(provider);
52919
- if (existing && (providerConfig.baseUrl || providerConfig.api || providerConfig.headers)) {
52984
+ if (existing && (providerConfig.baseUrl || providerConfig.api || providerConfig.headers || providerConfig.transport)) {
52920
52985
  for (const model of existing.values()) {
52921
52986
  this.upsert({
52922
52987
  ...model,
52923
52988
  baseUrl: providerConfig.baseUrl || model.baseUrl,
52924
52989
  api: providerConfig.api || model.api,
52990
+ transport: providerConfig.transport ?? model.transport,
52925
52991
  apiKeyEnv: providerConfig.apiKeyEnv || model.apiKeyEnv,
52926
52992
  displayName: providerConfig.displayName || model.displayName || provider,
52927
52993
  filePlatform: providerConfig.filePlatform || model.filePlatform,
@@ -52938,9 +53004,8 @@ var ModelRegistry = class {
52938
53004
  this.upsert({
52939
53005
  id: customModel.id,
52940
53006
  name: customModel.name || baseModel?.name || customModel.id,
52941
- api: providerConfig.api || baseModel?.api || "openai-completions",
53007
+ ...resolveProfileRoute(providerConfig, customModel),
52942
53008
  provider,
52943
- baseUrl: providerConfig.baseUrl || baseModel?.baseUrl || "",
52944
53009
  reasoning: customModel.reasoning ?? baseModel?.reasoning ?? false,
52945
53010
  input: customModel.input || baseModel?.input || ["text"],
52946
53011
  cost: customModel.cost || baseModel?.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@@ -52951,6 +53016,7 @@ var ModelRegistry = class {
52951
53016
  filePlatform: customModel.filePlatform || providerConfig.filePlatform || baseModel?.filePlatform,
52952
53017
  attachmentPlatform: customModel.attachmentPlatform || providerConfig.attachmentPlatform || baseModel?.attachmentPlatform,
52953
53018
  headers: providerConfig.headers ? { ...baseModel?.headers, ...providerConfig.headers, ...customModel.headers } : customModel.headers || baseModel?.headers,
53019
+ transport: customModel.transport ?? providerConfig.transport ?? baseModel?.transport,
52954
53020
  compat: customModel.compat || providerConfig.compat || baseModel?.compat,
52955
53021
  cacheCapability: customModel.cacheCapability || providerConfig.cacheCapability || baseModel?.cacheCapability,
52956
53022
  webSearch: customModel.webSearch || providerConfig.webSearch || baseModel?.webSearch,
@@ -53003,6 +53069,7 @@ var ModelRegistry = class {
53003
53069
  if (override.maxTokens !== void 0) patched.maxTokens = override.maxTokens;
53004
53070
  if (override.cost !== void 0) patched.cost = { ...existing.cost, ...override.cost };
53005
53071
  if (override.headers !== void 0) patched.headers = { ...existing.headers, ...override.headers };
53072
+ if (override.transport !== void 0) patched.transport = override.transport;
53006
53073
  if (override.compat !== void 0) patched.compat = deepMergePlainRecords(existing.compat, override.compat);
53007
53074
  if (override.cacheCapability !== void 0) patched.cacheCapability = { ...existing.cacheCapability, ...override.cacheCapability };
53008
53075
  if (override.webSearch !== void 0) patched.webSearch = { ...existing.webSearch, ...override.webSearch };
@@ -53024,9 +53091,8 @@ var ModelRegistry = class {
53024
53091
  this.upsert({
53025
53092
  id: customModel.id,
53026
53093
  name: customModel.name || customModel.id,
53027
- api: providerConfig.api || "openai-completions",
53094
+ ...resolveProfileRoute(providerConfig, customModel),
53028
53095
  provider,
53029
- baseUrl: providerConfig.baseUrl || "",
53030
53096
  reasoning: customModel.reasoning ?? false,
53031
53097
  input: customModel.input || ["text"],
53032
53098
  cost: customModel.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@@ -53037,6 +53103,7 @@ var ModelRegistry = class {
53037
53103
  filePlatform: customModel.filePlatform || providerConfig.filePlatform,
53038
53104
  attachmentPlatform: customModel.attachmentPlatform || providerConfig.attachmentPlatform,
53039
53105
  headers: { ...providerConfig.headers, ...customModel.headers },
53106
+ transport: customModel.transport ?? providerConfig.transport,
53040
53107
  compat: customModel.compat || providerConfig.compat,
53041
53108
  cacheCapability: customModel.cacheCapability || providerConfig.cacheCapability,
53042
53109
  webSearch: customModel.webSearch || providerConfig.webSearch,
@@ -53476,12 +53543,13 @@ var ModelRegistry = class {
53476
53543
  this.authStorage.setProviderEnvVar(provider, config.apiKeyEnv);
53477
53544
  }
53478
53545
  const existing = this.models.get(provider);
53479
- if (existing && (config.baseUrl || config.api || config.headers)) {
53546
+ if (existing && (config.baseUrl || config.api || config.headers || config.transport)) {
53480
53547
  for (const model of existing.values()) {
53481
53548
  this.upsert({
53482
53549
  ...model,
53483
53550
  baseUrl: config.baseUrl || model.baseUrl,
53484
53551
  api: config.api || model.api,
53552
+ transport: config.transport ?? model.transport,
53485
53553
  headers: config.headers ? { ...model.headers, ...config.headers } : model.headers
53486
53554
  });
53487
53555
  }
@@ -53502,6 +53570,7 @@ var ModelRegistry = class {
53502
53570
  apiKeyEnv: config.apiKeyEnv || baseModel?.apiKeyEnv,
53503
53571
  displayName: customModel.displayName || baseModel?.displayName || provider,
53504
53572
  headers: config.headers ? { ...baseModel?.headers, ...config.headers, ...customModel.headers } : customModel.headers || baseModel?.headers,
53573
+ transport: customModel.transport ?? config.transport ?? baseModel?.transport,
53505
53574
  compat: customModel.compat || baseModel?.compat,
53506
53575
  cacheCapability: customModel.cacheCapability || baseModel?.cacheCapability,
53507
53576
  webSearch: customModel.webSearch || baseModel?.webSearch,
@@ -53794,6 +53863,16 @@ var SettingsManager = class {
53794
53863
  this.settings.autoCompactReserveTokens = tokens;
53795
53864
  this.writeSettings();
53796
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
+ }
53797
53876
  getWebSearchMode() {
53798
53877
  const mode = this.settings.tools?.webSearch?.mode;
53799
53878
  if (mode === "disabled" || mode === "cached" || mode === "live") {
@@ -54053,7 +54132,7 @@ function checkComputerUse() {
54053
54132
  id: "computer_use_helper",
54054
54133
  label: "computer-use helper",
54055
54134
  status: "warn",
54056
- message: `computer-use helper app is not installed yet; it installs on first tool use to ${appPath}. Afterwards grant Accessibility and Screen Recording to pie-computer-use.app in System Settings \u2192 Privacy & Security.`,
54135
+ message: `computer-use helper app is not installed yet; it installs on first computer-use-axi use to ${appPath}. Afterwards grant Accessibility and Screen Recording to pie-computer-use.app in System Settings \u2192 Privacy & Security.`,
54057
54136
  details: { appPath }
54058
54137
  };
54059
54138
  }
@@ -54341,7 +54420,7 @@ var MODE_RULES = {
54341
54420
  "~/.pie is allowlisted for Pie configuration and session support.",
54342
54421
  "Shell commands run from the workspace and return structured output, timeout, and truncation details.",
54343
54422
  "web_research is read-only, uses provider-native web/search/fetch capabilities, and does not grant bash/curl web scraping permission.",
54344
- "computer-use observe tools only read the screen; act_ui synthesizes real desktop input and requires a first-use confirmation each session.",
54423
+ "Desktop control runs through the computer-use-axi skill CLI via bash: observation commands only read the screen, while act synthesizes real desktop input and goes through normal shell permission prompts.",
54345
54424
  "Protected paths from extensions can block reads/writes even when tools are present."
54346
54425
  ],
54347
54426
  plan: [
@@ -54351,7 +54430,7 @@ var MODE_RULES = {
54351
54430
  yolo: [
54352
54431
  "/settings/yolo relaxes filesystem sandbox directory restrictions.",
54353
54432
  "It does not make destructive shell commands safe; use it only in trusted workspaces.",
54354
- "It also bypasses the computer-use act_ui first-use confirmation."
54433
+ "It also auto-approves shell commands, including computer-use-axi desktop input."
54355
54434
  ],
54356
54435
  subagents: [
54357
54436
  "Subagents inherit the parent tool set, then runtime policy removes write and high-risk tools.",
@@ -55513,10 +55592,11 @@ async function forkStoredSession(params) {
55513
55592
  timestamp: entry.timestamp
55514
55593
  };
55515
55594
  if (copy.compactionData) {
55595
+ const selfAnchored = copy.compactionData.firstKeptEntryId === entry.id;
55516
55596
  const keptId = idMap.get(copy.compactionData.firstKeptEntryId);
55517
55597
  copy.compactionData = {
55518
55598
  ...copy.compactionData,
55519
- firstKeptEntryId: keptId ?? firstCopiedId ?? copy.id
55599
+ firstKeptEntryId: selfAnchored ? copy.id : keptId ?? firstCopiedId ?? copy.id
55520
55600
  };
55521
55601
  }
55522
55602
  idMap.set(summary.id, copy.id);
@@ -56502,7 +56582,10 @@ function createExtensionContext(extension, cwd, uiContext, abortFn, isIdleFn, ha
56502
56582
  },
56503
56583
  get yoloMode() {
56504
56584
  return actions?.yoloMode ?? false;
56505
- }
56585
+ },
56586
+ // Only expose setYoloMode when the host can actually toggle it — plan-mode
56587
+ // gates the option on typeof ctx.setYoloMode === "function".
56588
+ ...actions?.setYoloMode ? { setYoloMode: (enabled) => actions.setYoloMode(enabled) } : {}
56506
56589
  };
56507
56590
  return context;
56508
56591
  }
@@ -60395,8 +60478,8 @@ var Editor = class {
60395
60478
  this.historyIndex = -1;
60396
60479
  this.lastAction = null;
60397
60480
  this.pushUndoSnapshot();
60398
- const cleanText2 = pastedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
60399
- const tabExpandedText = cleanText2.replace(/\t/g, " ");
60481
+ const cleanText = pastedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
60482
+ const tabExpandedText = cleanText.replace(/\t/g, " ");
60400
60483
  let filteredText = tabExpandedText.split("").filter((char) => char === "\n" || char.charCodeAt(0) >= 32).join("");
60401
60484
  if (/^[/~.]/.test(filteredText)) {
60402
60485
  const currentLine = this.state.lines[this.state.cursorLine] || "";
@@ -61480,9 +61563,9 @@ var Input = class {
61480
61563
  handlePaste(pastedText) {
61481
61564
  this.lastAction = null;
61482
61565
  this.pushUndo();
61483
- const cleanText2 = pastedText.replace(/\r\n/g, "").replace(/\r/g, "").replace(/\n/g, "");
61484
- this.value = this.value.slice(0, this.cursor) + cleanText2 + this.value.slice(this.cursor);
61485
- this.cursor += cleanText2.length;
61566
+ const cleanText = pastedText.replace(/\r\n/g, "").replace(/\r/g, "").replace(/\n/g, "");
61567
+ this.value = this.value.slice(0, this.cursor) + cleanText + this.value.slice(this.cursor);
61568
+ this.cursor += cleanText.length;
61486
61569
  }
61487
61570
  invalidate() {
61488
61571
  }
@@ -64255,7 +64338,10 @@ var ExtensionRunner = class {
64255
64338
  },
64256
64339
  get yoloMode() {
64257
64340
  return runner.actions.yoloMode ?? false;
64258
- }
64341
+ },
64342
+ // Only expose setYoloMode when the host can actually toggle it — plan-mode
64343
+ // gates the option on typeof ctx.setYoloMode === "function".
64344
+ ...runner.actions.setYoloMode ? { setYoloMode: (enabled) => runner.actions.setYoloMode(enabled) } : {}
64259
64345
  };
64260
64346
  return createLoggedProxy(context, `ExtensionContext#${contextId}`);
64261
64347
  }
@@ -65005,6 +65091,23 @@ function buildCliFileAccessOptions(params) {
65005
65091
  };
65006
65092
  }
65007
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
+
65008
65111
  // src/theme/theme.ts
65009
65112
  import * as fs13 from "node:fs";
65010
65113
  import * as path12 from "node:path";
@@ -70202,14 +70305,18 @@ function computePromptPrefixHash(systemPrompt, tools) {
70202
70305
  function calculateCumulativeCacheHitRate(messages) {
70203
70306
  let input = 0;
70204
70307
  let cacheRead = 0;
70308
+ let cacheWrite = 0;
70309
+ let cacheWriteLong = 0;
70205
70310
  for (const message of messages) {
70206
70311
  if (message.role !== "assistant") continue;
70207
70312
  const usage = message.usage;
70208
70313
  if (!usage) continue;
70209
70314
  input += usage.input;
70210
70315
  cacheRead += usage.cacheRead;
70316
+ cacheWrite += usage.cacheWrite;
70317
+ cacheWriteLong += usage.cacheWriteLong ?? 0;
70211
70318
  }
70212
- const base = input + cacheRead;
70319
+ const base = input + cacheRead + cacheWrite + cacheWriteLong;
70213
70320
  return base > 0 ? cacheRead / base : null;
70214
70321
  }
70215
70322
  var signalMemories = /* @__PURE__ */ new WeakMap();
@@ -70219,20 +70326,52 @@ function getLastContextSignals(hostKey) {
70219
70326
  }
70220
70327
  function computeContextSignals(hostKey, systemPrompt, tools, messages) {
70221
70328
  const prefixHash = computePromptPrefixHash(systemPrompt, tools);
70329
+ const systemHash = computePromptPrefixHash(systemPrompt, []);
70330
+ const toolsHash = computePromptPrefixHash("", tools);
70222
70331
  const previous = signalMemories.get(hostKey);
70223
70332
  const prefixHashChanged = previous !== void 0 && previous.hash !== prefixHash;
70224
70333
  if (previous?.hash !== prefixHash) {
70225
- signalMemories.set(hostKey, { hash: prefixHash });
70334
+ signalMemories.set(hostKey, { hash: prefixHash, systemHash, toolsHash });
70226
70335
  }
70227
70336
  const signals = {
70228
70337
  prefixHash,
70229
70338
  prefixHashChanged,
70339
+ ...prefixHashChanged && previous ? { previousPrefixHash: previous.hash } : {},
70340
+ systemPromptChanged: prefixHashChanged && previous !== void 0 && previous.systemHash !== systemHash,
70341
+ toolsChanged: prefixHashChanged && previous !== void 0 && previous.toolsHash !== toolsHash,
70230
70342
  cacheHitRate: calculateCumulativeCacheHitRate(messages)
70231
70343
  };
70232
70344
  lastSignals.set(hostKey, signals);
70233
70345
  return signals;
70234
70346
  }
70235
70347
 
70348
+ // src/runtime/cache-invalidation-trace.ts
70349
+ function noteCacheInvalidationInChat(host, signals) {
70350
+ if (!signals || !signals.prefixHashChanged) return;
70351
+ const parts = [];
70352
+ if (signals.systemPromptChanged && signals.toolsChanged) parts.push("system prompt + tools rebuilt");
70353
+ else if (signals.toolsChanged) parts.push("tools rebuilt");
70354
+ else if (signals.systemPromptChanged) parts.push("system prompt rebuilt");
70355
+ else parts.push("prefix rebuilt");
70356
+ const from = signals.previousPrefixHash ?? "?";
70357
+ const line = `\u26A1 Prompt cache invalidated: \u2301${from} \u2192 \u2301${signals.prefixHash} (${parts.join(", ")})`;
70358
+ if (host.chatContainer && host.ui) {
70359
+ host.chatContainer.addChild(new Spacer(1));
70360
+ host.chatContainer.addChild(new Text(theme.fg("warning", line), 1, 0));
70361
+ host.ui.requestRender();
70362
+ }
70363
+ host.sessionTrace?.noteRuntimeEvent("cache_prefix_invalidated", {
70364
+ from,
70365
+ to: signals.prefixHash,
70366
+ systemPromptChanged: signals.systemPromptChanged,
70367
+ toolsChanged: signals.toolsChanged
70368
+ });
70369
+ }
70370
+ function applyContextSignalsWithTrace(host) {
70371
+ applyContextSignalsToFooter(host);
70372
+ noteCacheInvalidationInChat(host, getLastContextSignals(host));
70373
+ }
70374
+
70236
70375
  // src/commands/interactive-session-commands.ts
70237
70376
  import * as os6 from "os";
70238
70377
 
@@ -70434,6 +70573,11 @@ function formatReportTokens(count) {
70434
70573
  if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
70435
70574
  return `${Math.round(count / 1e6)}M`;
70436
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
+ }
70437
70581
  function buildSystemPromptBlocks(systemPrompt, skillsSection, knowledgeSection) {
70438
70582
  const totalTokens = estimateStringTokens(systemPrompt);
70439
70583
  const skillsTokens = skillsSection && systemPrompt.includes(skillsSection.trim()) ? estimateStringTokens(skillsSection) : null;
@@ -70689,8 +70833,12 @@ ${file.content}`).join("\n\n");
70689
70833
  messagesTokens,
70690
70834
  dynamicContextTokens: 0,
70691
70835
  estimatedTotalTokens,
70692
- actualTokens: inputs.actualTokens,
70836
+ estimatedCurrentTokens: inputs.estimatedCurrentTokens,
70837
+ providerMeasuredTokens: inputs.providerMeasuredTokens,
70838
+ providerMeasuredAt: inputs.providerMeasuredAt ?? null,
70839
+ measurementMatchesCurrentContext: inputs.measurementMatchesCurrentContext,
70693
70840
  contextWindow: inputs.contextWindow,
70841
+ autoCompactEnabled: inputs.autoCompactEnabled,
70694
70842
  autoCompactLimitTokens: inputs.autoCompactLimitTokens,
70695
70843
  reserveTokens: inputs.reserveTokens,
70696
70844
  promptPrefixHash: inputs.promptPrefixHash,
@@ -70747,13 +70895,14 @@ function buildContextOverviewText(data, themeInput) {
70747
70895
  }
70748
70896
  }
70749
70897
  });
70750
- 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)";
70751
70899
  lines.push(
70752
70900
  `${padEndVisible(` ${branch(sections.length)} auto-compact`, labelWidth)}${theme2.fg("dim", autoCompact)}`
70753
70901
  );
70754
- const reconciliation = data.actualTokens !== null ? `actual ${formatReportTokens(data.actualTokens)} (last response) \xB7 est\u2212actual \u2248 ${formatReportTokens(
70755
- Math.abs(data.estimatedTotalTokens - data.actualTokens)
70756
- )}${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`;
70757
70906
  lines.push(` ${theme2.fg("dim", reconciliation)}`);
70758
70907
  lines.push(theme2.fg("dim", " /debugs/context \xB7 Enter to drill into a section, Esc to go back"));
70759
70908
  return lines.join("\n");
@@ -70772,23 +70921,37 @@ function buildWindowRows(data) {
70772
70921
  label: "estimated total (prompt + tools + messages)",
70773
70922
  estTokens: data.estimatedTotalTokens,
70774
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"
70775
70930
  }
70776
70931
  ];
70777
- if (data.actualTokens !== null) {
70932
+ if (data.providerMeasuredTokens !== null) {
70933
+ const measuredAt = data.providerMeasuredAt !== null ? formatMeasuredTime(data.providerMeasuredAt) : "unknown time";
70778
70934
  rows.push({
70779
- id: "window:actual",
70780
- label: "actual (last model response)",
70781
- estTokens: data.actualTokens,
70782
- valueText: formatReportTokens(data.actualTokens),
70783
- 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}`
70784
70940
  });
70785
70941
  }
70786
70942
  rows.push({
70787
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",
70788
70951
  label: "auto-compact threshold",
70789
- estTokens: data.autoCompactLimitTokens ?? 0,
70790
- valueText: data.autoCompactLimitTokens ? `>${formatReportTokens(data.autoCompactLimitTokens)}` : "off",
70791
- 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"
70792
70955
  });
70793
70956
  if (data.promptPrefixHash) {
70794
70957
  rows.push({
@@ -71171,6 +71334,9 @@ var TreeSelectorComponent = class {
71171
71334
  pageNext() {
71172
71335
  if (this.goToNextPage()) {
71173
71336
  this.statusMessage = null;
71337
+ } else if (this.filteredRows.length > 0) {
71338
+ this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71339
+ this.statusMessage = "latest page - at newest entry";
71174
71340
  } else {
71175
71341
  this.statusMessage = this.pageSummaries.length === 0 ? "no entries" : "latest page";
71176
71342
  }
@@ -71178,10 +71344,58 @@ var TreeSelectorComponent = class {
71178
71344
  pagePrev() {
71179
71345
  if (this.goToPreviousPage()) {
71180
71346
  this.statusMessage = null;
71347
+ } else if (this.filteredRows.length > 0) {
71348
+ this.selectedIndex = 0;
71349
+ this.statusMessage = "oldest page - at first entry";
71181
71350
  } else {
71182
71351
  this.statusMessage = this.pageSummaries.length === 0 ? "no entries" : "oldest page";
71183
71352
  }
71184
71353
  }
71354
+ /** Jump to the oldest page (seqs start at 1; afterSeq 0 = from the start). */
71355
+ jumpToFirstPage() {
71356
+ if (this.pageSummaries.length > 0 && !this.hasPrev) {
71357
+ this.selectedIndex = 0;
71358
+ this.statusMessage = "oldest page - at first entry";
71359
+ return;
71360
+ }
71361
+ if (this.fetchWindow(0, null)) {
71362
+ this.selectedIndex = 0;
71363
+ this.statusMessage = null;
71364
+ }
71365
+ }
71366
+ /** Jump to the newest page. Seqs are 1..total (append-only journal), so the last window starts at total - pageSize. */
71367
+ jumpToLastPage() {
71368
+ if (this.pageSummaries.length > 0 && !this.hasNext) {
71369
+ this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71370
+ this.statusMessage = "latest page - at newest entry";
71371
+ return;
71372
+ }
71373
+ let anchor = Math.max(0, this.totalEntries - this.pageSizeValue);
71374
+ for (let attempt = 0; attempt < 4; attempt++) {
71375
+ if (this.fetchWindow(anchor, null)) {
71376
+ if (this.hasNext) {
71377
+ anchor = this.pageSummaries[this.pageSummaries.length - 1].seq;
71378
+ continue;
71379
+ }
71380
+ this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71381
+ this.statusMessage = null;
71382
+ return;
71383
+ }
71384
+ if (anchor === 0 || !this.hasNext) break;
71385
+ anchor = Math.max(0, anchor - this.pageSizeValue);
71386
+ }
71387
+ }
71388
+ /** Move the cursor one viewport within the resident page; ←/→ change pages. */
71389
+ screenScroll(delta) {
71390
+ if (this.filteredRows.length === 0) return;
71391
+ const next = Math.max(0, Math.min(this.selectedIndex + delta, this.filteredRows.length - 1));
71392
+ if (next === this.selectedIndex) {
71393
+ this.statusMessage = delta > 0 ? "end of page - \u2192 for next page" : "start of page - \u2190 for previous page";
71394
+ return;
71395
+ }
71396
+ this.selectedIndex = next;
71397
+ this.statusMessage = null;
71398
+ }
71185
71399
  /**
71186
71400
  * Bounded "next match": step to the next in-page match, otherwise scan
71187
71401
  * forward at most SEARCH_SCAN_PAGE_LIMIT pages per press. Never scans
@@ -71235,7 +71449,7 @@ var TreeSelectorComponent = class {
71235
71449
  lines.push(createLine(" Conversation Tree", width, false));
71236
71450
  lines.push(
71237
71451
  createLine(
71238
- " \u2191\u2193:move \u2190\u2192:page e:collapse tab:next match F:fork Enter:navigate Esc:cancel",
71452
+ " \u2191\u2193:move \u2190\u2192:page PgUp/PgDn:screen Home/End:jump e:collapse tab:match F:fork Enter:go Esc:cancel",
71239
71453
  width,
71240
71454
  false
71241
71455
  )
@@ -71304,10 +71518,18 @@ var TreeSelectorComponent = class {
71304
71518
  this.moveUp();
71305
71519
  } else if (kb.matches(keyData, "selectDown")) {
71306
71520
  this.moveDown();
71307
- } else if (kb.matches(keyData, "cursorRight") || kb.matches(keyData, "selectPageDown")) {
71521
+ } else if (kb.matches(keyData, "cursorRight")) {
71308
71522
  this.pageNext();
71309
- } else if (kb.matches(keyData, "cursorLeft") || kb.matches(keyData, "selectPageUp")) {
71523
+ } else if (kb.matches(keyData, "cursorLeft")) {
71310
71524
  this.pagePrev();
71525
+ } else if (kb.matches(keyData, "selectPageDown")) {
71526
+ this.screenScroll(this.maxVisibleLines);
71527
+ } else if (kb.matches(keyData, "selectPageUp")) {
71528
+ this.screenScroll(-this.maxVisibleLines);
71529
+ } else if (kb.matches(keyData, "cursorLineStart")) {
71530
+ this.jumpToFirstPage();
71531
+ } else if (kb.matches(keyData, "cursorLineEnd")) {
71532
+ this.jumpToLastPage();
71311
71533
  } else if (keyData === "e" || keyData === "E") {
71312
71534
  this.toggleCollapse();
71313
71535
  } else if (kb.matches(keyData, "selectConfirm")) {
@@ -72622,7 +72844,9 @@ function calculateSessionUsageTotals(messages) {
72622
72844
  let output = 0;
72623
72845
  let cacheRead = 0;
72624
72846
  let cacheWrite = 0;
72847
+ let cacheWriteLong = 0;
72625
72848
  let cost = 0;
72849
+ let costEstimated = false;
72626
72850
  for (const message of messages) {
72627
72851
  if (message.role !== "assistant") continue;
72628
72852
  const usage = message.usage;
@@ -72631,9 +72855,11 @@ function calculateSessionUsageTotals(messages) {
72631
72855
  output += usage.output;
72632
72856
  cacheRead += usage.cacheRead;
72633
72857
  cacheWrite += usage.cacheWrite;
72858
+ cacheWriteLong += usage.cacheWriteLong ?? 0;
72634
72859
  cost += usage.cost?.total ?? 0;
72860
+ if (usage.cost?.cacheWriteLongEstimated === true) costEstimated = true;
72635
72861
  }
72636
- return { input, output, cacheRead, cacheWrite, cost };
72862
+ return { input, output, cacheRead, cacheWrite, cacheWriteLong, cacheWriteTotal: cacheWrite + cacheWriteLong, cost, costEstimated };
72637
72863
  }
72638
72864
  function formatTokensCompact(count) {
72639
72865
  if (count < 1e3) return String(count);
@@ -72679,16 +72905,18 @@ ${options.theme.bold("Usage (active context)")}
72679
72905
  info += `${options.theme.fg("dim", "Cache Read:")} ${totals.cacheRead.toLocaleString()}
72680
72906
  `;
72681
72907
  }
72682
- if (totals.cacheWrite > 0) {
72683
- 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}
72684
72911
  `;
72685
72912
  }
72686
72913
  if (totals.cost > 0) {
72687
- 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}
72688
72916
  `;
72689
72917
  }
72690
- const hitRateBase = totals.input + totals.cacheRead;
72691
- if (hitRateBase > 0 && totals.cacheRead > 0) {
72918
+ const hitRateBase = totals.input + totals.cacheRead + totals.cacheWriteTotal;
72919
+ if (hitRateBase > 0) {
72692
72920
  const hitRate = (totals.cacheRead / hitRateBase * 100).toFixed(1);
72693
72921
  info += `${options.theme.fg("dim", "Cache Hit Rate:")} ${hitRate}% (~${formatTokensCompact(hitRateBase)} served)
72694
72922
  `;
@@ -72822,11 +73050,13 @@ async function loadSession(host, sessionId) {
72822
73050
  outputTokens: contextUsage.output,
72823
73051
  cacheRead: contextUsage.cacheRead,
72824
73052
  cacheWrite: contextUsage.cacheWrite,
73053
+ cacheWriteLong: contextUsage.cacheWriteLong,
72825
73054
  cost: contextUsage.cost,
73055
+ costEstimated: contextUsage.costEstimated,
72826
73056
  contextPercent,
72827
73057
  contextWindow
72828
73058
  });
72829
- applyContextSignalsToFooter(host);
73059
+ applyContextSignalsWithTrace(host);
72830
73060
  host.ui.requestRender();
72831
73061
  setTimeout(() => host.ui.requestRender(), 100);
72832
73062
  } catch (error) {
@@ -73860,11 +74090,13 @@ function bindInteractiveRuntimeEvents(host) {
73860
74090
  outputTokens: contextUsage.output,
73861
74091
  cacheRead: contextUsage.cacheRead,
73862
74092
  cacheWrite: contextUsage.cacheWrite,
74093
+ cacheWriteLong: contextUsage.cacheWriteLong,
73863
74094
  cost: contextUsage.cost,
74095
+ costEstimated: contextUsage.costEstimated,
73864
74096
  contextPercent,
73865
74097
  contextWindow
73866
74098
  });
73867
- applyContextSignalsToFooter(host);
74099
+ applyContextSignalsWithTrace(host);
73868
74100
  host.safeRequestRender();
73869
74101
  host.sessionManager.save().catch((err) => {
73870
74102
  console.error("[Session] Auto-save failed:", err);
@@ -74612,7 +74844,7 @@ function listDiscoveredSkills(host) {
74612
74844
  function applyReloadedSkills(host, result) {
74613
74845
  host.skills = result.skills.filter((skill) => host.options.settingsManager.isSkillEnabled(skill.name));
74614
74846
  host.skillsSection = formatSkillsForPrompt(host.skills);
74615
- host.applyRuntimeToolMode(host.allTools.length > 0 ? host.allTools : host.agent.state.tools);
74847
+ host.applyRuntimeToolMode();
74616
74848
  host.updateSystemPromptTools();
74617
74849
  for (const skill of host.skills) {
74618
74850
  const skillPath = ["skills", `skill:${skill.name}`];
@@ -74913,9 +75145,9 @@ function collectKnownToolNames(host) {
74913
75145
  function describeToggleEffect(host, name, enabled) {
74914
75146
  const loaded = collectKnownToolNames(host).active.includes(name);
74915
75147
  if (enabled) {
74916
- return loaded ? `Tool "${name}" enabled (active now).` : `Tool "${name}" enabled in ~/.pie/settings.json (loads on next start \u2014 it was not loaded this session).`;
75148
+ return loaded ? `Tool "${name}" enabled (saved to ~/.pie/settings.json; active now).` : `Tool "${name}" enabled in ~/.pie/settings.json (loads on next start \u2014 it was not loaded this session).`;
74917
75149
  }
74918
- return loaded ? `Tool "${name}" disabled (removed from this session).` : `Tool "${name}" disabled in ~/.pie/settings.json.`;
75150
+ return loaded ? `Tool "${name}" disabled (saved to ~/.pie/settings.json; removed from this and future sessions).` : `Tool "${name}" disabled in ~/.pie/settings.json.`;
74919
75151
  }
74920
75152
  function showToolsManager(host) {
74921
75153
  host.showSelector((done) => {
@@ -75020,7 +75252,7 @@ function handleToolsCommand(host, args) {
75020
75252
  host.chatContainer.addChild(new Text(theme.fg("dim", " No tools available."), 1, 0));
75021
75253
  }
75022
75254
  for (const name of active) {
75023
- const state = host.options.settingsManager.isToolEnabled(name) ? "enabled" : "disabled (removed from this session)";
75255
+ const state = host.options.settingsManager.isToolEnabled(name) ? "enabled" : "disabled (saved to ~/.pie/settings.json; removed this session)";
75024
75256
  host.chatContainer.addChild(new Text(theme.fg("dim", ` \u2022 ${name}: ${state}`), 1, 0));
75025
75257
  }
75026
75258
  for (const name of disabled) {
@@ -75040,6 +75272,23 @@ function handleToolsCommand(host, args) {
75040
75272
  var RESET2 = "\x1B[0m";
75041
75273
  var INVERSE2 = "\x1B[7m";
75042
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
+ }
75043
75292
  function createLine2(content, width, isSelected) {
75044
75293
  const normalized = content.replace(/[\r\n]+/g, " ");
75045
75294
  const body = isSelected ? INVERSE2 + normalized : normalized;
@@ -75180,12 +75429,33 @@ var ContextReportSelectorComponent = class {
75180
75429
  this.views.push({
75181
75430
  kind: "text",
75182
75431
  title,
75183
- 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,
75184
75436
  scroll: 0
75185
75437
  });
75186
75438
  this.selectedIndex = 0;
75187
75439
  this.statusMessage = null;
75188
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
+ }
75189
75459
  drillIntoSelected() {
75190
75460
  const view = this.currentView;
75191
75461
  if (view.kind !== "list") return;
@@ -75367,6 +75637,8 @@ var ContextReportSelectorComponent = class {
75367
75637
  )
75368
75638
  );
75369
75639
  } else {
75640
+ this.ensureTextWrapped(view, width);
75641
+ view.scroll = Math.max(0, Math.min(view.scroll, view.lines.length - 1));
75370
75642
  const total = view.lines.length;
75371
75643
  const start = Math.min(view.scroll, Math.max(0, total - 1));
75372
75644
  const end = Math.min(start + this.maxVisibleLines, total);
@@ -75531,6 +75803,7 @@ function handleContextReportCommand(host) {
75531
75803
  const usage = host.calculateContextUsage();
75532
75804
  const contextWindow = host.options.model?.contextWindow ?? 0;
75533
75805
  const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
75806
+ const autoCompactEnabled = host.options.settingsManager.getAutoCompactEnabled();
75534
75807
  const autoCompactLimit = contextWindow > 0 ? getAutoCompactTokenLimit(contextWindow, reserveTokens) ?? null : null;
75535
75808
  const prefixHash = computePromptPrefixHash(
75536
75809
  systemPrompt,
@@ -75562,7 +75835,11 @@ function handleContextReportCommand(host) {
75562
75835
  entries,
75563
75836
  compactionTokensBefore: (entryId) => host.sessionManager.getEntry(entryId)?.compactionData?.tokensBefore,
75564
75837
  contextWindow,
75565
- actualTokens: usage.tokens,
75838
+ estimatedCurrentTokens: usage.estimatedCurrentTokens,
75839
+ providerMeasuredTokens: usage.providerMeasuredTokens,
75840
+ providerMeasuredAt: usage.providerMeasuredAt,
75841
+ measurementMatchesCurrentContext: usage.measurementMatchesCurrentContext,
75842
+ autoCompactEnabled,
75566
75843
  autoCompactLimitTokens: autoCompactLimit,
75567
75844
  reserveTokens,
75568
75845
  promptPrefixHash: prefixHash,
@@ -76394,11 +76671,13 @@ async function handleClearCommand(host) {
76394
76671
  outputTokens: contextUsage.output,
76395
76672
  cacheRead: contextUsage.cacheRead,
76396
76673
  cacheWrite: contextUsage.cacheWrite,
76674
+ cacheWriteLong: contextUsage.cacheWriteLong,
76397
76675
  cost: contextUsage.cost,
76676
+ costEstimated: contextUsage.costEstimated,
76398
76677
  contextPercent: contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0,
76399
76678
  contextWindow
76400
76679
  });
76401
- applyContextSignalsToFooter(host);
76680
+ applyContextSignalsWithTrace(host);
76402
76681
  host.updateTerminalTitle();
76403
76682
  host.chatContainer.clear();
76404
76683
  host.streamingComponent = void 0;
@@ -76487,7 +76766,10 @@ function createExtensionContextForCommands(host) {
76487
76766
  console.error(`[Extension] Tools restricted to: ${tools.join(", ")}`);
76488
76767
  }
76489
76768
  },
76490
- getActiveTools: () => host.agent.state.tools.map((tool) => tool.name),
76769
+ getActiveTools: () => {
76770
+ const tools = host.agent.state.tools;
76771
+ return (host.planModeEnabled ? tools.filter((tool) => isToolAllowedInPlanMode(tool)) : tools).map((tool) => tool.name);
76772
+ },
76491
76773
  sendUserMessage: (content) => {
76492
76774
  if (host.editor.onSubmit) {
76493
76775
  void host.editor.onSubmit(content);
@@ -76754,11 +77036,16 @@ async function handleCompactCommand(host) {
76754
77036
  host.showStatus("Nothing to compact yet");
76755
77037
  return;
76756
77038
  }
76757
- const firstKeptEntryId = findFirstKeptEntrySummaryId(host.sessionManager.getActiveContextSummaries());
76758
- 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) {
76759
77045
  host.showStatus("Need at least one real user message before compacting");
76760
77046
  return;
76761
77047
  }
77048
+ const tokensBefore = calculateTotalTokens(preparation.messages);
76762
77049
  const compactOptions = {
76763
77050
  model: host.options.model,
76764
77051
  apiKey: host.options.apiKey,
@@ -76777,13 +77064,31 @@ async function handleCompactCommand(host) {
76777
77064
  if (beforeCompact?.summary) {
76778
77065
  summary = beforeCompact.summary;
76779
77066
  } else {
76780
- summary = await createCompactionSummary(existingMessages, compactOptions);
77067
+ summary = await createCompactionSummary(preparation.messages, compactOptions);
76781
77068
  }
76782
- const tokensBefore = calculateTotalTokens(existingMessages);
76783
- host.sessionManager.appendCompaction(summary, firstKeptEntryId, tokensBefore);
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}`);
77081
+ }
77082
+ host.sessionManager.appendCompactionCheckpoint({
77083
+ summary,
77084
+ retainedTail: verified.retainedTail,
77085
+ tokensBefore,
77086
+ excludeFailure: preparation.excludedFailure
77087
+ });
76784
77088
  const compactedMessages = host.sessionManager.getMessages();
76785
77089
  host.replaceAgentMessagesAndMarkSynced(compactedMessages);
76786
77090
  host.resetFileObservationState();
77091
+ host.resetRuntimeModeBlocks?.();
76787
77092
  host.streamingComponent = void 0;
76788
77093
  host.pendingTools.clear();
76789
77094
  host.chatContainer.clear();
@@ -76804,11 +77109,13 @@ async function handleCompactCommand(host) {
76804
77109
  outputTokens: contextUsage.output,
76805
77110
  cacheRead: contextUsage.cacheRead,
76806
77111
  cacheWrite: contextUsage.cacheWrite,
77112
+ cacheWriteLong: contextUsage.cacheWriteLong,
76807
77113
  cost: contextUsage.cost,
77114
+ costEstimated: contextUsage.costEstimated,
76808
77115
  contextPercent,
76809
77116
  contextWindow
76810
77117
  });
76811
- applyContextSignalsToFooter(host);
77118
+ applyContextSignalsWithTrace(host);
76812
77119
  await host.sessionManager.save();
76813
77120
  host.safeRequestRender();
76814
77121
  } catch (error) {
@@ -78927,8 +79234,12 @@ function queueMessage(host, text, mode, options = {}) {
78927
79234
  }
78928
79235
  const clearEditor = options.clearEditor ?? true;
78929
79236
  host.sessionTrace.notePendingUserText(text);
79237
+ const runtimeBlock = host.runtimeModeBlockForNextMessage?.();
79238
+ const content = runtimeBlock ? `${runtimeBlock}
79239
+
79240
+ ${text}` : text;
78930
79241
  void host.agentSessionController.prompt(
78931
- { role: "user", content: text, timestamp: Date.now() },
79242
+ { role: "user", content, timestamp: Date.now() },
78932
79243
  {
78933
79244
  source: "interactive",
78934
79245
  streamingBehavior: mode,
@@ -79183,8 +79494,12 @@ var FooterComponent = class {
79183
79494
  if (state.inputTokens) statsParts.push(`\u2191${formatTokens(state.inputTokens)}`);
79184
79495
  if (state.outputTokens) statsParts.push(`\u2193${formatTokens(state.outputTokens)}`);
79185
79496
  if (state.cacheRead) statsParts.push(`R${formatTokens(state.cacheRead)}`);
79186
- if (state.cacheWrite) statsParts.push(`W${formatTokens(state.cacheWrite)}`);
79187
- 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)}`);
79188
79503
  if (state.cacheHitRate != null) {
79189
79504
  statsParts.push(theme.fg("dim", `ca ${(state.cacheHitRate * 100).toFixed(0)}%`));
79190
79505
  }
@@ -79334,13 +79649,23 @@ function calculateCliContextUsage(params) {
79334
79649
  const estimate = estimateContextTokens(messages);
79335
79650
  const usageMessage = estimate.lastUsageIndex !== null ? messages[estimate.lastUsageIndex] : void 0;
79336
79651
  const usage = usageMessage?.role === "assistant" ? usageMessage.usage : void 0;
79652
+ const hasProviderMeasurement = usage !== void 0 && estimate.lastUsageIndex !== null;
79337
79653
  return {
79338
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,
79339
79662
  input: usage?.input ?? 0,
79340
79663
  output: usage?.output ?? 0,
79341
79664
  cacheRead: usage?.cacheRead ?? 0,
79342
79665
  cacheWrite: usage?.cacheWrite ?? 0,
79343
- cost: usage?.cost.total ?? 0
79666
+ cacheWriteLong: usage?.cacheWriteLong ?? 0,
79667
+ cost: usage?.cost.total ?? 0,
79668
+ costEstimated: usage?.cost?.cacheWriteLongEstimated === true
79344
79669
  };
79345
79670
  }
79346
79671
  function createCliAutoCompactDecision(params) {
@@ -79371,6 +79696,11 @@ function shouldCliAutoCompactTokens(params) {
79371
79696
  const tokenLimit = getAutoCompactTokenLimit(params.contextWindow, params.reserveTokens);
79372
79697
  return tokenLimit !== void 0 && params.tokens > tokenLimit;
79373
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
+ }
79374
79704
  async function createCliAutoCompactionResult(params) {
79375
79705
  if (params.context.signal?.aborted) {
79376
79706
  return { aborted: true };
@@ -79379,8 +79709,9 @@ async function createCliAutoCompactionResult(params) {
79379
79709
  if (!activeEntryId) {
79380
79710
  return { aborted: true };
79381
79711
  }
79382
- const firstKeptEntryId = findFirstKeptEntrySummaryId(params.context.sessionManager.getActiveContextSummaries());
79383
- 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) {
79384
79715
  return { aborted: true };
79385
79716
  }
79386
79717
  const compactOptions = {
@@ -79395,7 +79726,8 @@ async function createCliAutoCompactionResult(params) {
79395
79726
  }
79396
79727
  return {
79397
79728
  summary,
79398
- firstKeptEntryId,
79729
+ retainedTail,
79730
+ ...retainedTail.kind === "from-entry" ? { firstKeptEntryId: retainedTail.entryId } : {},
79399
79731
  tokensBefore: calculateTotalTokens(params.context.messages)
79400
79732
  };
79401
79733
  }
@@ -79556,7 +79888,20 @@ function createCliRetryPolicy(isRetryableErrorOverride) {
79556
79888
  function calculateContextUsage(host) {
79557
79889
  const messages = host.agent?.state.messages ?? [];
79558
79890
  if (messages.length === 0) {
79559
- 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
+ };
79560
79905
  }
79561
79906
  return calculateCliContextUsage({
79562
79907
  messages,
@@ -80307,6 +80652,9 @@ function createInteractiveAgentSessionController(host) {
80307
80652
  }
80308
80653
  return void 0;
80309
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 }),
80310
80658
  getAutoContinueMessage: () => host.getTodoAutoContinueMessage(),
80311
80659
  classifyFailure: (context) => classifyCliFailure(context),
80312
80660
  retry: createCliRetryPolicy(
@@ -80324,9 +80672,16 @@ function createInteractiveAgentSessionController(host) {
80324
80672
  return { aborted: true };
80325
80673
  }
80326
80674
  if (beforeCompact?.summary) {
80327
- const firstKeptEntryId = findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries());
80328
- if (!firstKeptEntryId) return { aborted: true };
80329
- 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
+ };
80330
80685
  }
80331
80686
  autoCompactBeforeStats = summarizeActiveContext(context.sessionManager.getActiveContextSummaries());
80332
80687
  return createCliAutoCompactionResult({
@@ -80488,6 +80843,13 @@ function createInteractiveAgentSessionController(host) {
80488
80843
  host.isProcessing = true;
80489
80844
  host.footer.setState({ status: "processing", lifecycleEvent: "auto_compact" });
80490
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
+ });
80491
80853
  } else if (event.type === "auto_compaction_end") {
80492
80854
  host.isCompacting = false;
80493
80855
  host.isProcessing = false;
@@ -80500,7 +80862,6 @@ function createInteractiveAgentSessionController(host) {
80500
80862
  autoCompactBeforeStats = null;
80501
80863
  return;
80502
80864
  }
80503
- host.resetFileObservationState();
80504
80865
  if (!event.result?.aborted) {
80505
80866
  host.streamingComponent = void 0;
80506
80867
  host.pendingTools.clear();
@@ -80519,11 +80880,13 @@ function createInteractiveAgentSessionController(host) {
80519
80880
  outputTokens: contextUsage.output,
80520
80881
  cacheRead: contextUsage.cacheRead,
80521
80882
  cacheWrite: contextUsage.cacheWrite,
80883
+ cacheWriteLong: contextUsage.cacheWriteLong,
80522
80884
  cost: contextUsage.cost,
80885
+ costEstimated: contextUsage.costEstimated,
80523
80886
  contextPercent,
80524
80887
  contextWindow
80525
80888
  });
80526
- applyContextSignalsToFooter(host);
80889
+ applyContextSignalsWithTrace(host);
80527
80890
  host.safeRequestRender();
80528
80891
  if (!event.result?.aborted) {
80529
80892
  if (autoCompactBeforeStats !== null) {
@@ -80552,94 +80915,80 @@ function createInteractiveAgentSessionController(host) {
80552
80915
  }
80553
80916
 
80554
80917
  // src/runtime/runtime-mode-context.ts
80555
- var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
80556
- var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
80557
- var STALE_SYSTEM_REMINDER_RE = /<system-reminder>\s*# Plan Mode[\s\S]*?<\/system-reminder>\s*/gi;
80558
- var STALE_RUNTIME_CONTEXT_RE = /<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>\s*/gi;
80559
- function isRecord3(value) {
80560
- return !!value && typeof value === "object" && !Array.isArray(value);
80918
+ function isAgentMessage(value) {
80919
+ return !!value && typeof value === "object" && "role" in value;
80561
80920
  }
80562
- function cleanText(text) {
80563
- let stripped = 0;
80564
- const withoutRuntime = text.replace(STALE_RUNTIME_CONTEXT_RE, () => {
80565
- stripped++;
80566
- return "";
80567
- });
80568
- const withoutPlanReminder = withoutRuntime.replace(STALE_SYSTEM_REMINDER_RE, () => {
80569
- stripped++;
80570
- return "";
80571
- });
80572
- return { text: withoutPlanReminder, stripped };
80573
- }
80574
- function stripUserMessage(message) {
80575
- if (!isRecord3(message) || message.role !== "user") {
80576
- return { message, stripped: 0 };
80921
+ function prependRuntimeModeBlockToPrompt(input, block) {
80922
+ if (!block) return input;
80923
+ if (typeof input === "string") {
80924
+ return input.trim() ? `${block}
80925
+
80926
+ ${input}` : block;
80577
80927
  }
80578
- const content = message.content;
80579
- if (typeof content === "string") {
80580
- const cleaned = cleanText(content);
80581
- if (cleaned.stripped === 0) return { message, stripped: 0 };
80582
- return {
80583
- message: {
80584
- ...message,
80585
- content: cleaned.text
80586
- },
80587
- stripped: cleaned.stripped
80588
- };
80928
+ if (Array.isArray(input)) {
80929
+ if (input.length > 0 && isAgentMessage(input[0])) {
80930
+ return prependBlockToMessages(input, block);
80931
+ }
80932
+ return [{ type: "text", text: block }, ...input];
80589
80933
  }
80590
- if (!Array.isArray(content)) {
80591
- return { message, stripped: 0 };
80592
- }
80593
- let stripped = 0;
80594
- const nextContent = content.map((block) => {
80595
- if (!isRecord3(block) || block.type !== "text" || typeof block.text !== "string") {
80596
- return block;
80597
- }
80598
- const cleaned = cleanText(block.text);
80599
- stripped += cleaned.stripped;
80600
- return { ...block, text: cleaned.text };
80601
- }).filter((block) => !(isRecord3(block) && block.type === "text" && String(block.text ?? "").length === 0));
80602
- if (stripped === 0) return { message, stripped: 0 };
80603
- return {
80604
- message: {
80605
- ...message,
80606
- content: nextContent
80607
- },
80608
- stripped
80609
- };
80934
+ if (isAgentMessage(input)) {
80935
+ return prependBlockToMessages([input], block)[0];
80936
+ }
80937
+ return [{ type: "text", text: block }, ...input];
80610
80938
  }
80611
- function stripRuntimeModeContext(messages) {
80612
- let stripped = 0;
80613
- const nextMessages = [];
80614
- for (const message of messages) {
80615
- const result = stripUserMessage(message);
80616
- stripped += result.stripped;
80617
- if (isRecord3(result.message) && result.message.role === "user") {
80618
- const content = result.message.content;
80619
- if (typeof content === "string" && content.trim().length === 0) {
80620
- continue;
80621
- }
80622
- if (Array.isArray(content) && content.length === 0) {
80623
- continue;
80624
- }
80939
+ function prependBlockToMessages(messages, block) {
80940
+ const next = [...messages];
80941
+ let target = -1;
80942
+ for (let index = next.length - 1; index >= 0; index--) {
80943
+ if (next[index]?.role === "user") {
80944
+ target = index;
80945
+ break;
80625
80946
  }
80626
- nextMessages.push(result.message);
80627
80947
  }
80628
- return { messages: nextMessages, stripped };
80948
+ if (target < 0) {
80949
+ next.push({ role: "user", content: block, timestamp: Date.now() });
80950
+ return next;
80951
+ }
80952
+ const message = next[target];
80953
+ if (typeof message.content === "string") {
80954
+ next[target] = { ...message, content: `${block}
80955
+
80956
+ ${message.content}` };
80957
+ } else if (Array.isArray(message.content)) {
80958
+ next[target] = { ...message, content: [{ type: "text", text: block }, ...message.content] };
80959
+ } else {
80960
+ next.splice(target, 0, { role: "user", content: block, timestamp: Date.now() });
80961
+ }
80962
+ return next;
80629
80963
  }
80964
+ var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
80965
+ var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
80630
80966
  function buildRuntimeModeContext(state) {
80631
80967
  const lines = [
80632
80968
  RUNTIME_MODE_CONTEXT_MARKER,
80633
- "Current runtime mode:",
80634
- `- Plan mode: ${state.planMode === "planning" ? "active (workspace read-only; plan_scratch permits session-local temporary drafts)" : "off"}.`,
80969
+ "Current runtime mode:"
80970
+ ];
80971
+ if (state.planMode === "planning") {
80972
+ lines.push(
80973
+ "- Plan mode: active (workspace read-only; plan_scratch permits session-local temporary drafts; submit the finished plan by calling plan_submit with the ordered steps for user approval)."
80974
+ );
80975
+ if (state.planDisabledToolNames.length > 0) {
80976
+ lines.push(
80977
+ `- Disabled this turn by plan mode (calls are denied): ${state.planDisabledToolNames.join(", ")}. Do not call them while planning.`
80978
+ );
80979
+ }
80980
+ } else {
80981
+ lines.push("- Plan mode: off.");
80982
+ }
80983
+ lines.push(
80635
80984
  `- YOLO mode: ${state.yoloMode ? "on (filesystem sandbox directory restrictions are disabled)" : "off (workspace/protected-path restrictions apply)"}.`,
80636
80985
  `- Filesystem scope: ${state.filesystemScope === "all" ? "all local paths may be passed directly to file tools when permitted" : "workspace paths and configured safe roots only"}.`
80637
- ];
80986
+ );
80638
80987
  if (state.pendingTransition) {
80639
80988
  const transitionText = {
80640
80989
  entered_yolo: "YOLO mode was just enabled for this turn.",
80641
80990
  exited_yolo: "YOLO mode was just disabled; do not assume previous absolute-path access still applies.",
80642
- entered_plan: "Plan mode was just enabled; analyze and plan without changing the workspace. Use plan_scratch for temporary drafts.",
80991
+ entered_plan: "Plan mode was just enabled; analyze and plan without changing the workspace. Use plan_scratch for temporary drafts, then submit the finished plan by calling plan_submit with the ordered steps for user approval.",
80643
80992
  exited_plan: "Plan mode was just disabled; execution tools may be used when the task requires them."
80644
80993
  };
80645
80994
  lines.push(`- Transition: ${transitionText[state.pendingTransition]}`);
@@ -80653,44 +81002,195 @@ function buildRuntimeModeContext(state) {
80653
81002
  lines.push(RUNTIME_MODE_CONTEXT_END_MARKER);
80654
81003
  return lines.join("\n");
80655
81004
  }
80656
- function findLatestUserIndex(messages) {
80657
- for (let index = messages.length - 1; index >= 0; index--) {
81005
+ function runtimeModeBlockKey(state) {
81006
+ return JSON.stringify({
81007
+ planMode: state.planMode,
81008
+ yoloMode: state.yoloMode,
81009
+ filesystemScope: state.filesystemScope,
81010
+ currentDate: state.currentDate ?? null,
81011
+ activeToolNames: state.activeToolNames,
81012
+ planDisabledToolNames: state.planDisabledToolNames
81013
+ });
81014
+ }
81015
+ function shouldInjectRuntimeModeBlock(lastKey, state) {
81016
+ if (lastKey === null) return true;
81017
+ return runtimeModeBlockKey(state) !== lastKey;
81018
+ }
81019
+ function runtimeModeTraceData(state) {
81020
+ return {
81021
+ yoloMode: state.yoloMode,
81022
+ planMode: state.planMode,
81023
+ filesystemScope: state.filesystemScope,
81024
+ pendingTransition: state.pendingTransition,
81025
+ currentDate: state.currentDate,
81026
+ timeZone: state.timeZone,
81027
+ activeToolCount: state.activeToolNames.length,
81028
+ planDisabledToolCount: state.planDisabledToolNames.length
81029
+ };
81030
+ }
81031
+ function currentDateInZone(timeZone) {
81032
+ const parts = new Intl.DateTimeFormat("en-US", {
81033
+ year: "numeric",
81034
+ month: "2-digit",
81035
+ day: "2-digit",
81036
+ timeZone
81037
+ }).formatToParts(/* @__PURE__ */ new Date());
81038
+ const part = (type) => parts.find((p) => p.type === type)?.value ?? "00";
81039
+ return `${part("year")}-${part("month")}-${part("day")}`;
81040
+ }
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) {
80658
81067
  const message = messages[index];
80659
- if (isRecord3(message) && message.role === "user") {
80660
- return index;
80661
- }
81068
+ if (message?.role !== "user") continue;
81069
+ return leadingRuntimeBlockOfMessage(message) === block;
80662
81070
  }
80663
- return -1;
81071
+ return false;
80664
81072
  }
80665
- function injectRuntimeModeContext(messages, state) {
80666
- const contextMessage = {
80667
- role: "user",
80668
- content: buildRuntimeModeContext(state),
80669
- timestamp: Date.now()
80670
- };
80671
- const latestUserIndex = findLatestUserIndex(messages);
80672
- if (latestUserIndex < 0) {
80673
- return [...messages, contextMessage];
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
+ }
80674
81092
  }
80675
- return [
80676
- ...messages.slice(0, latestUserIndex),
80677
- contextMessage,
80678
- ...messages.slice(latestUserIndex)
80679
- ];
81093
+ return [{ type: "text", text: block }, ...content];
80680
81094
  }
80681
- function createRuntimeModeTransformContext(options) {
80682
- return async (messages) => {
80683
- const stripped = stripRuntimeModeContext(messages);
80684
- if (stripped.stripped > 0) {
80685
- options.onStripped?.(stripped.stripped);
81095
+ function refreshRuntimeModeBlockOnLastUserMessage(messages, block) {
81096
+ for (let i = messages.length - 1; i >= 0; i--) {
81097
+ const message = messages[i];
81098
+ if (message?.role !== "user") continue;
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;
80686
81113
  }
80687
- const state = options.getState();
80688
- const nextMessages = injectRuntimeModeContext(stripped.messages, state);
80689
- options.onInjected?.(state);
80690
- if (state.pendingTransition) {
80691
- options.consumeTransition?.();
81114
+ return messages;
81115
+ }
81116
+ return messages;
81117
+ }
81118
+ var RuntimeModeBlockInjector = class {
81119
+ constructor(options) {
81120
+ this.options = options;
81121
+ }
81122
+ options;
81123
+ lastKey = null;
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;
81133
+ reset() {
81134
+ this.lastKey = null;
81135
+ this.overlayActive = true;
81136
+ }
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
+ */
81152
+ nextBlock() {
81153
+ return this.freshBlock();
81154
+ }
81155
+ /**
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.
81162
+ */
81163
+ consumeRetryRefreshBlock(messages) {
81164
+ if (!this.overlayActive) return null;
81165
+ const state = this.options.getState();
81166
+ const block = buildRuntimeModeContext(state);
81167
+ if (messages && lastUserMessageCarriesRuntimeBlock(messages, block)) {
81168
+ this.overlayActive = false;
81169
+ return null;
80692
81170
  }
80693
- return nextMessages;
81171
+ return block;
81172
+ }
81173
+ freshBlock() {
81174
+ const state = this.options.getState();
81175
+ if (!shouldInjectRuntimeModeBlock(this.lastKey, state)) return null;
81176
+ this.lastKey = runtimeModeBlockKey(state);
81177
+ if (state.pendingTransition) this.options.consumeTransition();
81178
+ this.options.noteTrace(state);
81179
+ return buildRuntimeModeContext(state);
81180
+ }
81181
+ };
81182
+
81183
+ // src/runtime/cache-retention.ts
81184
+ function createCacheRetentionStreamFn(configured, getConfiguredRetention) {
81185
+ const base = configured ?? ((model, context, options) => streamSimple(model, context, options));
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
+ });
80694
81194
  };
80695
81195
  }
80696
81196
 
@@ -80706,12 +81206,19 @@ function isMarkerOnlyUserMessage(message) {
80706
81206
  function stripGuardAdvisories(messages) {
80707
81207
  return messages.filter((message) => !isMarkerOnlyUserMessage(message));
80708
81208
  }
80709
- function findLatestUserIndex2(messages) {
81209
+ function findLatestUserIndex(messages) {
80710
81210
  for (let index = messages.length - 1; index >= 0; index -= 1) {
80711
81211
  if (messages[index].role === "user") return index;
80712
81212
  }
80713
81213
  return -1;
80714
81214
  }
81215
+ function applyRuntimeRequestTransform(messages, options) {
81216
+ const advisory = options.takeAdvisory();
81217
+ let next = advisory ? injectGuardAdvisory(messages, advisory) : stripGuardAdvisories(messages);
81218
+ const retryBlock = options.takeRetryBlock(next);
81219
+ if (retryBlock) next = refreshRuntimeModeBlockOnLastUserMessage(next, retryBlock);
81220
+ return next;
81221
+ }
80715
81222
  function injectGuardAdvisory(messages, guidance) {
80716
81223
  const advisoryMessage = {
80717
81224
  role: "user",
@@ -80720,7 +81227,7 @@ ${guidance}
80720
81227
  ${GUARD_ADVISORY_END_MARKER}`,
80721
81228
  timestamp: Date.now()
80722
81229
  };
80723
- const latestUserIndex = findLatestUserIndex2(messages);
81230
+ const latestUserIndex = findLatestUserIndex(messages);
80724
81231
  if (latestUserIndex < 0) {
80725
81232
  return [...messages, advisoryMessage];
80726
81233
  }
@@ -80731,11 +81238,6 @@ ${GUARD_ADVISORY_END_MARKER}`,
80731
81238
  ];
80732
81239
  }
80733
81240
 
80734
- // src/runtime/tool-policy.ts
80735
- function selectToolsForRuntimeMode(tools, mode) {
80736
- return selectToolsForRuntimePolicy(tools, mode);
80737
- }
80738
-
80739
81241
  // src/runtime/terminal-bell.ts
80740
81242
  var BEL = "\x07";
80741
81243
  var DEFAULT_MIN_INTERVAL_MS = 500;
@@ -80933,7 +81435,7 @@ function createInteractiveExtensionUIContext(host) {
80933
81435
  }
80934
81436
  };
80935
81437
  return {
80936
- notify: (message, type = "info") => showExtensionNotification(host, message, type),
81438
+ notify: (message, type = "info", opts) => showExtensionNotification(host, message, type, opts),
80937
81439
  select: (title, options, opts) => interact(() => showExtensionSelector2(host, title, options, opts)),
80938
81440
  confirm: async (title, message, opts) => await interact(() => showExtensionConfirm(host, title, message, opts)) === true,
80939
81441
  input: (title, placeholder, opts) => interact(() => showExtensionInput(host, title, placeholder, opts)),
@@ -81081,10 +81583,14 @@ function installInteractiveInteractionHandler(host) {
81081
81583
  }
81082
81584
  });
81083
81585
  }
81084
- function showExtensionNotification(host, message, type = "info") {
81586
+ function showExtensionNotification(host, message, type = "info", opts) {
81085
81587
  const prefix = type === "error" ? "Error: " : type === "warning" ? "Warning: " : "";
81086
81588
  host.chatContainer.addChild(new Spacer(1));
81087
- host.chatContainer.addChild(new Text(theme.fg(type === "error" ? "error" : type === "warning" ? "warning" : "dim", `${prefix}${message}`), 1, 0));
81589
+ if (opts?.markdown) {
81590
+ host.chatContainer.addChild(new Markdown(`${prefix}${message}`, 1, 0, getMarkdownTheme()));
81591
+ } else {
81592
+ host.chatContainer.addChild(new Text(theme.fg(type === "error" ? "error" : type === "warning" ? "warning" : "dim", `${prefix}${message}`), 1, 0));
81593
+ }
81088
81594
  host.ui.requestRender();
81089
81595
  }
81090
81596
  function showExtensionSelector2(host, title, options, opts) {
@@ -81455,7 +81961,10 @@ async function loadInteractiveExtensions(host) {
81455
81961
  console.error(`[Extension] Tools set to: ${toolNames.join(", ")}`);
81456
81962
  }
81457
81963
  },
81458
- getActiveTools: () => host.agent.state.tools.map((t) => t.name),
81964
+ getActiveTools: () => {
81965
+ const tools = host.agent.state.tools;
81966
+ return (host.planModeEnabled ? tools.filter((tool) => isToolAllowedInPlanMode(tool)) : tools).map((tool) => tool.name);
81967
+ },
81459
81968
  sendUserMessage: (content) => {
81460
81969
  if (host.editor.onSubmit) {
81461
81970
  void host.editor.onSubmit(content);
@@ -81545,7 +82054,8 @@ async function loadInteractiveExtensions(host) {
81545
82054
  reload: () => host.reloadRuntime(),
81546
82055
  get yoloMode() {
81547
82056
  return host.yoloMode;
81548
- }
82057
+ },
82058
+ setYoloMode: host.setYoloMode ? (enabled) => host.setYoloMode?.(enabled) : void 0
81549
82059
  };
81550
82060
  const contextActions = {
81551
82061
  isIdle: () => !host.isProcessing,
@@ -81576,13 +82086,24 @@ async function loadInteractiveExtensions(host) {
81576
82086
  console.error(`[Extension] Failed to load ${path34}: ${error}`);
81577
82087
  }
81578
82088
  }
81579
- const extensionTools = filterEnabledTools(convertExtensionTools(extensions), host.options.settingsManager);
82089
+ const policyContext = () => ({
82090
+ mode: host.planModeEnabled ? "plan" : "normal",
82091
+ yoloMode: host.yoloMode,
82092
+ confirmationPolicy: "allow"
82093
+ });
82094
+ const extensionTools = filterEnabledTools(
82095
+ createPolicyEnforcedTools(convertExtensionTools(extensions), { getContext: policyContext }),
82096
+ host.options.settingsManager
82097
+ );
81580
82098
  const activeTools = [...host.agent.state.tools, ...extensionTools];
81581
82099
  host.agent.state.tools = wrapToolsWithExtensions(activeTools, host.extensionRunner);
81582
82100
  const availableTools = [
81583
- ...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,
81584
82104
  ...extensionTools
81585
82105
  ];
82106
+ host.pristineTools = [...availableTools];
81586
82107
  host.allTools = wrapToolsWithExtensions(availableTools, host.extensionRunner);
81587
82108
  host.originalTools = [...host.allTools];
81588
82109
  if (host.options.compositionTrace) {
@@ -81653,12 +82174,20 @@ var InteractiveMode = class {
81653
82174
  pendingYoloModeTransition = null;
81654
82175
  executionReminder = null;
81655
82176
  pendingRuntimeGuardAdvisory = null;
82177
+ /** Freezes runtime mode blocks into user messages (cache-stable history). */
82178
+ runtimeModeBlocks = new RuntimeModeBlockInjector({
82179
+ getState: () => this.getRuntimeModeState(),
82180
+ consumeTransition: () => this.consumeRuntimeModeTransition(),
82181
+ noteTrace: (state) => this.sessionTrace.noteRuntimeEvent("runtime_context_injected", runtimeModeTraceData(state))
82182
+ });
81656
82183
  fileManager;
81657
82184
  mentionHandler;
81658
82185
  mentionPickerComponent;
81659
82186
  mentionPickerOptions = [];
81660
82187
  attachmentResolver;
81661
82188
  yoloMode = false;
82189
+ pristineTools = [];
82190
+ // Pristine (extension-unwrapped) tool assembly: the mode-rebuild source.
81662
82191
  /** Calm mode: render only questions and answers (display-only; persisted). */
81663
82192
  calmMode = false;
81664
82193
  originalTools = [];
@@ -81815,11 +82344,16 @@ var InteractiveMode = class {
81815
82344
  get controllerSetupHost() {
81816
82345
  return this;
81817
82346
  }
82347
+ getPolicyContext = () => ({
82348
+ mode: this.planModeEnabled ? "plan" : "normal",
82349
+ yoloMode: this.yoloMode,
82350
+ confirmationPolicy: "allow"
82351
+ });
81818
82352
  rebuildSkillAwareTools(sourceTools) {
81819
82353
  const preservedTools = sourceTools.filter(
81820
82354
  (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash"
81821
82355
  );
81822
- 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)) });
81823
82357
  const fileSystemToolOptions = (allowlistedDirs) => ({
81824
82358
  ...buildCliFileAccessOptions({
81825
82359
  cwd: this.options.cwd,
@@ -81840,19 +82374,14 @@ var InteractiveMode = class {
81840
82374
  });
81841
82375
  const normalFsTools = createCliFileSystemTools(this.options.cwd, fileSystemToolOptions([this.options.configDir]));
81842
82376
  const currentFsTools = this.yoloMode ? createCliFileSystemTools(this.options.cwd, fileSystemToolOptions(["/"])) : normalFsTools;
81843
- const getPolicyContext = () => ({
81844
- mode: this.planModeEnabled ? "plan" : "normal",
81845
- yoloMode: this.yoloMode,
81846
- confirmationPolicy: "allow"
81847
- });
81848
82377
  return {
81849
82378
  currentTools: createPolicyEnforcedTools(
81850
82379
  [...currentFsTools, bashTool, ...preservedTools],
81851
- { getContext: getPolicyContext }
82380
+ { getContext: this.getPolicyContext }
81852
82381
  ),
81853
82382
  originalTools: createPolicyEnforcedTools(
81854
82383
  [...normalFsTools, bashTool, ...preservedTools],
81855
- { getContext: getPolicyContext }
82384
+ { getContext: this.getPolicyContext }
81856
82385
  )
81857
82386
  };
81858
82387
  }
@@ -81860,26 +82389,27 @@ var InteractiveMode = class {
81860
82389
  this.options.fileObservationState.clear();
81861
82390
  }
81862
82391
  getRuntimeModeState() {
81863
- const pendingTransition = this.getPendingRuntimeModeTransition();
81864
82392
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "local";
81865
- const dateParts = new Intl.DateTimeFormat("en-US", {
81866
- year: "numeric",
81867
- month: "2-digit",
81868
- day: "2-digit",
81869
- timeZone
81870
- }).formatToParts(/* @__PURE__ */ new Date());
81871
- const datePart = (type) => dateParts.find((part) => part.type === type)?.value ?? "00";
81872
- const currentDate = `${datePart("year")}-${datePart("month")}-${datePart("day")}`;
82393
+ const activeTools = this.agent?.state?.tools ?? [];
81873
82394
  return {
81874
82395
  yoloMode: this.yoloMode,
81875
82396
  planMode: this.planModeEnabled ? "planning" : "off",
81876
82397
  filesystemScope: this.yoloMode ? "all" : "workspace",
81877
- ...pendingTransition ? { pendingTransition } : {},
81878
- currentDate,
82398
+ ...this.getPendingRuntimeModeTransition() ? { pendingTransition: this.getPendingRuntimeModeTransition() } : {},
82399
+ currentDate: currentDateInZone(timeZone),
81879
82400
  timeZone,
81880
- activeToolNames: this.agent?.state?.tools?.map((tool) => tool.name) ?? []
82401
+ activeToolNames: activeTools.map((tool) => tool.name),
82402
+ planDisabledToolNames: this.planModeEnabled ? activeTools.filter((tool) => !isToolAllowedInPlanMode(tool)).map((tool) => tool.name) : []
81881
82403
  };
81882
82404
  }
82405
+ /** Public reset for compaction flows (compaction may drop the latest block). */
82406
+ resetRuntimeModeBlocks() {
82407
+ this.runtimeModeBlocks.reset();
82408
+ }
82409
+ /** Runtime mode block for the next user message; frozen into it (append-only history, cache-stable prefixes). */
82410
+ runtimeModeBlockForNextMessage() {
82411
+ return this.runtimeModeBlocks.nextBlock();
82412
+ }
81883
82413
  getPendingRuntimeModeTransition() {
81884
82414
  if (this.pendingYoloModeTransition === "enter") return "entered_yolo";
81885
82415
  if (this.pendingYoloModeTransition === "exit") return "exited_yolo";
@@ -81893,15 +82423,12 @@ var InteractiveMode = class {
81893
82423
  }
81894
82424
  applyRuntimeToolMode(sourceTools) {
81895
82425
  if (!this.agent) return;
81896
- 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;
81897
82427
  const rebuilt = this.rebuildSkillAwareTools(completeToolSet);
81898
82428
  const currentTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.currentTools, this.extensionRunner) : rebuilt.currentTools;
81899
82429
  const originalTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.originalTools, this.extensionRunner) : rebuilt.originalTools;
81900
82430
  const activeTools = filterEnabledTools(
81901
- selectToolsForRuntimeMode(
81902
- currentTools,
81903
- this.planModeEnabled ? "plan" : "normal"
81904
- ),
82431
+ currentTools,
81905
82432
  this.options.settingsManager
81906
82433
  );
81907
82434
  this.agent.setTools(activeTools);
@@ -81918,6 +82445,7 @@ var InteractiveMode = class {
81918
82445
  });
81919
82446
  }
81920
82447
  noteRuntimeModeChanged(trigger) {
82448
+ this.runtimeModeBlocks?.armRefresh();
81921
82449
  const state = this.getRuntimeModeState();
81922
82450
  this.sessionTrace.noteRuntimeEvent("runtime_mode_changed", {
81923
82451
  trigger,
@@ -81934,7 +82462,8 @@ var InteractiveMode = class {
81934
82462
  this.safeRequestRender();
81935
82463
  }
81936
82464
  promptAgent(input) {
81937
- return this.agentSessionController.prompt(input, { source: "interactive" });
82465
+ const prompt = prependRuntimeModeBlockToPrompt(input, this.runtimeModeBlockForNextMessage());
82466
+ return this.agentSessionController.prompt(prompt, { source: "interactive" });
81938
82467
  }
81939
82468
  continueAgent() {
81940
82469
  return this.agentSessionController.continue();
@@ -81964,6 +82493,7 @@ var InteractiveMode = class {
81964
82493
  this.persistExecutionState();
81965
82494
  }
81966
82495
  this.executionReminder = this.executionState.getReminder();
82496
+ this.resetRuntimeModeBlocks();
81967
82497
  await this.refreshTodoWidget();
81968
82498
  await this.emitExtensionEvent({
81969
82499
  type: "session:changed",
@@ -81974,7 +82504,7 @@ var InteractiveMode = class {
81974
82504
  async init() {
81975
82505
  const activeSession = this.sessionManager.getActiveSession();
81976
82506
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
81977
- const { Agent } = await import("./chunks/src-OWQI5M6W.js");
82507
+ const { Agent } = await import("./chunks/src-C4KA4G75.js");
81978
82508
  this.agent = new Agent({
81979
82509
  initialState: {
81980
82510
  systemPrompt: this.buildSystemPrompt(),
@@ -81984,28 +82514,15 @@ var InteractiveMode = class {
81984
82514
  },
81985
82515
  apiKey: this.options.apiKey,
81986
82516
  sessionId: activeSession?.id,
81987
- streamFn: this.options.streamFn,
81988
- transformContext: async (messages) => {
81989
- const runtimeModeTransform = createRuntimeModeTransformContext({
81990
- getState: () => this.getRuntimeModeState(),
81991
- consumeTransition: () => this.consumeRuntimeModeTransition(),
81992
- onInjected: (state) => this.sessionTrace.noteRuntimeEvent("runtime_context_injected", {
81993
- yoloMode: state.yoloMode,
81994
- planMode: state.planMode,
81995
- filesystemScope: state.filesystemScope,
81996
- pendingTransition: state.pendingTransition,
81997
- currentDate: state.currentDate,
81998
- timeZone: state.timeZone,
81999
- activeToolCount: state.activeToolNames.length
82000
- }),
82001
- onStripped: (count) => this.sessionTrace.noteRuntimeEvent("stale_runtime_context_stripped", { count })
82002
- });
82003
- let transformed = await runtimeModeTransform(messages);
82004
- const advisory = this.pendingRuntimeGuardAdvisory;
82005
- this.pendingRuntimeGuardAdvisory = null;
82006
- transformed = advisory ? injectGuardAdvisory(transformed, advisory) : stripGuardAdvisories(transformed);
82007
- return transformed;
82008
- }
82517
+ streamFn: createTransportDiagnosticsStreamFn(createCacheRetentionStreamFn(this.options.streamFn, () => this.options.settingsManager.getPromptCacheRetention()), createTraceTransportSink((type, meta) => this.sessionTrace.noteRuntimeEvent(type, meta))),
82518
+ transformContext: async (messages) => applyRuntimeRequestTransform(messages, {
82519
+ takeAdvisory: () => {
82520
+ const advisory = this.pendingRuntimeGuardAdvisory;
82521
+ this.pendingRuntimeGuardAdvisory = null;
82522
+ return advisory;
82523
+ },
82524
+ takeRetryBlock: (snapshot) => this.runtimeModeBlocks.consumeRetryRefreshBlock(snapshot)
82525
+ })
82009
82526
  });
82010
82527
  this.agentSessionController = createInteractiveAgentSessionController(this.controllerSetupHost);
82011
82528
  this.updateTerminalTitle();
@@ -82097,6 +82614,7 @@ var InteractiveMode = class {
82097
82614
  }
82098
82615
  this.allTools = [...this.options.allTools];
82099
82616
  this.originalTools = [...this.options.allTools];
82617
+ this.pristineTools = [...this.options.allTools];
82100
82618
  this.agent.state.tools = [...this.options.allTools];
82101
82619
  await this.extensionRunner?.dispose("reload");
82102
82620
  this.extensionWidgets?.clearAll();
@@ -82189,6 +82707,9 @@ var InteractiveMode = class {
82189
82707
  async handleYoloCommand(args) {
82190
82708
  return handleYoloCommand(this, args);
82191
82709
  }
82710
+ setYoloMode(enabled) {
82711
+ void this.handleYoloCommand(enabled ? "on" : "off");
82712
+ }
82192
82713
  /**
82193
82714
  * Toggle calm mode and rebuild the chat so already-rendered rows follow
82194
82715
  * the new mode. Delivery, tool execution, and session storage are
@@ -82390,6 +82911,7 @@ var InteractiveMode = class {
82390
82911
 
82391
82912
  // src/app/runtime-composition.ts
82392
82913
  import { existsSync as existsSync18, mkdirSync as mkdirSync13 } from "fs";
82914
+ import { randomUUID } from "crypto";
82393
82915
  import * as path31 from "path";
82394
82916
  var CliMissingApiKeyError = class extends Error {
82395
82917
  constructor(message) {
@@ -82519,6 +83041,7 @@ async function composeCliRuntime(params) {
82519
83041
  await sessionManager.save();
82520
83042
  }
82521
83043
  const sessionId = sessionManager.getActiveSession()?.id;
83044
+ const computerUseExecutionId = randomUUID();
82522
83045
  const fileObservationState = createFileObservationState();
82523
83046
  if (sessionId) {
82524
83047
  if (process.env.PIE_DEBUG && !fileLogger.isEnabled()) {
@@ -82547,7 +83070,8 @@ async function composeCliRuntime(params) {
82547
83070
  getWebSearchMode: () => settingsManager.getWebSearchMode(),
82548
83071
  resolveToolModel: (purpose) => modelRegistry.resolveToolModel(purpose, getCurrentModel()),
82549
83072
  resolveToolModelCandidates: (purpose) => modelRegistry.resolveToolModelCandidates(purpose, getCurrentModel()),
82550
- configDir: paths.configDir
83073
+ configDir: paths.configDir,
83074
+ getBashEnv: () => computerUseOwnerEnv(sessionManager.getActiveSessionId() || COMPUTER_USE_STANDALONE_OWNER, computerUseExecutionId)
82551
83075
  });
82552
83076
  const fileSystemToolOptions = {
82553
83077
  ...buildCliFileAccessOptions({ cwd, configDir: paths.configDir, skills, allowlistedDirs: [paths.configDir] }),
@@ -82560,6 +83084,7 @@ async function composeCliRuntime(params) {
82560
83084
  getPolicyContext: () => ({ mode: "normal", confirmationPolicy: "allow" })
82561
83085
  };
82562
83086
  const rawTools = [
83087
+ createSearchContextTool(() => sessionManager),
82563
83088
  ...createCliFileSystemTools(cwd, fileSystemToolOptions),
82564
83089
  ...cliHostCapabilities.tools
82565
83090
  ];
@@ -82674,6 +83199,38 @@ function createPrintModeRetryStatusReporter() {
82674
83199
  };
82675
83200
  }
82676
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
+
82677
83234
  // src/app/print-mode.ts
82678
83235
  function createNoOpExtensionUIContext() {
82679
83236
  return {
@@ -82805,7 +83362,7 @@ async function runPrintMode(params) {
82805
83362
  `);
82806
83363
  console.log("Assistant: ");
82807
83364
  }
82808
- const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
83365
+ const { Agent: AgentClass } = await import("./chunks/src-C4KA4G75.js");
82809
83366
  let agent;
82810
83367
  const printModeExtensions = await loadPrintModeExtensions({
82811
83368
  cwd: runtime.cwd,
@@ -82849,7 +83406,8 @@ async function runPrintMode(params) {
82849
83406
  thinkingLevel: printThinkingLevel
82850
83407
  },
82851
83408
  apiKey: runtime.apiKey,
82852
- streamFn: params.streamFn
83409
+ // Body-free transport timeline for the main (print-mode) agent.
83410
+ streamFn: createTransportDiagnosticsStreamFn(params.streamFn ?? streamSimple, logTransportLifecycle)
82853
83411
  });
82854
83412
  const sessionTrace = new SessionTraceWriter(runtime.paths.sessionsDir, runtime.sessionManager);
82855
83413
  const reportRetryStatus = createPrintModeRetryStatusReporter();
@@ -82883,28 +83441,16 @@ async function runPrintMode(params) {
82883
83441
  latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
82884
83442
  isContextOverflowError: isCliContextOverflowError
82885
83443
  }),
82886
- compaction: {
82887
- compact: async (context) => {
82888
- const beforeCompact = await printModeExtensions.runner.emitSessionBeforeCompact({
82889
- type: "session:before_compact",
82890
- reason: context.reason === "overflow" ? "overflow" : "threshold",
82891
- messageCount: context.messages.length
82892
- });
82893
- if (beforeCompact.cancel) return { aborted: true };
82894
- if (beforeCompact.summary) {
82895
- const activeEntryId = context.sessionManager.getActiveEntryId();
82896
- const firstKeptEntryId = activeEntryId ? findFirstKeptEntrySummaryId(context.sessionManager.getActiveContextSummaries()) : void 0;
82897
- if (!firstKeptEntryId) return { aborted: true };
82898
- return { summary: beforeCompact.summary, firstKeptEntryId };
82899
- }
82900
- return createCliAutoCompactionResult({
82901
- context,
82902
- model: runtime.model,
82903
- apiKey: runtime.apiKey,
82904
- systemPrompt: agent?.state.systemPrompt ?? baseSystemPrompt
82905
- });
82906
- }
82907
- },
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
+ }),
82908
83454
  classifyFailure: (context) => classifyCliFailure(context),
82909
83455
  onEvent: (event) => {
82910
83456
  sessionTrace.noteRuntimeEvent(event.type, event);
@@ -82912,7 +83458,7 @@ async function runPrintMode(params) {
82912
83458
  console.error(event.failure.kind === "context_overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context before retry...");
82913
83459
  } else if (event.type === "auto_compaction_start") {
82914
83460
  console.error(event.reason === "overflow" ? "Context overflow detected, compacting..." : "Auto-compacting context...");
82915
- } else if (event.type === "auto_compaction_end" && !event.errorMessage) {
83461
+ } else if (event.type === "compaction_committed") {
82916
83462
  runtime.fileObservationState.clear();
82917
83463
  } else {
82918
83464
  reportRetryStatus(event);
@@ -83045,7 +83591,7 @@ function toJsonValue(value, depth = 0, cap = MAX_EVENT_STRING_LENGTH) {
83045
83591
  }
83046
83592
  return result;
83047
83593
  }
83048
- function isRecord4(value) {
83594
+ function isRecord3(value) {
83049
83595
  return !!value && typeof value === "object" && !Array.isArray(value);
83050
83596
  }
83051
83597
  function truncateForEvent(text, cap) {
@@ -83071,7 +83617,7 @@ function parseInput(line) {
83071
83617
  } catch {
83072
83618
  return null;
83073
83619
  }
83074
- if (!isRecord4(parsed) || typeof parsed.type !== "string") return null;
83620
+ if (!isRecord3(parsed) || typeof parsed.type !== "string") return null;
83075
83621
  if (parsed.type === "shutdown") {
83076
83622
  return { type: "shutdown", reason: typeof parsed.reason === "string" ? parsed.reason : void 0 };
83077
83623
  }
@@ -83122,7 +83668,7 @@ function parseInput(line) {
83122
83668
  prompt: parsed.prompt,
83123
83669
  streamingBehavior,
83124
83670
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0,
83125
- metadata: isRecord4(parsed.metadata) ? parsed.metadata : void 0
83671
+ metadata: isRecord3(parsed.metadata) ? parsed.metadata : void 0
83126
83672
  };
83127
83673
  }
83128
83674
  return null;
@@ -83162,15 +83708,15 @@ function readProgressText(value) {
83162
83708
  function extractJsonEventsToolProgressText(partialResult) {
83163
83709
  const plainText = readProgressText(partialResult).trim();
83164
83710
  if (plainText) return plainText;
83165
- if (!isRecord4(partialResult)) return "";
83711
+ if (!isRecord3(partialResult)) return "";
83166
83712
  const content = partialResult.content;
83167
83713
  if (Array.isArray(content)) {
83168
- return content.map((block) => isRecord4(block) && block.type === "text" ? readProgressText(block.text) : "").filter(Boolean).join("\n").trim();
83714
+ return content.map((block) => isRecord3(block) && block.type === "text" ? readProgressText(block.text) : "").filter(Boolean).join("\n").trim();
83169
83715
  }
83170
83716
  const directText = readProgressText(partialResult.text) || readProgressText(partialResult.message);
83171
83717
  if (directText) return directText.trim();
83172
83718
  const details = partialResult.details;
83173
- if (isRecord4(details)) {
83719
+ if (isRecord3(details)) {
83174
83720
  return (readProgressText(details.text) || readProgressText(details.message)).trim();
83175
83721
  }
83176
83722
  return "";
@@ -83266,6 +83812,9 @@ async function loadJsonModeExtensions(params) {
83266
83812
  extensionCount: extensions.length
83267
83813
  };
83268
83814
  }
83815
+ function createJsonEventsTransportStreamFn(streamFn) {
83816
+ return createTransportDiagnosticsStreamFn(streamFn ?? streamSimple, logTransportLifecycle);
83817
+ }
83269
83818
  async function runJsonEventsMode(params) {
83270
83819
  const eventDetail = params.eventDetail === FULL_DETAIL ? FULL_DETAIL : COMPACT_DETAIL;
83271
83820
  const eventStringCap = eventDetail === FULL_DETAIL ? MAX_EVENT_STRING_LENGTH : COMPACT_EVENT_STRING_LENGTH;
@@ -83666,7 +84215,7 @@ async function runJsonEventsMode(params) {
83666
84215
  return;
83667
84216
  }
83668
84217
  const parent = agent;
83669
- const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
84218
+ const { Agent: AgentClass } = await import("./chunks/src-C4KA4G75.js");
83670
84219
  const sideAgent = new AgentClass({
83671
84220
  initialState: {
83672
84221
  systemPrompt: parent.state.systemPrompt,
@@ -83676,7 +84225,11 @@ async function runJsonEventsMode(params) {
83676
84225
  messages: structuredClone(parent.state.messages)
83677
84226
  },
83678
84227
  apiKey: runtime.apiKey,
83679
- 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)
83680
84233
  });
83681
84234
  sidechatController.signal.addEventListener("abort", () => sideAgent.abort(), { once: true });
83682
84235
  const timer = setTimeout(() => {
@@ -83834,7 +84387,7 @@ async function runJsonEventsMode(params) {
83834
84387
  }
83835
84388
  function handleControllerEvent(event) {
83836
84389
  trace?.noteRuntimeEvent(event.type, event);
83837
- if (event.type === "auto_compaction_end" && !event.errorMessage) {
84390
+ if (event.type === "compaction_committed") {
83838
84391
  runtime?.fileObservationState.clear();
83839
84392
  }
83840
84393
  if (event.type === "turn_settled" && activeTurn) {
@@ -84010,7 +84563,7 @@ async function runJsonEventsMode(params) {
84010
84563
  if (runtime.initialModel.unconfigured || controller) {
84011
84564
  return;
84012
84565
  }
84013
- const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
84566
+ const { Agent: AgentClass } = await import("./chunks/src-C4KA4G75.js");
84014
84567
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
84015
84568
  extensionRunner = jsonModeExtensions.runner;
84016
84569
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
@@ -84042,7 +84595,8 @@ async function runJsonEventsMode(params) {
84042
84595
  thinkingLevel
84043
84596
  },
84044
84597
  apiKey: runtime.apiKey,
84045
- streamFn: params.streamFn
84598
+ // Body-free transport timeline for the main (JSON-events) agent.
84599
+ streamFn: createJsonEventsTransportStreamFn(params.streamFn)
84046
84600
  });
84047
84601
  controller = new AgentSessionController({
84048
84602
  agent,
@@ -84071,6 +84625,10 @@ async function runJsonEventsMode(params) {
84071
84625
  latestCompactionTimestamp: runtime.sessionManager.getLatestCompactionTimestamp(),
84072
84626
  isContextOverflowError: isCliContextOverflowError
84073
84627
  }),
84628
+ getCompactionBudgetTokens: createCliCompactionBudget({
84629
+ contextWindow: runtime.model.contextWindow,
84630
+ reserveTokens: runtime.settingsManager.getAutoCompactReserveTokens()
84631
+ }),
84074
84632
  compaction: {
84075
84633
  compact: async (context) => createCliAutoCompactionResult({
84076
84634
  context,
@@ -84604,7 +85162,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
84604
85162
  }
84605
85163
  async function createCliTestStreamFnFromEnv() {
84606
85164
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
84607
- return (await import("./chunks/test-stream-Y7WXELAJ.js")).createTestStreamFnFromSequenceEnv();
85165
+ return (await import("./chunks/test-stream-A5ZELKNG.js")).createTestStreamFnFromSequenceEnv();
84608
85166
  }
84609
85167
  function resolveJsonEventsDetailFlag() {
84610
85168
  for (let index = 0; index < process.argv.length; index += 1) {