dsh-llm-verifier 0.1.17 → 0.2.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.
Files changed (110) hide show
  1. package/README.md +62 -11
  2. package/lib/{caller-D1ZQNsVU.js → caller-DJd_FlrQ.js} +15 -7
  3. package/lib/caller-DJd_FlrQ.js.map +1 -0
  4. package/lib/caller.js +1 -1
  5. package/lib/client.js +683 -71
  6. package/lib/client.js.map +1 -1
  7. package/lib/core.js +37 -8
  8. package/lib/core.js.map +1 -1
  9. package/lib/index.js +1267 -568
  10. package/lib/index.js.map +1 -1
  11. package/lib/types/auto.d.ts +10 -0
  12. package/lib/types/auto.d.ts.map +1 -1
  13. package/lib/types/auto.js +86 -22
  14. package/lib/types/auto.js.map +1 -1
  15. package/lib/types/cache.d.ts +1 -0
  16. package/lib/types/cache.d.ts.map +1 -1
  17. package/lib/types/cache.js +20 -10
  18. package/lib/types/cache.js.map +1 -1
  19. package/lib/types/caller.d.ts +3 -0
  20. package/lib/types/caller.d.ts.map +1 -1
  21. package/lib/types/caller.js +18 -4
  22. package/lib/types/caller.js.map +1 -1
  23. package/lib/types/client-i18n.d.ts +169 -0
  24. package/lib/types/client-i18n.d.ts.map +1 -1
  25. package/lib/types/client-i18n.js +172 -14
  26. package/lib/types/client-i18n.js.map +1 -1
  27. package/lib/types/client-judges.d.ts +20 -0
  28. package/lib/types/client-judges.d.ts.map +1 -0
  29. package/lib/types/client-judges.js +109 -0
  30. package/lib/types/client-judges.js.map +1 -0
  31. package/lib/types/client.d.ts +77 -2
  32. package/lib/types/client.d.ts.map +1 -1
  33. package/lib/types/client.js +76 -7
  34. package/lib/types/client.js.map +1 -1
  35. package/lib/types/config.d.ts +30 -0
  36. package/lib/types/config.d.ts.map +1 -1
  37. package/lib/types/config.js +118 -3
  38. package/lib/types/config.js.map +1 -1
  39. package/lib/types/core.d.ts +16 -0
  40. package/lib/types/core.d.ts.map +1 -1
  41. package/lib/types/core.js +51 -7
  42. package/lib/types/core.js.map +1 -1
  43. package/lib/types/engine.d.ts +19 -1
  44. package/lib/types/engine.d.ts.map +1 -1
  45. package/lib/types/engine.js +315 -29
  46. package/lib/types/engine.js.map +1 -1
  47. package/lib/types/images.d.ts +5 -0
  48. package/lib/types/images.d.ts.map +1 -1
  49. package/lib/types/images.js +31 -9
  50. package/lib/types/images.js.map +1 -1
  51. package/lib/types/index.d.ts.map +1 -1
  52. package/lib/types/index.js +149 -43
  53. package/lib/types/index.js.map +1 -1
  54. package/lib/types/plan-gate.d.ts.map +1 -1
  55. package/lib/types/plan-gate.js +7 -7
  56. package/lib/types/plan-gate.js.map +1 -1
  57. package/lib/types/router.d.ts +20 -0
  58. package/lib/types/router.d.ts.map +1 -1
  59. package/lib/types/router.js +86 -25
  60. package/lib/types/router.js.map +1 -1
  61. package/lib/types/session.d.ts.map +1 -1
  62. package/lib/types/session.js +9 -1
  63. package/lib/types/session.js.map +1 -1
  64. package/lib/types/statistics.d.ts +19 -1
  65. package/lib/types/statistics.d.ts.map +1 -1
  66. package/lib/types/statistics.js +85 -10
  67. package/lib/types/statistics.js.map +1 -1
  68. package/lib/types/team-gate.d.ts +1 -0
  69. package/lib/types/team-gate.d.ts.map +1 -1
  70. package/lib/types/team-gate.js +15 -8
  71. package/lib/types/team-gate.js.map +1 -1
  72. package/lib/types/top-logprobs.d.ts +1 -1
  73. package/lib/types/top-logprobs.d.ts.map +1 -1
  74. package/lib/types/top-logprobs.js +2 -2
  75. package/lib/types/top-logprobs.js.map +1 -1
  76. package/package.json +1 -1
  77. package/src/auto.test.ts +46 -7
  78. package/src/auto.ts +86 -21
  79. package/src/cache.test.ts +133 -0
  80. package/src/cache.ts +19 -8
  81. package/src/caller.test.ts +191 -3
  82. package/src/caller.ts +23 -4
  83. package/src/client-i18n.ts +214 -14
  84. package/src/client-judges.ts +121 -0
  85. package/src/client.test.ts +439 -0
  86. package/src/client.tsx +146 -9
  87. package/src/config.test.ts +433 -0
  88. package/src/config.ts +164 -3
  89. package/src/core.test.ts +76 -0
  90. package/src/core.ts +53 -7
  91. package/src/engine.test.ts +427 -1
  92. package/src/engine.ts +419 -34
  93. package/src/images.test.ts +16 -1
  94. package/src/images.ts +28 -6
  95. package/src/index.test.ts +74 -0
  96. package/src/index.ts +150 -46
  97. package/src/parity.test.ts +5 -0
  98. package/src/plan-gate.test.ts +28 -0
  99. package/src/plan-gate.ts +7 -7
  100. package/src/router.test.ts +117 -1
  101. package/src/router.ts +87 -25
  102. package/src/session.test.ts +38 -0
  103. package/src/session.ts +8 -1
  104. package/src/statistics.test.ts +314 -3
  105. package/src/statistics.ts +93 -9
  106. package/src/team-gate.test.ts +70 -1
  107. package/src/team-gate.ts +17 -8
  108. package/src/top-logprobs.test.ts +54 -2
  109. package/src/top-logprobs.ts +2 -2
  110. package/lib/caller-D1ZQNsVU.js.map +0 -1
