dsh-model-switch 0.4.35 → 0.4.37

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/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ ## v0.4.37
2
+
3
+ 「能力路由」四张卡片现在说明自己的层级:摘要行是**默认(无组时)**的值,另起一行列出所有覆盖它的供应商配置组,改完组不再看起来像"没生效"。
4
+
5
+ - **纯展示改动,解析行为不变。** `resolveCapabilityRoute` 仍是「组 ?? 默认」(`src/session-route.ts`):压缩 / 自动审查 / 子代理是单次静态解析、不做失败回落,会话命名仍走既有候选链(组对 → 默认对 → 行自身对)。
6
+ - **新增 `capabilityGroupPins(groups, capability)`(`src/session-route.ts`)**:按 provider 排序收集"固定了该能力"的组;组内空串或半对一律视为未固定,因此 `titleProvider: ""`(跟随会话模型)不会被误当成固定值。
7
+ - **卡片新增一行 `note`(`src/client/ModelSwitchSettings.tsx`)**:子代理、自动审查、压缩、会话命名四处;≤3 个组时全列(`组覆盖:apigoto → apigoto · gpt-6-luna;…`),超过 3 个折叠为「… 等 N 个组」;无组覆盖时**不渲染**,与旧版输出一致。子代理行额外带上组里的 `subagentReasoningEffort`。
8
+ - **文案**:`src/client/locales.ts` 新增 `groupOverridesLabel` / `groupOverridesMore`(zh + en)。
9
+ - **测试**:`tests/capability-group-pins.spec.ts` 四例(provider 排序 / 子代理 effort / 空串跳过 / 无组表)。`pnpm run check` 绿(57 文件 / **508** 测试 + pack 闸门)。
10
+ - **Lab 验证(正式版 0.2.1-rc.2,`DSH_HOME=~/.dsh-lab`,3082,released artifact)**:注入一个 `deepseek-account` 组后打开「设置 → 模型切换」,子代理/自动审查/压缩三张卡片出现 `组覆盖:deepseek-account → deepseek-account · deepseek-flash;无组时用上方默认值`,会话命名因组内留空**不出现**;随后把组里自动审查与子代理两项清空,重载后组覆盖行从 3 条降为 1 条(只剩压缩)。改动在运行时重组后立即反映,无需重启。
11
+
12
+ ## v0.4.36
13
+
14
+ 「自动审查」不再在插件安装类重组后停在「完全权限」:Auto 选择改为**持续记录**,回放只消费真正恢复掉的会话。
15
+
16
+ - **P0 root cause: the snapshot could be destroyed before it was written (`src/auto-review.ts`).** `persistAutoSnapshot()` read the live session list at release time and **deleted the file whenever that read came back empty**. Two release orderings make it come back empty: the official gate's teardown can flip the `auto` sessions to the fallback preset *before* our teardown effect runs (a plugin install / bundle edit recompose reorders the rows), and a row that is going away can lose access to the session service entirely (`rememberAuto()` swallowed the error and returned `[]`). Either way the record was removed — `$DSH_HOME/model-switch/` was observed empty afterwards — and the next `claim()` had nothing to replay. Session-log evidence: four recomposes flipped and restored the selection within ~60 ms, and the single recompose that added a plugin row (18:20:56Z) flipped it and never restored.
17
+ - **Fix 1: a continuous record that survives reordering.** Every session's `permission/preset` feed is wired to the record as the session is seen; a change that arrives while this row has released the registration is the official teardown flip (not a user choice) and keeps its id recorded, anything else removes it. Release-time `persistAutoSnapshot()` now merges the live read **into** that record instead of replacing it, so an empty or failed read can no longer destroy the selection.
18
+ - **Fix 2: a replay consumes only what it restored (`src/auto-sessions-state.ts`).** `planAutoSnapshotReplay()` splits the recorded ids into the ones a live session claims and the rest; the rest stay in the file with their **original** `savedAt` — an early claim cannot eat the selection of sessions that load later, and the 15-minute replay window is not restarted behind the user's back. `flushAutoSnapshot()` removes the file only once every recorded id has been replayed.
19
+ - **Tests.** `tests/auto-sessions-state.spec.ts` adds the timestamp-preserving decode, the merge semantics that pin the out-of-order teardown case, the empty-list no-consume case, and a repeated-recompose pass that narrows the record to empty. `pnpm run check` green (56 test files / **504** tests + pack gate).
20
+ - **Lab verification (official 0.2.1-rc.2, `DSH_HOME=~/.dsh-lab`, port 3082) — and one gap it exposed, now closed.** The build was installed in both lab profiles and driven against the released `0.4.35` as the A/B: with a session on「自动审查」the plugin was removed and re-added, a new plugin row was inserted through `cordis.patch.yml`, and a bundle-list change forced a recompose. The record half of the fix ran end to end there — a teardown wrote `auto-sessions.json` with the exact session id, the record survived the plugin being absent, and the next mount's replay consumed it. That replay is what the lab caught being incomplete: a claim runs before the web Host has lazily loaded the session, so ids could sit in the file forever with nobody to consume them. The replay now also runs whenever a session is observed, on tool-call activity, and from a bounded 10-second sweep while the record holds ids — the file observed still pending after a plugin swap was consumed by the next session load. The flip itself (`auto` → `danger-full-access`) does not reproduce in the lab — even a full plugin removal leaves the stored preset name untouched there — so that scenario stays covered by the desktop session-log forensics above and the out-of-order teardown tests. Open-item audit of the deletion path: the empty-list delete has **no second loss face** — every caller (`trackAutoSelection`'s explicit un-select, a fully consumed replay, a teardown merge, and the stale/malformed cleanup in `flushAutoSnapshot`) deletes only a record that already decodes to empty. The one theoretical residual is on the *recording* side, not the deletion side: a session that switched to Auto while never observed (listener not yet wired) plus a teardown whose live read fails at the same moment; the observation passes (claim, replay, tool calls, 10-second sweep) bound that window to one sweep interval. Closing it entirely needs a session-load event on the public `sessions` face, which does not exist — recorded for a later upstream proposal, per the repo's public-seam rule.
21
+
1
22
  ## v0.4.35
2
23
 
3
24
  「自动审查」不再在每次热重载后掉回「完全权限」:释放 Auto 注册之前先把当时选着 Auto 的会话落盘,重新认领后按 id 恢复。