@leonhardthust/dsh-decision-engine 0.0.0-stage → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +55 -2
  3. package/README.zh.md +57 -0
  4. package/config.example.yml +436 -0
  5. package/cordis.patch.yml +14 -0
  6. package/docs/assets/config-page.png +0 -0
  7. package/docs/assets/decisions-tab.png +0 -0
  8. package/docs/assets/hero.jpeg +0 -0
  9. package/docs/assets/stats-charts-preview.png +0 -0
  10. package/docs/assets/stats-donut-toggle.png +0 -0
  11. package/docs/assets/stats-interactive-charts.png +0 -0
  12. package/docs/assets/stats-point-donut.png +0 -0
  13. package/docs/assets/stats-ranking-bars.png +0 -0
  14. package/docs/auto-review-hotfix.md +33 -0
  15. package/docs/lessons/cloudflare-builds-root-directory.md +51 -0
  16. package/docs/lessons/cold-start-timeout.md +134 -0
  17. package/docs/lessons/endpoint-path-vs-model.md +26 -0
  18. package/docs/lessons/model-field-required-by-provider.md +31 -0
  19. package/docs/local-dev-link.md +54 -0
  20. package/docs/releasing.md +69 -0
  21. package/docs/security-revision.md +130 -0
  22. package/icon.svg +6 -0
  23. package/lib/adapters/agent.js +314 -0
  24. package/lib/adapters/route.js +144 -0
  25. package/lib/adapters/tools.js +186 -0
  26. package/lib/adapters/translate.js +143 -0
  27. package/lib/approval-detail.js +325 -0
  28. package/lib/client.charts.js +36 -0
  29. package/lib/client.js +8715 -0
  30. package/lib/config-file.js +136 -0
  31. package/lib/credentials.js +187 -0
  32. package/lib/decision-cache.js +188 -0
  33. package/lib/decision-points.js +205 -0
  34. package/lib/decision-store.js +774 -0
  35. package/lib/endpoint-fallback.js +143 -0
  36. package/lib/engine.js +312 -0
  37. package/lib/guard/advisory.js +100 -0
  38. package/lib/guard/audit.js +81 -0
  39. package/lib/guard/context.js +128 -0
  40. package/lib/guard/effects.js +104 -0
  41. package/lib/guard/fallback.js +139 -0
  42. package/lib/guard/listener.js +214 -0
  43. package/lib/guard/messages.js +31 -0
  44. package/lib/guard/policy.js +28 -0
  45. package/lib/home-paths.js +86 -0
  46. package/lib/index.js +3823 -0
  47. package/lib/outcomes.js +169 -0
  48. package/lib/points/spec.js +645 -0
  49. package/lib/providers.js +958 -0
  50. package/lib/resilience.js +308 -0
  51. package/lib/scope.js +67 -0
  52. package/lib/server-config.js +351 -0
  53. package/lib/session-events.js +75 -0
  54. package/lib/stats-store.js +916 -0
  55. package/lib/stats.js +1293 -0
  56. package/lib/telemetry.js +465 -0
  57. package/lib/tokenizer.js +201 -0
  58. package/lib/tolerance.js +498 -0
  59. package/lib/vendor/microcharts-charts.css +47 -0
  60. package/lib/vendor-src/entry.js +13 -0
  61. package/lib/vendor-src/jsx-shim.js +34 -0
  62. package/locale/en.json +6 -0
  63. package/locale/zh.json +6 -0
  64. package/package.json +101 -3
  65. package/scripts/migrate-profile.mjs +102 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 zuosus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,56 @@