package/lib/client.js CHANGED
@@ -32,9 +32,9 @@ window.__ModuleLoader__.load({
32
32
  "field.autoRouteMaxCandidates.title": "最多候选数",
33
33
  "field.autoRouteMaxCandidates.help": "一次自动 select 最多纳入的真实候选数量,至少 3。",
34
34
  "field.autoRouteMaxPerTask.title": "每任务最多路由",
35
- "field.autoRouteMaxPerTask.help": "compare/select/track 自动路由的任务预算;指纹去重后仍受此上限约束。",
35
+ "field.autoRouteMaxPerTask.help": "自动路由(compare/select/track 与语义分类)的任务尝试上限;与「每任务最多验收」共享同一个计数器,实际可用次数是两者之和。",
36
36
  "field.autoRouteMaxPerSession.title": "每会话最多路由",
37
- "field.autoRouteMaxPerSession.help": "同一会话中 compare/select/track 自动路由总预算。",
37
+ "field.autoRouteMaxPerSession.help": "同一会话中自动路由的尝试上限;与「每会话最多验收」共享计数器,实际可用次数同样是两者之和。",
38
38
  "field.autoTrackCompletionThreshold.title": "进度完成阈值",
39
39
  "field.autoTrackCompletionThreshold.help": "track 任一检查点低于该值时 steering 要求 Agent 继续执行;范围 0–1。",
40
40
  "field.autoRouteMaxItemChars.title": "单项证据字符上限",
@@ -47,17 +47,21 @@ window.__ModuleLoader__.load({
47
47
  "field.autoMaxModelCallsPerSession.help": "整个会话所有自动验证阶段共享的估算模型调用上限。",
48
48
  "field.autoVerifyThreshold.title": "通过阈值",
49
49
  "field.autoVerifyThreshold.help": "会话证据分数达到该值且胜过空工作基线才允许结束;范围 0–1。",
50
- "field.autoVerifyRepeats.title": "自动评估轮次",
51
- "field.autoVerifyRepeats.help": "每项标准的自动评分重复次数;1 为低成本初筛。",
50
+ "field.autoVerifyRepeats.title": "路由评估轮次",
51
+ "field.autoVerifyRepeats.help": "自动路由(compare / select / track)每项标准的评分重复次数;1 为低成本初筛。",
52
+ "field.autoVerifyFinalRepeats.title": "最终验收轮次",
53
+ "field.autoVerifyFinalRepeats.help": "最终会话验收每项标准的评分重复次数;偶数轮会交换 A/B 位置以抵消位置偏好,默认 2。",
52
54
  "field.autoVerifyMinToolCalls.title": "智能模式最少工具调用",
53
- "field.autoVerifyMinToolCalls.help": "达到此工具证据数量且包含写入/执行类操作时才自动验收。",
55
+ "field.autoVerifyMinToolCalls.help": "统计除 Verifier 之外的全部工具调用(含 read/grep 等只读工具);此外还必须存在写入/执行类操作与至少一条成功结果。",
54
56
  "field.autoVerifyMaxChars.title": "最大证据字符",
55
57
  "field.autoVerifyMaxChars.help": "发送给裁判前保留的最近会话证据字符数。",
56
58
  "field.autoVerifyMaxPerTask.title": "每任务最多验收",
57
- "field.autoVerifyMaxPerTask.help": "低分反馈后允许再次验收的次数上限,防止循环。",
59
+ "field.autoVerifyMaxPerTask.help": "最终验收的任务尝试上限;与「每任务最多路由」共享同一个计数器,实际可用次数是两者之和,用于防止低分反馈形成无限修复循环。",
58
60
  "field.autoVerifyMaxPerSession.title": "每会话最多验收",
59
- "field.autoVerifyMaxPerSession.help": "同一会话中的自动验收总预算。",
61
+ "field.autoVerifyMaxPerSession.help": "同一会话中最终验收的尝试上限;与「每会话最多路由」共享计数器,实际可用次数同样是两者之和。",
60
62
  "field.autoVerify.warnNotice": "自动路由按 select → compare → track → current_session 的阶段顺序处理;脱敏后的任务、Assistant 轨迹与真实工具输出会发送给所选裁判模型。候选选择或进度不足会 steering 继续工作,最终验收未通过会要求修复并重新验证。",
63
+ "field.autoMaxModelCallsPerTask.warnBudget": "当前单任务预算为 {current} 次模型调用,低于 {judges} 位裁判完整锦标赛所需的最差情况({required} 次)。建议调大该预算以避免自动路由过早耗尽。",
64
+ "field.autoMaxModelCallsPerSession.warnBudget": "当前会话预算为 {current} 次模型调用,低于 {judges} 位裁判推荐的最差情况({required} 次)。建议调大该预算以避免会话验证过早耗尽。",
61
65
  "section.model": "裁判模型",
62
66
  "field.provider.title": "供应商",
63
67
  "field.provider.help": "当前 DSH 中可路由的模型供应商。",
@@ -68,13 +72,35 @@ window.__ModuleLoader__.load({
68
72
  "field.reasoningEffort.default": "模型默认",
69
73
  "field.maxTokens.title": "最大输出 Token",
70
74
  "field.maxTokens.help": "单次裁判请求允许生成的最大 Token 数。",
75
+ "field.temperature.title": "裁判采样温度",
76
+ "field.temperature.help": "裁判模型调用的采样温度,范围 0–2;较低值可保证判决的一致与可复现性,默认值为 0.2。",
77
+ "field.label.title": "裁判标签",
78
+ "field.label.help": "在统计看板和明细中显示的裁判名称,留空时默认使用模型名称。",
79
+ "field.label.placeholder": "默认使用模型名称",
80
+ "field.extraJudges.title": "附加裁判",
81
+ "field.extraJudges.help": "添加最多 4 个额外裁判模型组成评审团。同一供应商与模型不能重复,且不能与主裁判相同。",
82
+ "field.extraJudges.add": "添加裁判",
83
+ "field.extraJudges.remove": "删除",
84
+ "field.extraJudges.labelTitle": "标签",
85
+ "field.extraJudges.labelPlaceholder": "自定义标签(可选)",
86
+ "field.extraJudges.providerAria": "附加裁判 #{index} 供应商",
87
+ "field.extraJudges.modelAria": "附加裁判 #{index} 模型",
88
+ "field.extraJudges.effortAria": "附加裁判 #{index} 推理强度",
89
+ "field.extraJudges.labelAria": "附加裁判 #{index} 标签",
90
+ "field.extraJudges.removeAria": "删除附加裁判 #{index}",
91
+ "field.extraJudges.conflictPrimary": "附加裁判 #{index} 与主裁判模型重复({id}),请选择不同模型。",
92
+ "field.extraJudges.conflictDuplicate": "附加裁判 #{index} 与附加裁判 #{other} 重复({id}),请选择不同模型。",
71
93
  "section.execution": "执行",
72
94
  "field.maxConcurrency.title": "最大并发",
73
95
  "field.maxConcurrency.help": "所有 verifier 工具共享的请求并发上限。",
74
96
  "field.maxRetries.title": "最多重试",
75
97
  "field.maxRetries.help": "限流、超时和短暂网络错误的重试次数。",
98
+ "field.retryBaseDelayMs.title": "重试基础延迟",
99
+ "field.retryBaseDelayMs.help": "遇到限流或暂时网络错误时重试的基础等待时间,单位为毫秒。",
76
100
  "field.timeoutMs.title": "请求超时",
77
101
  "field.timeoutMs.help": "单次模型请求的超时时间,单位为毫秒。",
102
+ "field.cacheDir.title": "缓存相对目录",
103
+ "field.cacheDir.help": "评分持久化缓存存放的相对目录路径(相对于话题目录),不能为空。",
78
104
  "field.cacheMaxEntries.title": "缓存条目上限",
79
105
  "field.cacheMaxEntries.help": "本地持久评分缓存保留的最大条目数。",
80
106
  "section.cost": "费用估算",
@@ -101,6 +127,24 @@ window.__ModuleLoader__.load({
101
127
  "stats.successCalls": "成功调用",
102
128
  "stats.failedCalls": "失败调用",
103
129
  "stats.avgDuration": "平均耗时",
130
+ "stats.phase.explicit": "显式调用",
131
+ "stats.phase.compare": "两项对比",
132
+ "stats.phase.select": "多项优选",
133
+ "stats.phase.track": "进度跟踪",
134
+ "stats.phase.final": "最终验收",
135
+ "stats.phase.plan_review": "计划预审",
136
+ "stats.phase.team_task": "Team 任务",
137
+ "stats.phase.semantic": "语义路由",
138
+ "stats.outcome.passed": "通过",
139
+ "stats.outcome.below-threshold": "未达标",
140
+ "stats.outcome.tie": "平局",
141
+ "stats.outcome.error": "异常",
142
+ "stats.outcome.dropped-over-budget": "超预算丢弃",
143
+ "stats.outcome.invalid-references": "引用无效",
144
+ "stats.winner.tie": "平局",
145
+ "stats.verdict.winner": "胜方 {winner}",
146
+ "stats.verdict.scoreThreshold": "得分 {score}(阈值 {threshold})",
147
+ "stats.verdict.scoreOnly": "得分 {score}",
104
148
  "metric.cacheHitRate": "缓存命中率",
105
149
  "metric.cacheHitNote": "{hits} 命中 / {total} 评分",
106
150
  "metric.tokens": "Token",
@@ -168,9 +212,9 @@ window.__ModuleLoader__.load({
168
212
  "field.autoRouteMaxCandidates.title": "Max Candidates",
169
213
  "field.autoRouteMaxCandidates.help": "Maximum number of real candidates included in an automatic select, at least 3.",
170
214
  "field.autoRouteMaxPerTask.title": "Max Routes per Task",
171
- "field.autoRouteMaxPerTask.help": "Task budget for automatic compare/select/track routing; still constrained by this cap after fingerprint deduplication.",
215
+ "field.autoRouteMaxPerTask.help": "Task attempt cap for automatic routing (compare/select/track and semantic classification); shares one counter with \"Max Verifications per Task\", so the effective limit is the sum of both.",
172
216
  "field.autoRouteMaxPerSession.title": "Max Routes per Session",
173
- "field.autoRouteMaxPerSession.help": "Total budget for automatic compare/select/track routing within the same session.",
217
+ "field.autoRouteMaxPerSession.help": "Session attempt cap for automatic routing; shares its counter with \"Max Verifications per Session\" (effective limit is the sum of both).",
174
218
  "field.autoTrackCompletionThreshold.title": "Progress Completion Threshold",
175
219
  "field.autoTrackCompletionThreshold.help": "When any track checkpoint falls below this score, steering instructs the Agent to continue; range 0–1.",
176
220
  "field.autoRouteMaxItemChars.title": "Max Chars per Route Item",
@@ -181,19 +225,23 @@ window.__ModuleLoader__.load({
181
225
  "field.autoMaxModelCallsPerTask.help": "Estimated model call cap shared across semantic classification, intermediate routing, and final acceptance per task.",
182
226
  "field.autoMaxModelCallsPerSession.title": "Model Call Budget per Session",
183
227
  "field.autoMaxModelCallsPerSession.help": "Estimated model call cap shared across all automatic verification phases throughout the entire session.",
228
+ "field.autoVerifyRepeats.title": "Routing Repeats",
229
+ "field.autoVerifyRepeats.help": "Scoring repeats per criterion for automatic routing (compare / select / track); 1 is low-cost preliminary screening.",
230
+ "field.autoVerifyFinalRepeats.title": "Final Acceptance Repeats",
231
+ "field.autoVerifyFinalRepeats.help": "Scoring repeats per criterion for the final session acceptance; even rounds swap A/B positions to cancel position bias. Defaults to 2.",
184
232
  "field.autoVerifyThreshold.title": "Pass Threshold",
185
233
  "field.autoVerifyThreshold.help": "Evidence score must meet this threshold and beat the empty-work baseline to allow completion; range 0–1.",
186
- "field.autoVerifyRepeats.title": "Auto Evaluation Repeats",
187
- "field.autoVerifyRepeats.help": "Number of scoring repeats per criterion for auto evaluation; 1 is low-cost preliminary screening.",
188
234
  "field.autoVerifyMinToolCalls.title": "Min Tool Calls (Smart Mode)",
189
- "field.autoVerifyMinToolCalls.help": "Auto-verification triggers only when reaching this tool evidence count and containing write/exec operations.",
235
+ "field.autoVerifyMinToolCalls.help": "Counts every non-Verifier tool call, read-only tools included; consequential write/exec work and at least one successful result are still required.",
190
236
  "field.autoVerifyMaxChars.title": "Max Evidence Characters",
191
237
  "field.autoVerifyMaxChars.help": "Maximum recent session evidence characters retained before sending to the judge.",
192
238
  "field.autoVerifyMaxPerTask.title": "Max Verifications per Task",
193
- "field.autoVerifyMaxPerTask.help": "Maximum verification attempts allowed after low-score feedback to prevent infinite loops.",
239
+ "field.autoVerifyMaxPerTask.help": "Final-acceptance attempt cap per task; shares one counter with \"Max Routes per Task\", so the effective limit is the sum of both. Prevents endless low-score repair loops.",
194
240
  "field.autoVerifyMaxPerSession.title": "Max Verifications per Session",
195
- "field.autoVerifyMaxPerSession.help": "Total auto-verification budget within the same session.",
241
+ "field.autoVerifyMaxPerSession.help": "Final-acceptance attempt cap within one session; shares its counter with \"Max Routes per Session\" (effective limit is the sum of both).",
196
242
  "field.autoVerify.warnNotice": "Automatic routing follows the phase order: select → compare → track → current_session. Sanitized tasks, Assistant trajectories, and real tool outputs are sent to the judge model. Candidate selection or insufficient progress steers the Agent to keep working; failed final acceptance requires remediation and re-verification.",
243
+ "field.autoMaxModelCallsPerTask.warnBudget": "Current task budget ({current} calls) is below the worst-case requirement ({required} calls) for {judges} judge(s) in a full tournament. Consider increasing it to avoid premature budget exhaustion.",
244
+ "field.autoMaxModelCallsPerSession.warnBudget": "Current session budget ({current} calls) is below the worst-case requirement ({required} calls) for {judges} judge(s). Consider increasing it to avoid premature budget exhaustion.",
197
245
  "section.model": "Judge Model",
198
246
  "field.provider.title": "Provider",
199
247
  "field.provider.help": "Routable model providers currently available in DSH.",
@@ -204,13 +252,35 @@ window.__ModuleLoader__.load({
204
252
  "field.reasoningEffort.default": "Model Default",
205
253
  "field.maxTokens.title": "Max Output Tokens",
206
254
  "field.maxTokens.help": "Maximum tokens allowed in a single judge model completion.",
255
+ "field.temperature.title": "Judge Sampling Temperature",
256
+ "field.temperature.help": "Sampling temperature for judge model calls, range 0–2. Lower values make judge verdicts reproducible. Default is 0.2.",
257
+ "field.label.title": "Judge Label",
258
+ "field.label.help": "Display name for the judge in statistics and dashboards. Defaults to the model name when empty.",
259
+ "field.label.placeholder": "Defaults to model name",
260
+ "field.extraJudges.title": "Extra Judges",
261
+ "field.extraJudges.help": "Add up to 4 extra judge models for ensemble verification. Duplicate provider and model combinations are not allowed, including the primary judge.",
262
+ "field.extraJudges.add": "Add Judge",
263
+ "field.extraJudges.remove": "Remove",
264
+ "field.extraJudges.labelTitle": "Label",
265
+ "field.extraJudges.labelPlaceholder": "Custom label (optional)",
266
+ "field.extraJudges.providerAria": "Extra judge #{index} provider",
267
+ "field.extraJudges.modelAria": "Extra judge #{index} model",
268
+ "field.extraJudges.effortAria": "Extra judge #{index} reasoning effort",
269
+ "field.extraJudges.labelAria": "Extra judge #{index} label",
270
+ "field.extraJudges.removeAria": "Remove extra judge #{index}",
271
+ "field.extraJudges.conflictPrimary": "Extra judge #{index} duplicates primary judge ({id}). Please select a different model.",
272
+ "field.extraJudges.conflictDuplicate": "Extra judge #{index} duplicates extra judge #{other} ({id}). Please select a different model.",
207
273
  "section.execution": "Execution",
208
274
  "field.maxConcurrency.title": "Max Concurrency",
209
275
  "field.maxConcurrency.help": "Maximum concurrent requests shared across all verifier tools.",
210
276
  "field.maxRetries.title": "Max Retries",
211
277
  "field.maxRetries.help": "Retry attempts for rate limits, timeouts, and transient network errors.",
278
+ "field.retryBaseDelayMs.title": "Retry Base Delay",
279
+ "field.retryBaseDelayMs.help": "Base wait time before retrying on rate limits or transient network errors, in milliseconds.",
212
280
  "field.timeoutMs.title": "Request Timeout",
213
281
  "field.timeoutMs.help": "Timeout for a single model request, in milliseconds.",
282
+ "field.cacheDir.title": "Cache Relative Directory",
283
+ "field.cacheDir.help": "Relative directory for persistent score cache (relative to topic directory). Cannot be empty.",
214
284
  "field.cacheMaxEntries.title": "Max Cache Entries",
215
285
  "field.cacheMaxEntries.help": "Maximum entries retained in the local persistent score cache.",
216
286
  "section.cost": "Cost Estimation",
@@ -237,6 +307,24 @@ window.__ModuleLoader__.load({
237
307
  "stats.successCalls": "Successful",
238
308
  "stats.failedCalls": "Failed",
239
309
  "stats.avgDuration": "Avg Duration",
310
+ "stats.phase.explicit": "Explicit",
311
+ "stats.phase.compare": "Compare",
312
+ "stats.phase.select": "Select",
313
+ "stats.phase.track": "Track",
314
+ "stats.phase.final": "Final Acceptance",
315
+ "stats.phase.plan_review": "Plan Review",
316
+ "stats.phase.team_task": "Team Task",
317
+ "stats.phase.semantic": "Semantic Route",
318
+ "stats.outcome.passed": "Passed",
319
+ "stats.outcome.below-threshold": "Below Threshold",
320
+ "stats.outcome.tie": "Tie",
321
+ "stats.outcome.error": "Error",
322
+ "stats.outcome.dropped-over-budget": "Dropped (over budget)",
323
+ "stats.outcome.invalid-references": "Invalid references",
324
+ "stats.winner.tie": "Tie",
325
+ "stats.verdict.winner": "Winner {winner}",
326
+ "stats.verdict.scoreThreshold": "Score {score} (threshold {threshold})",
327
+ "stats.verdict.scoreOnly": "Score {score}",
240
328
  "metric.cacheHitRate": "Cache Hit Rate",
241
329
  "metric.cacheHitNote": "{hits} hits / {total} scored",
242
330
  "metric.tokens": "Tokens",
@@ -355,6 +443,174 @@ window.__ModuleLoader__.load({
355
443
  second: "2-digit"
356
444
  }).format(value);
357
445
  }
446
+ function resolveCacheDirOnSave(draft, previous) {
447
+ if (typeof draft === "string" && draft.trim()) return draft.trim();
448
+ if (typeof previous === "string" && previous.trim()) return previous.trim();
449
+ }
450
+ /** An eight-candidate select: ring + pivot rounds (18 pairs) x three criteria, one repeat. */
451
+ const WORST_CASE_ROUTE_CALLS_PER_JUDGE = 54;
452
+ /** Final acceptance: three criteria x the default two repeats (one per A/B position). */
453
+ const WORST_CASE_FINAL_CALLS_PER_JUDGE = 6;
454
+ const WORST_CASE_TASK_PER_JUDGE = 60;
455
+ const WORST_CASE_SESSION_PER_JUDGE = 160;
456
+ function computeJudgeCount(extraJudgesCount) {
457
+ return 1 + Math.max(0, extraJudgesCount);
458
+ }
459
+ function computeWorstCaseBudget(judgeCount) {
460
+ const count = Math.max(1, judgeCount);
461
+ return {
462
+ worstCaseTask: count * 60,
463
+ worstCaseSession: count * 160
464
+ };
465
+ }
466
+ function evaluateBudgetWarning(autoVerifyMode, extraJudgesCount, autoMaxModelCallsPerTask, autoMaxModelCallsPerSession) {
467
+ if (autoVerifyMode === "manual") return null;
468
+ const judgeCount = computeJudgeCount(extraJudgesCount);
469
+ const { worstCaseTask, worstCaseSession } = computeWorstCaseBudget(judgeCount);
470
+ const warnTask = autoMaxModelCallsPerTask < worstCaseTask;
471
+ const warnSession = autoMaxModelCallsPerSession < worstCaseSession;
472
+ if (!warnTask && !warnSession) return null;
473
+ return {
474
+ judgeCount,
475
+ worstCaseTask,
476
+ worstCaseSession,
477
+ warnTask,
478
+ warnSession
479
+ };
480
+ }
481
+ function isVerdictFailed(verdict, success = true) {
482
+ if (!success) return true;
483
+ if (!verdict) return false;
484
+ return verdict.outcome === "below-threshold" || verdict.outcome === "error";
485
+ }
486
+ function formatPercentage(val) {
487
+ if (!Number.isFinite(val)) return "--";
488
+ return `${(val * 100).toFixed(1)}%`;
489
+ }
490
+ function formatVerdictDetails(verdict, t) {
491
+ const isFailed = verdict.outcome === "below-threshold" || verdict.outcome === "error";
492
+ const outcomeKey = verdict.outcome ? `stats.outcome.${verdict.outcome}` : void 0;
493
+ const outcomeText = outcomeKey && t[outcomeKey] ? t[outcomeKey] : verdict.outcome;
494
+ const phaseKey = verdict.phase ? `stats.phase.${verdict.phase}` : void 0;
495
+ const phaseText = phaseKey && t[phaseKey] ? t[phaseKey] : verdict.phase;
496
+ let scoreText;
497
+ if (typeof verdict.score === "number") {
498
+ const scorePct = formatPercentage(verdict.score);
499
+ if (typeof verdict.threshold === "number") {
500
+ const threshPct = formatPercentage(verdict.threshold);
501
+ scoreText = tFormat(t["stats.verdict.scoreThreshold"], {
502
+ score: scorePct,
503
+ threshold: threshPct
504
+ });
505
+ } else scoreText = tFormat(t["stats.verdict.scoreOnly"], { score: scorePct });
506
+ }
507
+ let winnerText;
508
+ if (verdict.winner) {
509
+ const winnerValue = verdict.winner === "tie" ? t["stats.winner.tie"] : verdict.winner;
510
+ winnerText = tFormat(t["stats.verdict.winner"], { winner: winnerValue });
511
+ }
512
+ return {
513
+ outcomeText,
514
+ phaseText,
515
+ scoreText,
516
+ winnerText,
517
+ isFailed
518
+ };
519
+ }
520
+ function normalizeExtraJudges(value) {
521
+ if (!Array.isArray(value)) return [];
522
+ const results = [];
523
+ for (const item of value) {
524
+ if (!item || typeof item !== "object" || Array.isArray(item)) continue;
525
+ const raw = item;
526
+ if (typeof raw.provider !== "string" || typeof raw.model !== "string") continue;
527
+ const provider = raw.provider.trim();
528
+ const model = raw.model.trim();
529
+ if (!provider || !model) continue;
530
+ const draft = {
531
+ provider,
532
+ model
533
+ };
534
+ if (typeof raw.reasoningEffort === "string") {
535
+ const effort = raw.reasoningEffort.trim();
536
+ if (effort) draft.reasoningEffort = effort;
537
+ }
538
+ if (typeof raw.label === "string") {
539
+ const label = raw.label.trim();
540
+ if (label) draft.label = label;
541
+ }
542
+ results.push(draft);
543
+ if (results.length >= 4) break;
544
+ }
545
+ return results;
546
+ }
547
+ function judgeIdentity(provider, model) {
548
+ return `${provider.trim()}/${model.trim()}`;
549
+ }
550
+ function judgeConflict(primary, judges) {
551
+ const primaryId = primary.provider.trim() && primary.model.trim() ? judgeIdentity(primary.provider, primary.model) : void 0;
552
+ const seen = /* @__PURE__ */ new Map();
553
+ for (let i = 0; i < judges.length; i++) {
554
+ const judge = judges[i];
555
+ const p = judge.provider.trim();
556
+ const m = judge.model.trim();
557
+ if (!p || !m) continue;
558
+ const id = judgeIdentity(p, m);
559
+ if (primaryId !== void 0 && id === primaryId) return {
560
+ index: i,
561
+ duplicateOf: "primary"
562
+ };
563
+ const prev = seen.get(id);
564
+ if (prev !== void 0) return {
565
+ index: i,
566
+ duplicateOf: prev
567
+ };
568
+ seen.set(id, i);
569
+ }
570
+ }
571
+ function addExtraJudge(judges, draft) {
572
+ if (judges.length >= 4) return [...judges];
573
+ const item = {
574
+ provider: draft.provider.trim(),
575
+ model: draft.model.trim()
576
+ };
577
+ if (typeof draft.reasoningEffort === "string") {
578
+ const effort = draft.reasoningEffort.trim();
579
+ if (effort) item.reasoningEffort = effort;
580
+ }
581
+ if (typeof draft.label === "string") {
582
+ const label = draft.label.trim();
583
+ if (label) item.label = label;
584
+ }
585
+ return [...judges, item];
586
+ }
587
+ function removeExtraJudge(judges, index) {
588
+ if (index < 0 || index >= judges.length) return [...judges];
589
+ return judges.filter((_, i) => i !== index);
590
+ }
591
+ function serializeExtraJudges(judges) {
592
+ const list = judges.slice(0, 4);
593
+ const result = [];
594
+ for (const judge of list) {
595
+ const provider = judge.provider.trim();
596
+ const model = judge.model.trim();
597
+ if (!provider || !model) continue;
598
+ const entry = {
599
+ provider,
600
+ model
601
+ };
602
+ if (typeof judge.reasoningEffort === "string") {
603
+ const effort = judge.reasoningEffort.trim();
604
+ if (effort) entry.reasoningEffort = effort;
605
+ }
606
+ if (typeof judge.label === "string") {
607
+ const label = judge.label.trim();
608
+ if (label) entry.label = label;
609
+ }
610
+ result.push(entry);
611
+ }
612
+ return result;
613
+ }
358
614
  //#endregion
359
615
  //#region src/client.tsx
360
616
  const NS = "llm-verifier";
@@ -468,6 +724,7 @@ window.__ModuleLoader__.load({
468
724
  autoVerifyMode: mode,
469
725
  autoVerifyThreshold: Number(v.autoVerifyThreshold ?? .65),
470
726
  autoVerifyRepeats: Number(v.autoVerifyRepeats ?? 1),
727
+ autoVerifyFinalRepeats: Number(v.autoVerifyFinalRepeats ?? 2),
471
728
  autoVerifyMinToolCalls: Number(v.autoVerifyMinToolCalls ?? 3),
472
729
  autoVerifyMaxChars: Number(v.autoVerifyMaxChars ?? 8e4),
473
730
  autoVerifyMaxPerTask: Number(v.autoVerifyMaxPerTask ?? 2),
@@ -480,7 +737,7 @@ window.__ModuleLoader__.load({
480
737
  autoTrackCompletionThreshold: Number(v.autoTrackCompletionThreshold ?? .8),
481
738
  autoRouteMaxItemChars: Number(v.autoRouteMaxItemChars ?? 2e4),
482
739
  autoRouteMaxInputChars: Number(v.autoRouteMaxInputChars ?? 6e4),
483
- autoMaxModelCallsPerTask: Number(v.autoMaxModelCallsPerTask ?? 64),
740
+ autoMaxModelCallsPerTask: Number(v.autoMaxModelCallsPerTask ?? 96),
484
741
  autoMaxModelCallsPerSession: Number(v.autoMaxModelCallsPerSession ?? 240),
485
742
  autoVerifyTeamTasks: v.autoVerifyTeamTasks !== false,
486
743
  autoVerifyPlanMode: v.autoVerifyPlanMode !== false,
@@ -488,13 +745,18 @@ window.__ModuleLoader__.load({
488
745
  model: String(v.model ?? ""),
489
746
  ...typeof v.reasoningEffort === "string" ? { reasoningEffort: v.reasoningEffort } : {},
490
747
  maxTokens: Number(v.maxTokens ?? 32768),
748
+ temperature: Number(v.temperature ?? .2),
749
+ ...typeof v.label === "string" && v.label.trim() ? { label: v.label.trim() } : {},
491
750
  maxConcurrency: Number(v.maxConcurrency ?? 8),
492
751
  maxRetries: Number(v.maxRetries ?? 3),
752
+ retryBaseDelayMs: Number(v.retryBaseDelayMs ?? 500),
493
753
  timeoutMs: Number(v.timeoutMs ?? 3e5),
754
+ cacheDir: typeof v.cacheDir === "string" && v.cacheDir.trim() ? v.cacheDir.trim() : "verifier",
494
755
  cacheMaxEntries: Number(v.cacheMaxEntries ?? 1e4),
495
756
  estimatedInputUsdPerMillion: Number(v.estimatedInputUsdPerMillion ?? 0),
496
757
  estimatedOutputUsdPerMillion: Number(v.estimatedOutputUsdPerMillion ?? 0),
497
- autoVerifySubagents: v.autoVerifySubagents === true
758
+ autoVerifySubagents: v.autoVerifySubagents === true,
759
+ extraJudges: normalizeExtraJudges(v.extraJudges)
498
760
  };
499
761
  }
500
762
  function message(error) {
@@ -583,17 +845,66 @@ window.__ModuleLoader__.load({
583
845
  [key]: value
584
846
  } : v);
585
847
  };
848
+ const conflict = (0, react.useMemo)(() => draft ? judgeConflict({
849
+ provider: draft.provider,
850
+ model: draft.model
851
+ }, draft.extraJudges) : void 0, [
852
+ draft?.provider,
853
+ draft?.model,
854
+ draft?.extraJudges
855
+ ]);
856
+ const addJudge = () => {
857
+ if (!loaded || !draft || draft.extraJudges.length >= 4) return;
858
+ const firstGroup = loaded.groups[0];
859
+ const firstProvider = firstGroup?.id ?? "";
860
+ const firstModelObj = firstGroup?.models[0];
861
+ const firstModel = firstModelObj?.id ?? "";
862
+ const defaultEffort = firstModelObj?.reasoning?.defaultEffort;
863
+ const newJudge = {
864
+ provider: firstProvider,
865
+ model: firstModel,
866
+ ...defaultEffort ? { reasoningEffort: defaultEffort } : {}
867
+ };
868
+ patch("extraJudges", addExtraJudge(draft.extraJudges, newJudge));
869
+ };
870
+ const removeJudge = (idx) => {
871
+ if (!draft) return;
872
+ patch("extraJudges", removeExtraJudge(draft.extraJudges, idx));
873
+ };
874
+ const updateJudge = (idx, updates) => {
875
+ if (!draft) return;
876
+ const next = draft.extraJudges.map((j, i) => i === idx ? {
877
+ ...j,
878
+ ...updates
879
+ } : j);
880
+ patch("extraJudges", next);
881
+ };
882
+ const budgetWarning = (0, react.useMemo)(() => {
883
+ if (!draft) return null;
884
+ return evaluateBudgetWarning(draft.autoVerifyMode, draft.extraJudges.length, draft.autoMaxModelCallsPerTask, draft.autoMaxModelCallsPerSession);
885
+ }, [
886
+ draft?.autoVerifyMode,
887
+ draft?.extraJudges.length,
888
+ draft?.autoMaxModelCallsPerTask,
889
+ draft?.autoMaxModelCallsPerSession
890
+ ]);
586
891
  const save = async () => {
587
- if (!loaded || !draft) return;
892
+ if (!loaded || !draft || conflict) return;
588
893
  setBusy(true);
589
894
  setSaved(false);
590
895
  setError(null);
591
896
  try {
592
897
  const section = {
593
898
  ...record(loaded.settings.user),
594
- ...draft
899
+ ...draft,
900
+ extraJudges: serializeExtraJudges(draft.extraJudges)
595
901
  };
596
902
  if (!draft.reasoningEffort) delete section.reasoningEffort;
903
+ if (!draft.label || !draft.label.trim()) delete section.label;
904
+ else section.label = draft.label.trim();
905
+ const resolvedCacheDir = resolveCacheDirOnSave(draft.cacheDir, values(loaded.settings).cacheDir);
906
+ if (!resolvedCacheDir) delete section.cacheDir;
907
+ else section.cacheDir = resolvedCacheDir;
597
908
  const res = await remote.settings.update(NS, section, loaded.settings.revision);
598
909
  if (!res.ok) throw new Error(res.error.message);
599
910
  setLoaded((v) => v ? {
@@ -654,6 +965,19 @@ window.__ModuleLoader__.load({
654
965
  return next;
655
966
  })
656
967
  });
968
+ const textField = (key, placeholder) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
969
+ style: {
970
+ width: "100%",
971
+ height: 36,
972
+ borderRadius: 8
973
+ },
974
+ type: "text",
975
+ disabled: busy,
976
+ placeholder,
977
+ "aria-label": t["field." + key + ".title"] ?? String(key),
978
+ value: String(draft[key] ?? ""),
979
+ onChange: (e) => patch(key, e.target.value)
980
+ });
657
981
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
658
982
  style: shell,
659
983
  children: [
@@ -842,6 +1166,19 @@ window.__ModuleLoader__.load({
842
1166
  help: t["field.autoMaxModelCallsPerTask.help"]
843
1167
  }), numeric("autoMaxModelCallsPerTask", 1)]
844
1168
  }),
1169
+ budgetWarning?.warnTask && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1170
+ style: {
1171
+ margin: "-4px 0 10px",
1172
+ fontSize: 12,
1173
+ lineHeight: "18px",
1174
+ color: "var(--dsw-alias-state-warn-label)"
1175
+ },
1176
+ children: tFormat(t["field.autoMaxModelCallsPerTask.warnBudget"], {
1177
+ current: draft.autoMaxModelCallsPerTask,
1178
+ required: budgetWarning.worstCaseTask,
1179
+ judges: budgetWarning.judgeCount
1180
+ })
1181
+ }),
845
1182
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
846
1183
  style: row,
847
1184
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
@@ -849,6 +1186,19 @@ window.__ModuleLoader__.load({
849
1186
  help: t["field.autoMaxModelCallsPerSession.help"]
850
1187
  }), numeric("autoMaxModelCallsPerSession", 1)]
851
1188
  }),
1189
+ budgetWarning?.warnSession && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1190
+ style: {
1191
+ margin: "-4px 0 10px",
1192
+ fontSize: 12,
1193
+ lineHeight: "18px",
1194
+ color: "var(--dsw-alias-state-warn-label)"
1195
+ },
1196
+ children: tFormat(t["field.autoMaxModelCallsPerSession.warnBudget"], {
1197
+ current: draft.autoMaxModelCallsPerSession,
1198
+ required: budgetWarning.worstCaseSession,
1199
+ judges: budgetWarning.judgeCount
1200
+ })
1201
+ }),
852
1202
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
853
1203
  style: row,
854
1204
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
@@ -863,6 +1213,13 @@ window.__ModuleLoader__.load({
863
1213
  help: t["field.autoVerifyRepeats.help"]
864
1214
  }), numeric("autoVerifyRepeats", 1)]
865
1215
  }),
1216
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1217
+ style: row,
1218
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
1219
+ title: t["field.autoVerifyFinalRepeats.title"],
1220
+ help: t["field.autoVerifyFinalRepeats.help"]
1221
+ }), numeric("autoVerifyFinalRepeats", 1)]
1222
+ }),
866
1223
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
867
1224
  style: row,
868
1225
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
@@ -993,6 +1350,188 @@ window.__ModuleLoader__.load({
993
1350
  title: t["field.maxTokens.title"],
994
1351
  help: t["field.maxTokens.help"]
995
1352
  }), numeric("maxTokens", 1)]
1353
+ }),
1354
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1355
+ style: row,
1356
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
1357
+ title: t["field.temperature.title"],
1358
+ help: t["field.temperature.help"]
1359
+ }), numeric("temperature", 0)]
1360
+ }),
1361
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1362
+ style: row,
1363
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
1364
+ title: t["field.label.title"],
1365
+ help: t["field.label.help"]
1366
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1367
+ style: {
1368
+ width: "100%",
1369
+ height: 36,
1370
+ borderRadius: 8
1371
+ },
1372
+ type: "text",
1373
+ disabled: busy,
1374
+ placeholder: draft.model || t["field.label.placeholder"],
1375
+ "aria-label": t["field.label.title"],
1376
+ value: draft.label ?? "",
1377
+ onChange: (e) => patch("label", e.target.value || void 0)
1378
+ })]
1379
+ }),
1380
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1381
+ style: {
1382
+ ...row,
1383
+ gridTemplateColumns: "minmax(180px, 1fr) auto",
1384
+ minHeight: 48
1385
+ },
1386
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
1387
+ title: t["field.extraJudges.title"],
1388
+ help: t["field.extraJudges.help"]
1389
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1390
+ variant: "outline",
1391
+ disabled: busy || draft.extraJudges.length >= 4,
1392
+ onClick: addJudge,
1393
+ children: t["field.extraJudges.add"]
1394
+ })]
1395
+ }),
1396
+ draft.extraJudges.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1397
+ style: {
1398
+ display: "flex",
1399
+ flexDirection: "column",
1400
+ gap: 8,
1401
+ padding: "10px 0",
1402
+ borderBottom: "1px solid var(--dsw-alias-border-l2)"
1403
+ },
1404
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1405
+ style: {
1406
+ display: "grid",
1407
+ gridTemplateColumns: "minmax(110px, 1.2fr) minmax(120px, 1.3fr) minmax(95px, 1fr) minmax(95px, 1fr) auto",
1408
+ gap: 8,
1409
+ fontSize: 12,
1410
+ color: "var(--dsw-alias-label-tertiary)",
1411
+ paddingBottom: 2
1412
+ },
1413
+ children: [
1414
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t["field.provider.title"] }),
1415
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t["field.model.title"] }),
1416
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t["field.reasoningEffort.title"] }),
1417
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: t["field.extraJudges.labelTitle"] }),
1418
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {})
1419
+ ]
1420
+ }), draft.extraJudges.map((judge, idx) => {
1421
+ const judgeModels = loaded.groups.find((g) => g.id === judge.provider)?.models ?? [];
1422
+ const judgeEfforts = judgeModels.find((m) => m.id === judge.model)?.reasoning?.efforts ?? [];
1423
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1424
+ style: {
1425
+ display: "grid",
1426
+ gridTemplateColumns: "minmax(110px, 1.2fr) minmax(120px, 1.3fr) minmax(95px, 1fr) minmax(95px, 1fr) auto",
1427
+ gap: 8,
1428
+ alignItems: "center"
1429
+ },
1430
+ children: [
1431
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1432
+ style: selectStyle,
1433
+ disabled: busy,
1434
+ "aria-label": tFormat(t["field.extraJudges.providerAria"], { index: idx + 1 }),
1435
+ value: judge.provider,
1436
+ onChange: (e) => {
1437
+ const newProvider = e.target.value;
1438
+ const firstM = loaded.groups.find((g) => g.id === newProvider)?.models[0];
1439
+ updateJudge(idx, {
1440
+ provider: newProvider,
1441
+ model: firstM?.id ?? "",
1442
+ ...firstM?.reasoning?.defaultEffort ? { reasoningEffort: firstM.reasoning.defaultEffort } : { reasoningEffort: void 0 }
1443
+ });
1444
+ },
1445
+ children: [!loaded.groups.some((g) => g.id === judge.provider) && judge.provider && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1446
+ value: judge.provider,
1447
+ children: judge.provider
1448
+ }), loaded.groups.map((g) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
1449
+ value: g.id,
1450
+ children: [
1451
+ g.name,
1452
+ " · ",
1453
+ g.id
1454
+ ]
1455
+ }, g.id))]
1456
+ }),
1457
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1458
+ style: selectStyle,
1459
+ disabled: busy,
1460
+ "aria-label": tFormat(t["field.extraJudges.modelAria"], { index: idx + 1 }),
1461
+ value: judge.model,
1462
+ onChange: (e) => {
1463
+ const newModel = e.target.value;
1464
+ const found = judgeModels.find((m) => m.id === newModel);
1465
+ updateJudge(idx, {
1466
+ model: newModel,
1467
+ ...found?.reasoning?.defaultEffort ? { reasoningEffort: found.reasoning.defaultEffort } : { reasoningEffort: void 0 }
1468
+ });
1469
+ },
1470
+ children: [!judgeModels.some((m) => m.id === judge.model) && judge.model && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1471
+ value: judge.model,
1472
+ children: judge.model
1473
+ }), judgeModels.map((m) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
1474
+ value: m.id,
1475
+ children: [
1476
+ m.name,
1477
+ " · ",
1478
+ m.id
1479
+ ]
1480
+ }, m.id))]
1481
+ }),
1482
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
1483
+ style: selectStyle,
1484
+ disabled: busy,
1485
+ "aria-label": tFormat(t["field.extraJudges.effortAria"], { index: idx + 1 }),
1486
+ value: judge.reasoningEffort ?? "",
1487
+ onChange: (e) => updateJudge(idx, { reasoningEffort: e.target.value || void 0 }),
1488
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1489
+ value: "",
1490
+ children: t["field.reasoningEffort.default"]
1491
+ }), judgeEfforts.map((ef) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
1492
+ value: ef.id,
1493
+ children: ef.name
1494
+ }, ef.id))]
1495
+ }),
1496
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Input, {
1497
+ style: {
1498
+ width: "100%",
1499
+ height: 36,
1500
+ borderRadius: 8
1501
+ },
1502
+ type: "text",
1503
+ disabled: busy,
1504
+ placeholder: t["field.extraJudges.labelPlaceholder"],
1505
+ "aria-label": tFormat(t["field.extraJudges.labelAria"], { index: idx + 1 }),
1506
+ value: judge.label ?? "",
1507
+ onChange: (e) => updateJudge(idx, { label: e.target.value })
1508
+ }),
1509
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1510
+ variant: "outline",
1511
+ disabled: busy,
1512
+ "aria-label": tFormat(t["field.extraJudges.removeAria"], { index: idx + 1 }),
1513
+ onClick: () => removeJudge(idx),
1514
+ children: t["field.extraJudges.remove"]
1515
+ })
1516
+ ]
1517
+ }, idx);
1518
+ })]
1519
+ }),
1520
+ conflict && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1521
+ style: {
1522
+ margin: "8px 0 0",
1523
+ fontSize: 12,
1524
+ lineHeight: "18px",
1525
+ color: "var(--dsw-alias-state-warn-label)"
1526
+ },
1527
+ children: conflict.duplicateOf === "primary" ? tFormat(t["field.extraJudges.conflictPrimary"], {
1528
+ index: conflict.index + 1,
1529
+ id: judgeIdentity(draft.extraJudges[conflict.index]?.provider ?? "", draft.extraJudges[conflict.index]?.model ?? "")
1530
+ }) : tFormat(t["field.extraJudges.conflictDuplicate"], {
1531
+ index: conflict.index + 1,
1532
+ other: conflict.duplicateOf + 1,
1533
+ id: judgeIdentity(draft.extraJudges[conflict.index]?.provider ?? "", draft.extraJudges[conflict.index]?.model ?? "")
1534
+ })
996
1535
  })
