dsh-auto-model-routing 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +8 -8
- package/README.zh.md +8 -8
- package/docs/feedback-evaluation.md +4 -4
- package/docs/host-capability-report.md +2 -0
- package/docs/jev-policy-v2-validation.md +3 -1
- package/docs/magpie-live-acceptance.md +2 -0
- package/docs/policy-v2-development.md +5 -3
- package/docs/policy-v2-real-evaluation-plan.md +2 -0
- package/lib/client/control.d.ts +2 -2
- package/lib/client/control.js +10 -20
- package/lib/client/index.d.ts +0 -1
- package/lib/client/index.js +9 -6
- package/lib/client/label.d.ts +7 -10
- package/lib/client/label.js +17 -30
- package/lib/client/settings-section.d.ts +2 -2
- package/lib/client/settings-section.js +7 -69
- package/lib/client/strategy-details.js +3 -4
- package/lib/client.js +165 -481
- package/lib/context.d.ts +1 -3
- package/lib/context.js +3 -4
- package/lib/effort.d.ts +2 -10
- package/lib/effort.js +1 -31
- package/lib/endpoints.d.ts +11 -10
- package/lib/endpoints.js +29 -46
- package/lib/feedback.d.ts +60 -7
- package/lib/feedback.js +15 -5
- package/lib/index.d.ts +4 -0
- package/lib/index.js +17 -86
- package/lib/jev.d.ts +17 -29
- package/lib/jev.js +6 -34
- package/lib/policy-host.d.ts +6 -8
- package/lib/policy-host.js +39 -38
- package/lib/policy-store.d.ts +9 -20
- package/lib/policy-store.js +28 -16
- package/lib/routing.d.ts +20 -0
- package/lib/routing.js +51 -0
- package/lib/rubric.json +15 -6
- package/lib/settings-contract.d.ts +5 -21
- package/lib/settings-contract.js +3 -2
- package/lib/settings-host.d.ts +4 -8
- package/lib/settings-host.js +9 -10
- package/lib/settings-validation.d.ts +1 -7
- package/lib/settings-validation.js +0 -51
- package/lib/settings.d.ts +3 -8
- package/lib/settings.js +18 -177
- package/lib/state.d.ts +2 -4
- package/lib/state.js +2 -7
- package/lib/strategies/claude.d.ts +21 -0
- package/lib/strategies/claude.js +8 -0
- package/lib/strategies/codex.d.ts +21 -0
- package/lib/strategies/codex.js +9 -0
- package/lib/strategies/index.d.ts +22 -0
- package/lib/strategies/index.js +85 -0
- package/lib/strategies/types.d.ts +14 -0
- package/lib/strategies/types.js +1 -0
- package/lib/subagent-tool.d.ts +7 -7
- package/lib/subagent-tool.js +33 -76
- package/lib/support.d.ts +7 -8
- package/lib/support.js +9 -13
- package/lib/usage-report.d.ts +2 -2
- package/lib/usage-report.js +25 -17
- package/package.json +3 -4
- package/skills/auto-model-routing-report/SKILL.md +4 -3
- package/skills/subagent-model-routing/SKILL.md +14 -30
- package/lib/client/route-picker.d.ts +0 -21
- package/lib/client/route-picker.js +0 -75
- package/lib/judgements.d.ts +0 -41
- package/lib/judgements.js +0 -55
- package/lib/policy-v2-rubric.json +0 -21
- package/lib/policy-v2.d.ts +0 -21
- package/lib/policy-v2.js +0 -54
- package/lib/router.d.ts +0 -68
- package/lib/router.js +0 -153
- package/scripts/label-report.mjs +0 -50
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.5.0
|
|
4
|
+
|
|
5
|
+
- **Breaking — one AUTO strategy.** The opt-in legacy four-tier chain is removed: the tier router and its rubric, the per-family `mainEfforts` / `childRoutes` mappings and their settings tabs, the reply-bar tier menu and `label` parameter, `judgements.jsonl` writes, `scripts/label-report.mjs` and the `eval/run.mjs` baseline. Default AUTO behavior (main effort, child capability routing, exact-child binding, Magpie transports, caps, allowlists, fork refusal) is unchanged.
|
|
6
|
+
- **Breaking — no strategy versions.** The `policy` setting and the `policyVersion` / `mappingVersion` / `rubricVersion` markers are gone, as is the `version` field on child records and feedback events and the usage report's `schema`. New feedback snapshots record the strategy family (`strategy`). The rule file is now `rubric.json` (same bytes).
|
|
7
|
+
- **Family strategies.** Everything about the Claude and GPT families (supported main models, transport prefixes, effort starting points, capability slots, accepted providers) lives in `src/strategies/`; a new family is one strategy file plus registration. The registry rejects conflicting strategies at load.
|
|
8
|
+
- **Old data stays readable.** Retired `policy`, `mainEfforts` and `childRoutes` keys are ignored, listed as `deprecated` and removed on the next settings save; submitting them is rejected. Child records written by 0.4.x restore without a version check (only the three known historical keys are tolerated, then dropped). Old feedback rows and events stay readable; the usage report keeps them in their original historical group. `judgements.jsonl` is kept read-only for the report.
|
|
9
|
+
- **Rollback.** Pin the published 0.4.2 package: in `dsh.yaml`, set this entry to `source: remote`, `spec: dsh-auto-model-routing@0.4.2`, `version: 0.4.2`, run `dsh build` and restart the Host. Data boundary: child records rewritten by 0.5.0 and feedback rows written by 0.5.0 are not readable by 0.4.2 (fail closed; recreate those child sessions), and settings saved under 0.5.0 no longer contain the retired mapping keys. Back up `state/auto-model-routing/` and the plugin `config.json` before upgrading if you may roll back.
|
|
10
|
+
- The Jev decision deadline now defaults to 5000 ms (was 3000 ms) and can be set up to 8000 ms (was 4500 ms); an explicitly saved value is kept. The `route_subagent` tool deadline is 10 s so it outlasts the longest decision.
|
|
11
|
+
- The package build now clears `lib/` before compiling, so deleted or renamed modules never ship from a stale output directory.
|
|
12
|
+
|
|
3
13
|
## 0.4.2
|
|
4
14
|
|
|
5
15
|
- AUTO now handles Claude and GPT model families only: selecting any other main model switches the session to MANUAL instead of failing the turn in the effort mapper.
|
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ Preset model menus expose documented versions, not live availability guarantees.
|
|
|
77
77
|
|
|
78
78
|
### Magpie transports and picker display
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
AUTO accepts both exact transports, `dsh-magpie-connector` and `dsh-magpie-connect`. They are not aliases: full model ids, live model capabilities, supported effort/caps, the current allowlist and the parent's captured allowlist all retain their exact transport identity. Families without a registered strategy need an explicit model and a verifiable effort. With a MANUAL root session the preview returns `routed: false` without judging or state writes. Already bound children replay their saved route.
|
|
81
81
|
|
|
82
82
|
AUTO applies the selected effort to the current main request and awaits official `selectModel` on the same provider/model when the effort changes. This also updates global defaults; the official picker shows one effort value. MANUAL requests pass through unchanged and bound children never write the main selection. A selection-sync failure is logged while the current request retains its selected effort. Magpie menu grouping still requires ohmydsh's managed five-package Host extension; stock rc.2 keeps a flat list. See [Magpie setup and display requirements](<../dsh-magpie-connector/README.md#model-menu-groups-and-effort>); local tests do not establish deployment or live GUI acceptance.
|
|
83
83
|
|
|
@@ -87,9 +87,9 @@ AUTO applies the selected effort to the current main request and awaits official
|
|
|
87
87
|
- **MANUAL stops adjustments, not history.** It does not restore an earlier effort. The official current selection remains in control; existing bound children keep their route while the plugin is enabled.
|
|
88
88
|
- **Data leaves the machine.** AUTO sends shape-redacted task text (up to 8000 characters) and short previous-turn excerpts to your chosen service. Redaction is best effort, not a guarantee that all secrets are removed. Test Connection sends only a fixed public sample and can incur a small charge.
|
|
89
89
|
- **Keys use official DSH credentials**, never plugin configuration. Launch-environment keys take precedence and are read-only. Service changes clear key drafts; save the service before managing its separate key. Changing a custom endpoint does not create a new credential namespace—review the key before trusting a new host.
|
|
90
|
-
- **Local state:** configuration is under `$DSH_HOME/plugins/dsh-auto-model-routing/`; mode, child routes and feedback are under `$DSH_HOME/state/auto-model-routing/`. Feedback stores resource metadata, IDs,
|
|
90
|
+
- **Local state:** configuration is under `$DSH_HOME/plugins/dsh-auto-model-routing/`; mode, child routes and feedback are under `$DSH_HOME/state/auto-model-routing/`. Feedback stores resource metadata, IDs, the strategy family, returned distributions and timestamps—not task/reply text. Request event IDs allow later usage correlation; costs are not fabricated when unavailable. See [feedback and evaluation](docs/feedback-evaluation.md).
|
|
91
91
|
- **No automatic learning.** Human allocation feedback is observational. It does not change routing or prove quality/cost benefits. Old messages without decision bindings are not backfilled; old records with missing evaluation fields remain unknown.
|
|
92
|
-
- **Upgrade
|
|
92
|
+
- **Upgrade to 0.5.0:** there is one AUTO strategy and no version setting. Existing sessions keep their stored mode; new chats still default to MANUAL unless configured otherwise. Retired `policy` / `mainEfforts` / `childRoutes` keys in the plugin configuration are ignored, shown as no longer effective in Settings, and removed on the next save. Child decisions and feedback recorded by 0.4.x stay readable. To go back, pin the published `dsh-auto-model-routing@0.4.2` (see [CHANGELOG](CHANGELOG.md) for the data boundary).
|
|
93
93
|
## Architecture
|
|
94
94
|
<!-- section: architecture -->
|
|
95
95
|
|
|
@@ -97,14 +97,14 @@ AUTO applies the selected effort to the current main request and awaits official
|
|
|
97
97
|
|
|
98
98
|
Main failures disable AUTO; child failures stop dispatch. Explicit model/effort constraints are preserved. The diagram shows the AUTO paths; MANUAL makes no new judgments and fork is unsupported. Feedback does not automatically alter routing.
|
|
99
99
|
|
|
100
|
-
Entry: [index.ts](src/index.ts). Enforcement: [policy-host.ts](src/policy-host.ts). Actual rubric: [
|
|
100
|
+
Entry: [index.ts](src/index.ts). Enforcement: [policy-host.ts](src/policy-host.ts). Actual rubric: [rubric.json](src/rubric.json). Family strategies (supported models, transports, effort starting points, capability slots): [strategies](src/strategies/index.ts); routing: [routing.ts](src/routing.ts). UI: [client](src/client/index.ts). Coverage and limitations (historical records): [development verification](docs/policy-v2-development.md), [real Magpie acceptance](docs/magpie-live-acceptance.md).
|
|
101
101
|
|
|
102
102
|
<details>
|
|
103
|
-
<summary>Earlier four-tier UI demos (
|
|
103
|
+
<summary>Earlier four-tier UI demos (removed in 0.5.0)</summary>
|
|
104
104
|
|
|
105
|
-
These isolated-host recordings use mock Jev/model responses and show
|
|
105
|
+
These isolated-host recordings use mock Jev/model responses and show a UI that no longer exists.
|
|
106
106
|
|
|
107
|
-

|
|
108
108
|
|
|
109
109
|
</details>
|
|
110
110
|
## Development
|
|
@@ -115,7 +115,7 @@ npm run typecheck
|
|
|
115
115
|
npm run build
|
|
116
116
|
```
|
|
117
117
|
|
|
118
|
-
Tests use the official AgentLoop with fake LLM streams and mock/loopback Jev responses, not paid model calls.
|
|
118
|
+
Tests use the official AgentLoop with fake LLM streams and mock/loopback Jev responses, not paid model calls. To observe AUTO decisions and their actual token usage, ask the agent to use the `auto-model-routing-report` skill (on demand only; it runs the read-only `lib/usage-report.js`). It is observational, not a savings measurement. Evaluation schema and deduplication rules are in [feedback-evaluation.md](docs/feedback-evaluation.md).
|
|
119
119
|
|
|
120
120
|
Development stays in the source monorepo; this repository is its release mirror. The package builds and loads independently using its declared dependencies, bundled skill and official DSH services; no monorepo checkout or monorepo instruction file is required. External design references are optional background, not runtime inputs.
|
|
121
121
|
|
package/README.zh.md
CHANGED
|
@@ -77,7 +77,7 @@ dsh plugin --profile web add prgrmrwy/dsh-auto-model-routing
|
|
|
77
77
|
|
|
78
78
|
### Magpie transport 与选择器展示
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
AUTO 接受 `dsh-magpie-connector`、`dsh-magpie-connect` 两个精确 transport,不互为 alias:完整 model id、实时模型能力、支持 effort/cap、当前允许列表与父 captured allowlist 都按原 transport 校验。没有注册策略的家族需显式 model 及可验证的 effort。根会话为 MANUAL 时预览直接返回 `routed: false`,不判断、不写状态。已绑定子实例恢复原路线。
|
|
81
81
|
|
|
82
82
|
AUTO 将选定 effort 应用于本次主请求;effort 改变时,等待官方 `selectModel` 同步同一 provider/model,并允许保存全局默认值。官方选择器只显示一个 effort。MANUAL 请求原样通过,已绑定子会话不写主选择;同步失败会记录告警,本次请求仍保留选定 effort。Magpie 菜单分组仍需 ohmydsh 受管五包 Host 扩展,stock rc.2 保持 flat 列表。见 [Magpie 配置与展示前提](<../dsh-magpie-connector/README.zh.md#模型菜单分组与-effort>);本地测试不等于已部署或 live GUI 验收。
|
|
83
83
|
|
|
@@ -87,9 +87,9 @@ AUTO 将选定 effort 应用于本次主请求;effort 改变时,等待官方
|
|
|
87
87
|
- **MANUAL 停止调整,不复原历史值**:使用官方当前选择;只要插件启用,已绑定子任务仍保留原路线。
|
|
88
88
|
- **任务会外发**:AUTO 将按形态脱敏的任务文本(最多 8000 字符)及上一轮少量片段发送给所选服务。脱敏尽力而为,不保证去除全部秘密。测试连接只发送固定公开样例,可能产生少量费用。
|
|
89
89
|
- **key 经官方 DSH 凭据服务保存**,不写入插件配置。启动环境 key 优先且只读;切换服务清空 key 草稿,先保存服务再管理新 key。修改自定义端点不会产生新凭据空间,换地址前请确认专用 key 的信任边界。
|
|
90
|
-
- **本地状态**:配置在 `$DSH_HOME/plugins/dsh-auto-model-routing/`,模式、子路由及反馈在 `$DSH_HOME/state/auto-model-routing
|
|
90
|
+
- **本地状态**:配置在 `$DSH_HOME/plugins/dsh-auto-model-routing/`,模式、子路由及反馈在 `$DSH_HOME/state/auto-model-routing/`。反馈只保存资源元数据、身份、策略家族、返回分布及时间,不保存任务或回复正文。请求事件序号供后续关联用量,缺失成本不会编造。见[反馈评估说明](docs/feedback-evaluation.md)。
|
|
91
91
|
- **不自动学习**:人工反馈仅作观察,不改变路由,也不能直接证明质量提升或节省。无决策绑定的旧消息不回填,旧记录缺少的评估字段保持未知。
|
|
92
|
-
-
|
|
92
|
+
- **升级到 0.5.0**:只有一套 AUTO 策略,没有版本设置。已有会话保留原模式;新会话缺省仍为 MANUAL,除非你改过设置。插件配置中残留的 `policy`、`mainEfforts`、`childRoutes` 会被忽略,设置页提示不再生效,下次保存时移除。0.4.x 记录的子任务决策与反馈仍可读取。如需退回,固定已发布的 `dsh-auto-model-routing@0.4.2`(数据边界见 [CHANGELOG](CHANGELOG.md))。
|
|
93
93
|
|
|
94
94
|
## 架构
|
|
95
95
|
<!-- section: architecture -->
|
|
@@ -98,14 +98,14 @@ AUTO 将选定 effort 应用于本次主请求;effort 改变时,等待官方
|
|
|
98
98
|
|
|
99
99
|
主判断失败停用 AUTO,子失败停止派发;保留显式 model/effort。图示为 AUTO 路径,MANUAL 不做新判断,fork 暂不支持。反馈不会自动改变路由。
|
|
100
100
|
|
|
101
|
-
入口:[index.ts](src/index.ts)。执行接入:[policy-host.ts](src/policy-host.ts)。实际判据:[
|
|
101
|
+
入口:[index.ts](src/index.ts)。执行接入:[policy-host.ts](src/policy-host.ts)。实际判据:[rubric.json](src/rubric.json)。家族策略(支持的模型、transport、effort 起点、能力槽):[strategies](src/strategies/index.ts);路由计算:[routing.ts](src/routing.ts)。界面:[client](src/client/index.ts)。覆盖边界(历史记录):[开发验证](docs/policy-v2-development.md)、[真实 Magpie 验收](docs/magpie-live-acceptance.md)。
|
|
102
102
|
|
|
103
103
|
<details>
|
|
104
|
-
<summary
|
|
104
|
+
<summary>早期四档界面演示(已于 0.5.0 删除)</summary>
|
|
105
105
|
|
|
106
|
-
下列隔离宿主录屏使用模拟 Jev
|
|
106
|
+
下列隔离宿主录屏使用模拟 Jev/模型响应,展示的界面已不存在。
|
|
107
107
|
|
|
108
|
-

|
|
109
109
|
|
|
110
110
|
</details>
|
|
111
111
|
## 开发验证
|
|
@@ -116,7 +116,7 @@ npm run typecheck
|
|
|
116
116
|
npm run build
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
-
测试使用官方 AgentLoop、假 LLM 流和 mock/回环 Jev
|
|
119
|
+
测试使用官方 AgentLoop、假 LLM 流和 mock/回环 Jev,不产生真实模型费用。如需观察 AUTO 决策及实际 token 用量,请明确让 agent 使用 `auto-model-routing-report` skill(仅按需调用,运行只读的 `lib/usage-report.js`);它是观察统计,不衡量节省;新数据结构与去重口径见[feedback-evaluation.md](docs/feedback-evaluation.md)。
|
|
120
120
|
|
|
121
121
|
继续在主仓开发,本仓库仅作发布镜像。插件使用自身声明的依赖、包内 skill 和官方 DSH 服务即可独立构建与加载,不要求检出主仓,也不要求安装主仓环境指令。外部设计链接仅供背景阅读,不是运行输入。
|
|
122
122
|
|
|
@@ -3,20 +3,20 @@
|
|
|
3
3
|
## 记录
|
|
4
4
|
`$DSH_HOME/state/auto-model-routing/allocation-feedback.jsonl` 是本地append-only记录,无会话正文和key。
|
|
5
5
|
|
|
6
|
-
绑定行使用sessionId/messageId/decisionId、depth、实际model/effort,新增boundAt与不可变snapshot:decidedAt/appliedAt、
|
|
6
|
+
绑定行使用sessionId/messageId/decisionId、depth、实际model/effort,新增boundAt与不可变snapshot:decidedAt/appliedAt、strategy(实际主模型所属策略家族id,无匹配家族时省略)、decisionProvider/decisionModel、判断ms、返回的confidence/probabilities、实际provider、requestSeq及可得时requestTurn。0.5.0之前写入的快照带policyVersion/mappingVersion而无strategy,仍原样可读,缺失的strategy保持未知,不补造;`label`端点返回时去掉这两个历史键。requestSeq是同session请求头事件,可与该会话历史关联actual request及usage;绑定并非完成任务或用量已知的证明。
|
|
7
7
|
|
|
8
|
-
反馈事件为`type:feedback
|
|
8
|
+
反馈事件为`type:feedback`,带sessionId/messageId/decisionId、at、feedback;新事件不带version,带`version:2`的旧事件照常读取,其它未知键按损坏行跳过。每次修改或撤销独立追加;null表示撤销。读取时按追加顺序应用到既有精确绑定。历史完整记录仍兼容,原快照不可被后续同身份行替换;旧记录没有时间或快照则未知,禁止用迁移时刻或最新配置补造。
|
|
9
9
|
|
|
10
10
|
## 评估
|
|
11
11
|
- 评估单位为(sessionId, decisionId),不是每个assistant/message,避免同轮多条工具消息导致重复计数。先按消息取最后事件,再按决策取最后反馈;历史无时间以追加顺序作本文件内顺序,跨文件不可猜。
|
|
12
12
|
- appropriate/too-high/too-low分别统计;unknown不是满意,null撤销不计有效评价;未评价不能计为合适。多条消息评价冲突也应单独报告。
|
|
13
|
-
-
|
|
13
|
+
- 按策略家族(strategy)及depth/实际路由分组;0.5.0之前的快照按原policyVersion/mappingVersion组成历史组(标记historical),无快照的记录独立unknown组;历史组与家族组不合并比较。
|
|
14
14
|
- 以requestSeq和消息ID关联实际usage/延迟/返工证据;拿不到价格或token就未知。用户主观反馈不等于正确性oracle,适合率也不能单独证明质量或节省。
|
|
15
15
|
- 按需观察报表见包内 skill `auto-model-routing-report`(`lib/usage-report.js`,只读);仍不做自动阈值调整、自动训练或换模重试。
|
|
16
16
|
|
|
17
17
|
## 隐式事实与分布(2026-10-26)
|
|
18
18
|
|
|
19
|
-
运行时只追加 `{type:'fact',
|
|
19
|
+
运行时只追加 `{type:'fact', sessionId, decisionId, fact, at}`(0.5.0之前的事实行带 `version:1`,照常读取)。`fact` 仅为 `exposed` 或 `manual-switch`;按决策/类型保留首次事实,时间由 Host 生成,不收正文。曝光要求回复操作栏在前台、聚焦页面连续完整可见 3 秒;切 MANUAL 在清除运行态之前取当前主决策,只在切换成功后记录。写入失败只记录日志,不阻断主线。
|
|
20
20
|
|
|
21
21
|
回合结束与继续不重复写账本,按需报告从已有日志推导。决策所在回合取最后一次关联调用的实际回合号,不是归属的用户回合。第一个后续用户回合的开始时间为继续时间;曝光时间必须早于继续时间,否则视为事后翻看,不算有效曝光。没有继续时曝光仍有效,但不能得到隐式无异议。
|
|
22
22
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# AUTO 宿主能力核验(官方 0.2.0-rc.2)
|
|
2
2
|
|
|
3
|
+
> 历史记录:命名、路径与行为以记录时的 0.4.x 版本为准;0.5.0 起只有一套 AUTO 策略,规则文件为 `src/rubric.json`,家族策略见 `src/strategies/`。
|
|
4
|
+
|
|
3
5
|
## 结论
|
|
4
6
|
|
|
5
7
|
**官方入口足以让插件在普通子会话、fork、teammate 的首次模型请求前读取精确子身份和本轮任务,并改写实际 provider/model/effort;不需要改宿主或 compat。** 这证明的是宿主接入能力,不是现有 AUTO 已支持这些路径。
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Jev policy-v2 验证(2026-10-24)
|
|
2
2
|
|
|
3
|
+
> 历史记录:命名、路径与行为以记录时的 0.4.x 版本为准;0.5.0 起只有一套 AUTO 策略,规则文件为 `src/rubric.json`,家族策略见 `src/strategies/`。
|
|
4
|
+
|
|
3
5
|
## 结论
|
|
4
6
|
|
|
5
7
|
官方接口和真实 `typesafe/jev-1.13` 均支持一次请求中独立返回 capability 与 depth。10 次合成任务请求全部返回合法的双 Choice;预先定义的 development / holdout 各 5 例均符合预期,不调整 rubric、不重试。**这只证明接口与小样本分类,不证明实际子模型质量、成本收益或默认推广条件。**
|
|
@@ -26,7 +28,7 @@
|
|
|
26
28
|
| hold-constraints | holdout | frontier | higher |
|
|
27
29
|
| hold-unknown | holdout | insufficient_context | baseline |
|
|
28
30
|
|
|
29
|
-
[测试输入及独立预期](../eval/
|
|
31
|
+
[测试输入及独立预期](../eval/cases.json)、[问题定义](../src/rubric.json)、[有预算保护的实测脚本](../eval/live.mjs)(当时文件名分别带 `policy-v2-` 前缀)。depth 从同一任务证据独立判断相对适配模型的推荐投入,不引用另一个尚未返回的 capability 答案。两者都指定时不判;只指定一个维度时仅询问另一个。
|
|
30
32
|
|
|
31
33
|
实测脚本先保留每次费用预算再请求,失败也计一次;预留每次 65536 input tokens 远高于合成输入。未知 usage.cost 停止,不记零。尚有 10 次授权余量,不默认消耗,也不因生成一个新结果文件就获得新预算。跨多次运行的全局预算需由执行者核对,脚本不是账户级额度控制。
|
|
32
34
|
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# policy-v2 开发与验收记录
|
|
2
2
|
|
|
3
|
+
> 历史记录:命名、路径与行为以记录时的 0.4.x 版本为准;0.5.0 起只有一套 AUTO 策略,规则文件为 `src/rubric.json`,家族策略见 `src/strategies/`。
|
|
4
|
+
|
|
3
5
|
## 状态
|
|
4
6
|
|
|
5
7
|
2026-10-25 用户在最小真实 Magpie 验收后追加授权:AUTO 默认升级,不再要求版本开关。默认配置为内部 policy-v2;显式 legacy 仅供回滚。旧四档配置保留但不参与新策略,MANUAL 不变。已构建部署、通过分离控制进程重启现有3080服务,并在实际GUI验证无版本选择、显示 AUTO 已使用新策略。真实质量/返工/成本对照仍未完成,不宣称性能收益。
|
|
@@ -7,7 +9,7 @@
|
|
|
7
9
|
## 实现入口
|
|
8
10
|
|
|
9
11
|
- [Jev 契约](../src/jev.ts):复用认证、deadline、body 上限、错误分类;原 tier 问题不变。新主请求只问 depth;子请求一次问 capability/depth,显式约束只问未指定维度。
|
|
10
|
-
- [独立映射](../src/policy-v2.ts
|
|
12
|
+
- [独立映射](../src/routing.ts)(当时为 `src/policy-v2.ts`):版本化能力槽,effort 推荐起点按受支持序列最多移动一档,受 cap 限制,不自动使用 max。映射是初始基线,Codex analytical/frontier 都是 Sol,未虚构第三模型。
|
|
11
13
|
- [宿主接入](../src/policy-host.ts):官方 `tools/execute` → AsyncLocalStorage → `agent/created` 绑定精确 child ID 和 parent/root;不依赖 prompt 相似度。`agent/pre-step` 获取首次任务并决策,`agent/request` 应用;`request/header` 记录匹配证据。
|
|
12
14
|
- [持久化](../src/policy-store.ts):每 child 哈希路径、严格 schema、owner-only 原子替换,只有身份/版本/约束/路由/状态,不含任务原文。缺失、损坏、未完成恢复不默默继承;未完成决策不能用后续消息重新分类。
|
|
13
15
|
- [工具](../src/subagent-tool.ts):policy-v2 下 optional preview,仅 recommended;普通创建工具负责精确应用,不把预览冒充 applied。
|
|
@@ -18,7 +20,7 @@
|
|
|
18
20
|
|
|
19
21
|
| 路径/行为 | 证据 | 结果/边界 |
|
|
20
22
|
|---|---|---|
|
|
21
|
-
| 官方 ordinary fresh continuable 工具与 provider | [整链脚本](../scripts/policy-v2-host-probe.mjs
|
|
23
|
+
| 官方 ordinary fresh continuable 工具与 provider | [整链脚本](../scripts/host-probe-children.mjs)(当时为 `policy-v2-host-probe.mjs`)加载官方 0.2.0-rc.2、真实生产插件,fake Jev/adapter | 实际请求 model/effort 匹配;全宿主关闭重建后恢复,不重判 |
|
|
22
24
|
| 官方 fresh teammate 工具与服务 | 同上 | 创建、后续消息和全宿主恢复通过,不重选 |
|
|
23
25
|
| 并发创建与显式模型/effort | [宿主测试](../test/policy-host.integration.test.ts)官方 Tools/Agents,测试创建工具 | 并发精确身份隔离;两者指定零判断;普通/teammate 官方 provider 的并发压力未测 |
|
|
24
26
|
| fork / teammate fork | tools wrapper 可提前拒绝 | unsupported,真实历史兼容未验证,不擅自转 fresh |
|
|
@@ -41,7 +43,7 @@
|
|
|
41
43
|
重跑整链:先 package build;在原临时官方 prefix 的基础上,精确安装 `@deepseek-ai/dsh-tool-subagent@0.2.0-rc.2`、`@deepseek-ai/dsh-experimental-tool-agent-team@0.2.0-rc.2`、`@deepseek-ai/dsh-launch-environment@0.2.0-rc.2`(`--save-exact --ignore-scripts`),然后:
|
|
42
44
|
|
|
43
45
|
```sh
|
|
44
|
-
node packages/auto-model-routing/scripts/
|
|
46
|
+
node packages/auto-model-routing/scripts/host-probe-children.mjs "$PROBE_HOME"
|
|
45
47
|
```
|
|
46
48
|
|
|
47
49
|
官方 standing tool 的 `modelSelectionSettings:true` 要求 preset-scoped context;隔离脚本没有伪造该 composition,使用 standing fixed-schema 工具 + 合成 allowlist。因此显式选择与父快照另用 host tests 验证,真实 preset/GUI 组合不记为通过。
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# policy-v2 真实任务对照(计划;完整对照未执行)
|
|
2
2
|
|
|
3
|
+
> 历史记录:命名、路径与行为以记录时的 0.4.x 版本为准;0.5.0 起只有一套 AUTO 策略,规则文件为 `src/rubric.json`,家族策略见 `src/strategies/`。
|
|
4
|
+
|
|
3
5
|
## 目的与门槛
|
|
4
6
|
|
|
5
7
|
下一步不是直接部署,而是在独立评审修复后验证真实子模型质量、耗时与总开销。现有 10 个 Jev 分类样例不是执行任务基准,不能把其标签命中率当任务成功率。不得消耗剩余 Jev 授权来调用其它模型;2026-10-25 用户已授权以最小必要订阅调用验收并本地部署,不再按美元预算审批;仅经 Magpie。当前真实最小验收已完成,完整对照仍未完成,见 [实测记录](magpie-live-acceptance.md)。
|
package/lib/client/control.d.ts
CHANGED
|
@@ -16,13 +16,13 @@ export declare const panelOpenAfter: (event: PanelEvent) => boolean;
|
|
|
16
16
|
export interface Decision {
|
|
17
17
|
id: number;
|
|
18
18
|
source: 'main' | 'subagent';
|
|
19
|
-
tier
|
|
19
|
+
tier?: string;
|
|
20
20
|
effort: string;
|
|
21
21
|
model?: string;
|
|
22
22
|
confidence?: number;
|
|
23
23
|
probabilities?: Record<string, number>;
|
|
24
24
|
ms: number;
|
|
25
|
-
depth
|
|
25
|
+
depth: string;
|
|
26
26
|
capability?: string;
|
|
27
27
|
capabilityConfidence?: number;
|
|
28
28
|
capabilityProbabilities?: Record<string, number>;
|
package/lib/client/control.js
CHANGED
|
@@ -6,7 +6,6 @@ export async function callRouting(rpc, sessionId, mode) {
|
|
|
6
6
|
}
|
|
7
7
|
export const chipOrder = -10;
|
|
8
8
|
export const panelOpenAfter = (event) => event === 'enter' || event === 'focus';
|
|
9
|
-
const TIER_ORDER = ['simple', 'standard', 'complex', 'escalated'];
|
|
10
9
|
const pct = (v) => `${Math.round(v * 100)}%`;
|
|
11
10
|
export const TOAST_MS = 3000;
|
|
12
11
|
const spreadText = (order, values) => {
|
|
@@ -15,25 +14,16 @@ const spreadText = (order, values) => {
|
|
|
15
14
|
};
|
|
16
15
|
/** Shared by the persistent hover panel and transient decision hint. */
|
|
17
16
|
export function toastText(d) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
];
|
|
29
|
-
const head = [
|
|
30
|
-
d.source === 'main' ? '主会话判断' : '子会话判断',
|
|
31
|
-
`${d.tier} → ${d.model ? `${d.model} · ` : ''}${d.effort}`,
|
|
32
|
-
`${(d.ms / 1000).toFixed(2)}s`,
|
|
33
|
-
].join(' · ');
|
|
34
|
-
const spread = TIER_ORDER.filter(t => d.probabilities?.[t] !== undefined && Math.round(d.probabilities[t] * 100) > 0)
|
|
35
|
-
.map(t => `${t} ${pct(d.probabilities[t])}`).join(' / ');
|
|
36
|
-
return spread ? [head, spread] : [head];
|
|
17
|
+
return [
|
|
18
|
+
`${d.source === 'main' ? '主会话' : '子任务'} · ${d.status === 'applied' ? '已应用' : d.status === 'failed' ? '未应用' : d.status === 'unsupported' ? '不支持调整' : '已判断/待应用'}`,
|
|
19
|
+
...(d.capability ? [`能力 ${d.capability} · 判定确定度 ${d.capabilityConfidence === undefined ? '未提供' : pct(d.capabilityConfidence)}`] : []),
|
|
20
|
+
...(d.capability ? [`能力分布:${spreadText(['bounded', 'analytical', 'frontier', 'insufficient_context'], d.capabilityProbabilities)}`] : []),
|
|
21
|
+
`投入 ${d.depth} · 判定确定度 ${d.confidence === undefined ? '未提供' : pct(d.confidence)}`,
|
|
22
|
+
`投入分布:${spreadText(['lower', 'baseline', 'higher'], d.probabilities)}`,
|
|
23
|
+
`${d.provider ?? ''} / ${d.model ?? '待确定'} · ${d.effort}`,
|
|
24
|
+
`${d.decisionProvider ?? '未知服务'} / ${d.decisionModel ?? '未知模型'} · ${(d.ms / 1000).toFixed(2)}s`,
|
|
25
|
+
'确定度 = (最高概率 − 1/n) ÷ (1 − 1/n),n 为候选数;不是任务成功率',
|
|
26
|
+
];
|
|
37
27
|
}
|
|
38
28
|
/** A hint is shown only for a decision id not seen before (it is hidden TOAST_MS later by the chip). */
|
|
39
29
|
export function nextToast(seen, d) {
|
package/lib/client/index.d.ts
CHANGED
package/lib/client/index.js
CHANGED
|
@@ -6,6 +6,9 @@ import { IconInfoOutlineRegular, IconGaugeOutlineRegular, Menu, Tooltip } from '
|
|
|
6
6
|
import { LabelButton } from './label.js';
|
|
7
7
|
import * as settingsUi from '@deepseek-ai/dsh-client-ui-primitives';
|
|
8
8
|
import { RoutingSettings } from './settings-section.js';
|
|
9
|
+
import { registry } from '../strategies/index.js';
|
|
10
|
+
/** Supported families as the user sees them. */
|
|
11
|
+
const FAMILIES = registry.labels().join(' / ');
|
|
9
12
|
import { installRoutingNavIcon } from './nav-icon.js';
|
|
10
13
|
export const name = 'dsh-auto-model-routing/client';
|
|
11
14
|
export const inject = ['slots', 'connection'];
|
|
@@ -165,18 +168,18 @@ export function RoutingChip({ sessionId, rpc, initialView, initiallyOpen = false
|
|
|
165
168
|
? [
|
|
166
169
|
h('p', { key: 'locked-title' }, h('strong', null, 'AUTO 不支持当前模型')),
|
|
167
170
|
h('p', { key: 'locked-model' }, `${lockedModel} 不在自动路由支持范围,已切换为 MANUAL。`),
|
|
168
|
-
h('p', { key: 'locked-note' },
|
|
171
|
+
h('p', { key: 'locked-note' }, `目前仅支持 ${FAMILIES} 系列模型;其他模型策略欢迎提交 issue 或 PR:`),
|
|
169
172
|
h('p', { key: 'locked-link' }, h('a', { href: REPO_URL, target: '_blank', rel: 'noopener noreferrer' }, REPO_URL)),
|
|
170
|
-
h('p', { key: 'locked-hint', className: 'amr-muted' },
|
|
173
|
+
h('p', { key: 'locked-hint', className: 'amr-muted' }, `切回 ${FAMILIES} 系列模型后,可重新点击启用 AUTO。`),
|
|
171
174
|
error && h('p', { key: 'error', role: 'alert', className: 'amr-error' }, error),
|
|
172
175
|
]
|
|
173
176
|
: [
|
|
174
177
|
h('p', { key: 'title' }, h('strong', null, view?.mode === 'AUTO' ? '自动调整推理强度' : '手动:不自动调整')),
|
|
175
178
|
h('p', { key: 'output' }, `本回合插件输出:${view?.effort ?? '未改写'} · ${view?.reason ?? '尚无决定'}`),
|
|
176
179
|
view?.decision && h('div', { key: 'latest', className: 'amr-latest' }, h('p', null, h('strong', null, '最近决策')), ...toastText(view.decision).map((line, index) => h('p', { key: index, className: index ? 'amr-muted' : undefined }, line))),
|
|
177
|
-
h('p', { key: '
|
|
180
|
+
h('p', { key: 'depth', className: 'amr-muted' }, `AUTO · ${view?.depth ?? '尚无判断'};主模型不变,推理投入按请求应用;子任务自动选配。`),
|
|
178
181
|
off && h('p', { key: 'disabled', role: 'status', className: 'amr-error' }, `Jev 停用(${off.category}):${hint}。${new Date(off.at).toLocaleString()}。点击 AUTO 重试。`),
|
|
179
|
-
h('p', { key: 'data', className: 'amr-muted' }, 'AUTO
|
|
182
|
+
h('p', { key: 'data', className: 'amr-muted' }, 'AUTO 会把脱敏任务文本发送给设置中所选的决策服务判断。'),
|
|
180
183
|
error && h('p', { key: 'error', role: 'alert', className: 'amr-error' }, error),
|
|
181
184
|
];
|
|
182
185
|
return h('span', { ref: (el) => { rootRef.current = el; remember(); }, className: 'amr-root', onMouseEnter: on('enter'), onMouseLeave: on('leave'), onFocus: on('focus'), onBlur: on('blur') }, h('style', null, CSS), h('button', {
|
|
@@ -184,7 +187,7 @@ export function RoutingChip({ sessionId, rpc, initialView, initiallyOpen = false
|
|
|
184
187
|
return; void toggle(); },
|
|
185
188
|
'data-disabled': off ? 'true' : undefined, 'data-locked': locked ? 'true' : undefined,
|
|
186
189
|
'aria-disabled': locked ? 'true' : undefined, style: { border: 'none' },
|
|
187
|
-
title: locked ?
|
|
190
|
+
title: locked ? `AUTO 仅支持 ${FAMILIES} 系列模型;悬停查看说明` : off ? `Jev 判断已停用:${hint}。点击 AUTO 重试` : '点击切换自动推理强度',
|
|
188
191
|
'aria-label': locked ? `推理强度模式:${view?.mode ?? 'MANUAL'}(AUTO 不支持当前模型,不可切换)` : `推理强度模式:${view?.mode ?? '加载中'},点击切换`,
|
|
189
192
|
}, h('span', null, locked ? (view?.mode ?? 'MANUAL') : off ? `${view?.mode} · Jev 停用` : view?.mode ?? '…'), locked ? h(IconInfoOutlineRegular, { size: 14, className: 'amr-icon amr-info' }) : h(SwapIcon)), toast && !open && (typeof document === 'undefined'
|
|
190
193
|
? h('div', { className: 'amr-toast', role: 'status', style: { pointerEvents: 'none' } }, ...toast.lines.map((line, index) => h('div', { key: index, className: index ? 'amr-muted' : undefined }, line)))
|
|
@@ -220,7 +223,7 @@ export function apply(ctx) {
|
|
|
220
223
|
inject: () => ({ rpc: ctx.connection.rpc, ui: settingsUi }),
|
|
221
224
|
}, RoutingSettings));
|
|
222
225
|
ctx.effect(() => typeof document === 'undefined' ? () => { } : installRoutingNavIcon(document), 'auto-model-routing: settings navigation icon');
|
|
223
|
-
// Reply-bar
|
|
226
|
+
// Reply-bar allocation feedback, after the official feedback (order 10); rendered only on bound replies.
|
|
224
227
|
ctx.slots.inject('conversation.chat.assistant-actions', () => ctx.slots.register({
|
|
225
228
|
name: 'conversation.chat.assistant-actions', id: 'auto-model-routing-label', order: 20,
|
|
226
229
|
inject: (sessionId) => ({ sessionId, rpc: ctx.connection.rpc, ui: labelUi }),
|
package/lib/client/label.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
import { type ComponentType, type ReactElement, type ReactNode } from 'react';
|
|
2
2
|
import type { Rpc } from './control.js';
|
|
3
|
-
|
|
4
|
-
interface
|
|
5
|
-
|
|
6
|
-
effort
|
|
7
|
-
|
|
8
|
-
policy?: string;
|
|
9
|
-
depth?: string;
|
|
10
|
-
model?: string;
|
|
3
|
+
/** A reply bound to an applied AUTO decision; `{}` means no binding. */
|
|
4
|
+
interface Allocation {
|
|
5
|
+
depth: string;
|
|
6
|
+
effort: string;
|
|
7
|
+
model: string;
|
|
11
8
|
feedback?: string | null;
|
|
12
9
|
}
|
|
13
10
|
/** Official primitives, injected so the component carries no styles of its own (tests pass doubles). */
|
|
@@ -38,8 +35,8 @@ type MenuEntry = {
|
|
|
38
35
|
id: string;
|
|
39
36
|
text: string;
|
|
40
37
|
};
|
|
41
|
-
export declare function tooltipText(view:
|
|
42
|
-
/** Reply-bar
|
|
38
|
+
export declare function tooltipText(view: Allocation): string;
|
|
39
|
+
/** Reply-bar allocation feedback: shown only for replies bound to an applied AUTO decision. */
|
|
43
40
|
export declare function LabelButton({ sessionId, messageId, rpc, ui }: {
|
|
44
41
|
sessionId: string;
|
|
45
42
|
messageId: string;
|
package/lib/client/label.js
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { createElement as h, useEffect, useRef, useState } from 'react';
|
|
2
2
|
const FEEDBACK = { appropriate: '分配合适', 'too-high': '投入偏高', 'too-low': '投入不足', unknown: '无法判断' };
|
|
3
|
-
const
|
|
4
|
-
const NAMES = { simple: '简单', standard: '标准', complex: '复杂', escalated: '升级' };
|
|
3
|
+
const bound = (value) => typeof value.depth === 'string';
|
|
5
4
|
const CSS = `.amr-label-action{width:calc(28px + var(--dsh-content-font-delta,0px));height:calc(28px + var(--dsh-content-font-delta,0px));border-radius:var(--dsw-radius-sm);color:var(--dsw-alias-label-tertiary);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:6px;display:inline-flex}.amr-label-action svg{width:calc(17px + var(--dsh-content-font-delta,0px));height:calc(17px + var(--dsh-content-font-delta,0px))}.amr-label-action:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}`;
|
|
6
|
-
async function callLabel(rpc, sessionId, messageId,
|
|
7
|
-
const result = await rpc.call('/api', 'auto-model-routing.label', { sessionId, messageId, ...(
|
|
5
|
+
async function callLabel(rpc, sessionId, messageId, feedback) {
|
|
6
|
+
const result = await rpc.call('/api', 'auto-model-routing.label', { sessionId, messageId, ...(feedback !== undefined ? { feedback } : {}) });
|
|
8
7
|
if (!result.ok)
|
|
9
|
-
throw new Error(result.error?.message ?? '
|
|
10
|
-
return result.value;
|
|
8
|
+
throw new Error(result.error?.message ?? '反馈请求失败');
|
|
9
|
+
return (result.value ?? {});
|
|
11
10
|
}
|
|
12
11
|
export function tooltipText(view) {
|
|
13
|
-
|
|
14
|
-
return `AUTO 分配:${view.depth} → ${view.effort} · ${view.model}${view.feedback ? ` · 你反馈:${FEEDBACK[view.feedback]}` : ' · 评价资源分配是否合适'}`;
|
|
15
|
-
return `AUTO 判档:${NAMES[view.tier]}${view.effort ? ` → ${view.effort}` : ''}${view.label ? ` · 你标注:${NAMES[view.label]}` : ''}`;
|
|
12
|
+
return `AUTO 分配:${view.depth} → ${view.effort} · ${view.model}${view.feedback ? ` · 你反馈:${FEEDBACK[view.feedback]}` : ' · 评价资源分配是否合适'}`;
|
|
16
13
|
}
|
|
17
|
-
/** Reply-bar
|
|
14
|
+
/** Reply-bar allocation feedback: shown only for replies bound to an applied AUTO decision. */
|
|
18
15
|
export function LabelButton({ sessionId, messageId, rpc, ui }) {
|
|
19
16
|
const [view, setView] = useState(undefined);
|
|
20
17
|
const [open, setOpen] = useState(false);
|
|
@@ -26,13 +23,13 @@ export function LabelButton({ sessionId, messageId, rpc, ui }) {
|
|
|
26
23
|
let active = true;
|
|
27
24
|
setView(undefined);
|
|
28
25
|
// One query per message; `{}` (or an error) renders nothing.
|
|
29
|
-
callLabel(rpc, sessionId, messageId).then(value => { if (active && (
|
|
26
|
+
callLabel(rpc, sessionId, messageId).then(value => { if (active && bound(value))
|
|
30
27
|
setView(value); }, () => { });
|
|
31
28
|
return () => { active = false; };
|
|
32
29
|
}, [rpc, sessionId, messageId]);
|
|
33
30
|
useEffect(() => {
|
|
34
31
|
const target = exposureTarget.current;
|
|
35
|
-
if (view
|
|
32
|
+
if (!view || !target || typeof IntersectionObserver === 'undefined')
|
|
36
33
|
return;
|
|
37
34
|
let visible = false, sent = false;
|
|
38
35
|
let timer;
|
|
@@ -63,35 +60,25 @@ export function LabelButton({ sessionId, messageId, rpc, ui }) {
|
|
|
63
60
|
window.removeEventListener('focus', update);
|
|
64
61
|
window.removeEventListener('blur', update);
|
|
65
62
|
};
|
|
66
|
-
}, [view
|
|
63
|
+
}, [view !== undefined, rpc, sessionId, messageId]);
|
|
67
64
|
if (!view)
|
|
68
65
|
return null;
|
|
69
|
-
const choose = (label) => {
|
|
70
|
-
setOpen(false);
|
|
71
|
-
// A failure leaves the shown label unchanged.
|
|
72
|
-
callLabel(rpc, sessionId, messageId, label).then(value => { if (alive.current && ('tier' in value || value.policy === 'policy-v2'))
|
|
73
|
-
setView(value); }, () => { });
|
|
74
|
-
};
|
|
75
|
-
const allocation = view.policy === 'policy-v2';
|
|
76
66
|
const chooseFeedback = (id) => {
|
|
77
67
|
setOpen(false);
|
|
78
68
|
setError('');
|
|
79
|
-
|
|
69
|
+
// A failure leaves the shown feedback unchanged.
|
|
70
|
+
callLabel(rpc, sessionId, messageId, id === 'clear' ? null : id).then(value => { if (alive.current && bound(value))
|
|
80
71
|
setView(value); }, () => { if (alive.current)
|
|
81
72
|
setError('反馈未保存,请重试'); });
|
|
82
73
|
};
|
|
83
74
|
const button = h('button', { type: 'button', className: 'amr-label-action', 'aria-label': tooltipText(view), onClick: () => setOpen(value => !value) }, h(ui.Icon));
|
|
84
75
|
return h('span', { ref: exposureTarget }, h('style', null, CSS), h(ui.Menu, {
|
|
85
76
|
open, side: 'top', portal: true, onClose: () => setOpen(false),
|
|
86
|
-
// The official menu draws the check on the selected
|
|
87
|
-
...(
|
|
88
|
-
items:
|
|
89
|
-
onSelect: id => { if (
|
|
90
|
-
|
|
91
|
-
chooseFeedback(id);
|
|
92
|
-
}
|
|
93
|
-
else if (TIERS.includes(id))
|
|
94
|
-
choose(id); },
|
|
77
|
+
// The official menu draws the check on the selected row.
|
|
78
|
+
...(view.feedback ? { selectedId: view.feedback } : {}),
|
|
79
|
+
items: [{ type: 'label', id: 'title', text: '这次资源分配合适吗?' }, ...Object.entries(FEEDBACK).map(([id, label]) => ({ id, label })), ...(view.feedback ? [{ id: 'clear', label: '撤销反馈' }] : [])],
|
|
80
|
+
onSelect: id => { if (Object.hasOwn(FEEDBACK, id) || id === 'clear')
|
|
81
|
+
chooseFeedback(id); },
|
|
95
82
|
anchor: h(ui.Tooltip, { label: tooltipText(view), side: 'bottom', children: button }),
|
|
96
83
|
}), error && h('span', { role: 'alert' }, error));
|
|
97
84
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { Rpc } from './control.js';
|
|
2
2
|
import { type RoutingCatalog, type Settings, type SettingsIssue } from '../settings-contract.js';
|
|
3
|
-
import { type Tier } from '../effort.js';
|
|
4
3
|
import type { SettingsUi } from './settings-ui.js';
|
|
5
4
|
export interface SettingsView extends RoutingCatalog {
|
|
6
5
|
settings: Settings;
|
|
@@ -13,7 +12,8 @@ export interface SettingsView extends RoutingCatalog {
|
|
|
13
12
|
writable: boolean;
|
|
14
13
|
available: boolean;
|
|
15
14
|
};
|
|
16
|
-
|
|
15
|
+
/** Retired mapping keys still present in the settings file. */
|
|
16
|
+
deprecated?: string[];
|
|
17
17
|
}
|
|
18
18
|
/** Settings drafts are independent from the official session model selection. */
|
|
19
19
|
export declare function RoutingSettings({ rpc, ui }: {
|