@cydm/pie 2.6.12 → 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-PPEWRUMQ.js → chunk-6RBSTII7.js} +657 -594
  11. package/dist/chunks/{chunk-AAEBH6KX.js → chunk-IPNAFMOJ.js} +172 -230
  12. package/dist/chunks/{chunk-7B4IIBVS.js → chunk-R5I2QLF2.js} +1269 -4
  13. package/dist/chunks/{chunk-HHNOENEH.js → chunk-TWILEFLD.js} +14 -1
  14. package/dist/chunks/{src-OWQI5M6W.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 +463 -232
  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-AAEBH6KX.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-PPEWRUMQ.js";
67
- import "./chunks/chunk-HHNOENEH.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
  }
@@ -70219,20 +70287,52 @@ function getLastContextSignals(hostKey) {
70219
70287
  }
70220
70288
  function computeContextSignals(hostKey, systemPrompt, tools, messages) {
70221
70289
  const prefixHash = computePromptPrefixHash(systemPrompt, tools);
70290
+ const systemHash = computePromptPrefixHash(systemPrompt, []);
70291
+ const toolsHash = computePromptPrefixHash("", tools);
70222
70292
  const previous = signalMemories.get(hostKey);
70223
70293
  const prefixHashChanged = previous !== void 0 && previous.hash !== prefixHash;
70224
70294
  if (previous?.hash !== prefixHash) {
70225
- signalMemories.set(hostKey, { hash: prefixHash });
70295
+ signalMemories.set(hostKey, { hash: prefixHash, systemHash, toolsHash });
70226
70296
  }
70227
70297
  const signals = {
70228
70298
  prefixHash,
70229
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,
70230
70303
  cacheHitRate: calculateCumulativeCacheHitRate(messages)
70231
70304
  };
70232
70305
  lastSignals.set(hostKey, signals);
70233
70306
  return signals;
70234
70307
  }
70235
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
+
70236
70336
  // src/commands/interactive-session-commands.ts
70237
70337
  import * as os6 from "os";
70238
70338
 