1
- # Temporary Holding Version
1
+ # dsh-decision-engine
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Published fork: **`@leonhardthust/dsh-decision-engine`**. Based on the MIT-licensed upstream by [zuosus](https://github.com/zuosus/dsh-decision-engine). Do not enable both packages at once. See [publishing and installation](docs/releasing.md).
4
+
5
+ ![dsh-decision-engine — decision engine for DeepSeek Harness](docs/assets/hero.jpeg)
6
+
7
+ **Free decision model usage provided**
8
+
9
+ [website](https://dsh.zuos.us/dsh-decision-engine/) ·
10
+ [config.example.yml](config.example.yml) · [DECISIONS.md](DECISIONS.md)
11
+
12
+ [简体中文](./README.zh.md)
13
+
14
+ A judgement layer for DeepSeek Harness: a cheap decision model judges before the
15
+ main model acts.
16
+
17
+ ## Features
18
+
19
+ **Eight decision points**
20
+
21
+ - tool authorization
22
+ - model routing
23
+ - delegation
24
+ - compaction
25
+ - turn continuation
26
+ - retrieval necessity
27
+ - skill selection
28
+ - COT translation gate
29
+
30
+ **Modes**: off · observe · enforce
31
+
32
+ **Providers**: system one · bamboonode · 硅基流动 SiliconFlow (CN / Global) ·
33
+ Perplexity · OpenRouter · custom base URL
34
+
35
+ **Review tolerance**: autonomous · relaxed · default · cautious
36
+
37
+ **UI**: config page with collapsible sections · Decisions tab · stats ·
38
+ readable approval card
39
+
40
+ | Config page | Decisions tab |
41
+ |---|---|
42
+ | ![config page](docs/assets/config-page.png) | ![decisions tab](docs/assets/decisions-tab.png) |
43
+
44
+ > Screenshots predate the collapsible-section layout (DEC-051).
45
+
46
+ **Token saving**: exact cache · read-only tools bypass · state trimming
47
+
48
+ **Safety layers**: trusted host context → tool effects → mandatory permissions → model advisory → soft review tolerance → host outcome audit.
49
+ Unknown high-impact calls fail closed; Auto still runs decision review. Soft asks use a selected host model (including reasoning effort), the session model, allow, or deny.
50
+ Neither model review nor the allow fallback can replace mandatory approval.
51
+
52
+ See the [security revision and verification notes](docs/security-revision.md). The plugin remains disabled by default; observation mode does not enforce protection.
53
+
54
+ ## License
55
+
56
+ MIT
package/README.zh.md ADDED
@@ -0,0 +1,57 @@
1
+ # dsh-decision-engine
2
+
3
+ 本分支发布包:**`@leonhardthust/dsh-decision-engine`**,基于 [zuosus 上游](https://github.com/zuosus/dsh-decision-engine) 的 MIT 代码。不要同时启用原包与本分支;安装与发布方法见 [发布说明](docs/releasing.md)。
4
+
5
+ ![dsh-decision-engine — DeepSeek Harness 决策引擎](docs/assets/hero.jpeg)
6
+
7
+ **提供免费决策模型**
8
+
9
+ [网站](https://dsh.zuos.us/dsh-decision-engine/) ·
10
+ [config.example.yml](config.example.yml) · [DECISIONS.md](DECISIONS.md)
11
+
12
+ [English](./README.md)
13
+
14
+ 给 DeepSeek Harness 的决策层:主模型动手之前,用一个便宜的决策小模型做结构化判断。
15
+
16
+ ## 功能
17
+
18
+ **八个决策点**
19
+
20
+ - 工具授权
21
+ - 模型路由
22
+ - 子任务分派
23
+ - 上下文压缩
24
+ - 轮次延续
25
+ - 检索必要性
26
+ - 技能选择
27
+ - 思维链翻译
28
+
29
+ **三态**:关闭 · 观察 · 生效
30
+
31
+ **决策服务**:system one · bamboonode · 硅基流动 SiliconFlow(国内 / 国际)·
32
+ Perplexity · OpenRouter · 自定义 base URL
33
+
34
+ **人工审核比例**:自治 · 轻松 · 默认 · 谨慎
35
+
36
+ **界面**:配置页(四个可折叠分组)· 决策历史标签页 · 统计与对比 ·
37
+ 审批卡正文可读化
38
+
39
+ | 配置页 | 决策历史 |
40
+ |---|---|
41
+ | ![配置页](docs/assets/config-page.png) | ![决策历史](docs/assets/decisions-tab.png) |
42
+
43
+ > 截图拍于分组折叠(DEC-051)之前的版式,当前界面为四个可折叠分组。
44
+
45
+ **省 token**:精确缓存 · 只读工具放行 · state 截断
46
+
47
+ **安全分层**:可信宿主上下文 → 工具副作用 → 强制权限策略 → 模型建议 → 软审核容忍度 → 宿主结果审计。
48
+ 未知高影响操作不再异常放行;自动审查保留决策模型,并将软性“人工决定”交给所选 Fallback。
49
+
50
+ **自动审查 Fallback**:指定宿主模型(含 reasoning effort)· 沿用会话模型 · 全部允许 · 全部阻止。
51
+ “全部允许”只影响软审查,不能覆盖强制拒绝或必需的真人批准。
52
+
53
+ 详见 [安全修订与验证说明](docs/security-revision.md)。默认仍未启用插件,观察模式不提供拦截保护。
54
+
55
+ ## 许可证
56
+
57
+ MIT
@@ -0,0 +1,436 @@
1
+ # dsh-decision-engine 配置模板
2
+ #
3
+ # 用法:复制到数据目录,按需修改。
4
+ # mkdir -p ~/.dsh/dsh-decision-engine
5
+ # cp config.example.yml ~/.dsh/dsh-decision-engine/config.yml
6
+ #
7
+ # 文件名是 config.yml:本插件早已不是「只服务 startlux 一家」的插件,
8
+ # 而是任意决策服务(system one / bamboonode / 硅基流动 / custom)的决策层,
9
+ # 名字里再挂某个服务商只会误导。旧名 startlux.yml 仍会被自动读取并迁移,
10
+ # 升级不需要重新填 key。
11
+ #
12
+ # 插件按以下顺序找配置,取第一个存在的:
13
+ # ~/.dsh/dsh-decision-engine/config.yml (首选位置)
14
+ # ~/.dsh/dsh-decision-engine/startlux.yml (旧名,首次启动自动改名)
15
+ # ~/.dsh/dsh-startlux-decision/startlux.yml (上一代数据目录;自动拷到新目录)
16
+ # ~/.dsh/plugins/dsh-startlux-decision/startlux.yml
17
+ # ~/.dsh/plugins/dsh-startlux-guard/startlux.yml
18
+ #
19
+ # 数据目录(插件自动读取):
20
+ # <上述目录>/config.yml 配置
21
+ # <上述目录>/telemetry.jsonl 决策记录(人读的流水账)
22
+ # <上述目录>/stats.jsonl 统计记录(配置页「统计与对比」读它)
23
+ # <上述目录>/analytics.json 匿名统计的本地状态(安装 id,可删除以重置)
24
+ #
25
+ # stats.jsonl 记的是按 provider × model 分开的 token 与耗时,用于统计与
26
+ # 节省量对比;telemetry.jsonl 不含 provider 与 output token,满足不了统计
27
+ # 所需,因此两者分开存。两者都是追加写的 JSON Lines,可直接 tail 查看。
28
+ #
29
+ # 改完立即生效:每次工具调用 / 每一步前都会检查 mtime,无需重启 DSH。
30
+
31
+ # 总开关:true 才开始工作(默认 false,不会擅自介入你的会话)
32
+ enabled: false
33
+
34
+ # shadow = 只记录判断结果,绝不拦截(建议先用它观察一段时间)
35
+ # enforce = 真正执行各决策点的处置
36
+ #
37
+ # 注意:这一项现在只是「工具授权」这一个决策点的历史开关。
38
+ # 新写法是给每个决策点单独指定模式,见下面的 pointXxx。
39
+ # pointAuthorize 留 auto 时,就沿用这里的 mode(shadow→observe、enforce→enforce)。
40
+ mode: shadow
41
+
42
+ # ============================================================================
43
+ # 服务提供方(provider)
44
+ # ============================================================================
45
+ # 七选一:
46
+ # bamboonode 内置。StartLux 官方端点 POST /v1/decisions,本插件的原有默认值。
47
+ # systemone 内置。任意 System One 协议服务 POST /v1/systemone,
48
+ # 默认指向 TypeSafe(https://api.typesafe.ai/v1)。
49
+ # siliconflow 内置。硅基流动(国内站)https://api.siliconflow.cn/v1/systemone
50
+ # siliconflow-intl 内置。Silicon Flow 国际站 https://api.siliconflow.com/v1/systemone
51
+ # (与国内站是两套账号,key 互不通用)
52
+ # perplexity 内置。Perplexity Decider(api.perplexity.ai);
53
+ # 地址与默认模型待实测,端点风格 auto(404 自动换路径重试)
54
+ # openrouter 内置。OpenRouter(https://openrouter.ai/api/v1),
55
+ # 模型填 OpenRouter 的 slug(如 openai/gpt-4o-mini);同样待实测
56
+ # custom 自己填 base URL,接任何提供决策端点的服务
57
+ # (自建 llama.cpp / vLLM、企业网关、第三方中转)。
58
+ #
59
+ # 不写这一项 = bamboonode,老配置行为完全不变。
60
+ #
61
+ # siliconflow / siliconflow-intl 是「开箱即用」的:地址、路径、默认模型都已
62
+ # 预置,只要粘一个 API key 就能跑(详见下方「密钥」一节)。
63
+ #
64
+ # perplexity / openrouter 也已预置,但**端点风格与默认模型尚未实测**(接入时
65
+ # 按各家公开 API 地址写的):首次使用若 404,插件会自动换另一条路径重试;
66
+ # 跑通后建议把实测结论写回 lib/providers.js 的预设表。
67
+ provider: bamboonode
68
+
69
+ # ---- bamboonode 预设的地址覆盖 ----------------------------------------------
70
+ # 只在 provider=bamboonode 时生效。老版本唯一的地址字段,继续支持,因此
71
+ # 已有的自建 bamboonode 兼容地址不会因为升级被改回官方。
72
+ # 接受多种写法(等价):
73
+ # https://api.bamboonode.dev/v1
74
+ # https://api.bamboonode.dev/v1/decisions
75
+ # api.bamboonode.dev/v1 (会补 https://)
76
+ # 末尾斜杠会被规范化,不会拼出 //decisions 或 /v1/v1/decisions。
77
+ baseUrl: https://api.bamboonode.dev/v1
78
+
79
+ # ---- 模型(按 provider 分开存,切换时互不覆盖)------------------------------
80
+ # bamboonode 的模型。留空 = 自动(由服务端选当前默认决策模型),
81
+ # 这样服务商将来换模型名时,本配置不会失效。
82
+ model:
83
+
84
+ # system one 的模型。留空 = 服务端默认(jev-latest)。
85
+ # 与上面的 model 分开:否则切到 system one 会把 bamboonode 的模型名发过去,
86
+ # 得到 400 model not served。
87
+ systemoneModel: ""
88
+
89
+ # 硅基流动 / Silicon Flow 的模型。**留空 = 用预设默认 Kev-4B**——
90
+ # 这两个端点把 model 列为必填(实测省略或空串都回 400
91
+ # "Model field is required."),所以这里的「留空」不是「让服务端挑」,
92
+ # 而是用预设默认。想换模型就填 SemIf(官方目前只支持这两个决策模型)。
93
+ #
94
+ # 你写的 kev-4b 与 Kev-4B 是同一个模型:实测该端点对大小写不敏感
95
+ #(三种写法都 200),/v1/models 里的规范写法是 Kev-4B。
96
+ siliconflowModel: ""
97
+ siliconflowIntlModel: ""
98
+
99
+ # ---- custom 预设(provider=custom 时才生效)--------------------------------
100
+ # base URL 必填,且必须是 http/https 地址;不带 scheme 会被拒绝(内置预设
101
+ # 允许省略 scheme,custom 不允许——自定义地址必须显式,避免猜错协议)。
102
+ # customBaseUrl: https://api.example.com/v1
103
+ #
104
+ # 端点风格(**首选**路径,不是唯一路径):
105
+ # auto 地址已带 /decisions 或 /systemone 就原样用,否则先试 decisions
106
+ # decisions 先试 POST <base>/decisions
107
+ # systemone 先试 POST <base>/systemone
108
+ #
109
+ # 路径后缀由**服务商**决定,不由模型决定。实测(2026-10-25,真实端点):
110
+ # SiliconFlow 的 Kev-4B /v1/decisions → 404,/v1/systemone → 200
111
+ # bamboonode 的决策模型 两条路径都 200
112
+ #
113
+ # 所以首选路径返回 404 时,插件会自动改用另一条再试一次,并记住试通的那条。
114
+ # 只有 404 会触发换路径:400(模型/内容不合法)与 401(key 不对)都跟路径无关。
115
+ customEndpointStyle: auto
116
+ #
117
+ # custom 自己的模型(留空 = 服务端默认)。与内置预设的 model 分开存,
118
+ # 这样在两者之间来回切换不会互相覆盖。
119
+ customModel: ""
120
+ # custom 的 key;留空则读 customApiKeyEnv(环境变量或 .credentials.yaml 的 refs)
121
+ customApiKey: ""
122
+ customApiKeyEnv: ""
123
+ # 附加请求头,格式 `名称: 值`,多项用分号分隔。
124
+ # authorization / content-type / host 由插件固定,写了也会被忽略。
125
+ customHeaders: ""
126
+
127
+ # ============================================================================
128
+ # 密钥
129
+ # ============================================================================
130
+ # 每个 provider 有**自己的** key 名,各自保存各自的 key:
131
+ #
132
+ # system one -> TYPESAFE_API_KEY
133
+ # bamboonode -> BAMBOO_API_KEY
134
+ # 硅基流动(国内站) -> SILICONFLOW_API_KEY
135
+ # Silicon Flow 国际站 -> SILICONFLOW_INTL_API_KEY
136
+ # Perplexity Decider -> PERPLEXITY_API_KEY
137
+ # OpenRouter -> OPENROUTER_API_KEY
138
+ # custom -> customApiKeyEnv(没填时用 DECISION_ENGINE_CUSTOM_KEY)
139
+ #
140
+ # 各格互不覆盖,可以同时配好;切换 provider 不会串用别人的 key。
141
+ # 两个 SiliconFlow 站点是**两套账号**,key 不通用,因此各占一格——
142
+ # 共用一格的话,切过去就会拿错 key 得到 401,而用户看不出是拿错了还是失效了。
143
+
144
+ # 密钥存到哪里:
145
+ # credentials = ~/.dsh/.credentials.yaml 的 refs 段(默认,与其他服务统一托管)
146
+ # config = 本文件(自包含,便于随目录备份;注意这里是明文)
147
+ # 配置页上这一项不挨着 API Key,而是放在最后一组(匿名统计之后):它管的是
148
+ # 「key 存到哪」这件账务上的事,不是连接本身,夹在连接组里会打断
149
+ # 「provider → 模型 → key → 生效端点」这条阅读线。
150
+ keyStorage: credentials
151
+
152
+ # 变量名可以覆盖(留空即用上面的默认名):
153
+ # systemoneApiKeyEnv: ""
154
+ # bamboonodeApiKeyEnv: ""
155
+ # siliconflowApiKeyEnv: ""
156
+ # siliconflowIntlApiKeyEnv: ""
157
+ # perplexityApiKeyEnv: ""
158
+ # openrouterApiKeyEnv: ""
159
+ # customApiKeyEnv: ""
160
+
161
+ # 直接写明文 key 也可以(各 provider 一格,互不影响)。
162
+ # 若 keyStorage=credentials,在设置界面粘贴的 key 会写进 .credentials.yaml,
163
+ # 不会落到这里;只有 keyStorage=config 时下面这些字段才会被写入。
164
+ # systemoneApiKey: ""
165
+ # bamboonodeApiKey: ""
166
+ # siliconflowApiKey: ""
167
+ # siliconflowIntlApiKey: ""
168
+ # perplexityApiKey: ""
169
+ # openrouterApiKey: ""
170
+ # customApiKey: ""
171
+
172
+ # .credentials.yaml 里的形态就是 refs 段:
173
+ # refs:
174
+ # BAMBOO_API_KEY: sk-你的key
175
+ # TYPESAFE_API_KEY: sk-另一个key
176
+ # SILICONFLOW_API_KEY: sk-国内站的key
177
+ # SILICONFLOW_INTL_API_KEY: sk-国际站的key
178
+ # PERPLEXITY_API_KEY: sk-perplexity的key
179
+ # OPENROUTER_API_KEY: sk-openrouter的key
180
+
181
+ # ============================================================================
182
+ # 服务端下发(DEC-030)——配置页上叫「使用免费模型」
183
+ # ============================================================================
184
+ # 打开后,地址 / 模型 / key 由内置端点 https://dsh.zuos.us/api/provider-config
185
+ # 下发并直接使用。**地址不可配置**是刻意的:它就是「开箱即用」的全部含义。
186
+ #
187
+ # 名字为什么是「使用免费模型」而不是「跟随服务端下发」:它确实是免费下发的模型,
188
+ # 那是用户能直接感知到的事实;而配置页在这行说明下面还多一行小字「可能会失效」
189
+ # ——这条免费通道由项目方自掏额度维护,随时可能停用或换 key。风险写在界面上,
190
+ # 不给「免费」一个不存在的承诺。开关默认仍为 false,行为与改名前完全一样。
191
+ #
192
+ # 关于下发的那把 key(重要):
193
+ # - 只保存在插件进程的内存里,**永不落盘**:不写本文件、不写 .credentials.yaml、
194
+ # 不进遥测 / 统计 / 决策明细;任何接口只回传末四位(…abcd)。
195
+ # - 进程退出即消失,下次启动重新获取(下发带 TTL,默认 600 秒)。
196
+ # - 关掉这个开关会立刻把它从内存里清掉。
197
+ #
198
+ # 服务端不可用时(404 / 403 / 超时 / 载荷非法 / 已过期且刷新失败)**静默回落到下面的
199
+ # 本地配置**(包括你自己填的 key),配置页显示一行原因,不会中断守卫。
200
+ #
201
+ # 手动填 key 的整套流程完全不变;服务端生效期间本地值只是暂不生效,关掉开关立刻恢复。
202
+ #
203
+ # 可选门禁:Worker 侧若设了 PROVIDER_CONFIG_TOKEN,本机要能读到同一把令牌——
204
+ # 优先环境变量 DECISION_SERVER_CONFIG_TOKEN,其次下方 .credentials.yaml 的 refs 段同名键。
205
+ serverProvider: false
206
+
207
+ # 超时预算(毫秒)。超时仅使模型建议失效,强制策略仍生效;连续失败进入冷却。
208
+ #
209
+ # 为什么是 20000 而不是 8000:这个服务端有**冷启动尾延迟**。闲置约 110 秒后的
210
+ # 首发实测要 7.4–15.9 秒,焐热之后只要 1.0–1.3 秒。8 秒会把冷启动首发**稳定
211
+ # 误杀**,而那恰好是用户点「测试」时的常态——每次点都落在「闲置后首发」这个
212
+ # 最差点上。成功请求的延迟分布(n=2215)里 ≥8s 占 **0%**:不是没有慢请求,
213
+ # 是慢的那些全被 8 秒预算掐掉了,根本没算进成功。
214
+ # 代码里的 DEFAULT_TIMEOUT_MS 本来就是 20000,模板这里只是跟上。
215
+ timeoutMs: 20000
216
+
217
+ # 超时 / 网络错误的重试次数。**只对这两类重试**:4xx 是配置错(地址、模型、
218
+ # key),5xx 是服务端自己的故障,重试只是多烧一次额度、拿回同一个错误。
219
+ # 实测:冷启动首发被掐断时服务端其实已经被焐热,第二发 1.1–1.7 秒必中
220
+ # (4 组独立实验一致)。总预算封顶 min(2 × timeoutMs, 30s)。
221
+ retryAttempts: 1
222
+
223
+ # 冷却期的试探间隔(毫秒)。冷却不再是「整段全黑」:每隔这么久放行一次试探
224
+ # 请求,成功即彻底恢复。避免一次 3 秒的抖动换来 60 秒完全不工作。
225
+ cooldownProbeMs: 10000
226
+
227
+ # ---- 冷却与失败阈值(可选调优项)----
228
+ # 冷却已改为**半开 + 指数退避**:连续失败达到阈值进入冷却,冷却中每
229
+ # cooldownProbeMs 放行一次试探;试探失败则冷却时长按轮次翻倍
230
+ # (cooldownMs → 2× → 4× …,封顶 10 × cooldownMs),试探成功即彻底解除。
231
+ # 注意 cooldownMs 现在是**首轮**冷却时长,不再是上限——上限是它的 10 倍。
232
+ # cooldownMs: 60000 # 首轮冷却时长(上限为其 10 倍)
233
+ # failureThreshold: 3 # 连续失败多少次进入冷却
234
+
235
+ # 思考模式(startlux 的 think_mode),三档:
236
+ # never 不思考——最省延迟(实测约 1.9s vs 7.2s),默认值,热路径首选
237
+ # auto 仅在模型自己判断需要时思考——折中
238
+ # always 总是思考——判断更充分,但每次工具调用约多等 5 秒
239
+ # 该端点 output_tokens 恒为 0,思考开销不计入计量,所以省的主要是延迟。
240
+ thinkMode: never
241
+
242
+ # ============================================================================
243
+ # 决策模型参与点
244
+ # ============================================================================
245
+ # 三态:off 不参与 / observe 只记录不干预 / enforce 真正生效。
246
+ # 九个点全部已实装。新点默认 off —— 升级不会改变现有行为。
247
+ #
248
+ # 建议:先 observe 跑一段时间,对着 telemetry.jsonl 确认与人工判断一致,
249
+ # 再逐点切 enforce。热路径的点(route / delegate / cotTranslate)尤其如此。
250
+
251
+ # ---- 1. 工具授权:每次工具调用前判断能否放行 -------------------------------
252
+ # 替代 auto-review——后者把完整会话历史发给主模型,每次调用都跑一遍。
253
+ pointAuthorize: auto
254
+
255
+ # ---- 2. 模型路由:机械步骤下沉到便宜模型 -----------------------------------
256
+ # 热路径。三道闸控量:每轮最多 routeMaxStepsPerTurn 步、路由未变不问、
257
+ # 新用户输入后的步骤不问。
258
+ # 必须同时配好 routeCheapProvider + routeCheapModel 才会真的换模型;
259
+ # 两者留空时即便打开此点也只会记录「本该换」。
260
+ pointRoute: off
261
+ routeCheapProvider: ""
262
+ routeCheapModel: ""
263
+ # routeMaxStepsPerTurn: 3
264
+ # routeMaxScore: 0.4 # 能力需求归一化后超过此值就保留强模型
265
+ # routeMechanicalFloor: 0.6 # 「机械步骤」的最低置信
266
+ # routeRiskFloor: 0.3 # 风险达线一律保留强模型
267
+
268
+ # ---- 3. 子任务分派:接着做 / 开 subagent / 组 agent team -------------------
269
+ # 热路径。只在每轮第一步且带新用户输入时判断(分派只在这一刻可能改变)。
270
+ # 生效时注入一条提示,不替模型决定——工具选择仍归模型。
271
+ pointDelegate: off
272
+ # delegateConfidenceFloor: 0.5 # 三选项均分 0.33,实测需 ≥0.5 才算有倾向
273
+ # delegateBlockingFloor: 0.6 # 被本地事实阻塞时继续内联
274
+ # delegateParallelFloor: 0.5 # 组队还需真并行才有意义
275
+
276
+ # ---- 4. 上下文压缩:摘要 vs 只丢陈旧工具结果 -------------------------------
277
+ # 非热路径。只在实测 token 达触发线(compactThresholdRatio × 窗口)才问。
278
+ # 生效方式:先跑宿主的确定性工具结果裁剪;compaction-basic 随后重新测压,
279
+ # 低于阈值就跳过摘要——这才是真正省下的那一次调用。
280
+ pointCompact: off
281
+ # compactContextWindow: 0 # 0 = 用 compactTriggerTokens 绝对阈值
282
+ # compactThresholdRatio: 0.7 # 提前于 compaction-basic 的 0.8 介入
283
+ # compactTriggerTokens: 100000
284
+ # compactToolResultChars: 8192
285
+ # compactDistilledFloor: 0.6 # 结论已落地 → 允许只裁剪
286
+ # compactVerbatinFloor: 0.6 # 仍需逐字原文 → 一律摘要(安全否决优先)
287
+
288
+ # ---- 5. 轮次延续:再来一轮有没有增量价值 -----------------------------------
289
+ # 非热路径。生效方式是用 agent.steer() 续一轮,每轮最多 turnStopMaxSteers 次。
290
+ pointTurnStop: off
291
+ # turnStopMaxSteers: 1
292
+ # turnStopClosedFloor: 0.6
293
+ # turnStopUnfinishedFloor: 0.6
294
+ # turnStopValueFloor: 0.35 # 只作弱确认;主判据是 unfinished
295
+ # 「这一轮在等用户」的判定门槛(DEC-045)。达线即让位:不续轮,把决定权交还用户。
296
+ # 这条只用于**让位**,不会变成「所以替你选了方案 A」。
297
+ # 取 0.5 是因为两类错代价不对等:漏判 = 替你把选择顶掉(最不该犯的错),
298
+ # 误判 = 少续一轮(代价小得多)。
299
+ # turnStopAwaitingUserFloor: 0.5
300
+
301
+ # ---- 6. 检索必要性:这条信息是不是已经知道 ---------------------------------
302
+ # 非热路径。只对检索类工具生效,其余工具零开销。
303
+ pointRetrieval: off
304
+ # retrievalTools: [web_search, web_fetch, research, multi_search, advanced_search]
305
+ # retrievalCoveredFloor: 0.5 # 上下文已覆盖该事实
306
+ # retrievalWouldAddCeiling: 0.8 # 且再搜基本没有新信息
307
+
308
+ # ---- 7. 技能选择:该装哪个技能 ---------------------------------------------
309
+ # 非热路径。只对 skill 工具、且候选 ≥2 时判断。
310
+ # 生效时拦下不匹配的加载并指路,避免「装错再换」付两遍正文 token。
311
+ pointSkillPick: off
312
+ # skillPickCandidates: 8 # 送给模型挑选的候选上限(只带名称+描述)
313
+ # skillPickConfidenceFloor: 0.5
314
+
315
+ # ---- 8. 思维链翻译门槛:这条内容真的需要翻译吗 -----------------------------
316
+ # 热路径(每条消息)。本地先做免费预筛(去代码块/URL 后判断有无正文),
317
+ # 预筛不过的直接跳过,不发请求。生效时判为「不需要」则返回原文。
318
+ pointCotTranslate: off
319
+ # cotProseFloor: 0.5
320
+ # cotSameLanguageFloor: 0.8
321
+
322
+ # ============================================================================
323
+ # 自身开销护栏
324
+ # ============================================================================
325
+ # 决策模型自己也烧 token(实测约 1.9k/次,p50 约 0.5s),靠缓存挡住重复调用:
326
+ # 命中缓存就不发请求。本插件不限制每小时的总用量,因此没有预算字段。
327
+ # 「同类复用」软层默认关闭(fail-closed),工具授权点永久禁用;
328
+ # cacheSize / cacheTtlMs 同时约束精确层与(显式启用后的)软层。
329
+
330
+ # ---- 以下为可选调优项 ----
331
+ # cacheSize: 256 # 缓存条数上限
332
+ # cacheTtlMs: 600000 # 裁决存活时间
333
+ # recentMessages: 4 # 带给模型的上下文条数
334
+ # guardReadOnlyTools / guardExcludeTools / guardDeferOutsideWorkspace 保留旧键解析,
335
+ # 不再是绕过强制安全策略的白名单;越界写入不能无条件交给宿主。
336
+ # guardDenyConfidence 不再参与新授权策略。confidence 不能充当危险概率。
337
+ # guardIrreversibleCeiling: 0.2
338
+ # guardTaskRelevanceFloor: 0.5
339
+
340
+ # 新策略默认 enforce;observe 仅用于隔离对照诊断,不实施 mandatory 拦截。
341
+ # 只有 enabled=true、guardEnabled=true、pointAuthorize=enforce 才真正拦截。
342
+ guardPolicyMode: enforce
343
+
344
+ # 自动审查(宿主 auto 或 tolerance=autonomous)的软性 ask 和建议失败时:
345
+ # model 指定宿主注册的 provider / model / reasoning effort
346
+ # session 沿用当前会话 request/header 的模型和 reasoning effort(默认)
347
+ # allow 跳过软审查,仍不可覆盖 mandatory deny / requires_approval
348
+ # deny 阻止软审查请求
349
+ # 指定模型不可用、超时、响应非法一律拒绝,不再嵌套回退成 allow。
350
+ guardFallback: session
351
+ guardFallbackProvider: ""
352
+ guardFallbackModel: ""
353
+ guardFallbackReasoningEffort: ""
354
+
355
+ # 四档只调整软审核摩擦。mandatory deny 全档不变;requires_approval 必须
356
+ # 获得当前调用的宿主 allowed-once,无审批通道时拒绝,模型不能代批。
357
+ # autonomous 将软 ask 交给上述 Fallback,不等于直接放行。
358
+ # relaxed / default / cautious 保留既有软摩擦档位行为;旧默认裁决函数
359
+ # decideGuardLegacy 仅用于回放,不接入生产权限路径。
360
+ # severity 是排序分数,分位数就绪不代表危险概率已校准。
361
+ # tolerance: default
362
+
363
+ # ============================================================================
364
+ # 匿名使用统计(可选)
365
+ # ============================================================================
366
+ # 只上报「装了多少台、每天有多少人在用」,用来改进插件。字段只有七个:
367
+ # 匿名安装 id(首次运行随机生成的 UUIDv4,与账号无关)、插件版本、平台、
368
+ # 架构、宿主版本、当日决策数、事件类型。
369
+ #
370
+ # 绝不上报:API key、prompt / 工具参数、文件路径、provider 地址、会话内容、
371
+ # 用户名;服务端也不记录 IP。删掉 analytics.json 即换新身份。
372
+ #
373
+ # off 完全联网都不做(连版本检查都没有)
374
+ # install 仅首次运行上报一次「装了」
375
+ # daily 每天上报一次,附带当日决策数(默认)
376
+ #
377
+ # telemetryLevel: daily
378
+ # telemetryEndpoint: "" # 留空用内置端点
379
+
380
+ # ============================================================================
381
+ # 决策历史(DEC-022)——会话里的「决策历史」标签页
382
+ # ============================================================================
383
+ # 开启后,会话标签栏会在「对话」「轨迹」之后多出一个「决策历史」标签页,
384
+ # 按时间列出**本会话**里经本插件做出的每一次决策:决策点、provider、
385
+ # model、裁决、耗时、token,展开还能看到发给决策模型的现场(state)、
386
+ # 问题(questions)与它回来的回答(answers)。
387
+ #
388
+ # 数据只落在本机 $DSH_HOME/dsh-decision-engine/decisions.jsonl,
389
+ # **不经过任何网络请求**,也**不进**上面的匿名统计。
390
+ #
391
+ # 关掉之后**完全不写这个文件**(不采集内容、不占磁盘,标签页也随之消失)
392
+ # ——不是「写了藏起来」。明细里可能有会话正文与命令片段,用户说不记录
393
+ # 就该真的不落盘。
394
+ #
395
+ # decisionsTabEnabled: true # 默认开启;**这是唯一的开关**(DEC-026 / DEC-029):
396
+ # 开 = 记录明细,且**一定记录决策内容**;
397
+ # 关 = 完全不写 decisions.jsonl(见上),
398
+ # 没有「只存元数据」的中间档。
399
+ #
400
+ # 下面两项是**高级档位**,配置页上没有对应的开关:需要时手改本文件。
401
+ # (一个开关管「这个功能要不要用」就够了;「记多细、最多占多大」是要主动
402
+ # 去调的旋钮,不该天天摆在配置页上。)
403
+ #
404
+ # 单条内容的上限(字符)。超出时依次裁剪:工具参数预览 → 最近对话摘要 →
405
+ # 整个 state;只有连「问题 + 回答」都装不下时才退回只留元数据。
406
+ #
407
+ # decisionsMaxContentChars: 4000
408
+ #
409
+ # 明细文件体积上限(字节)。到顶轮转成 decisions.jsonl.1(只留一代),
410
+ # 因此磁盘占用最多约两倍上限。
411
+ #
412
+ # decisionsMaxBytes: 8388608 # 8 MB
413
+
414
+ # ============================================================================
415
+ # 审批卡正文可读化(DEC-043)——审批卡上那块命令怎么显示
416
+ # ============================================================================
417
+ # 默认**开启**,不需要做任何事就能得到:插件接管宿主的
418
+ # conversation.approval.detail,把标题下那块命令改成「摘要 + 可展开的原文」——
419
+ # 先说清「几步、其中哪几步是危险动作」(rm -rf / curl … | sh / sudo 这类会被
420
+ # 标出来),再逐条列出每个步骤;原文默认收起,点开可逐字核对。
421
+ #
422
+ # 摘要由**确定性规则**切分(&& / || / ; / | / 换行,heredoc 整段识别成「执行
423
+ # 一段脚本(N 行)」),**不经过决策模型**——端点只有 choice / score / noul
424
+ # 三种题型、没有自由文本,让模型写一句人话解释在架构上做不到。
425
+ #
426
+ # **真正执行的永远是原文**。摘要只是导读,与原文不一致就是欺骗,所以原文
427
+ # 永远可展开核对。
428
+ #
429
+ # 想保留宿主自己的样子就关掉这一项:关 = 完全不接管,宿主自己的渲染原样回来,
430
+ # 与 1.5.x 逐字一致(换行照旧由宿主折叠成一个长条)。
431
+ #
432
+ # 一句实话:接管这块意味着宿主将来对它的改进**不会自动落到我们这里**,直到我们
433
+ # 跟上;步骤清单本身是启发式的(刻意不做完整 shell 解析)。这两点是「保留这个
434
+ # 开关、并让原文永远可展开」的理由,不是把可读卡片藏起来的理由。
435
+ #
436
+ # approvalDetailReadable: true # 默认开启;关掉则完全沿用宿主渲染
@@ -0,0 +1,14 @@
1
+ # dsh-decision-engine 的 bundle 层。
2
+ #
3
+ # 这里**刻意不写 config**。
4
+ #
5
+ # 原因:bundle patch 属于 profile 配置层,而 DSH 传给插件 apply() 的 config
6
+ # 是 Schemastery 解析后的完整默认值(enabled:false / mode:shadow ...),
7
+ # 并非用户显式配置。若在此硬编码,会盖掉用户在配置页保存的选择,
8
+ # 表现为「一按保存就自动弹回关闭」。
9
+ #
10
+ # 运行时配置的唯一来源是插件目录下的 config.yml(配置页保存的也是它)。
11
+ # 插件内的 DEFAULTS 只负责兜底,不在此重复声明。
12
+ - insert:
13
+ - id: decision-engine
14
+ name: '@leonhardthust/dsh-decision-engine'
Binary file
Binary file
Binary file
@@ -0,0 +1,33 @@
1
+ # 1.9.1 自动审查热修复
2
+
3
+ ## 根因
4
+
5
+ 1.9.0 把“不认识工具 / 无法静态解析 shell、MCP 或网络效果”直接升级为 `requires_approval`。监听器在调用决策模型之前就返回 ask/deny,Auto 没有机会使用大模型回退。
6
+
7
+ 这些提前返回的记录缺少 provider/model,latency 为 0,state/questions 尚未构建,因而只显示 `unknown-high-impact-effect-requires-approval` 和空分数。它们不是模型瞬间完成的结论。
8
+
9
+ ## 修复
10
+
11
+ - 不确定效果为 `unknown`,不是宿主要求真人审批的证据。Auto 先调用决策模型;不确定效果即使初判 low,也进入独立 LLM 或用户显式选择的回退策略。
12
+ - medium/high、异常和无效模型结果同样进入回退,不直接交给用户。默认模型回退失败、超时或无效输出拒绝;显式 allow/deny 仅调整软审查。
13
+ - 手动模式可询问;审批禁用时不能伪造用户批准。
14
+ - 显式 `sandbox_permissions`、宿主拒绝、已知越界写入、只读环境写入、已识别提权/破坏等硬约束仍先于模型。LLM allow 不写成宿主 allowed-once。
15
+ - 多目标补丁优先保留确定越界;另一条无法解析的路径不能掩盖它。
16
+ - `skill`、Inspect、模型目录等严格只读查询不再自锁。
17
+
18
+ ## 审计与 UI
19
+
20
+ 新增 `decisionSource` 与 `modelEvaluation.primary/fallback`,分开记录未请求、完成、失败、缓存命中及实际 provider/model/latency。
21
+
22
+ - 本地规则显示“本地权限规则 / 未调用模型 / 未调用”,不再展示成未知提供方的 0ms 模型推理。
23
+ - 模型失败与缓存命中使用明确标签;回退模型身份与原 primary 身份分开保留。
24
+ - 早期规则仍构建真实 state/questions 摘要,未请求时 questions.status 为 not_requested。
25
+ - 没有模型事实的规则路径不伪造模型费用记录。
26
+ - 用户提示为可读说明,机器码留在 reasonCode / denial info。
27
+ - 旧记录证据不足时保持原样,不把 0ms 倒推为特定调用状态。
28
+
29
+ ## 验证边界
30
+
31
+ 125 项专项回归、212 项组合回归通过;24 个独立合成情景的四档矩阵通过。组合之间有重叠,不相加成独立样本数。模型 stub 的 allow/deny/failure 仅验证路由不变量,不代表真实危险 FNR 或概率校准。
32
+
33
+ 本次按 hunk 集成决策历史状态展示,不覆盖其他任务的 dark-mode UI 改动;其他任务是否已提交以 Git 记录为准。发布仍需干净提交的 Linux 完整 CI 成功;已安装的 1.9.0 不会因源码修改自动升级。