dsh-request-doctor 0.13.0 → 0.13.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.
- package/client.js +24 -1
- package/docs/01-/350/275/257/344/273/266/351/234/200/346/261/202/350/247/204/346/240/274/350/257/264/346/230/216.md +194 -192
- package/docs/02-/350/275/257/344/273/266/350/256/276/350/256/241/350/257/264/346/230/216.md +1 -1
- package/docs/03-/350/275/257/344/273/266/346/265/213/350/257/225/346/212/245/345/221/212.md +12 -9
- package/docs/04-/345/256/211/350/243/205/351/203/250/347/275/262/350/257/264/346/230/216.md +1 -1
- package/docs/05-/347/224/250/346/210/267/346/211/213/345/206/214.md +10 -1
- package/docs/06-/350/275/257/344/273/266/350/264/250/351/207/217/345/272/246/351/207/217/346/212/245/345/221/212.md +1 -1
- package/docs/quality-report.json +9 -9
- package/index.js +4 -1
- package/lib/panel-briefs.js +20 -0
- package/package.json +1 -1
package/client.js
CHANGED
|
@@ -30,6 +30,9 @@ window.__ModuleLoader__.load({
|
|
|
30
30
|
sourceManager: '来源:插件管理器拒装',
|
|
31
31
|
sourceAudit: '来源:本插件审计',
|
|
32
32
|
sourceUnknown: '来源:未知',
|
|
33
|
+
verdict: '判定:',
|
|
34
|
+
missing: '缺符号:',
|
|
35
|
+
environment: '环境:',
|
|
33
36
|
offline: '连不上 Host 侧的安装路由,可以改用命令:',
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -62,6 +65,23 @@ window.__ModuleLoader__.load({
|
|
|
62
65
|
return response.json()
|
|
63
66
|
}
|
|
64
67
|
|
|
68
|
+
// 一行一条审计结论:判定、理由、缺的符号、环境。
|
|
69
|
+
// 没有结论的(只有管理器拒装)就什么都不加,不占地方。
|
|
70
|
+
function briefLines(brief, style) {
|
|
71
|
+
if (brief === undefined) return []
|
|
72
|
+
const lines = [h('div', { key: `v-${brief.verdict}`, style: style.hush }, `${TEXT.verdict} ${brief.verdict}`)]
|
|
73
|
+
for (const reason of (brief.reasons || []).slice(-2)) {
|
|
74
|
+
lines.push(h('div', { key: `r-${reason}`, style: style.hush }, reason))
|
|
75
|
+
}
|
|
76
|
+
if ((brief.missing || []).length > 0) {
|
|
77
|
+
lines.push(h('div', { key: 'missing', style: style.hush }, `${TEXT.missing} ${brief.missing.join(', ')}`))
|
|
78
|
+
}
|
|
79
|
+
if (brief.environment !== undefined) {
|
|
80
|
+
lines.push(h('div', { key: 'env', style: style.hush }, `${TEXT.environment} ${brief.environment}`))
|
|
81
|
+
}
|
|
82
|
+
return lines
|
|
83
|
+
}
|
|
84
|
+
|
|
65
85
|
// 这条是谁给的:管理器拒装和本插件审计,含义完全不同。
|
|
66
86
|
function sourceLabel(source) {
|
|
67
87
|
if (source === 'manager') return TEXT.sourceManager
|
|
@@ -74,7 +94,7 @@ window.__ModuleLoader__.load({
|
|
|
74
94
|
async function loadPanel() {
|
|
75
95
|
const data = await requestJson('/doctor/incompatible', { headers: { accept: 'application/json' } })
|
|
76
96
|
if (data.ok !== true) throw new Error(data.reason || 'host declined')
|
|
77
|
-
return { rows: data.rows || [], restart: data.restart || [] }
|
|
97
|
+
return { rows: data.rows || [], restart: data.restart || [], briefs: data.briefs || {} }
|
|
78
98
|
}
|
|
79
99
|
|
|
80
100
|
// 装一个包。授权、重试、声明对齐都在 Host 侧完成,这里只拿结果。
|
|
@@ -99,12 +119,14 @@ window.__ModuleLoader__.load({
|
|
|
99
119
|
const [busy, setBusy] = React.useState(false)
|
|
100
120
|
const [note, setNote] = React.useState('')
|
|
101
121
|
const [restart, setRestart] = React.useState([])
|
|
122
|
+
const [briefs, setBriefs] = React.useState({})
|
|
102
123
|
|
|
103
124
|
const refresh = React.useCallback(async () => {
|
|
104
125
|
try {
|
|
105
126
|
const panel = await loadPanel()
|
|
106
127
|
setRows(panel.rows)
|
|
107
128
|
setRestart(panel.restart)
|
|
129
|
+
setBriefs(panel.briefs || {})
|
|
108
130
|
} catch (error) {
|
|
109
131
|
// 路由不通就把可用的替代办法说出来,不留空白。
|
|
110
132
|
setNote(`${TEXT.offline} /doctor install <包名>(${String((error && error.message) || error)})`)
|
|
@@ -160,6 +182,7 @@ window.__ModuleLoader__.load({
|
|
|
160
182
|
h('div', { style: style.title }, `${row.name}@${row.version}`),
|
|
161
183
|
h('div', { style: style.hush }, `${TEXT.requires} ${row.runtimeVersion || '?'}`),
|
|
162
184
|
h('div', { style: style.hush }, sourceLabel(row.source)),
|
|
185
|
+
...briefLines(briefs[row.name], style),
|
|
163
186
|
h('button', {
|
|
164
187
|
type: 'button', style: style.press, disabled: busy,
|
|
165
188
|
onClick: () => run(`${row.name}@${row.version}`, () => postInstall(`${row.name}@${row.version}`)),
|
|
@@ -1,192 +1,194 @@
|
|
|
1
|
-
# 软件需求规格说明
|
|
2
|
-
|
|
3
|
-
**产品名称**:request-doctor(请求急救员)
|
|
4
|
-
**产品标识**:request-doctor
|
|
5
|
-
**版本**:0.13.
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 1 引言
|
|
10
|
-
|
|
11
|
-
### 1.1 标识
|
|
12
|
-
|
|
13
|
-
本文档适用于 request-doctor 插件。该插件以 npm 包形式发布,模块名 `request-doctor`,运行于 DeepSeek Harness(以下简称 DSH)宿主进程内,属于 Host 端插件,无客户端一半。
|
|
14
|
-
|
|
15
|
-
### 1.2 系统概述
|
|
16
|
-
|
|
17
|
-
DSH 是一个以 Cordis 插件框架组织的智能体运行时。插件通过组合包(bundle)安装进某个 profile,由 Loader 在启动时组合。系统存在两类对用户可见的失败:
|
|
18
|
-
|
|
19
|
-
1. **请求失败**:模型请求、工具调用、浏览器载体请求在传输层、承载层或超时层失败,本身可重试,但运行时默认不重试或重试策略不足以覆盖。
|
|
20
|
-
2. **版本不匹配**:已安装插件声明的 DSH 依赖范围与当前运行时版本不符,导致插件被 Loader 拒绝组合,或安装时被管理器拒绝。
|
|
21
|
-
|
|
22
|
-
本插件对这两类失败自动处置,无需人工介入。
|
|
23
|
-
|
|
24
|
-
### 1.3 文档概述
|
|
25
|
-
|
|
26
|
-
本文档规定功能需求、外部接口需求、性能需求、设计约束、软件质量特性与验收准则。安全与隐私需求见第 5 章。
|
|
27
|
-
|
|
28
|
-
---
|
|
29
|
-
|
|
30
|
-
## 2 引用文件
|
|
31
|
-
|
|
32
|
-
| 编号 | 名称 | 说明 |
|
|
33
|
-
| --- | --- | --- |
|
|
34
|
-
| GB/T 8567-2006 | 计算机软件文档编制规范 | 本文档及配套文档的编制依据 |
|
|
35
|
-
| GB/T 45717-2025 | 信息技术 软件测量 软件质量测量 自动化的源代码质量测度 | 源代码质量度量依据 |
|
|
36
|
-
| GB/T 8566-2007 | 信息技术 软件生存周期过程 | 验证与确认过程依据 |
|
|
37
|
-
| GB/T 25000.51-2016 | 系统与软件质量要求和评价(SQuaRE) | 质量特性分类参考 |
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## 3 需求
|
|
42
|
-
|
|
43
|
-
### 3.1 所需状态和方式
|
|
44
|
-
|
|
45
|
-
插件在 DSH 宿主进程内以单例方式运行,生命周期由 Cordis 上下文管理。插件被禁用或卸载时,所有定时器、进行中的重试、监听器必须一并释放,不得残留。
|
|
46
|
-
|
|
47
|
-
### 3.2 功能需求
|
|
48
|
-
|
|
49
|
-
#### FR-1 模型请求自动修复
|
|
50
|
-
|
|
51
|
-
当 `agent/request-error` 事件触发时:
|
|
52
|
-
|
|
53
|
-
- FR-1.1 必须在**所有下游监听器放弃重试之后**才接管,避免与 provider 自有重试策略形成双重重试链。
|
|
54
|
-
- FR-1.2 必须区分"客户端原因"与"服务端原因",仅修复前者。
|
|
55
|
-
- FR-1.3 客户端原因必须覆盖下列形态,并归入对应的处置策略:
|
|
56
|
-
|
|
57
|
-
| 形态 | 归类 | 处置 |
|
|
58
|
-
| --- | --- | --- |
|
|
59
|
-
| 传输中断、流式断连、socket 重置 | `transport` | 退避重试 |
|
|
60
|
-
| SSE 长连接超时、事件流被截断 | `stream` | 退避重试 |
|
|
61
|
-
| 超时 | `timeout` | 退避重试 |
|
|
62
|
-
| 空响应 | `empty` | 退避重试 |
|
|
63
|
-
| 上下文累加超限、裁剪策略失效 | `overflow` | 先压缩再重试 |
|
|
64
|
-
| 压缩后仍超限、压缩自身打转 | `compaction-loop` | 先修历史,再重试 |
|
|
65
|
-
| 会话历史损坏、规则文件解析失败、配置 JSON 解析失败 | `history` | 先修历史,再重试 |
|
|
66
|
-
| Wire API 版本不匹配 | `protocol` | 有限重试 |
|
|
67
|
-
| 角色字段不被接受、客户端追加独有参数、tool_call 格式不符 | `protocol` | 有限重试 |
|
|
68
|
-
| 模型 ID 写死、模型能力判断错、模型列表缓存过期 | `model` | 先刷新模型目录,再重试 |
|
|
69
|
-
| Header 构造错误、OAuth 会话过期、本地缓存 token 失效、不自动刷新 | `auth` | 先刷新凭据,再重试 |
|
|
70
|
-
| 多 Key 轮换挑到失效项、Provider 侧缓存了旧凭据 | `auth` | 先刷新凭据,再重试 |
|
|
71
|
-
| MCP 子服务独立鉴权失败 | `auth` | 先刷新凭据,再重试 |
|
|
72
|
-
| MCP 会话关闭、握手失败、子服务未起 | `mcp` | 退避重试 |
|
|
73
|
-
| `PI_AI_ERROR` | `provider-error` | 退避重试 |
|
|
74
|
-
| 端口占用、句柄耗尽、内存不足、子进程崩溃、本地中转服务崩溃 | `local-env` | 退避重试 |
|
|
75
|
-
| 权限不足、路径不存在、磁盘写满、沙箱拦截 | `local-blocked` | **不重试** |
|
|
76
|
-
| TLS 证书校验失败、自签名、证书过期 | `tls` | **不重试** |
|
|
77
|
-
| 配额耗尽、欠费、限流、内容审核、图片超限 | `quota` / `policy` / `image` | **不处置** |
|
|
78
|
-
|
|
79
|
-
- FR-1.4 服务端原因不得处置,包括:配额耗尽、账号欠费、限流、上游 5xx、内容审核拦截、图片超限。
|
|
80
|
-
- FR-1.5 重试必须采用有界指数退避,带抖动,且受单步预算约束。
|
|
81
|
-
- FR-1.6 上下文超限时,必须先尝试压缩与工具结果裁剪,再重试。
|
|
82
|
-
- FR-1.7 适配器缺失时,必须先触发版本修复,修复无效则不重试。
|
|
83
|
-
- FR-1.7a 凭据失效**不得**走版本修复:重装插件不会让过期 token 变新。应先请求本机凭据层刷新,再重试。
|
|
84
|
-
- FR-1.8 重试过程必须以既有的 `llm/retry` / `llm/retry-started` 会话事件记录,**不得新增会话事件类型**。
|
|
85
|
-
- FR-1.9 同一处失败(按轮次、步骤、provider、类别定位)同时只允许一个重试在飞,避免并发重试叠加。
|
|
86
|
-
- FR-1.10 压缩或修历史连续两轮未见进展时,必须停止重试并留下记录,不得无限循环。
|
|
87
|
-
- FR-1.11 鉴权类失败的重试额度必须独立于普通重试额度,取值更小。
|
|
88
|
-
- FR-1.12 不可重试类(证书、权限、路径、磁盘)必须直接放弃,不得进入退避。
|
|
89
|
-
|
|
90
|
-
#### FR-2 工具调用自动修复
|
|
91
|
-
|
|
92
|
-
当 `tools/execute` 事件触发且结果为瞬时失败时:
|
|
93
|
-
|
|
94
|
-
- FR-2.1 仅允许重放**只读类**工具,白名单可配置。
|
|
95
|
-
- FR-2.2 有副作用的工具(命令执行、写文件、发送消息等)一律不得重放。
|
|
96
|
-
- FR-2.3 重放次数受 `maxToolRetries` 约束。
|
|
97
|
-
- FR-2.4 同名同参的调用连续出现次数受 `maxRepeatedToolCalls` 约束;超过后**不得再重放**,避免把宿主侧失效的循环检测放大成重放风暴。
|
|
98
|
-
|
|
99
|
-
#### FR-3 浏览器载体自动修复
|
|
100
|
-
|
|
101
|
-
当 `connection/request` 事件在响应头发出前失败,且属于瞬时传输错误时:
|
|
102
|
-
|
|
103
|
-
- FR-3.1 必须替换为 `503` 并附 `retry-after`,使前端自身恢复逻辑可以重发。
|
|
104
|
-
- FR-3.2 响应头已发出或响应体已结束时,必须原样抛出原错误,不得写出残缺响应。
|
|
105
|
-
|
|
106
|
-
#### FR-4 版本不匹配自动修复
|
|
107
|
-
|
|
108
|
-
- FR-4.1 必须发现**全部**不兼容插件,而非只处理首个。
|
|
109
|
-
- FR-4.2 发现来源必须覆盖:Loader 拒绝的包、已启用但未被拒绝的包、已禁用但已安装的包、安装时被延迟处理的包。
|
|
110
|
-
- FR-4.3 修复前必须审计:该插件所导入的每个 `@deepseek-ai/*` 符号在现行运行时中是否仍然存在。
|
|
111
|
-
- FR-4.4 审计通过者才可修复;审计未通过者只报告,不修改。
|
|
112
|
-
- FR-4.5 修复方式为**兼容环境**:在插件目录内建立嵌套 `node_modules`,其中每个被重定位的包是一个声明旧版本号的垫片,转发到现行实现。
|
|
113
|
-
- FR-4.6 修复必须同时对齐插件自身 manifest 中的 DSH 依赖声明,因为 Loader 在组合前读取该文件。
|
|
114
|
-
- FR-4.7 **不得绕过版本检查**:修复后的声明必须真实满足当前运行时。
|
|
115
|
-
- FR-4.8 现行运行时不存在的符号**不得伪造**。必须为插件安装它期望的那份真实旧版本;旧版本也没有时如实报缺。
|
|
116
|
-
- FR-4.13 兼容环境装入真实旧版本后重载失败、且失败原因是缺少导出时,必须判定为「需要重启」,向用户给出明确提示,**不得**记为环境失败。
|
|
117
|
-
- FR-4.12 **不得使用占位实现**。缺符号只有两种出路:装真实旧版本,或如实拒绝。
|
|
118
|
-
- FR-4.9 环境建成后必须**逐条核对**插件所需符号是否真能提供;核对不通过不得计为已修复。
|
|
119
|
-
- FR-4.10 核对无法进行时(目标文件不可读)不得判为失败,应单独记录为未核对。
|
|
120
|
-
- FR-4.11 扫描须识别「垫片存在但内容不对」的情况并重新适配。
|
|
121
|
-
|
|
122
|
-
#### FR-5 不兼容插件安装支持
|
|
123
|
-
|
|
124
|
-
- FR-5.1 必须提供在安装前预置兼容环境的手段,使管理器的版本检查按其自身规则通过。
|
|
125
|
-
- FR-5.2 安装失败或被取消时,必须完整回滚到候选目录原状。
|
|
126
|
-
- FR-5.3 仅支持本地目录;注册表包字节尚未落盘,必须明确报告为不支持,不得静默失败。
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
- FR-6.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
- PR-
|
|
139
|
-
- PR-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
- DR-
|
|
146
|
-
- DR-
|
|
147
|
-
- DR-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
- AR-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
|
162
|
-
|
|
|
163
|
-
| FR-
|
|
164
|
-
| FR-
|
|
165
|
-
| FR-
|
|
166
|
-
| FR-4.
|
|
167
|
-
| FR-
|
|
168
|
-
| FR-
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
- SR-
|
|
179
|
-
- SR-
|
|
180
|
-
- SR-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
-
|
|
190
|
-
-
|
|
191
|
-
-
|
|
192
|
-
-
|
|
1
|
+
# 软件需求规格说明
|
|
2
|
+
|
|
3
|
+
**产品名称**:request-doctor(请求急救员)
|
|
4
|
+
**产品标识**:request-doctor
|
|
5
|
+
**版本**:0.13.1
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 1 引言
|
|
10
|
+
|
|
11
|
+
### 1.1 标识
|
|
12
|
+
|
|
13
|
+
本文档适用于 request-doctor 插件。该插件以 npm 包形式发布,模块名 `request-doctor`,运行于 DeepSeek Harness(以下简称 DSH)宿主进程内,属于 Host 端插件,无客户端一半。
|
|
14
|
+
|
|
15
|
+
### 1.2 系统概述
|
|
16
|
+
|
|
17
|
+
DSH 是一个以 Cordis 插件框架组织的智能体运行时。插件通过组合包(bundle)安装进某个 profile,由 Loader 在启动时组合。系统存在两类对用户可见的失败:
|
|
18
|
+
|
|
19
|
+
1. **请求失败**:模型请求、工具调用、浏览器载体请求在传输层、承载层或超时层失败,本身可重试,但运行时默认不重试或重试策略不足以覆盖。
|
|
20
|
+
2. **版本不匹配**:已安装插件声明的 DSH 依赖范围与当前运行时版本不符,导致插件被 Loader 拒绝组合,或安装时被管理器拒绝。
|
|
21
|
+
|
|
22
|
+
本插件对这两类失败自动处置,无需人工介入。
|
|
23
|
+
|
|
24
|
+
### 1.3 文档概述
|
|
25
|
+
|
|
26
|
+
本文档规定功能需求、外部接口需求、性能需求、设计约束、软件质量特性与验收准则。安全与隐私需求见第 5 章。
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2 引用文件
|
|
31
|
+
|
|
32
|
+
| 编号 | 名称 | 说明 |
|
|
33
|
+
| --- | --- | --- |
|
|
34
|
+
| GB/T 8567-2006 | 计算机软件文档编制规范 | 本文档及配套文档的编制依据 |
|
|
35
|
+
| GB/T 45717-2025 | 信息技术 软件测量 软件质量测量 自动化的源代码质量测度 | 源代码质量度量依据 |
|
|
36
|
+
| GB/T 8566-2007 | 信息技术 软件生存周期过程 | 验证与确认过程依据 |
|
|
37
|
+
| GB/T 25000.51-2016 | 系统与软件质量要求和评价(SQuaRE) | 质量特性分类参考 |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 3 需求
|
|
42
|
+
|
|
43
|
+
### 3.1 所需状态和方式
|
|
44
|
+
|
|
45
|
+
插件在 DSH 宿主进程内以单例方式运行,生命周期由 Cordis 上下文管理。插件被禁用或卸载时,所有定时器、进行中的重试、监听器必须一并释放,不得残留。
|
|
46
|
+
|
|
47
|
+
### 3.2 功能需求
|
|
48
|
+
|
|
49
|
+
#### FR-1 模型请求自动修复
|
|
50
|
+
|
|
51
|
+
当 `agent/request-error` 事件触发时:
|
|
52
|
+
|
|
53
|
+
- FR-1.1 必须在**所有下游监听器放弃重试之后**才接管,避免与 provider 自有重试策略形成双重重试链。
|
|
54
|
+
- FR-1.2 必须区分"客户端原因"与"服务端原因",仅修复前者。
|
|
55
|
+
- FR-1.3 客户端原因必须覆盖下列形态,并归入对应的处置策略:
|
|
56
|
+
|
|
57
|
+
| 形态 | 归类 | 处置 |
|
|
58
|
+
| --- | --- | --- |
|
|
59
|
+
| 传输中断、流式断连、socket 重置 | `transport` | 退避重试 |
|
|
60
|
+
| SSE 长连接超时、事件流被截断 | `stream` | 退避重试 |
|
|
61
|
+
| 超时 | `timeout` | 退避重试 |
|
|
62
|
+
| 空响应 | `empty` | 退避重试 |
|
|
63
|
+
| 上下文累加超限、裁剪策略失效 | `overflow` | 先压缩再重试 |
|
|
64
|
+
| 压缩后仍超限、压缩自身打转 | `compaction-loop` | 先修历史,再重试 |
|
|
65
|
+
| 会话历史损坏、规则文件解析失败、配置 JSON 解析失败 | `history` | 先修历史,再重试 |
|
|
66
|
+
| Wire API 版本不匹配 | `protocol` | 有限重试 |
|
|
67
|
+
| 角色字段不被接受、客户端追加独有参数、tool_call 格式不符 | `protocol` | 有限重试 |
|
|
68
|
+
| 模型 ID 写死、模型能力判断错、模型列表缓存过期 | `model` | 先刷新模型目录,再重试 |
|
|
69
|
+
| Header 构造错误、OAuth 会话过期、本地缓存 token 失效、不自动刷新 | `auth` | 先刷新凭据,再重试 |
|
|
70
|
+
| 多 Key 轮换挑到失效项、Provider 侧缓存了旧凭据 | `auth` | 先刷新凭据,再重试 |
|
|
71
|
+
| MCP 子服务独立鉴权失败 | `auth` | 先刷新凭据,再重试 |
|
|
72
|
+
| MCP 会话关闭、握手失败、子服务未起 | `mcp` | 退避重试 |
|
|
73
|
+
| `PI_AI_ERROR` | `provider-error` | 退避重试 |
|
|
74
|
+
| 端口占用、句柄耗尽、内存不足、子进程崩溃、本地中转服务崩溃 | `local-env` | 退避重试 |
|
|
75
|
+
| 权限不足、路径不存在、磁盘写满、沙箱拦截 | `local-blocked` | **不重试** |
|
|
76
|
+
| TLS 证书校验失败、自签名、证书过期 | `tls` | **不重试** |
|
|
77
|
+
| 配额耗尽、欠费、限流、内容审核、图片超限 | `quota` / `policy` / `image` | **不处置** |
|
|
78
|
+
|
|
79
|
+
- FR-1.4 服务端原因不得处置,包括:配额耗尽、账号欠费、限流、上游 5xx、内容审核拦截、图片超限。
|
|
80
|
+
- FR-1.5 重试必须采用有界指数退避,带抖动,且受单步预算约束。
|
|
81
|
+
- FR-1.6 上下文超限时,必须先尝试压缩与工具结果裁剪,再重试。
|
|
82
|
+
- FR-1.7 适配器缺失时,必须先触发版本修复,修复无效则不重试。
|
|
83
|
+
- FR-1.7a 凭据失效**不得**走版本修复:重装插件不会让过期 token 变新。应先请求本机凭据层刷新,再重试。
|
|
84
|
+
- FR-1.8 重试过程必须以既有的 `llm/retry` / `llm/retry-started` 会话事件记录,**不得新增会话事件类型**。
|
|
85
|
+
- FR-1.9 同一处失败(按轮次、步骤、provider、类别定位)同时只允许一个重试在飞,避免并发重试叠加。
|
|
86
|
+
- FR-1.10 压缩或修历史连续两轮未见进展时,必须停止重试并留下记录,不得无限循环。
|
|
87
|
+
- FR-1.11 鉴权类失败的重试额度必须独立于普通重试额度,取值更小。
|
|
88
|
+
- FR-1.12 不可重试类(证书、权限、路径、磁盘)必须直接放弃,不得进入退避。
|
|
89
|
+
|
|
90
|
+
#### FR-2 工具调用自动修复
|
|
91
|
+
|
|
92
|
+
当 `tools/execute` 事件触发且结果为瞬时失败时:
|
|
93
|
+
|
|
94
|
+
- FR-2.1 仅允许重放**只读类**工具,白名单可配置。
|
|
95
|
+
- FR-2.2 有副作用的工具(命令执行、写文件、发送消息等)一律不得重放。
|
|
96
|
+
- FR-2.3 重放次数受 `maxToolRetries` 约束。
|
|
97
|
+
- FR-2.4 同名同参的调用连续出现次数受 `maxRepeatedToolCalls` 约束;超过后**不得再重放**,避免把宿主侧失效的循环检测放大成重放风暴。
|
|
98
|
+
|
|
99
|
+
#### FR-3 浏览器载体自动修复
|
|
100
|
+
|
|
101
|
+
当 `connection/request` 事件在响应头发出前失败,且属于瞬时传输错误时:
|
|
102
|
+
|
|
103
|
+
- FR-3.1 必须替换为 `503` 并附 `retry-after`,使前端自身恢复逻辑可以重发。
|
|
104
|
+
- FR-3.2 响应头已发出或响应体已结束时,必须原样抛出原错误,不得写出残缺响应。
|
|
105
|
+
|
|
106
|
+
#### FR-4 版本不匹配自动修复
|
|
107
|
+
|
|
108
|
+
- FR-4.1 必须发现**全部**不兼容插件,而非只处理首个。
|
|
109
|
+
- FR-4.2 发现来源必须覆盖:Loader 拒绝的包、已启用但未被拒绝的包、已禁用但已安装的包、安装时被延迟处理的包。
|
|
110
|
+
- FR-4.3 修复前必须审计:该插件所导入的每个 `@deepseek-ai/*` 符号在现行运行时中是否仍然存在。
|
|
111
|
+
- FR-4.4 审计通过者才可修复;审计未通过者只报告,不修改。
|
|
112
|
+
- FR-4.5 修复方式为**兼容环境**:在插件目录内建立嵌套 `node_modules`,其中每个被重定位的包是一个声明旧版本号的垫片,转发到现行实现。
|
|
113
|
+
- FR-4.6 修复必须同时对齐插件自身 manifest 中的 DSH 依赖声明,因为 Loader 在组合前读取该文件。
|
|
114
|
+
- FR-4.7 **不得绕过版本检查**:修复后的声明必须真实满足当前运行时。
|
|
115
|
+
- FR-4.8 现行运行时不存在的符号**不得伪造**。必须为插件安装它期望的那份真实旧版本;旧版本也没有时如实报缺。
|
|
116
|
+
- FR-4.13 兼容环境装入真实旧版本后重载失败、且失败原因是缺少导出时,必须判定为「需要重启」,向用户给出明确提示,**不得**记为环境失败。
|
|
117
|
+
- FR-4.12 **不得使用占位实现**。缺符号只有两种出路:装真实旧版本,或如实拒绝。
|
|
118
|
+
- FR-4.9 环境建成后必须**逐条核对**插件所需符号是否真能提供;核对不通过不得计为已修复。
|
|
119
|
+
- FR-4.10 核对无法进行时(目标文件不可读)不得判为失败,应单独记录为未核对。
|
|
120
|
+
- FR-4.11 扫描须识别「垫片存在但内容不对」的情况并重新适配。
|
|
121
|
+
|
|
122
|
+
#### FR-5 不兼容插件安装支持
|
|
123
|
+
|
|
124
|
+
- FR-5.1 必须提供在安装前预置兼容环境的手段,使管理器的版本检查按其自身规则通过。
|
|
125
|
+
- FR-5.2 安装失败或被取消时,必须完整回滚到候选目录原状。
|
|
126
|
+
- FR-5.3 仅支持本地目录;注册表包字节尚未落盘,必须明确报告为不支持,不得静默失败。
|
|
127
|
+
- FR-5.4 界面每行必须能直接读到该插件的审计判定、理由、缺符号与环境,不必切到命令行;没有审计结论的行不得渲染空的判定信息。
|
|
128
|
+
- FR-5.5 界面必须说明每条记录来自插件管理器拒装还是本插件审计。
|
|
129
|
+
|
|
130
|
+
#### FR-6 状态与诊断
|
|
131
|
+
|
|
132
|
+
- FR-6.1 必须提供 `/doctor` 命令,输出各类修复计数与最近事件。
|
|
133
|
+
- FR-6.2 `/doctor install <路径>` 执行安装前预置。
|
|
134
|
+
- FR-6.3 诊断快照可选写入文件,写入不得阻塞事件循环。
|
|
135
|
+
|
|
136
|
+
### 3.3 性能需求
|
|
137
|
+
|
|
138
|
+
- PR-1 稳态下后台扫描的 CPU 与 I/O 开销必须可忽略,采用退避策略。
|
|
139
|
+
- PR-2 已适配插件必须从后续扫描的审计集合中自然退出,不得反复审计。
|
|
140
|
+
- PR-3 任何长期存活的数据结构必须有界。
|
|
141
|
+
- PR-4 定时器必须不阻止进程退出。
|
|
142
|
+
|
|
143
|
+
### 3.4 设计约束
|
|
144
|
+
|
|
145
|
+
- DR-1 不得依赖 `node_modules` 之外的第三方包;插件须在无额外依赖的 profile 中运行。
|
|
146
|
+
- DR-2 不得调用 `setBundleEnabled` / `removeBundle` 操作本插件扫描范围之外的插件。
|
|
147
|
+
- DR-3 路由冲突只报告,不处置——归属用户决定。
|
|
148
|
+
- DR-4 不得新增会话事件类型,避免未知事件被读取方拒绝。
|
|
149
|
+
- DR-5 源码质量须满足 GB/T 45717-2025 度量限值,见《软件质量度量报告》。
|
|
150
|
+
|
|
151
|
+
### 3.5 属性
|
|
152
|
+
|
|
153
|
+
- AR-1 平台:Windows / macOS / Linux,Node.js ≥ 20。
|
|
154
|
+
- AR-2 运行时:DSH 0.2.x。
|
|
155
|
+
- AR-3 并发:同一 profile 内单实例;修复过程以互斥标志防止重入。
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 4 合格性规定
|
|
160
|
+
|
|
161
|
+
| 需求 | 验证方法 | 证据 |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| FR-1.1 ~ FR-1.8 | 自动化测试 | `test/plugin.test.mjs`、`test/classify.test.mjs` |
|
|
164
|
+
| FR-2.1 ~ FR-2.3 | 自动化测试 | `test/plugin.test.mjs` |
|
|
165
|
+
| FR-3.1 ~ FR-3.2 | 自动化测试 | `test/plugin.test.mjs` |
|
|
166
|
+
| FR-4.1 ~ FR-4.8 | 自动化测试 + 实机验证 | `test/compat*.test.mjs`、`RELEASE_CHECKLIST.md` |
|
|
167
|
+
| FR-4.9 ~ FR-4.11 | 自动化测试 | `test/env-verify.test.mjs`、`test/verify-roundtrip.test.mjs` |
|
|
168
|
+
| FR-4.12 不用占位 | 自动化测试 | `test/compat-env.test.mjs`(装真实旧版本 / 旧版本也没有则拒绝)、`test/dry-run.test.mjs` |
|
|
169
|
+
| FR-5.1 ~ FR-5.3 | 自动化测试 + 实机验证 | `test/install-support.test.mjs` |
|
|
170
|
+
| FR-6.1 ~ FR-6.3 | 代码审查 + 实机验证 | `index.js`、`lib/diagnostics.js` |
|
|
171
|
+
| PR-1 ~ PR-4 | 代码审查 + 度量 | `lib/stat-cache.js`、`lib/timers.js`、《软件质量度量报告》 |
|
|
172
|
+
| DR-1 ~ DR-5 | 代码审查 | 依赖清单、事件清单 |
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## 5 安全与隐私
|
|
177
|
+
|
|
178
|
+
- SR-1 插件不读取、不传输任何凭据内容;仅判断凭据相关错误码。
|
|
179
|
+
- SR-2 插件不外发任何数据;全部处理在本地进程内完成。
|
|
180
|
+
- SR-3 修改插件目录前必须备份原 manifest,备份文件名为 `package.json.request-doctor-original`。
|
|
181
|
+
- SR-4 删除兼容环境时只删除本插件创建的包,不得删除插件自带的依赖。
|
|
182
|
+
- SR-5 诊断快照默认不写文件;启用后内容限于计数、事件类别与配置项,不含请求体。
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 6 注释
|
|
187
|
+
|
|
188
|
+
- 6.1 术语
|
|
189
|
+
- **兼容环境**:插件目录内嵌套的 `node_modules`,含声明旧版本号的垫片包。
|
|
190
|
+
- **垫片(shim)**:只做 `export *` 转发的最小包。
|
|
191
|
+
- **审计**:判定插件所需符号在现行运行时是否存在的过程。
|
|
192
|
+
- **退避**:重试间隔按指数增长并加抖动。
|
|
193
|
+
- 6.2 待定事项
|
|
194
|
+
- 安装前预置目前需显式调用 `/doctor install`;拦截 `install_bundle` 工具调用可进一步自动化,但会改变既有工具行为,需单独确认。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 软件测试报告
|
|
2
2
|
|
|
3
3
|
**产品名称**:request-doctor(请求急救员)
|
|
4
|
-
**版本**:0.13.
|
|
4
|
+
**版本**:0.13.1
|
|
5
5
|
**编制依据**:GB/T 8567-2006《计算机软件文档编制规范》6.6;GB/T 8566-2007 验证与确认过程
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -59,11 +59,12 @@
|
|
|
59
59
|
| `test/pinned-peers.test.mjs` | 1 | peer 钉死成上一补丁版本:六条全对齐、只给导入的建垫片 |
|
|
60
60
|
| `test/install-rescue.test.mjs` | 4 | 从失败里取出包名与版本,供授权使用 |
|
|
61
61
|
| `test/install-in-place.test.mjs` | 2 | 已装着的包就地修复,不再重复安装 |
|
|
62
|
-
| `test/client-half.test.mjs` |
|
|
63
|
-
| `test/diagnostics.test.mjs` | 3 | 快照可选写入:不配置不写盘、突发合并、坏路径不致命 |
|
|
64
|
-
| `test/restart-notice.test.mjs` | 3 |
|
|
62
|
+
| `test/client-half.test.mjs` | 9 | 客户端半:模块身份、注入声明、apply 注册、组件能渲染 |
|
|
63
|
+
| `test/diagnostics.test.mjs` | 3 | 快照可选写入:不配置不写盘、突发合并、坏路径不致命 |
|
|
64
|
+
| `test/restart-notice.test.mjs` | 3 |
|
|
65
|
+
| `test/panel-briefs.test.mjs` | 3 | 界面摘要:按名索引、缺数据不抛、不污染诊断 |
|
|
65
66
|
| `test/verdict-report.test.mjs` | 29 | why 与 list:判定、理由、环境摘要、自动扫描、分组、缺口、按形态给建议 | 缺导出判为需要重启;其它失败不误判;多次累积不覆盖 |
|
|
66
|
-
| **合计** | **
|
|
67
|
+
| **合计** | **196** | **全部通过** |
|
|
67
68
|
|
|
68
69
|
执行结果:
|
|
69
70
|
|
|
@@ -108,13 +109,15 @@
|
|
|
108
109
|
| FR-4.9 建完必须核对 | `verify-roundtrip.test.mjs` — 建完必能验过 | 通过 |
|
|
109
110
|
| FR-4.10 不可核对不算失败 | `env-verify.test.mjs` — 目标不可读记为未核对 | 通过 |
|
|
110
111
|
| FR-4.11 坏垫片能被发现 | `verify-roundtrip.test.mjs` — 手工破坏的垫片验不过 | 通过 |
|
|
111
|
-
| FR-7.5 共同缺口要点出 | `verdict-report.test.mjs` — 同一符号缺两次即汇总,按涉及数排序 | 通过 |
|
|
112
|
-
| FR-7.4 why 能解释判定 | `verdict-report.test.mjs` — 五种情形各有用例 | 通过 |
|
|
113
|
-
| FR-4.13 需要重启要说明 | `restart-notice.test.mjs` — 缺导出归入重启提示 | 通过 |
|
|
112
|
+
| FR-7.5 共同缺口要点出 | `verdict-report.test.mjs` — 同一符号缺两次即汇总,按涉及数排序 | 通过 |
|
|
113
|
+
| FR-7.4 why 能解释判定 | `verdict-report.test.mjs` — 五种情形各有用例 | 通过 |
|
|
114
|
+
| FR-4.13 需要重启要说明 | `restart-notice.test.mjs` — 缺导出归入重启提示 | 通过 |
|
|
114
115
|
| FR-4.12 不用占位 | `compat-env.test.mjs` — 装真实旧版本;旧版本也没有则拒绝 | 通过 |
|
|
115
116
|
| FR-5.1 安装前预置 | `install-support.test.mjs` — 预置后检查通过 | 通过 |
|
|
116
117
|
| FR-5.2 失败回滚 | `install-support.test.mjs` — 目录恢复原状 | 通过 |
|
|
117
118
|
| FR-5.3 远程包明示不支持 | `install-support.test.mjs` — 返回 reason | 通过 |
|
|
119
|
+
| FR-5.4 界面能看判定 | `panel-briefs.test.mjs`、`client-half.test.mjs` — 有结论才渲染判定行 | 通过 |
|
|
120
|
+
| FR-5.5 界面标来源 | `client-half.test.mjs` — 管理器/审计/未知三种来源各有标签 | 通过 |
|
|
118
121
|
| FR-4.4 审计不过只报告 | `compat.test.mjs` — 缺符号的插件不建环境 | 通过 |
|
|
119
122
|
| FR-6.2 安装前预置 | `install-support.test.mjs` — 预置后检查通过 | 通过 |
|
|
120
123
|
| FR-6.3 快照不阻塞 | `diagnostics.test.mjs` — 未配置不写盘、突发合并写、坏路径不致命 | 通过 |
|
|
@@ -214,4 +217,4 @@
|
|
|
214
217
|
|
|
215
218
|
### 7.2 分文件执行结果
|
|
216
219
|
|
|
217
|
-
|
|
220
|
+
32 个测试文件逐个单独执行,每个都有用例且全部通过,不存在因导入失败而静默跳过的文件。用例数分布:compat.test.mjs 17、plugin.test.mjs 15、compat-env.test.mjs 14、classify.test.mjs 13,其余按职能分散,合计 196。
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 软件用户手册
|
|
2
2
|
|
|
3
3
|
**产品名称**:request-doctor(请求急救员)
|
|
4
|
-
**版本**:0.13.
|
|
4
|
+
**版本**:0.13.1
|
|
5
5
|
**编制依据**:GB/T 8567-2006《计算机软件文档编制规范》6.5
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -204,6 +204,15 @@ Plugin xxx@1.0.0 is incompatible with dsh 0.2.1-rc.3
|
|
|
204
204
|
|
|
205
205
|
同一个插件两处都出现时只显示管理器那条,因为它是「现在真的装不上」。
|
|
206
206
|
|
|
207
|
+
本插件审计过的插件,行里还会直接列出判定:
|
|
208
|
+
|
|
209
|
+
```n判定: incompatible
|
|
210
|
+
needs older api
|
|
211
|
+
缺符号: offloadRequestImagesWithPolicy
|
|
212
|
+
环境: 真实旧版本 1 个(需重启):@deepseek-ai/dsh-llm
|
|
213
|
+
```n
|
|
214
|
+
这样不必切到命令行敲 `/doctor why` 也能看清原因。
|
|
215
|
+
|
|
207
216
|
## 9 查某个插件为什么装不上
|
|
208
217
|
|
|
209
218
|
```
|
package/docs/quality-report.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"standard": "GB/T 45717-2025 信息技术 软件测量 软件质量测量 自动化的源代码质量测度",
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-29T16:07:40.290Z",
|
|
4
4
|
"scope": {
|
|
5
|
-
"files":
|
|
6
|
-
"totalLines":
|
|
7
|
-
"sourceLines":
|
|
8
|
-
"commentLines":
|
|
5
|
+
"files": 32,
|
|
6
|
+
"totalLines": 5245,
|
|
7
|
+
"sourceLines": 3803,
|
|
8
|
+
"commentLines": 960
|
|
9
9
|
},
|
|
10
10
|
"measures": {
|
|
11
|
-
"commentDensity": 25.
|
|
12
|
-
"duplicationPercent": 4.
|
|
13
|
-
"functionCount":
|
|
11
|
+
"commentDensity": 25.24,
|
|
12
|
+
"duplicationPercent": 4.89,
|
|
13
|
+
"functionCount": 400,
|
|
14
14
|
"maxComplexity": 10,
|
|
15
15
|
"maxFunctionLines": 59,
|
|
16
16
|
"maxFileLines": 500,
|
|
17
|
-
"averageComplexity": 3.
|
|
17
|
+
"averageComplexity": 3.87
|
|
18
18
|
},
|
|
19
19
|
"limits": {
|
|
20
20
|
"maxComplexity": 10,
|
package/index.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
//
|
|
19
19
|
// 这个文件是装配处:只管生命周期和监听器,具体判断都在 lib/ 里。
|
|
20
20
|
import { explainVerdict, listAudited } from './lib/verdict-report.js';
|
|
21
|
+
import { panelBriefs } from './lib/panel-briefs.js';
|
|
21
22
|
import { parseRetryConfig } from './lib/classify.js';
|
|
22
23
|
import { collectPackageFacts, entryPathOf } from './lib/compat-repair.js';
|
|
23
24
|
import { unmetPeers } from './lib/compat.js';
|
|
@@ -262,10 +263,12 @@ function dedupeRows(rows) {
|
|
|
262
263
|
|
|
263
264
|
// 界面要的一份数据:待处理的行,加上「修好了但需要重启」的提示。
|
|
264
265
|
async function panelPayload(deps, manager) {
|
|
266
|
+
const box = deps.diagnostics?.diagnostics ?? {};
|
|
265
267
|
return {
|
|
266
268
|
ok: true,
|
|
267
269
|
rows: await collectPanelRows(deps, manager),
|
|
268
|
-
restart:
|
|
270
|
+
restart: box.restartRequired ?? [],
|
|
271
|
+
briefs: panelBriefs(box),
|
|
269
272
|
};
|
|
270
273
|
}
|
|
271
274
|
// 界面用的 HTTP 路由。
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// 给界面用的审计摘要。
|
|
2
|
+
//
|
|
3
|
+
// 命令行里 /doctor why 能看到判定理由,界面那一栏看不到,用户得切去敲命令。
|
|
4
|
+
// 这里把同一份结论按插件名整理好,随清单一起送过去。
|
|
5
|
+
|
|
6
|
+
// @param {object} diagnostics - 诊断对象。
|
|
7
|
+
// @returns {Record<string, {verdict: string, reasons: string[], missing: string[], environment?: string}>}
|
|
8
|
+
export function panelBriefs(diagnostics) {
|
|
9
|
+
const audited = diagnostics?.versionScan?.audited ?? [];
|
|
10
|
+
const briefs = {};
|
|
11
|
+
for (const item of audited) {
|
|
12
|
+
briefs[item.name] = {
|
|
13
|
+
verdict: item.verdict,
|
|
14
|
+
reasons: [...(item.reasons ?? [])],
|
|
15
|
+
missing: [...(item.missing ?? [])],
|
|
16
|
+
environment: item.environment,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return briefs;
|
|
20
|
+
}
|