997
1536
  ]
998
1537
  }),
@@ -1014,6 +1553,13 @@ window.__ModuleLoader__.load({
1014
1553
  help: t["field.maxRetries.help"]
1015
1554
  }), numeric("maxRetries", 0)]
1016
1555
  }),
1556
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1557
+ style: row,
1558
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
1559
+ title: t["field.retryBaseDelayMs.title"],
1560
+ help: t["field.retryBaseDelayMs.help"]
1561
+ }), numeric("retryBaseDelayMs", 1)]
1562
+ }),
1017
1563
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1018
1564
  style: row,
1019
1565
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
@@ -1021,6 +1567,13 @@ window.__ModuleLoader__.load({
1021
1567
  help: t["field.timeoutMs.help"]
1022
1568
  }), numeric("timeoutMs", 1)]
1023
1569
  }),
1570
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1571
+ style: row,
1572
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
1573
+ title: t["field.cacheDir.title"],
1574
+ help: t["field.cacheDir.help"]
1575
+ }), textField("cacheDir", "verifier")]
1576
+ }),
1024
1577
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1025
1578
  style: row,
1026
1579
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Label, {
@@ -1099,7 +1652,7 @@ window.__ModuleLoader__.load({
1099
1652
  children: t["settings.reload"]
1100
1653
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
1101
1654
  variant: "primary",
1102
- disabled: busy || !loaded.writable,
1655
+ disabled: busy || !loaded.writable || Boolean(conflict),
1103
1656
  onClick: () => void save(),
1104
1657
  children: busy ? t["settings.saving"] : t["settings.save"]
1105
1658
  })]
