dsh-llm-verifier 0.1.3 → 0.1.5

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 (44) hide show
  1. package/README.md +68 -6
  2. package/lib/{caller-CGlgZ-Su.js → caller-BiGOwSlS.js} +9 -3
  3. package/lib/caller.js +2 -2
  4. package/lib/client.js +180 -0
  5. package/lib/core.js +2 -1
  6. package/lib/index.js +1137 -78
  7. package/lib/types/auto.d.ts +26 -0
  8. package/lib/types/auto.js +88 -0
  9. package/lib/types/cache.js +14 -2
  10. package/lib/types/caller.d.ts +2 -0
  11. package/lib/types/caller.js +7 -0
  12. package/lib/types/client.js +4 -4
  13. package/lib/types/config.d.ts +35 -0
  14. package/lib/types/config.js +52 -3
  15. package/lib/types/core.js +3 -1
  16. package/lib/types/index.d.ts +3 -0
  17. package/lib/types/index.js +201 -19
  18. package/lib/types/router.d.ts +115 -0
  19. package/lib/types/router.js +286 -0
  20. package/lib/types/session.d.ts +3 -0
  21. package/lib/types/session.js +22 -5
  22. package/lib/types/statistics.d.ts +3 -1
  23. package/lib/types/statistics.js +50 -1
  24. package/lib/types/topic-storage.d.ts +14 -0
  25. package/lib/types/topic-storage.js +24 -0
  26. package/package.json +2 -2
  27. package/src/auto.test.ts +77 -0
  28. package/src/auto.ts +115 -0
  29. package/src/cache.ts +14 -2
  30. package/src/caller.ts +7 -1
  31. package/src/client.tsx +25 -4
  32. package/src/config.ts +80 -3
  33. package/src/core.test.ts +4 -0
  34. package/src/core.ts +2 -1
  35. package/src/index.ts +181 -20
  36. package/src/parity.test.ts +15 -1
  37. package/src/router.test.ts +81 -0
  38. package/src/router.ts +291 -0
  39. package/src/session.test.ts +3 -1
  40. package/src/session.ts +22 -5
  41. package/src/statistics.test.ts +17 -1
  42. package/src/statistics.ts +46 -1
  43. package/src/topic-storage.test.ts +24 -0
  44. package/src/topic-storage.ts +27 -0
package/README.md CHANGED
@@ -8,12 +8,14 @@
8
8
 
9
9
  `dsh-llm-verifier` 为 DeepSeek Harness(DSH)引入了一套**独立的裁判复核机制**。在主 Agent 负责生成代码、执行命令与工具交互的同时,Verifier 会收集当前任务目标、各候选方案过程以及真实的终端执行结果,交由你在设置中指定的独立 DSH 模型进行仲裁:评估哪个方案更可靠、当前任务的实际完成进度、以及是否存在未发现的潜在错误。
10
10
 
11
- 只有在主动调用以下工具时,插件才会向裁判模型发起请求:
11
+ 插件提供四个显式工具,并支持可配置的宿主级自动会话验收:
12
12
 
13
13
  - `verifier_compare`:对两个候选执行过程进行成对比较(Pairwise Comparison);
14
- - `verifier_select`:在多个候选方案中通过锦标赛机制选出最优解;
15
- - `verifier_track`:评估任务在不同检查点(Checkpoint)的完成度与进展;
16
- - `verifier_current_session`:显式提取当前 DSH 会话记录,进行脱敏并执行复核。
14
+ - `verifier_select`:在多个候选方案中通过锦标赛机制选出最优解,供 Best-of-N / 多候选编排器直接调用;
15
+ - `verifier_track`:评估任务在已有检查点(Checkpoint)的完成度与进展,供 Goal / Workflow 等长任务编排器直接调用;
16
+ - `verifier_current_session`:显式提取当前 DSH 会话记录,进行脱敏并执行复核;
17
+ - **四工具自动路由**:智能或严格策略在 `agent/turn-stopping` 生命周期边界按阶段调度 `select → compare → track → current_session`。第一阶段只信任 Workflow 的版本化候选协议与发生真实变化的 Todo 快照;普通 Subagent 输出必须经第二阶段的证据引用分类,避免把不同子任务误当候选;
18
+ - **自动验收门控**:候选选择与进度检查完成后,宿主运行同一会话验收逻辑;未通过时以插件 steering 反馈要求 Agent 修复并重新验证,而不是依赖模型是否主动想起工具。
17
19
 
