pi-cache-optimizer 2.7.0 → 2.8.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.
package/README.md CHANGED
@@ -68,10 +68,10 @@ This extension is validated against Pi 0.83.0 and remains designed for Pi 0.82+.
68
68
  | `/cache-optimizer compat` | Shows copyable compat advice for the active model, if applicable. |
69
69
  | `/cache-optimizer stats` | Shows today's local provider/model counters and recent trend for the active model. |
70
70
  | `/cache-optimizer reset` | Resets local footer stats for the active provider/model; upstream provider cache is not modified. |
71
- | `/cache-optimizer config footer-mode session\|total` | Persist the footer stats mode. Persistent command configuration overrides the environment variable. |
71
+ | `/cache-optimizer config footer-mode total\|session\|process` | Persist the footer stats mode. Persistent command configuration overrides the environment variable. |
72
72
  | `/cache-optimizer fix` | Auto-repairs safe compat issues for the active model (adaptive thinking, DeepSeek reasoning, OpenAI proxy session affinity). Shows preview + risk warning, requires confirmation. **Only modifies `models.json` after explicit user approval.** |
73
73
 
74
- `enable` / `disable` are current-process switches. For a persistent opt-out, use environment variables below.
74
+ The interactive `/cache-optimizer` menu includes `Footer mode`, where you can choose `total`, `session`, or `process`. `enable` / `disable` are current-process switches. For a persistent opt-out, use environment variables below.
75
75
 
76
76
  ## Persistent opt-out
77
77
 
@@ -84,21 +84,24 @@ This extension is validated against Pi 0.83.0 and remains designed for Pi 0.82+.
84
84
 
85
85
  ## Footer cache stats mode
86
86
 
87
- **v2.7.0+** supports both daily cumulative and current-session footer scopes. The footer defaults to `total`, which shows the provider/model's local counters for the current day across Pi sessions and process restarts. Use either the command or environment variable to select the scope:
87
+ **v2.7.0+** supports daily cumulative and conversation-session footer scopes. **v2.7.1** adds the Footer mode option to the interactive `/cache-optimizer` menu. **v2.8.0+** also supports process-scoped counters. The footer defaults to `total`, which shows the provider/model's local counters for the current day across Pi sessions and process restarts. Use either the command or environment variable to select the scope:
88
88
 
89
89
  | Value | Effect |
90
90
  |---|---|
91
91
  | `total` (default) | Show today's restart-persistent provider/model totals across sessions. Local day rollover resets the counters. |
92
- | `session` | Show only the current hashed Pi session's counters. A fresh session starts at `0/0`; `/reload` in the same session restores that session bucket. |
92
+ | `session` | Show only the current hashed Pi conversation session's counters. A fresh conversation session starts at `0/0`; restarting Pi while resuming the same session restores that session bucket. |
93
+
94
+ | `process` | Show only counters collected by the current Pi process. It starts at `0/0` after Pi restart or extension reload and is never restored from disk. |
93
95
 
94
96
  Persistent command configuration takes precedence over the environment variable:
95
97
 
96
98
  ```text
97
- /cache-optimizer config footer-mode session
98
99
  /cache-optimizer config footer-mode total
100
+ /cache-optimizer config footer-mode session
101
+ /cache-optimizer config footer-mode process
99
102
  ```
100
103
 
101
- The explicit setting is stored in `pi-cache-optimizer-config.json` under Pi's agent directory. If no command override exists, `PI_CACHE_OPTIMIZER_FOOTER_MODE=session|total` is used; values are case-insensitive, and missing or invalid values fall back to `total`. To return an existing installation to environment-controlled behavior, manually delete `pi-cache-optimizer-config.json` and run `/reload`.
104
+ The explicit setting is stored in `pi-cache-optimizer-config.json` under Pi's agent directory. If no command override exists, `PI_CACHE_OPTIMIZER_FOOTER_MODE=total|session|process` is used; values are case-insensitive, and missing or invalid values fall back to `total`. To return an existing installation to environment-controlled behavior, manually delete `pi-cache-optimizer-config.json` and run `/reload`.
102
105
 
