pi-cache-optimizer 2.8.6 → 2.8.8

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 (4) hide show
  1. package/README.md +65 -8
  2. package/README.zh-CN.md +63 -7
  3. package/index.ts +2843 -231
  4. package/package.json +3 -2
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  Pi extension for improving provider-side KV / prompt cache hit rates. It keeps stable prompt content near the front, adds a conservative OpenAI-compatible `prompt_cache_key` fallback, warns about common proxy cache-routing gaps, and shows read-only footer cache stats.
11
11
 
12
- > Renamed from `pi-deepseek-cache-optimizer`. Existing footer counters migrate automatically. The extension does **not** touch Pi's `models.json` during normal operation (default: `~/.pi/agent/models.json`; custom agent dirs use `PI_CODING_AGENT_DIR`); only `/cache-optimizer fix` can edit it, and only after an explicit interactive preview + confirmation with an automatic timestamped backup.
12
+ > Renamed from `pi-deepseek-cache-optimizer`. Existing footer counters migrate automatically. The extension does **not** touch Pi's `models.json` during normal hook operation (default: `~/.pi/agent/models.json`; custom agent dirs use `PI_CODING_AGENT_DIR`). Only `/cache-optimizer fix` and `/cache-optimizer rollback` may edit it, both after an explicit interactive preview + confirmation with an automatic timestamped backup.
13
13
 
14
14
  ## Contents
15
15
 