18
20
  ## 安装与启用 (Installation & Usage)
19
21
 
@@ -164,7 +166,20 @@ flowchart LR
164
166
 
165
167
  | 配置项 | 说明 |
166
168
  |---|---|
167
- | **启用工具 (Enabled)** | 是否允许 Agent 调用 verifier 工具;关闭后调用会立即返回错误,不产生任何模型请求(设置页「工具开关」按钮可切换) |
169
+ | **启用工具 (Enabled)** | 是否允许显式工具与自动验收向裁判模型发起请求;关闭后显式调用立即报错且自动门控不运行 |
170
+ | **调用策略** | `manual` 仅显式调用;`smart` 结构化优先,并只在有候选/检查点线索时做高置信语义路由,达到工具证据门槛后最终验收;`strict` 每个结束边界都尝试语义路由,并对任一已完成关键操作最终验收,路由或验收异常时 fail closed |
171
+ | **混合语义路由** | 结构化证据不足时,是否允许裁判模型保守分类 `compare/select/track/none`;不会生成新候选或编造检查点 |
172
+ | **语义路由置信度** | 语义识别达到该值才执行对应工具,默认 `0.9` |
173
+ | **最多候选数** | 自动 `select` 一次最多纳入的候选数,默认 `8` |
174
+ | **每任务/每会话最多路由** | 独立于最终验收预算,限制 `compare/select/track` 及语义分类的自动次数 |
175
+ | **进度完成阈值** | `track` 任一检查点低于该值时 steering 要求继续工作,默认 `0.8` |
176
+ | **单项/总证据字符上限** | 自动候选与检查点脱敏后的单项、整次路由输入硬上限,默认 `20000 / 60000` |
177
+ | **任务/会话模型调用预算** | 分类、compare/select/track 与最终验收共享的估算请求预算,默认 `48 / 160` |
178
+ | **通过阈值** | 自动会话验收要求证据分数达到阈值且胜过“未执行有效工作”基线 |
179
+ | **自动评估轮次** | 自动验收每个标准的重复轮次;默认 1,作为低成本初筛 |
180
+ | **智能模式最少工具调用** | `smart` 策略需要的最少非 Verifier 工具调用数 |
181
+ | **最大证据字符** | 自动发送给裁判的最近会话轨迹字符上限 |
182
+ | **每任务/每会话最多验收** | 防止低分反馈形成无限修复循环并限制成本 |
168
183
  | **供应商 (Provider)** | 从 DSH 当前已配置且可路由的 Provider 列表中选择 |
169
184
  | **模型 (Model)** | 从所选 Provider 的模型目录中指定具体裁判模型 |
170
185
  | **推理强度 (Reasoning Effort)** | 使用 Adapter 为该模型声明的思考强度,或保留模型默认值 |
@@ -178,8 +193,55 @@ flowchart LR
178
193
  > [!NOTE]
179
194
  > **多模态与图片支持**:若选定的裁判模型不支持图像输入,传入图片时将由对应 DSH Adapter 明确报错拦截,插件绝不会静默丢弃图片证据。
180
195
 
