pi-cache-optimizer 2.8.14 → 2.8.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -97,6 +97,20 @@ The interactive `/cache-optimizer` menu includes `Footer mode`, where you can ch
97
97
  | `PI_CACHE_OPTIMIZER_NO_OPENAI_CACHE_KEY=1` | Disable the OpenAI-compatible `prompt_cache_key` fallback. Preferred explicit opt-out. |
98
98
  | `PI_CACHE_OPTIMIZER_OPENAI_CACHE_KEY=0` | Disable the same fallback via the legacy inverse switch. Values `0`, `false`, `no`, or `off` disable it. |
99
99
 
100
+ Persistent feature settings are managed under the Pi agent directory by the native command interface. They take precedence over their corresponding environment variables:
101
+
102
+ ```text
103
+ /cache-optimizer config prompt-rewrite on|off
104
+ /cache-optimizer config virtual-rewrite on|off
105
+ /cache-optimizer config skill-compression on|off
106
+ /cache-optimizer config openai-cache-key on|off
107
+ /cache-optimizer config tool-order on|off
108
+ /cache-optimizer config footer-mode total|session|process
109
+ /cache-optimizer config reset
110
+ ```
111
+
112
+ `config reset` removes persistent feature overrides while preserving the existing footer mode and model-specific prompt-cache-key settings. These commands do not modify shell startup files or `PI_CACHE_RETENTION`; `enable` and `disable` remain current-process runtime switches.
113
+
100
114
  ## Opt-in deterministic tool ordering
101
115
 
102
116
  `PI_CACHE_OPTIMIZER_TOOL_ORDER=1` enables deterministic ordering for verified tool definitions in Pi's built-in OpenAI Completions, Anthropic, Google, and Bedrock payload shapes. Truthy values are `1`, `true`, `yes`, or `on` (case-insensitive). The feature is off by default, process-local, and suppressed by `/cache-optimizer disable`.
package/README.zh-CN.md CHANGED
@@ -97,6 +97,20 @@ Pi 0.79.7 及之后,`pi update` 默认只更新 Pi 本体。若要更新已安
97
97
  | `PI_CACHE_OPTIMIZER_NO_OPENAI_CACHE_KEY=1` | 关闭 OpenAI-compatible `prompt_cache_key` fallback。推荐使用这个显式 opt-out。 |
98
98
  | `PI_CACHE_OPTIMIZER_OPENAI_CACHE_KEY=0` | 通过旧的反向开关关闭同一个 fallback。取值 `0`、`false`、`no`、`off` 时关闭。 |
99
99
 
100
+ 持久化 feature 配置由 Pi agent 目录中的原生命令管理,并且优先于对应环境变量:
101
+
102
+ ```text
103
+ /cache-optimizer config prompt-rewrite on|off
104
+ /cache-optimizer config virtual-rewrite on|off
105
+ /cache-optimizer config skill-compression on|off
106
+ /cache-optimizer config openai-cache-key on|off
107
+ /cache-optimizer config tool-order on|off
108
+ /cache-optimizer config footer-mode total|session|process
109
+ /cache-optimizer config reset
110
+ ```
111
+
112
+ `config reset` 会移除持久化 feature 覆盖,同时保留现有 footer mode 和按模型配置的 prompt-cache-key 设置。这些命令不会修改 shell 启动文件或 `PI_CACHE_RETENTION`;`enable` 和 `disable` 仍然是当前进程的运行时开关。
113
+
100
114
  ## Opt-in 确定性工具排序
101
115
 
102
116
  `PI_CACHE_OPTIMIZER_TOOL_ORDER=1` 会对 Pi 内置 OpenAI Completions、Anthropic、Google 与 Bedrock payload 中已验证的工具定义进行确定性排序。Truthy 取值为(不区分大小写)`1`、`true`、`yes` 或 `on`。该能力默认关闭、仅在当前进程生效,并会被 `/cache-optimizer disable` 抑制。
package/index.ts CHANGED
@@ -180,6 +180,7 @@ type PersistedCacheOptimizerConfigV1 = {
180
180
  version: 1;
181
181
  footerMode?: FooterStatsMode;
182
182
  };