103
106
  ## OpenAI-compatible proxy setup
104
107
 
@@ -271,7 +274,7 @@ If only one model should change, use `modelOverrides`:
271
274
 
272
275
  ## Footer stats
273
276
 
274
- Stats are read-only local counters stored in Pi's agent directory (default: `~/.pi/agent/pi-cache-optimizer-stats.json`; custom agent dirs use `PI_CODING_AGENT_DIR`). Both today's provider/model totals and hashed session buckets are maintained. The footer shows daily totals by default, or the current session bucket when `session` mode is selected. The stats file contains only dates and numeric counters — no API keys, prompts, payloads, headers, responses, or model output. Footer mode configuration is stored separately in `pi-cache-optimizer-config.json`.
277
+ Stats are read-only local counters stored in Pi's agent directory (default: `~/.pi/agent/pi-cache-optimizer-stats.json`; custom agent dirs use `PI_CODING_AGENT_DIR`). Both today's provider/model totals and hashed session buckets are maintained. The footer shows daily totals by default, the conversation-session bucket in `session` mode, or the in-memory process bucket in `process` mode. The stats file contains only dates and numeric counters — no API keys, prompts, payloads, headers, responses, or model output. Footer mode configuration is stored separately in `pi-cache-optimizer-config.json`. Process-mode counters are memory-only and are intentionally absent from that file.
275
278
 
276
279
  Pi 0.79+ also includes a built-in footer `CH` marker for the latest prompt cache hit rate. This extension complements that marker with persisted provider/model counters plus proxy compat diagnostics.
277
280
 
package/README.zh-CN.md CHANGED
@@ -68,10 +68,10 @@ Pi 0.79.7 及之后,`pi update` 默认只更新 Pi 本体。若要更新已安
68
68
  | `/cache-optimizer compat` | 对当前模型显示可复制的 compat 建议(如适用)。 |
69
69
  | `/cache-optimizer stats` | 显示当前模型今天的本地 provider/model 统计和近期趋势。 |
70
70
  | `/cache-optimizer reset` | 重置当前 provider/model 的本地 footer 统计;不会修改上游 provider 缓存。 |
71
- | `/cache-optimizer config footer-mode session\|total` | 持久设置 footer 统计模式;持久命令配置优先于环境变量。 |
71
+ | `/cache-optimizer config footer-mode total\|session\|process` | 持久设置 footer 统计模式;持久命令配置优先于环境变量。 |
72
72
  | `/cache-optimizer fix` | 为当前模型自动修复安全的 compat 问题(adaptive thinking、DeepSeek reasoning、OpenAI proxy session affinity)。展示预览 + 风险提示,需要用户确认。**仅在用户明确批准后才修改 `models.json`。** |
73
73
 
74
- `enable` / `disable` 是当前进程内开关。若要持久关闭某些能力,请使用下面的环境变量。
74
+ 交互式 `/cache-optimizer` 菜单包含 `Footer mode`,可以选择 `total`、`session` 或 `process`。`enable` / `disable` 是当前进程内开关。若要持久关闭某些能力,请使用下面的环境变量。
75
75
 
76
76
  ## 持久 Opt-out
77
77
 
@@ -84,21 +84,23 @@ Pi 0.79.7 及之后,`pi update` 默认只更新 Pi 本体。若要更新已安
84
84
 
85
85
  ## Footer 缓存统计模式
86
86
 
87
- **v2.7.0+** 同时支持每日累计与当前 session 两种 footer 统计范围。Footer 默认使用 `total`,显示当前本地日期内、跨 Pi session 和进程重启延续的 provider/model 统计。可以通过命令或环境变量切换显示范围:
87
+ **v2.7.0+** 支持每日累计和 conversation session 两种 footer 统计范围。**v2.7.1** 在交互式 `/cache-optimizer` 菜单中加入了 Footer mode 选项。**v2.8.0+** 另外支持进程级统计。Footer 默认使用 `total`,显示当前本地日期内、跨 Pi session 和进程重启延续的 provider/model 统计。可以通过命令或环境变量切换显示范围:
88
88
 