196
+ ## 四工具自动调度
197
+
198
+ 自动路由发生在 Agent **准备停止但尚未提交 `turn/end`** 的边界,四个工具不是互斥替代关系,而是覆盖不同阶段:
199
+
200
+ ```text
201
+ 3 个以上同组真实候选 → verifier_select → steering 实施胜出候选
202
+ 恰好 2 个同组真实候选 → verifier_compare → steering 实施胜出候选
203
+ 已有多个进度快照/检查点 → verifier_track → 未达阈值则 steering 继续
204
+ 候选决策和进度阶段完成 → verifier_current_session → 最终交付验收
205
+ ```
206
+
207
+ ### 第一阶段:结构化优先
208
+
209
+ 插件确定性识别以下对象,不额外调用分类模型:
210
+
211
+ - 同一个 Agent step 中完成的两个同步 `subagent` / `subagent_fork` 结果视为一个候选组;后台启动返回的 Subagent ID 不会被当作候选;
212
+ - `workflow` 只有返回以下版本化协议才会被视为可信候选;普通 JSON、裸数组及 Subagent 文本不会直接触发结构化比较:
213
+
214
+ ```json
215
+ {
216
+ "protocol": "dsh-verifier-candidates",
217
+ "version": 1,
218
+ "groupId": "auth-implementation",
219
+ "candidates": [
220
+ { "id": "jwt", "label": "JWT", "status": "completed", "content": "..." },
221
+ { "id": "session", "label": "Session", "status": "completed", "content": "..." }
222
+ ]
223
+ }
224
+ ```
225
+
226
+ - 同一任务内至少两个内容或状态发生真实变化的 `todo/write` 快照形成进度检查点;完全相同的重复快照会被折叠。
227
+
228
+ 显式调用过对应的 `verifier_compare`、`verifier_select` 或 `verifier_track` 后,自动 Router 不会再对同类结构化对象重复执行。每个输入还会计算稳定指纹,同一证据不会重复消费预算。
229
+
230
+ ### 第二阶段:混合语义识别
231
+
232
+ 当结构化对象不足时,启用“混合语义路由”可让配置的裁判模型只做严格 JSON 分类:`none / compare / select / track`。分类器不能返回自由文本证据,只能返回会话中已经存在且成功配对的 `tool/call` callId 或真实 `todo/write` seq;宿主再从不可变快照重新提取、脱敏和限长。额外 prose、Markdown fence、未知字段、重复/不存在的引用、非递增检查点都会 fail closed。
233
+
234
+ - `smart`:仅在会话出现 Subagent、Workflow、Goal、Todo 或“候选/方案/检查点”等明确线索时分类;
235
+ - `strict`:每个准备结束边界都分类,无法确认时返回 `none`;路由调用失败时注入 steering,阻止静默跳过;
236
+ - `manual`:结构化与语义自动路由、最终自动验收都关闭,四个工具仍可显式调用。
237
+
238
+ 候选选择产生 steering 后,Agent 必须实施胜出候选;进度未达阈值也会 steering 继续。任何自动 `compare/select/track` 成功都会设置 `finalVerificationRequired`,下一停止边界即使没有传统写入类工具也必须执行 `verifier_current_session`;只有针对最新快照且达到阈值的最终验收才能清除要求。路由采用 reservation/commit/fail 状态,取消、过期或失败不会被误记为成功;strict 会维持阻断并 fail closed。
239
+
240
+ 所有自动阶段共享任务/会话级模型调用预算;分类请求以 `verifier_route_classify` 单独记录到统计。候选、步骤和标签在发送前统一执行默认脱敏、单项字符上限和总字符上限,图片证据按与显式工具相同的路径传入。
241
+
181
242
  ## 缓存、重试与遥测统计
182
243
 
244
+ - **按话题持久化**:成功评分缓存与调用统计写入当前 DSH 话题的持久化目录(`~/.dsh/sessions/<workspace>/<session-id>/verifier/`)。不同话题相互隔离;永久删除话题时,该目录会随会话日志一并删除,不再在桌面或项目工作目录生成 `.dsh-verifier-cache`。
183
245
  - **持久化缓存**:成功的评分结果将基于 SHA-256 哈希值进行持久化缓存。Key 的计算维度包含:Provider、Model、任务描述、候选内容、评审标准、轮次编号、参数配置及图片摘要。失败或被取消的请求不会写入缓存。
184
246
  - **并发请求合并**:相同的并发比对请求会自动共用在途 Promise(In-flight Deduplication),避免重复调用。
185
247
  - **运行统计与监控**:每次调用返回完整的统计信息,涵盖实际请求次数、重试次数(Retries)、各类 Token 消耗(输入/缓存命中/输出/推理 Token)、缓存命中与未命中次数(Cache Hits/Misses)以及费用估算;其中:
@@ -189,7 +251,7 @@ flowchart LR
189
251
 
190
252
  ## 隐私与数据安全边界
191
253
 
192
- `verifier_current_session` 工具**仅在被显式调用时**才会读取当前 DSH 会话内容:
254
+ `verifier_current_session` 会在显式调用时读取当前 DSH 会话;启用 `smart` / `strict` 策略后,宿主也会为混合语义路由和满足证据门槛的最终验收调用同一脱敏提取路径:
193
255
 
194
256
  - **提取范围**:仅提取直接的用户消息、Assistant 回复、工具调用(Tool Call)及实际工具执行结果(Tool Result);自动排除插件和系统内置指令(Plugin/System Instructions);
