@cydm/pie 2.6.11 → 2.7.0

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 (34) hide show
  1. package/README.md +68 -2
  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 +4 -5
  5. package/dist/builtin/extensions/todo/index.js +3 -3
  6. package/dist/builtin/skills/computer-use-axi/SKILL.md +103 -0
  7. package/dist/builtin/skills/computer-use-axi/computer-use-axi-cli.js +37 -0
  8. package/dist/builtin/{extensions/computer-use/index.js → skills/computer-use-axi/computer-use-axi-runtime.js} +754 -642
  9. package/dist/builtin/skills/computer-use-axi/package.json +11 -0
  10. package/dist/chunks/{chunk-ZVM234WE.js → chunk-6RBSTII7.js} +669 -594
  11. package/dist/chunks/{chunk-MA4CHFN4.js → chunk-IPNAFMOJ.js} +172 -230
  12. package/dist/chunks/{chunk-7B4IIBVS.js → chunk-R5I2QLF2.js} +1269 -4
  13. package/dist/chunks/{chunk-PAC62XU4.js → chunk-TWILEFLD.js} +25 -3
  14. package/dist/chunks/{src-HPIBCE2I.js → src-E7NCXMU5.js} +2 -2
  15. package/dist/chunks/{test-stream-Y7WXELAJ.js → test-stream-BWBJTFNI.js} +1 -1
  16. package/dist/cli.js +1979 -384
  17. package/models.schema.json +52 -0
  18. package/package.json +3 -3
  19. package/dist/builtin/extensions/computer-use/package.json +0 -14
  20. package/dist/builtin/extensions/computer-use/scripts/build-native.mjs +0 -278
  21. package/dist/builtin/extensions/computer-use/scripts/test-native-retention.mjs +0 -204
  22. package/dist/chunks/chunk-5353DEEC.js +0 -130
  23. package/dist/chunks/webapi-MBCKO7ZV.js +0 -2481
  24. package/dist/code-intel-worker.cjs +0 -211227
  25. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/arm64/linux-bridge +0 -0
  26. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/linux/x64/linux-bridge +0 -0
  27. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/arm64/pie-computer-use +0 -0
  28. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/Info.plist +0 -0
  29. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/MacOS/pie-computer-use +0 -0
  30. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/universal/pie-computer-use.app/Contents/_CodeSignature/CodeResources +0 -0
  31. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/macos/x64/pie-computer-use +0 -0
  32. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/prebuilt/windows/windows-bridge.exe +0 -0
  33. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/helper-path.mjs +0 -0
  34. /package/dist/builtin/{extensions/computer-use → skills/computer-use-axi}/scripts/setup-helper.mjs +0 -0
package/dist/cli.js CHANGED
@@ -1,13 +1,29 @@
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,
8
9
  formatSize,
10
+ getAgentDir,
11
+ getBuiltinDir,
12
+ getConfigDir,
13
+ getCustomThemesDir,
14
+ getLogsDir,
15
+ getPackageDir,
16
+ getProjectSettingsPath,
17
+ getRuntimeLogPath,
18
+ getSessionsDir,
19
+ getSettingsPath,
20
+ getThemesDir,
21
+ migrateConfigFromAgentDir,
9
22
  truncateHeadTail