89
89
  | 值 | 作用 |
90
90
  |---|---|
91
91
  | `total`(默认) | 显示今天跨 session 持久延续的 provider/model 累计统计;本地日期切换时重置。 |
92
- | `session` | 仅显示当前 hashed Pi session 的统计。新 session 从 `0/0` 开始;同一 session 内 `/reload` 会恢复该 session 桶。 |
92
+ | `session` | 仅显示当前 hashed Pi conversation session 的统计。新 conversation session 从 `0/0` 开始;重启 Pi 后如果恢复同一 session,会恢复该 session 桶。 |
93
+ | `process` | 仅显示当前 Pi 进程采集的统计。Pi 重启或 extension reload 后从 `0/0` 开始,且不会从磁盘恢复。 |
93
94
 
94
95
  持久命令配置优先于环境变量:
95
96
 
96
97
  ```text
97
- /cache-optimizer config footer-mode session
98
98
  /cache-optimizer config footer-mode total
99
+ /cache-optimizer config footer-mode session
100
+ /cache-optimizer config footer-mode process
99
101
  ```
100
102
 
101
- 显式设置保存在 Pi agent 目录下的 `pi-cache-optimizer-config.json`。没有命令覆盖时,读取 `PI_CACHE_OPTIMIZER_FOOTER_MODE=session|total`;值不区分大小写,缺失或非法值均回退到 `total`。如需让已有安装重新由环境变量控制,请手动删除 `pi-cache-optimizer-config.json`,然后运行 `/reload`。
103
+ 显式设置保存在 Pi agent 目录下的 `pi-cache-optimizer-config.json`。没有命令覆盖时,读取 `PI_CACHE_OPTIMIZER_FOOTER_MODE=total|session|process`;值不区分大小写,缺失或非法值均回退到 `total`。如需让已有安装重新由环境变量控制,请手动删除 `pi-cache-optimizer-config.json`,然后运行 `/reload`。
102
104
 
103
105
  ## OpenAI-compatible 代理配置
104
106
 
@@ -271,7 +273,7 @@ Provider 级最小 override:
271
273
 
272
274
  ## Footer 统计
273
275
 
274
- 统计是只读本地计数,保存在 Pi agent 目录(默认 `~/.pi/agent/pi-cache-optimizer-stats.json`;自定义 agent 目录使用 `PI_CODING_AGENT_DIR`)。扩展同时维护当天的 provider/model totals 和 hashed session buckets。Footer 默认显示每日 totals;选择 `session` 模式后显示当前 session 桶。统计文件只包含日期和数字计数,不包含 API key、prompt、payload、headers、响应或模型输出。Footer 模式配置单独保存在 `pi-cache-optimizer-config.json`。
276
+ 统计是只读本地计数,保存在 Pi agent 目录(默认 `~/.pi/agent/pi-cache-optimizer-stats.json`;自定义 agent 目录使用 `PI_CODING_AGENT_DIR`)。扩展同时维护当天的 provider/model totals 和 hashed session buckets。Footer 默认显示每日 totals;选择 `session` 模式后显示当前 conversation session 桶,选择 `process` 模式后显示当前进程内存桶。统计文件只包含日期和数字计数,不包含 API key、prompt、payload、headers、响应或模型输出。Footer 模式配置单独保存在 `pi-cache-optimizer-config.json`。Process 模式统计只存在内存中,不会写入该文件。
275
277
 
276
278
  Pi 0.79+ 已内置 footer `CH` 标记,用于显示最近一次 prompt cache hit rate。本扩展在此基础上补充持久化的 provider/model 计数,以及代理 compat 诊断。
277
279
 
package/index.ts CHANGED
@@ -103,7 +103,7 @@ const OPENAI_PROMPT_CACHE_KEY_MAX_LENGTH = 64;
103
103
  const NO_SKILL_COMPRESSION_ENV = "PI_CACHE_OPTIMIZER_NO_SKILL_COMPRESSION";