@@ -1768,65 +2321,113 @@ window.__ModuleLoader__.load({
1768
2321
  maxHeight: 360,
1769
2322
  overflow: "auto"
1770
2323
  },
1771
- children: [(data?.recent ?? []).map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1772
- style: {
1773
- display: "grid",
1774
- gridTemplateColumns: "minmax(170px,1fr) auto",
1775
- gap: 12,
1776
- padding: "11px 8px",
1777
- borderTop: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))"
1778
- },
1779
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2324
+ children: [(data?.recent ?? []).map((item) => {
2325
+ const verdictInfo = item.verdict ? formatVerdictDetails(item.verdict, t) : void 0;
2326
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1780
2327
  style: {
1781
- display: "flex",
1782
- alignItems: "center",
1783
- gap: 8
2328
+ display: "grid",
2329
+ gridTemplateColumns: "minmax(170px,1fr) auto",
2330
+ gap: 12,
2331
+ padding: "11px 8px",
2332
+ borderTop: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))"
1784
2333
  },
1785
- children: [
1786
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
1787
- width: 7,
1788
- height: 7,
1789
- borderRadius: "50%",
1790
- background: item.success ? "#59c985" : "#e76565"
1791
- } }),
1792
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
1793
- style: { fontSize: 13 },
1794
- children: labels[item.toolName] ?? item.toolName
2334
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
2335
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2336
+ style: {
2337
+ display: "flex",
2338
+ alignItems: "center",
2339
+ gap: 8
2340
+ },
2341
+ children: [
2342
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { style: {
2343
+ width: 7,
2344
+ height: 7,
2345
+ borderRadius: "50%",
2346
+ background: !item.success || (verdictInfo ? verdictInfo.isFailed : false) ? "#e76565" : "#59c985"
2347
+ } }),
2348
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("strong", {
2349
+ style: { fontSize: 13 },
2350
+ children: labels[item.toolName] ?? item.toolName
2351
+ }),
2352
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
2353
+ style: muted,
2354
+ children: [
2355
+ item.provider,
2356
+ "/",
2357
+ item.model
2358
+ ]
2359
+ })
2360
+ ]
1795
2361
  }),