@@ -71171,6 +71271,9 @@ var TreeSelectorComponent = class {
71171
71271
  pageNext() {
71172
71272
  if (this.goToNextPage()) {
71173
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";
71174
71277
  } else {
71175
71278
  this.statusMessage = this.pageSummaries.length === 0 ? "no entries" : "latest page";
71176
71279
  }
@@ -71178,10 +71281,58 @@ var TreeSelectorComponent = class {
71178
71281
  pagePrev() {
71179
71282
  if (this.goToPreviousPage()) {
71180
71283
  this.statusMessage = null;
71284
+ } else if (this.filteredRows.length > 0) {
71285
+ this.selectedIndex = 0;
71286
+ this.statusMessage = "oldest page - at first entry";
71181
71287
  } else {
71182
71288
  this.statusMessage = this.pageSummaries.length === 0 ? "no entries" : "oldest page";
71183
71289
  }
71184
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
+ }
71185
71336
  /**
71186
71337
  * Bounded "next match": step to the next in-page match, otherwise scan
71187
71338
  * forward at most SEARCH_SCAN_PAGE_LIMIT pages per press. Never scans
@@ -71235,7 +71386,7 @@ var TreeSelectorComponent = class {
71235
71386
  lines.push(createLine(" Conversation Tree", width, false));
71236
71387
  lines.push(
71237
71388
  createLine(
71238
- " \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",
71239
71390
  width,
71240
71391
  false
71241
71392
  )
@@ -71304,10 +71455,18 @@ var TreeSelectorComponent = class {
71304
71455
  this.moveUp();
71305
71456
  } else if (kb.matches(keyData, "selectDown")) {
71306
71457
  this.moveDown();
71307
- } else if (kb.matches(keyData, "cursorRight") || kb.matches(keyData, "selectPageDown")) {
71458
+ } else if (kb.matches(keyData, "cursorRight")) {
71308
71459
  this.pageNext();
71309
- } else if (kb.matches(keyData, "cursorLeft") || kb.matches(keyData, "selectPageUp")) {
71460
+ } else if (kb.matches(keyData, "cursorLeft")) {
71310
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();
71311
71470
  } else if (keyData === "e" || keyData === "E") {
71312
71471
  this.toggleCollapse();
71313
71472
  } else if (kb.matches(keyData, "selectConfirm")) {
@@ -72826,7 +72985,7 @@ async function loadSession(host, sessionId) {
72826
72985
  contextPercent,
72827
72986
  contextWindow
72828
72987
  });
72829
- applyContextSignalsToFooter(host);
72988
+ applyContextSignalsWithTrace(host);
72830
72989
  host.ui.requestRender();
72831
72990
  setTimeout(() => host.ui.requestRender(), 100);
72832
72991
  } catch (error) {
@@ -73864,7 +74023,7 @@ function bindInteractiveRuntimeEvents(host) {
73864
74023
  contextPercent,
73865
74024
  contextWindow
73866
74025
  });
73867
- applyContextSignalsToFooter(host);
74026
+ applyContextSignalsWithTrace(host);
73868
74027
  host.safeRequestRender();
73869
74028
  host.sessionManager.save().catch((err) => {
73870
74029
  console.error("[Session] Auto-save failed:", err);
@@ -74913,9 +75072,9 @@ function collectKnownToolNames(host) {
74913
75072
  function describeToggleEffect(host, name, enabled) {
74914
75073
  const loaded = collectKnownToolNames(host).active.includes(name);
74915
75074
  if (enabled) {
74916
- return loaded ? `Tool "${name}" enabled (active now).` : `Tool "${name}" enabled in ~/.pie/settings.json (loads on next start \u2014 it was not loaded this session).`;
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).`;
74917
75076
  }
74918
- 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.`;
74919
75078
  }
74920
75079
  function showToolsManager(host) {
74921
75080
  host.showSelector((done) => {
@@ -75020,7 +75179,7 @@ function handleToolsCommand(host, args) {
75020
75179
  host.chatContainer.addChild(new Text(theme.fg("dim", " No tools available."), 1, 0));
75021
75180
  }
75022
75181
  for (const name of active) {
75023
- 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)";
75024
75183
  host.chatContainer.addChild(new Text(theme.fg("dim", ` \u2022 ${name}: ${state}`), 1, 0));
75025
75184
  }
75026
75185
  for (const name of disabled) {
@@ -76398,7 +76557,7 @@ async function handleClearCommand(host) {
76398
76557
  contextPercent: contextWindow > 0 && contextUsage.tokens !== null ? contextUsage.tokens / contextWindow * 100 : 0,
76399
76558
  contextWindow
76400
76559
  });
76401
- applyContextSignalsToFooter(host);
76560
+ applyContextSignalsWithTrace(host);
76402
76561
  host.updateTerminalTitle();
76403
76562
  host.chatContainer.clear();
76404
76563
  host.streamingComponent = void 0;
@@ -76487,7 +76646,10 @@ function createExtensionContextForCommands(host) {
76487
76646
  console.error(`[Extension] Tools restricted to: ${tools.join(", ")}`);
76488
76647
  }
76489
76648
  },
76490
- 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
+ },
76491
76653
  sendUserMessage: (content) => {
76492
76654
  if (host.editor.onSubmit) {
76493
76655
  void host.editor.onSubmit(content);
@@ -76784,6 +76946,7 @@ async function handleCompactCommand(host) {
76784
76946
  const compactedMessages = host.sessionManager.getMessages();
76785
76947
  host.replaceAgentMessagesAndMarkSynced(compactedMessages);
76786
76948
  host.resetFileObservationState();
76949
+ host.resetRuntimeModeBlocks?.();
76787
76950
  host.streamingComponent = void 0;
76788
76951
  host.pendingTools.clear();
76789
76952
  host.chatContainer.clear();
@@ -76808,7 +76971,7 @@ async function handleCompactCommand(host) {
76808
76971
  contextPercent,
76809
76972
  contextWindow
76810
76973
  });
76811
- applyContextSignalsToFooter(host);
76974
+ applyContextSignalsWithTrace(host);
76812
76975
  await host.sessionManager.save();
76813
76976
  host.safeRequestRender();
76814
76977
  } catch (error) {
@@ -78927,8 +79090,12 @@ function queueMessage(host, text, mode, options = {}) {
78927
79090
  }
78928
79091
  const clearEditor = options.clearEditor ?? true;
78929
79092
  host.sessionTrace.notePendingUserText(text);
79093
+ const runtimeBlock = host.runtimeModeBlockForNextMessage?.();
79094
+ const content = runtimeBlock ? `${runtimeBlock}
79095
+
79096
+ ${text}` : text;
78930
79097
  void host.agentSessionController.prompt(
78931
- { role: "user", content: text, timestamp: Date.now() },
79098
+ { role: "user", content, timestamp: Date.now() },
78932
79099
  {
78933
79100
  source: "interactive",
78934
79101
  streamingBehavior: mode,
@@ -80501,6 +80668,7 @@ function createInteractiveAgentSessionController(host) {
80501
80668
  return;
80502
80669
  }
80503
80670
  host.resetFileObservationState();
80671
+ host.resetRuntimeModeBlocks?.();
80504
80672
  if (!event.result?.aborted) {
80505
80673
  host.streamingComponent = void 0;
80506
80674
  host.pendingTools.clear();
@@ -80523,7 +80691,7 @@ function createInteractiveAgentSessionController(host) {
80523
80691
  contextPercent,
80524
80692
  contextWindow
80525
80693
  });
80526
- applyContextSignalsToFooter(host);
80694
+ applyContextSignalsWithTrace(host);
80527
80695
  host.safeRequestRender();
80528
80696
  if (!event.result?.aborted) {
80529
80697
  if (autoCompactBeforeStats !== null) {
@@ -80552,94 +80720,80 @@ function createInteractiveAgentSessionController(host) {
80552
80720
  }
80553
80721
 
80554
80722
  // src/runtime/runtime-mode-context.ts
80555
- var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
80556
- var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
80557
- var STALE_SYSTEM_REMINDER_RE = /<system-reminder>\s*# Plan Mode[\s\S]*?<\/system-reminder>\s*/gi;
80558
- var STALE_RUNTIME_CONTEXT_RE = /<runtime-mode-context>[\s\S]*?<\/runtime-mode-context>\s*/gi;
80559
- function isRecord3(value) {
80560
- return !!value && typeof value === "object" && !Array.isArray(value);
80723
+ function isAgentMessage(value) {
80724
+ return !!value && typeof value === "object" && "role" in value;
80561
80725
  }
80562
- function cleanText(text) {
80563
- let stripped = 0;
80564
- const withoutRuntime = text.replace(STALE_RUNTIME_CONTEXT_RE, () => {
80565
- stripped++;
80566
- return "";
80567
- });
80568
- const withoutPlanReminder = withoutRuntime.replace(STALE_SYSTEM_REMINDER_RE, () => {
80569
- stripped++;
80570
- return "";
80571
- });
80572
- return { text: withoutPlanReminder, stripped };
80573
- }
80574
- function stripUserMessage(message) {
80575
- if (!isRecord3(message) || message.role !== "user") {
80576
- return { message, stripped: 0 };
80726
+ function prependRuntimeModeBlockToPrompt(input, block) {
80727
+ if (!block) return input;
80728
+ if (typeof input === "string") {
80729
+ return input.trim() ? `${block}
80730
+
80731
+ ${input}` : block;
80577
80732
  }
80578
- const content = message.content;
80579
- if (typeof content === "string") {
80580
- const cleaned = cleanText(content);
80581
- if (cleaned.stripped === 0) return { message, stripped: 0 };
80582
- return {
80583
- message: {
80584
- ...message,
80585
- content: cleaned.text
80586
- },
80587
- stripped: cleaned.stripped
80588
- };
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];
80589
80738
  }
80590
- if (!Array.isArray(content)) {
80591
- return { message, stripped: 0 };
80592
- }
80593
- let stripped = 0;
80594
- const nextContent = content.map((block) => {
80595
- if (!isRecord3(block) || block.type !== "text" || typeof block.text !== "string") {
80596
- return block;
80597
- }
80598
- const cleaned = cleanText(block.text);
80599
- stripped += cleaned.stripped;
80600
- return { ...block, text: cleaned.text };
80601
- }).filter((block) => !(isRecord3(block) && block.type === "text" && String(block.text ?? "").length === 0));
80602
- if (stripped === 0) return { message, stripped: 0 };
80603
- return {
80604
- message: {
80605
- ...message,
80606
- content: nextContent
80607
- },
80608
- stripped
80609
- };
80739
+ if (isAgentMessage(input)) {
80740
+ return prependBlockToMessages([input], block)[0];
80741
+ }
80742
+ return [{ type: "text", text: block }, ...input];
80610
80743
  }
80611
- function stripRuntimeModeContext(messages) {
80612
- let stripped = 0;
80613
- const nextMessages = [];
80614
- for (const message of messages) {
80615
- const result = stripUserMessage(message);
80616
- stripped += result.stripped;
80617
- if (isRecord3(result.message) && result.message.role === "user") {
80618
- const content = result.message.content;
80619
- if (typeof content === "string" && content.trim().length === 0) {
80620
- continue;
80621
- }
80622
- if (Array.isArray(content) && content.length === 0) {
80623
- continue;
80624
- }
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;
80625
80751
  }
80626
- nextMessages.push(result.message);
80627
80752
  }
80628
- 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;
80629
80768
  }
80769
+ var RUNTIME_MODE_CONTEXT_MARKER = "<runtime-mode-context>";
80770
+ var RUNTIME_MODE_CONTEXT_END_MARKER = "</runtime-mode-context>";
80630
80771
  function buildRuntimeModeContext(state) {
80631
80772
  const lines = [
80632
80773
  RUNTIME_MODE_CONTEXT_MARKER,
80633
- "Current runtime mode:",
80634
- `- Plan mode: ${state.planMode === "planning" ? "active (workspace read-only; plan_scratch permits session-local temporary drafts)" : "off"}.`,
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(
80635
80789
  `- YOLO mode: ${state.yoloMode ? "on (filesystem sandbox directory restrictions are disabled)" : "off (workspace/protected-path restrictions apply)"}.`,
80636
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"}.`
80637
- ];
80791
+ );
80638
80792
  if (state.pendingTransition) {
80639
80793
  const transitionText = {
80640
80794
  entered_yolo: "YOLO mode was just enabled for this turn.",
80641
80795
  exited_yolo: "YOLO mode was just disabled; do not assume previous absolute-path access still applies.",
80642
- entered_plan: "Plan mode was just enabled; analyze and plan without changing the workspace. Use plan_scratch for temporary drafts.",
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.",
80643
80797
  exited_plan: "Plan mode was just disabled; execution tools may be used when the task requires them."
80644
80798
  };
80645
80799
  lines.push(`- Transition: ${transitionText[state.pendingTransition]}`);
@@ -80653,46 +80807,108 @@ function buildRuntimeModeContext(state) {
80653
80807
  lines.push(RUNTIME_MODE_CONTEXT_END_MARKER);
80654
80808
  return lines.join("\n");
80655
80809
  }
80656
- function findLatestUserIndex(messages) {
80657
- for (let index = messages.length - 1; index >= 0; index--) {
80658
- const message = messages[index];
80659
- if (isRecord3(message) && message.role === "user") {
80660
- return index;
80661
- }
80662
- }
80663
- 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
+ });
80664
80819
  }
80665
- function injectRuntimeModeContext(messages, state) {
80666
- const contextMessage = {
80667
- role: "user",
80668
- content: buildRuntimeModeContext(state),
80669
- timestamp: Date.now()
80670
- };
80671
- const latestUserIndex = findLatestUserIndex(messages);
80672
- if (latestUserIndex < 0) {
80673
- return [...messages, contextMessage];
80674
- }
80675
- return [
80676
- ...messages.slice(0, latestUserIndex),
80677
- contextMessage,
80678
- ...messages.slice(latestUserIndex)
80679
- ];
80820
+ function shouldInjectRuntimeModeBlock(lastKey, state) {
80821
+ if (lastKey === null) return true;
80822
+ return runtimeModeBlockKey(state) !== lastKey;
80680
80823
  }
80681
- function createRuntimeModeTransformContext(options) {
80682
- return async (messages) => {
80683
- const stripped = stripRuntimeModeContext(messages);
80684
- if (stripped.stripped > 0) {
80685
- options.onStripped?.(stripped.stripped);
80686
- }
80687
- const state = options.getState();
80688
- const nextMessages = injectRuntimeModeContext(stripped.messages, state);
80689
- options.onInjected?.(state);
80690
- if (state.pendingTransition) {
80691
- options.consumeTransition?.();
80692
- }
80693
- 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
80694
80834
  };
80695
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
+ }
80696
80912
 
80697
80913
  // src/runtime/guard-advisory-context.ts
80698
80914
  var GUARD_ADVISORY_MARKER = "<runtime-guard-advisory>";
@@ -80706,12 +80922,19 @@ function isMarkerOnlyUserMessage(message) {
80706
80922
  function stripGuardAdvisories(messages) {
80707
80923
  return messages.filter((message) => !isMarkerOnlyUserMessage(message));
80708
80924
  }
80709
- function findLatestUserIndex2(messages) {
80925
+ function findLatestUserIndex(messages) {
80710
80926
  for (let index = messages.length - 1; index >= 0; index -= 1) {
80711
80927
  if (messages[index].role === "user") return index;
80712
80928
  }
80713
80929
  return -1;
80714
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
+ }
80715
80938
  function injectGuardAdvisory(messages, guidance) {
80716
80939
  const advisoryMessage = {
80717
80940
  role: "user",
@@ -80720,7 +80943,7 @@ ${guidance}
80720
80943
  ${GUARD_ADVISORY_END_MARKER}`,
80721
80944
  timestamp: Date.now()
80722
80945
  };
80723
- const latestUserIndex = findLatestUserIndex2(messages);
80946
+ const latestUserIndex = findLatestUserIndex(messages);
80724
80947
  if (latestUserIndex < 0) {
80725
80948
  return [...messages, advisoryMessage];
80726
80949
  }
@@ -80731,11 +80954,6 @@ ${GUARD_ADVISORY_END_MARKER}`,
80731
80954
  ];
80732
80955
  }
80733
80956
 
80734
- // src/runtime/tool-policy.ts
80735
- function selectToolsForRuntimeMode(tools, mode) {
80736
- return selectToolsForRuntimePolicy(tools, mode);
80737
- }
80738
-
80739
80957
  // src/runtime/terminal-bell.ts
80740
80958
  var BEL = "\x07";
80741
80959
  var DEFAULT_MIN_INTERVAL_MS = 500;
@@ -80933,7 +81151,7 @@ function createInteractiveExtensionUIContext(host) {
80933
81151
  }
80934
81152
  };
80935
81153
  return {
80936
- notify: (message, type = "info") => showExtensionNotification(host, message, type),
81154
+ notify: (message, type = "info", opts) => showExtensionNotification(host, message, type, opts),
80937
81155
  select: (title, options, opts) => interact(() => showExtensionSelector2(host, title, options, opts)),
80938
81156
  confirm: async (title, message, opts) => await interact(() => showExtensionConfirm(host, title, message, opts)) === true,
80939
81157
  input: (title, placeholder, opts) => interact(() => showExtensionInput(host, title, placeholder, opts)),
@@ -81081,10 +81299,14 @@ function installInteractiveInteractionHandler(host) {
81081
81299
  }
81082
81300
  });
81083
81301
  }
81084
- function showExtensionNotification(host, message, type = "info") {
81302
+ function showExtensionNotification(host, message, type = "info", opts) {
81085
81303
  const prefix = type === "error" ? "Error: " : type === "warning" ? "Warning: " : "";
81086
81304
  host.chatContainer.addChild(new Spacer(1));
81087
- host.chatContainer.addChild(new Text(theme.fg(type === "error" ? "error" : type === "warning" ? "warning" : "dim", `${prefix}${message}`), 1, 0));
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
+ }
81088
81310
  host.ui.requestRender();
81089
81311
  }
81090
81312
  function showExtensionSelector2(host, title, options, opts) {
@@ -81455,7 +81677,10 @@ async function loadInteractiveExtensions(host) {
81455
81677
  console.error(`[Extension] Tools set to: ${toolNames.join(", ")}`);
81456
81678
  }
81457
81679
  },
81458
- 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
+ },
81459
81684
  sendUserMessage: (content) => {
81460
81685
  if (host.editor.onSubmit) {
81461
81686
  void host.editor.onSubmit(content);
@@ -81545,7 +81770,8 @@ async function loadInteractiveExtensions(host) {
81545
81770
  reload: () => host.reloadRuntime(),
81546
81771
  get yoloMode() {
81547
81772
  return host.yoloMode;
81548
- }
81773
+ },
81774
+ setYoloMode: host.setYoloMode ? (enabled) => host.setYoloMode?.(enabled) : void 0
81549
81775
  };
81550
81776
  const contextActions = {
81551
81777
  isIdle: () => !host.isProcessing,
@@ -81576,7 +81802,15 @@ async function loadInteractiveExtensions(host) {
81576
81802
  console.error(`[Extension] Failed to load ${path34}: ${error}`);
81577
81803
  }
81578
81804
  }
81579
- 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
+ );
81580
81814
  const activeTools = [...host.agent.state.tools, ...extensionTools];
81581
81815
  host.agent.state.tools = wrapToolsWithExtensions(activeTools, host.extensionRunner);
81582
81816
  const availableTools = [
@@ -81653,6 +81887,12 @@ var InteractiveMode = class {
81653
81887
  pendingYoloModeTransition = null;
81654
81888
  executionReminder = null;
81655
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
+ });
81656
81896
  fileManager;
81657
81897
  mentionHandler;
81658
81898
  mentionPickerComponent;
@@ -81815,6 +82055,11 @@ var InteractiveMode = class {
81815
82055
  get controllerSetupHost() {
81816
82056
  return this;
81817
82057
  }
82058
+ getPolicyContext = () => ({
82059
+ mode: this.planModeEnabled ? "plan" : "normal",
82060
+ yoloMode: this.yoloMode,
82061
+ confirmationPolicy: "allow"
82062
+ });
81818
82063
  rebuildSkillAwareTools(sourceTools) {
81819
82064
  const preservedTools = sourceTools.filter(
81820
82065
  (tool) => !CLI_FILESYSTEM_TOOL_NAMES.includes(tool.name) && tool.name !== "bash"
@@ -81840,19 +82085,14 @@ var InteractiveMode = class {
81840
82085
  });
81841
82086
  const normalFsTools = createCliFileSystemTools(this.options.cwd, fileSystemToolOptions([this.options.configDir]));
81842
82087
  const currentFsTools = this.yoloMode ? createCliFileSystemTools(this.options.cwd, fileSystemToolOptions(["/"])) : normalFsTools;
81843
- const getPolicyContext = () => ({
81844
- mode: this.planModeEnabled ? "plan" : "normal",
81845
- yoloMode: this.yoloMode,
81846
- confirmationPolicy: "allow"
81847
- });
81848
82088
  return {
81849
82089
  currentTools: createPolicyEnforcedTools(
81850
82090
  [...currentFsTools, bashTool, ...preservedTools],
81851
- { getContext: getPolicyContext }
82091
+ { getContext: this.getPolicyContext }
81852
82092
  ),
81853
82093
  originalTools: createPolicyEnforcedTools(
81854
82094
  [...normalFsTools, bashTool, ...preservedTools],
81855
- { getContext: getPolicyContext }
82095
+ { getContext: this.getPolicyContext }
81856
82096
  )
81857
82097
  };
81858
82098
  }
@@ -81860,26 +82100,27 @@ var InteractiveMode = class {
81860
82100
  this.options.fileObservationState.clear();
81861
82101
  }
81862
82102
  getRuntimeModeState() {
81863
- const pendingTransition = this.getPendingRuntimeModeTransition();
81864
82103
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone || "local";
81865
- const dateParts = new Intl.DateTimeFormat("en-US", {
81866
- year: "numeric",
81867
- month: "2-digit",
81868
- day: "2-digit",
81869
- timeZone
81870
- }).formatToParts(/* @__PURE__ */ new Date());
81871
- const datePart = (type) => dateParts.find((part) => part.type === type)?.value ?? "00";
81872
- const currentDate = `${datePart("year")}-${datePart("month")}-${datePart("day")}`;
82104
+ const activeTools = this.agent?.state?.tools ?? [];
81873
82105
  return {
81874
82106
  yoloMode: this.yoloMode,
81875
82107
  planMode: this.planModeEnabled ? "planning" : "off",
81876
82108
  filesystemScope: this.yoloMode ? "all" : "workspace",
81877
- ...pendingTransition ? { pendingTransition } : {},
81878
- currentDate,
82109
+ ...this.getPendingRuntimeModeTransition() ? { pendingTransition: this.getPendingRuntimeModeTransition() } : {},
82110
+ currentDate: currentDateInZone(timeZone),
81879
82111
  timeZone,
81880
- 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) : []
81881
82114
  };
81882
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
+ }
81883
82124
  getPendingRuntimeModeTransition() {
81884
82125
  if (this.pendingYoloModeTransition === "enter") return "entered_yolo";
81885
82126
  if (this.pendingYoloModeTransition === "exit") return "exited_yolo";
@@ -81898,10 +82139,7 @@ var InteractiveMode = class {
81898
82139
  const currentTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.currentTools, this.extensionRunner) : rebuilt.currentTools;
81899
82140
  const originalTools = this.extensionRunner ? wrapToolsWithExtensions(rebuilt.originalTools, this.extensionRunner) : rebuilt.originalTools;
81900
82141
  const activeTools = filterEnabledTools(
81901
- selectToolsForRuntimeMode(
81902
- currentTools,
81903
- this.planModeEnabled ? "plan" : "normal"
81904
- ),
82142
+ currentTools,
81905
82143
  this.options.settingsManager
81906
82144
  );
81907
82145
  this.agent.setTools(activeTools);
@@ -81934,7 +82172,8 @@ var InteractiveMode = class {
81934
82172
  this.safeRequestRender();
81935
82173
  }
81936
82174
  promptAgent(input) {
81937
- return this.agentSessionController.prompt(input, { source: "interactive" });
82175
+ const prompt = prependRuntimeModeBlockToPrompt(input, this.runtimeModeBlockForNextMessage());
82176
+ return this.agentSessionController.prompt(prompt, { source: "interactive" });
81938
82177
  }
81939
82178
  continueAgent() {
81940
82179
  return this.agentSessionController.continue();
@@ -81964,6 +82203,7 @@ var InteractiveMode = class {
81964
82203
  this.persistExecutionState();
81965
82204
  }
81966
82205
  this.executionReminder = this.executionState.getReminder();
82206
+ this.resetRuntimeModeBlocks();
81967
82207
  await this.refreshTodoWidget();
81968
82208
  await this.emitExtensionEvent({
81969
82209
  type: "session:changed",
@@ -81974,7 +82214,7 @@ var InteractiveMode = class {
81974
82214
  async init() {
81975
82215
  const activeSession = this.sessionManager.getActiveSession();
81976
82216
  this.thinkingLevel = resolveInitialThinkingLevel({ session: activeSession, settingsManager: this.options.settingsManager });
81977
- const { Agent } = await import("./chunks/src-OWQI5M6W.js");
82217
+ const { Agent } = await import("./chunks/src-E7NCXMU5.js");
81978
82218
  this.agent = new Agent({
81979
82219
  initialState: {
81980
82220
  systemPrompt: this.buildSystemPrompt(),
@@ -81984,28 +82224,15 @@ var InteractiveMode = class {
81984
82224
  },
81985
82225
  apiKey: this.options.apiKey,
81986
82226
  sessionId: activeSession?.id,
81987
- streamFn: this.options.streamFn,
81988
- transformContext: async (messages) => {
81989
- const runtimeModeTransform = createRuntimeModeTransformContext({
81990
- getState: () => this.getRuntimeModeState(),
81991
- consumeTransition: () => this.consumeRuntimeModeTransition(),
81992
- onInjected: (state) => this.sessionTrace.noteRuntimeEvent("runtime_context_injected", {
81993
- yoloMode: state.yoloMode,
81994
- planMode: state.planMode,
81995
- filesystemScope: state.filesystemScope,
81996
- pendingTransition: state.pendingTransition,
81997
- currentDate: state.currentDate,
81998
- timeZone: state.timeZone,
81999
- activeToolCount: state.activeToolNames.length
82000
- }),
82001
- onStripped: (count) => this.sessionTrace.noteRuntimeEvent("stale_runtime_context_stripped", { count })
82002
- });
82003
- let transformed = await runtimeModeTransform(messages);
82004
- const advisory = this.pendingRuntimeGuardAdvisory;
82005
- this.pendingRuntimeGuardAdvisory = null;
82006
- transformed = advisory ? injectGuardAdvisory(transformed, advisory) : stripGuardAdvisories(transformed);
82007
- return transformed;
82008
- }
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
+ })
82009
82236
  });
82010
82237
  this.agentSessionController = createInteractiveAgentSessionController(this.controllerSetupHost);
82011
82238
  this.updateTerminalTitle();
@@ -82189,6 +82416,9 @@ var InteractiveMode = class {
82189
82416
  async handleYoloCommand(args) {
82190
82417
  return handleYoloCommand(this, args);
82191
82418
  }
82419
+ setYoloMode(enabled) {
82420
+ void this.handleYoloCommand(enabled ? "on" : "off");
82421
+ }
82192
82422
  /**
82193
82423
  * Toggle calm mode and rebuild the chat so already-rendered rows follow
82194
82424
  * the new mode. Delivery, tool execution, and session storage are
@@ -82560,6 +82790,7 @@ async function composeCliRuntime(params) {
82560
82790
  getPolicyContext: () => ({ mode: "normal", confirmationPolicy: "allow" })
82561
82791
  };
82562
82792
  const rawTools = [
82793
+ createSearchContextTool(() => sessionManager),
82563
82794
  ...createCliFileSystemTools(cwd, fileSystemToolOptions),
82564
82795
  ...cliHostCapabilities.tools
82565
82796
  ];
@@ -82805,7 +83036,7 @@ async function runPrintMode(params) {
82805
83036
  `);
82806
83037
  console.log("Assistant: ");
82807
83038
  }
82808
- const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
83039
+ const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
82809
83040
  let agent;
82810
83041
  const printModeExtensions = await loadPrintModeExtensions({
82811
83042
  cwd: runtime.cwd,
@@ -83045,7 +83276,7 @@ function toJsonValue(value, depth = 0, cap = MAX_EVENT_STRING_LENGTH) {
83045
83276
  }
83046
83277
  return result;
83047
83278
  }
83048
- function isRecord4(value) {
83279
+ function isRecord3(value) {
83049
83280
  return !!value && typeof value === "object" && !Array.isArray(value);
83050
83281
  }
83051
83282
  function truncateForEvent(text, cap) {
@@ -83071,7 +83302,7 @@ function parseInput(line) {
83071
83302
  } catch {
83072
83303
  return null;
83073
83304
  }
83074
- if (!isRecord4(parsed) || typeof parsed.type !== "string") return null;
83305
+ if (!isRecord3(parsed) || typeof parsed.type !== "string") return null;
83075
83306
  if (parsed.type === "shutdown") {
83076
83307
  return { type: "shutdown", reason: typeof parsed.reason === "string" ? parsed.reason : void 0 };
83077
83308
  }
@@ -83122,7 +83353,7 @@ function parseInput(line) {
83122
83353
  prompt: parsed.prompt,
83123
83354
  streamingBehavior,
83124
83355
  cwd: typeof parsed.cwd === "string" ? parsed.cwd : void 0,
83125
- metadata: isRecord4(parsed.metadata) ? parsed.metadata : void 0
83356
+ metadata: isRecord3(parsed.metadata) ? parsed.metadata : void 0
83126
83357
  };
83127
83358
  }
83128
83359
  return null;
@@ -83162,15 +83393,15 @@ function readProgressText(value) {
83162
83393
  function extractJsonEventsToolProgressText(partialResult) {
83163
83394
  const plainText = readProgressText(partialResult).trim();
83164
83395
  if (plainText) return plainText;
83165
- if (!isRecord4(partialResult)) return "";
83396
+ if (!isRecord3(partialResult)) return "";
83166
83397
  const content = partialResult.content;
83167
83398
  if (Array.isArray(content)) {
83168
- 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();
83169
83400
  }
83170
83401
  const directText = readProgressText(partialResult.text) || readProgressText(partialResult.message);
83171
83402
  if (directText) return directText.trim();
83172
83403
  const details = partialResult.details;
83173
- if (isRecord4(details)) {
83404
+ if (isRecord3(details)) {
83174
83405
  return (readProgressText(details.text) || readProgressText(details.message)).trim();
83175
83406
  }
83176
83407
  return "";
@@ -83666,7 +83897,7 @@ async function runJsonEventsMode(params) {
83666
83897
  return;
83667
83898
  }
83668
83899
  const parent = agent;
83669
- const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
83900
+ const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
83670
83901
  const sideAgent = new AgentClass({
83671
83902
  initialState: {
83672
83903
  systemPrompt: parent.state.systemPrompt,
@@ -84010,7 +84241,7 @@ async function runJsonEventsMode(params) {
84010
84241
  if (runtime.initialModel.unconfigured || controller) {
84011
84242
  return;
84012
84243
  }
84013
- const { Agent: AgentClass } = await import("./chunks/src-OWQI5M6W.js");
84244
+ const { Agent: AgentClass } = await import("./chunks/src-E7NCXMU5.js");
84014
84245
  const jsonModeExtensions = await loadJsonModeExtensions({ runtime, getAgent: () => agent });
84015
84246
  extensionRunner = jsonModeExtensions.runner;
84016
84247
  const tools = [...runtime.tools, ...jsonModeExtensions.extensionTools];
@@ -84604,7 +84835,7 @@ async function runWithJsonEventsConsoleIsolation(fn) {
84604
84835
  }
84605
84836
  async function createCliTestStreamFnFromEnv() {
84606
84837
  if (!process.env.PIE_TEST_MOCK_STREAM_SEQUENCE) return void 0;
84607
- return (await import("./chunks/test-stream-Y7WXELAJ.js")).createTestStreamFnFromSequenceEnv();
84838
+ return (await import("./chunks/test-stream-BWBJTFNI.js")).createTestStreamFnFromSequenceEnv();
84608
84839
  }
84609
84840
  function resolveJsonEventsDetailFlag() {
84610
84841
  for (let index = 0; index < process.argv.length; index += 1) {