@@ -17,10 +17,13 @@ Pi extension for improving provider-side KV / prompt cache hit rates. It keeps s
17
17
  - [Install](#install)
18
18
  - [Commands](#commands)
19
19
  - [Persistent opt-out](#persistent-opt-out)
20
+ - [Per-model `prompt_cache_key` opt-out](#per-model-prompt_cache_key-opt-out)
21
+ - [Opt-in deterministic tool ordering](#opt-in-deterministic-tool-ordering)
20
22
  - [Footer cache stats mode](#footer-cache-stats-mode)
21
23
  - [OpenAI-compatible proxy setup](#openai-compatible-proxy-setup)
22
24
  - [Adaptive thinking models](#adaptive-thinking-models)
23
25
  - [Auto-repair with `/cache-optimizer fix`](#auto-repair-with-cache-optimizer-fix)
26
+ - [DeepSeek protocol safety and rollback](#deepseek-protocol-safety-and-rollback)
24
27
  - [Footer stats](#footer-stats)
25
28
  - [For router / virtual-channel extension authors](#for-router--virtual-channel-extension-authors)
26
29
  - [Uninstall](#uninstall)
@@ -38,6 +41,7 @@ Pi extension for improving provider-side KV / prompt cache hit rates. It keeps s
38
41
  - Stores cache statistics in per-extension-instance atomic shards, so parent sessions, child Pi agents, and parallel Pi processes cannot overwrite one another.
39
42
  - Shows current conversation-session provider/model footer stats by default; `total` aggregates all valid local shards for the exact provider/model.
40
43
  - Supports optional router-extension integration through versioned global protocols (`Symbol.for("pi.routing.registry.v1")` and `Symbol.for("pi.cache.hints.v1")`) without importing router packages.
44
+ - Includes disabled-by-default deterministic ordering for verified built-in tool payloads.
41
45
 
42
46
  Caching is provider-side and best-effort. Third-party proxies and router extensions can still hide cache usage, reject unsupported parameters, or route requests across multiple upstreams.
43
47
 
@@ -57,7 +61,7 @@ Run `/reload` in Pi after install/update/remove so extension hooks refresh.
57
61
 
58
62
  On Pi 0.79.7 and newer, `pi update` updates Pi itself only. To update installed Pi packages such as this extension, run `pi update --extensions` (packages only) or `pi update --all` (Pi + packages).
59
63
 
60
- This extension requires Pi 0.82+ and is validated against Pi 0.84.2. It uses the official Pi package types directly for type-checking, along with extension hooks, `getAgentDir()`, and prompt options shared by those versions; it does not depend on Pi 0.83+ APIs such as `ctx.scopedModels` or the bundled TypeBox 1.3 aliases.
64
+ This extension requires Pi 0.82+ and is validated against Pi 0.85.0. It uses the official Pi package types directly for type-checking, along with extension hooks, `getAgentDir()`, and prompt options shared by those versions; it does not depend on Pi 0.83+ APIs such as `ctx.scopedModels` or the bundled TypeBox 1.3 aliases.
61
65
 
62
66
  ## Commands
63
67
 
@@ -73,9 +77,11 @@ This extension requires Pi 0.82+ and is validated against Pi 0.84.2. It uses the
73
77
  | `/cache-optimizer stats contributors` | Shows current/other contributing sessions for the active exact provider/model without exposing session ids. |
74
78
  | `/cache-optimizer reset` | Resets local footer stats for the active provider/model; upstream provider cache is not modified. |
75
79
  | `/cache-optimizer config footer-mode total\|session\|process` | Persist the footer stats mode. Persistent command configuration overrides the environment variable. |
76
- | `/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.** |
80
+ | `/cache-optimizer fix` | Auto-repairs safe compat issues for the active model. Shows preview + risk warning, requires confirmation. It writes `models.json` only for native compat fixes, or the extension config for an evidenced `prompt_cache_key` issue. |
81
+ | `/cache-optimizer fix prompt-cache-key` | Explicitly configures the active OpenAI-compatible provider/model to omit `prompt_cache_key` and `promptCacheKey` from the final request body. Requires confirmation. |
82
+ | `/cache-optimizer rollback` | Reviews the latest matching confirmed fix and, after UI confirmation, restores either the extension config or `models.json` fix safely. |
77
83
 
78
- `/cache-optimizer` uses Pi's native Tab completion. Type `/cache-optimizer <Tab>` for the supported subcommands, `/cache-optimizer stats <Tab>` for `all` or `contributors`, `/cache-optimizer c<Tab>` for `config`, `/cache-optimizer config <Tab>` for `footer-mode`, and `/cache-optimizer config footer-mode <Tab>` for `total`, `session`, or `process`. Suggestions are prefix-filtered and invalid prefixes are left to Pi's normal fallback behavior.
84
+ `/cache-optimizer` uses Pi's native Tab completion. Type `/cache-optimizer <Tab>` for the supported subcommands, `/cache-optimizer stats <Tab>` for `all` or `contributors`, `/cache-optimizer fix <Tab>` for `prompt-cache-key`, `/cache-optimizer c<Tab>` for `config`, `/cache-optimizer config <Tab>` for `footer-mode`, and `/cache-optimizer config footer-mode <Tab>` for `total`, `session`, or `process`. Suggestions are prefix-filtered and invalid prefixes are left to Pi's normal fallback behavior.
79
85
 
80
86
  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.
81
87
 
@@ -88,6 +94,22 @@ The interactive `/cache-optimizer` menu includes `Footer mode`, where you can ch
88
94
  | `PI_CACHE_OPTIMIZER_NO_OPENAI_CACHE_KEY=1` | Disable the OpenAI-compatible `prompt_cache_key` fallback. Preferred explicit opt-out. |
89
95
  | `PI_CACHE_OPTIMIZER_OPENAI_CACHE_KEY=0` | Disable the same fallback via the legacy inverse switch. Values `0`, `false`, `no`, or `off` disable it. |
90
96
 
97
+ ## Opt-in deterministic tool ordering
98
+
99
+ `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`.
100
+
101
+ Tools are sorted by exact name with their original index as the stable tie-breaker. The normalizer shallow-clones only the verified object/array path that changes. Tool objects and unrelated request fields retain their identity, including Google/Vertex `AbortSignal`; tool schemas, choices, routing fields, and caller input are preserved.
102
+
103
+ For safety, payloads with any top-level tool `cache_control` marker or Anthropic `defer_loading` grouping are unchanged. Unknown/custom APIs, malformed tools, missing names, and unsupported shapes are also unchanged. The pure helper recognizes OpenAI Responses fixtures, but the request hook preserves the existing Responses/Codex bypass and does not reorder those requests.
104
+
105
+ Rollback is immediate: unset the variable (or set a non-truthy value) and run `/reload`. To verify the transformation with local fixtures without contacting a provider, run:
106
+
107
+ ```bash
108
+ bun .trellis/tasks/09-03-context-epoch-tool-ordering/verify.ts
109
+ ```
110
+
111
+ The fixture verifier reports numeric tool-order changes and confirms cache-marked payloads remain unchanged. Because it does not contact a provider, provider cache usage is explicitly unavailable and no synthetic cache hit is claimed.
112
+
91
113
  ## Footer cache stats mode
92
114
 
93
115
  Current versions store stats under `pi-cache-optimizer-stats.d/shards/` in Pi's agent directory. Each loaded extension instance owns one UUID-named shard and writes it through temp-file + atomic rename. This prevents parent/child/parallel Pi processes from overwriting one another. Upgrading from the old v6 single-file format deletes the old local stats files and starts footer counters from zero; upstream provider caches are not affected.
@@ -110,6 +132,18 @@ Persistent command configuration takes precedence over the environment variable:
110
132
 
111
133
  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 `session`. To return an existing installation to environment-controlled behavior, manually delete `pi-cache-optimizer-config.json` and run `/reload`.
112
134
 
135
+ ## Per-model `prompt_cache_key` opt-out
136
+
137
+ Some OpenAI-compatible endpoints reject `prompt_cache_key` with HTTP 400 even though the same field is valid for other providers. Pi 0.85.1 has no native `supportsPromptCacheKey` compat field; do **not** add that unknown field to `models.json`. `supportsLongCacheRetention` is not an equivalent switch and should not be used for this purpose.
138
+
139
+ When the extension observes an explicit `prompt_cache_key` unsupported error for the exact provider/model, ordinary `/cache-optimizer fix` offers a confirmed model-scoped repair. If you already know that the endpoint rejects the field, use the explicit command:
140
+
141
+ ```text
142
+ /cache-optimizer fix prompt-cache-key
143
+ ```
144
+
145
+ The preview explains that the setting is stored in the extension-owned `pi-cache-optimizer-config.json`. After confirmation, the final `before_provider_request` stage removes both `prompt_cache_key` and `promptCacheKey`, including a key that Pi core supplied earlier. This can reduce provider prompt-cache hits for that exact model, while other models retain the existing fallback behavior. The file is updated atomically, a privacy-safe backup/receipt is created, and `/reload` or a Pi restart is required. `/cache-optimizer rollback` restores the previous extension configuration without resetting `footerMode`.
146
+
113
147
  ## OpenAI-compatible proxy setup
114
148
 
115
149
  Third-party `openai-completions` proxies (LiteLLM / OneAPI / NewAPI / OpenRouter-like channels) often route one session across multiple upstream backends. That splits provider-side prompt caches.
@@ -142,7 +176,8 @@ Notes:
142
176
  - `supportsLongCacheRetention: true` is optional. Add it only when the endpoint explicitly supports OpenAI long prompt cache retention.
143
177
  - If you see `400 Unsupported parameter: prompt_cache_retention`, remove/avoid `supportsLongCacheRetention` for that channel. Keep `sendSessionAffinityHeaders` if supported. The extension detects the explicit error from response headers or the finalized assistant error message and strips the parameter from subsequent requests in the current process.
144
178
  - Use `/cache-optimizer compat` or `/cache-optimizer doctor` to see model-specific advice.
145
- - For DeepSeek models, the Pi Mono guidance expects `compat.requiresReasoningContentOnAssistantMessages: true` and `compat.thinkingFormat: "deepseek"` alongside cache/session-affinity flags when the endpoint supports them.
179
+ - DeepSeek model names select the `DS cache` adapter only; they do not prove a reasoning wire protocol. Generic cache/routing advice remains active for absent or non-DeepSeek formats. DeepSeek replay advice is shown only when effective `compat.thinkingFormat: "deepseek"` is explicitly configured; it never treats `thinkingFormat` as a missing fix key.
180
+ - Do not add `thinkingFormat: "deepseek"` merely because a model id contains `deepseek`; `openai`, `qwen`, `openrouter`, `together`, or no explicit format are all valid catalog cases.
146
181
  - This extension's `doctor` and `compat` commands only advise; they do not modify `models.json`.
147
182
 
148
183
  ## Anthropic cache TTL compatibility
@@ -223,7 +258,7 @@ Pi 0.80.9+ already includes Kimi K3 in built-in Kimi Coding, Moonshot AI / China
223
258
  **v2.6.0+** adds a `fix` subcommand that can auto-repair safe compat issues:
224
259
 
225
260
  - Adaptive thinking (`forceAdaptiveThinking: true`; Kimi Coding K3 / `kimi-for-coding` also `allowEmptySignature: true`)
226
- - DeepSeek Pi Mono reasoning compat (`thinkingFormat: "deepseek"`, `requiresReasoningContentOnAssistantMessages: true`)
261
+ - DeepSeek Pi Mono replay compat (`requiresReasoningContentOnAssistantMessages: true` only when `thinkingFormat: "deepseek"` is already explicit; `/fix` never invents that format)
227
262
  - OpenAI-compatible proxy session affinity (`sendSessionAffinityHeaders: true` for `openai-completions`). Pi 0.80.7+ controls `openai-responses` header shape with `sessionAffinityFormat` and auto-detects its default; this extension no longer writes the removed `sendSessionIdHeader` field.
228
263
 
229
264
  **Scope:** only the currently active model. Other channels require switching models and running `fix` again.
@@ -236,14 +271,21 @@ Pi 0.80.9+ already includes Kimi K3 in built-in Kimi Coding, Moonshot AI / China
236
271
  4. Requires explicit user confirmation (interactive prompt or `ui.select`)
237
272
  5. Writes and restores atomically (temp + rename); self-validates after write
238
273
  6. Preserves the existing `models.json` access mode exactly — it does not tighten or loosen permissions (for example, `0600` stays `0600`, `0644` stays `0644`)
239
- 7. Uses unique, non-overwriting backup names and falls back to manual guidance if the JSONC scanner cannot confidently locate the target
274
+ 7. Writes a privacy-safe, versioned receipt atomically only after a successful write; the receipt contains transaction/model identity, placement, scalar compat before/after values, file hashes, backup filename, timestamps/status, and no credentials or request data
275
+ 8. Uses unique, non-overwriting backup names and falls back to manual guidance if the JSONC scanner cannot confidently locate the target
240
276
 
241
- Existing `modelOverrides[modelId]` entries have Pi's highest precedence, so `fix` repairs them directly. For built-in or API-login models without a custom `models[]` entry, `fix` creates a compat-only `modelOverrides` entry instead of inventing a custom model definition. Self-validation checks the effective three-layer compat result, so a lower-level edit shadowed by an override is rejected.
277
+ Existing `modelOverrides[modelId]` entries have Pi's highest precedence, so `fix` repairs them directly. For built-in or API-login models without a custom `models[]` entry, `fix` creates a compat-only `modelOverrides` entry instead of inventing a custom model definition. Runtime-observed provider failures are always written to that highest-precedence model override so extension-provided runtime compat cannot shadow the repair. Self-validation checks the full provider → custom model → runtime model → modelOverride result and rejects an ineffective lower-layer edit.
242
278
 
243
279
  **Non-interactive mode:** refuses to write; shows manual edit guidance instead.
244
280
 
245
281
  **Run:** `/cache-optimizer fix` when the active model has detected compat issues. The command shows "nothing to fix" when compat is already complete.
246
282
 
283
+ ## `/cache-optimizer rollback`
284
+
285
+ Rollback is available through completion, direct execution, and the interactive menu. It always requires UI confirmation; without an interactive UI it gives manual-review guidance and does not write. The command selects the latest unapplied receipt for the active provider/model, validates the recorded backup and current file hashes, creates a new access-mode-preserving rollback backup, and uses temp-file + atomic rename. Fix and rollback transactions are serialized across extension instances; rollback also binds the receipt transaction id/hash from preview through commit and refuses if another transaction replaces it.
286
+
287
+ If `models.json` is unchanged since the fix, rollback can restore the exact pre-fix JSONC. If the file changed, it never blindly replaces it: it may restore only receipt-owned scalar compat keys whose recorded post-fix values are still present, preserving later user changes. If a receipt-owned key changed, the target was removed/moved, or the fix created a new target entry, it refuses and points to the recorded backup for manual review. Successful rollback marks the receipt and requires `/reload` or a restart.
288
+
247
289
  ### Channels without a `models.json` provider entry
248
290
 
249
291
  Some Pi channels may be available even when there is no provider block in Pi's agent `models.json` yet (default: `~/.pi/agent/models.json`; with `PI_CODING_AGENT_DIR`, use `$PI_CODING_AGENT_DIR/models.json`). Keep existing authentication as-is and do not copy credentials, tokens, or API keys. Add only cache/routing compatibility overrides in `models.json`.
@@ -282,6 +324,21 @@ If only one model should change, use `modelOverrides`:
282
324
  }
283
325
  ```
284
326
 
327
+ ## DeepSeek protocol safety and rollback
328
+
329
+ Pi's model-family name and its reasoning wire protocol are separate. A DeepSeek-named model using an OpenAI-compatible endpoint may send standard top-level `reasoning_effort`, DeepSeek-style `thinking`, or another provider-specific format. `supportsReasoningEffort: true` alone is not proof either way. `/cache-optimizer fix` therefore never invents `thinkingFormat: "deepseek"`; configure an explicit `thinkingFormat` only when the endpoint documentation or an explicit provider signal supports it.
330
+
331
+ The novice-safe workflow is staged:
332
+
333
+ 1. Run `/cache-optimizer fix` for protocol-neutral cache/routing repairs such as session affinity. It shows the exact placement and requires confirmation.
334
+ 2. Make a normal request. If an OpenAI-compatible DeepSeek-like model explicitly rejects the `thinking` parameter in favor of `reasoning_effort`, the extension keeps only a model-scoped, process-local category. It does not persist or display the complete provider error, send a hidden probe, or edit configuration from a response hook.
335
+ 3. Run `/cache-optimizer fix` again to review an evidence-based model-level protocol repair. It is written through the highest-precedence `modelOverrides[modelId].compat` layer and validated against runtime compat, so an extension-provided model cannot silently shadow it. A provider-level change is never broadened from a model name alone. Explicit `openai`, `qwen`, `openrouter`, and `together` formats remain respected.
336
+ 4. If the latest confirmed fix caused the problem, run `/cache-optimizer rollback`. Rollback always requires UI confirmation.
337
+
338
+ Every successful interactive fix writes one atomic, versioned receipt at `pi-cache-optimizer-fix-receipt.json`. It contains only a transaction id, provider/model identity, placement, changed scalar compat keys with before/after values, file hashes, backup filename, and timestamps/status. It never stores API keys, credentials, prompts, payloads, headers, response bodies, or raw errors.
339
+
340
+ Rollback creates a new access-mode-preserving backup and uses atomic replacement. If `models.json` is unchanged since the fix and the receipt backup matches the pre-fix hash, it can restore the exact pre-fix file. If the file changed, it only reverts receipt-owned scalar keys that still have their recorded post-fix values and preserves unrelated user changes; otherwise it refuses safely and points to the recorded backup for manual review. The receipt is marked rolled back after validation, and `/reload` or a restart is required.
341
+
285
342
  ## Footer stats
286
343
 
287
344
  Stats are read-only local counters stored as UUID-owned shards under `pi-cache-optimizer-stats.d/shards/` in Pi's agent directory (custom agent dirs use `PI_CODING_AGENT_DIR`). Shards contain only dates, opaque session hashes, exact provider/model counters, reset epochs, and process lifecycle metadata — no API keys, prompts, payloads, headers, responses, or model output. The footer defaults to the current conversation session; `total` aggregates all valid current-day shards for the exact provider/model, and `process` shows only the current extension instance. Footer mode configuration is stored separately in `pi-cache-optimizer-config.json`. Upgrading from the old v6 shared stats files deletes those local counters instead of migrating them.
package/README.zh-CN.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  用于提升 Pi 中 provider 侧 KV Cache / Prompt Cache 命中率的扩展:把稳定 prompt 内容前置,给 OpenAI-compatible 请求补保守的 `prompt_cache_key`,提示代理渠道常见缓存路由兼容问题,并在底部显示只读缓存统计。
11
11
 
12
- > 本包已从 `pi-deepseek-cache-optimizer` 改名。已有底部统计会自动迁移。正常运行时扩展不会触碰 Pi 的 `models.json`(默认 `~/.pi/agent/models.json`;自定义 agent 目录使用 `PI_CODING_AGENT_DIR`);只有 `/cache-optimizer fix` 会在展示交互式预览、风险提示并得到明确确认后写入,且会先创建带时间戳的自动备份。
12
+ > 本包已从 `pi-deepseek-cache-optimizer` 改名。已有底部统计会自动迁移。正常 hook 运行时扩展不会触碰 Pi 的 `models.json`(默认 `~/.pi/agent/models.json`;自定义 agent 目录使用 `PI_CODING_AGENT_DIR`);只有 `/cache-optimizer fix` 和 `/cache-optimizer rollback` 可在展示交互式预览、风险提示并得到明确确认后写入,且都会先创建带时间戳的自动备份。
13
13
 
14
14
  ## 目录
15
15
 
@@ -17,10 +17,13 @@
17
17
  - [安装](#安装)
18
18
  - [命令](#命令)
19
19
  - [持久 Opt-out](#持久-opt-out)
20
+ - [按模型关闭 `prompt_cache_key`](#按模型关闭-prompt_cache_key)
21
+ - [Opt-in 确定性工具排序](#opt-in-确定性工具排序)
20
22
  - [Footer 缓存统计模式](#footer-缓存统计模式)
21
23
  - [OpenAI-compatible 代理配置](#openai-compatible-代理配置)
22
24
  - [Adaptive thinking 模型](#adaptive-thinking-模型)
23
25
  - [使用 `/cache-optimizer fix` 自动修复](#使用-cache-optimizer-fix-自动修复)
26
+ - [DeepSeek 协议安全与回滚](#deepseek-协议安全与回滚)
24
27
  - [Footer 统计](#footer-统计)
25
28
  - [Router / Virtual-channel 扩展作者指南](#router--virtual-channel-扩展作者指南)
26
29
  - [卸载](#卸载)
@@ -38,6 +41,7 @@
38
41
  - 使用每个 extension instance 独占的原子 shard 保存缓存统计,避免父会话、子 Pi agent 和并行 Pi 进程互相覆盖。
39
42
  - Footer 默认显示当前 conversation session 的 provider/model 统计;`total` 可聚合同一精确 provider/model 的所有有效本地 shard。
40
43
  - 通过版本化全局协议(`Symbol.for("pi.routing.registry.v1")` 与 `Symbol.for("pi.cache.hints.v1")`)支持可选的 router extension 集成,而不导入任何 router 包。
44
+ - 提供默认关闭的确定性排序,用于已验证的 Pi 内置工具 payload。
41
45
 
42
46
  缓存是 provider 侧的 best-effort 行为。第三方代理和 router extension 仍可能隐藏缓存 usage、拒绝不支持的参数,或把请求路由到多个上游。
43
47
 
@@ -57,7 +61,7 @@ pi remove npm:pi-deepseek-cache-optimizer && pi install npm:pi-cache-optimizer
57
61
 
58
62
  Pi 0.79.7 及之后,`pi update` 默认只更新 Pi 本体。若要更新已安装的 Pi package(包括本扩展),请运行 `pi update --extensions`(只更新 packages)或 `pi update --all`(Pi 与 packages 一起更新)。
59
63
 
60
- 本扩展要求 Pi 0.82+,并已使用 Pi 0.84.2 验证。TypeScript 校验直接使用官方 Pi package 类型,同时只使用这些版本共有的 extension hooks、`getAgentDir()` 和 prompt options;不依赖 Pi 0.83+ 专有 API(例如 `ctx.scopedModels` 或 bundled TypeBox 1.3 aliases)。
64
+ 本扩展要求 Pi 0.82+,并已使用 Pi 0.85.0 验证。TypeScript 校验直接使用官方 Pi package 类型,同时只使用这些版本共有的 extension hooks、`getAgentDir()` 和 prompt options;不依赖 Pi 0.83+ 专有 API(例如 `ctx.scopedModels` 或 bundled TypeBox 1.3 aliases)。
61
65
 
62
66
  ## 命令
63
67
 
@@ -73,9 +77,11 @@ Pi 0.79.7 及之后,`pi update` 默认只更新 Pi 本体。若要更新已安
73
77
  | `/cache-optimizer stats contributors` | 显示当前精确 provider/model 的当前/其他贡献 session,不暴露 session id。 |
74
78
  | `/cache-optimizer reset` | 重置当前 provider/model 的本地 footer 统计;不会修改上游 provider 缓存。 |
75
79
  | `/cache-optimizer config footer-mode total\|session\|process` | 持久设置 footer 统计模式;持久命令配置优先于环境变量。 |
76
- | `/cache-optimizer fix` | 为当前模型自动修复安全的 compat 问题(adaptive thinking、DeepSeek reasoning、OpenAI proxy session affinity)。展示预览 + 风险提示,需要用户确认。**仅在用户明确批准后才修改 `models.json`。** |
80
+ | `/cache-optimizer fix` | 为当前模型自动修复安全的 compat 问题。展示预览 + 风险提示,需要用户确认;原生 compat 修复写 `models.json`,已观测到的 `prompt_cache_key` 问题写扩展配置。 |
81
+ | `/cache-optimizer fix prompt-cache-key` | 明确把当前 OpenAI-compatible provider/model 配置为从最终请求体移除 `prompt_cache_key` 与 `promptCacheKey`。需要确认。 |
82
+ | `/cache-optimizer rollback` | 查看最近一次匹配的已确认修复;经 UI 确认后安全恢复扩展配置或 `models.json` 修复。 |
77
83
 
78
- `/cache-optimizer` 使用 Pi 原生 Tab 补全:输入 `/cache-optimizer <Tab>` 查看支持的子命令,输入 `/cache-optimizer stats <Tab>` 补全 `all` 或 `contributors`,输入 `/cache-optimizer c<Tab>` 补全 `config`,输入 `/cache-optimizer config <Tab>` 补全 `footer-mode`,输入 `/cache-optimizer config footer-mode <Tab>` 补全 `total`、`session` 或 `process`。建议会按当前前缀过滤;无效前缀返回空结果,由 Pi 正常回退处理。
84
+ `/cache-optimizer` 使用 Pi 原生 Tab 补全:输入 `/cache-optimizer <Tab>` 查看支持的子命令,输入 `/cache-optimizer stats <Tab>` 补全 `all` 或 `contributors`,输入 `/cache-optimizer fix <Tab>` 补全 `prompt-cache-key`,输入 `/cache-optimizer c<Tab>` 补全 `config`,输入 `/cache-optimizer config <Tab>` 补全 `footer-mode`,输入 `/cache-optimizer config footer-mode <Tab>` 补全 `total`、`session` 或 `process`。建议会按当前前缀过滤;无效前缀返回空结果,由 Pi 正常回退处理。
79
85
 
80
86
  交互式 `/cache-optimizer` 菜单包含 `Footer mode`,可以选择 `total`、`session` 或 `process`。`enable` / `disable` 是当前进程内开关。若要持久关闭某些能力,请使用下面的环境变量。
81
87
 
@@ -88,6 +94,22 @@ Pi 0.79.7 及之后,`pi update` 默认只更新 Pi 本体。若要更新已安
88
94
  | `PI_CACHE_OPTIMIZER_NO_OPENAI_CACHE_KEY=1` | 关闭 OpenAI-compatible `prompt_cache_key` fallback。推荐使用这个显式 opt-out。 |
89
95
  | `PI_CACHE_OPTIMIZER_OPENAI_CACHE_KEY=0` | 通过旧的反向开关关闭同一个 fallback。取值 `0`、`false`、`no`、`off` 时关闭。 |
90
96
 
97
+ ## Opt-in 确定性工具排序
98
+
99
+ `PI_CACHE_OPTIMIZER_TOOL_ORDER=1` 会对 Pi 内置 OpenAI Completions、Anthropic、Google 与 Bedrock payload 中已验证的工具定义进行确定性排序。Truthy 取值为(不区分大小写)`1`、`true`、`yes` 或 `on`。该能力默认关闭、仅在当前进程生效,并会被 `/cache-optimizer disable` 抑制。
100
+
101
+ 工具按精确名称排序,并用原始 index 作为稳定的同名 tie-breaker。排序只对发生变化的已验证对象/数组路径做浅 clone。工具对象和无关请求字段保持原引用,包括 Google/Vertex 的 `AbortSignal`;工具 schema、tool choice、routing 字段和调用方输入都保持不变。
102
+
103
+ 出于安全考虑,只要工具存在顶层 `cache_control` marker,或 Anthropic payload 包含 `defer_loading` 分组,payload 就保持不变。未知 / 自定义 API、畸形工具、缺少名称或不支持的 shape 同样保持不变。纯 helper 可识别 OpenAI Responses fixture,但 request hook 保留既有 Responses/Codex bypass,不会重排这些请求。
104
+
105
+ 回滚很简单:删除变量或设为非 truthy 值,然后运行 `/reload`。如需使用本地 fixture 验证转换、且不连接 provider,可运行:
106
+
107
+ ```bash
108
+ bun .trellis/tasks/09-03-context-epoch-tool-ordering/verify.ts
109
+ ```
110
+
111
+ 该 verifier 只报告数字形式的工具排序变化,并确认带 cache marker 的 payload 不会变化。由于它不会连接 provider,provider cache usage 会明确报告为 unavailable,也不会虚构 cache hit。
112
+
91
113
  ## Footer 缓存统计模式
92
114
 
93
115
  当前版本把统计保存在 Pi agent 目录的 `pi-cache-optimizer-stats.d/shards/` 下。每个已加载的 extension instance 独占一个 UUID 命名 shard,并通过临时文件 + 原子 rename 写入,因此父/子/并行 Pi 进程不会互相覆盖。旧 v6 单文件统计在升级时直接删除,本地 footer 计数从零开始;不会影响上游 provider 的实际缓存。
@@ -110,6 +132,18 @@ Footer 默认使用 `session`,避免另一个并行 Pi 终端使用相同 prov
110
132
 
111
133
  显式设置保存在 Pi agent 目录下的 `pi-cache-optimizer-config.json`。没有命令覆盖时,读取 `PI_CACHE_OPTIMIZER_FOOTER_MODE=total|session|process`;值不区分大小写,缺失或非法值均回退到 `session`。如需让已有安装重新由环境变量控制,请手动删除 `pi-cache-optimizer-config.json`,然后运行 `/reload`。
112
134
 
135
+ ## 按模型关闭 `prompt_cache_key`
136
+
137
+ 某些 OpenAI-compatible endpoint 会因 `prompt_cache_key` 返回 HTTP 400,但同一个字段对其他 provider 可能有效。Pi 0.85.1 没有原生的 `supportsPromptCacheKey` compat 字段,**不要**把这个未知字段加入 `models.json`。`supportsLongCacheRetention` 也不是等价开关,不应借用来实现此目的。
138
+
139
+ 当扩展观察到精确 provider/model 明确拒绝 `prompt_cache_key` 后,普通 `/cache-optimizer fix` 会提供经过确认的模型级修复。如果你已经确定 endpoint 不支持该字段,可以主动执行:
140
+
141
+ ```text
142
+ /cache-optimizer fix prompt-cache-key
143
+ ```
144
+
145
+ 预览会说明设置保存在扩展自有的 `pi-cache-optimizer-config.json` 中。确认后,`before_provider_request` 的最终阶段会移除 `prompt_cache_key` 和 `promptCacheKey`,包括 Pi core 之前已经放入的 key。这个精确模型可能因此失去 provider prompt-cache 命中,其他模型仍保持原有 fallback 行为。配置会使用原子替换写入,并生成不含敏感内容的备份/receipt;需要 `/reload` 或重启 Pi。`/cache-optimizer rollback` 可恢复之前的扩展配置,且不会重置 `footerMode`。
146
+
113
147
  ## OpenAI-compatible 代理配置
114
148
 
115
149
  LiteLLM / OneAPI / NewAPI / 类 OpenRouter 渠道等第三方 `openai-completions` 代理,常会把同一个 session 分散到多个上游后端,导致 provider 侧 prompt cache 被拆散。
@@ -142,7 +176,8 @@ Pi 0.84.1 还修复了内置 Fireworks 渠道对拒绝 `prompt_cache_retention`
142
176
  - `supportsLongCacheRetention: true` 是可选项。只有 endpoint 明确支持 OpenAI long prompt cache retention 时才添加。
143
177
  - 如果出现 `400 Unsupported parameter: prompt_cache_retention`,请为该渠道移除 / 避免 `supportsLongCacheRetention`;如支持,可保留 `sendSessionAffinityHeaders`。扩展会从响应头或最终 assistant error message 中识别这条明确错误,并在当前进程的后续请求中移除该参数。
144
178
  - 使用 `/cache-optimizer compat` 或 `/cache-optimizer doctor` 查看当前模型的具体建议。
145
- - 对 DeepSeek 模型,Pi Mono 指南期望在支持时同时设置 `compat.requiresReasoningContentOnAssistantMessages: true` 和 `compat.thinkingFormat: "deepseek"`,再配合缓存 / session-affinity 相关 compat。
179
+ - DeepSeek 模型名只用于选择 `DS cache` adapter,不能证明 reasoning wire protocol。缺少或使用非 DeepSeek format 时仍保留通用缓存 / 路由建议;只有 effective `compat.thinkingFormat: "deepseek"` 被明确配置时,才显示 DeepSeek replay 建议,且不会把 `thinkingFormat` 列为缺失修复项。
180
+ - 不要因为模型 id 含有 `deepseek` 就添加 `thinkingFormat: "deepseek"`;catalog 中也存在 `openai`、`qwen`、`openrouter`、`together` 或没有显式 format 的情况。
146
181
  - 本扩展的 `doctor` 和 `compat` 命令只给建议,不会修改 `models.json`。
147
182
 
148
183
  ## Anthropic 缓存 TTL 兼容
@@ -223,7 +258,7 @@ Pi 0.80.9+ 已在内置 Kimi Coding、Moonshot AI / 中国区、OpenRouter 和 V
223
258
  **v2.6.0+** 新增 `fix` 子命令,可自动修复安全的 compat 问题:
224
259
 
225
260
  - Adaptive thinking(`forceAdaptiveThinking: true`;Kimi Coding K3 / `kimi-for-coding` 还包括 `allowEmptySignature: true`)
226
- - DeepSeek Pi Mono reasoning compat(`thinkingFormat: "deepseek"`、`requiresReasoningContentOnAssistantMessages: true`)
261
+ - DeepSeek Pi Mono replay compat(仅当已明确配置 `thinkingFormat: "deepseek"` 时使用 `requiresReasoningContentOnAssistantMessages: true`;`/fix` 不会自行推断该 format)
227
262
  - OpenAI-compatible proxy session affinity(`openai-completions` 使用 `sendSessionAffinityHeaders: true`)。Pi 0.80.7+ 使用 `sessionAffinityFormat` 控制 `openai-responses` header 形式并自动检测默认值;本扩展不再写入已移除的 `sendSessionIdHeader`。
228
263
 
229
264
  **范围:** 仅当前 active model。其他渠道需切换模型后再次运行 `fix`。
@@ -238,12 +273,18 @@ Pi 0.80.9+ 已在内置 Kimi Coding、Moonshot AI / 中国区、OpenRouter 和 V
238
273
  6. 完全保留 `models.json` 原有访问权限,不主动收紧或放宽(例如 `0600` 保持 `0600`,`0644` 保持 `0644`)
239
274
  7. 备份名唯一且不会覆盖已有备份;如果 JSONC 扫描器无法置信定位目标,则回退到手动修改指引
240
275
 
241
- 已有的 `modelOverrides[modelId]` 具有 Pi 的最高优先级,因此 `fix` 会直接修复该 entry。对于没有自定义 `models[]` entry 的内置模型或 API-login 模型,`fix` 会创建仅含 compat 的 `modelOverrides` entry,而不会凭空添加自定义模型定义。自检会验证三层合并后的有效 compat,因此被更高层 override 遮蔽的低层修改会被拒绝。
276
+ 已有的 `modelOverrides[modelId]` 具有 Pi 的最高优先级,因此 `fix` 会直接修复该 entry。对于没有自定义 `models[]` entry 的内置模型或 API-login 模型,`fix` 会创建仅含 compat 的 `modelOverrides` entry,而不会凭空添加自定义模型定义。运行时观察到的 provider 失败也始终写入这一最高优先级 model override,避免 extension-provided runtime compat 遮挡修复。自检会验证完整的 provider → custom model → runtime model → modelOverride 结果;无效的低层写入会被拒绝。
242
277
 
243
278
  **非交互模式:** 拒绝写入,显示手动编辑指引。
244
279
 
245
280
  **运行:** 当 active model 检测到 compat 问题时执行 `/cache-optimizer fix`。compat 已完整时,命令显示"无需修复"。
246
281
 
282
+ ## `/cache-optimizer rollback`
283
+
284
+ Rollback 可通过补全、直接命令和交互菜单使用,并始终需要 UI 确认。命令会选择匹配当前 provider/model 的最新未回滚 receipt,验证备份与文件 hash,创建新的保留访问权限的 rollback backup,并使用临时文件 + 原子替换。Fix 与 rollback 在多个 extension instance 间串行执行;rollback 还会把预览时的 receipt transaction id/hash 绑定到提交阶段,若另一事务替换 receipt 就安全拒绝。没有交互式 UI 时只提供手动恢复指引,不会写入文件。
285
+
286
+ 如果 `models.json` 自 fix 后没有变化,rollback 可以恢复完整的 pre-fix JSONC;如果文件发生变化,则绝不会盲目替换整个文件,只会在 receipt-owned scalar key 仍等于记录的 post-fix 值时撤销该 key,并保留之后的用户修改。如果 receipt-owned key 已变化、目标被删除/移动,或 fix 新建了目标项,命令会安全拒绝并指向记录的 backup 供手动检查。成功后 receipt 会标记为已回滚,并需要 `/reload` 或重启。
287
+
247
288
  ### 没有 `models.json` provider entry 的渠道
248
289
 
249
290
  有些 Pi 渠道可用时,Pi agent `models.json`(默认 `~/.pi/agent/models.json`;若设置了 `PI_CODING_AGENT_DIR`,则为 `$PI_CODING_AGENT_DIR/models.json`)里可能还没有对应 provider block。保留现有认证方式,不要复制 credential、token 或 API key。只在 `models.json` 里添加缓存 / 路由兼容覆盖。
@@ -282,6 +323,21 @@ Pi Cache Optimizer 按 Pi 的优先级解析有效 compat(`provider.compat`
282
323
  }
283
324
  ```
284
325
 
326
+ ## DeepSeek 协议安全与回滚
327
+
328
+ 模型家族名称与 reasoning wire protocol 是两件事。使用 OpenAI-compatible endpoint 的 DeepSeek 名称模型,可能使用标准顶层 `reasoning_effort`、DeepSeek 风格 `thinking`,或其它 provider-specific format。单独的 `supportsReasoningEffort: true` 不能证明协议。为此,`/cache-optimizer fix` 永远不会凭模型名推断或添加 `thinkingFormat: "deepseek"`;只有 endpoint 文档或 provider 的明确证据支持时,才应配置显式 format。
329
+
330
+ 面向新手的安全流程是分阶段的:
331
+
332
+ 1. 先运行 `/cache-optimizer fix`,只处理协议无关的缓存 / 路由修复,例如 session affinity;命令会展示具体位置并要求确认。
333
+ 2. 发起一次普通请求。如果 OpenAI-compatible 的 DeepSeek-like 模型明确拒绝 `thinking` 并要求使用 `reasoning_effort`,扩展只在当前进程保留 model-scoped 分类,不会持久化或显示完整错误,不会发送隐藏探测请求,也不会在 response hook 中自动修改配置。
334
+ 3. 再次运行 `/cache-optimizer fix`,查看基于证据的 model-level 协议修复;该修复写入最高优先级的 `modelOverrides[modelId].compat`,并结合 runtime compat 自检,避免 extension-provided model 静默遮挡。不会仅凭共享 provider 或模型名字扩大修改范围,显式 `openai`、`qwen`、`openrouter`、`together` format 会被尊重。
335
+ 4. 如果最近一次已确认修复导致问题,运行 `/cache-optimizer rollback`;回滚始终需要 UI 确认。
336
+
337
+ 每次成功的交互式 fix 都会以原子方式写入版本化 receipt:`pi-cache-optimizer-fix-receipt.json`。它只包含 transaction id、provider/model identity、placement、发生变化的 scalar compat key 及 before/after 值、文件 hash、备份文件名和时间戳/status。不会保存 API key、credential、prompt、payload、header、response body 或原始错误。
338
+
339
+ Rollback 会创建新的、保留访问权限的备份并使用原子替换。如果 `models.json` 自 fix 后未变化,且 receipt 备份匹配 pre-fix hash,则可恢复完整 pre-fix 文件。如果文件发生变化,只会在 receipt-owned scalar key 仍等于记录的 post-fix 值时撤销该 key,并保留其它用户修改;否则安全拒绝并指向记录的备份供手动检查。验证后 receipt 会标记为已回滚,需要 `/reload` 或重启 Pi。
340
+
285
341
  ## Footer 统计
286
342
 
287
343
  统计是只读本地计数,保存在 Pi agent 目录的 UUID shard(`pi-cache-optimizer-stats.d/shards/`;自定义 agent 目录使用 `PI_CODING_AGENT_DIR`)。Shard 只包含日期、opaque session hash、精确 provider/model 计数、reset epoch 和进程生命周期元数据,不包含 API key、prompt、payload、headers、响应或模型输出。Footer 默认显示当前 conversation session;`total` 聚合同一精确 provider/model 的所有有效本地 shard,`process` 只显示当前 extension instance。旧 v6 共享统计文件升级时直接删除,不迁移旧计数。