dsh-approval-review 0.2.1 → 0.2.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.
- package/README-zh.md +1 -1
- package/README.md +1 -1
- package/cordis.patch.yml +4 -1
- package/lib/index.js +29 -41
- package/package.json +1 -1
package/README-zh.md
CHANGED
|
@@ -68,7 +68,7 @@ dsh --profile <profile> --dump-config | grep -A6 'id: approval-review'
|
|
|
68
68
|
| `reviewer.provider` / `.model` | *(继承)* | 复核路由;不填则继承调用 Agent 自己的路由。会话内可用 `/approval-review model [<provider>/]<id>` 覆盖(**「审批」页签右上角可以直接选**:点开即列出本机配置的模型,候选来自客户端自己的模型目录服务 `modelDirectories`——和 `/model` 选择器、输入框里的模型座位读的是同一份目录。列表由插件自己渲染(原生 `datalist`/`select` 的弹层字号字重无法用 CSS 控制,会显得比页面吵),支持输入过滤、方向键+回车,也可以手打目录里没有的 id)。 |
|
|
69
69
|
| `reviewer.subagentProvider` | `fork` | `mode: subagent` 用的子代理后端(`fork` / `spawn`)。 |
|
|
70
70
|
| `reviewer.tools` | `[read, glob, grep]` | 复核子代理的工具白名单。留空会回退到只读默认,而不是继承父代理的全部工具。 |
|
|
71
|
-
| `reviewer.timeoutMs` | `
|
|
71
|
+
| `reviewer.timeoutMs` | `120000` | 单次复核的硬超时。慢路由 + 推理型复核者实测要 ~50 秒;超时不是「否决」,而是按失败策略走 fail-closed。 |
|
|
72
72
|
| `reviewer.maxTokens` | `1024` | 输出上限。 |
|
|
73
73
|
| `reviewer.temperature` | `0` | 采样温度。 |
|
|
74
74
|
| `reviewer.policyText` | *(内置策略)* | 替换裁决策略正文。 |
|
package/README.md
CHANGED
|
@@ -88,7 +88,7 @@ schema defaults.
|
|
|
88
88
|
| `reviewer.provider` / `.model` | *(inherit)* | Reviewer route; unset inherits the calling agent's own route. |
|
|
89
89
|
| `reviewer.subagentProvider` | `fork` | Subagent backend for `mode: subagent` (`fork` / `spawn`). |
|
|
90
90
|
| `reviewer.tools` | `[read, glob, grep]` | The reviewer child's tool allow-list. An empty list falls back to the read-only default rather than the parent's whole face. |
|
|
91
|
-
| `reviewer.timeoutMs` | `
|
|
91
|
+
| `reviewer.timeoutMs` | `120000` | Hard deadline for one reviewer call. A slow route plus a reasoning reviewer can take ~50s; a deadline that expires mid-review becomes a fail-closed refusal, not a verdict. |
|
|
92
92
|
| `reviewer.maxTokens` | `1024` | Output cap. |
|
|
93
93
|
| `reviewer.temperature` | `0` | Sampling temperature. |
|
|
94
94
|
| `reviewer.policyText` | *(shipping policy)* | Replaces the ruling policy text. |
|
package/cordis.patch.yml
CHANGED
|
@@ -35,7 +35,10 @@
|
|
|
35
35
|
# Reviewer model, prompt, and size limits. `provider`/`model` unset means
|
|
36
36
|
# the reviewer inherits the calling agent's own route.
|
|
37
37
|
reviewer:
|
|
38
|
-
|
|
38
|
+
# 120s, not 60s: a reasoning reviewer on a slower route measured 52.5s
|
|
39
|
+
# here, and a deadline that expires mid-review is not a "no" — it is a
|
|
40
|
+
# fail-closed refusal under the default `onReviewerFailure: rejected`.
|
|
41
|
+
timeoutMs: 120000
|
|
39
42
|
maxTokens: 1024
|
|
40
43
|
temperature: 0
|
|
41
44
|
argumentMaxChars: 4000
|
package/lib/index.js
CHANGED
|
@@ -2,7 +2,6 @@ import Schema from "@deepseek-ai/schemastery";
|
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { BlockAssembler, LlmError, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
|
-
import { assertObjectJsonSchema } from "@deepseek-ai/dsh-tools";
|
|
6
5
|
//#region src/review-types.ts
|
|
7
6
|
/** Every {@link RiskLevel}, least to most dangerous (index is the rank). */
|
|
8
7
|
const RISK_LEVELS = [
|
|
@@ -56,7 +55,7 @@ const Config = Schema.object({
|
|
|
56
55
|
"glob",
|
|
57
56
|
"grep"
|
|
58
57
|
]).description("The reviewer child's tool allow-list. An empty list falls back to the read-only default rather than the parent's whole face."),
|
|
59
|
-
timeoutMs: Schema.number().step(1).min(1e3).default(
|
|
58
|
+
timeoutMs: Schema.number().step(1).min(1e3).default(12e4).description("Hard deadline for one reviewer call. A reasoning reviewer on a slow route can take tens of seconds; a deadline that is too tight turns into a fail-closed refusal (the default failure policy) rather than a verdict."),
|
|
60
59
|
maxTokens: Schema.number().step(1).min(64).default(1024).description("Output-token cap for one reviewer call (`mode: direct`)."),
|
|
61
60
|
temperature: Schema.number().min(0).max(2).default(0).description("Sampling temperature; 0 keeps the reviewer near-deterministic."),
|
|
62
61
|
policyText: Schema.string().description("Ruling policy appended to the reviewer prompt."),
|
|
@@ -1361,41 +1360,6 @@ var VerdictCache = class {
|
|
|
1361
1360
|
};
|
|
1362
1361
|
//#endregion
|
|
1363
1362
|
//#region src/subagent-reviewer.ts
|
|
1364
|
-
/**
|
|
1365
|
-
* The reviewer's requested structured output. An object-rooted schema is the
|
|
1366
|
-
* reliable channel: a subagent returns it validated rather than as text this
|
|
1367
|
-
* plugin has to salvage.
|
|
1368
|
-
*/
|
|
1369
|
-
const REVIEWER_OUTPUT_SCHEMA = {
|
|
1370
|
-
type: "object",
|
|
1371
|
-
properties: {
|
|
1372
|
-
decision: {
|
|
1373
|
-
type: "string",
|
|
1374
|
-
enum: [
|
|
1375
|
-
"allow",
|
|
1376
|
-
"deny",
|
|
1377
|
-
"uncertain"
|
|
1378
|
-
]
|
|
1379
|
-
},
|
|
1380
|
-
risk: {
|
|
1381
|
-
type: "string",
|
|
1382
|
-
enum: [
|
|
1383
|
-
"low",
|
|
1384
|
-
"medium",
|
|
1385
|
-
"high",
|
|
1386
|
-
"critical"
|
|
1387
|
-
]
|
|
1388
|
-
},
|
|
1389
|
-
reason: { type: "string" },
|
|
1390
|
-
suggestion: { type: "string" }
|
|
1391
|
-
},
|
|
1392
|
-
required: [
|
|
1393
|
-
"decision",
|
|
1394
|
-
"risk",
|
|
1395
|
-
"reason"
|
|
1396
|
-
],
|
|
1397
|
-
additionalProperties: false
|
|
1398
|
-
};
|
|
1399
1363
|
/** Join text blocks from a child's output, walking nested tool-result blocks. */
|
|
1400
1364
|
function childText(blocks) {
|
|
1401
1365
|
const out = [];
|
|
@@ -1440,8 +1404,6 @@ async function runSubagentReviewer(ctx, input) {
|
|
|
1440
1404
|
failure: "cancelled before dispatch",
|
|
1441
1405
|
durationMs: 0
|
|
1442
1406
|
};
|
|
1443
|
-
const schema = REVIEWER_OUTPUT_SCHEMA;
|
|
1444
|
-
assertObjectJsonSchema(schema);
|
|
1445
1407
|
const evidence = buildReviewerUserMessage({
|
|
1446
1408
|
toolName: input.evidence.toolName,
|
|
1447
1409
|
argumentsText: input.evidence.argumentsText,
|
|
@@ -1466,7 +1428,6 @@ async function runSubagentReviewer(ctx, input) {
|
|
|
1466
1428
|
"grep"
|
|
1467
1429
|
] },
|
|
1468
1430
|
maxDepth: 1,
|
|
1469
|
-
outputSchema: schema,
|
|
1470
1431
|
...input.provider === void 0 && input.model === void 0 ? {} : { agentOptions: {
|
|
1471
1432
|
...input.provider === void 0 ? {} : { provider: input.provider },
|
|
1472
1433
|
...input.model === void 0 ? {} : { model: input.model }
|
|
@@ -1555,6 +1516,8 @@ var ReviewRuntime = class {
|
|
|
1555
1516
|
reviewerSessions = /* @__PURE__ */ new Set();
|
|
1556
1517
|
/** Latest folded audit state per session, for the live view defaults. */
|
|
1557
1518
|
auditStates = /* @__PURE__ */ new WeakMap();
|
|
1519
|
+
/** Sessions whose committed log has already been replayed into that fold. */
|
|
1520
|
+
replayed = /* @__PURE__ */ new WeakSet();
|
|
1558
1521
|
/** Reused verdicts for identical actions, when the evidence allows it. */
|
|
1559
1522
|
cache;
|
|
1560
1523
|
/** Number of verdicts served from the cache since mount. */
|
|
@@ -1613,6 +1576,16 @@ var ReviewRuntime = class {
|
|
|
1613
1576
|
*/
|
|
1614
1577
|
observeEvent(session, event) {
|
|
1615
1578
|
this.sessions.observe(session, event);
|
|
1579
|
+
if (!this.replayed.has(session)) {
|
|
1580
|
+
this.replayed.add(session);
|
|
1581
|
+
let state = initAuditState();
|
|
1582
|
+
for (let seq = 0; seq < session.seq; seq += 1) {
|
|
1583
|
+
const committed = session.eventAt(seq);
|
|
1584
|
+
if (committed !== void 0) state = applyAuditEvent(state, committed, this.config);
|
|
1585
|
+
}
|
|
1586
|
+
this.auditStates.set(session, state);
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1616
1589
|
const previous = this.auditStates.get(session) ?? initAuditState();
|
|
1617
1590
|
this.auditStates.set(session, applyAuditEvent(previous, event, this.config));
|
|
1618
1591
|
}
|
|
@@ -1887,7 +1860,7 @@ var ReviewRuntime = class {
|
|
|
1887
1860
|
}
|
|
1888
1861
|
this.putRefusal(callId, {
|
|
1889
1862
|
marker: formatReviewMarker({
|
|
1890
|
-
reason: verdict?.reason ?? gate.note,
|
|
1863
|
+
reason: clampReason(verdict?.reason ?? (failure === void 0 ? gate.note : `${gate.note} — ${failure}`), this.config.reasonMaxChars),
|
|
1891
1864
|
...verdict?.suggestion === void 0 ? {} : { suggestion: verdict.suggestion },
|
|
1892
1865
|
...verdict?.risk === void 0 ? {} : { risk: verdict.risk },
|
|
1893
1866
|
reviewerRoute: `${route.provider}/${route.model}`,
|
|
@@ -2042,6 +2015,21 @@ var ReviewRuntime = class {
|
|
|
2042
2015
|
}
|
|
2043
2016
|
}
|
|
2044
2017
|
};
|
|
2018
|
+
/**
|
|
2019
|
+
* Bound a reason the plugin is about to publish.
|
|
2020
|
+
*
|
|
2021
|
+
* The marker rides the tool result, which is model context, so `reasonMaxChars`
|
|
2022
|
+
* has to hold here rather than only in the config schema. This is also what
|
|
2023
|
+
* keeps a provider's multi-line error digest from swallowing the guidance that
|
|
2024
|
+
* follows it.
|
|
2025
|
+
* @param reason - the assembled reason.
|
|
2026
|
+
* @param max - configured cap.
|
|
2027
|
+
* @returns the reason, truncated with an ellipsis when it exceeds the cap.
|
|
2028
|
+
*/
|
|
2029
|
+
function clampReason(reason, max) {
|
|
2030
|
+
if (reason.length <= max) return reason;
|
|
2031
|
+
return `${reason.slice(0, Math.max(0, max - 1))}…`;
|
|
2032
|
+
}
|
|
2045
2033
|
/** Join the text of a content-block list, walking nested tool-result blocks. */
|
|
2046
2034
|
function blocksToText(blocks) {
|
|
2047
2035
|
const out = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-approval-review",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Codex-style agent auto-approval for DeepSeek Harness: an independent reviewer model decides allow/deny on the approval answerer chain, fail-closed, with a per-decision rationale — refusals and allows alike — in a dedicated Approvals tab. · DSH 插件:Codex 风格的 Agent 自动审批,独立 reviewer 模型裁决、fail-closed、每次审批(放行与否决)都留下详细理由,并在独立的「审批」页签中展示。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|