dsh-model-switch 0.4.18 → 0.4.20
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 +19 -0
- package/PRODUCT.md +4 -4
- package/README.md +10 -0
- package/README.zh.md +10 -0
- package/cordis.patch.yml +12 -0
- package/lib/auto-review.js +120 -0
- package/lib/client.js +664 -84
- package/lib/index.js +251 -6
- package/lib/types/auto-review.d.ts +68 -0
- package/lib/types/capabilities-rpc.d.ts +3 -2
- package/lib/types/client/ModelSwitchSettings.d.ts +3 -1
- package/lib/types/client/locales.d.ts +7 -0
- package/lib/types/client/picker/PlanReviewCard.d.ts +9 -0
- package/lib/types/client/picker/locales.d.ts +18 -0
- package/lib/types/client/picker/plan-preview.d.ts +49 -0
- package/lib/types/client-contract.d.ts +12 -0
- package/lib/types/host-runtime.d.ts +6 -0
- package/lib/types/host-settings.d.ts +15 -0
- package/lib/types/picker/plan-compact.d.ts +48 -0
- package/lib/types/picker/plan-goal.d.ts +77 -0
- package/lib/types/plan-compact.d.ts +82 -0
- package/package.json +14 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## v0.4.20
|
|
2
|
+
|
|
3
|
+
Three coordinated additions: the Plan Review card shows only the official summary and restores the right-sidebar full-plan preview; the official Auto reviewer's model becomes configurable; and switching models at plan exit compacts long context first.
|
|
4
|
+
|
|
5
|
+
- **Plan Review body = summary, full plan in the sidebar.** The card renders the official two-part summary (`extractMarkdownPlainText` first-line + first-paragraph) instead of the whole document, with a "View full plan" header action. The full plan auto-opens in the right sidebar by driving ui-plan's registered preview opener: a composer-takeover entry cannot render `conversation.plan-review.actions` (one declarer per child slot, plus per-entry `renderSlot` authorization), so the card drives ui-plan's own inject face — address, params, logged-plan branch, and mounted-session gate all stay ui-plan's. `planReviewOf` now passes `intent.callId` through, matching the official narrow. Recorded in `docs/seam-gap.md`.
|
|
6
|
+
- **Auto review model.** New settings card (default **off = reviews follow the session model**; on seeds from Main) pins the reviewer route in the shared `model-switch` namespace. `cordis.patch.yml` disables the official `auto-review` row and inserts `dsh-model-switch/auto-review`, which runs the **same official `apply`** under a scoped child context whose `llm.stream` `provider/model` are rewritten only when both settings fields are set (identity passthrough otherwise, route re-read per call). The row mounts only when the profile started with the optional `@deepseek-ai/dsh-experimental-auto-review` bundle and reports an already-active official row instead of double-registering. Optional peer `@deepseek-ai/dsh-experimental-auto-review >=0.1.7-rc.2` (lowest verified; the devDep pins the matching 0.1.7-rc.2 line).
|
|
7
|
+
- **Plan-exit compaction.** When an approval switches provider/model and the estimated next request exceeds the new threshold (tokens, default 200,000, `0` disables, edited in the Send protection group), the card calls a new `plan-compact` plugin-RPC endpoint **before** committing anything: the Host prices the session with `ctx.tokenMeter` (larger of the two envelope prices) and, over threshold, summarizes once through an isolated `compaction-basic` engine pinned to the switched-to model via the turn-safe `compactIfNeeded(..., 'context-overflow')` — `exit_plan_mode` awaits its question inside a live tool call, so the idle-only `compactNow` cannot run at plan exit. Then the model commits and the review answers; **Set as goal and run** compacts before arming the goal. Same-model or effort-only approvals never trigger the endpoint, an empty history proceeds after a notice, and a failed compaction keeps the review pending and retryable. Seams and the borrowed overflow trigger are recorded in `docs/seam-gap.md`.
|
|
8
|
+
- Verification: `pnpm run check` green (`tsc` + `tsdown`, 50 test files / 348 tests, pack gate); reverse verification — restoring the full-plan body, removing the switch gate, and dropping the route rewrite each turn the targeted tests red, then green again after restore.
|
|
9
|
+
|
|
10
|
+
## v0.4.19
|
|
11
|
+
|
|
12
|
+
Plan Review gains a **Set as goal and run** action: it arms the reviewed Plan as the session's goal and only then answers it, so `dsh-goal-round-driver` carries the Plan after plan mode exits.
|
|
13
|
+
|
|
14
|
+
- The goal is set before the answer, matching the card's existing commit-then-answer order; a refusal keeps the review pending and reports the Host's own wire code.
|
|
15
|
+
- An unfinished goal is replaced through `get` + `clear` + `create` rather than edited, so a new Plan starts with a fresh round cap, and the card says it replaced the previous goal.
|
|
16
|
+
- The goal face is resolved per render (`ctx.get('remote.goals', false)`) instead of listed in the static client `inject`, so a deployment that mounts no `@deepseek-ai/dsh-goal` keeps loading this plugin and the action disables itself with an explicit reason while the other three actions keep working. `@deepseek-ai/dsh-goal` is declared as an optional peer.
|
|
17
|
+
- The objective is the Plan's trimmed markdown. The plugin stores no goal state, owns no part of the goal domain, and schedules no rounds.
|
|
18
|
+
- Records the seam in `docs/seam-gap.md` (the released `remote.goals.create` exists while DSH's own goal surface deliberately omits creation) and the action in PRODUCT.md and both READMEs.
|
|
19
|
+
|
|
1
20
|
## v0.4.18
|
|
2
21
|
|
|
3
22
|
Declare official DSH `0.2.0-rc.2` compatible in `package.json#dsh.compatibility.dshReleases`.
|
package/PRODUCT.md
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
- **Vision:** excluded. Model Switch exposes no Vision setting, does not shadow `read_image`, and does not preprocess chat attachments.
|
|
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. On a Host version that also persists session selection as the deployment default, the plugin restores the pre-switch Main default through the public Settings RPC with a revision fence; a concurrent Settings-page edit wins.
|
|
13
|
-
- **Plan Review:** intercepts the official `plan-review` composer takeover with a header execution-model picker and right-aligned Discuss in chat / Reject / Confirm actions. Confirm commits the selected model before answering; a refused commit leaves the review pending and retryable, while a deployment default this plugin could not restore is announced after the answer instead of holding the human's decision back. Reject carries optional human feedback as the answer's own `custom` text, which plan mode reports to the model. 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.
|
|
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, while a deployment default this plugin could not restore is announced after the answer instead of holding the human's decision back. 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. **Plan-exit compaction:** an approval that switches provider/model first runs the Host's compaction check — over a user threshold (tokens, default 200,000, 0 = off) it summarizes history once with the switched-to model, then commits and answers; same-model or effort-only approvals never trigger it, nothing compactable proceeds after a notice, and a failed compaction keeps the review pending and retryable. 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
14
|
|
|
15
15
|
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.
|
|
16
16
|
|
|
@@ -20,14 +20,14 @@ Official `agent-presets` stay in the process. A nested `ctx.plugin(AgentPresets)
|
|
|
20
20
|
|
|
21
21
|
## Settings and lifecycle
|
|
22
22
|
|
|
23
|
-
The settings surface aggregates public Settings namespaces, reads and mutates with optimistic revisions, and saves rows independently. Changing a Main or 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. Switch compaction is a B-row in the Send protection group (发送保护), default on, written immediately. Every registration, watcher, and dynamic tool generation has clean disposal. All visible copy lives in zh/en locale dictionaries.
|
|
23
|
+
The settings surface aggregates public Settings namespaces, reads and mutates with optimistic revisions, and saves rows independently. Changing a Main or 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. Switch compaction is a B-row in the Send protection group (发送保护), default on, written immediately; the Plan-exit compaction threshold is the adjacent numeric B-row (tokens, default 200,000, 0 disables, validated as a non-negative integer on blur/Enter). The Auto review card is a RouteCard with an instant switch defaulting **off = reviews follow the session model**; turning it on seeds the pinned reviewer route from Main and stays editable. Every registration, watcher, and dynamic tool generation has clean disposal. All visible copy lives in zh/en locale dictionaries.
|
|
24
24
|
|
|
25
25
|
## Exclusions
|
|
26
26
|
|
|
27
|
-
Send-time context protection (check, and compact with the previous model when a sent message would exceed the target window)
|
|
27
|
+
Send-time context protection (check, and compact with the previous model when a sent message would exceed the target window) and plan-exit compaction (summarize once with the switched-to model before a switched approval executes) are in scope. 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. Model Switch does not own global compaction policy, titles, fetch routing, fallback, load balancing, provider credentials, CLI/API, configuration history, or DSH Core edits. Coordinated changes are limited to Model Switch and self-maintained provider plugins. Installing this package replaces a standalone `dsh-composer-picker` install; keep only one composer model seat.
|
|
28
28
|
|
|
29
29
|
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.
|
|
30
30
|
|
|
31
31
|
## Delivery architecture
|
|
32
32
|
|
|
33
|
-
The package foundation and pure deep modules remain the policy/test surface. Runtime delivery disables the independent Subagent Loader row with its official name and inserts a uniquely identified replacement. Official AgentPresets stay mounted so they share the process `dsh-scope` identity. Search uses the released `ctx.web.registerSearchProvider()` seam so official tool ownership is preserved. Image uses a unique tool name and provider-owned authenticated adapters. Self-maintained Codex/Grok plugins register optional adapters through the public Model Switch service when present; standalone provider behavior is unchanged. Vision remains disabled. Replacement modules preserve the official public interfaces and fail closed when compatibility or capability validation fails.
|
|
33
|
+
The package foundation and pure deep modules remain the policy/test surface. Runtime delivery disables the independent Subagent Loader row with its official name and inserts a uniquely identified replacement; the experimental auto-review row is disabled the same way and replaced by `dsh-model-switch/auto-review`, which runs the official gate's `apply` unchanged under a scoped `llm` shadow (one `registerAuto`, and only when the profile started with the optional auto-review bundle). Official AgentPresets stay mounted so they share the process `dsh-scope` identity. Search uses the released `ctx.web.registerSearchProvider()` seam so official tool ownership is preserved. Image uses a unique tool name and provider-owned authenticated adapters. Self-maintained Codex/Grok plugins register optional adapters through the public Model Switch service when present; standalone provider behavior is unchanged. Vision remains disabled. Replacement modules preserve the official public interfaces and fail closed when compatibility or capability validation fails.
|
package/README.md
CHANGED
|
@@ -79,12 +79,22 @@ Plan Review owns an execution-model draft separate from Main. **Confirm** first
|
|
|
79
79
|
|
|
80
80
|
**Reject** sends the option the asker offered, and any text typed in the card's feedback field as the answer's own `custom` text — plan mode reports that text back to the model as the reason to keep planning, so a rejection can carry what to change. **Discuss in chat** withdraws the takeover so the composer returns and the human speaks in their own words; it uses the Host's `dismiss()` where published and the older generation's `cancel()` otherwise, and names the Host when neither exists. Answering a settled review closes Confirm and Reject, while Discuss and the reload affordance stay reachable so the card always has a way back. Failures keep their wire code (`session/writer-held: …`) visible instead of a bare localized sentence.
|
|
81
81
|
|
|
82
|
+
**Set as goal and run** arms this Plan as the session's goal and only then answers it, so `dsh-goal-round-driver` keeps the Plan moving after plan mode exits. It calls the released `remote.goals.create`; when a non-complete goal already exists it clears that one first so the new Plan starts with a fresh round cap, and says so in the card. A refusal keeps the review pending with its wire code (`GOAL_AGENT_NOT_LIVE: …`), and a deployment that mounts no goal service disables the button with an explicit reason while the other three actions keep working. The objective is the Plan's full markdown, trimmed — the goal domain imposes no length limit, and the objective rides along as model-visible context every round, so a long Plan costs tokens on each continuation.
|
|
83
|
+
|
|
84
|
+
The card body shows only the Plan's first-line title and first-paragraph summary; the **full plan** opens automatically in the right sidebar through official ui-plan (the header's "View full plan" reopens it any time). The takeover entry cannot render the official `conversation.plan-review.actions` slot (a child slot allows exactly one declarer), so the card drives ui-plan's registered opener instead — the preview document's address and params stay ui-plan's own construction (see `docs/seam-gap.md`).
|
|
85
|
+
|
|
82
86
|

|
|
83
87
|
|
|
88
|
+
**Plan-exit context compaction (0.4.20):** when an approval **switches the execution model** (provider/model change; an effort-only change does not count) and the estimated next-request input exceeds the threshold (in tokens, default 200,000, adjustable in Settings, 0 = off), the card first calls the Host's compaction endpoint — summarizing history once **with the switched-to model** — and only then commits the model and answers; **Set as goal and run** compacts before arming the goal too. Approvals without a model switch never trigger it. Nothing compactable proceeds after a notice; a failed compaction aborts execution, keeps the review pending, and stays retryable.
|
|
89
|
+
|
|
84
90
|
## Send-time context protection
|
|
85
91
|
|
|
86
92
|
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.
|
|
87
93
|
|
|
94
|
+
## Auto review model
|
|
95
|
+
|
|
96
|
+
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 pinned in Settings. The "Auto review model" card defaults to off = reviews follow the session model; turning it on seeds from Main and stays editable, 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`.
|
|
97
|
+
|
|
88
98
|
## What Model Switch does not change
|
|
89
99
|
|
|
90
100
|
- `web_fetch` and its configured provider
|
package/README.zh.md
CHANGED
|
@@ -77,12 +77,22 @@ Plan Review 拥有独立于 Main 的执行模型草稿。**确认执行**会先
|
|
|
77
77
|
|
|
78
78
|
**拒绝**会发送提问方给出的选项,并把卡片反馈框里的文字作为答复自身的 `custom` 文本一并提交 —— plan 模式会把它作为"继续规划"的理由回报给模型,所以打回时可以带上要改什么。**去聊天里说**会撤下这张卡、把输入框还给你,用你自己的话说明;它在 Host 提供 `dismiss()` 时用它,在旧代际用 `cancel()`,两者都没有时明确指出这一点。审核一旦落定,**确认执行**与**拒绝**关闭,而**去聊天里说**与"重新加载"始终可达,卡片不会变成死路。失败会保留线上错误码(如 `session/writer-held: …`),而不是只给一句本地化文案。
|
|
79
79
|
|
|
80
|
+
**设置为目标并执行** 会先把这份 Plan 设为当前会话的 goal、再答复,于是 `dsh-goal-round-driver` 能在退出计划模式后继续推进。它调用已发布的 `remote.goals.create`;若已存在一个未完成的 goal,会先 `clear` 再 `create`,让新 Plan 拿到完整轮次预算,并在替换后提示。目标设置被拒时审核保持待处理、错误带真实错误码(如 `GOAL_AGENT_NOT_LIVE: …`);部署未挂 goal 服务时该按钮禁用并给出原因,其余三个动作照常可用。objective 取计划全文(trim 后):goal 域不设长度上限,但它会作为模型可见上下文出现在每一轮续跑里,所以计划很长时每轮都花 token。
|
|
81
|
+
|
|
82
|
+
卡片正文只显示计划的首行标题与首段摘要,**完整计划**会由官方 ui-plan 自动开到右侧侧边栏(头部的"查看完整计划"可随时再开)。接管的 composer 条目无权渲染官方的 `conversation.plan-review.actions` 槽位(每个子槽只允许一个声明者),因此卡片直接驱动 ui-plan 已注册的 opener——计划文档的地址与参数始终由 ui-plan 自己构造(见 `docs/seam-gap.md`)。
|
|
83
|
+
|
|
84
|
+
**退出计划时的上下文压缩(0.4.20)**:当这次批准**切换了执行模型**(Provider/模型变化,仅改推理强度不算),且估算的下次请求输入超过阈值(Token 数,默认 200,000,可在设置里调整,0 = 关闭)时,卡片会先调用 Host 的压缩端点——**用切换后的模型**摘要一次历史——再提交模型、再答复;「设置为目标并执行」同样先压缩再设目标。未切换模型的批准完全不触发。压缩无可压缩历史时提示后继续执行;压缩出错时执行中止、审核保持待处理并可重试。
|
|
85
|
+
|
|
80
86
|

|
|
81
87
|
|
|
82
88
|
## 发送时的上下文保护
|
|
83
89
|
|
|
84
90
|
当发送后的实际请求将使用与上次不同的模型或窗口档位时,Model Switch 会在目标请求发出前检查完整待发上下文。仅在选择器中选模型不会压缩。超出目标可用容量时,使用切换前的模型、通过私有压缩引擎压缩一次历史;不会改 DSH 自身的自动压缩设置。进度以插件通知出现在正文中。预算包含完成提示和待提交的模型切换提示;摘要仍超限时会阻止请求。失败时用户消息留在会话里,不会发送目标模型请求,也不会出现切换成功提示。可在 **设置 → 模型切换 → 发送时自动检查并压缩上下文** 关闭(默认开启)。原生 External Agent 运行时的内部上下文不会被当成 DSH 可压缩历史。
|
|
85
91
|
|
|
92
|
+
## 自动审查模型(Auto review)
|
|
93
|
+
|
|
94
|
+
官方 `@deepseek-ai/dsh-experimental-auto-review`(Auto 权限预设的逐调用审查)把审查请求固定在会话自身的路由上。本插件通过 `cordis.patch.yml` 的行替换接管这一行:官方行被禁用,改挂 `dsh-model-switch/auto-review`,它在作用域子上下文里运行**同一份官方 `apply`**,只把 `llm.stream` 的 `provider/model` 改写为设置里固定的审查模型。设置页「自动审查模型」默认关闭 = 审查跟随当前会话模型;打开时从主模型种子化、可再改成任意 Provider/模型,保存后对下一次审查立即生效。审查未配置官方能力包(`@deepseek-ai/dsh-experimental-auto-review` 未随 profile 启动)时本行不挂载,Auto 预设保持官方的关闭状态;官方行若已在别处注册,本行明确报错而不产生第二个 `registerAuto`。
|
|
95
|
+
|
|
86
96
|
## Model Switch 不会改变什么
|
|
87
97
|
|
|
88
98
|
- `web_fetch` 及其配置的 provider
|
package/cordis.patch.yml
CHANGED
|
@@ -9,6 +9,14 @@
|
|
|
9
9
|
name: '@deepseek-ai/dsh-subagent'
|
|
10
10
|
disabled: true
|
|
11
11
|
|
|
12
|
+
# The official Auto reviewer hardwires the session route; Model Switch replaces
|
|
13
|
+
# it with its own row so the reviewer model stays configurable (one registerAuto
|
|
14
|
+
# per context). A profile that never enabled the optional bundle has no such
|
|
15
|
+
# row: the non-insert target is then unknown and skipped with a warning.
|
|
16
|
+
- id: auto-review
|
|
17
|
+
name: '@deepseek-ai/dsh-experimental-auto-review'
|
|
18
|
+
disabled: true
|
|
19
|
+
|
|
12
20
|
- insert:
|
|
13
21
|
# Register Model Switch Host Settings/Main runtime before routed Subagents.
|
|
14
22
|
- id: model-switch
|
|
@@ -16,3 +24,7 @@
|
|
|
16
24
|
|
|
17
25
|
- id: model-switch-subagent-runtime
|
|
18
26
|
name: 'dsh-model-switch/subagent-runtime'
|
|
27
|
+
|
|
28
|
+
# The official auto-review gate under the reviewer-route llm scope.
|
|
29
|
+
- id: model-switch-auto-review
|
|
30
|
+
name: 'dsh-model-switch/auto-review'
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
//#region lib/types/auto-review.js
|
|
2
|
+
/**
|
|
3
|
+
* Model Switch-owned Auto reviewer row.
|
|
4
|
+
*
|
|
5
|
+
* The official `@deepseek-ai/dsh-experimental-auto-review` gate hardwires its
|
|
6
|
+
* reviewer to the session's own request route (`header.config`). This row runs
|
|
7
|
+
* the official `apply` under a scoped child context whose `llm` service is
|
|
8
|
+
* shadowed by a wrapper that rewrites only `stream()`'s provider/model — and
|
|
9
|
+
* only while the profile has a complete reviewer route configured. With no
|
|
10
|
+
* route configured the wrapper passes options through by identity, so the
|
|
11
|
+
* reviewer follows the session model exactly.
|
|
12
|
+
*
|
|
13
|
+
* The official gate stays an opt-in capability: the row mounts it only when
|
|
14
|
+
* the profile actually started with the experimental-auto-review bundle.
|
|
15
|
+
* `cordis.patch.yml` disables the official row (one `registerAuto` per
|
|
16
|
+
* context), so this row is its replacement rather than a second registration.
|
|
17
|
+
*
|
|
18
|
+
* @module dsh-model-switch/auto-review
|
|
19
|
+
*/
|
|
20
|
+
/** Cordis plugin name used by loader diagnostics. */
|
|
21
|
+
const name = "model-switch-auto-review";
|
|
22
|
+
/** The official gate's host services plus Model Switch's settings owner. */
|
|
23
|
+
const inject = [
|
|
24
|
+
"modelSwitch",
|
|
25
|
+
"approval",
|
|
26
|
+
"llm",
|
|
27
|
+
"permissionPresets",
|
|
28
|
+
"sessions",
|
|
29
|
+
"tools"
|
|
30
|
+
];
|
|
31
|
+
/** Optional bundle whose composition opts a profile into Auto review. */
|
|
32
|
+
const AUTO_REVIEW_BUNDLE = "@deepseek-ai/dsh-experimental-auto-review";
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the pinned reviewer route from live settings.
|
|
35
|
+
* @param settings - Model Switch settings carrying the optional reviewer pair.
|
|
36
|
+
* @returns Both identifiers trimmed and non-empty, or undefined (follow session).
|
|
37
|
+
*/
|
|
38
|
+
function autoReviewRouteOf(settings) {
|
|
39
|
+
const provider = settings?.autoReviewProvider?.trim();
|
|
40
|
+
const model = settings?.autoReviewModel?.trim();
|
|
41
|
+
if (provider === void 0 || provider === "" || model === void 0 || model === "") return void 0;
|
|
42
|
+
return {
|
|
43
|
+
provider,
|
|
44
|
+
model
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Wrap the real llm service so only `stream()` route fields are rewritten.
|
|
49
|
+
*
|
|
50
|
+
* Non-`stream` members pass through bound to the real service (Proxy reads
|
|
51
|
+
* would otherwise hand methods the proxy as `this`). The route is re-read per
|
|
52
|
+
* call so a settings change applies to the very next review without a reload.
|
|
53
|
+
*
|
|
54
|
+
* @param real - the host's `ctx.llm`.
|
|
55
|
+
* @param route - live route resolver; undefined means "do not rewrite".
|
|
56
|
+
* @returns A structurally compatible llm face for the scoped context.
|
|
57
|
+
*/
|
|
58
|
+
function reviewLlm(real, route) {
|
|
59
|
+
return new Proxy(real, { get(target, property, _receiver) {
|
|
60
|
+
if (property === "stream") return (options) => {
|
|
61
|
+
const pinned = route();
|
|
62
|
+
return pinned === void 0 ? target.stream(options) : target.stream({
|
|
63
|
+
...options,
|
|
64
|
+
provider: pinned.provider,
|
|
65
|
+
model: pinned.model
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
const value = Reflect.get(target, property, target);
|
|
69
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
70
|
+
} });
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Build the scoped context the official gate runs under.
|
|
74
|
+
* @param ctx - host context whose modelSwitch settings pin the reviewer route.
|
|
75
|
+
* @returns A child context shadowing only the llm service.
|
|
76
|
+
*/
|
|
77
|
+
function reviewScope(ctx) {
|
|
78
|
+
return ctx.extend({ llm: reviewLlm(ctx.llm, () => autoReviewRouteOf(ctx.modelSwitch.currentSettings())) });
|
|
79
|
+
}
|
|
80
|
+
/** Structural read of launcher-provided profile facts (the app-boot seam). */
|
|
81
|
+
function autoReviewBundleActive(ctx) {
|
|
82
|
+
let profile;
|
|
83
|
+
try {
|
|
84
|
+
profile = ctx.get("profileContext", false);
|
|
85
|
+
} catch {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
if (profile === null || typeof profile !== "object") return false;
|
|
89
|
+
const bundles = profile.startedBundles;
|
|
90
|
+
return Array.isArray(bundles) && bundles.includes(AUTO_REVIEW_BUNDLE);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Mount the official gate under the reviewer-route scope.
|
|
94
|
+
*
|
|
95
|
+
* A profile that never enabled the optional bundle keeps no Auto preset and no
|
|
96
|
+
* reviewer; a deployment missing the official package reports the lost
|
|
97
|
+
* capability explicitly instead of guessing another path.
|
|
98
|
+
*/
|
|
99
|
+
async function apply(ctx) {
|
|
100
|
+
if (!autoReviewBundleActive(ctx)) {
|
|
101
|
+
ctx.logger.info("model-switch: auto-review bundle is not active in this profile; the reviewer route override stays unmounted");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
let official;
|
|
105
|
+
try {
|
|
106
|
+
official = await import("@deepseek-ai/dsh-experimental-auto-review");
|
|
107
|
+
} catch (cause) {
|
|
108
|
+
ctx.logger.error("model-switch: @deepseek-ai/dsh-experimental-auto-review is unavailable; the Auto reviewer cannot run at all");
|
|
109
|
+
ctx.logger.error(cause);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
official.apply(reviewScope(ctx));
|
|
114
|
+
} catch (cause) {
|
|
115
|
+
ctx.logger.error("model-switch: the official auto-review row is already active; the reviewer route override is unavailable");
|
|
116
|
+
ctx.logger.error(cause);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
//#endregion
|
|
120
|
+
export { apply, autoReviewRouteOf, inject, name, reviewLlm, reviewScope };
|