195
257
  - **敏感信息脱敏**:默认对常见 Bearer Token、API Key、通用 Token、Password 及 Secret 进行脱敏替换;
@@ -1,5 +1,5 @@
1
- import { settingsNamespace } from "@deepseek-ai/dsh-settings";
2
1
  import { BlockAssembler, ReasoningEffortId, createUserMessage, deepFreeze } from "@deepseek-ai/dsh-llm";
2
+ import { settingsNamespace } from "@deepseek-ai/dsh-settings";
3
3
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
4
4
  //#region src/top-logprobs.ts
5
5
  var TopLogprobsUnsupportedError = class extends Error {
@@ -254,6 +254,7 @@ var RequestLimiter = class {
254
254
  queue = [];
255
255
  constructor(limit) {
256
256
  this.limit = limit;
257
+ if (!Number.isSafeInteger(limit) || limit < 1) throw new Error("llm-verifier: request concurrency limit must be a positive integer");
257
258
  }
258
259
  async run(operation, signal) {
259
260
  if (this.active >= this.limit) await new Promise((resolve, reject) => {
@@ -296,6 +297,11 @@ async function callVerifier(config, prompt, signal, images) {
296
297
  const invoke = () => callAutomatic(config, prompt, signal, images);
297
298
  return config.limiter === void 0 ? invoke() : config.limiter.run(invoke, signal);
298
299
  }
300
+ /** Plain-text verifier call for conservative JSON routing; probability labels are intentionally bypassed. */
301
+ async function callVerifierText(config, prompt, signal) {
302
+ const invoke = () => callExplicitTag(config, prompt, signal);
303
+ return config.limiter === void 0 ? invoke() : config.limiter.run(invoke, signal);
304
+ }
299
305
  function addUsage(target, source) {
300
306
  for (const key of [
301
307
  "calls",
@@ -319,6 +325,6 @@ function emptyUsage() {
319
325
  };
320
326
  }
321
327
  //#endregion
322
- export { TopLogprobCapabilityCache as a, emptyUsage as i, addUsage as n, callVerifier as r, RequestLimiter as t };
328
+ export { emptyUsage as a, callVerifierText as i, addUsage as n, TopLogprobCapabilityCache as o, callVerifier as r, RequestLimiter as t };
323
329
 
324
- //# sourceMappingURL=caller-CGlgZ-Su.js.map
330
+ //# sourceMappingURL=caller-BiGOwSlS.js.map
package/lib/caller.js CHANGED
@@ -1,2 +1,2 @@
1
- import { i as emptyUsage, n as addUsage, r as callVerifier, t as RequestLimiter } from "./caller-CGlgZ-Su.js";
2
- export { RequestLimiter, addUsage, callVerifier, emptyUsage };
1
+ import { a as emptyUsage, i as callVerifierText, n as addUsage, r as callVerifier, t as RequestLimiter } from "./caller-BiGOwSlS.js";
2
+ export { RequestLimiter, addUsage, callVerifier, callVerifierText, emptyUsage };
package/lib/client.js CHANGED
@@ -102,12 +102,14 @@ window.__ModuleLoader__.load({
102
102
  fontSize: 12
103
103
  };
104
104
  const toolLabels = {
105
+ verifier_route_classify: "路由分类",
105
106
  verifier_compare: "两项对比",
106
107
  verifier_select: "多项优选",
107
108
  verifier_track: "进度跟踪",
108
109
  verifier_current_session: "会话验收"
109
110
  };
110
111
  const toolColors = {
112
+ verifier_route_classify: "#d97706",
111
113
  verifier_compare: "#4f8cff",
112
114
  verifier_select: "#8b6df6",
113
115
  verifier_track: "#2fc5c9",
@@ -118,8 +120,26 @@ window.__ModuleLoader__.load({
118
120
  }
119
121
  function values(view) {
120
122
  const v = record(view.value);
123
+ const mode = v.autoVerifyMode === "manual" || v.autoVerifyMode === "strict" ? v.autoVerifyMode : "smart";
121
124
  return {
122
125
  enabled: v.enabled !== false,
126
+ autoVerifyMode: mode,
127
+ autoVerifyThreshold: Number(v.autoVerifyThreshold ?? .65),
128
+ autoVerifyRepeats: Number(v.autoVerifyRepeats ?? 1),
129
+ autoVerifyMinToolCalls: Number(v.autoVerifyMinToolCalls ?? 3),
130
+ autoVerifyMaxChars: Number(v.autoVerifyMaxChars ?? 8e4),
131
+ autoVerifyMaxPerTask: Number(v.autoVerifyMaxPerTask ?? 2),
132
+ autoVerifyMaxPerSession: Number(v.autoVerifyMaxPerSession ?? 8),
133
+ autoRouteSemantic: v.autoRouteSemantic !== false,
134
+ autoRouteMinConfidence: Number(v.autoRouteMinConfidence ?? .9),
135
+ autoRouteMaxCandidates: Number(v.autoRouteMaxCandidates ?? 8),
136
+ autoRouteMaxPerTask: Number(v.autoRouteMaxPerTask ?? 2),
137
+ autoRouteMaxPerSession: Number(v.autoRouteMaxPerSession ?? 8),
138
+ autoTrackCompletionThreshold: Number(v.autoTrackCompletionThreshold ?? .8),
139
+ autoRouteMaxItemChars: Number(v.autoRouteMaxItemChars ?? 2e4),
140
+ autoRouteMaxInputChars: Number(v.autoRouteMaxInputChars ?? 6e4),
141
+ autoMaxModelCallsPerTask: Number(v.autoMaxModelCallsPerTask ?? 48),
142
+ autoMaxModelCallsPerSession: Number(v.autoMaxModelCallsPerSession ?? 160),
123
143
  provider: String(v.provider ?? ""),
124
144
  model: String(v.model ?? ""),
125
145
  ...typeof v.reasoningEffort === "string" ? { reasoningEffort: v.reasoningEffort } : {},
@@ -335,6 +355,166 @@ window.__ModuleLoader__.load({
335
355
  })
336
356
  ]
337
357
  }),
358
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
359
+ style: group,
360
+ children: [
361
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GroupTitle, { children: "自动验收" }),
362
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
363
+ style: row,
364
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
365
+ title: "调用策略",
366
+ help: "手动仅暴露工具;智能在有结构化/高置信证据时路由四工具;严格还会主动执行语义路由并对任何关键操作做最终验收。"
367
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
368
+ style: selectStyle,
369
+ value: draft.autoVerifyMode,
370
+ onChange: (e) => patch("autoVerifyMode", e.target.value),
371
+ children: [
372
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
373
+ value: "manual",
374
+ children: "手动"
375
+ }),
376
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
377
+ value: "smart",
378
+ children: "智能(推荐)"
379
+ }),
380
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
381
+ value: "strict",
382
+ children: "严格"
383
+ })
384
+ ]
385
+ })]
386
+ }),
387
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
388
+ style: row,
389
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
390
+ title: "混合语义路由",
391
+ help: "结构化候选或检查点不足时,由裁判模型保守识别真实的 compare/select/track 对象。智能模式仅在有候选线索时运行;严格模式每次结束边界都会检查。"
392
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
393
+ type: "button",
394
+ role: "switch",
395
+ "aria-checked": draft.autoRouteSemantic,
396
+ "aria-label": "混合语义路由",
397
+ onClick: () => patch("autoRouteSemantic", !draft.autoRouteSemantic),
398
+ style: toggleStyle(draft.autoRouteSemantic),
399
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: toggleThumbStyle(draft.autoRouteSemantic) })
400
+ })]
401
+ }),
402
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
403
+ style: row,
404
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
405
+ title: "语义路由置信度",
406
+ help: "语义识别达到该置信度才执行 compare/select/track;范围 0–1,建议保持 0.9。"
407
+ }), numeric("autoRouteMinConfidence", 0)]
408
+ }),
409
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
410
+ style: row,
411
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
412
+ title: "最多候选数",
413
+ help: "一次自动 select 最多纳入的真实候选数量,至少 3。"
414
+ }), numeric("autoRouteMaxCandidates", 3)]
415
+ }),
416
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
417
+ style: row,
418
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
419
+ title: "每任务最多路由",
420
+ help: "compare/select/track 自动路由的任务预算;指纹去重后仍受此上限约束。"
421
+ }), numeric("autoRouteMaxPerTask", 1)]
422
+ }),
423
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
424
+ style: row,
425
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
426
+ title: "每会话最多路由",
427
+ help: "同一会话中 compare/select/track 自动路由总预算。"
428
+ }), numeric("autoRouteMaxPerSession", 1)]
429
+ }),
430
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
431
+ style: row,
432
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
433
+ title: "进度完成阈值",
434
+ help: "track 任一检查点低于该值时 steering 要求 Agent 继续执行;范围 0–1。"
435
+ }), numeric("autoTrackCompletionThreshold", 0)]
436
+ }),
437
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
438
+ style: row,
439
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
440
+ title: "单项证据字符上限",
441
+ help: "每个候选或检查点在脱敏后允许发送给裁判的最大字符数。"
442
+ }), numeric("autoRouteMaxItemChars", 100)]
443
+ }),
444
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
445
+ style: row,
446
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
447
+ title: "路由证据总字符上限",
448
+ help: "一次 compare/select/track 所有候选或步骤的总字符预算。"
449
+ }), numeric("autoRouteMaxInputChars", 1e3)]
450
+ }),
451
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
452
+ style: row,
453
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
454
+ title: "每任务模型调用预算",
455
+ help: "语义分类、中间路由和最终验收共享的估算模型调用上限。"
456
+ }), numeric("autoMaxModelCallsPerTask", 1)]
457
+ }),
458
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
459
+ style: row,
460
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
461
+ title: "每会话模型调用预算",
462
+ help: "整个会话所有自动验证阶段共享的估算模型调用上限。"
463
+ }), numeric("autoMaxModelCallsPerSession", 1)]
464
+ }),
465
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
466
+ style: row,
467
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
468
+ title: "通过阈值",
469
+ help: "会话证据分数达到该值且胜过空工作基线才允许结束;范围 0–1。"
470
+ }), numeric("autoVerifyThreshold", 0)]
471
+ }),
472
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
473
+ style: row,
474
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
475
+ title: "自动评估轮次",
476
+ help: "每项标准的自动评分重复次数;1 为低成本初筛。"
477
+ }), numeric("autoVerifyRepeats", 1)]
478
+ }),
479
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
480
+ style: row,
481
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
482
+ title: "智能模式最少工具调用",
483
+ help: "达到此工具证据数量且包含写入/执行类操作时才自动验收。"
484
+ }), numeric("autoVerifyMinToolCalls", 1)]
485
+ }),
486
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
487
+ style: row,
488
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
489
+ title: "最大证据字符",
490
+ help: "发送给裁判前保留的最近会话证据字符数。"
491
+ }), numeric("autoVerifyMaxChars", 1e3)]
492
+ }),
493
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
494
+ style: row,
495
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
496
+ title: "每任务最多验收",
497
+ help: "低分反馈后允许再次验收的次数上限,防止循环。"
498
+ }), numeric("autoVerifyMaxPerTask", 1)]
499
+ }),
500
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
501
+ style: row,
502
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
503
+ title: "每会话最多验收",
504
+ help: "同一会话中的自动验收总预算。"
505
+ }), numeric("autoVerifyMaxPerSession", 1)]
506
+ }),
507
+ draft.autoVerifyMode !== "manual" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
508
+ style: {
509
+ margin: "8px 0 0",
510
+ fontSize: 12,
511
+ lineHeight: "18px",
512
+ color: "var(--dsw-alias-state-warn-label)"
513
+ },
514
+ children: "自动路由按 select → compare → track → current_session 的阶段顺序处理;脱敏后的任务、Assistant 轨迹与真实工具输出会发送给所选裁判模型。候选选择或进度不足会 steering 继续工作,最终验收未通过会要求修复并重新验证。"
515
+ })
516
+ ]
517
+ }),
338
518
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
339
519
  style: group,
340
520
  children: [
package/lib/core.js CHANGED
@@ -73,7 +73,8 @@ function extractScore(completion, tag) {
73
73
  let last = null;
74
74
  for (let match = regex.exec(completion.text); match !== null; match = regex.exec(completion.text)) last = match;
75
75
  const letter = normalizeScoreLetter(last?.[1] ?? "");
76
- return letter === void 0 ? .5 : (letterValue(letter) - 1) / 19;
76
+ if (letter === void 0) throw new Error("llm-verifier: verifier response did not contain a valid " + tag + " A-T score");
77
+ return (letterValue(letter) - 1) / 19;
77
78
  }
78
79
  function buildPairwisePrompt(problem, traceA, traceB, criterion, groundTruthNote = DEFAULT_GROUND_TRUTH_NOTE) {
79
80
  return [