183
+ type PersistedCacheOptimizerFeature = "promptRewrite" | "virtualRewrite" | "skillCompression" | "openAICacheKey" | "toolOrder";
183
184
  type PersistedCacheOptimizerConfigV2 = {
184
185
  version: 2;
185
186
  footerMode?: FooterStatsMode;
@@ -187,7 +188,13 @@ type PersistedCacheOptimizerConfigV2 = {
187
188
  omit?: string[];
188
189
  };
189
190
  };
190
- type PersistedCacheOptimizerConfig = PersistedCacheOptimizerConfigV1 | PersistedCacheOptimizerConfigV2;
191
+ type PersistedCacheOptimizerConfigV3 = {
192
+ version: 2 | 3;
193
+ footerMode?: FooterStatsMode;
194
+ promptCacheKey?: { omit?: string[] };
195
+ features?: Partial<Record<PersistedCacheOptimizerFeature, boolean>>;
196
+ };
197
+ type PersistedCacheOptimizerConfig = PersistedCacheOptimizerConfigV1 | PersistedCacheOptimizerConfigV2 | PersistedCacheOptimizerConfigV3;
191
198
  type PromptCacheKeyConfigReceipt = {
192
199
  version: 2;
193
200
  kind: "pi-cache-optimizer-config-receipt";
@@ -923,7 +930,7 @@ function compressSkillsInSystemPrompt(
923
930
  prompt: string,
924
931
  opts: BuildSystemPromptOptions,
925
932
  ): string {
926
- if (isEnabledEnv(process.env[NO_SKILL_COMPRESSION_ENV])) return prompt;
933
+ if (!featureEnabled("skillCompression", NO_SKILL_COMPRESSION_ENV, true)) return prompt;
927
934
  if (!opts.skills || opts.skills.length === 0) return prompt;
928
935
 
929
936
  const visible = opts.skills.filter((skill) => !skill.disableModelInvocation);
@@ -1365,7 +1372,7 @@ function canRewriteNativeVirtualPrompt(
1365
1372
  model: PiModel | undefined,
1366
1373
  ctx?: ContextWithOptionalModelRegistry,
1367
1374
  ): boolean {
1368
- if (!isNativeVirtualModel(model) || !isEnabledEnv(process.env[VIRTUAL_REWRITE_ENV])) return false;
1375
+ if (!isNativeVirtualModel(model) || !featureEnabled("virtualRewrite", VIRTUAL_REWRITE_ENV, false)) return false;
1369
1376
  const candidates = resolveNativeVirtualCandidateModels(model, ctx);
1370
1377
  return !!candidates?.length && candidates.every((candidate) =>
1371
1378
  candidate.api === "openai-completions" || candidate.api === "anthropic-messages",
@@ -2082,8 +2089,18 @@ function isEnabledEnv(value: string | undefined): boolean {
2082
2089
  return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
2083
2090
  }
2084
2091
 
2092
+ function featureEnabled(feature: PersistedCacheOptimizerFeature, envName: string, defaultValue: boolean): boolean {
2093
+ const configured = (persistedCacheOptimizerConfig as PersistedCacheOptimizerConfigV3).features?.[feature];
2094
+ if (configured !== undefined) return configured;
2095
+ if (feature === "virtualRewrite") return isEnabledEnv(process.env[envName]);
2096
+ if (feature === "promptRewrite" || feature === "skillCompression" || feature === "openAICacheKey") {
2097
+ return !isEnabledEnv(process.env[envName]);
2098
+ }
2099
+ return isEnabledEnv(process.env[envName]) || defaultValue;
2100
+ }
2101
+
2085
2102
  function isToolOrderEnabled(env: MutableEnv = process.env): boolean {
2086
- return runtimeOptimizerEnabled && isEnabledEnv(env[TOOL_ORDER_ENV]);
2103
+ return runtimeOptimizerEnabled && featureEnabled("toolOrder", TOOL_ORDER_ENV, false);
2087
2104
  }
2088
2105
 
2089
2106
  function parseFooterStatsMode(value: unknown): FooterStatsMode | undefined {
@@ -2109,8 +2126,17 @@ const CACHE_OPTIMIZER_COMMANDS = [
2109
2126
  "fix",
2110
2127
  "rollback",
2111
2128
  ] as const;
2112
- const CACHE_OPTIMIZER_CONFIG_ARGUMENTS = ["footer-mode"] as const;
2129
+ const CACHE_OPTIMIZER_CONFIG_ARGUMENTS = ["footer-mode", "prompt-rewrite", "virtual-rewrite", "skill-compression", "openai-cache-key", "tool-order", "reset"] as const;
2130
+ const CACHE_OPTIMIZER_FEATURE_COMMANDS = ["prompt-rewrite", "virtual-rewrite", "skill-compression", "openai-cache-key", "tool-order"] as const;
2131
+ const CACHE_OPTIMIZER_FEATURE_VALUES = ["on", "off"] as const;
2113
2132
  const CACHE_OPTIMIZER_FOOTER_MODES = ["total", "session", "process"] as const;
2133
+ const FEATURE_COMMAND_MAP: Record<string, PersistedCacheOptimizerFeature> = {
2134
+ "prompt-rewrite": "promptRewrite",
2135
+ "virtual-rewrite": "virtualRewrite",
2136
+ "skill-compression": "skillCompression",
2137
+ "openai-cache-key": "openAICacheKey",
2138
+ "tool-order": "toolOrder",
2139
+ };
2114
2140
  const CACHE_OPTIMIZER_STATS_ARGUMENTS = ["all", "contributors"] as const;
2115
2141
  const CACHE_OPTIMIZER_FIX_ARGUMENTS = ["prompt-cache-key"] as const;
2116
2142
 
@@ -2182,19 +2208,28 @@ function getCacheOptimizerArgumentCompletions(argumentPrefix: string): CommandCo
2182
2208
  if (parts.length === 3 && parts[1].toLowerCase() === "footer-mode") {
2183
2209
  return filterCommandCompletionItems(CACHE_OPTIMIZER_FOOTER_MODES, parts[2], "config footer-mode");
2184
2210
  }
2211
+ if (parts.length === 3 && CACHE_OPTIMIZER_FEATURE_COMMANDS.includes(parts[1] as typeof CACHE_OPTIMIZER_FEATURE_COMMANDS[number])) {
2212
+ return filterCommandCompletionItems(CACHE_OPTIMIZER_FEATURE_VALUES, parts[2], `config ${parts[1]}`);
2213
+ }
2185
2214
 
2186
2215
  return null;
2187
2216
  }
2188
2217
 
2189
2218
  function parsePersistedCacheOptimizerConfig(value: unknown): PersistedCacheOptimizerConfig | undefined {
2190
2219
  const record = asRecord(value);
2191
- if (!record || (record.version !== 1 && record.version !== 2)) return undefined;
2192
- const allowedTopLevel = new Set(record.version === 1 ? ["version", "footerMode"] : ["version", "footerMode", "promptCacheKey"]);
2220
+ if (!record || (record.version !== 1 && record.version !== 2 && record.version !== 3)) return undefined;
2221
+ const allowedTopLevel = new Set(record.version === 1 ? ["version", "footerMode"] : record.version === 2 ? ["version", "footerMode", "promptCacheKey"] : ["version", "footerMode", "promptCacheKey", "features"]);
2193
2222
  if (Object.keys(record).some((key) => !allowedTopLevel.has(key))) return undefined;
2194
2223
  const footerMode = parseFooterStatsMode(record.footerMode);
2195
2224
  if (record.footerMode !== undefined && !footerMode) return undefined;
2196
2225
  if (record.version === 1) return { version: 1, ...(footerMode ? { footerMode } : {}) };
2197
2226
 
2227
+ const rawFeatures = record.version === 3 ? record.features : undefined;
2228
+ if (rawFeatures !== undefined && !asRecord(rawFeatures)) return undefined;
2229
+ const featuresRecord = asRecord(rawFeatures);
2230
+ const featureNames = new Set<PersistedCacheOptimizerFeature>(["promptRewrite", "virtualRewrite", "skillCompression", "openAICacheKey", "toolOrder"]);
2231
+ if (featuresRecord && Object.keys(featuresRecord).some((key) => !featureNames.has(key as PersistedCacheOptimizerFeature) || typeof featuresRecord[key] !== "boolean")) return undefined;
2232
+
2198
2233
  const rawPromptCacheKey = record.promptCacheKey;
2199
2234
  if (rawPromptCacheKey !== undefined && !asRecord(rawPromptCacheKey)) return undefined;
2200
2235
  const promptCacheKey = asRecord(rawPromptCacheKey);
@@ -2204,20 +2239,25 @@ function parsePersistedCacheOptimizerConfig(value: unknown): PersistedCacheOptim
2204
2239
  const stringOmit = omit as string[] | undefined;
2205
2240
  const uniqueOmit = stringOmit ? [...new Set(stringOmit.map((value) => value.trim()))].sort() : undefined;
2206
2241
  return {
2207
- version: 2,
2242
+ version: record.version === 3 ? 3 : 2,
2208
2243
  ...(footerMode ? { footerMode } : {}),
2209
2244
  ...(uniqueOmit && uniqueOmit.length > 0 ? { promptCacheKey: { omit: uniqueOmit } } : {}),
2210
- };
2245
+ ...(featuresRecord ? { features: Object.fromEntries(Object.entries(featuresRecord)) as Partial<Record<PersistedCacheOptimizerFeature, boolean>> } : {}),
2246
+ } as PersistedCacheOptimizerConfigV3;
2211
2247
  }
2212
2248
 
2213
- function normalizePersistedCacheOptimizerConfig(value: PersistedCacheOptimizerConfig | undefined): PersistedCacheOptimizerConfigV2 {
2249
+ function normalizePersistedCacheOptimizerConfig(value: PersistedCacheOptimizerConfig | undefined): PersistedCacheOptimizerConfigV3 {
2214
2250
  if (!value) return { version: 2 };
2215
- return value.version === 2
2216
- ? value
2217
- : { version: 2, ...(value.footerMode ? { footerMode: value.footerMode } : {}) };
2251
+ const features = value.version === 3 ? value.features : undefined;
2252
+ return {
2253
+ version: features ? 3 : 2,
2254
+ ...(value.footerMode ? { footerMode: value.footerMode } : {}),
2255
+ ...(value.version !== 1 && value.promptCacheKey ? { promptCacheKey: value.promptCacheKey } : {}),
2256
+ ...(features ? { features } : {}),
2257
+ };
2218
2258
  }
2219
2259
 
2220
- function readPersistedCacheOptimizerConfig(configPath: string = CONFIG_FILE_PATH): PersistedCacheOptimizerConfigV2 {
2260
+ function readPersistedCacheOptimizerConfig(configPath: string = CONFIG_FILE_PATH): PersistedCacheOptimizerConfigV3 {
2221
2261
  try {
2222
2262
  return normalizePersistedCacheOptimizerConfig(parsePersistedCacheOptimizerConfig(JSON.parse(readFileSync(configPath, "utf8"))));
2223
2263
  } catch (error) {
@@ -2231,7 +2271,7 @@ function readPersistedFooterMode(configPath: string = CONFIG_FILE_PATH): FooterS
2231
2271
  }
2232
2272
 
2233
2273
  async function writePersistedCacheOptimizerConfigUnlocked(
2234
- config: PersistedCacheOptimizerConfigV2,
2274
+ config: PersistedCacheOptimizerConfig | PersistedCacheOptimizerConfigV3,
2235
2275
  configPath: string,
2236
2276
  ): Promise<void> {
2237
2277
  await mkdir(dirname(configPath), { recursive: true });
@@ -2261,7 +2301,7 @@ async function writePersistedCacheOptimizerConfigUnlocked(
2261
2301
  }
2262
2302
 
2263
2303
  async function writePersistedCacheOptimizerConfig(
2264
- config: PersistedCacheOptimizerConfigV2,
2304
+ config: PersistedCacheOptimizerConfig | PersistedCacheOptimizerConfigV3,
2265
2305
  configPath: string = CONFIG_FILE_PATH,
2266
2306
  ): Promise<void> {
2267
2307
  await withModelsJsonTransactionLock(() => writePersistedCacheOptimizerConfigUnlocked(config, configPath));
@@ -2271,14 +2311,14 @@ async function writePersistedFooterModeUnlocked(
2271
2311
  mode: FooterStatsMode,
2272
2312
  configPath: string,
2273
2313
  ): Promise<void> {
2274
- let version: 1 | 2 = 1;
2314
+ let version: 1 | 2 | 3 = 1;
2275
2315
  let raw: PersistedCacheOptimizerConfig | undefined;
2276
2316
  let targetExists = false;
2277
2317
  try {
2278
2318
  raw = parsePersistedCacheOptimizerConfig(JSON.parse(readFileSync(configPath, "utf8")));
2279
2319
  targetExists = true;
2280
2320
  if (!raw) throw new Error("invalid footer config schema");
2281
- version = raw.version === 2 ? 2 : 1;
2321
+ version = raw.version === 3 ? 3 : raw.version === 2 ? 2 : 1;
2282
2322
  } catch (error) {
2283
2323
  if (getErrorCode(error) !== "ENOENT") throw new Error("invalid footer config schema");
2284
2324
  }
@@ -2301,7 +2341,7 @@ async function writePersistedFooterModeUnlocked(
2301
2341
  }
2302
2342
  return;
2303
2343
  }
2304
- await writePersistedCacheOptimizerConfigUnlocked({ ...current, version: 2, footerMode: mode }, configPath);
2344
+ await writePersistedCacheOptimizerConfigUnlocked({ ...current, version: 3, footerMode: mode } as PersistedCacheOptimizerConfigV3, configPath);
2305
2345
  }
2306
2346
 
2307
2347
  async function writePersistedFooterMode(
@@ -2311,6 +2351,17 @@ async function writePersistedFooterMode(
2311
2351
  await withModelsJsonTransactionLock(() => writePersistedFooterModeUnlocked(mode, configPath));
2312
2352
  }
2313
2353
 
2354
+ async function writePersistedFeature(feature: PersistedCacheOptimizerFeature, enabled: boolean): Promise<void> {
2355
+ const current = readPersistedCacheOptimizerConfig();
2356
+ const next: PersistedCacheOptimizerConfigV3 = {
2357
+ ...current,
2358
+ version: 3,
2359
+ features: { ...(current.features ?? {}), [feature]: enabled },
2360
+ };
2361
+ await writePersistedCacheOptimizerConfig(next);
2362
+ setPersistedCacheOptimizerConfig(readPersistedCacheOptimizerConfig());
2363
+ }
2364
+
2314
2365
  function configReceiptBackupPath(receipt: PromptCacheKeyConfigReceipt, receiptPath: string = CONFIG_RECEIPT_PATH): string {
2315
2366
  return join(dirname(receiptPath), receipt.backupFile);
2316
2367
  }
@@ -2703,7 +2754,7 @@ function isDisabledEnv(value: string | undefined): boolean {
2703
2754
 
2704
2755
  function shouldInjectOpenAIPromptCacheKey(): boolean {
2705
2756
  if (!runtimeOptimizerEnabled) return false;
2706
- if (isEnabledEnv(process.env[NO_OPENAI_CACHE_KEY_ENV])) return false;
2757
+ if (!featureEnabled("openAICacheKey", NO_OPENAI_CACHE_KEY_ENV, true)) return false;
2707
2758
  if (isDisabledEnv(process.env[OPENAI_CACHE_KEY_ENV])) return false;
2708
2759
  return true;
2709
2760
  }
@@ -2725,7 +2776,9 @@ function getOptimizerRuntimeModeLines(): string[] {
2725
2776
  const state = runtimeOptimizerEnabled ? "enabled" : "disabled";
2726
2777
  const lines: string[] = [];
2727
2778
  lines.push(`Runtime state: ${state}`);
2728
- lines.push(`• Prompt rewrite: ${runtimeOptimizerEnabled && !isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV]) ? "on" : "off"}`);
2779
+ lines.push(`• Prompt rewrite: ${runtimeOptimizerEnabled && featureEnabled("promptRewrite", NO_PROMPT_REWRITE_ENV, true) ? "on" : "off"}`);
2780
+ lines.push(`• Native virtual rewrite: ${featureEnabled("virtualRewrite", VIRTUAL_REWRITE_ENV, false) ? "opt-in" : "off"}`);
2781
+ lines.push(`• Skill compression: ${featureEnabled("skillCompression", NO_SKILL_COMPRESSION_ENV, true) ? "on" : "off"}`);
2729
2782
  lines.push(`• Deterministic tool ordering: ${isToolOrderEnabled() ? "on (verified built-in payloads, opt-in)" : "off"}`);
2730
2783
  lines.push(`• OpenAI prompt_cache_key fallback: ${shouldInjectOpenAIPromptCacheKey() ? "on" : "off"}`);
2731
2784
  lines.push(`• Footer cache stats: on${runtimeOptimizerEnabled ? "" : " (comparison mode)"}`);
@@ -2733,7 +2786,7 @@ function getOptimizerRuntimeModeLines(): string[] {
2733
2786
  lines.push(`• ${PI_CACHE_RETENTION_ENV}: ${process.env[PI_CACHE_RETENTION_ENV] ?? "(unset)"}`);
2734
2787
  if (!runtimeOptimizerEnabled) {
2735
2788
  lines.push("This is a current-process switch. Run /reload or restart Pi to return to startup behavior.");
2736
- } else if (isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV]) || !shouldInjectOpenAIPromptCacheKey()) {
2789
+ } else if (!featureEnabled("promptRewrite", NO_PROMPT_REWRITE_ENV, true) || !shouldInjectOpenAIPromptCacheKey()) {
2737
2790
  lines.push("Some features are still disabled by environment variables.");
2738
2791
  }
2739
2792
  return lines;
@@ -2820,12 +2873,12 @@ function shouldInjectOpenAIPromptCacheKeyForModel(model: PiModel | undefined): b
2820
2873
  return isOpenAICompatibleApi(model?.api);
2821
2874
  }
2822
2875
 
2823
- function isPromptCacheKeyOmittedForModel(model: PiModel | undefined, config: PersistedCacheOptimizerConfigV2 = persistedCacheOptimizerConfig): boolean {
2876
+ function isPromptCacheKeyOmittedForModel(model: PiModel | undefined, config: PersistedCacheOptimizerConfig | PersistedCacheOptimizerConfigV3 = persistedCacheOptimizerConfig): boolean {
2824
2877
  if (!model || !isOpenAICompatibleApi(model.api)) return false;
2825
- return config.promptCacheKey?.omit?.includes(modelKey(model)) === true;
2878
+ return "promptCacheKey" in config && config.promptCacheKey?.omit?.includes(modelKey(model)) === true;
2826
2879
  }
2827
2880
 
2828
- function setPersistedCacheOptimizerConfig(config: PersistedCacheOptimizerConfigV2): void {
2881
+ function setPersistedCacheOptimizerConfig(config: PersistedCacheOptimizerConfig | PersistedCacheOptimizerConfigV3): void {
2829
2882
  persistedCacheOptimizerConfig = normalizePersistedCacheOptimizerConfig(config);
2830
2883
  persistedFooterStatsMode = persistedCacheOptimizerConfig.footerMode;
2831
2884
  }
@@ -11252,7 +11305,7 @@ export default function (pi: ExtensionAPI) {
11252
11305
  // prompt mutations below (session-overview churn strip, skill compression,
11253
11306
  // and stable-prefix reordering). Footer stats and the OpenAI
11254
11307
  // prompt_cache_key fallback remain active.
11255
- if (isEnabledEnv(process.env[NO_PROMPT_REWRITE_ENV])) return {};
11308
+ if (!featureEnabled("promptRewrite", NO_PROMPT_REWRITE_ENV, true)) return {};
11256
11309
 
11257
11310
  // Step 1: strip per-turn churn from <session-overview>.
11258
11311
  // Removing RECENT COMMITS, Working directory status, and
@@ -11821,10 +11874,34 @@ export default function (pi: ExtensionAPI) {
11821
11874
  } else if (subcommand === "config") {
11822
11875
  const configKey = commandParts[1];
11823
11876
  const requestedMode = commandParts[2];
11877
+ const feature = configKey ? FEATURE_COMMAND_MAP[configKey] : undefined;
11878
+ if (feature && (requestedMode === "on" || requestedMode === "off")) {
11879
+ try {
11880
+ await writePersistedFeature(feature, requestedMode === "on");
11881
+ lastStatusText = undefined;
11882
+ await publishStatus(cmdCtx, model);
11883
+ cmdCtx.ui.notify(`✅ ${configKey} set to ${requestedMode}. Persistent config overrides its environment variable.`, "info");
11884
+ } catch (error) {
11885
+ cmdCtx.ui.notify(`❌ Could not update ${configKey}: ${error instanceof Error ? error.message : String(error)}`, "error");
11886
+ }
11887
+ return;
11888
+ }
11889
+ if (configKey === "reset") {
11890
+ try {
11891
+ await writePersistedCacheOptimizerConfig({ version: 2, footerMode: persistedFooterStatsMode, promptCacheKey: persistedCacheOptimizerConfig.promptCacheKey });
11892
+ setPersistedCacheOptimizerConfig(readPersistedCacheOptimizerConfig());
11893
+ cmdCtx.ui.notify("✅ Feature configuration reset. Environment variables now apply again.", "info");
11894
+ } catch (error) {
11895
+ cmdCtx.ui.notify(`❌ Could not reset feature configuration: ${error instanceof Error ? error.message : String(error)}`, "error");
11896
+ }
11897
+ return;
11898
+ }
11824
11899
  if (configKey !== "footer-mode" || !requestedMode || !["session", "total", "process"].includes(requestedMode)) {
11825
11900
  const resolved = resolveFooterStatsMode(persistedFooterStatsMode);
11826
11901
  cmdCtx.ui.notify(
11827
11902
  `Usage: /cache-optimizer config footer-mode total|session|process\n` +
11903
+ ` /cache-optimizer config prompt-rewrite|virtual-rewrite|skill-compression|openai-cache-key|tool-order on|off\n` +
11904
+ ` /cache-optimizer config reset\n` +
11828
11905
  `Current footer mode: ${resolved.mode} (${resolved.source})`,
11829
11906
  "info",
11830
11907
  );
@@ -12641,7 +12718,9 @@ export default function (pi: ExtensionAPI) {
12641
12718
  diagnosis.push(" stats all — Show detailed totals for every model across all local sessions");
12642
12719
  diagnosis.push(" stats contributors — Show per-session contributors for the active model");
12643
12720
  diagnosis.push(" compat — Show compat suggestion with edit location");
12721
+ diagnosis.push(" config prompt-rewrite|virtual-rewrite|skill-compression|openai-cache-key|tool-order on|off — Persist feature settings");
12644
12722
  diagnosis.push(" config footer-mode total|session|process — Persist the footer stats mode");
12723
+ diagnosis.push(" config reset — Remove persistent feature overrides");
12645
12724
  diagnosis.push(" fix — Auto-fix compat issues (writes models.json or extension config, requires UI)");
12646
12725
  diagnosis.push(" fix prompt-cache-key — Explicitly omit prompt_cache_key for the active model");
12647
12726
  diagnosis.push(" rollback — Undo the latest confirmed fix (requires UI confirmation)");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cache-optimizer",
3
- "version": "2.8.14",
3
+ "version": "2.8.15",
4
4
  "description": "Improve Pi prompt/KV cache hit rates with stable prompts, OpenAI-compatible cache keys, proxy compat warnings, and footer cache stats.",
5
5
  "keywords": [
6
6
  "pi-package",