1796
- /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1797
- style: muted,
2362
+ !item.success && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2363
+ title: item.errorMessage,
2364
+ style: {
2365
+ margin: "5px 0 0 15px",
2366
+ fontSize: 11,
2367
+ color: "#e76565",
2368
+ overflow: "hidden",
2369
+ textOverflow: "ellipsis",
2370
+ whiteSpace: "nowrap"
2371
+ },
2372
+ children: item.errorMessage ?? item.errorName
2373
+ }),
2374
+ item.verdict && verdictInfo && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2375
+ style: {
2376
+ display: "flex",
2377
+ alignItems: "center",
2378
+ flexWrap: "wrap",
2379
+ gap: 6,
2380
+ margin: "6px 0 0 15px",
2381
+ fontSize: 11
2382
+ },
1798
2383
  children: [
1799
- item.provider,
1800
- "/",
1801
- item.model
2384
+ verdictInfo.outcomeText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2385
+ style: {
2386
+ padding: "1px 5px",
2387
+ borderRadius: 4,
2388
+ fontWeight: 500,
2389
+ fontSize: 11,
2390
+ background: verdictInfo.isFailed ? "rgba(231,101,101,.16)" : item.verdict.outcome === "tie" ? "rgba(227,189,99,.16)" : "rgba(89,201,133,.16)",
2391
+ color: verdictInfo.isFailed ? "#e76565" : item.verdict.outcome === "tie" ? "#e3bd63" : "#77d49b",
2392
+ border: `1px solid ${verdictInfo.isFailed ? "rgba(231,101,101,.3)" : item.verdict.outcome === "tie" ? "rgba(227,189,99,.3)" : "rgba(89,201,133,.3)"}`
2393
+ },
2394
+ children: verdictInfo.outcomeText
2395
+ }),
2396
+ verdictInfo.phaseText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2397
+ style: {
2398
+ padding: "1px 5px",
2399
+ borderRadius: 4,
2400
+ background: "var(--dsw-surface-sunken)",
2401
+ color: "var(--dsw-text-secondary)",
2402
+ border: "1px solid var(--dsw-alias-border-l2, rgba(255,255,255,.1))"
2403
+ },
2404
+ children: verdictInfo.phaseText
2405
+ }),
2406
+ verdictInfo.scoreText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2407
+ style: { color: typeof item.verdict.threshold === "number" && typeof item.verdict.score === "number" && item.verdict.score < item.verdict.threshold ? "#e76565" : "var(--dsw-text-primary)" },
2408
+ children: verdictInfo.scoreText
2409
+ }),
2410
+ verdictInfo.winnerText && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
2411
+ style: { color: "var(--dsw-text-secondary)" },
2412
+ children: verdictInfo.winnerText
2413
+ })
1802
2414
  ]