10
- } from "./chunks/chunk-MA4CHFN4.js";
23
+ } from "./chunks/chunk-IPNAFMOJ.js";
24
+ import {
25
+ resolveCliProjectRoot
26
+ } from "./chunks/chunk-WLFOBECN.js";
11
27
  import {
12
28
  AGENTS_CONTEXT_FILE_NAME,
13
29
  AgentSessionController,
@@ -30,6 +46,7 @@ import {
30
46
  createFileObservationState,
31
47
  createPolicyEnforcedTools,
32
48
  createReadFileUnderstandingHandler,
49
+ createSearchContextTool,
33
50
  createSessionManager,
34
51
  createSharedFileSystemTools,
35
52
  createSharedWebSearchTool,
@@ -51,6 +68,7 @@ import {
51
68
  interactionAttentionResolution,
52
69
  isCompactSummaryMessage,
53
70
  isExtensionEnabled,
71
+ isToolAllowedInPlanMode,
54
72
  maybeAdvanceTodoExecutionState,
55
73
  modalityForReadFileUnderstandingKind,
56
74
  pauseExecutionState,
@@ -58,13 +76,12 @@ import {
58
76
  requestInteraction,
59
77
  resolveExtensionIdentity,
60
78
  restoreExecutionState,
61
- selectToolsForRuntimePolicy,
62
79
  setEnablementInMap,
63
80
  setExtensionEnabledInSettings,
64
81
  shouldPreserveExecutionStateForUserText,
65
82
  supersedeExecutionState
66
- } from "./chunks/chunk-ZVM234WE.js";
67
- import "./chunks/chunk-PAC62XU4.js";
83
+ } from "./chunks/chunk-6RBSTII7.js";
84
+ import "./chunks/chunk-TWILEFLD.js";
68
85
  import {
69
86
  Deref,
70
87
  EMPTY_ASSISTANT_CONTENT_ERROR,
@@ -87,26 +104,9 @@ import {
87
104
  requiredCapabilityForToolPurpose,
88
105
  setLogger,
89
106
  sortToolModelCandidatesByCapability,
107
+ streamSimple,
90
108
  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";
109
+ } from "./chunks/chunk-R5I2QLF2.js";
110
110
  import {
111
111
  IsArray,
112
112
  IsBigInt,
@@ -51971,6 +51971,7 @@ var MODEL_OVERRIDE_KEYS = [
51971
51971
  "cost",
51972
51972
  "contextWindow",
51973
51973
  "maxTokens",
51974
+ "transport",
51974
51975
  "headers",
51975
51976
  "compat",
51976
51977
  "cacheCapability",
@@ -51988,9 +51989,11 @@ function validateExtensionProviderConfig(provider, config) {
51988
51989
  throw new Error(`registerProvider("${provider}"): baseUrl must be an absolute URL`);
51989
51990
  }
51990
51991
  assertHeadersShape(config.headers, `registerProvider("${provider}")`);
51992
+ validateTransport(config.transport, `registerProvider("${provider}")`);
51991
51993
  for (const model of config.models ?? []) {
51992
51994
  if (!model || typeof model.id !== "string" || !model.id.trim() || /\s/.test(model.id)) throw new Error(`registerProvider("${provider}"): invalid model id`);
51993
51995
  assertHeadersShape(model.headers, `registerProvider("${provider}") model "${model.id}"`);
51996
+ validateTransport(model.transport, `Model "${model.id}"`);
51994
51997
  validateCompat(model.compat, `registerProvider("${provider}") model "${model.id}" compat`);
51995
51998
  validateCacheCapability(model.cacheCapability, `registerProvider("${provider}") model "${model.id}" cacheCapability`);
51996
51999
  validateWebSearchCapability(model.webSearch, `registerProvider("${provider}") model "${model.id}" webSearch`);
@@ -51998,7 +52001,7 @@ function validateExtensionProviderConfig(provider, config) {
51998
52001
  }
51999
52002
  }
52000
52003
  var REMOTE_CATALOG_CACHE_VERSION = 4;
52001
- var SUPPORTED_REMOTE_APIS = /* @__PURE__ */ new Set(["openai-completions", "anthropic-messages"]);
52004
+ var SUPPORTED_REMOTE_APIS = /* @__PURE__ */ new Set(["openai-completions", "anthropic-messages", "openai-responses"]);
52002
52005
  var MAX_REMOTE_CATALOG_BYTES = 2 * 1024 * 1024;
52003
52006
  var OAUTH_RESTORE_LEEWAY_MS = 6e4;
52004
52007
  var OAUTH_REFRESH_TIMEOUT_MS = 3e4;
@@ -52495,11 +52498,17 @@ function validateFileCapabilities(value, label) {
52495
52498
  }
52496
52499
  }
52497
52500
  }
52501
+ function validateTransport(value, label) {
52502
+ if (value !== void 0 && value !== "sse" && value !== "websocket" && value !== "auto") throw new Error(`${label}.transport is invalid`);
52503
+ }
52498
52504
  function validateCompat(value, label) {
52499
52505
  if (value === void 0) return;
52500
52506
  if (!isPlainRecord(value)) throw new Error(`${label} is invalid`);
52501
52507
  assertOnlyKeys(value, [
52502
52508
  "forcedReasoningEffort",
52509
+ "store",
52510
+ "supportsEncryptedReasoning",
52511
+ "responsesContinuation",
52503
52512
  "supportsStore",
52504
52513
  "supportsDeveloperRole",
52505
52514
  "supportsReasoningEffort",
@@ -52517,6 +52526,9 @@ function validateCompat(value, label) {
52517
52526
  throw new Error(`${label}.forcedReasoningEffort must be "none"`);
52518
52527
  }
52519
52528
  for (const field of [
52529
+ "store",
52530
+ "supportsEncryptedReasoning",
52531
+ "responsesContinuation",
52520
52532
  "supportsStore",
52521
52533
  "supportsDeveloperRole",
52522
52534
  "supportsReasoningEffort",
@@ -52537,12 +52549,14 @@ function validateRemoteModel(profileId, model) {
52537
52549
  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
52550
  assertOnlyKeys(model, [
52539
52551
  "id",
52552
+ "api",
52540
52553
  "name",
52541
52554
  "reasoning",
52542
52555
  "input",
52543
52556
  "cost",
52544
52557
  "contextWindow",
52545
52558
  "maxTokens",
52559
+ "transport",
52546
52560
  "compat",
52547
52561
  "cacheCapability",
52548
52562
  "webSearch",
@@ -52556,6 +52570,7 @@ function validateRemoteModel(profileId, model) {
52556
52570
  for (const field of ["name", "displayName", "filePlatform", "attachmentPlatform", "apiKeyEnv"]) {
52557
52571
  assertOptionalString(model[field], `Remote profile "${profileId}" model "${model.id}" ${field}`);
52558
52572
  }
52573
+ if (model.api !== void 0 && !SUPPORTED_REMOTE_APIS.has(model.api)) throw new Error(`Model "${model.id}" has unsupported api ${model.api}`);
52559
52574
  assertOptionalBoolean(model.reasoning, `Remote profile "${profileId}" model "${model.id}" reasoning`);
52560
52575
  if (!Array.isArray(model.input) || model.input.length === 0 || model.input.some((item) => !["text", "image", "audio", "video"].includes(item))) {
52561
52576
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid input capabilities`);
@@ -52569,6 +52584,7 @@ function validateRemoteModel(profileId, model) {
52569
52584
  throw new Error(`Remote profile "${profileId}" model "${model.id}" has invalid token limits`);
52570
52585
  }
52571
52586
  if (new Set(model.input).size !== model.input.length) throw new Error(`Remote profile "${profileId}" model "${model.id}" has duplicate input capabilities`);
52587
+ validateTransport(model.transport, `Model "${model.id}"`);
52572
52588
  validateCompat(model.compat, `Remote profile "${profileId}" model "${model.id}" compat`);
52573
52589
  validateCacheCapability(model.cacheCapability, `Remote profile "${profileId}" model "${model.id}" cacheCapability`);
52574
52590
  validateWebSearchCapability(model.webSearch, `Remote profile "${profileId}" model "${model.id}" webSearch`);
@@ -52604,6 +52620,7 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52604
52620
  if (!provider || provider !== provider.trim() || /\s/.test(provider) || !isPlainRecord(source)) throw new Error("catalog contains an invalid profile");
52605
52621
  assertOnlyKeys(source, [
52606
52622
  "baseUrl",
52623
+ "endpoints",
52607
52624
  "api",
52608
52625
  "apiKey",
52609
52626
  "apiKeyEnv",
@@ -52611,6 +52628,7 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52611
52628
  "displayName",
52612
52629
  "filePlatform",
52613
52630
  "attachmentPlatform",
52631
+ "transport",
52614
52632
  "compat",
52615
52633
  "cacheCapability",
52616
52634
  "webSearch",
@@ -52626,8 +52644,13 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52626
52644
  assertOptionalString(source[field], `Remote profile "${provider}" ${field}`);
52627
52645
  }
52628
52646
  validateRemoteEndpoint(source.baseUrl, catalogUrl, `Remote profile "${provider}"`);
52647
+ validateProfileEndpoints(source, `Remote profile "${provider}"`);
52648
+ for (const [api, endpoint] of Object.entries(source.endpoints ?? {})) {
52649
+ validateRemoteEndpoint(endpoint, catalogUrl, `Remote profile "${provider}" endpoints.${api}`);
52650
+ }
52629
52651
  if (!Array.isArray(source.models) || source.models.length === 0) throw new Error(`Remote profile "${provider}" has no models`);
52630
52652
  assertRemoteHeadersSafe(source.headers, `Remote profile "${provider}"`);
52653
+ validateTransport(source.transport, `Remote profile "${provider}"`);
52631
52654
  validateCompat(source.compat, `Remote profile "${provider}" compat`);
52632
52655
  validateCacheCapability(source.cacheCapability, `Remote profile "${provider}" cacheCapability`);
52633
52656
  validateWebSearchCapability(source.webSearch, `Remote profile "${provider}" webSearch`);
@@ -52644,7 +52667,9 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52644
52667
  }
52645
52668
  const sanitized = {
52646
52669
  baseUrl: source.baseUrl,
52670
+ ...source.endpoints === void 0 ? {} : { endpoints: { ...source.endpoints } },
52647
52671
  api: source.api,
52672
+ transport: source.transport,
52648
52673
  apiKeyEnv: remote.apiKeyEnv,
52649
52674
  ...source.headers === void 0 ? {} : { headers: { ...source.headers } },
52650
52675
  ...source.displayName === void 0 ? {} : { displayName: source.displayName },
@@ -52656,6 +52681,7 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52656
52681
  ...source.fileCapabilities === void 0 ? {} : { fileCapabilities: { ...source.fileCapabilities } },
52657
52682
  models: source.models.map((model) => ({
52658
52683
  id: model.id,
52684
+ ...model.api === void 0 ? {} : { api: model.api },
52659
52685
  ...model.name === void 0 ? {} : { name: model.name },
52660
52686
  ...model.reasoning === void 0 ? {} : { reasoning: model.reasoning },
52661
52687
  input: [...model.input],
@@ -52667,6 +52693,7 @@ function sanitizeRemotePayload(decoded, remote, catalogUrl, allowLocallyInjected
52667
52693
  },
52668
52694
  contextWindow: model.contextWindow,
52669
52695
  maxTokens: model.maxTokens,
52696
+ ...model.transport === void 0 ? {} : { transport: model.transport },
52670
52697
  ...model.compat === void 0 ? {} : { compat: { ...model.compat } },
52671
52698
  ...model.cacheCapability === void 0 ? {} : { cacheCapability: { ...model.cacheCapability } },
52672
52699
  ...model.webSearch === void 0 ? {} : { webSearch: { ...model.webSearch } },
@@ -52739,16 +52766,41 @@ function validateModelOverrides(value) {
52739
52766
  if (typeof headerValue !== "string") throw new Error(`models.json modelOverrides["${key}"].headers.${headerName} must be a string`);
52740
52767
  }
52741
52768
  }
52769
+ validateTransport(override.transport, `Model override "${key}"`);
52742
52770
  validateCompat(override.compat, `models.json modelOverrides["${key}"].compat`);
52743
52771
  validateCacheCapability(override.cacheCapability, `models.json modelOverrides["${key}"].cacheCapability`);
52744
52772
  validateWebSearchCapability(override.webSearch, `models.json modelOverrides["${key}"].webSearch`);
52745
52773
  validateFileCapabilities(override.fileCapabilities, `models.json modelOverrides["${key}"].fileCapabilities`);
52746
52774
  }
52747
52775
  }
52776
+ function validateProfileEndpoints(profile, label) {
52777
+ if (profile.endpoints === void 0) return;
52778
+ if (!isPlainRecord(profile.endpoints)) throw new Error(`${label} endpoints must be an object`);
52779
+ for (const [api, value] of Object.entries(profile.endpoints)) {
52780
+ if (!SUPPORTED_REMOTE_APIS.has(api)) throw new Error(`${label} endpoints has unsupported api ${api}`);
52781
+ try {
52782
+ if (typeof value !== "string") throw new Error("invalid URL");
52783
+ const endpoint = new URL(value);
52784
+ if (endpoint.protocol !== "http:" && endpoint.protocol !== "https:") throw new Error("unsupported protocol");
52785
+ } catch {
52786
+ throw new Error(`${label} endpoints.${api} must be an absolute HTTP(S) URL`);
52787
+ }
52788
+ }
52789
+ }
52790
+ function resolveProfileRoute(profile, model) {
52791
+ const api = model.api ?? profile.api;
52792
+ if (!SUPPORTED_REMOTE_APIS.has(api)) throw new Error(`Model "${model.id}" has unsupported api ${api}`);
52793
+ const endpoint = profile.endpoints?.[api];
52794
+ if (model.api !== void 0 && endpoint === void 0 && api !== profile.api) {
52795
+ throw new Error(`Model "${model.id}" api ${api} requires profile endpoints.${api} (otherwise api must equal profile api)`);
52796
+ }
52797
+ return { api, baseUrl: endpoint ?? profile.baseUrl };
52798
+ }
52748
52799
  function validateProfileConfig(profileId, profileConfig) {
52749
52800
  if (!isPlainRecord(profileConfig)) throw new Error(`Profile "${profileId}" must be an object.`);
52750
52801
  assertOnlyKeys(profileConfig, [
52751
52802
  "baseUrl",
52803
+ "endpoints",
52752
52804
  "api",
52753
52805
  "apiKey",
52754
52806
  "apiKeyEnv",
@@ -52756,6 +52808,7 @@ function validateProfileConfig(profileId, profileConfig) {
52756
52808
  "displayName",
52757
52809
  "filePlatform",
52758
52810
  "attachmentPlatform",
52811
+ "transport",
52759
52812
  "compat",
52760
52813
  "cacheCapability",
52761
52814
  "webSearch",
@@ -52787,7 +52840,9 @@ function validateProfileConfig(profileId, profileConfig) {
52787
52840
  } catch {
52788
52841
  throw new Error(`Profile "${profileId}" baseUrl must be an absolute HTTP(S) URL`);
52789
52842
  }
52843
+ validateProfileEndpoints(profileConfig, `Profile "${profileId}"`);
52790
52844
  assertHeadersShape(profileConfig.headers, `Profile "${profileId}"`);
52845
+ validateTransport(profileConfig.transport, `Profile "${profileId}"`);
52791
52846
  validateCompat(profileConfig.compat, `Profile "${profileId}" compat`);
52792
52847
  validateCacheCapability(profileConfig.cacheCapability, `Profile "${profileId}" cacheCapability`);
52793
52848
  validateWebSearchCapability(profileConfig.webSearch, `Profile "${profileId}" webSearch`);
@@ -52797,12 +52852,14 @@ function validateProfileConfig(profileId, profileConfig) {
52797
52852
  if (!isPlainRecord(modelConfig)) throw new Error(`Profile "${profileId}" model #${index} must be an object.`);
52798
52853
  assertOnlyKeys(modelConfig, [
52799
52854
  "id",
52855
+ "api",
52800
52856
  "name",
52801
52857
  "reasoning",
52802
52858
  "input",
52803
52859
  "cost",
52804
52860
  "contextWindow",
52805
52861
  "maxTokens",
52862
+ "transport",
52806
52863
  "compat",
52807
52864
  "cacheCapability",
52808
52865
  "webSearch",
@@ -52826,6 +52883,7 @@ function validateProfileConfig(profileId, profileConfig) {
52826
52883
  );
52827
52884
  }
52828
52885
  validateRemoteModel(profileId, modelConfig);
52886
+ resolveProfileRoute(profileConfig, modelConfig);
52829
52887
  assertHeadersShape(modelConfig.headers, `Profile "${profileId}" model "${modelConfig.id}"`);
52830
52888
  assertOptionalEnvName(modelConfig.apiKeyEnv, `Profile "${profileId}" model "${modelConfig.id}" apiKeyEnv`);
52831
52889
  if (seenModelIds.has(modelConfig.id)) throw new Error(`Profile "${profileId}" contains duplicate model "${modelConfig.id}".`);
@@ -52916,12 +52974,13 @@ var ModelRegistry = class {
52916
52974
  );
52917
52975
  }
52918
52976
  const existing = this.models.get(provider);
52919
- if (existing && (providerConfig.baseUrl || providerConfig.api || providerConfig.headers)) {
52977
+ if (existing && (providerConfig.baseUrl || providerConfig.api || providerConfig.headers || providerConfig.transport)) {
52920
52978
  for (const model of existing.values()) {
52921
52979
  this.upsert({
52922
52980
  ...model,
52923
52981
  baseUrl: providerConfig.baseUrl || model.baseUrl,
52924
52982
  api: providerConfig.api || model.api,
52983
+ transport: providerConfig.transport ?? model.transport,
52925
52984
  apiKeyEnv: providerConfig.apiKeyEnv || model.apiKeyEnv,
52926
52985
  displayName: providerConfig.displayName || model.displayName || provider,
52927
52986
  filePlatform: providerConfig.filePlatform || model.filePlatform,
@@ -52938,9 +52997,8 @@ var ModelRegistry = class {
52938
52997
  this.upsert({
52939
52998
  id: customModel.id,
52940
52999
  name: customModel.name || baseModel?.name || customModel.id,
52941
- api: providerConfig.api || baseModel?.api || "openai-completions",
53000
+ ...resolveProfileRoute(providerConfig, customModel),
52942
53001
  provider,
52943
- baseUrl: providerConfig.baseUrl || baseModel?.baseUrl || "",
52944
53002
  reasoning: customModel.reasoning ?? baseModel?.reasoning ?? false,
52945
53003
  input: customModel.input || baseModel?.input || ["text"],
52946
53004
  cost: customModel.cost || baseModel?.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@@ -52951,6 +53009,7 @@ var ModelRegistry = class {
52951
53009
  filePlatform: customModel.filePlatform || providerConfig.filePlatform || baseModel?.filePlatform,
52952
53010
  attachmentPlatform: customModel.attachmentPlatform || providerConfig.attachmentPlatform || baseModel?.attachmentPlatform,
52953
53011
  headers: providerConfig.headers ? { ...baseModel?.headers, ...providerConfig.headers, ...customModel.headers } : customModel.headers || baseModel?.headers,
53012
+ transport: customModel.transport ?? providerConfig.transport ?? baseModel?.transport,
52954
53013
  compat: customModel.compat || providerConfig.compat || baseModel?.compat,
52955
53014
  cacheCapability: customModel.cacheCapability || providerConfig.cacheCapability || baseModel?.cacheCapability,
52956
53015
  webSearch: customModel.webSearch || providerConfig.webSearch || baseModel?.webSearch,
@@ -53003,6 +53062,7 @@ var ModelRegistry = class {
53003
53062
  if (override.maxTokens !== void 0) patched.maxTokens = override.maxTokens;
53004
53063
  if (override.cost !== void 0) patched.cost = { ...existing.cost, ...override.cost };
53005
53064
  if (override.headers !== void 0) patched.headers = { ...existing.headers, ...override.headers };
53065
+ if (override.transport !== void 0) patched.transport = override.transport;
53006
53066
  if (override.compat !== void 0) patched.compat = deepMergePlainRecords(existing.compat, override.compat);
53007
53067
  if (override.cacheCapability !== void 0) patched.cacheCapability = { ...existing.cacheCapability, ...override.cacheCapability };
53008
53068
  if (override.webSearch !== void 0) patched.webSearch = { ...existing.webSearch, ...override.webSearch };
@@ -53024,9 +53084,8 @@ var ModelRegistry = class {
53024
53084
  this.upsert({
53025
53085
  id: customModel.id,
53026
53086
  name: customModel.name || customModel.id,
53027
- api: providerConfig.api || "openai-completions",
53087
+ ...resolveProfileRoute(providerConfig, customModel),
53028
53088
  provider,
53029
- baseUrl: providerConfig.baseUrl || "",
53030
53089
  reasoning: customModel.reasoning ?? false,
53031
53090
  input: customModel.input || ["text"],
53032
53091
  cost: customModel.cost || { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
@@ -53037,6 +53096,7 @@ var ModelRegistry = class {
53037
53096
  filePlatform: customModel.filePlatform || providerConfig.filePlatform,
53038
53097
  attachmentPlatform: customModel.attachmentPlatform || providerConfig.attachmentPlatform,
53039
53098
  headers: { ...providerConfig.headers, ...customModel.headers },
53099
+ transport: customModel.transport ?? providerConfig.transport,
53040
53100
  compat: customModel.compat || providerConfig.compat,
53041
53101
  cacheCapability: customModel.cacheCapability || providerConfig.cacheCapability,
53042
53102
  webSearch: customModel.webSearch || providerConfig.webSearch,
@@ -53476,12 +53536,13 @@ var ModelRegistry = class {
53476
53536
  this.authStorage.setProviderEnvVar(provider, config.apiKeyEnv);
53477
53537
  }
53478
53538
  const existing = this.models.get(provider);
53479
- if (existing && (config.baseUrl || config.api || config.headers)) {
53539
+ if (existing && (config.baseUrl || config.api || config.headers || config.transport)) {
53480
53540
  for (const model of existing.values()) {
53481
53541
  this.upsert({
53482
53542
  ...model,
53483
53543
  baseUrl: config.baseUrl || model.baseUrl,
53484
53544
  api: config.api || model.api,
53545
+ transport: config.transport ?? model.transport,
53485
53546
  headers: config.headers ? { ...model.headers, ...config.headers } : model.headers
53486
53547
  });
53487
53548
  }
@@ -53502,6 +53563,7 @@ var ModelRegistry = class {
53502
53563
  apiKeyEnv: config.apiKeyEnv || baseModel?.apiKeyEnv,
53503
53564
  displayName: customModel.displayName || baseModel?.displayName || provider,
53504
53565
  headers: config.headers ? { ...baseModel?.headers, ...config.headers, ...customModel.headers } : customModel.headers || baseModel?.headers,
53566
+ transport: customModel.transport ?? config.transport ?? baseModel?.transport,
53505
53567
  compat: customModel.compat || baseModel?.compat,
53506
53568
  cacheCapability: customModel.cacheCapability || baseModel?.cacheCapability,
53507
53569
  webSearch: customModel.webSearch || baseModel?.webSearch,
@@ -54053,7 +54115,7 @@ function checkComputerUse() {
54053
54115
  id: "computer_use_helper",
54054
54116
  label: "computer-use helper",
54055
54117
  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.`,
54118
+ 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
54119
  details: { appPath }
54058
54120
  };
54059
54121
  }
@@ -54341,7 +54403,7 @@ var MODE_RULES = {
54341
54403
  "~/.pie is allowlisted for Pie configuration and session support.",
54342
54404
  "Shell commands run from the workspace and return structured output, timeout, and truncation details.",
54343
54405
  "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.",
54406
+ "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
54407
  "Protected paths from extensions can block reads/writes even when tools are present."
54346
54408
  ],
54347
54409
  plan: [
@@ -54351,7 +54413,7 @@ var MODE_RULES = {
54351
54413
  yolo: [
54352
54414
  "/settings/yolo relaxes filesystem sandbox directory restrictions.",
54353
54415
  "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."
54416
+ "It also auto-approves shell commands, including computer-use-axi desktop input."
54355
54417
  ],
54356
54418
  subagents: [
54357
54419
  "Subagents inherit the parent tool set, then runtime policy removes write and high-risk tools.",
@@ -56502,7 +56564,10 @@ function createExtensionContext(extension, cwd, uiContext, abortFn, isIdleFn, ha
56502
56564
  },
56503
56565
  get yoloMode() {
56504
56566
  return actions?.yoloMode ?? false;
56505
- }
56567
+ },
56568
+ // Only expose setYoloMode when the host can actually toggle it — plan-mode
56569
+ // gates the option on typeof ctx.setYoloMode === "function".
56570
+ ...actions?.setYoloMode ? { setYoloMode: (enabled) => actions.setYoloMode(enabled) } : {}
56506
56571
  };
56507
56572
  return context;
56508
56573
  }
@@ -60395,8 +60460,8 @@ var Editor = class {
60395
60460
  this.historyIndex = -1;
60396
60461
  this.lastAction = null;
60397
60462
  this.pushUndoSnapshot();
60398
- const cleanText2 = pastedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
60399
- const tabExpandedText = cleanText2.replace(/\t/g, " ");
60463
+ const cleanText = pastedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
60464
+ const tabExpandedText = cleanText.replace(/\t/g, " ");
60400
60465
  let filteredText = tabExpandedText.split("").filter((char) => char === "\n" || char.charCodeAt(0) >= 32).join("");
60401
60466
  if (/^[/~.]/.test(filteredText)) {
60402
60467
  const currentLine = this.state.lines[this.state.cursorLine] || "";
@@ -61480,9 +61545,9 @@ var Input = class {
61480
61545
  handlePaste(pastedText) {
61481
61546
  this.lastAction = null;
61482
61547
  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;
61548
+ const cleanText = pastedText.replace(/\r\n/g, "").replace(/\r/g, "").replace(/\n/g, "");
61549
+ this.value = this.value.slice(0, this.cursor) + cleanText + this.value.slice(this.cursor);
61550
+ this.cursor += cleanText.length;
61486
61551
  }
61487
61552
  invalidate() {
61488
61553
  }
@@ -64255,7 +64320,10 @@ var ExtensionRunner = class {
64255
64320
  },
64256
64321
  get yoloMode() {
64257
64322
  return runner.actions.yoloMode ?? false;
64258
- }
64323
+ },
64324
+ // Only expose setYoloMode when the host can actually toggle it — plan-mode
64325
+ // gates the option on typeof ctx.setYoloMode === "function".
64326
+ ...runner.actions.setYoloMode ? { setYoloMode: (enabled) => runner.actions.setYoloMode(enabled) } : {}
64259
64327
  };
64260
64328
  return createLoggedProxy(context, `ExtensionContext#${contextId}`);
64261
64329
  }
@@ -70171,6 +70239,100 @@ var SessionTraceWriter = class extends SessionTraceSnapshotWriter {
70171
70239
  sessionManager;
70172
70240
  };
70173
70241
 
70242
+ // src/runtime/context-signals.ts
70243
+ import { createHash as createHash4 } from "crypto";
70244
+ function applyContextSignalsToFooter(target) {
70245
+ if (!target.agent?.state) return;
70246
+ const signals = computeContextSignals(
70247
+ target,
70248
+ target.agent.state.systemPrompt ?? "",
70249
+ target.agent.state.tools,
70250
+ target.sessionManager.getMessages()
70251
+ );
70252
+ target.footer.setState({
70253
+ prefixHash: signals.prefixHash,
70254
+ prefixHashChanged: signals.prefixHashChanged,
70255
+ cacheHitRate: signals.cacheHitRate
70256
+ });
70257
+ }
70258
+ function computePromptPrefixHash(systemPrompt, tools) {
70259
+ return createHash4("sha256").update(
70260
+ JSON.stringify({
70261
+ systemPrompt,
70262
+ tools: tools.map((tool) => ({
70263
+ name: tool.name,
70264
+ description: tool.description ?? "",
70265
+ parameters: tool.parameters ?? null
70266
+ }))
70267
+ })
70268
+ ).digest("hex").slice(0, 8);
70269
+ }
70270
+ function calculateCumulativeCacheHitRate(messages) {
70271
+ let input = 0;
70272
+ let cacheRead = 0;
70273
+ for (const message of messages) {
70274
+ if (message.role !== "assistant") continue;
70275
+ const usage = message.usage;
70276
+ if (!usage) continue;
70277
+ input += usage.input;
70278
+ cacheRead += usage.cacheRead;
70279
+ }
70280
+ const base = input + cacheRead;
70281
+ return base > 0 ? cacheRead / base : null;
70282
+ }
70283
+ var signalMemories = /* @__PURE__ */ new WeakMap();
70284
+ var lastSignals = /* @__PURE__ */ new WeakMap();
70285
+ function getLastContextSignals(hostKey) {
70286
+ return lastSignals.get(hostKey) ?? null;
70287
+ }
70288
+ function computeContextSignals(hostKey, systemPrompt, tools, messages) {
70289
+ const prefixHash = computePromptPrefixHash(systemPrompt, tools);
70290
+ const systemHash = computePromptPrefixHash(systemPrompt, []);
70291
+ const toolsHash = computePromptPrefixHash("", tools);
70292
+ const previous = signalMemories.get(hostKey);
70293
+ const prefixHashChanged = previous !== void 0 && previous.hash !== prefixHash;
70294
+ if (previous?.hash !== prefixHash) {
70295
+ signalMemories.set(hostKey, { hash: prefixHash, systemHash, toolsHash });
70296
+ }
70297
+ const signals = {
70298
+ prefixHash,
70299
+ prefixHashChanged,
70300
+ ...prefixHashChanged && previous ? { previousPrefixHash: previous.hash } : {},
70301
+ systemPromptChanged: prefixHashChanged && previous !== void 0 && previous.systemHash !== systemHash,
70302
+ toolsChanged: prefixHashChanged && previous !== void 0 && previous.toolsHash !== toolsHash,
70303
+ cacheHitRate: calculateCumulativeCacheHitRate(messages)
70304
+ };
70305
+ lastSignals.set(hostKey, signals);
70306
+ return signals;
70307
+ }
70308
+
70309
+ // src/runtime/cache-invalidation-trace.ts
70310
+ function noteCacheInvalidationInChat(host, signals) {
70311
+ if (!signals || !signals.prefixHashChanged) return;
70312
+ const parts = [];
70313
+ if (signals.systemPromptChanged && signals.toolsChanged) parts.push("system prompt + tools rebuilt");
70314
+ else if (signals.toolsChanged) parts.push("tools rebuilt");
70315
+ else if (signals.systemPromptChanged) parts.push("system prompt rebuilt");
70316
+ else parts.push("prefix rebuilt");
70317
+ const from = signals.previousPrefixHash ?? "?";
70318
+ const line = `\u26A1 Prompt cache invalidated: \u2301${from} \u2192 \u2301${signals.prefixHash} (${parts.join(", ")})`;
70319
+ if (host.chatContainer && host.ui) {
70320
+ host.chatContainer.addChild(new Spacer(1));
70321
+ host.chatContainer.addChild(new Text(theme.fg("warning", line), 1, 0));
70322
+ host.ui.requestRender();
70323
+ }
70324
+ host.sessionTrace?.noteRuntimeEvent("cache_prefix_invalidated", {
70325
+ from,
70326
+ to: signals.prefixHash,
70327
+ systemPromptChanged: signals.systemPromptChanged,
70328
+ toolsChanged: signals.toolsChanged
70329
+ });
70330
+ }
70331
+ function applyContextSignalsWithTrace(host) {
70332
+ applyContextSignalsToFooter(host);
70333
+ noteCacheInvalidationInChat(host, getLastContextSignals(host));
70334
+ }
70335
+
70174
70336
  // src/commands/interactive-session-commands.ts
70175
70337
  import * as os6 from "os";
70176
70338
 
@@ -70359,6 +70521,387 @@ var SessionSelectorComponent = class extends Container {
70359
70521
  }
70360
70522
  };
70361
70523
 
70524
+ // src/core/context-report.ts
70525
+ var APPROX_BYTES_PER_TOKEN = 4;
70526
+ function estimateStringTokens(text) {
70527
+ if (!text) return 0;
70528
+ return Math.ceil(Buffer.byteLength(text, "utf8") / APPROX_BYTES_PER_TOKEN);
70529
+ }
70530
+ function formatReportTokens(count) {
70531
+ if (count < 1e3) return String(count);
70532
+ if (count < 1e4) return `${(count / 1e3).toFixed(1)}k`;
70533
+ if (count < 1e6) return `${Math.round(count / 1e3)}k`;
70534
+ if (count < 1e7) return `${(count / 1e6).toFixed(1)}M`;
70535
+ return `${Math.round(count / 1e6)}M`;
70536
+ }
70537
+ function buildSystemPromptBlocks(systemPrompt, skillsSection, knowledgeSection) {
70538
+ const totalTokens = estimateStringTokens(systemPrompt);
70539
+ const skillsTokens = skillsSection && systemPrompt.includes(skillsSection.trim()) ? estimateStringTokens(skillsSection) : null;
70540
+ const knowledgeTokens = knowledgeSection && systemPrompt.includes(knowledgeSection.trim()) ? estimateStringTokens(knowledgeSection) : null;
70541
+ const accounted = (skillsTokens ?? 0) + (knowledgeTokens ?? 0);
70542
+ return {
70543
+ totalTokens,
70544
+ baseTokens: Math.max(0, totalTokens - accounted),
70545
+ skillsTokens,
70546
+ knowledgeTokens
70547
+ };
70548
+ }
70549
+ function escapeXmlName(name) {
70550
+ return name.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
70551
+ }
70552
+ function extractSkillBlocks(section) {
70553
+ if (!section) return [];
70554
+ return section.split(/(?=<skill>)/).map((block) => block.trim()).filter((block) => block.startsWith("<skill>"));
70555
+ }
70556
+ function buildSkillRows(skillsSection, skills) {
70557
+ const blocks = extractSkillBlocks(skillsSection);
70558
+ if (blocks.length === 0) {
70559
+ return [
70560
+ {
70561
+ id: "skills:none",
70562
+ label: "no skills in prompt",
70563
+ estTokens: 0
70564
+ }
70565
+ ];
70566
+ }
70567
+ return blocks.map((block, index) => {
70568
+ const escapedNameMatch = block.match(/<name>([\s\S]*?)<\/name>/);
70569
+ const escapedName = escapedNameMatch?.[1] ?? "";
70570
+ const skill = skills.find((candidate) => escapeXmlName(candidate.name) === escapedName) ?? skills.find((candidate) => block.includes(escapeXmlName(candidate.name)));
70571
+ const estTokens = estimateStringTokens(block);
70572
+ const source = skill?.source ? `source:${skill.source}` : void 0;
70573
+ const detail = skill?.inlineContent !== void 0 ? "inline content (already loaded)" : skill?.filePath ?? void 0;
70574
+ return {
70575
+ id: `skill:${skill?.name ?? index}`,
70576
+ label: skill?.name ?? `<skill #${index + 1}>`,
70577
+ estTokens,
70578
+ note: source,
70579
+ detail,
70580
+ drill: {
70581
+ kind: "text",
70582
+ title: `Skill \xB7 ${skill?.name ?? `#${index + 1}`}`,
70583
+ text: block
70584
+ }
70585
+ };
70586
+ }).sort((a, b2) => b2.estTokens - a.estTokens);
70587
+ }
70588
+ function parseProjectContextSection(section) {
70589
+ const parts = section.split(/^### (.+)$/m);
70590
+ const files = [];
70591
+ for (let i = 1; i + 1 < parts.length + 1; i += 2) {
70592
+ const path34 = (parts[i] ?? "").trim();
70593
+ const content = (parts[i + 1] ?? "").trim();
70594
+ if (!path34 || !content) continue;
70595
+ files.push({ path: path34, content });
70596
+ }
70597
+ return files;
70598
+ }
70599
+ function buildKnowledgeRows(files) {
70600
+ if (files.length === 0) {
70601
+ return [
70602
+ {
70603
+ id: "knowledge:none",
70604
+ label: "no AGENTS.md files found",
70605
+ estTokens: 0
70606
+ }
70607
+ ];
70608
+ }
70609
+ return files.map((file) => {
70610
+ const lineCount = file.content.split("\n").length;
70611
+ return {
70612
+ id: `knowledge:${file.path}`,
70613
+ label: file.path,
70614
+ estTokens: estimateStringTokens(file.content),
70615
+ note: `${lineCount} lines`,
70616
+ detail: `AGENTS.md`,
70617
+ drill: {
70618
+ kind: "text",
70619
+ title: `Project Context \xB7 ${file.path}`,
70620
+ text: file.content
70621
+ }
70622
+ };
70623
+ });
70624
+ }
70625
+ function estimateToolTokens(tool) {
70626
+ const parameters = tool.parameters === void 0 ? "" : JSON.stringify(tool.parameters);
70627
+ return estimateStringTokens(`${tool.name}
70628
+ ${tool.description ?? ""}
70629
+ ${parameters}`);
70630
+ }
70631
+ var MAX_TOOL_DRILL_CHARS = 6e3;
70632
+ function buildToolRows(tools) {
70633
+ if (tools.length === 0) {
70634
+ return [{ id: "tools:none", label: "no tools registered", estTokens: 0 }];
70635
+ }
70636
+ return tools.map((tool) => {
70637
+ const paramsJson = tool.parameters === void 0 ? "" : JSON.stringify(tool.parameters, null, 2);
70638
+ const drillText = [
70639
+ tool.description ? `# description
70640
+ ${tool.description}` : "",
70641
+ paramsJson ? `# parameters (schema)
70642
+ ${paramsJson}` : ""
70643
+ ].filter(Boolean).join("\n\n").slice(0, MAX_TOOL_DRILL_CHARS);
70644
+ return {
70645
+ id: `tool:${tool.name}`,
70646
+ label: tool.name,
70647
+ estTokens: estimateToolTokens(tool),
70648
+ note: tool.label && tool.label !== tool.name ? tool.label : void 0,
70649
+ detail: tool.description?.split("\n")[0]?.slice(0, 120),
70650
+ drill: { kind: "text", title: `Tool \xB7 ${tool.name}`, text: drillText }
70651
+ };
70652
+ }).sort((a, b2) => b2.estTokens - a.estTokens);
70653
+ }
70654
+ function entryRoleTag(entry) {
70655
+ return entry.role ?? entry.type;
70656
+ }
70657
+ function buildMessageRows(entries, compactionTokensBefore) {
70658
+ if (entries.length === 0) {
70659
+ return [{ id: "messages:none", label: "active context is empty", estTokens: 0 }];
70660
+ }
70661
+ return entries.map((entry) => {
70662
+ const roleTag = entryRoleTag(entry);
70663
+ const isCompaction = entry.type === "compaction";
70664
+ const tokensBefore = isCompaction ? compactionTokensBefore?.(entry.id) : void 0;
70665
+ return {
70666
+ id: `entry:${entry.id}`,
70667
+ label: `#${entry.seq} ${roleTag}`,
70668
+ estTokens: entry.estTokens,
70669
+ roleTag,
70670
+ marker: isCompaction ? "\u2691" : void 0,
70671
+ note: isCompaction ? tokensBefore !== void 0 ? `checkpoint \xB7 context was ~${formatReportTokens(tokensBefore)} before` : "checkpoint" : entry.bodyBytes > 0 ? `${formatReportTokens(entry.bodyBytes)}B body` : void 0,
70672
+ detail: entry.preview,
70673
+ drill: { kind: "entry", entryId: entry.id }
70674
+ };
70675
+ });
70676
+ }
70677
+ var MAX_PROMPT_DRILL_CHARS = 12e3;
70678
+ function buildContextReportData(inputs) {
70679
+ const knowledgeSectionText = inputs.knowledgeFiles.map((file) => `### ${file.path}
70680
+ ${file.content}`).join("\n\n");
70681
+ const blocks = buildSystemPromptBlocks(
70682
+ inputs.systemPrompt,
70683
+ inputs.skillsSection,
70684
+ knowledgeSectionText
70685
+ );
70686
+ const systemPromptRows = [
70687
+ {
70688
+ id: "prompt:base",
70689
+ label: "base prompt (persona + tool guidance + guidelines + docs)",
70690
+ estTokens: blocks.baseTokens,
70691
+ drill: {
70692
+ kind: "text",
70693
+ title: "System Prompt",
70694
+ text: inputs.systemPrompt.slice(0, MAX_PROMPT_DRILL_CHARS)
70695
+ }
70696
+ }
70697
+ ];
70698
+ if (blocks.skillsTokens !== null || inputs.skills.length > 0) {
70699
+ systemPromptRows.push({
70700
+ id: "prompt:skills",
70701
+ label: "skills section",
70702
+ estTokens: blocks.skillsTokens ?? 0,
70703
+ note: `${inputs.skills.length} skills`,
70704
+ drill: {
70705
+ kind: "rows",
70706
+ title: "System Prompt \u203A Skills",
70707
+ rows: buildSkillRows(inputs.skillsSection, inputs.skills),
70708
+ rowKind: "plain"
70709
+ }
70710
+ });
70711
+ }
70712
+ if (blocks.knowledgeTokens !== null || inputs.knowledgeFiles.length > 0) {
70713
+ systemPromptRows.push({
70714
+ id: "prompt:knowledge",
70715
+ label: "project context (AGENTS.md)",
70716
+ estTokens: blocks.knowledgeTokens ?? 0,
70717
+ note: `${inputs.knowledgeFiles.length} files`,
70718
+ drill: {
70719
+ kind: "rows",
70720
+ title: "System Prompt \u203A Project Context",
70721
+ rows: buildKnowledgeRows(inputs.knowledgeFiles),
70722
+ rowKind: "plain"
70723
+ }
70724
+ });
70725
+ }
70726
+ const systemPromptSection = {
70727
+ id: "system-prompt",
70728
+ title: "System prompt",
70729
+ estTokens: blocks.totalTokens,
70730
+ note: `base ~${formatReportTokens(blocks.baseTokens)}`,
70731
+ rows: systemPromptRows
70732
+ };
70733
+ const toolRows = buildToolRows(inputs.tools);
70734
+ const toolsTokens = toolRows.reduce((sum, row) => sum + row.estTokens, 0);
70735
+ const toolsSection = {
70736
+ id: "tools",
70737
+ title: `Tools (${inputs.tools.length})`,
70738
+ estTokens: toolsTokens,
70739
+ note: toolRows[0] ? `largest: ${toolRows[0].label} ~${formatReportTokens(toolRows[0].estTokens)}` : void 0,
70740
+ rows: toolRows
70741
+ };
70742
+ const messageRows = buildMessageRows(inputs.entries, inputs.compactionTokensBefore);
70743
+ const messagesTokens = inputs.entries.reduce((sum, entry) => sum + entry.estTokens, 0);
70744
+ const messagesSection = {
70745
+ id: "messages",
70746
+ title: `Messages (${inputs.entries.length})`,
70747
+ estTokens: messagesTokens,
70748
+ rows: messageRows
70749
+ };
70750
+ const dynamicContextSection = {
70751
+ id: "dynamic-context",
70752
+ title: "Dynamic context",
70753
+ estTokens: 0,
70754
+ note: "unmetered",
70755
+ rows: [
70756
+ {
70757
+ id: "dynamic:runtime-mode",
70758
+ label: "runtime-mode per-turn injections are not counted in these estimates",
70759
+ estTokens: 0,
70760
+ detail: "the host injects a <runtime-mode-context> block (mode flags, date, active tool names) on every request via transformContext; it is part of the est\u2212actual gap below"
70761
+ }
70762
+ ]
70763
+ };
70764
+ const roleCountsMap = /* @__PURE__ */ new Map();
70765
+ for (const entry of inputs.entries) {
70766
+ const tag = entryRoleTag(entry);
70767
+ roleCountsMap.set(tag, (roleCountsMap.get(tag) ?? 0) + 1);
70768
+ }
70769
+ const roleCounts = [...roleCountsMap.entries()].map(([role, count]) => ({ role, count })).sort((a, b2) => b2.count - a.count || a.role.localeCompare(b2.role));
70770
+ const topEntries = [...inputs.entries].sort((a, b2) => b2.estTokens - a.estTokens).slice(0, 5).map((entry) => ({
70771
+ seq: entry.seq,
70772
+ label: `${entryRoleTag(entry)} \xB7 ${entry.preview}`.trim(),
70773
+ estTokens: entry.estTokens
70774
+ }));
70775
+ let latestCompaction = null;
70776
+ for (const entry of inputs.entries) {
70777
+ if (entry.type === "compaction") {
70778
+ latestCompaction = {
70779
+ seq: entry.seq,
70780
+ tokensBefore: inputs.compactionTokensBefore?.(entry.id)
70781
+ };
70782
+ }
70783
+ }
70784
+ const estimatedTotalTokens = blocks.totalTokens + toolsTokens + messagesTokens;
70785
+ return {
70786
+ sections: [systemPromptSection, toolsSection, messagesSection, dynamicContextSection],
70787
+ systemPromptTokens: blocks.totalTokens,
70788
+ toolsTokens,
70789
+ messagesTokens,
70790
+ dynamicContextTokens: 0,
70791
+ estimatedTotalTokens,
70792
+ actualTokens: inputs.actualTokens,
70793
+ contextWindow: inputs.contextWindow,
70794
+ autoCompactLimitTokens: inputs.autoCompactLimitTokens,
70795
+ reserveTokens: inputs.reserveTokens,
70796
+ promptPrefixHash: inputs.promptPrefixHash,
70797
+ promptPrefixChanged: inputs.promptPrefixChanged,
70798
+ roleCounts,
70799
+ topEntries,
70800
+ latestCompaction,
70801
+ entryCount: inputs.entries.length
70802
+ };
70803
+ }
70804
+ var identityTheme = {
70805
+ bold: (text) => text,
70806
+ fg: (_token, text) => text
70807
+ };
70808
+ function padEndVisible(text, width) {
70809
+ return text.length >= width ? text : text + " ".repeat(width - text.length);
70810
+ }
70811
+ function formatReportHeadline(data) {
70812
+ const est = `~${formatReportTokens(data.estimatedTotalTokens)}`;
70813
+ if (data.contextWindow > 0) {
70814
+ const percent = (data.estimatedTotalTokens / data.contextWindow * 100).toFixed(1);
70815
+ return `est ${est} / ${formatReportTokens(data.contextWindow)} (${percent}%)`;
70816
+ }
70817
+ return `est ${est} / window unknown`;
70818
+ }
70819
+ function buildContextOverviewText(data, themeInput) {
70820
+ const theme2 = themeInput ?? identityTheme;
70821
+ const lines = [];
70822
+ const sections = data.sections;
70823
+ lines.push(theme2.bold("Context Report") + theme2.fg("dim", ` \xB7 ${formatReportHeadline(data)}`));
70824
+ const labelWidth = 20;
70825
+ const branch = (index) => index === sections.length ? "\u2514" : "\u251C";
70826
+ sections.forEach((section, index) => {
70827
+ const label = padEndVisible(` ${branch(index)} ${section.title}`, labelWidth);
70828
+ const tokens = `~${formatReportTokens(section.estTokens)}`;
70829
+ const note = section.note ? theme2.fg("dim", ` ${section.note}`) : "";
70830
+ lines.push(`${label}${theme2.fg("accent", tokens)}${note}`);
70831
+ if (section.id === "messages") {
70832
+ const counts = data.roleCounts.map((rc) => `${rc.role} ${rc.count}`).join(" \xB7 ");
70833
+ if (counts) {
70834
+ lines.push(`${" ".repeat(labelWidth + 1)}${theme2.fg("dim", counts)}`);
70835
+ }
70836
+ for (const top of data.topEntries.slice(0, 3)) {
70837
+ const preview = top.label.length > 60 ? `${top.label.slice(0, 59)}\u2026` : top.label;
70838
+ lines.push(
70839
+ `${" ".repeat(labelWidth + 1)}${theme2.fg("dim", `top: #${top.seq} ${preview} (~${formatReportTokens(top.estTokens)})`)}`
70840
+ );
70841
+ }
70842
+ if (data.latestCompaction) {
70843
+ const before = data.latestCompaction.tokensBefore !== void 0 ? ` \xB7 context was ~${formatReportTokens(data.latestCompaction.tokensBefore)} before` : "";
70844
+ lines.push(
70845
+ `${" ".repeat(labelWidth + 1)}${theme2.fg("dim", `\u2691 compact checkpoint @#${data.latestCompaction.seq}${before}`)}`
70846
+ );
70847
+ }
70848
+ }
70849
+ });
70850
+ const autoCompact = data.autoCompactLimitTokens ? `triggers > ~${formatReportTokens(data.autoCompactLimitTokens)} (reserve ${formatReportTokens(data.reserveTokens)})` : "disabled";
70851
+ lines.push(
70852
+ `${padEndVisible(` ${branch(sections.length)} auto-compact`, labelWidth)}${theme2.fg("dim", autoCompact)}`
70853
+ );
70854
+ const reconciliation = data.actualTokens !== null ? `actual ${formatReportTokens(data.actualTokens)} (last response) \xB7 est\u2212actual \u2248 ${formatReportTokens(
70855
+ Math.abs(data.estimatedTotalTokens - data.actualTokens)
70856
+ )}${data.estimatedTotalTokens >= data.actualTokens ? " overhead" : " undercount"}` : "actual unknown until the next model response";
70857
+ lines.push(` ${theme2.fg("dim", reconciliation)}`);
70858
+ lines.push(theme2.fg("dim", " /debugs/context \xB7 Enter to drill into a section, Esc to go back"));
70859
+ return lines.join("\n");
70860
+ }
70861
+ function buildWindowRows(data) {
70862
+ const rows = [
70863
+ {
70864
+ id: "window:window",
70865
+ label: "context window",
70866
+ estTokens: data.contextWindow,
70867
+ valueText: data.contextWindow > 0 ? formatReportTokens(data.contextWindow) : "?",
70868
+ note: data.contextWindow > 0 ? `${(data.estimatedTotalTokens / data.contextWindow * 100).toFixed(1)}% used (est)` : "unknown"
70869
+ },
70870
+ {
70871
+ id: "window:estimated",
70872
+ label: "estimated total (prompt + tools + messages)",
70873
+ estTokens: data.estimatedTotalTokens,
70874
+ note: `prompt ~${formatReportTokens(data.systemPromptTokens)} \xB7 tools ~${formatReportTokens(data.toolsTokens)} \xB7 messages ~${formatReportTokens(data.messagesTokens)}`
70875
+ }
70876
+ ];
70877
+ if (data.actualTokens !== null) {
70878
+ rows.push({
70879
+ id: "window:actual",
70880
+ label: "actual (last model response)",
70881
+ estTokens: data.actualTokens,
70882
+ valueText: formatReportTokens(data.actualTokens),
70883
+ note: `gap \u2248 ${formatReportTokens(Math.abs(data.estimatedTotalTokens - data.actualTokens))}`
70884
+ });
70885
+ }
70886
+ rows.push({
70887
+ id: "window:auto-compact",
70888
+ label: "auto-compact threshold",
70889
+ estTokens: data.autoCompactLimitTokens ?? 0,
70890
+ valueText: data.autoCompactLimitTokens ? `>${formatReportTokens(data.autoCompactLimitTokens)}` : "off",
70891
+ note: data.autoCompactLimitTokens ? `reserve ${formatReportTokens(data.reserveTokens)}` : "disabled"
70892
+ });
70893
+ if (data.promptPrefixHash) {
70894
+ rows.push({
70895
+ id: "window:prefix-hash",
70896
+ label: "prompt+tools hash (cache prefix)",
70897
+ estTokens: 0,
70898
+ valueText: data.promptPrefixHash,
70899
+ note: data.promptPrefixChanged ? "changed since last turn \u2014 provider cache invalidated \u26A1" : "stable \u2014 cache-friendly"
70900
+ });
70901
+ }
70902
+ return rows;
70903
+ }
70904
+
70362
70905
  // src/components/tree-selector.ts
70363
70906
  var RESET = "\x1B[0m";
70364
70907
  var INVERSE = "\x1B[7m";
@@ -70728,6 +71271,9 @@ var TreeSelectorComponent = class {
70728
71271
  pageNext() {
70729
71272
  if (this.goToNextPage()) {
70730
71273
  this.statusMessage = null;
71274
+ } else if (this.filteredRows.length > 0) {
71275
+ this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71276
+ this.statusMessage = "latest page - at newest entry";
70731
71277
  } else {
70732
71278
  this.statusMessage = this.pageSummaries.length === 0 ? "no entries" : "latest page";
70733
71279
  }
@@ -70735,10 +71281,58 @@ var TreeSelectorComponent = class {
70735
71281
  pagePrev() {
70736
71282
  if (this.goToPreviousPage()) {
70737
71283
  this.statusMessage = null;
71284
+ } else if (this.filteredRows.length > 0) {
71285
+ this.selectedIndex = 0;
71286
+ this.statusMessage = "oldest page - at first entry";
70738
71287
  } else {
70739
71288
  this.statusMessage = this.pageSummaries.length === 0 ? "no entries" : "oldest page";
70740
71289
  }
70741
71290
  }
71291
+ /** Jump to the oldest page (seqs start at 1; afterSeq 0 = from the start). */
71292
+ jumpToFirstPage() {
71293
+ if (this.pageSummaries.length > 0 && !this.hasPrev) {
71294
+ this.selectedIndex = 0;
71295
+ this.statusMessage = "oldest page - at first entry";
71296
+ return;
71297
+ }
71298
+ if (this.fetchWindow(0, null)) {
71299
+ this.selectedIndex = 0;
71300
+ this.statusMessage = null;
71301
+ }
71302
+ }
71303
+ /** Jump to the newest page. Seqs are 1..total (append-only journal), so the last window starts at total - pageSize. */
71304
+ jumpToLastPage() {
71305
+ if (this.pageSummaries.length > 0 && !this.hasNext) {
71306
+ this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71307
+ this.statusMessage = "latest page - at newest entry";
71308
+ return;
71309
+ }
71310
+ let anchor = Math.max(0, this.totalEntries - this.pageSizeValue);
71311
+ for (let attempt = 0; attempt < 4; attempt++) {
71312
+ if (this.fetchWindow(anchor, null)) {
71313
+ if (this.hasNext) {
71314
+ anchor = this.pageSummaries[this.pageSummaries.length - 1].seq;
71315
+ continue;
71316
+ }
71317
+ this.selectedIndex = Math.max(0, this.filteredRows.length - 1);
71318
+ this.statusMessage = null;
71319
+ return;
71320
+ }
71321
+ if (anchor === 0 || !this.hasNext) break;
71322
+ anchor = Math.max(0, anchor - this.pageSizeValue);
71323
+ }
71324
+ }
71325
+ /** Move the cursor one viewport within the resident page; ←/→ change pages. */
71326
+ screenScroll(delta) {
71327
+ if (this.filteredRows.length === 0) return;
71328
+ const next = Math.max(0, Math.min(this.selectedIndex + delta, this.filteredRows.length - 1));
71329
+ if (next === this.selectedIndex) {
71330
+ this.statusMessage = delta > 0 ? "end of page - \u2192 for next page" : "start of page - \u2190 for previous page";
71331
+ return;
71332
+ }
71333
+ this.selectedIndex = next;
71334
+ this.statusMessage = null;
71335
+ }
70742
71336
  /**
70743
71337
  * Bounded "next match": step to the next in-page match, otherwise scan
70744
71338
  * forward at most SEARCH_SCAN_PAGE_LIMIT pages per press. Never scans
@@ -70792,7 +71386,7 @@ var TreeSelectorComponent = class {
70792
71386
  lines.push(createLine(" Conversation Tree", width, false));
70793
71387
  lines.push(
70794
71388
  createLine(
70795
- " \u2191\u2193:move \u2190\u2192:page e:collapse tab:next match F:fork Enter:navigate Esc:cancel",
71389
+ " \u2191\u2193:move \u2190\u2192:page PgUp/PgDn:screen Home/End:jump e:collapse tab:match F:fork Enter:go Esc:cancel",
70796
71390
  width,
70797
71391
  false
70798
71392
  )
@@ -70829,12 +71423,13 @@ var TreeSelectorComponent = class {
70829
71423
  const activeIndicator = this.options.activePathSeqs.has(row.summary.seq) ? theme.fg("success", TREE_CHARS.active) : DIM + TREE_CHARS.inactive + RESET;
70830
71424
  const expandIndicator = row.inWindowChildren ? theme.fg("dim", this.collapsed.has(row.summary.id) ? "\u25B6 " : "\u25BC ") : row.hasChildren ? theme.fg("dim", "\xB7\xB7") : " ";
70831
71425
  const role = roleColor(roleName(row.summary));
71426
+ const tokenNote = row.summary.estTokens > 0 ? theme.fg("dim", ` ~${formatReportTokens(row.summary.estTokens)}`) : "";
70832
71427
  const indicatorWidth = visibleWidth(activeIndicator) + visibleWidth(expandIndicator) + visibleWidth(role) + 2;
70833
- const prefixBudget = Math.max(0, Math.min(Math.floor(width * 0.35), width - indicatorWidth - MIN_BODY_WIDTH));
71428
+ const prefixBudget = Math.max(0, Math.min(Math.floor(width * 0.35), width - indicatorWidth - MIN_BODY_WIDTH - visibleWidth(tokenNote)));
70834
71429
  const displayPrefix = clampPrefixForDisplay(row.prefix, prefixBudget);
70835
- const availableWidth = Math.max(0, width - visibleWidth(displayPrefix) - indicatorWidth);
71430
+ const availableWidth = Math.max(0, width - visibleWidth(displayPrefix) - indicatorWidth - visibleWidth(tokenNote) - 2);
70836
71431
  const content = truncateToWidth(row.summary.preview ?? "", availableWidth);
70837
- lines.push(createLine(`${displayPrefix}${activeIndicator}${expandIndicator}${role}: ${content}`, width, i === this.selectedIndex));
71432
+ lines.push(createLine(`${displayPrefix}${activeIndicator}${expandIndicator}${role}: ${content}${tokenNote}`, width, i === this.selectedIndex));
70838
71433
  }
70839
71434
  for (let i = endIndex - startIndex; i < this.maxVisibleLines; i++) {
70840
71435
  lines.push(createLine("", width, false));
@@ -70860,10 +71455,18 @@ var TreeSelectorComponent = class {
70860
71455
  this.moveUp();
70861
71456
  } else if (kb.matches(keyData, "selectDown")) {
70862
71457
  this.moveDown();
70863
- } else if (kb.matches(keyData, "cursorRight") || kb.matches(keyData, "selectPageDown")) {
71458
+ } else if (kb.matches(keyData, "cursorRight")) {
70864
71459
  this.pageNext();
70865
- } else if (kb.matches(keyData, "cursorLeft") || kb.matches(keyData, "selectPageUp")) {
71460
+ } else if (kb.matches(keyData, "cursorLeft")) {
70866
71461
  this.pagePrev();
71462
+ } else if (kb.matches(keyData, "selectPageDown")) {
71463
+ this.screenScroll(this.maxVisibleLines);
71464
+ } else if (kb.matches(keyData, "selectPageUp")) {
71465
+ this.screenScroll(-this.maxVisibleLines);
71466
+ } else if (kb.matches(keyData, "cursorLineStart")) {
71467
+ this.jumpToFirstPage();
71468
+ } else if (kb.matches(keyData, "cursorLineEnd")) {
71469
+ this.jumpToLastPage();
70867
71470
  } else if (keyData === "e" || keyData === "E") {
70868
71471
  this.toggleCollapse();
70869
71472
  } else if (kb.matches(keyData, "selectConfirm")) {
@@ -72173,6 +72776,29 @@ function deriveSessionTitle(options) {
72173
72776
  }
72174
72777
  return `S:${activeSession.id.slice(-6)}`;
72175
72778
  }
72779
+ function calculateSessionUsageTotals(messages) {
72780
+ let input = 0;
72781
+ let output = 0;
72782
+ let cacheRead = 0;
72783
+ let cacheWrite = 0;
72784
+ let cost = 0;
72785
+ for (const message of messages) {
72786
+ if (message.role !== "assistant") continue;
72787
+ const usage = message.usage;
72788
+ if (!usage) continue;
72789
+ input += usage.input;
72790
+ output += usage.output;
72791
+ cacheRead += usage.cacheRead;
72792
+ cacheWrite += usage.cacheWrite;
72793
+ cost += usage.cost?.total ?? 0;
72794
+ }
72795
+ return { input, output, cacheRead, cacheWrite, cost };
72796
+ }
72797
+ function formatTokensCompact(count) {
72798
+ if (count < 1e3) return String(count);
72799
+ if (count < 1e6) return `${(count / 1e3).toFixed(1)}k`;
72800
+ return `${(count / 1e6).toFixed(2)}M`;
72801
+ }
72176
72802
  function buildSessionInfoText(options) {
72177
72803
  const displayCwd = options.homeDir && options.cwd.startsWith(options.homeDir) ? `~${options.cwd.slice(options.homeDir.length)}` : options.cwd;
72178
72804
  let info = options.theme.bold("Session Info") + "\n\n";
@@ -72195,6 +72821,39 @@ function buildSessionInfoText(options) {
72195
72821
  `;
72196
72822
  info += `${options.theme.fg("dim", "Thinking Level:")} ${options.thinkingLevel}
72197
72823
  `;
72824
+ if (options.messages && options.messages.length > 0) {
72825
+ const totals = calculateSessionUsageTotals(options.messages);
72826
+ const turnCount = options.messages.filter((m2) => m2.role === "assistant").length;
72827
+ if (turnCount > 0) {
72828
+ info += `
72829
+ ${options.theme.bold("Usage (active context)")}
72830
+ `;
72831
+ info += `${options.theme.fg("dim", "Turns:")} ${turnCount}
72832
+ `;
72833
+ info += `${options.theme.fg("dim", "Input:")} ${totals.input.toLocaleString()}
72834
+ `;
72835
+ info += `${options.theme.fg("dim", "Output:")} ${totals.output.toLocaleString()}
72836
+ `;
72837
+ if (totals.cacheRead > 0) {
72838
+ info += `${options.theme.fg("dim", "Cache Read:")} ${totals.cacheRead.toLocaleString()}
72839
+ `;
72840
+ }
72841
+ if (totals.cacheWrite > 0) {
72842
+ info += `${options.theme.fg("dim", "Cache Write:")} ${totals.cacheWrite.toLocaleString()}
72843
+ `;
72844
+ }
72845
+ if (totals.cost > 0) {
72846
+ info += `${options.theme.fg("dim", "Cost:")} $${totals.cost.toFixed(4)}
72847
+ `;
72848
+ }
72849
+ const hitRateBase = totals.input + totals.cacheRead;
72850
+ if (hitRateBase > 0 && totals.cacheRead > 0) {
72851
+ const hitRate = (totals.cacheRead / hitRateBase * 100).toFixed(1);
72852
+ info += `${options.theme.fg("dim", "Cache Hit Rate:")} ${hitRate}% (~${formatTokensCompact(hitRateBase)} served)
72853
+ `;
72854
+ }
72855
+ }
72856
+ }
72198
72857
  return info;
72199
72858
  }
72200
72859
  function extractUserText(msg) {
@@ -72326,6 +72985,7 @@ async function loadSession(host, sessionId) {
72326
72985
  contextPercent,
72327
72986
  contextWindow
72328
72987
  });
72988
+ applyContextSignalsWithTrace(host);
72329
72989
  host.ui.requestRender();
72330
72990
  setTimeout(() => host.ui.requestRender(), 100);
72331
72991
  } catch (error) {
@@ -73103,8 +73763,9 @@ var BUILTIN_SLASH_COMMANDS = [
73103
73763
  { name: "settings/calm", description: "Toggle calm mode: show only questions and final answers" },
73104
73764
  { name: "settings/yolo", description: "Toggle or set YOLO mode" },
73105
73765
  { name: "debugs/cache-test", description: "Run provider cache diagnostics with a dedicated 2-request probe" },
73106
- { name: "debugs/context", description: "Append a context summary to ~/.pie/logs/pie-cli.log" },
73107
- { name: "debugs/package", description: "Append a package summary to ~/.pie/logs/pie-cli.log" },
73766
+ { name: "debugs/context", description: "Context composition report: sections, tokens, drill-down" },
73767
+ { name: "debugs/trace", description: "Session trace: timeline, tool stats, runtime events" },
73768
+ { name: "debugs/boot", description: "Boot composition pipeline: how the runtime was assembled" },
73108
73769
  { name: "settings/quit", description: "Exit Pie" },
73109
73770
  { name: "skills", description: "Manage skills: list, toggle enable/disable, invoke" },
73110
73771
  { name: "skills/list", description: "List available skills (plain text)" },
@@ -73362,6 +74023,7 @@ function bindInteractiveRuntimeEvents(host) {
73362
74023
  contextPercent,
73363
74024
  contextWindow
73364
74025
  });
74026
+ applyContextSignalsWithTrace(host);
73365
74027
  host.safeRequestRender();
73366
74028
  host.sessionManager.save().catch((err) => {
73367
74029
  console.error("[Session] Auto-save failed:", err);
@@ -73810,7 +74472,7 @@ function assistantDisplayBlocks(content) {
73810
74472
  }
73811
74473
 
73812
74474
  // src/commands/interactive-debug-commands.ts
73813
- import { createHash as createHash4 } from "crypto";
74475
+ import { createHash as createHash5 } from "crypto";
73814
74476
  function errorMessage(error) {
73815
74477
  return error instanceof Error ? error.message : String(error);
73816
74478
  }
@@ -73848,7 +74510,7 @@ async function handleCacheTest(host) {
73848
74510
  timestamp: fixedTimestamp
73849
74511
  }
73850
74512
  ];
73851
- const prefixHash = createHash4("sha256").update(
74513
+ const prefixHash = createHash5("sha256").update(
73852
74514
  JSON.stringify({
73853
74515
  systemPrompt: stablePrefix,
73854
74516
  tools: toolDefs.map((tool) => ({
@@ -73957,126 +74619,6 @@ async function handleCacheTest(host) {
73957
74619
  host.chatContainer.addChild(new Spacer(1));
73958
74620
  host.ui.requestRender();
73959
74621
  }
73960
- async function handleDebugContextCommand(host) {
73961
- host.chatContainer.addChild(new Spacer(1));
73962
- host.chatContainer.addChild(new Text(theme.bold("Debug Context"), 1, 0));
73963
- host.ui.requestRender();
73964
- try {
73965
- const state = host.agent.state;
73966
- const debugInfo = {
73967
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
73968
- systemPrompt: state.systemPrompt,
73969
- model: state.model,
73970
- thinkingLevel: state.thinkingLevel,
73971
- tools: state.tools.map((t) => ({
73972
- name: t.name,
73973
- description: t.description,
73974
- label: t.label
73975
- })),
73976
- messageCount: state.messages.length,
73977
- messages: state.messages,
73978
- isStreaming: state.isStreaming,
73979
- pendingToolCalls: Array.from(state.pendingToolCalls)
73980
- };
73981
- getLogger().child({ module: "context_dump" }).info("Agent context snapshot", {
73982
- snapshot: {
73983
- timestamp: debugInfo.timestamp,
73984
- modelId: debugInfo.model?.id ?? "",
73985
- provider: debugInfo.model?.provider ?? "",
73986
- thinkingLevel: debugInfo.thinkingLevel,
73987
- toolCount: debugInfo.tools.length,
73988
- messageCount: debugInfo.messageCount,
73989
- isStreaming: debugInfo.isStreaming,
73990
- pendingToolCalls: debugInfo.pendingToolCalls.length
73991
- }
73992
- });
73993
- host.chatContainer.addChild(new Text(
73994
- theme.fg("success", "\u2713 Context summary appended to ~/.pie/logs/pie-cli.log"),
73995
- 1,
73996
- 0
73997
- ));
73998
- host.chatContainer.addChild(new Text(
73999
- theme.fg("dim", ` Messages: ${state.messages.length}, Tools: ${state.tools.length}`),
74000
- 1,
74001
- 0
74002
- ));
74003
- } catch (err) {
74004
- host.showError(`Failed to save context: ${errorMessage(err)}`);
74005
- }
74006
- host.chatContainer.addChild(new Spacer(1));
74007
- host.ui.requestRender();
74008
- }
74009
- async function handleDebugPackageCommand(host) {
74010
- host.chatContainer.addChild(new Spacer(1));
74011
- host.chatContainer.addChild(new Text(theme.bold("Debug Package"), 1, 0));
74012
- host.ui.requestRender();
74013
- try {
74014
- const messages = host.agent.state.messages;
74015
- const packageInfo = {
74016
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
74017
- totalMessages: messages.length,
74018
- conversation: messages.map((msg, index) => {
74019
- const baseInfo = {
74020
- index,
74021
- role: msg.role
74022
- };
74023
- if (msg.role === "user") {
74024
- return {
74025
- ...baseInfo,
74026
- direction: "\u2192 SEND",
74027
- content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content, null, 2)
74028
- };
74029
- } else if (msg.role === "assistant") {
74030
- const assistantMsg = msg;
74031
- const toolCalls = assistantMsg.content.filter((c) => c.type === "toolCall").map((c) => ({ name: c.name, arguments: c.arguments, id: c.id }));
74032
- return {
74033
- ...baseInfo,
74034
- direction: "\u2190 RECEIVE",
74035
- content: assistantMsg.content,
74036
- toolCalls,
74037
- usage: assistantMsg.usage
74038
- };
74039
- } else if (msg.role === "toolResult") {
74040
- const toolMsg = msg;
74041
- return {
74042
- ...baseInfo,
74043
- direction: "\u2192 TOOL RESULT",
74044
- toolCallId: toolMsg.toolCallId,
74045
- content: toolMsg.content
74046
- };
74047
- }
74048
- return baseInfo;
74049
- })
74050
- };
74051
- getLogger().child({ module: "package_dump" }).info("Agent package snapshot", {
74052
- snapshot: {
74053
- timestamp: packageInfo.timestamp,
74054
- totalMessages: packageInfo.totalMessages,
74055
- userMessages: packageInfo.conversation.filter((m2) => m2.role === "user").length,
74056
- assistantMessages: packageInfo.conversation.filter((m2) => m2.role === "assistant").length,
74057
- toolMessages: packageInfo.conversation.filter((m2) => m2.role === "toolResult").length,
74058
- lastMessageRole: packageInfo.conversation.length > 0 ? packageInfo.conversation[packageInfo.conversation.length - 1].role : ""
74059
- }
74060
- });
74061
- host.chatContainer.addChild(new Text(
74062
- theme.fg("success", "\u2713 Package summary appended to ~/.pie/logs/pie-cli.log"),
74063
- 1,
74064
- 0
74065
- ));
74066
- const userCount = messages.filter((m2) => m2.role === "user").length;
74067
- const assistantCount = messages.filter((m2) => m2.role === "assistant").length;
74068
- const toolCount = messages.filter((m2) => m2.role === "toolResult").length;
74069
- host.chatContainer.addChild(new Text(
74070
- theme.fg("dim", ` User: ${userCount}, Assistant: ${assistantCount}, Tool: ${toolCount}`),
74071
- 1,
74072
- 0
74073
- ));
74074
- } catch (err) {
74075
- host.showError(`Failed to save package log: ${errorMessage(err)}`);
74076
- }
74077
- host.chatContainer.addChild(new Spacer(1));
74078
- host.ui.requestRender();
74079
- }
74080
74622
 
74081
74623
  // src/commands/interactive-skill-commands.ts
74082
74624
  import * as fs24 from "fs";
@@ -74530,9 +75072,9 @@ function collectKnownToolNames(host) {
74530
75072
  function describeToggleEffect(host, name, enabled) {
74531
75073
  const loaded = collectKnownToolNames(host).active.includes(name);
74532
75074
  if (enabled) {
74533
- 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).`;
75075
+ 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).`;
74534
75076
  }
74535
- return loaded ? `Tool "${name}" disabled (removed from this session).` : `Tool "${name}" disabled in ~/.pie/settings.json.`;
75077
+ return loaded ? `Tool "${name}" disabled (saved to ~/.pie/settings.json; removed from this and future sessions).` : `Tool "${name}" disabled in ~/.pie/settings.json.`;
74536
75078
  }
74537
75079
  function showToolsManager(host) {
74538
75080
  host.showSelector((done) => {
@@ -74637,7 +75179,7 @@ function handleToolsCommand(host, args) {
74637
75179
  host.chatContainer.addChild(new Text(theme.fg("dim", " No tools available."), 1, 0));
74638
75180
  }
74639
75181
  for (const name of active) {
74640
- const state = host.options.settingsManager.isToolEnabled(name) ? "enabled" : "disabled (removed from this session)";
75182
+ const state = host.options.settingsManager.isToolEnabled(name) ? "enabled" : "disabled (saved to ~/.pie/settings.json; removed this session)";
74641
75183
  host.chatContainer.addChild(new Text(theme.fg("dim", ` \u2022 ${name}: ${state}`), 1, 0));
74642
75184
  }
74643
75185
  for (const name of disabled) {
@@ -74653,6 +75195,944 @@ function handleToolsCommand(host, args) {
74653
75195
  showToolsManager(host);
74654
75196
  }
74655
75197
 
75198
+ // src/components/context-report-selector.ts
75199
+ var RESET2 = "\x1B[0m";
75200
+ var INVERSE2 = "\x1B[7m";
75201
+ var ROLE_FILTER_CYCLE = ["all", "user", "assistant", "toolResult", "compaction"];
75202
+ function createLine2(content, width, isSelected) {
75203
+ const normalized = content.replace(/[\r\n]+/g, " ");
75204
+ const body = isSelected ? INVERSE2 + normalized : normalized;
75205
+ return truncateToWidth(body + RESET2, width, "...", true);
75206
+ }
75207
+ function buildRootRows(sections, windowSection) {
75208
+ const rows = sections.map((section) => ({
75209
+ id: `section:${section.id}`,
75210
+ label: section.title,
75211
+ estTokens: section.estTokens,
75212
+ note: section.note,
75213
+ detail: section.id === "messages" ? "active context entries (Enter to list)" : void 0,
75214
+ drill: {
75215
+ kind: "rows",
75216
+ title: `${section.title}`,
75217
+ rows: section.rows,
75218
+ rowKind: section.id === "messages" ? "messages" : "plain"
75219
+ }
75220
+ }));
75221
+ if (windowSection) {
75222
+ rows.push({
75223
+ id: "section:window",
75224
+ label: windowSection.label,
75225
+ estTokens: windowSection.estTokens,
75226
+ valueText: windowSection.valueText,
75227
+ detail: windowSection.detail,
75228
+ drill: {
75229
+ kind: "rows",
75230
+ title: windowSection.title ?? windowSection.label,
75231
+ rows: windowSection.rows,
75232
+ rowKind: "plain"
75233
+ }
75234
+ });
75235
+ }
75236
+ return rows;
75237
+ }
75238
+ var ContextReportSelectorComponent = class {
75239
+ options;
75240
+ maxVisibleLines;
75241
+ views = [];
75242
+ selectedIndex = 0;
75243
+ searchQuery = "";
75244
+ statusMessage = null;
75245
+ _focused = false;
75246
+ get focused() {
75247
+ return this._focused;
75248
+ }
75249
+ set focused(value) {
75250
+ this._focused = value;
75251
+ }
75252
+ constructor(options) {
75253
+ this.options = options;
75254
+ this.maxVisibleLines = Math.max(15, Math.floor(options.terminalHeight * 0.6));
75255
+ this.views = [
75256
+ {
75257
+ kind: "list",
75258
+ title: options.title,
75259
+ rows: buildRootRows(options.sections, options.windowSection),
75260
+ rowKind: "root",
75261
+ roleFilter: "all",
75262
+ sortBySize: false
75263
+ }
75264
+ ];
75265
+ }
75266
+ // ---------------------------------------------------------------------------
75267
+ // Filtering / sorting
75268
+ // ---------------------------------------------------------------------------
75269
+ get currentView() {
75270
+ return this.views[this.views.length - 1];
75271
+ }
75272
+ visibleListRows(view) {
75273
+ let rows = view.rows;
75274
+ if (view.roleFilter !== "all") {
75275
+ rows = rows.filter((row) => row.roleTag === view.roleFilter);
75276
+ }
75277
+ if (view.rowKind === "messages" && view.sortBySize) {
75278
+ rows = [...rows].sort((a, b2) => b2.estTokens - a.estTokens);
75279
+ }
75280
+ if (this.searchQuery) {
75281
+ const query = this.searchQuery.toLowerCase();
75282
+ rows = rows.filter(
75283
+ (row) => `${row.label} ${row.detail ?? ""} ${row.note ?? ""} ${row.roleTag ?? ""}`.toLowerCase().includes(query)
75284
+ );
75285
+ }
75286
+ return rows;
75287
+ }
75288
+ applyFilterChange() {
75289
+ this.selectedIndex = 0;
75290
+ this.statusMessage = null;
75291
+ }
75292
+ // ---------------------------------------------------------------------------
75293
+ // Navigation
75294
+ // ---------------------------------------------------------------------------
75295
+ moveUp() {
75296
+ const view = this.currentView;
75297
+ if (view.kind === "list") {
75298
+ const rows = this.visibleListRows(view);
75299
+ if (rows.length > 0) {
75300
+ this.selectedIndex = (this.selectedIndex - 1 + rows.length) % rows.length;
75301
+ }
75302
+ } else {
75303
+ view.scroll = Math.max(0, view.scroll - 1);
75304
+ }
75305
+ }
75306
+ moveDown() {
75307
+ const view = this.currentView;
75308
+ if (view.kind === "list") {
75309
+ const rows = this.visibleListRows(view);
75310
+ if (rows.length > 0) {
75311
+ this.selectedIndex = (this.selectedIndex + 1) % rows.length;
75312
+ }
75313
+ } else {
75314
+ view.scroll = Math.min(Math.max(0, view.lines.length - 1), view.scroll + 1);
75315
+ }
75316
+ }
75317
+ pageUp() {
75318
+ const view = this.currentView;
75319
+ if (view.kind === "list") {
75320
+ this.selectedIndex = Math.max(0, this.selectedIndex - this.maxVisibleLines);
75321
+ } else {
75322
+ view.scroll = Math.max(0, view.scroll - this.maxVisibleLines);
75323
+ }
75324
+ }
75325
+ pageDown() {
75326
+ const view = this.currentView;
75327
+ if (view.kind === "list") {
75328
+ const rows = this.visibleListRows(view);
75329
+ this.selectedIndex = Math.min(Math.max(0, rows.length - 1), this.selectedIndex + this.maxVisibleLines);
75330
+ } else {
75331
+ view.scroll = Math.min(
75332
+ Math.max(0, view.lines.length - 1),
75333
+ view.scroll + this.maxVisibleLines
75334
+ );
75335
+ }
75336
+ }
75337
+ pushTextView(title, text) {
75338
+ this.searchQuery = "";
75339
+ this.views.push({
75340
+ kind: "text",
75341
+ title,
75342
+ lines: text.replace(/\r\n/g, "\n").split("\n"),
75343
+ scroll: 0
75344
+ });
75345
+ this.selectedIndex = 0;
75346
+ this.statusMessage = null;
75347
+ }
75348
+ drillIntoSelected() {
75349
+ const view = this.currentView;
75350
+ if (view.kind !== "list") return;
75351
+ const rows = this.visibleListRows(view);
75352
+ const row = rows[this.selectedIndex];
75353
+ if (!row) return;
75354
+ if (!row.drill) {
75355
+ this.statusMessage = "no further detail";
75356
+ return;
75357
+ }
75358
+ switch (row.drill.kind) {
75359
+ case "rows":
75360
+ this.searchQuery = "";
75361
+ this.views.push({
75362
+ kind: "list",
75363
+ title: row.drill.title,
75364
+ rows: row.drill.rows,
75365
+ rowKind: row.drill.rowKind,
75366
+ roleFilter: "all",
75367
+ sortBySize: false
75368
+ });
75369
+ this.selectedIndex = 0;
75370
+ this.statusMessage = null;
75371
+ break;
75372
+ case "text":
75373
+ this.pushTextView(row.drill.title, row.drill.text);
75374
+ break;
75375
+ case "entry": {
75376
+ const text = this.options.getEntryText?.(row.drill.entryId) ?? null;
75377
+ if (text === null) {
75378
+ this.statusMessage = "entry body unavailable";
75379
+ } else {
75380
+ this.pushTextView(`Entry \xB7 ${row.label}`, text);
75381
+ }
75382
+ break;
75383
+ }
75384
+ }
75385
+ }
75386
+ popView() {
75387
+ if (this.views.length > 1) {
75388
+ this.views.pop();
75389
+ this.selectedIndex = 0;
75390
+ this.statusMessage = null;
75391
+ } else {
75392
+ this.options.onDone();
75393
+ }
75394
+ }
75395
+ // ---------------------------------------------------------------------------
75396
+ // Input
75397
+ // ---------------------------------------------------------------------------
75398
+ handleInput(keyData) {
75399
+ const kb = getEditorKeybindings();
75400
+ if (kb.matches(keyData, "selectUp")) {
75401
+ this.moveUp();
75402
+ } else if (kb.matches(keyData, "selectDown")) {
75403
+ this.moveDown();
75404
+ } else if (kb.matches(keyData, "selectPageUp")) {
75405
+ this.pageUp();
75406
+ } else if (kb.matches(keyData, "selectPageDown")) {
75407
+ this.pageDown();
75408
+ } else if (kb.matches(keyData, "selectConfirm")) {
75409
+ this.drillIntoSelected();
75410
+ } else if (kb.matches(keyData, "selectCancel")) {
75411
+ if (this.searchQuery) {
75412
+ this.searchQuery = "";
75413
+ this.applyFilterChange();
75414
+ } else {
75415
+ this.popView();
75416
+ }
75417
+ } else if (matchesKey(keyData, "ctrl+u")) {
75418
+ const view = this.currentView;
75419
+ if (view.kind === "list" && view.rowKind === "messages") {
75420
+ const next = ROLE_FILTER_CYCLE[(ROLE_FILTER_CYCLE.indexOf(view.roleFilter) + 1) % ROLE_FILTER_CYCLE.length];
75421
+ view.roleFilter = next;
75422
+ this.applyFilterChange();
75423
+ }
75424
+ } else if ((keyData === "s" || keyData === "S") && this.searchQuery === "") {
75425
+ const view = this.currentView;
75426
+ if (view.kind === "list" && view.rowKind === "messages") {
75427
+ view.sortBySize = !view.sortBySize;
75428
+ this.applyFilterChange();
75429
+ }
75430
+ } else if (kb.matches(keyData, "deleteCharBackward")) {
75431
+ if (this.searchQuery.length > 0) {
75432
+ this.searchQuery = this.searchQuery.slice(0, -1);
75433
+ this.applyFilterChange();
75434
+ }
75435
+ } else {
75436
+ const hasControlChars = [...keyData].some((ch) => {
75437
+ const code = ch.charCodeAt(0);
75438
+ return code < 32 || code === 127 || code >= 128 && code <= 159;
75439
+ });
75440
+ if (!hasControlChars && keyData.length > 0 && this.searchQuery.length < 100) {
75441
+ this.searchQuery += keyData;
75442
+ this.applyFilterChange();
75443
+ }
75444
+ }
75445
+ }
75446
+ // ---------------------------------------------------------------------------
75447
+ // Rendering
75448
+ // ---------------------------------------------------------------------------
75449
+ invalidate() {
75450
+ }
75451
+ hintsLine(view) {
75452
+ if (view.kind === "text") {
75453
+ return " \u2191\u2193:scroll Esc:back";
75454
+ }
75455
+ const base = " \u2191\u2193:move Enter:drill Esc:back type:search PgUp/PgDn:page";
75456
+ if (view.rowKind === "messages") {
75457
+ return `${base} ctrl+u:role(${view.roleFilter}) s:sort(${view.sortBySize ? "size" : "seq"})`;
75458
+ }
75459
+ return base;
75460
+ }
75461
+ renderRowLine(row, width, selected) {
75462
+ const valueText = row.valueText ?? `~${formatReportTokens(row.estTokens)}`;
75463
+ const rightParts = [theme.fg("accent", valueText)];
75464
+ if (row.note) {
75465
+ rightParts.push(theme.fg("dim", row.note));
75466
+ }
75467
+ const right = rightParts.join(" ");
75468
+ const rightWidth = visibleWidth(right);
75469
+ const marker = row.marker ? theme.fg("warning", `${row.marker} `) : "";
75470
+ const detail = row.detail ? theme.fg("dim", ` \xB7 ${row.detail}`) : "";
75471
+ let left = ` ${marker}${row.label}${detail}`;
75472
+ const leftBudget = Math.max(12, width - rightWidth - 3);
75473
+ if (visibleWidth(left) > leftBudget) {
75474
+ left = truncateToWidth(left, leftBudget, "\u2026");
75475
+ }
75476
+ const gap = Math.max(1, width - visibleWidth(left) - rightWidth - 1);
75477
+ return createLine2(`${left}${" ".repeat(gap)}${right}`, width, selected);
75478
+ }
75479
+ render(width) {
75480
+ const lines = [];
75481
+ const totalLines = this.maxVisibleLines + 5;
75482
+ const view = this.currentView;
75483
+ const depthSuffix = this.views.length > 1 ? theme.fg("dim", ` (${this.views.length} levels)`) : "";
75484
+ lines.push(createLine2(` ${theme.bold(view.title)}${depthSuffix}`, width, false));
75485
+ lines.push(createLine2(this.hintsLine(view), width, false));
75486
+ const searchPart = this.searchQuery ? `Search: ${this.searchQuery}` : "";
75487
+ const statusPart = this.statusMessage ? theme.fg("warning", this.statusMessage) : "";
75488
+ const statusLine = [searchPart, statusPart].filter(Boolean).join(" \xB7 ");
75489
+ lines.push(createLine2(statusLine, width, false));
75490
+ lines.push(createLine2("", width, false));
75491
+ if (view.kind === "list") {
75492
+ const rows = this.visibleListRows(view);
75493
+ if (rows.length === 0) {
75494
+ const notice = this.searchQuery || view.roleFilter !== "all" ? " (no matching rows)" : " (empty)";
75495
+ lines.push(createLine2(notice, width, false));
75496
+ for (let i = 1; i < this.maxVisibleLines; i++) {
75497
+ lines.push(createLine2("", width, false));
75498
+ }
75499
+ } else {
75500
+ this.selectedIndex = Math.max(0, Math.min(this.selectedIndex, rows.length - 1));
75501
+ let startIndex = Math.max(
75502
+ 0,
75503
+ Math.min(
75504
+ this.selectedIndex - Math.floor(this.maxVisibleLines / 2),
75505
+ rows.length - this.maxVisibleLines
75506
+ )
75507
+ );
75508
+ const endIndex = Math.min(startIndex + this.maxVisibleLines, rows.length);
75509
+ if (endIndex - startIndex < this.maxVisibleLines && startIndex > 0) {
75510
+ startIndex = Math.max(0, endIndex - this.maxVisibleLines);
75511
+ }
75512
+ for (let i = startIndex; i < endIndex; i++) {
75513
+ lines.push(this.renderRowLine(rows[i], width, i === this.selectedIndex));
75514
+ }
75515
+ for (let i = endIndex - startIndex; i < this.maxVisibleLines; i++) {
75516
+ lines.push(createLine2("", width, false));
75517
+ }
75518
+ }
75519
+ const filterSuffix = view.roleFilter !== "all" ? ` [${view.roleFilter}]` : "";
75520
+ const sortSuffix = view.rowKind === "messages" && view.sortBySize ? " [size]" : "";
75521
+ lines.push(
75522
+ createLine2(
75523
+ ` (${this.selectedIndex + 1}/${this.visibleListRows(view).length})${filterSuffix}${sortSuffix}`,
75524
+ width,
75525
+ false
75526
+ )
75527
+ );
75528
+ } else {
75529
+ const total = view.lines.length;
75530
+ const start = Math.min(view.scroll, Math.max(0, total - 1));
75531
+ const end = Math.min(start + this.maxVisibleLines, total);
75532
+ for (let i = start; i < end; i++) {
75533
+ lines.push(createLine2(` ${view.lines[i]}`, width, false));
75534
+ }
75535
+ for (let i = end - start; i < this.maxVisibleLines; i++) {
75536
+ lines.push(createLine2("", width, false));
75537
+ }
75538
+ lines.push(
75539
+ createLine2(` (lines ${total > 0 ? start + 1 : 0}-${end} of ${total})`, width, false)
75540
+ );
75541
+ }
75542
+ while (lines.length < totalLines) {
75543
+ lines.push(createLine2("", width, false));
75544
+ }
75545
+ return lines.slice(0, totalLines);
75546
+ }
75547
+ };
75548
+
75549
+ // src/commands/interactive-context-command.ts
75550
+ var MAX_ENTRY_BODY_CHARS = 1e4;
75551
+ var MAX_ARCHIVED_ROWS = 200;
75552
+ function fetchArchivedSummaries(sessionManager, firstKeptSeq) {
75553
+ const result = [];
75554
+ let afterSeq;
75555
+ while (result.length < MAX_ARCHIVED_ROWS) {
75556
+ const page = sessionManager.getEntrySummariesPage({ limit: 100, afterSeq });
75557
+ if (page.summaries.length === 0) break;
75558
+ let crossedCutPoint = false;
75559
+ for (const summary of page.summaries) {
75560
+ if (summary.seq >= firstKeptSeq) {
75561
+ crossedCutPoint = true;
75562
+ break;
75563
+ }
75564
+ if (result.length >= MAX_ARCHIVED_ROWS) break;
75565
+ result.push(summary);
75566
+ }
75567
+ if (crossedCutPoint || !page.hasMore) break;
75568
+ afterSeq = page.summaries[page.summaries.length - 1].seq;
75569
+ }
75570
+ return result;
75571
+ }
75572
+ function enhanceCheckpointRowWithArchive(report, host, entries) {
75573
+ const messagesSection = report.sections.find((section) => section.id === "messages");
75574
+ const checkpoint = [...entries].reverse().find((entry) => entry.type === "compaction");
75575
+ if (!messagesSection || !checkpoint) return;
75576
+ const compactionEntry = host.sessionManager.getEntry(checkpoint.id);
75577
+ const firstKeptEntryId = compactionEntry?.compactionData?.firstKeptEntryId;
75578
+ if (!firstKeptEntryId) return;
75579
+ const firstKeptSeq = host.sessionManager.getEntrySummary(firstKeptEntryId)?.seq;
75580
+ if (firstKeptSeq === void 0) return;
75581
+ const archived = fetchArchivedSummaries(host.sessionManager, firstKeptSeq);
75582
+ if (archived.length === 0) return;
75583
+ const row = messagesSection.rows.find((candidate) => candidate.id === `entry:${checkpoint.id}`);
75584
+ if (!row) return;
75585
+ const truncated = archived.length >= MAX_ARCHIVED_ROWS;
75586
+ row.drill = {
75587
+ kind: "rows",
75588
+ title: `Archived @ checkpoint #${checkpoint.seq}`,
75589
+ rowKind: "plain",
75590
+ rows: [
75591
+ {
75592
+ id: "checkpoint-summary",
75593
+ label: "checkpoint summary",
75594
+ estTokens: checkpoint.estTokens,
75595
+ drill: {
75596
+ kind: "text",
75597
+ title: "Checkpoint Summary",
75598
+ text: compactionEntry?.compactionData?.summary ?? ""
75599
+ }
75600
+ },
75601
+ ...archived.map((summary) => ({
75602
+ id: `archived:${summary.id}`,
75603
+ label: `#${summary.seq} ${summary.role ?? summary.type}`,
75604
+ estTokens: summary.estTokens,
75605
+ detail: summary.preview,
75606
+ drill: { kind: "entry", entryId: summary.id }
75607
+ })),
75608
+ ...truncated ? [{
75609
+ id: "archived:truncated",
75610
+ label: `\u2026 archived list capped at ${MAX_ARCHIVED_ROWS} entries (oldest first)`,
75611
+ estTokens: 0
75612
+ }] : []
75613
+ ]
75614
+ };
75615
+ }
75616
+ function formatEntryBodyText(entry) {
75617
+ if (!entry) return null;
75618
+ if (entry.type === "compaction") {
75619
+ const summary = entry.compactionData?.summary ?? "";
75620
+ const before = entry.compactionData?.tokensBefore;
75621
+ const header = before !== void 0 ? `# compaction checkpoint (context was ~${formatReportTokens(before)} before)
75622
+ ` : "# compaction checkpoint\n";
75623
+ return `${header}
75624
+ ${summary}`.slice(0, MAX_ENTRY_BODY_CHARS) || null;
75625
+ }
75626
+ const message = entry.message;
75627
+ if (!message) return null;
75628
+ if (message.role === "user") {
75629
+ const parts = ["# user message"];
75630
+ const content = message.content;
75631
+ if (typeof content === "string") {
75632
+ parts.push(content);
75633
+ } else if (Array.isArray(content)) {
75634
+ for (const block of content) {
75635
+ if (block && block.type === "text" && typeof block.text === "string") {
75636
+ parts.push(block.text);
75637
+ } else if (block && "type" in block) {
75638
+ parts.push(`[${block.type}]`);
75639
+ }
75640
+ }
75641
+ }
75642
+ return parts.join("\n").slice(0, MAX_ENTRY_BODY_CHARS) || null;
75643
+ }
75644
+ if (message.role === "assistant") {
75645
+ const parts = ["# assistant message"];
75646
+ const usage = message.usage;
75647
+ if (usage) {
75648
+ parts.push(
75649
+ `usage: input ${usage.input} \xB7 output ${usage.output} \xB7 cacheRead ${usage.cacheRead} \xB7 cacheWrite ${usage.cacheWrite}`
75650
+ );
75651
+ }
75652
+ if (Array.isArray(message.content)) {
75653
+ for (const block of message.content) {
75654
+ if (block.type === "text") {
75655
+ parts.push(block.text);
75656
+ } else if (block.type === "thinking") {
75657
+ parts.push("[thinking omitted from context on most providers]");
75658
+ } else if (block.type === "toolCall") {
75659
+ parts.push(`\u2192 ${block.name}(${JSON.stringify(block.arguments)})`);
75660
+ }
75661
+ }
75662
+ }
75663
+ return parts.join("\n").slice(0, MAX_ENTRY_BODY_CHARS) || null;
75664
+ }
75665
+ if (message.role === "toolResult") {
75666
+ const parts = [`# tool result (${message.toolCallId})`];
75667
+ if (Array.isArray(message.content)) {
75668
+ for (const block of message.content) {
75669
+ if (block.type === "text") {
75670
+ parts.push(block.text);
75671
+ } else if (block.type === "image") {
75672
+ parts.push("[image]");
75673
+ } else if (block.type === "audio") {
75674
+ parts.push("[audio]");
75675
+ } else if (block.type === "video") {
75676
+ parts.push("[video]");
75677
+ } else {
75678
+ parts.push(`[${block.type}]`);
75679
+ }
75680
+ }
75681
+ }
75682
+ return parts.join("\n").slice(0, MAX_ENTRY_BODY_CHARS) || null;
75683
+ }
75684
+ return JSON.stringify(message, null, 2).slice(0, MAX_ENTRY_BODY_CHARS) || null;
75685
+ }
75686
+ function handleContextReportCommand(host) {
75687
+ const systemPrompt = host.agent.state.systemPrompt ?? "";
75688
+ const knowledgeFiles = parseProjectContextSection(host.knowledgeSection);
75689
+ const entries = host.sessionManager.getActiveContextSummaries();
75690
+ const usage = host.calculateContextUsage();
75691
+ const contextWindow = host.options.model?.contextWindow ?? 0;
75692
+ const reserveTokens = host.options.settingsManager.getAutoCompactReserveTokens();
75693
+ const autoCompactLimit = contextWindow > 0 ? getAutoCompactTokenLimit(contextWindow, reserveTokens) ?? null : null;
75694
+ const prefixHash = computePromptPrefixHash(
75695
+ systemPrompt,
75696
+ host.agent.state.tools.map((tool) => ({
75697
+ name: tool.name,
75698
+ description: tool.description,
75699
+ parameters: tool.parameters
75700
+ }))
75701
+ );
75702
+ const lastSignals2 = getLastContextSignals(host);
75703
+ const promptPrefixChanged = lastSignals2 !== null && lastSignals2.prefixHash !== prefixHash;
75704
+ const report = buildContextReportData({
75705
+ systemPrompt,
75706
+ skillsSection: host.skillsSection,
75707
+ knowledgeFiles,
75708
+ skills: host.skills.map((skill) => ({
75709
+ name: skill.name,
75710
+ description: skill.description,
75711
+ filePath: skill.filePath,
75712
+ baseDir: skill.baseDir,
75713
+ source: skill.source
75714
+ })),
75715
+ tools: host.agent.state.tools.map((tool) => ({
75716
+ name: tool.name,
75717
+ label: tool.label,
75718
+ description: tool.description,
75719
+ parameters: tool.parameters
75720
+ })),
75721
+ entries,
75722
+ compactionTokensBefore: (entryId) => host.sessionManager.getEntry(entryId)?.compactionData?.tokensBefore,
75723
+ contextWindow,
75724
+ actualTokens: usage.tokens,
75725
+ autoCompactLimitTokens: autoCompactLimit,
75726
+ reserveTokens,
75727
+ promptPrefixHash: prefixHash,
75728
+ promptPrefixChanged
75729
+ });
75730
+ enhanceCheckpointRowWithArchive(report, host, entries);
75731
+ host.chatContainer.addChild(new Spacer(1));
75732
+ host.chatContainer.addChild(new Text(buildContextOverviewText(report, theme), 1, 0));
75733
+ host.ui.requestRender();
75734
+ host.showSelector((done) => {
75735
+ const selector = new ContextReportSelectorComponent({
75736
+ title: "Context Report",
75737
+ sections: report.sections,
75738
+ windowSection: {
75739
+ label: "window & thresholds",
75740
+ estTokens: report.contextWindow,
75741
+ valueText: report.contextWindow > 0 ? formatReportTokens(report.contextWindow) : "?",
75742
+ title: "Window & Thresholds",
75743
+ detail: "context window, estimate vs actual, auto-compact",
75744
+ rows: buildWindowRows(report)
75745
+ },
75746
+ terminalHeight: host.ui.terminal.rows,
75747
+ getEntryText: (entryId) => formatEntryBodyText(host.sessionManager.getEntry(entryId)),
75748
+ onDone: done
75749
+ });
75750
+ return { component: selector, focus: selector };
75751
+ });
75752
+ }
75753
+
75754
+ // src/core/trace-report.ts
75755
+ var MAX_DRILL_CHARS = 8e3;
75756
+ function formatTime(at) {
75757
+ return new Date(at).toLocaleTimeString([], { hour12: false });
75758
+ }
75759
+ function formatDurationMs(ms) {
75760
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
75761
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
75762
+ return `${Math.floor(ms / 6e4)}m${Math.round(ms % 6e4 / 1e3)}s`;
75763
+ }
75764
+ function detailsToText(event) {
75765
+ const header = [
75766
+ `# ${event.type}`,
75767
+ `at: ${new Date(event.at).toISOString()}`,
75768
+ event.summary ? `summary: ${event.summary}` : "",
75769
+ event.failure ? "failure: true" : ""
75770
+ ].filter(Boolean).join("\n");
75771
+ const details = event.details === void 0 ? "" : `
75772
+
75773
+ # details
75774
+ ${safeStringify(event.details).slice(0, MAX_DRILL_CHARS)}`;
75775
+ return `${header}${details}`;
75776
+ }
75777
+ function safeStringify(value) {
75778
+ try {
75779
+ return typeof value === "string" ? value : JSON.stringify(value, null, 2);
75780
+ } catch {
75781
+ return String(value);
75782
+ }
75783
+ }
75784
+ function buildTimelineRows(timeline) {
75785
+ if (timeline.length === 0) {
75786
+ return [{ id: "timeline:none", label: "no runtime events recorded yet", estTokens: 0 }];
75787
+ }
75788
+ return [...timeline].sort((a, b2) => b2.at - a.at).map((event, index) => ({
75789
+ id: `timeline:${event.at}:${index}`,
75790
+ label: `${formatTime(event.at)} ${event.type}`,
75791
+ estTokens: 0,
75792
+ detail: event.summary || void 0,
75793
+ marker: event.failure ? "\u2716" : void 0,
75794
+ drill: {
75795
+ kind: "text",
75796
+ title: `Event \xB7 ${event.type}`,
75797
+ text: detailsToText(event)
75798
+ }
75799
+ }));
75800
+ }
75801
+ function aggregateToolStats(turns) {
75802
+ const stats = /* @__PURE__ */ new Map();
75803
+ for (const turn of turns) {
75804
+ for (const call of turn.toolCalls) {
75805
+ const stat2 = stats.get(call.toolName) ?? {
75806
+ name: call.toolName,
75807
+ calls: 0,
75808
+ errors: 0,
75809
+ totalMs: 0
75810
+ };
75811
+ stat2.calls += 1;
75812
+ if (call.status === "failed") stat2.errors += 1;
75813
+ if (call.endedAt !== void 0 && call.endedAt >= call.startedAt) {
75814
+ stat2.totalMs += call.endedAt - call.startedAt;
75815
+ }
75816
+ stats.set(call.toolName, stat2);
75817
+ }
75818
+ }
75819
+ return [...stats.values()].sort((a, b2) => b2.totalMs - a.totalMs || b2.calls - a.calls);
75820
+ }
75821
+ function buildToolStatRows(turns) {
75822
+ const stats = aggregateToolStats(turns);
75823
+ if (stats.length === 0) {
75824
+ return [{ id: "tools:none", label: "no tool calls in recent turns", estTokens: 0 }];
75825
+ }
75826
+ return stats.map((stat2) => ({
75827
+ id: `toolstat:${stat2.name}`,
75828
+ label: stat2.name,
75829
+ estTokens: 0,
75830
+ valueText: formatDurationMs(stat2.totalMs),
75831
+ note: `${stat2.calls} calls${stat2.errors > 0 ? ` \xB7 ${stat2.errors} errors` : ""}`,
75832
+ drill: {
75833
+ kind: "rows",
75834
+ title: `Tool Calls \xB7 ${stat2.name}`,
75835
+ rowKind: "plain",
75836
+ rows: collectCalls(turns, stat2.name)
75837
+ }
75838
+ }));
75839
+ }
75840
+ function collectCalls(turns, toolName) {
75841
+ const rows = [];
75842
+ for (const turn of turns) {
75843
+ for (const call of turn.toolCalls) {
75844
+ if (call.toolName !== toolName) continue;
75845
+ const duration = call.endedAt !== void 0 && call.endedAt >= call.startedAt ? formatDurationMs(call.endedAt - call.startedAt) : "running";
75846
+ rows.push({
75847
+ id: `call:${call.toolCallId ?? `${call.startedAt}:${rows.length}`}`,
75848
+ label: `${formatTime(call.startedAt)} ${call.status}`,
75849
+ estTokens: 0,
75850
+ valueText: duration,
75851
+ detail: call.resultSummary || void 0,
75852
+ marker: call.status === "failed" ? "\u2716" : void 0,
75853
+ drill: {
75854
+ kind: "text",
75855
+ title: `Call \xB7 ${toolName}`,
75856
+ text: [
75857
+ `# ${toolName} (${call.status})`,
75858
+ `started: ${new Date(call.startedAt).toISOString()}`,
75859
+ call.endedAt !== void 0 ? `ended: ${new Date(call.endedAt).toISOString()}` : "",
75860
+ call.outcome !== void 0 ? `
75861
+ # outcome
75862
+ ${safeStringify(call.outcome)}` : ""
75863
+ ].filter(Boolean).join("\n").slice(0, MAX_DRILL_CHARS)
75864
+ }
75865
+ });
75866
+ }
75867
+ }
75868
+ return rows.sort((a, b2) => a.id.localeCompare(b2.id));
75869
+ }
75870
+ function buildTurnRows(turns) {
75871
+ if (turns.length === 0) {
75872
+ return [{ id: "turns:none", label: "no completed turns recorded yet", estTokens: 0 }];
75873
+ }
75874
+ return [...turns].sort((a, b2) => b2.turnIndex - a.turnIndex).map((turn) => {
75875
+ const duration = turn.completedAt !== void 0 && turn.startedAt !== void 0 && turn.completedAt >= turn.startedAt ? formatDurationMs(turn.completedAt - turn.startedAt) : "\u2014";
75876
+ return {
75877
+ id: `turn:${turn.turnIndex}`,
75878
+ label: `#${turn.turnIndex} ${turn.state}`,
75879
+ estTokens: 0,
75880
+ valueText: duration,
75881
+ note: `${turn.toolCalls.length} tool calls`,
75882
+ detail: turn.userText?.slice(0, 80) || void 0,
75883
+ marker: turn.state === "failed" ? "\u2716" : void 0,
75884
+ drill: {
75885
+ kind: "text",
75886
+ title: `Turn #${turn.turnIndex}`,
75887
+ text: turnToText(turn)
75888
+ }
75889
+ };
75890
+ });
75891
+ }
75892
+ function turnToText(turn) {
75893
+ const parts = [
75894
+ `# turn ${turn.turnIndex} (${turn.state})`,
75895
+ turn.startedAt !== void 0 ? `started: ${new Date(turn.startedAt).toISOString()}` : "",
75896
+ turn.completedAt !== void 0 ? `completed: ${new Date(turn.completedAt).toISOString()}` : ""
75897
+ ];
75898
+ if (turn.userText) parts.push(`
75899
+ # user
75900
+ ${turn.userText.slice(0, 2e3)}`);
75901
+ if (turn.assistantText) parts.push(`
75902
+ # assistant
75903
+ ${turn.assistantText.slice(0, 2e3)}`);
75904
+ if (turn.errorMessage) parts.push(`
75905
+ # error
75906
+ ${turn.errorMessage}`);
75907
+ if (turn.toolCalls.length > 0) {
75908
+ parts.push(
75909
+ `
75910
+ # tool calls
75911
+ ${turn.toolCalls.map(
75912
+ (call) => `- ${call.toolName} (${call.status})${call.endedAt !== void 0 && call.endedAt >= call.startedAt ? ` ${formatDurationMs(call.endedAt - call.startedAt)}` : ""}${call.resultSummary ? ` \u2014 ${call.resultSummary.slice(0, 200)}` : ""}`
75913
+ ).join("\n")}`
75914
+ );
75915
+ }
75916
+ return parts.filter(Boolean).join("\n").slice(0, MAX_DRILL_CHARS);
75917
+ }
75918
+ function buildRuntimeSummaryRows(snapshot) {
75919
+ const summary = snapshot.runtimeSummary;
75920
+ if (!summary) {
75921
+ return [{ id: "runtime:none", label: "no runtime summary recorded yet", estTokens: 0 }];
75922
+ }
75923
+ const entries = [
75924
+ ["tool results", summary.toolResults, "completed tool calls (recent turns)"],
75925
+ ["tool errors", summary.toolErrors, "failed tool calls (recent turns)"],
75926
+ ["retries", summary.retryEvents, "provider/overflow retries"],
75927
+ ["auto-compactions", summary.autoCompactionEvents, "context checkpoints this session"],
75928
+ ["runtime guard events", summary.runtimeGuardEvents, "guard advisories triggered"],
75929
+ ["queued turn events", summary.queuedTurnEvents, "steer/followUp queue dispatches"],
75930
+ ["subagent events", summary.subagentEvents, "subagent lifecycle events"]
75931
+ ];
75932
+ return entries.map(([label, count, detail]) => ({
75933
+ id: `runtime:${label}`,
75934
+ label,
75935
+ estTokens: count,
75936
+ valueText: String(count),
75937
+ note: count > 0 ? void 0 : "none",
75938
+ detail
75939
+ }));
75940
+ }
75941
+ function buildSubagentRows(snapshot) {
75942
+ const summaries = snapshot.subagentSummaries ?? [];
75943
+ if (summaries.length === 0) {
75944
+ return [{ id: "subagents:none", label: "no subagent tasks recorded yet", estTokens: 0 }];
75945
+ }
75946
+ return summaries.map((sub, index) => ({
75947
+ id: `subagent:${sub.toolCallId ?? index}`,
75948
+ label: sub.name,
75949
+ estTokens: 0,
75950
+ valueText: sub.status,
75951
+ note: sub.modelId ? `${sub.modelClass ? `${sub.modelClass}/` : ""}${sub.modelId}` : void 0,
75952
+ detail: [
75953
+ sub.evidenceCount > 0 ? `${sub.evidenceCount} evidence` : "",
75954
+ sub.failureReason ? `failed: ${sub.failureReason}` : "",
75955
+ sub.modelFallbackReason ? `fallback: ${sub.modelFallbackReason}` : ""
75956
+ ].filter(Boolean).join(" \xB7 ") || void 0,
75957
+ marker: sub.status === "failed" || sub.failureReason ? "\u2716" : void 0,
75958
+ drill: {
75959
+ kind: "text",
75960
+ title: `Subagent \xB7 ${sub.name}`,
75961
+ text: safeStringify(sub).slice(0, MAX_DRILL_CHARS)
75962
+ }
75963
+ }));
75964
+ }
75965
+ function buildTraceSections(snapshot) {
75966
+ const turns = [...snapshot.recentTurns];
75967
+ if (snapshot.activeTurn) turns.push(snapshot.activeTurn);
75968
+ const timeline = snapshot.recentTimeline ?? [];
75969
+ return [
75970
+ {
75971
+ id: "trace-timeline",
75972
+ title: `Timeline (${timeline.length})`,
75973
+ estTokens: 0,
75974
+ rows: buildTimelineRows(timeline)
75975
+ },
75976
+ {
75977
+ id: "trace-tools",
75978
+ title: "Tool stats (recent turns)",
75979
+ estTokens: 0,
75980
+ note: aggregateToolStats(turns)[0] ? `slowest: ${aggregateToolStats(turns)[0].name} ${formatDurationMs(aggregateToolStats(turns)[0].totalMs)}` : void 0,
75981
+ rows: buildToolStatRows(turns)
75982
+ },
75983
+ {
75984
+ id: "trace-turns",
75985
+ title: `Recent turns (${snapshot.recentTurns.length}${snapshot.activeTurn ? " +1 active" : ""})`,
75986
+ estTokens: 0,
75987
+ rows: buildTurnRows(
75988
+ snapshot.activeTurn ? [...snapshot.recentTurns, snapshot.activeTurn] : snapshot.recentTurns
75989
+ )
75990
+ },
75991
+ {
75992
+ id: "trace-runtime",
75993
+ title: "Runtime summary",
75994
+ estTokens: 0,
75995
+ rows: buildRuntimeSummaryRows(snapshot)
75996
+ },
75997
+ {
75998
+ id: "trace-subagents",
75999
+ title: `Subagents (${(snapshot.subagentSummaries ?? []).length})`,
76000
+ estTokens: 0,
76001
+ rows: buildSubagentRows(snapshot)
76002
+ }
76003
+ ];
76004
+ }
76005
+ function buildTraceOverviewText(snapshot, theme2) {
76006
+ const t = theme2 ?? { bold: (s) => s, fg: (_k, s) => s };
76007
+ const lines = [];
76008
+ const turns = snapshot.recentTurns.length + (snapshot.activeTurn ? 1 : 0);
76009
+ const timeline = snapshot.recentTimeline ?? [];
76010
+ lines.push(t.bold("Session Trace") + t.fg("dim", ` \xB7 ${turns} recent turns \xB7 ${timeline.length} events`));
76011
+ const summary = snapshot.runtimeSummary;
76012
+ if (summary) {
76013
+ const notable = [];
76014
+ if (summary.retryEvents > 0) notable.push(`retries ${summary.retryEvents}`);
76015
+ if (summary.autoCompactionEvents > 0) notable.push(`compactions ${summary.autoCompactionEvents}`);
76016
+ if (summary.runtimeGuardEvents > 0) notable.push(`guard ${summary.runtimeGuardEvents}`);
76017
+ if (summary.toolErrors > 0) notable.push(`tool errors ${summary.toolErrors}`);
76018
+ if (notable.length > 0) {
76019
+ lines.push(t.fg("dim", ` events: ${notable.join(" \xB7 ")}`));
76020
+ } else {
76021
+ lines.push(t.fg("dim", " no retries, compactions, or guard events"));
76022
+ }
76023
+ }
76024
+ const stats = aggregateToolStats([...snapshot.recentTurns, ...snapshot.activeTurn ? [snapshot.activeTurn] : []]);
76025
+ if (stats.length > 0) {
76026
+ const top = stats.slice(0, 3).map((s) => `${s.name} ${formatDurationMs(s.totalMs)} (${s.calls})`);
76027
+ lines.push(t.fg("dim", ` tools: ${top.join(" \xB7 ")}`));
76028
+ }
76029
+ lines.push(t.fg("dim", " /debugs/trace \xB7 Enter to drill, Esc to go back"));
76030
+ return lines.join("\n");
76031
+ }
76032
+
76033
+ // src/commands/interactive-trace-command.ts
76034
+ function handleTraceCommand(host, snapshot) {
76035
+ if (!snapshot) {
76036
+ host.showStatus("No session trace yet \u2014 it starts with the first turn");
76037
+ return;
76038
+ }
76039
+ const sections = buildTraceSections(snapshot);
76040
+ host.chatContainer.addChild(new Spacer(1));
76041
+ host.chatContainer.addChild(new Text(buildTraceOverviewText(snapshot, theme), 1, 0));
76042
+ host.ui.requestRender();
76043
+ host.showSelector((done) => {
76044
+ const selector = new ContextReportSelectorComponent({
76045
+ title: "Session Trace",
76046
+ sections,
76047
+ terminalHeight: host.ui.terminal.rows,
76048
+ onDone: done
76049
+ });
76050
+ return { component: selector, focus: selector };
76051
+ });
76052
+ }
76053
+
76054
+ // src/core/boot-report.ts
76055
+ function stepToText(step) {
76056
+ const lines = [
76057
+ `# ${step.id}`,
76058
+ `input: ${step.inputSummary}`,
76059
+ `output: ${step.outputSummary}`
76060
+ ];
76061
+ if (step.toolCount !== void 0) lines.push(`tools: ${step.toolCount}`);
76062
+ if (step.policyDecisions?.length) {
76063
+ lines.push("", "# policy decisions", ...step.policyDecisions.map((decision) => `- ${decision}`));
76064
+ }
76065
+ if (step.extensionWrapping) {
76066
+ lines.push(
76067
+ "",
76068
+ "# extension wrapping",
76069
+ `extensions: ${step.extensionWrapping.extensionCount}`,
76070
+ `extension tools: ${step.extensionWrapping.extensionToolCount}`,
76071
+ `wrapped tools: ${step.extensionWrapping.wrappedToolCount}`
76072
+ );
76073
+ }
76074
+ return lines.join("\n");
76075
+ }
76076
+ function buildBootRows(trace) {
76077
+ if (trace.steps.length === 0) {
76078
+ return [{ id: "boot:none", label: "no composition steps recorded", estTokens: 0 }];
76079
+ }
76080
+ return trace.steps.map((step, index) => ({
76081
+ id: `boot:${step.id}`,
76082
+ label: `${index + 1}. ${step.id}`,
76083
+ estTokens: 0,
76084
+ note: step.toolCount !== void 0 ? `${step.toolCount} tools` : void 0,
76085
+ detail: step.outputSummary,
76086
+ drill: {
76087
+ kind: "text",
76088
+ title: `Step \xB7 ${step.id}`,
76089
+ text: stepToText(step)
76090
+ }
76091
+ }));
76092
+ }
76093
+ function buildBootSections(trace) {
76094
+ return [
76095
+ {
76096
+ id: "boot-pipeline",
76097
+ title: `Composition pipeline (${trace.steps.length} steps)`,
76098
+ estTokens: 0,
76099
+ rows: buildBootRows(trace)
76100
+ }
76101
+ ];
76102
+ }
76103
+ function buildBootOverviewText(trace, theme2) {
76104
+ const t = theme2 ?? { bold: (s) => s, fg: (_k, s) => s };
76105
+ const lines = [];
76106
+ lines.push(t.bold("Boot Composition") + t.fg("dim", ` \xB7 ${trace.steps.length} steps`));
76107
+ for (const step of trace.steps) {
76108
+ lines.push(t.fg("dim", ` ${step.id} \u2014 ${step.outputSummary}`));
76109
+ }
76110
+ lines.push(t.fg("dim", " /debugs/boot \xB7 Enter to drill into a step"));
76111
+ return lines.join("\n");
76112
+ }
76113
+
76114
+ // src/commands/interactive-boot-command.ts
76115
+ function handleBootCommand(host) {
76116
+ const trace = host.options.compositionTrace;
76117
+ if (!trace || trace.steps.length === 0) {
76118
+ host.showStatus("No composition trace available for this runtime");
76119
+ return;
76120
+ }
76121
+ const sections = buildBootSections(trace);
76122
+ host.chatContainer.addChild(new Spacer(1));
76123
+ host.chatContainer.addChild(new Text(buildBootOverviewText(trace, theme), 1, 0));
76124
+ host.ui.requestRender();
76125
+ host.showSelector((done) => {
76126
+ const selector = new ContextReportSelectorComponent({
76127
+ title: "Boot Composition",
76128
+ sections,
76129
+ terminalHeight: host.ui.terminal.rows,
76130
+ onDone: done
76131
+ });
76132
+ return { component: selector, focus: selector };
76133
+ });
76134
+ }
76135
+
74656
76136
  // src/commands/interactive-command-handlers.ts
74657
76137
  function showSettingsSelector(host, focusItem) {
74658
76138
  host.showSelector((done) => {
@@ -75077,6 +76557,7 @@ async function handleClearCommand(host) {
75077
76557
  contextPercent: contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0,
75078
76558
  contextWindow
75079
76559
  });
76560
+ applyContextSignalsWithTrace(host);
75080
76561
  host.updateTerminalTitle();
75081
76562
  host.chatContainer.clear();
75082
76563
  host.streamingComponent = void 0;
@@ -75123,6 +76604,7 @@ function handleSessionCommand(host) {
75123
76604
  homeDir: process.env.HOME || process.env.USERPROFILE || "",
75124
76605
  modelId: host.options.model?.id,
75125
76606
  thinkingLevel: host.thinkingLevel,
76607
+ messages: host.sessionManager.getMessages(),
75126
76608
  theme
75127
76609
  });
75128
76610
  host.chatContainer.addChild(new Spacer(1));
@@ -75164,7 +76646,10 @@ function createExtensionContextForCommands(host) {
75164
76646
  console.error(`[Extension] Tools restricted to: ${tools.join(", ")}`);
75165
76647
  }
75166
76648
  },
75167
- getActiveTools: () => host.agent.state.tools.map((tool) => tool.name),
76649
+ getActiveTools: () => {
76650
+ const tools = host.agent.state.tools;
76651
+ return (host.planModeEnabled ? tools.filter((tool) => isToolAllowedInPlanMode(tool)) : tools).map((tool) => tool.name);
76652
+ },
75168
76653
  sendUserMessage: (content) => {
75169
76654
  if (host.editor.onSubmit) {
75170
76655
  void host.editor.onSubmit(content);
@@ -75356,12 +76841,16 @@ function registerBuiltinCommands(host) {
75356
76841
  handler: () => host.handleCacheTest()
75357
76842
  });
75358
76843
  commandTree.register(["debugs", "context"], {
75359
- description: "Append a context summary to ~/.pie/logs/pie-cli.log",
75360
- handler: () => host.handleDebugContextCommand()
76844
+ description: "Context composition report: sections, tokens, drill-down",
76845
+ handler: () => host.handleDebugReportCommand("context")
76846
+ });
76847
+ commandTree.register(["debugs", "trace"], {
76848
+ description: "Session trace: timeline, tool stats, runtime events",
76849
+ handler: () => host.handleDebugReportCommand("trace")
75361
76850
  });
75362
- commandTree.register(["debugs", "package"], {
75363
- description: "Append a package summary to ~/.pie/logs/pie-cli.log",
75364
- handler: () => host.handleDebugPackageCommand()
76851
+ commandTree.register(["debugs", "boot"], {
76852
+ description: "Boot composition pipeline: how the runtime was assembled",
76853
+ handler: () => host.handleDebugReportCommand("boot")
75365
76854
  });
75366
76855
  commandTree.register(["settings", "quit"], {
75367
76856
  description: "Exit Pie",
@@ -75372,6 +76861,36 @@ function registerBuiltinCommands(host) {
75372
76861
  });
75373
76862
  }
75374
76863
 
76864
+ // src/runtime/compaction-diff.ts
76865
+ function summarizeActiveContext(summaries) {
76866
+ let tokens = 0;
76867
+ for (const summary of summaries) {
76868
+ tokens += summary.estTokens;
76869
+ }
76870
+ return { count: summaries.length, tokens };
76871
+ }
76872
+ function formatCompactionTokenLine(stats) {
76873
+ const saved = Math.max(0, stats.before.tokens - stats.after.tokens);
76874
+ const percent = stats.before.tokens > 0 ? Math.round(saved / stats.before.tokens * 100) : 0;
76875
+ return `~${formatReportTokens(stats.before.tokens)} \u2192 ~${formatReportTokens(stats.after.tokens)} tokens (saved ~${formatReportTokens(saved)}, \u2212${percent}%)`;
76876
+ }
76877
+ function renderCompactionDiffPanel(target, stats) {
76878
+ const archived = Math.max(0, stats.before.count - stats.after.count);
76879
+ target.chatContainer.addChild(new Spacer(1));
76880
+ target.chatContainer.addChild(new Text(theme.fg("accent", "\u2691 Context checkpoint"), 1, 0));
76881
+ target.chatContainer.addChild(
76882
+ new Text(
76883
+ theme.fg(
76884
+ "dim",
76885
+ ` kept ${stats.after.count} entries \xB7 archived ${archived} entries \xB7 ${formatCompactionTokenLine(stats)}`
76886
+ ),
76887
+ 1,
76888
+ 0
76889
+ )
76890
+ );
76891
+ target.safeRequestRender();
76892
+ }
76893
+
75375
76894
  // src/commands/interactive-compact-command.ts
75376
76895
  async function handleCompactCommand(host) {
75377
76896
  if (host.submitLock || host.isProcessing || host.isCompacting) {
@@ -75390,6 +76909,7 @@ async function handleCompactCommand(host) {
75390
76909
  host.chatContainer.addChild(new Spacer(1));
75391
76910
  host.chatContainer.addChild(new Text(theme.fg("dim", "[/sessions/compact]"), 1, 0));
75392
76911
  host.safeRequestRender();
76912
+ const beforeStats = summarizeActiveContext(host.sessionManager.getActiveContextSummaries());
75393
76913
  try {
75394
76914
  const activeEntryId = host.sessionManager.getActiveEntryId();
75395
76915
  if (!activeEntryId) {
@@ -75426,14 +76946,17 @@ async function handleCompactCommand(host) {
75426
76946
  const compactedMessages = host.sessionManager.getMessages();
75427
76947
  host.replaceAgentMessagesAndMarkSynced(compactedMessages);
75428
76948
  host.resetFileObservationState();
76949
+ host.resetRuntimeModeBlocks?.();
75429
76950
  host.streamingComponent = void 0;
75430
76951
  host.pendingTools.clear();
75431
76952
  host.chatContainer.clear();
75432
76953
  host.lastStatusSpacer = void 0;
75433
76954
  host.lastStatusText = void 0;
75434
76955
  host.renderMessageHistory(compactedMessages);
75435
- host.chatContainer.addChild(new Spacer(1));
75436
- host.chatContainer.addChild(new Text(theme.fg("accent", "Context compacted"), 1, 0));
76956
+ renderCompactionDiffPanel(host, {
76957
+ before: beforeStats,
76958
+ after: summarizeActiveContext(host.sessionManager.getActiveContextSummaries())
76959
+ });
75437
76960
  const contextUsage = host.calculateContextUsage();
75438
76961
  const contextWindow = host.options.model?.contextWindow ?? 0;
75439
76962
  const contextPercent = contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0;
@@ -75448,6 +76971,7 @@ async function handleCompactCommand(host) {
75448
76971
  contextPercent,
75449
76972
  contextWindow
75450
76973
  });
76974
+ applyContextSignalsWithTrace(host);
75451
76975
  await host.sessionManager.save();
75452
76976
  host.safeRequestRender();
75453
76977
  } catch (error) {
@@ -77566,8 +79090,12 @@ function queueMessage(host, text, mode, options = {}) {
77566
79090
  }
77567
79091
  const clearEditor = options.clearEditor ?? true;
77568
79092
  host.sessionTrace.notePendingUserText(text);
79093
+ const runtimeBlock = host.runtimeModeBlockForNextMessage?.();
79094
+ const content = runtimeBlock ? `${runtimeBlock}
79095
+
79096
+ ${text}` : text;
77569
79097
  void host.agentSessionController.prompt(
77570
- { role: "user", content: text, timestamp: Date.now() },
79098
+ { role: "user", content, timestamp: Date.now() },
77571
79099
  {
77572
79100
  source: "interactive",
77573
79101
  streamingBehavior: mode,
@@ -77824,6 +79352,14 @@ var FooterComponent = class {
77824
79352
  if (state.cacheRead) statsParts.push(`R${formatTokens(state.cacheRead)}`);
77825
79353
  if (state.cacheWrite) statsParts.push(`W${formatTokens(state.cacheWrite)}`);
77826
79354
  if (state.cost) statsParts.push(`$${state.cost.toFixed(3)}`);
79355
+ if (state.cacheHitRate != null) {
79356
+ statsParts.push(theme.fg("dim", `ca ${(state.cacheHitRate * 100).toFixed(0)}%`));
79357
+ }
79358
+ if (state.prefixHash) {
79359
+ statsParts.push(
79360
+ state.prefixHashChanged ? theme.fg("warning", `\u26A1${state.prefixHash}`) : theme.fg("dim", `\u2301${state.prefixHash}`)
79361
+ );
79362
+ }
77827
79363
  if (state.contextWindow) {
77828
79364
  const autoIndicator = this.autoCompactEnabled ? " (auto)" : "";
77829
79365
  const percent = state.contextPercent ?? 0;
@@ -77896,27 +79432,13 @@ var FooterComponent = class {
77896
79432
  // src/components/todo-widget.ts
77897
79433
  var TodoWidgetComponent = class extends Container {
77898
79434
  state;
77899
- calmMode = false;
77900
79435
  constructor(state) {
77901
79436
  super();
77902
79437
  this.state = state;
77903
79438
  void this.refresh();
77904
79439
  }
77905
- /** Calm mode hides the progress widget (questions/answers only). */
77906
- setCalmMode(calm) {
77907
- if (this.calmMode === calm) return;
77908
- this.calmMode = calm;
77909
- if (calm) {
77910
- this.clear();
77911
- } else {
77912
- void this.refresh();
77913
- }
77914
- }
77915
79440
  async refresh() {
77916
79441
  this.clear();
77917
- if (this.calmMode) {
77918
- return;
77919
- }
77920
79442
  const lines = renderExecutionWidgetLines(this.state.getWidgetSteps());
77921
79443
  if (lines.length === 0) {
77922
79444
  return;
@@ -78907,6 +80429,7 @@ function createInteractiveAgentSessionController(host) {
78907
80429
  }
78908
80430
  let retryElapsedTicker;
78909
80431
  let retryElapsedStartedAt = 0;
80432
+ let autoCompactBeforeStats = null;
78910
80433
  const attention = cliAttention(host);
78911
80434
  const stopRetryElapsedTicker = () => {
78912
80435
  if (retryElapsedTicker !== void 0) {
@@ -78972,6 +80495,7 @@ function createInteractiveAgentSessionController(host) {
78972
80495
  if (!firstKeptEntryId) return { aborted: true };
78973
80496
  return { summary: beforeCompact.summary, firstKeptEntryId };
78974
80497
  }
80498
+ autoCompactBeforeStats = summarizeActiveContext(context.sessionManager.getActiveContextSummaries());
78975
80499
  return createCliAutoCompactionResult({
78976
80500
  context,
78977
80501
  model: host.options.model,
@@ -79140,9 +80664,11 @@ function createInteractiveAgentSessionController(host) {
79140
80664
  host.showError(`${prefix}: ${event.errorMessage}`);
79141
80665
  host.footer.setState({ status: "ready" });
79142
80666
  host.setControllerBusy(false);
80667
+ autoCompactBeforeStats = null;
79143
80668
  return;
79144
80669
  }
79145
80670
  host.resetFileObservationState();
80671
+ host.resetRuntimeModeBlocks?.();
79146
80672
  if (!event.result?.aborted) {
79147
80673
  host.streamingComponent = void 0;
79148
80674
  host.pendingTools.clear();
@@ -79165,10 +80691,20 @@ function createInteractiveAgentSessionController(host) {
79165
80691
  contextPercent,
79166
80692
  contextWindow
79167
80693
  });
80694
+ applyContextSignalsWithTrace(host);
79168
80695
  host.safeRequestRender();
79169
- if (!event.willRetry && !event.result?.aborted) {
79170
- host.showStatus("Context auto-compacted");
80696
+ if (!event.result?.aborted) {
80697
+ if (autoCompactBeforeStats !== null) {
80698
+ renderCompactionDiffPanel(host, {
80699
+ before: autoCompactBeforeStats,
80700
+ after: summarizeActiveContext(host.sessionManager.getActiveContextSummaries())
80701
+ });
80702
+ }
80703
+ if (!event.willRetry) {
80704
+ host.showStatus("Context auto-compacted");
80705
+ }
79171
80706
  }
80707
+ autoCompactBeforeStats = null;
79172
80708
  } else if (event.type === "auto_continue_scheduled") {
79173
80709
  host.setControllerBusy(true);
79174
80710
  if (event.reason === "todo_closure") {
@@ -79184,94 +80720,80 @@ function createInteractiveAgentSessionController(host) {
79184
80720
  }
79185
80721
 
79186
80722
  // src/runtime/runtime-mode-context.ts
79187
- var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
79188
- var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
79189
- var STALE_SYSTEM_REMINDER_RE = /<system-reminder>\s*# Plan Mode[\s\S]*?<\/system-reminder>\s*/gi;
79190
- var STALE_RUNTIME_CONTEXT_RE = /<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>\s*/gi;
79191
- function isRecord3(value) {
79192
- return !!value && typeof value === "object" && !Array.isArray(value);
80723
+ function isAgentMessage(value) {
80724
+ return !!value && typeof value === "object" && "role" in value;
79193
80725
  }
79194
- function cleanText(text) {
79195
- let stripped = 0;
79196
- const withoutRuntime = text.replace(STALE_RUNTIME_CONTEXT_RE, () => {
79197
- stripped++;
79198
- return "";
79199
- });
79200
- const withoutPlanReminder = withoutRuntime.replace(STALE_SYSTEM_REMINDER_RE, () => {
79201
- stripped++;
79202
- return "";
79203
- });
79204
- return { text: withoutPlanReminder, stripped };
79205
- }
79206
- function stripUserMessage(message) {
79207
- if (!isRecord3(message) || message.role !== "user") {
79208
- return { message, stripped: 0 };
80726
+ function prependRuntimeModeBlockToPrompt(input, block) {
80727
+ if (!block) return input;
80728
+ if (typeof input === "string") {
80729
+ return input.trim() ? `${block}
80730
+
80731
+ ${input}` : block;
79209
80732
  }
79210
- const content = message.content;
79211
- if (typeof content === "string") {
79212
- const cleaned = cleanText(content);
79213
- if (cleaned.stripped === 0) return { message, stripped: 0 };
79214
- return {
79215
- message: {
79216
- ...message,
79217
- content: cleaned.text
79218
- },
79219
- stripped: cleaned.stripped
79220
- };
80733
+ if (Array.isArray(input)) {
80734
+ if (input.length > 0 && isAgentMessage(input[0])) {
80735
+ return prependBlockToMessages(input, block);
80736
+ }
80737
+ return [{ type: "text", text: block }, ...input];
79221
80738
  }
79222
- if (!Array.isArray(content)) {
79223
- return { message, stripped: 0 };
79224
- }
79225
- let stripped = 0;
79226
- const nextContent = content.map((block) => {
79227
- if (!isRecord3(block) || block.type !== "text" || typeof block.text !== "string") {
79228
- return block;
79229
- }
79230
- const cleaned = cleanText(block.text);
79231
- stripped += cleaned.stripped;
79232
- return { ...block, text: cleaned.text };
79233
- }).filter((block) => !(isRecord3(block) && block.type === "text" && String(block.text ?? "").length === 0));
79234
- if (stripped === 0) return { message, stripped: 0 };
79235
- return {
79236
- message: {
79237
- ...message,
79238
- content: nextContent
79239
- },
79240
- stripped
79241
- };
80739
+ if (isAgentMessage(input)) {
80740
+ return prependBlockToMessages([input], block)[0];
80741
+ }
80742
+ return [{ type: "text", text: block }, ...input];
79242
80743
  }
79243
- function stripRuntimeModeContext(messages) {
79244
- let stripped = 0;
79245
- const nextMessages = [];
79246
- for (const message of messages) {
79247
- const result = stripUserMessage(message);
79248
- stripped += result.stripped;
79249
- if (isRecord3(result.message) && result.message.role === "user") {
79250
- const content = result.message.content;
79251
- if (typeof content === "string" && content.trim().length === 0) {
79252
- continue;
79253
- }
79254
- if (Array.isArray(content) && content.length === 0) {
79255
- continue;
79256
- }
80744
+ function prependBlockToMessages(messages, block) {
80745
+ const next = [...messages];
80746
+ let target = -1;
80747
+ for (let index = next.length - 1; index >= 0; index--) {
80748
+ if (next[index]?.role === "user") {
80749
+ target = index;
80750
+ break;
79257
80751
  }
79258
- nextMessages.push(result.message);
79259
80752
  }
79260
- return { messages: nextMessages, stripped };
80753
+ if (target < 0) {
80754
+ next.push({ role: "user", content: block, timestamp: Date.now() });
80755
+ return next;
80756
+ }
80757
+ const message = next[target];
80758
+ if (typeof message.content === "string") {
80759
+ next[target] = { ...message, content: `${block}
80760
+
80761
+ ${message.content}` };
80762
+ } else if (Array.isArray(message.content)) {
80763
+ next[target] = { ...message, content: [{ type: "text", text: block }, ...message.content] };
80764
+ } else {
80765
+ next.splice(target, 0, { role: "user", content: block, timestamp: Date.now() });
80766
+ }
80767
+ return next;
79261
80768
  }
80769
+ var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
80770
+ var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
79262
80771
  function buildRuntimeModeContext(state) {
79263
80772
  const lines = [
79264
80773
  RUNTIME_MODE_CONTEXT_MARKER,
79265
- "Current runtime mode:",
79266
- `- Plan mode: ${state.planMode === "planning" ? "active (workspace read-only; plan_scratch permits session-local temporary drafts)" : "off"}.`,
80774
+ "Current runtime mode:"
80775
+ ];
80776
+ if (state.planMode === "planning") {
80777
+ lines.push(
80778
+ "- 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)."
80779
+ );
80780
+ if (state.planDisabledToolNames.length > 0) {
80781
+ lines.push(
80782
+ `- Disabled this turn by plan mode (calls are denied): ${state.planDisabledToolNames.join(", ")}. Do not call them while planning.`
80783
+ );
80784
+ }
80785
+ } else {
80786
+ lines.push("- Plan mode: off.");
80787
+ }
80788
+ lines.push(
79267
80789
  `- YOLO mode: ${state.yoloMode ? "on (filesystem sandbox directory restrictions are disabled)" : "off (workspace/protected-path restrictions apply)"}.`,
79268
80790
  `- Filesystem scope: ${state.filesystemScope === "all" ? "all local paths may be passed directly to file tools when permitted" : "workspace paths and configured safe roots only"}.`
79269
- ];
80791
+ );
79270
80792
  if (state.pendingTransition) {
79271
80793
  const transitionText = {
79272
80794
  entered_yolo: "YOLO mode was just enabled for this turn.",
79273
80795
  exited_yolo: "YOLO mode was just disabled; do not assume previous absolute-path access still applies.",
79274
- entered_plan: "Plan mode was just enabled; analyze and plan without changing the workspace. Use plan_scratch for temporary drafts.",
80796
+ 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.",
79275
80797
  exited_plan: "Plan mode was just disabled; execution tools may be used when the task requires them."
79276
80798
  };
79277
80799
  lines.push(`- Transition: ${transitionText[state.pendingTransition]}`);
@@ -79285,46 +80807,108 @@ function buildRuntimeModeContext(state) {
79285
80807
  lines.push(RUNTIME_MODE_CONTEXT_END_MARKER);
79286
80808
  return lines.join("\n");
79287
80809
  }
79288
- function findLatestUserIndex(messages) {
79289
- for (let index = messages.length - 1; index >= 0; index--) {
79290
- const message = messages[index];
79291
- if (isRecord3(message) && message.role === "user") {
79292
- return index;
79293
- }
79294
- }
79295
- return -1;
80810
+ function runtimeModeBlockKey(state) {
80811
+ return JSON.stringify({
80812
+ planMode: state.planMode,
80813
+ yoloMode: state.yoloMode,
80814
+ filesystemScope: state.filesystemScope,
80815
+ currentDate: state.currentDate ?? null,
80816
+ activeToolNames: state.activeToolNames,
80817
+ planDisabledToolNames: state.planDisabledToolNames
80818
+ });
79296
80819
  }
79297
- function injectRuntimeModeContext(messages, state) {
79298
- const contextMessage = {
79299
- role: "user",
79300
- content: buildRuntimeModeContext(state),
79301
- timestamp: Date.now()
79302
- };
79303
- const latestUserIndex = findLatestUserIndex(messages);
79304
- if (latestUserIndex < 0) {
79305
- return [...messages, contextMessage];
79306
- }
79307
- return [
79308
- ...messages.slice(0, latestUserIndex),
79309
- contextMessage,
79310
- ...messages.slice(latestUserIndex)
79311
- ];
80820
+ function shouldInjectRuntimeModeBlock(lastKey, state) {
80821
+ if (lastKey === null) return true;
80822
+ return runtimeModeBlockKey(state) !== lastKey;
79312
80823
  }
79313
- function createRuntimeModeTransformContext(options) {
79314
- return async (messages) => {
79315
- const stripped = stripRuntimeModeContext(messages);
79316
- if (stripped.stripped > 0) {
79317
- options.onStripped?.(stripped.stripped);
79318
- }
79319
- const state = options.getState();
79320
- const nextMessages = injectRuntimeModeContext(stripped.messages, state);
79321
- options.onInjected?.(state);
79322
- if (state.pendingTransition) {
79323
- options.consumeTransition?.();
79324
- }
79325
- return nextMessages;
80824
+ function runtimeModeTraceData(state) {
80825
+ return {
80826
+ yoloMode: state.yoloMode,
80827
+ planMode: state.planMode,
80828
+ filesystemScope: state.filesystemScope,
80829
+ pendingTransition: state.pendingTransition,
80830
+ currentDate: state.currentDate,
80831
+ timeZone: state.timeZone,
80832
+ activeToolCount: state.activeToolNames.length,
80833
+ planDisabledToolCount: state.planDisabledToolNames.length
79326
80834
  };
79327
80835
  }
80836
+ function currentDateInZone(timeZone) {
80837
+ const parts = new Intl.DateTimeFormat("en-US", {
80838
+ year: "numeric",
80839
+ month: "2-digit",
80840
+ day: "2-digit",
80841
+ timeZone
80842
+ }).formatToParts(/* @__PURE__ */ new Date());
80843
+ const part = (type) => parts.find((p) => p.type === type)?.value ?? "00";
80844
+ return `${part("year")}-${part("month")}-${part("day")}`;
80845
+ }
80846
+ var RUNTIME_MODE_BLOCK_PATTERN = /^[\t ]*<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>[\t ]*(?:\r?\n){0,2}/;
80847
+ function refreshRuntimeModeBlockOnLastUserMessage(messages, block) {
80848
+ for (let i = messages.length - 1; i >= 0; i--) {
80849
+ const message = messages[i];
80850
+ if (message?.role !== "user") continue;
80851
+ const content = typeof message.content === "string" ? message.content : "";
80852
+ const stripped = content.replace(RUNTIME_MODE_BLOCK_PATTERN, "");
80853
+ const refreshed = stripped === content && content.includes("<runtime-mode-context>") ? content : `${block}
80854
+
80855
+ ${stripped}`;
80856
+ if (refreshed === content) return messages;
80857
+ const next = [...messages];
80858
+ next[i] = { ...message, content: refreshed };
80859
+ return next;
80860
+ }
80861
+ return messages;
80862
+ }
80863
+ var RuntimeModeBlockInjector = class {
80864
+ constructor(options) {
80865
+ this.options = options;
80866
+ }
80867
+ options;
80868
+ lastKey = null;
80869
+ pendingRetryRefresh = false;
80870
+ reset() {
80871
+ this.lastKey = null;
80872
+ this.pendingRetryRefresh = true;
80873
+ }
80874
+ /** Block to prepend to the next user message, or null when unchanged. */
80875
+ nextBlock() {
80876
+ const block = this.freshBlock();
80877
+ this.pendingRetryRefresh = false;
80878
+ return block;
80879
+ }
80880
+ /**
80881
+ * Latest block for a request that reuses existing history (overflow retry
80882
+ * after compaction, manual compaction continue): the last user message may
80883
+ * carry a stale or evicted block, so the request-level transform refreshes
80884
+ * it. Consumed once per reset; null when nothing is pending.
80885
+ */
80886
+ consumeRetryRefreshBlock() {
80887
+ if (!this.pendingRetryRefresh) return null;
80888
+ this.pendingRetryRefresh = false;
80889
+ const state = this.options.getState();
80890
+ return buildRuntimeModeContext(state);
80891
+ }
80892
+ freshBlock() {
80893
+ const state = this.options.getState();
80894
+ if (!shouldInjectRuntimeModeBlock(this.lastKey, state)) return null;
80895
+ this.lastKey = runtimeModeBlockKey(state);
80896
+ if (state.pendingTransition) this.options.consumeTransition();
80897
+ this.options.noteTrace(state);
80898
+ return buildRuntimeModeContext(state);
80899
+ }
80900
+ };
80901
+
80902
+ // src/runtime/cache-retention.ts
80903
+ function createCacheRetentionStreamFn(configured) {
80904
+ const base = configured ?? ((model, context, options) => streamSimple(model, context, options));
80905
+ return (model, context, options) => base(model, context, {
80906
+ ...options,
80907
+ // Default only: explicit none/short requests (tests, debug commands) win.
80908
+ // Note: cost estimates still price cache writes at the short-TTL rate.
80909
+ cacheRetention: model.api === "anthropic-messages" && options?.cacheRetention === void 0 ? "long" : options?.cacheRetention
80910
+ });
80911
+ }
79328
80912
 
79329
80913
  // src/runtime/guard-advisory-context.ts
79330
80914
  var GUARD_ADVISORY_MARKER = "<runtime-guard-advisory>";
@@ -79338,12 +80922,19 @@ function isMarkerOnlyUserMessage(message) {
79338
80922
  function stripGuardAdvisories(messages) {
79339
80923
  return messages.filter((message) => !isMarkerOnlyUserMessage(message));
79340
80924
  }
79341
- function findLatestUserIndex2(messages) {
80925
+ function findLatestUserIndex(messages) {
79342
80926
  for (let index = messages.length - 1; index >= 0; index -= 1) {
79343
80927
  if (messages[index].role === "user") return index;
79344
80928
  }
79345
80929
  return -1;
79346
80930
  }
80931
+ function applyRuntimeRequestTransform(messages, options) {
80932
+ const advisory = options.takeAdvisory();
80933
+ let next = advisory ? injectGuardAdvisory(messages, advisory) : stripGuardAdvisories(messages);
80934
+ const retryBlock = options.takeRetryBlock();
80935
+ if (retryBlock) next = refreshRuntimeModeBlockOnLastUserMessage(next, retryBlock);
80936
+ return next;
80937
+ }
79347
80938
  function injectGuardAdvisory(messages, guidance) {
79348
80939
  const advisoryMessage = {
79349
80940
  role: "user",
@@ -79352,7 +80943,7 @@ ${guidance}
79352
80943
  ${GUARD_ADVISORY_END_MARKER}`,
79353
80944
  timestamp: Date.now()
79354
80945
  };
79355
- const latestUserIndex = findLatestUserIndex2(messages);
80946
+ const latestUserIndex = findLatestUserIndex(messages);
79356
80947
  if (latestUserIndex < 0) {
79357
80948
  return [...messages, advisoryMessage];
79358
80949
  }
@@ -79363,11 +80954,6 @@ ${GUARD_ADVISORY_END_MARKER}`,
79363
80954
  ];
79364
80955
  }
79365
80956
 
79366
- // src/runtime/tool-policy.ts
79367
- function selectToolsForRuntimeMode(tools, mode) {
79368
- return selectToolsForRuntimePolicy(tools, mode);
79369
- }
79370
-
79371
80957
  // src/runtime/terminal-bell.ts
79372
80958
  var BEL = "\x07";
79373
80959
  var DEFAULT_MIN_INTERVAL_MS = 500;
@@ -79565,7 +81151,7 @@ function createInteractiveExtensionUIContext(host) {
79565
81151
  }
79566
81152
  };
79567
81153
  return {
79568
- notify: (message, type = "info") => showExtensionNotification(host, message, type),
81154
+ notify: (message, type = "info", opts) => showExtensionNotification(host, message, type, opts),
79569
81155
  select: (title, options, opts) => interact(() => showExtensionSelector2(host, title, options, opts)),
79570
81156
  confirm: async (title, message, opts) => await interact(() => showExtensionConfirm(host, title, message, opts)) === true,
79571
81157
  input: (title, placeholder, opts) => interact(() => showExtensionInput(host, title, placeholder, opts)),
@@ -79713,10 +81299,14 @@ function installInteractiveInteractionHandler(host) {
79713
81299
  }
79714
81300
  });
79715
81301
  }
79716
- function showExtensionNotification(host, message, type = "info") {
81302
+ function showExtensionNotification(host, message, type = "info", opts) {
79717
81303
  const prefix = type === "error" ? "Error: " : type === "warning" ? "Warning: " : "";
79718
81304
  host.chatContainer.addChild(new Spacer(1));
79719
- host.chatContainer.addChild(new Text(theme.fg(type === "error" ? "error" : type === "warning" ? "warning" : "dim", `${prefix}${message}`), 1, 0));
81305
+ if (opts?.markdown) {
81306
+ host.chatContainer.addChild(new Markdown(`${prefix}${message}`, 1, 0, getMarkdownTheme()));
81307
+ } else {
81308
+ host.chatContainer.addChild(new Text(theme.fg(type === "error" ? "error" : type === "warning" ? "warning" : "dim", `${prefix}${message}`), 1, 0));
81309
+ }
79720
81310
  host.ui.requestRender();
79721
81311
  }
79722
81312
  function showExtensionSelector2(host, title, options, opts) {
@@ -80087,7 +81677,10 @@ async function loadInteractiveExtensions(host) {
80087
81677
  console.error(`[Extension] Tools set to: ${toolNames.join(", ")}`);
80088
81678
  }
80089
81679
  },
80090
- getActiveTools: () => host.agent.state.tools.map((t) => t.name),
81680
+ getActiveTools: () => {
81681
+ const tools = host.agent.state.tools;
81682
+ return (host.planModeEnabled ? tools.filter((tool) => isToolAllowedInPlanMode(tool)) : tools).map((tool) => tool.name);
81683
+ },
80091
81684
  sendUserMessage: (content) => {
80092
81685
  if (host.editor.onSubmit) {
80093
81686
  void host.editor.onSubmit(content);
@@ -80177,7 +81770,8 @@ async function loadInteractiveExtensions(host) {
80177
81770
  reload: () => host.reloadRuntime(),
80178
81771
  get yoloMode() {
80179
81772
  return host.yoloMode;
80180
- }
81773
+ },
81774
+ setYoloMode: host.setYoloMode ? (enabled) => host.setYoloMode?.(enabled) : void 0
80181
81775
  };
80182
81776
  const contextActions = {
80183
81777
  isIdle: () => !host.isProcessing,
@@ -80208,7 +81802,15 @@ async function loadInteractiveExtensions(host) {
80208
81802
  console.error(`[Extension] Failed to load ${path34}: ${error}`);
80209
81803
  }
80210
81804
  }
80211
- const extensionTools = filterEnabledTools(convertExtensionTools(extensions), host.options.settingsManager);
81805
+ const policyContext = () => ({
81806
+ mode: host.planModeEnabled ? "plan" : "normal",
81807
+ yoloMode: host.yoloMode,
81808
+ confirmationPolicy: "allow"
81809
+ });
81810
+ const extensionTools = filterEnabledTools(
81811
+ createPolicyEnforcedTools(convertExtensionTools(extensions), { getContext: policyContext }),
81812
+ host.options.settingsManager
81813
+ );
80212
81814
  const activeTools = [...host.agent.state.tools, ...extensionTools];
80213
81815
  host.agent.state.tools = wrapToolsWithExtensions(activeTools, host.extensionRunner);
80214
81816
  const availableTools = [
@@ -80285,6 +81887,12 @@ var InteractiveMode = class {
80285
81887
  pendingYoloModeTransition = null;
80286
81888
  executionReminder = null;
80287
81889
  pendingRuntimeGuardAdvisory = null;
81890
+ /** Freezes runtime mode blocks into user messages (cache-stable history). */
81891
+ runtimeModeBlocks = new RuntimeModeBlockInjector({
81892
+ getState: () => this.getRuntimeModeState(),
81893
+ consumeTransition: () => this.consumeRuntimeModeTransition(),
81894
+ noteTrace: (state) => this.sessionTrace.noteRuntimeEvent("runtime_context_injected", runtimeModeTraceData(state))
81895
+ });
80288
81896
  fileManager;
80289
81897
  mentionHandler;
80290
81898
  mentionPickerComponent;
@@ -80447,6 +82055,11 @@ var InteractiveMode = class {
80447
82055
  get controllerSetupHost() {
80448
82056
  return this;
80449
82057
  }
82058
+ getPolicyContext = () => ({
82059
+ mode: this.planModeEnabled ? "plan" : "normal",
82060
+ yoloMode: this.yoloMode,
82061
+ confirmationPolicy: "allow"
82062
+ });
80450
82063
  rebuildSkillAwareTools(sourceTools) {
80451
82064
  const preservedTools = sourceTools.filter(
80452
82065
  (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash"
@@ -80472,19 +82085,14 @@ var InteractiveMode = class {
80472
82085
  });
80473
82086
  const normalFsTools = createCliFileSystemTools(this.options.cwd, fileSystemToolOptions([this.options.configDir]));
80474
82087
  const currentFsTools = this.yoloMode ? createCliFileSystemTools(this.options.cwd, fileSystemToolOptions(["/"])) : normalFsTools;
80475
- const getPolicyContext = () => ({
80476
- mode: this.planModeEnabled ? "plan" : "normal",
80477
- yoloMode: this.yoloMode,
80478
- confirmationPolicy: "allow"
80479
- });
80480
82088
  return {
80481
82089
  currentTools: createPolicyEnforcedTools(
80482
82090
  [...currentFsTools, bashTool, ...preservedTools],
80483
- { getContext: getPolicyContext }
82091
+ { getContext: this.getPolicyContext }
80484
82092
  ),
80485
82093
  originalTools: createPolicyEnforcedTools(
80486
82094
  [...normalFsTools, bashTool, ...preservedTools],
80487
- { getContext: getPolicyContext }
82095
+ { getContext: this.getPolicyContext }
80488
82096
  )
80489
82097
  };
80490
82098
  }
@@ -80492,26 +82100,27 @@ var InteractiveMode = class {
80492
82100
  this.options.fileObservationState.clear();
80493
82101
  }
80494
82102
  getRuntimeModeState() {
80495
- const pendingTransition = this.getPendingRuntimeModeTransition();
80496
82103
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "local";
80497
- const dateParts = new Intl.DateTimeFormat("en-US", {
80498
- year: "numeric",
80499
- month: "2-digit",
80500
- day: "2-digit",
80501
- timeZone
80502
- }).formatToParts(/* @__PURE__ */ new Date());
80503
- const datePart = (type) => dateParts.find((part) => part.type === type)?.value ?? "00";
80504
- const currentDate = `${datePart("year")}-${datePart("month")}-${datePart("day")}`;
82104
+ const activeTools = this.agent?.state?.tools ?? [];
80505
82105
  return {
80506
82106
  yoloMode: this.yoloMode,
80507
82107
  planMode: this.planModeEnabled ? "planning" : "off",
80508
82108
  filesystemScope: this.yoloMode ? "all" : "workspace",
80509
- ...pendingTransition ? { pendingTransition } : {},
80510
- currentDate,
82109
+ ...this.getPendingRuntimeModeTransition() ? { pendingTransition: this.getPendingRuntimeModeTransition() } : {},
82110
+ currentDate: currentDateInZone(timeZone),
80511
82111
  timeZone,
80512
- activeToolNames: this.agent?.state?.tools?.map((tool) => tool.name) ?? []
82112
+ activeToolNames: activeTools.map((tool) => tool.name),
82113
+ planDisabledToolNames: this.planModeEnabled ? activeTools.filter((tool) => !isToolAllowedInPlanMode(tool)).map((tool) => tool.name) : []
80513
82114
  };
80514
82115
  }
82116
+ /** Public reset for compaction flows (compaction may drop the latest block). */
82117
+ resetRuntimeModeBlocks() {
82118
+ this.runtimeModeBlocks.reset();
82119
+ }
82120
+ /** Runtime mode block for the next user message; frozen into it (append-only history, cache-stable prefixes). */
82121
+ runtimeModeBlockForNextMessage() {
82122
+ return this.runtimeModeBlocks.nextBlock();
82123
+ }
80515
82124
  getPendingRuntimeModeTransition() {
80516
82125
  if (this.pendingYoloModeTransition === "enter") return "entered_yolo";
80517
82126
  if (this.pendingYoloModeTransition === "exit") return "exited_yolo";
@@ -80530,10 +82139,7 @@ var InteractiveMode = class {
80530
82139
  const currentTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.currentTools, this.extensionRunner) : rebuilt.currentTools;
80531
82140
  const originalTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.originalTools, this.extensionRunner) : rebuilt.originalTools;
80532
82141
  const activeTools = filterEnabledTools(
80533
- selectToolsForRuntimeMode(
80534
- currentTools,
80535
- this.planModeEnabled ? "plan" : "normal"
80536
- ),
82142
+ currentTools,
80537
82143
  this.options.settingsManager
80538
82144
  );
80539
82145
  this.agent.setTools(activeTools);
@@ -80566,7 +82172,8 @@ var InteractiveMode = class {
80566
82172
  this.safeRequestRender();
80567
82173
  }
80568
82174
  promptAgent(input) {
80569
- return this.agentSessionController.prompt(input, { source: "interactive" });
82175
+ const prompt = prependRuntimeModeBlockToPrompt(input, this.runtimeModeBlockForNextMessage());
82176
+ return this.agentSessionController.prompt(prompt, { source: "interactive" });
80570
82177
  }
80571
82178
  continueAgent() {
80572
82179
  return this.agentSessionController.continue();
@@ -80596,6 +82203,7 @@ var InteractiveMode = class {
80596
82203
  this.persistExecutionState();
80597
82204
  }
80598
82205
  this.executionReminder = this.executionState.getReminder();
82206
+ this.resetRuntimeModeBlocks();
80599
82207
  await this.refreshTodoWidget();
80600
82208
  await this.emitExtensionEvent({
80601
82209
  type: "session:changed",
@@ -80606,7 +82214,7 @@ var InteractiveMode = class {
80606
82214
  async init() {
80607
82215
  const activeSession = this.sessionManager.getActiveSession();
80608
82216
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
80609
- const { Agent } = await import("./chunks/src-HPIBCE2I.js");
82217
+ const { Agent } = await import("./chunks/src-E7NCXMU5.js");
80610
82218
  this.agent = new Agent({
80611
82219
  initialState: {
80612
82220
  systemPrompt: this.buildSystemPrompt(),
@@ -80616,28 +82224,15 @@ var InteractiveMode = class {
80616
82224
  },
80617
82225
  apiKey: this.options.apiKey,
80618
82226
  sessionId: activeSession?.id,
80619
- streamFn: this.options.streamFn,
80620
- transformContext: async (messages) => {
80621
- const runtimeModeTransform = createRuntimeModeTransformContext({
80622
- getState: () => this.getRuntimeModeState(),
80623
- consumeTransition: () => this.consumeRuntimeModeTransition(),
80624
- onInjected: (state) => this.sessionTrace.noteRuntimeEvent("runtime_context_injected", {
80625
- yoloMode: state.yoloMode,
80626
- planMode: state.planMode,
80627
- filesystemScope: state.filesystemScope,
80628
- pendingTransition: state.pendingTransition,
80629
- currentDate: state.currentDate,
80630
- timeZone: state.timeZone,
80631
- activeToolCount: state.activeToolNames.length
80632
- }),
80633
- onStripped: (count) => this.sessionTrace.noteRuntimeEvent("stale_runtime_context_stripped", { count })
80634
- });
80635
- let transformed = await runtimeModeTransform(messages);
80636
- const advisory = this.pendingRuntimeGuardAdvisory;
80637
- this.pendingRuntimeGuardAdvisory = null;
80638
- transformed = advisory ? injectGuardAdvisory(transformed, advisory) : stripGuardAdvisories(transformed);
80639
- return transformed;
80640
- }
82227
+ streamFn: createCacheRetentionStreamFn(this.options.streamFn),
82228
+ transformContext: async (messages) => applyRuntimeRequestTransform(messages, {
82229
+ takeAdvisory: () => {
82230
+ const advisory = this.pendingRuntimeGuardAdvisory;
82231
+ this.pendingRuntimeGuardAdvisory = null;
82232
+ return advisory;
82233
+ },
82234
+ takeRetryBlock: () => this.runtimeModeBlocks.consumeRetryRefreshBlock()
82235
+ })
80641
82236
  });
80642
82237
  this.agentSessionController = createInteractiveAgentSessionController(this.controllerSetupHost);
80643
82238
  this.updateTerminalTitle();
@@ -80821,6 +82416,9 @@ var InteractiveMode = class {
80821
82416
  async handleYoloCommand(args) {
80822
82417
  return handleYoloCommand(this, args);
80823
82418
  }
82419
+ setYoloMode(enabled) {
82420
+ void this.handleYoloCommand(enabled ? "on" : "off");
82421
+ }
80824
82422
  /**
80825
82423
  * Toggle calm mode and rebuild the chat so already-rendered rows follow
80826
82424
  * the new mode. Delivery, tool execution, and session storage are
@@ -80832,7 +82430,6 @@ var InteractiveMode = class {
80832
82430
  this.options.settingsManager.setCalmModeEnabled(enabled);
80833
82431
  this.footer.setState({ calmMode: enabled });
80834
82432
  applyCalmMode(this, enabled);
80835
- this.todoWidget.setCalmMode(enabled);
80836
82433
  this.extensionWidgets.setCalmMode(enabled);
80837
82434
  this.showStatus(`Calm mode ${enabled ? "on \u2014 showing questions and final answers only" : "off"}`);
80838
82435
  this.safeRequestRender(true);
@@ -80870,11 +82467,11 @@ var InteractiveMode = class {
80870
82467
  async handleCacheTest() {
80871
82468
  return handleCacheTest(this);
80872
82469
  }
80873
- async handleDebugContextCommand() {
80874
- return handleDebugContextCommand(this);
82470
+ async handleTreeCommand() {
82471
+ return handleTreeCommand(this);
80875
82472
  }
80876
- async handleDebugPackageCommand() {
80877
- return handleDebugPackageCommand(this);
82473
+ handleDebugReportCommand(kind) {
82474
+ return kind === "context" ? handleContextReportCommand(this) : kind === "trace" ? handleTraceCommand(this, this.sessionTrace.getLiveSnapshot()) : handleBootCommand(this);
80878
82475
  }
80879
82476
  async handleResumeCommand() {
80880
82477
  return handleResumeCommand(this);
@@ -80894,9 +82491,6 @@ var InteractiveMode = class {
80894
82491
  setWaitingForUser(waiting) {
80895
82492
  return setWaitingForUser(this, waiting);
80896
82493
  }
80897
- async handleTreeCommand() {
80898
- return handleTreeCommand(this);
80899
- }
80900
82494
  async forkAtEntry(entryId) {
80901
82495
  return forkAtEntry(this, entryId);
80902
82496
  }
@@ -81196,6 +82790,7 @@ async function composeCliRuntime(params) {
81196
82790
  getPolicyContext: () => ({ mode: "normal", confirmationPolicy: "allow" })
81197
82791
  };
81198
82792
  const rawTools = [
82793
+ createSearchContextTool(() => sessionManager),
81199
82794
  ...createCliFileSystemTools(cwd, fileSystemToolOptions),
81200
82795
  ...cliHostCapabilities.tools
81201
82796
  ];
@@ -81441,7 +83036,7 @@ async function runPrintMode(params) {
81441
83036
  `);
81442
83037
  console.log("Assistant: ");
81443
83038
  }
81444
- const { Agent: AgentClass } = await import("./chunks/src-HPIBCE2I.js");
83039
+ const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
81445
83040
  let agent;
81446
83041
  const printModeExtensions = await loadPrintModeExtensions({
81447
83042
  cwd: runtime.cwd,
@@ -81681,7 +83276,7 @@ function toJsonValue(value, depth = 0, cap = MAX_EVENT_STRING_LENGTH) {
81681
83276
  }
81682
83277
  return result;
81683
83278
  }
81684
- function isRecord4(value) {
83279
+ function isRecord3(value) {
81685
83280
  return !!value && typeof value === "object" && !Array.isArray(value);
81686
83281
  }
81687
83282
  function truncateForEvent(text, cap) {
@@ -81707,7 +83302,7 @@ function parseInput(line) {
81707
83302
  } catch {
81708
83303
  return null;
81709
83304
  }
81710
- if (!isRecord4(parsed) || typeof parsed.type !== "string") return null;
83305
+ if (!isRecord3(parsed) || typeof parsed.type !== "string") return null;
81711
83306
  if (parsed.type === "shutdown") {
81712
83307
  return { type: "shutdown", reason: typeof parsed.reason === "string" ? parsed.reason : void 0 };
81713
83308
  }
@@ -81758,7 +83353,7 @@ function parseInput(line) {
81758
83353
  prompt: parsed.prompt,
81759
83354
  streamingBehavior,
81760
83355
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0,
81761
- metadata: isRecord4(parsed.metadata) ? parsed.metadata : void 0
83356
+ metadata: isRecord3(parsed.metadata) ? parsed.metadata : void 0
81762
83357
  };
81763
83358
  }
81764
83359
  return null;
@@ -81798,15 +83393,15 @@ function readProgressText(value) {
81798
83393
  function extractJsonEventsToolProgressText(partialResult) {
81799
83394
  const plainText = readProgressText(partialResult).trim();
81800
83395
  if (plainText) return plainText;
81801
- if (!isRecord4(partialResult)) return "";
83396
+ if (!isRecord3(partialResult)) return "";
81802
83397
  const content = partialResult.content;
81803
83398
  if (Array.isArray(content)) {
81804
- return content.map((block) => isRecord4(block) && block.type === "text" ? readProgressText(block.text) : "").filter(Boolean).join("\n").trim();
83399
+ return content.map((block) => isRecord3(block) && block.type === "text" ? readProgressText(block.text) : "").filter(Boolean).join("\n").trim();
81805
83400
  }
81806
83401
  const directText = readProgressText(partialResult.text) || readProgressText(partialResult.message);
81807
83402
  if (directText) return directText.trim();
81808
83403
  const details = partialResult.details;
81809
- if (isRecord4(details)) {
83404
+ if (isRecord3(details)) {
81810
83405
  return (readProgressText(details.text) || readProgressText(details.message)).trim();
81811
83406
  }
81812
83407
  return "";
@@ -82302,7 +83897,7 @@ async function runJsonEventsMode(params) {
82302
83897
  return;
82303
83898
  }
82304
83899
  const parent = agent;
82305
- const { Agent: AgentClass } = await import("./chunks/src-HPIBCE2I.js");
83900
+ const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
82306
83901
  const sideAgent = new AgentClass({
82307
83902
  initialState: {
82308
83903
  systemPrompt: parent.state.systemPrompt,
@@ -82646,7 +84241,7 @@ async function runJsonEventsMode(params) {
82646
84241
  if (runtime.initialModel.unconfigured || controller) {
82647
84242
  return;
82648
84243
  }
82649
- const { Agent: AgentClass } = await import("./chunks/src-HPIBCE2I.js");
84244
+ const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
82650
84245
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
82651
84246
  extensionRunner = jsonModeExtensions.runner;
82652
84247
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
@@ -83240,7 +84835,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
83240
84835
  }
83241
84836
  async function createCliTestStreamFnFromEnv() {
83242
84837
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
83243
- return (await import("./chunks/test-stream-Y7WXELAJ.js")).createTestStreamFnFromSequenceEnv();
84838
+ return (await import("./chunks/test-stream-BWBJTFNI.js")).createTestStreamFnFromSequenceEnv();
83244
84839
  }
83245
84840
  function resolveJsonEventsDetailFlag() {
83246
84841
  for (let index = 0; index < process.argv.length; index += 1) {