104
104
  const NO_PROMPT_REWRITE_ENV = "PI_CACHE_OPTIMIZER_NO_PROMPT_REWRITE";
105
105
  const FOOTER_MODE_ENV = "PI_CACHE_OPTIMIZER_FOOTER_MODE";
106
- type FooterStatsMode = "session" | "total";
106
+ type FooterStatsMode = "session" | "total" | "process";
107
107
  type FooterStatsModeSource = "config" | "env" | "default";
108
108
  type PersistedCacheOptimizerConfigV1 = {
109
109
  version: 1;
@@ -1143,7 +1143,7 @@ function isEnabledEnv(value: string | undefined): boolean {
1143
1143
  function parseFooterStatsMode(value: unknown): FooterStatsMode | undefined {
1144
1144
  if (typeof value !== "string") return undefined;
1145
1145
  const normalized = value.trim().toLowerCase();
1146
- return normalized === "session" || normalized === "total" ? normalized : undefined;
1146
+ return normalized === "session" || normalized === "total" || normalized === "process" ? normalized : undefined;
1147
1147
  }
1148
1148
 
1149
1149
  function parsePersistedCacheOptimizerConfig(value: unknown): PersistedCacheOptimizerConfigV1 | undefined {
@@ -3897,8 +3897,10 @@ function selectFooterStatsForModel(
3897
3897
  statsByModel: Record<string, CacheStats>,
3898
3898
  totalsByModel: Record<string, CacheStats>,
3899
3899
  model: { provider: string; id: string },
3900
+ processByModel: Record<string, CacheStats> = {},
3900
3901
  ): CacheStats | undefined {
3901
3902
  if (mode === "total") return totalsByModel[modelKey(model)];
3903
+ if (mode === "process") return processByModel[modelKey(model)];
3902
3904
  if (!sessionHash) return undefined;
3903
3905
  return statsByModel[makeSessionModelKey(sessionHash, model.provider, model.id)];
3904
3906
  }
@@ -3909,6 +3911,7 @@ function buildExactRouterStatusEntry(
3909
3911
  lastRoutedModel: PersistedRoutedModelRef | undefined,
3910
3912
  totalsByModel: Record<string, CacheStats> = {},
3911
3913
  mode: FooterStatsMode = "total",
3914
+ processByModel: Record<string, CacheStats> = {},
3912
3915
  ): { model: PiModel; adapter: CacheProviderAdapter; stats: CacheStats } | undefined {
3913
3916
  if (!sessionHash || !lastRoutedModel) return undefined;
3914
3917
 
@@ -3919,7 +3922,7 @@ function buildExactRouterStatusEntry(
3919
3922
  return {
3920
3923
  model,
3921
3924
  adapter,
3922
- stats: selectFooterStatsForModel(mode, sessionHash, statsByModel, totalsByModel, model) ?? emptyCacheStats(),
3925
+ stats: selectFooterStatsForModel(mode, sessionHash, statsByModel, totalsByModel, model, processByModel) ?? emptyCacheStats(),
3923
3926
  };
3924
3927
  }
3925
3928
 
@@ -3928,9 +3931,12 @@ function findBestRouterModelStats(
3928
3931
  sessionHash: string | undefined,
3929
3932
  statsByModel: Record<string, CacheStats>,
3930
3933
  totalsByModel: Record<string, CacheStats>,
3934
+ processByModel: Record<string, CacheStats> = {},
3931
3935
  ): { model: PiModel; adapter: CacheProviderAdapter; stats: CacheStats } | undefined {
3932
3936
  const entries = mode === "total"
3933
3937
  ? Object.entries(totalsByModel)
3938
+ : mode === "process"
3939
+ ? Object.entries(processByModel)
3934
3940
  : sessionHash
3935
3941
  ? Object.entries(statsByModel)
3936
3942
  .filter(([key]) => key.startsWith(`${sessionHash}:`))
@@ -6752,6 +6758,7 @@ export default function (pi: ExtensionAPI) {
6752
6758
  const openAISdkHeader403Models = new Set<string>();
6753
6759
  const warnedOpenAISdkHeader403Models = new Set<string>();
6754
6760
  let cacheStatsByModel: Record<string, CacheStats> = {};
6761
+ let cacheStatsProcessByModel: Record<string, CacheStats> = {};
6755
6762
  let cacheStatsTotalsByModel: Record<string, CacheStats> = {};
6756
6763
  let cacheStatsLegacyFamily: Partial<Record<CacheProviderId, CacheStats>> = emptyAllCacheStats();
6757
6764
  let lastStatusText: string | undefined;
@@ -6920,6 +6927,17 @@ export default function (pi: ExtensionAPI) {
6920
6927
  return created;
6921
6928
  }
6922
6929
 
6930
+ /** Get or create the current-process provider/model stats entry. */
6931
+ function getOrCreateProcessStatsForModel(model: PiModel): CacheStats {
6932
+ const key = modelKey(model);
6933
+ const existing = cacheStatsProcessByModel[key];
6934
+ if (existing) return existing;
6935
+
6936
+ const created = emptyCacheStats();
6937
+ cacheStatsProcessByModel[key] = created;
6938
+ return created;
6939
+ }
6940
+
6923
6941
  /** Get or create the cumulative provider/model stats entry shown in the footer. */
6924
6942
  function getOrCreateTotalStatsForModel(model: PiModel): CacheStats {
6925
6943
  const key = modelKey(model);
@@ -6937,6 +6955,7 @@ export default function (pi: ExtensionAPI) {
6937
6955
  if (modelKeyFromSessionScoped(key) === displayKey) delete cacheStatsByModel[key];
6938
6956
  }
6939
6957
  delete cacheStatsTotalsByModel[displayKey];
6958
+ delete cacheStatsProcessByModel[displayKey];
6940
6959
  pendingDeletedTotalModelKeys.add(displayKey);
6941
6960
  for (const key of Array.from(recentSamplesByModelKey.keys())) {
6942
6961
  if (modelKeyFromSessionScoped(key) === displayKey) recentSamplesByModelKey.delete(key);
@@ -6949,6 +6968,7 @@ export default function (pi: ExtensionAPI) {
6949
6968
  for (const key of Object.keys(cacheStatsByModel)) {
6950
6969
  if (key.startsWith(prefix)) delete cacheStatsByModel[key];
6951
6970
  }
6971
+ cacheStatsProcessByModel = {};
6952
6972
  cacheStatsTotalsByModel = {};
6953
6973
  replacePersistedTotalsOnNextWrite = true;
6954
6974
  for (const key of Array.from(recentSamplesByModelKey.keys())) {
@@ -7051,6 +7071,14 @@ export default function (pi: ExtensionAPI) {
7051
7071
  }
7052
7072
 
7053
7073
  // Roll over cumulative provider/model totals.
7074
+ for (const key of Object.keys(cacheStatsProcessByModel)) {
7075
+ const stats = cacheStatsProcessByModel[key];
7076
+ if (stats && stats.day !== day) {
7077
+ cacheStatsProcessByModel[key] = emptyCacheStats(day);
7078
+ changed = true;
7079
+ }
7080
+ }
7081
+
7054
7082
  for (const key of Object.keys(cacheStatsTotalsByModel)) {
7055
7083
  const stats = cacheStatsTotalsByModel[key];
7056
7084
  if (stats && stats.day !== day) {
@@ -7091,6 +7119,7 @@ export default function (pi: ExtensionAPI) {
7091
7119
  persisted,
7092
7120
  currentSessionHashSet ? currentSessionHash : undefined,
7093
7121
  );
7122
+ cacheStatsProcessByModel = {};
7094
7123
  cacheStatsTotalsByModel = persisted?.totalsByModel ?? {};
7095
7124
  cacheStatsLegacyFamily = persisted?.legacyFamily ?? emptyAllCacheStats();
7096
7125
  lastActualRoutedModel = currentSessionHashSet
@@ -7109,6 +7138,7 @@ export default function (pi: ExtensionAPI) {
7109
7138
  persisted,
7110
7139
  currentSessionHashSet ? currentSessionHash : undefined,
7111
7140
  );
7141
+ cacheStatsProcessByModel = {};
7112
7142
  cacheStatsTotalsByModel = persisted?.totalsByModel ?? {};
7113
7143
  cacheStatsLegacyFamily = persisted?.legacyFamily ?? emptyAllCacheStats();
7114
7144
  lastActualRoutedModel = currentSessionHashSet
@@ -7143,11 +7173,13 @@ export default function (pi: ExtensionAPI) {
7143
7173
  lastActualRoutedModel,
7144
7174
  cacheStatsTotalsByModel,
7145
7175
  mode,
7176
+ cacheStatsProcessByModel,
7146
7177
  ) ?? findBestRouterModelStats(
7147
7178
  mode,
7148
7179
  sessionHash,
7149
7180
  cacheStatsByModel,
7150
7181
  cacheStatsTotalsByModel,
7182
+ cacheStatsProcessByModel,
7151
7183
  );
7152
7184
  if (realEntry) {
7153
7185
  const statsText = formatCacheStats(realEntry.adapter, realEntry.stats);
@@ -7161,7 +7193,7 @@ export default function (pi: ExtensionAPI) {
7161
7193
  // Footer mode defaults to daily provider/model totals. Users may select
7162
7194
  // current-session counters through persistent command config or the env var.
7163
7195
  const stats = displayModel
7164
- ? selectFooterStatsForModel(mode, sessionHash, cacheStatsByModel, cacheStatsTotalsByModel, displayModel)
7196
+ ? selectFooterStatsForModel(mode, sessionHash, cacheStatsByModel, cacheStatsTotalsByModel, displayModel, cacheStatsProcessByModel)
7165
7197
  : undefined;
7166
7198
  const statsText = formatCacheStats(adapter, stats ?? emptyCacheStats());
7167
7199
  statusText = runtimeOptimizerEnabled ? statsText : `Cache Optimizer disabled · ${statsText}`;
@@ -7584,11 +7616,12 @@ export default function (pi: ExtensionAPI) {
7584
7616
 
7585
7617
  await rollOverStatsIfNeeded(ctx);
7586
7618
 
7587
- // Update stats scoped to current session + actual routed model.
7588
- // Falls back to legacy family when no model is available.
7619
+ // Update session, process-local, and cumulative buckets for the actual
7620
+ // routed model. The process bucket is intentionally never persisted.
7589
7621
  if (statsModel) {
7590
7622
  const sk = sessionModelKey(statsModel);
7591
7623
  addUsageToCacheStats(getOrCreateStatsByModelKey(sk), usage);
7624
+ addUsageToCacheStats(getOrCreateProcessStatsForModel(statsModel), usage);
7592
7625
  addUsageToCacheStats(getOrCreateTotalStatsForModel(statsModel), usage);
7593
7626
  } else {
7594
7627
  addUsageToCacheStats(getStatsForModel(undefined, adapter), usage);
@@ -7607,7 +7640,7 @@ export default function (pi: ExtensionAPI) {
7607
7640
  // with low-hit diagnosis
7608
7641
  // stats — show active model stats bucket, recent trend, usage
7609
7642
  // compat — show compat suggestion with file path
7610
- // config footer-mode session|total — persist footer mode override
7643
+ // config footer-mode total|session|process — persist footer mode override
7611
7644
  // fix — auto-fix compat issues (writes models.json, requires UI)
7612
7645
  // reset — reset current provider/model footer stats bucket (local only)
7613
7646
  // (no args) — interactive menu (with UI) or help summary
@@ -7662,10 +7695,10 @@ export default function (pi: ExtensionAPI) {
7662
7695
  } else if (subcommand === "config") {
7663
7696
  const configKey = commandParts[1];
7664
7697
  const requestedMode = commandParts[2];
7665
- if (configKey !== "footer-mode" || !requestedMode || !["session", "total"].includes(requestedMode)) {
7698
+ if (configKey !== "footer-mode" || !requestedMode || !["session", "total", "process"].includes(requestedMode)) {
7666
7699
  const resolved = resolveFooterStatsMode(persistedFooterStatsMode);
7667
7700
  cmdCtx.ui.notify(
7668
- `Usage: /cache-optimizer config footer-mode session|total\n` +
7701
+ `Usage: /cache-optimizer config footer-mode total|session|process\n` +
7669
7702
  `Current footer mode: ${resolved.mode} (${resolved.source})`,
7670
7703
  "info",
7671
7704
  );
@@ -8074,6 +8107,7 @@ export default function (pi: ExtensionAPI) {
8074
8107
  "Stats — Show cache stats and trend",
8075
8108
  "Compat — Show compat suggestion",
8076
8109
  "Fix — Auto-fix compat issues (writes models.json)",
8110
+ "Footer mode — Choose total, session, or process stats",
8077
8111
  "Reset — Reset local provider/model stats",
8078
8112
  "Cancel",
8079
8113
  ];
@@ -8256,6 +8290,33 @@ export default function (pi: ExtensionAPI) {
8256
8290
  );
8257
8291
  }
8258
8292
  } else if (choice === menuOptions[6]) {
8293
+ const modeOptions = ["total — Daily cumulative totals (default)", "session — Current Pi conversation session", "process — Current Pi process only", "Cancel"];
8294
+ const modeChoice = await cmdCtx.ui.select("Footer cache stats mode", modeOptions);
8295
+ const nextMode = modeChoice === modeOptions[0]
8296
+ ? "total"
8297
+ : modeChoice === modeOptions[1]
8298
+ ? "session"
8299
+ : modeChoice === modeOptions[2]
8300
+ ? "process"
8301
+ : undefined;
8302
+ if (nextMode) {
8303
+ try {
8304
+ await writePersistedFooterMode(nextMode);
8305
+ persistedFooterStatsMode = nextMode;
8306
+ lastStatusText = undefined;
8307
+ await publishStatus(cmdCtx as unknown as ExtensionContext, model);
8308
+ cmdCtx.ui.notify(
8309
+ `✅ Footer mode set to ${nextMode}. Persistent config overrides ${FOOTER_MODE_ENV}.`,
8310
+ "info",
8311
+ );
8312
+ } catch (error) {
8313
+ cmdCtx.ui.notify(
8314
+ `❌ Could not update footer mode config: ${error instanceof Error ? error.message : String(error)}`,
8315
+ "error",
8316
+ );
8317
+ }
8318
+ }
8319
+ } else if (choice === menuOptions[7]) {
8259
8320
  if (!model) {
8260
8321
  cmdCtx.ui.notify("No active model selected. Select a model first with /model or pi --model.", "warning");
8261
8322
  } else {
@@ -8287,7 +8348,7 @@ export default function (pi: ExtensionAPI) {
8287
8348
  diagnosis.push(" doctor — Show current model/provider/api/baseUrl/compat and low-hit diagnosis");
8288
8349
  diagnosis.push(" stats — Show active model stats bucket and recent trend");
8289
8350
  diagnosis.push(" compat — Show compat suggestion with edit location");
8290
- diagnosis.push(" config footer-mode session|total — Persist the footer stats mode");
8351
+ diagnosis.push(" config footer-mode total|session|process — Persist the footer stats mode");
8291
8352
  diagnosis.push(" fix — Auto-fix compat issues (writes models.json, requires UI)");
8292
8353
  diagnosis.push(" reset — Reset local provider/model stats for current model (does not affect upstream)");
8293
8354
  diagnosis.push("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-cache-optimizer",
3
- "version": "2.7.0",
3
+ "version": "2.8.0",
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",