1803
2415
  })
1804
- ]
1805
- }), !item.success && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1806
- title: item.errorMessage,
1807
- style: {
1808
- margin: "5px 0 0 15px",
1809
- fontSize: 11,
1810
- color: "#e76565",
1811
- overflow: "hidden",
1812
- textOverflow: "ellipsis",
1813
- whiteSpace: "nowrap"
1814
- },
1815
- children: item.errorMessage ?? item.errorName
1816
- })] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1817
- style: { textAlign: "right" },
1818
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1819
- style: { fontSize: 12 },
1820
- children: duration(item.durationMs)
1821
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1822
- style: {
1823
- ...muted,
1824
- marginTop: 3
1825
- },
1826
- children: dateTime(item.startedAt, lang)
2416
+ ] }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2417
+ style: { textAlign: "right" },
2418
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2419
+ style: { fontSize: 12 },
2420
+ children: duration(item.durationMs)
2421
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2422
+ style: {
2423
+ ...muted,
2424
+ marginTop: 3
2425
+ },
2426
+ children: dateTime(item.startedAt, lang)
2427
+ })]
1827
2428
  })]
1828
- })]
1829
- }, item.id)), (data?.recent.length ?? 0) === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2429
+ }, item.id);
2430
+ }), (data?.recent.length ?? 0) === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1830
2431
  style: {
1831
2432
  padding: 24,
1832
2433
  textAlign: "center",
@@ -2012,15 +2613,26 @@ window.__ModuleLoader__.load({
2012
2613
  exports.StatisticsPage = StatisticsPage;
2013
2614
  exports.VerifierSettings = VerifierSettings;
2014
2615
  exports.VerifierSidebarIcon = VerifierSidebarIcon;
2616
+ exports.WORST_CASE_FINAL_CALLS_PER_JUDGE = WORST_CASE_FINAL_CALLS_PER_JUDGE;
2617
+ exports.WORST_CASE_ROUTE_CALLS_PER_JUDGE = WORST_CASE_ROUTE_CALLS_PER_JUDGE;
2618
+ exports.WORST_CASE_SESSION_PER_JUDGE = WORST_CASE_SESSION_PER_JUDGE;
2619
+ exports.WORST_CASE_TASK_PER_JUDGE = WORST_CASE_TASK_PER_JUDGE;
2015
2620
  exports.apply = apply;
2016
2621
  exports.compact = compact;
2622
+ exports.computeJudgeCount = computeJudgeCount;
2623
+ exports.computeWorstCaseBudget = computeWorstCaseBudget;
2017
2624
  exports.dateTime = dateTime;
2018
2625
  exports.detectLanguage = detectLanguage;
2019
2626
  exports.dictionaries = dictionaries;
2020
2627
  exports.duration = duration;
2021
2628
  exports.en = en;
2629
+ exports.evaluateBudgetWarning = evaluateBudgetWarning;
2630
+ exports.formatPercentage = formatPercentage;
2631
+ exports.formatVerdictDetails = formatVerdictDetails;
2022
2632
  exports.inject = inject;
2633
+ exports.isVerdictFailed = isVerdictFailed;
2023
2634
  exports.money = money;
2635
+ exports.resolveCacheDirOnSave = resolveCacheDirOnSave;
2024
2636
  exports.tFormat = tFormat;
2025
2637
  exports.toolLabels = toolLabels;
2026
2638
  exports.useLanguage = useLanguage;