dsh-model-switch 0.4.24 → 0.4.25
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 +11 -0
- package/PRODUCT.md +4 -2
- package/README.md +5 -11
- package/README.zh.md +5 -11
- package/lib/auto-review.js +57 -14
- package/lib/client.js +208 -139
- package/lib/compaction.js +97 -2
- package/lib/index.js +1 -301
- package/lib/{provider-group-CsZi7H29.js → provider-group-CwadImhH.js} +3 -9
- package/lib/{session-route-h9SAqajc.js → session-route-BCQFzqRP.js} +1 -1
- package/lib/session-title.js +1 -1
- package/lib/subagent-runtime.js +1 -1
- package/lib/types/adapter-registry.d.ts +1 -1
- package/lib/types/auto-review.d.ts +38 -11
- package/lib/types/client/ModelSwitchSettings.d.ts +0 -1
- package/lib/types/client/locales.d.ts +2 -5
- package/lib/types/client-contract.d.ts +0 -1
- package/lib/types/compaction.d.ts +50 -6
- package/lib/types/host-settings.d.ts +0 -2
- package/lib/types/provider-group.d.ts +4 -10
- package/package.json +1 -1
- package/lib/types/client/derived-settings-scope.d.ts +0 -4
- package/lib/types/client/picker/main-default-restore.d.ts +0 -56
- package/lib/types/client/picker/provider-group-watch.d.ts +0 -48
- package/lib/types/client/remote-settings-scope.d.ts +0 -45
- package/lib/types/switch-compact.d.ts +0 -72
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
## v0.4.25
|
|
2
|
+
|
|
3
|
+
Three coordinated changes: Provider groups now cover all four per-session capabilities (compaction, auto review, session naming, and subagents) with mid-session provider-switch following; capability switches stay disabled until the model catalog has loaded (and while any row save is in flight) so clicks are never silently dropped; and send-time switch compaction (`compactOnSwitch` / `src/switch-compact.ts`) has been removed completely.
|
|
4
|
+
|
|
5
|
+
- **Provider groups cover compaction, auto review, session naming, and subagents per session.**
|
|
6
|
+
- **Compaction (`src/compaction.ts`):** `mountEngine` mounts the official `BasicCompactionEngine` under `compactionScope(ctx)` (`ctx.extend({ llm: compactionLlm(...) })`). When `options.purpose === 'compaction'` and `options.sessionId` resolves to a session in `ctx.sessions`, `compactionLlm` derives the session's provider (`pending` selection preferred over `lastUsed`, then logged routes), resolves **provider group → flat compaction card → engine default**, and updates `options.provider` / `options.model` in place before delegating to `real.stream(options)`. Because `summarizeWithLlm` returns `{ provider: options.provider, model: options.model }`, the durable `compaction/summary` record always matches the exact model that wrote the summary, and edits to `providerGroups` apply to the very next compaction with zero engine remounts.
|
|
7
|
+
- **Auto review (`src/auto-review.ts`):** Immediately after running `official.apply(scope)`, `bindAutoReviewSessionCorrelation` registers a `{ prepend: true }` `tools/pre-execute` listener on the review scope — which Cordis places ahead of the official gate's own prepended listener in the waterfall — and binds `exec.agent?.session` in an `AsyncLocalStorage` scope around `next()`. When `classifyRisk` calls `scope.llm.stream(options)`, the wrapper reads the active session, resolves **provider group → flat reviewer card → session model**, and passes a shallow copy of the frozen options with the resolved route. Parallel sessions on different providers therefore review on their own configured models without cross-session interference, and mid-session provider switches take effect on the very next review.
|
|
8
|
+
- **Provider groups UI (`src/client/ModelSwitchSettings.tsx`):** Restored the **压缩模型 (`compaction`)** and **自动审查模型 (`autoReview`)** rows alongside **会话命名模型 (`sessionTitle`)** and **子代理默认路由 (`subagent`)** in the Provider groups card, seeded unsaved groups from all four active cards, and automatically reactivated stored `compactProvider`/`compactModel` and `autoReviewProvider`/`autoReviewModel` entries from v0.4.22/23 without migration.
|
|
9
|
+
- **Fixed intermittent unresponsive capability switches (`src/client/ModelSwitchSettings.tsx`).** Previously, clicking a capability switch (such as Session naming) before the async model catalog finished loading — or while another card's save was in flight — hit a silent `return` in `persist*Enabled` / `run()` while the `InstantSwitch` itself rendered enabled. Every switch's `disabled` prop now reflects `busy !== undefined`, `!writable`, `value === undefined`, and `catalogSeed === undefined` when enabling a seed-dependent route.
|
|
10
|
+
- **Removed send-time switch compaction (`compactOnSwitch` / `src/switch-compact.ts`).** Removed the Send protection (`发送保护` / `切换时压缩`) setting, UI section, and `agent/pre-step` hook. All compaction policy and triggers now belong exclusively to DSH's official compaction engine (`/compact` and automatic pressure/overflow compaction).
|
|
11
|
+
|
|
1
12
|
## v0.4.24
|
|
2
13
|
|
|
3
14
|
Three coordinated changes: session naming gains a configurable model; provider groups (and the Default Subagent route) become per-session derived policy instead of a write, fixing sessions on different providers overwriting or redirecting each other's auxiliary routes; and every settings row save becomes one atomic revision-fenced mutation, fixing the save-while-running freeze, half-written provider/model pairs, and the ambiguous ⚠ marker.
|
package/PRODUCT.md
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
- **Image Router:** owns a new, uniquely named stable `generate_image` tool. It selects the configured Codex or Grok adapter/model at execution time, rejects backend-incompatible fields, and returns normalized image metadata. Existing provider-specific image tools remain available as rollback paths.
|
|
12
12
|
- **Composer Picker:** replaces the composer model seat with a suffix-grouped Model / Effort / Context / Fast / Thinking picker and submits through the official Model Directory. Variant changes preserve an effort only when the target catalog row supports it, otherwise they use the target default or omit effort. Picker changes are session-local. A submitted prompt reserves the selected runtime immediately (composer submitting, pending first turn, or running). Native Antigravity keeps its models and effort; DSH keeps LLM-provider choices and disables Agent providers. After a successful native binding, other groups stay disabled. Cancel without a binding releases the reservation; existing DSH history still cannot convert onto an External Agent. The deployment default write a Host performs alongside a Session switch is left to DSH (since 0.4.23 the plugin no longer restores a pre-switch default).
|
|
13
13
|
- **Plan Review:** intercepts the official `plan-review` composer takeover with a header execution-model picker and right-aligned Discuss in chat / Reject / Set as goal and run / Confirm actions. Confirm commits the selected model before answering; a refused commit leaves the review pending and retryable. Reject carries optional human feedback as the answer's own `custom` text, which plan mode reports to the model. **Set as goal and run** arms this Plan as the session's goal through the released `remote.goals.create` — replacing an unfinished goal with `get` + `clear` first so the new Plan receives a fresh round cap — and only then answers, so `dsh-goal-round-driver` carries the Plan after plan mode exits. The goal is set before the answer, a refusal keeps the review pending and retryable, and a deployment that mounts no goal service disables the action with an explicit reason; the plugin stores no goal state, schedules no rounds, and owns no part of the goal domain. The card body carries only the official two-part summary (first line, first paragraph); the full plan opens in the right sidebar through ui-plan's registered preview opener, which this card drives because a composer-takeover entry cannot declare the official actions slot. Discuss in chat withdraws the takeover through the Host's own verb — `dismiss()` where the Host publishes it, `cancel()` on the older generation — and names the Host when neither seam exists, so a settled review is never a dead end. The same runtime reservation applies.
|
|
14
|
+
- **Auto review:** the reviewer model for the official `@deepseek-ai/dsh-experimental-auto-review` per-call gate, off by default — both fields empty means reviews follow the session's own model. On pins a deployment-wide provider/model, and each review resolves its route per session: the session's provider group's reviewer pair → the flat card pair → the session's own model. Session correlation rides an outer `{ prepend: true }` `tools/pre-execute` listener on the review scope (`exec.agent.session`), so parallel sessions on different providers never interfere and mid-session provider switches take effect on the very next review.
|
|
15
|
+
- **Compaction:** the summarization model behind `/compact` and DSH's automatic compaction, off by default — both fields empty means compaction follows the conversation target model. On pins a deployment-wide provider/model, and each summarization call resolves its route per session: the session's provider group's compaction pair → the flat card pair → the official row's `modelPolicies` / conversation-target default. When a per-session route applies, the child context's `llm` wrapper updates `options.provider`/`options.model` in place on `purpose === 'compaction'` calls so `summarizeWithLlm` returns and durably records the exact route that wrote the summary in `compaction/summary`.
|
|
14
16
|
- **Session naming:** the model behind DSH's automatic session titles, off by default — both fields empty means naming follows the session's own model, which is the official behavior. On pins a deployment-wide provider/model that each session re-resolves per request: provider group → flat pair → the session's own model. The route is read through on every naming request, so a save applies to the very next request with no restart and never interrupts an in-flight request; the exact pair used is recorded in the session's own `session/title-llm-request` event by the official naming implementation, so the record and the request always agree. The plugin replaces the official `session-title-llm` row (one registered naming provider per context) and mirrors the cadence that row declared; naming cadence and title policy remain DSH's.
|
|
15
17
|
|
|
16
18
|
Model choices are capability-driven. Invalid, unavailable, or unsupported selections remain visible to configuration surfaces and fail loudly at use; routing never silently falls back.
|
|
@@ -21,11 +23,11 @@ Official `agent-presets` stay in the process. A nested `ctx.plugin(AgentPresets)
|
|
|
21
23
|
|
|
22
24
|
## Settings and lifecycle
|
|
23
25
|
|
|
24
|
-
The settings surface aggregates public Settings namespaces, reads and mutates with optimistic revisions, and saves rows independently. Changing a fixed Subagent model applies only the target catalog row's default effort, so an effort never leaks across providers or models. The Subagent card is a RouteCard with an instant on/off for the Default Subagent route; turning it off keeps the last provider/model on disk.
|
|
26
|
+
The settings surface aggregates public Settings namespaces, reads and mutates with optimistic revisions, and saves rows independently. Changing a fixed Subagent model applies only the target catalog row's default effort, so an effort never leaks across providers or models. The Subagent card is a RouteCard with an instant on/off for the Default Subagent route; turning it off keeps the last provider/model on disk. The Auto review, Compaction, and Session naming cards are RouteCards with instant switches defaulting **off = follow the session/conversation model**; turning a switch on pins the catalog head as an editable starting point, and the switch stays disabled until the async model catalog has loaded (or while any row save is in flight) so clicks are never silently dropped. The settings page hosts six route cards — Subagent, Web search, Image generation, Auto review, Compaction, and Session naming — plus the Provider groups card. Provider groups store one compaction / auto-review / session-naming / subagent route group per provider in the shared `model-switch` namespace as a DERIVED policy, never a write: each session and each request resolves those four routes from its own provider (`pending` selection preferred over `lastUsed`, then logged routes) — group pair → flat deployment-wide pair → official default — so switching a session's provider writes nothing, parallel sessions on different providers never overwrite each other, and mid-session provider switches take effect on the very next call. Every row save is one atomic, revision-fenced mutation through the official ConfigForm `mutate`.
|
|
25
27
|
|
|
26
28
|
## Exclusions
|
|
27
29
|
|
|
28
|
-
Send-time
|
|
30
|
+
Send-time switch compaction (`compactOnSwitch`) was removed in 0.4.25: injecting plugin-authored compaction checkpoints and notices during model switches interacted with the official `compaction-basic` step-pressure loop when a session's working set stayed near the target window's pressure threshold. All compaction policy and triggers belong exclusively to DSH's official compaction engine (`/compact` and automatic pressure/overflow compaction). The Auto reviewer route override rides a name-matched replacement of the experimental auto-review row and changes only the review request's provider/model. The compaction summarization route override rides a name-matched replacement of the official compaction-basic engine and changes only the summarization request's provider/model, forwarding the row's own engine config unchanged. Because the Web surface runs session compaction inside each agent preset's isolated compaction group and profile patches cannot address rows inside a preset's plugins list, the plugin replaces the shipped preset rows wholesale (the Web preset editor's own save mechanism) to swap that group's engine; the copies are re-synced per DSH release during the existing compatibility verification, and a deployment that edits presets in the Web editor owns the merge from its own wholesale row. Model Switch does not own global compaction policy, titles, fetch routing, fallback, load balancing, provider credentials, CLI/API, config migration, or DSH Core modifications.
|
|
29
31
|
|
|
30
32
|
Official `settings.section` has no icon field. The Settings nav glyph is a label-matched DOM swap, the same published pattern as usage-monitor, until an official icon seam exists. The Plan Review takeover uses priority -5, and the composer model seat uses priority -10.
|
|
31
33
|
|
package/README.md
CHANGED
|
@@ -90,19 +90,15 @@ The card body shows only the Plan's first-line title and first-paragraph summary
|
|
|
90
90
|
|
|
91
91
|

|
|
92
92
|
|
|
93
|
-
## Send-time context protection
|
|
94
|
-
|
|
95
|
-
When a sent message will use a different model or context-tier id than the last request, Model Switch checks the full pending request against the target window before that request is sent. Choosing a model in the picker does not compact. If the target is over budget, history is compacted once with the previous model through a private compaction engine; DSH's own automatic compaction settings are not changed. Progress appears as plugin notices in the transcript. The budget includes the completion notice and pending model-change notice; an oversized summary blocks the request. Failure keeps the user message in the session and does not send the target request or emit a switch-success notice. Turn the behavior off with **Settings → Model Switch → Check and compact context on send** (on by default). Native External Agent runtimes are not treated as DSH-compactable history.
|
|
96
|
-
|
|
97
93
|
## Auto review model
|
|
98
94
|
|
|
99
|
-
The official `@deepseek-ai/dsh-experimental-auto-review` (the per-call reviewer for the Auto permission preset) hardwires its review requests onto the session's own route. This plugin takes that row over through a `cordis.patch.yml` row replacement: the official row is disabled and `dsh-model-switch/auto-review` mounts the **same official `apply`** under a scoped child context whose `llm.stream` `provider/model` are rewritten to the reviewer model
|
|
95
|
+
The official `@deepseek-ai/dsh-experimental-auto-review` (the per-call reviewer for the Auto permission preset) hardwires its review requests onto the session's own route. This plugin takes that row over through a `cordis.patch.yml` row replacement: the official row is disabled and `dsh-model-switch/auto-review` mounts the **same official `apply`** under a scoped child context whose `llm.stream` `provider/model` are rewritten to the reviewer route resolved for that session (provider group → flat card → session model). Session correlation rides a `{ prepend: true }` `tools/pre-execute` listener registered on the review scope immediately after `official.apply(scope)`, binding `exec.agent.session` in an `AsyncLocalStorage` scope around `next()` so parallel sessions on different providers never interfere and mid-session provider switches take effect on the very next review. The "Auto review model" card defaults to off = reviews follow the session model; turning it on pins the catalog head as an editable starting point, and saved changes apply to the very next review. When the optional auto-review bundle never started with the profile the row stays unmounted and the Auto preset keeps its official off state; an official row already active elsewhere is reported explicitly instead of producing a second `registerAuto`.
|
|
100
96
|
|
|
101
97
|
## Compaction model
|
|
102
98
|
|
|
103
99
|
Official compaction — `/compact` and DSH's automatic compaction — summarizes history with the conversation's target model by default. The "Compaction model" card (default off) pins that summarization route instead: turning it on pins the catalog head as an editable starting point, and saved changes apply to the very next compaction. Leaving both fields empty returns to the official default. Saving while a compaction is running may interrupt it; it retries next time.
|
|
104
100
|
|
|
105
|
-
|
|
101
|
+
Each summarization call (`options.purpose === 'compaction'`) also resolves its route per session through `compactionScope`: the session's provider group's compaction pair → the flat card pair → the official row's `modelPolicies` / conversation-target default. When a route applies, the child context's `llm` wrapper updates `options.provider` and `options.model` in place before `ctx.llm.stream(options)`, so `summarizeWithLlm` returns and durably records the exact provider/model that wrote the summary in `compaction/summary`. Exact-target `modelPolicies` on the profile's `compaction-basic` row config are still forwarded verbatim:
|
|
106
102
|
|
|
107
103
|
```yaml
|
|
108
104
|
- id: compaction-basic
|
|
@@ -115,7 +111,7 @@ The pair stays deployment-wide by design: the official compaction transaction du
|
|
|
115
111
|
summarizationModel: deepseek-reasoner
|
|
116
112
|
```
|
|
117
113
|
|
|
118
|
-
How the route reaches the engine depends on where the deployment runs its agent plane. The Web surface moves compaction into each agent preset's isolated `compaction` group, and profile patches cannot address rows inside a preset's `plugins` list — so this plugin replaces each shipped preset row wholesale (the same id-targeted mechanism the Web preset editor saves with) and swaps that group's official `compaction-basic` for `model-switch-compaction`, which mounts the **same official `BasicCompactionEngine`**
|
|
114
|
+
How the route reaches the engine depends on where the deployment runs its agent plane. The Web surface moves compaction into each agent preset's isolated `compaction` group, and profile patches cannot address rows inside a preset's `plugins` list — so this plugin replaces each shipped preset row wholesale (the same id-targeted mechanism the Web preset editor saves with) and swaps that group's official `compaction-basic` for `model-switch-compaction`, which mounts the **same official `BasicCompactionEngine`** under `compactionScope`. Surfaces that compose the agent process-wide (the TUI) use the top-level row replacement instead. Two consequences: a DSH release that edits those presets needs the copies re-synced (this plugin's per-release verification covers it), and a deployment that edits presets in the Web editor writes its own wholesale row that then owns the merge.
|
|
119
115
|
|
|
120
116
|
Known issue (upstream; recorded in `docs/seam-gap.md`): while the working set stays above the official engine's pressure threshold, automatic compaction can fire on every step and keep re-attempting ranges whose summaries fail the engine's own "summary must be smaller" guard — a visible loop of automatic compactions that this plugin does not control (verified in a session A/B with the plugin enabled and disabled). Mitigations on the forwarded row config: `auto: false` (keeps `/compact`), a higher `thresholdRatio`, a smaller mounted tool schema, or a pinned compaction model whose summaries are leaner.
|
|
121
117
|
|
|
@@ -127,11 +123,9 @@ The route is read through on every naming request: a saved change applies to the
|
|
|
127
123
|
|
|
128
124
|
## Provider groups
|
|
129
125
|
|
|
130
|
-
The "Provider groups" card stores one route group per provider — the session-naming and subagent routes saved together — and a group saved for provider X applies to the sessions whose own provider is X. Groups are **derived, not written**: switching a session's provider writes nothing, so two sessions on different providers can
|
|
131
|
-
|
|
132
|
-
Every request derives its own route from durable state: the session's provider (its model selection — a just-made pending selection counts — else its latest logged request route), then the group pair for that provider, then the flat deployment-wide pair, then the official default (the session's own model for naming; official inherit for subagents, where the fixed card applies only in `fixed` mode). Because nothing is written, this works for sessions this browser never opened and for switches made from the TUI or another client, with no watcher and no residual boundary; a session with no observable provider simply resolves to the deployment default. A switch to a provider without a group changes nothing, and a switch within one provider re-resolves to the same routes.
|
|
126
|
+
The "Provider groups" card stores one route group per provider — the compaction, auto-review, session-naming, and subagent routes saved together — and a group saved for provider X applies to the sessions whose own provider is X. Groups are **derived, not written**: switching a session's provider writes nothing, so two sessions on different providers can never overwrite or redirect each other's auxiliary routes.
|
|
133
127
|
|
|
134
|
-
|
|
128
|
+
Every request derives its own route from durable state: the session's provider (its model selection — a just-made pending selection counts — else its latest logged request route), then the group pair for that provider, then the flat deployment-wide pair, then the official default (the conversation target for compaction; the session's own model for auto review and naming; official inherit for subagents, where the fixed card applies only in `fixed` mode). Because nothing is written, this works for sessions this browser never opened and for switches made from the TUI or another client, with no watcher and no residual boundary; a session with no observable provider simply resolves to the deployment default. Mid-session provider switches take effect on the very next compaction, review, title, or subagent call. In the editor, a provider's draft seeds from the currently active routes the first time you configure it, each row's off state means "no pin — fall through to the card, then the official default", and **Delete group** removes the provider's entry entirely (clearing the stored field when the last group goes).
|
|
135
129
|
|
|
136
130
|
## What Model Switch does not change
|
|
137
131
|
|
package/README.zh.md
CHANGED
|
@@ -88,19 +88,15 @@ Plan Review 拥有独立于会话当前路由的执行模型草稿。**确认执
|
|
|
88
88
|
|
|
89
89
|

|
|
90
90
|
|
|
91
|
-
## 发送时的上下文保护
|
|
92
|
-
|
|
93
|
-
当发送后的实际请求将使用与上次不同的模型或窗口档位时,Model Switch 会在目标请求发出前检查完整待发上下文。仅在选择器中选模型不会压缩。超出目标可用容量时,使用切换前的模型、通过私有压缩引擎压缩一次历史;不会改 DSH 自身的自动压缩设置。进度以插件通知出现在正文中。预算包含完成提示和待提交的模型切换提示;摘要仍超限时会阻止请求。失败时用户消息留在会话里,不会发送目标模型请求,也不会出现切换成功提示。可在 **设置 → 模型切换 → 发送时自动检查并压缩上下文** 关闭(默认开启)。原生 External Agent 运行时的内部上下文不会被当成 DSH 可压缩历史。
|
|
94
|
-
|
|
95
91
|
## 自动审查模型(Auto review)
|
|
96
92
|
|
|
97
|
-
官方 `@deepseek-ai/dsh-experimental-auto-review`(Auto 权限预设的逐调用审查)把审查请求固定在会话自身的路由上。本插件通过 `cordis.patch.yml` 的行替换接管这一行:官方行被禁用,改挂 `dsh-model-switch/auto-review`,它在作用域子上下文里运行**同一份官方 `apply
|
|
93
|
+
官方 `@deepseek-ai/dsh-experimental-auto-review`(Auto 权限预设的逐调用审查)把审查请求固定在会话自身的路由上。本插件通过 `cordis.patch.yml` 的行替换接管这一行:官方行被禁用,改挂 `dsh-model-switch/auto-review`,它在作用域子上下文里运行**同一份官方 `apply`**,把 `llm.stream` 的 `provider/model` 改写为该会话解析出的审查模型(供应商配置组 → 全局审查卡片 → 会话自身模型)。在 `official.apply(scope)` 之后,本行立即在同一作用域注册 `{ prepend: true }` 的 `tools/pre-execute` 监听器(在 Cordis 瀑布流中排在官方监听器之前),用 `AsyncLocalStorage` 将 `exec.agent.session` 绑定到审查调用链上,因此不同供应商的多个会话并行时互不干扰,会话中途切换供应商也会在下一次审查立即生效。设置页「自动审查模型」默认关闭 = 审查跟随当前会话模型;打开时以目录首位模型作为可再改的固定起点,保存后对下一次审查立即生效。审查未配置官方能力包(`@deepseek-ai/dsh-experimental-auto-review` 未随 profile 启动)时本行不挂载,Auto 预设保持官方的关闭状态;官方行若已在别处注册,本行明确报错而不产生第二个 `registerAuto`。
|
|
98
94
|
|
|
99
95
|
## 压缩模型(Compaction model)
|
|
100
96
|
|
|
101
97
|
官方压缩——`/compact` 与 DSH 的自动压缩——默认使用会话当前的目标模型做摘要。「压缩模型」卡片(默认关闭)改为固定摘要路由:打开时以目录首位模型作为可再改的固定起点,保存后对下一次压缩立即生效。两个字段都留空即回到官方默认。保存时若恰有压缩进行中,可能被打断并在下次重试。
|
|
102
98
|
|
|
103
|
-
|
|
99
|
+
每次摘要调用(`options.purpose === 'compaction'`)同样通过 `compactionScope` 按会话实时解析路由:该会话供应商组的压缩对 → 全局压缩卡片对 → 官方行的 `modelPolicies` / 会话目标模型默认。当解析出按会话路由时,子上下文的 `llm` 包装器在调用 `ctx.llm.stream(options)` 前原地更新 `options.provider` 与 `options.model`,从而让官方 `summarizeWithLlm` 返回并持久记录到 `compaction/summary` 的模型对与实际写摘要的模型完全一致。同时,profile 的 `compaction-basic` 行配置里的精确目标 `modelPolicies` 数组仍原样转发:
|
|
104
100
|
|
|
105
101
|
```yaml
|
|
106
102
|
- id: compaction-basic
|
|
@@ -113,7 +109,7 @@ Plan Review 拥有独立于会话当前路由的执行模型草稿。**确认执
|
|
|
113
109
|
summarizationModel: deepseek-reasoner
|
|
114
110
|
```
|
|
115
111
|
|
|
116
|
-
路由如何到达压缩引擎取决于部署在哪一层组合 agent。Web 面把压缩放进每个 Agent 预设的隔离 `compaction` 组里,而 profile patch 无法寻址 preset `plugins` 列表内部的行——因此本插件整表替换各预设行(与 Web 预设编辑器保存修改用的是同一套 id 定位机制),把该组的官方 `compaction-basic` 换成 `model-switch-compaction
|
|
112
|
+
路由如何到达压缩引擎取决于部署在哪一层组合 agent。Web 面把压缩放进每个 Agent 预设的隔离 `compaction` 组里,而 profile patch 无法寻址 preset `plugins` 列表内部的行——因此本插件整表替换各预设行(与 Web 预设编辑器保存修改用的是同一套 id 定位机制),把该组的官方 `compaction-basic` 换成 `model-switch-compaction`,由它在 `compactionScope` 下挂载**同一份官方 `BasicCompactionEngine`**。进程级组合 agent 的面(如 TUI)则走顶层行替换。两个后果:DSH 发布若改动这些预设,随包副本需要同步(本插件的逐版本核对覆盖这一步);部署若用 Web 编辑器改预设,会写出自己的整表行、并从那之后自行负责合并。
|
|
117
113
|
|
|
118
114
|
已知问题(上游侧,见 `docs/seam-gap.md`):当工作集持续超过官方引擎的压力阈值时,自动压缩可能每步触发,并反复重试摘要过不了官方「摘要必须更小」守卫的小片段——表现为"不停自动压缩",本插件不控制该行为(已在同一会话做过插件开/关 A/B 对照)。可用的缓解(都在转发行配置上):`auto: false`(保留 `/compact`)、调高 `thresholdRatio`、减少挂载的工具 schema,或固定一个摘要更精炼的压缩模型。
|
|
119
115
|
|
|
@@ -125,11 +121,9 @@ DSH 会自动为新会话起名;命名的触发节奏由 profile 的官方命
|
|
|
125
121
|
|
|
126
122
|
## 供应商配置组(Provider groups)
|
|
127
123
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
每次请求都从持久状态推导自己的路由:先取该会话的供应商(它的模型选择——刚做出、尚未被请求消费的 pending 选择优先——其次最近一次已记录的请求路由),再取该供应商的组对,再取部署级的平铺对,最后是官方默认(命名用会话自身模型;子代理用官方继承,且平铺卡片仅在 `fixed` 模式下生效)。因为什么都不写,本浏览器从未打开过的会话、以及来自 TUI 或其他客户端的切换同样生效——没有 watcher,也没有剩余边界;观察不到供应商的会话直接落到部署默认。切换到没有配置组的供应商不改变任何东西,同一供应商内部切换推导出的还是同一些路由。
|
|
124
|
+
「供应商配置组」卡片为每个供应商保存一组路由——压缩、自动审查、会话命名与子代理四条路由一起存;为供应商 X 保存的组,作用于自身供应商就是 X 的会话。组是**推导出来的,而不是写进去的**:切换某个会话的供应商不写任何东西,因此不同供应商上的多个会话并行时再也不会互相覆盖、互相改写对方的辅助路由。
|
|
131
125
|
|
|
132
|
-
|
|
126
|
+
每次请求都从持久状态推导自己的路由:先取该会话的供应商(它的模型选择——刚做出、尚未被请求消费的 pending 选择优先——其次最近一次已记录的请求路由),再取该供应商的组对,再取部署级的平铺对,最后是官方默认(压缩用会话目标模型;自动审查与命名用会话自身模型;子代理用官方继承,且平铺卡片仅在 `fixed` 模式下生效)。因为什么都不写,本浏览器从未打开过的会话、以及来自 TUI 或其他客户端的切换同样生效——没有 watcher,也没有剩余边界;观察不到供应商的会话直接落到部署默认。会话中途切换供应商后,下一次压缩、自动审查、命名或子代理调用都会立即跟随新供应商的配置组。编辑器里首次配置某供应商时,草稿从当前生效的路由种子化;每行的关闭态表示"不固定——先落到卡片、再落到官方默认";「删除该组」彻底移除该供应商的条目(删掉最后一组时同时清掉存储字段)。
|
|
133
127
|
|
|
134
128
|
## Model Switch 不会改变什么
|
|
135
129
|
|
package/lib/auto-review.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { n as sessionProviderOf, t as resolveCapabilityRoute } from "./session-route-BCQFzqRP.js";
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
1
3
|
//#region lib/types/auto-review.js
|
|
2
4
|
/**
|
|
3
5
|
* Model Switch-owned Auto reviewer row.
|
|
@@ -6,17 +8,20 @@
|
|
|
6
8
|
* reviewer to the session's own request route (`header.config`). This row runs
|
|
7
9
|
* the official `apply` under a scoped child context whose `llm` service is
|
|
8
10
|
* shadowed by a wrapper that rewrites only `stream()`'s provider/model — and
|
|
9
|
-
* only
|
|
10
|
-
*
|
|
11
|
-
*
|
|
11
|
+
* only when the reviewing session's derived route (its provider group's
|
|
12
|
+
* reviewer pair, else the deployment-wide reviewer card) pins one. With no
|
|
13
|
+
* route pinned the wrapper passes options through by identity, so the reviewer
|
|
14
|
+
* follows the session model exactly.
|
|
12
15
|
*
|
|
13
|
-
*
|
|
14
|
-
* its
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
16
|
+
* Although the official gate omits `sessionId` and `purpose` from its frozen
|
|
17
|
+
* `GenerateOptions`, its `tools/pre-execute` waterfall listener receives
|
|
18
|
+
* `exec.agent` (`exec.agent.session`). After running `official.apply(scope)`,
|
|
19
|
+
* this module registers a `{ prepend: true }` `tools/pre-execute` listener on
|
|
20
|
+
* the same scope — which Cordis places ahead of the official gate's listener in
|
|
21
|
+
* the waterfall — and binds `exec.agent?.session` in an `AsyncLocalStorage`
|
|
22
|
+
* scope around `next()`. Every `scope.llm.stream()` call from `classifyRisk`
|
|
23
|
+
* therefore resolves the exact session being reviewed, with zero cross-session
|
|
24
|
+
* interference when multiple sessions review tool calls concurrently.
|
|
20
25
|
*
|
|
21
26
|
* The official gate stays an opt-in capability: the row mounts it only when
|
|
22
27
|
* the profile actually started with the experimental-auto-review bundle.
|
|
@@ -78,12 +83,47 @@ function reviewLlm(real, route) {
|
|
|
78
83
|
} });
|
|
79
84
|
}
|
|
80
85
|
/**
|
|
86
|
+
* Resolve the reviewer route for one session (or deployment-wide when no session is bound).
|
|
87
|
+
*
|
|
88
|
+
* @param ctx - host context whose modelSwitch settings and sessionProjections derive the route.
|
|
89
|
+
* @param session - the session whose tool call is being reviewed, when known.
|
|
90
|
+
* @returns The derived reviewer route, or undefined to follow the session model.
|
|
91
|
+
*/
|
|
92
|
+
function resolveSessionAutoReviewRoute(ctx, session) {
|
|
93
|
+
let settings;
|
|
94
|
+
try {
|
|
95
|
+
settings = ctx.modelSwitch.currentSettings();
|
|
96
|
+
} catch {
|
|
97
|
+
settings = void 0;
|
|
98
|
+
}
|
|
99
|
+
const provider = sessionProviderOf(ctx, session);
|
|
100
|
+
return resolveCapabilityRoute(settings, provider, "autoReview");
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Install the outer `tools/pre-execute` correlation listener ahead of the official gate.
|
|
104
|
+
*
|
|
105
|
+
* Because Cordis `on(..., { prepend: true })` unshifts onto the event hook list,
|
|
106
|
+
* registering this listener AFTER `official.apply(scope)` places it at index 0
|
|
107
|
+
* — directly ahead of the official gate's own prepended listener — so
|
|
108
|
+
* `activeSession.run(exec.agent?.session, next)` wraps the entire official
|
|
109
|
+
* review execution.
|
|
110
|
+
*
|
|
111
|
+
* @param scope - the review scope context where the gate is mounted.
|
|
112
|
+
* @param activeSession - AsyncLocalStorage holding the session under review.
|
|
113
|
+
*/
|
|
114
|
+
function bindAutoReviewSessionCorrelation(scope, activeSession) {
|
|
115
|
+
scope.effect(function* () {
|
|
116
|
+
yield scope.on("tools/pre-execute", (exec, next) => activeSession.run(exec.agent?.session, next), { prepend: true });
|
|
117
|
+
}, "model-switch: auto-review session correlation");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
81
120
|
* Build the scoped context the official gate runs under.
|
|
82
121
|
* @param ctx - host context whose modelSwitch settings pin the reviewer route.
|
|
122
|
+
* @param activeSession - optional AsyncLocalStorage correlating each `tools/pre-execute` call to its session.
|
|
83
123
|
* @returns A child context shadowing only the llm service.
|
|
84
124
|
*/
|
|
85
|
-
function reviewScope(ctx) {
|
|
86
|
-
return ctx.extend({ llm: reviewLlm(ctx.llm, () =>
|
|
125
|
+
function reviewScope(ctx, activeSession = new AsyncLocalStorage()) {
|
|
126
|
+
return ctx.extend({ llm: reviewLlm(ctx.llm, () => resolveSessionAutoReviewRoute(ctx, activeSession.getStore())) });
|
|
87
127
|
}
|
|
88
128
|
/** Structural read of launcher-provided profile facts (the app-boot seam). */
|
|
89
129
|
function autoReviewBundleActive(ctx) {
|
|
@@ -117,12 +157,15 @@ async function apply(ctx) {
|
|
|
117
157
|
ctx.logger.error(cause);
|
|
118
158
|
return;
|
|
119
159
|
}
|
|
160
|
+
const activeSession = new AsyncLocalStorage();
|
|
161
|
+
const scope = reviewScope(ctx, activeSession);
|
|
120
162
|
try {
|
|
121
|
-
official.apply(
|
|
163
|
+
official.apply(scope);
|
|
164
|
+
bindAutoReviewSessionCorrelation(scope, activeSession);
|
|
122
165
|
} catch (cause) {
|
|
123
166
|
ctx.logger.error("model-switch: the official auto-review row is already active; the reviewer route override is unavailable");
|
|
124
167
|
ctx.logger.error(cause);
|
|
125
168
|
}
|
|
126
169
|
}
|
|
127
170
|
//#endregion
|
|
128
|
-
export { apply, autoReviewRouteOf, inject, name, reviewLlm, reviewScope };
|
|
171
|
+
export { apply, autoReviewRouteOf, bindAutoReviewSessionCorrelation, inject, name, resolveSessionAutoReviewRoute, reviewLlm, reviewScope };
|