@duke-dsh-plugins/dsh-agent-approval 1.8.2 → 1.10.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.
- package/README.md +9 -10
- package/client.js +176 -172
- package/index.js +478 -228
- package/package.json +2 -2
- package/typert.host.js +13 -5
package/README.md
CHANGED
|
@@ -23,11 +23,11 @@
|
|
|
23
23
|
| 🛡 **权限菜单第四项** | `/permission` 菜单新增 **自动审批** 预设;选中即开启,切到其他预设自动关闭,跨重启保持 |
|
|
24
24
|
| 🤖 新权限模式 | 开启后:沙箱基线固定 `workspace-write`,审批策略切到 `ask`(内部接管),**不再弹人工审批** |
|
|
25
25
|
| 🤖 自动裁决(默认 LLM 直连) | 每次提权请求由**一次 LLM 直连调用**裁决(v1.8.0 起默认):与子代理同一套审批人格 / 提示词 / 结构化裁决 `{decision, riskLevel, rationale}`,但**不创建审批子会话**(零上下文污染);设置页可切回「隔离子代理」(一次性 `spawn` 子代理:独立会话、零工具、只读材料) |
|
|
26
|
-
| 🕵️ 自动审查(逐调用,实验) |
|
|
26
|
+
| 🕵️ 自动审查(逐调用,实验) | 与自动审批**完全独立**的第二种模式(需先在设置页配置 Jev,配置即启用):**Full access 基线**,每个工具调用(含 PTC 内层)执行前经 Jev 判定一次,风险调用**直接拒绝、body 不执行、不转人工**(fail-closed);规则表与会话内信任先行短路降噪;`/agent-review on\|off` 或菜单「自动审查」开启。v1.9.0 起**有效代码展开**:命令引用的解释器脚本(node / python / bash / ps1)内容会被读出并放进裁决 state,agent 临时写的脚本按**实际代码**受审而不是只看命令行(仅读 workspace 内文件,越界只标注、永不读取) |
|
|
27
27
|
| ⛔ 风险即拒绝 | 破坏性 / 不可逆 / 越界(含修改操作系统或其他应用数据)/ 理由与实际命令不符 → 直接 `reject`;仅"安全、可逆、与任务相符、理由诚实"才 `approve`——项目自身的安装/部署脚本写其文档指定路径属任务所需 |
|
|
28
28
|
| 🔒 Fail-closed | 审批 Agent 启动失败、超时(可配 30s–600s)、结果不合法 → 一律按拒绝处理,绝不静默放行 |
|
|
29
|
-
| ⚙️ 审批模型可配置 | 设置页选择 Provider + Model
|
|
30
|
-
| ⚡ TypeSafe Jev
|
|
29
|
+
| ⚙️ 审批模型可配置 | 设置页选择 Provider + Model(**只能是 LLM 路由**),不选则固定用 **Harness 默认模型**(不跟随请求会话,口径稳定);选择与超时**持久保存**,重启不丢 |
|
|
30
|
+
| ⚡ TypeSafe Jev 决策模型(仅自动审查) | Jev(System One 结构化决策模型)是**自动审查模式专属**的判定后端:设置页「自动审查」卡片独立配置(API Key / Endpoint / 模型 / 置信度阈值),直连其 API 用类型化问题(Choice/Noul)毫秒级返回带校准概率的裁决;置信度低于阈值按 fail-closed 处理,审计理由由概率合成(也可设 `TYPESAFE_API_KEY`)。**v1.10.0 起不再是自动审批的判定选项**——Jev 的风险判断弱于 LLM,而提权正是人工审批原本要守住的路径 |
|
|
31
31
|
| 📋 审计记录(随会话) | 会话窗口顶部的**「审批」标签页**(轨迹旁)查看本会话全部审批:结论 / 风险等级 / 模型 / 耗时 / 理由;悬停看完整理由与**精确工具参数**;审批 Agent 的会话 id 可回溯完整推理;已批准行可一键**「加白」**存为放行规则。记录存在**会话存储目录内的独立文件**——随会话恢复,删除会话即随之删除 |
|
|
32
32
|
| 🔁 可逆开关 | 权限菜单「自动审批」预设、`/agent-approval on\|off` 命令两条等价路径;关闭时**恢复开启前的权限旋钮** |
|
|
33
33
|
|
|
@@ -42,10 +42,9 @@
|
|
|
42
42
|
└─ 本插件 prepend 抢占(先于人工弹窗 answerer)
|
|
43
43
|
├─ (默认)LLM 直连裁决(一次 stream 调用 · 同套人格/提示词/裁决格式 · 零子会话)
|
|
44
44
|
├─ (可选)spawn 审批 Agent(独立会话 · 零工具 · 结构化裁决 · 不会递归审批)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
└─ 超时/故障/低置信/取消 → fail-closed(按拒绝处理)
|
|
45
|
+
│ ├─ approve → allowed-once(该次放行)
|
|
46
|
+
│ ├─ reject → rejected(风险操作,最终拒绝)
|
|
47
|
+
│ └─ 超时/故障/低置信/取消 → fail-closed(按拒绝处理)
|
|
49
48
|
└─ 记入审计(会话目录内的独立文件,「审批」标签页可见)
|
|
50
49
|
```
|
|
51
50
|
|
|
@@ -81,15 +80,15 @@ dsh plugin --profile web add https://github.com/MoonlitDropOfBlood/dsh-agent-app
|
|
|
81
80
|
1. **开启**:在 `/permission` 菜单选 **自动审批**,或输入 `/agent-approval on`。
|
|
82
81
|
2. **自动裁决**:之后该会话里的提权请求(例如命令被沙箱拒绝后带 `sandbox_permissions` 的重试)不再弹窗,由审批 Agent 在后台裁决并放行/拒绝。
|
|
83
82
|
3. **审计**:会话窗口顶部的**「审批」标签页**(轨迹旁)查看本会话的审批记录;悬停"审批理由"看完整理由与工具参数;已批准行可「加白」存为放行规则。记录存在会话存储目录内的独立文件,删除会话即随之删除;v1.4 的旧全局记录用 `node scripts/migrate-records.mjs` 一次性迁移(`--dry-run` 预览)。
|
|
84
|
-
4.
|
|
85
|
-
5.
|
|
83
|
+
4. **配置自动审批**:设置 → **自动审批** 设置审批模型(Harness 默认模型或指定 Provider/Model——**只能是 LLM 路由**)、**裁决方式**(LLM 直连 / 隔离子代理)、审批超时与放行/拒绝规则。
|
|
84
|
+
5. **配置自动审查(独立)**:设置页「自动审查」卡片填 Jev 的 API Key(可调 Endpoint / 模型版本 / 置信度阈值)——**保存即启用**逐调用审查(新开会话自动进入,菜单出现 **自动审查** 预设,或执行 `/agent-review on`):该会话切到 Full access 基线,**每个工具调用**执行前经 Jev 判定,风险调用直接拒绝(不转人工);关闭恢复原旋钮。
|
|
86
85
|
6. **关闭**:菜单切回其他预设,或 `/agent-approval off`,恢复开启前的沙箱模式与审批策略。
|
|
87
86
|
|
|
88
87
|
## 目录结构
|
|
89
88
|
|
|
90
89
|
```
|
|
91
90
|
dsh-agent-approval/
|
|
92
|
-
├── index.js # Host 半:AgentApprovalService(审批瀑布抢占 + LLM
|
|
91
|
+
├── index.js # Host 半:AgentApprovalService(审批瀑布抢占 + LLM 直连/子代理裁决 + Jev 逐调用审查 + 审计)
|
|
93
92
|
├── client.js # Client 半:设置页「自动审批」+ 会话「审批」审计标签页 bundle
|
|
94
93
|
├── typert.host.js # Typert Host manifest(agentApproval 10 个 Remote 方法的描述)
|
|
95
94
|
├── cordis.patch.yml # dsh bundle patch(挂载行 + permission 预设表覆盖)
|
package/client.js
CHANGED
|
@@ -11,12 +11,16 @@
|
|
|
11
11
|
* session — restored with it after a restart, gone when the session is
|
|
12
12
|
* deleted. Rows offer the one-click「加白」rule shortcut.
|
|
13
13
|
*
|
|
14
|
-
* 2. A "自动审批" page in the Settings panel (`settings.section`)
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
14
|
+
* 2. A "自动审批" page in the Settings panel (`settings.section`). It
|
|
15
|
+
* presents the two modes as SEPARATE blocks since v1.10.0:
|
|
16
|
+
* - 自动审批: approval model picker (harness provider + model, or the
|
|
17
|
+
* harness default), judge invocation mode (direct LLM vs isolated
|
|
18
|
+
* subagent — an LLM either way, Jev is not selectable here), the
|
|
19
|
+
* fail-closed timeout, enabled sessions and the allow/deny rules.
|
|
20
|
+
* - 自动审查: the per-call review mode's own TypeSafe Jev settings
|
|
21
|
+
* (API key / model / endpoint / confidence gate) plus the global
|
|
22
|
+
* "new sessions enter 自动审查" switch. Configuring Jev here is what
|
|
23
|
+
* enables the mode; the approval judge is untouched by it.
|
|
20
24
|
*
|
|
21
25
|
* Session-level on/off lives in the /permission menu (the "自动审批"
|
|
22
26
|
* preset, registered by the package's cordis.patch.yml bundle patch) and the
|
|
@@ -106,7 +110,7 @@ window.__ModuleLoader__.load({
|
|
|
106
110
|
would bounce the selection back anyway. */
|
|
107
111
|
[data-dsh-agent-approval-review-gate="off"] [data-dsh-agent-approval-review-item]{display:none}
|
|
108
112
|
[data-dsh-agent-approval-review-item]::before{content:'';flex:none;width:16px;height:16px;background:currentColor;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z' stroke='black' stroke-width='1.31831' stroke-linejoin='round'/%3E%3Cpath d='M8 3.2L9.1 5.9L11.8 7L9.1 8.1L8 10.8L6.9 8.1L4.2 7L6.9 5.9Z' fill='black'/%3E%3C/svg%3E") center/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='M8.20554 0.899994L14.7901 3.36857V7.01026C14.7901 12 11.0466 14.2103 8.20554 15.3C5.36446 14.2103 1.62012 12 1.62012 7.01026V3.36857L8.20554 0.899994Z' stroke='black' stroke-width='1.31831' stroke-linejoin='round'/%3E%3Cpath d='M8 3.2L9.1 5.9L11.8 7L9.1 8.1L8 10.8L6.9 8.1L4.2 7L6.9 5.9Z' fill='black'/%3E%3C/svg%3E") center/contain no-repeat}
|
|
109
|
-
.aapr-mode-review{margin
|
|
113
|
+
.aapr-mode-review{display:inline-block;margin:2px 0 2px 6px;padding:1px 6px;font-size:10px;line-height:1.5;border:1px solid var(--dsw-alias-border-l2);border-radius:4px;background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-secondary);white-space:nowrap;vertical-align:1px}
|
|
110
114
|
`;
|
|
111
115
|
|
|
112
116
|
// ---- Settings nav icon --------------------------------------------------
|
|
@@ -551,8 +555,9 @@ window.__ModuleLoader__.load({
|
|
|
551
555
|
const setReviewDefault = reviewDefaultSlot[1];
|
|
552
556
|
const timeoutSlot = React.useState("");
|
|
553
557
|
const setTimeoutDraft = timeoutSlot[1];
|
|
554
|
-
// Jev backend drafts
|
|
555
|
-
//
|
|
558
|
+
// Jev backend drafts — the 自动审查 card's own configuration, shown
|
|
559
|
+
// unconditionally since v1.10.0 (it no longer hangs off a judge
|
|
560
|
+
// provider selection).
|
|
556
561
|
const jevKeySlot = React.useState("");
|
|
557
562
|
const setJevKey = jevKeySlot[1];
|
|
558
563
|
const jevEndpointSlot = React.useState("");
|
|
@@ -667,9 +672,19 @@ window.__ModuleLoader__.load({
|
|
|
667
672
|
}
|
|
668
673
|
remote
|
|
669
674
|
.setJevConfig({ apiKey: jevKey, endpoint: jevEndpoint, model: jevModel, confidence: conf })
|
|
670
|
-
.then(() => {
|
|
675
|
+
.then((res) => {
|
|
676
|
+
const v = pick(res) || {};
|
|
677
|
+
setReviewGate(v.reviewAvailable === true);
|
|
678
|
+
setReviewDefault(v.reviewDefault === true);
|
|
671
679
|
refresh();
|
|
672
|
-
|
|
680
|
+
// Configuring Jev opens the review gate; the Host turns the
|
|
681
|
+
// per-call review default on at that exact moment (v1.10.0).
|
|
682
|
+
setNote(
|
|
683
|
+
v.reviewAvailable === true
|
|
684
|
+
? "Jev 配置已保存:自动审查已就绪" +
|
|
685
|
+
(v.reviewDefault === true ? ",新开会话自动进入逐调用审查" : "")
|
|
686
|
+
: "Jev 配置已保存,但 API Key 仍为空:自动审查保持关闭",
|
|
687
|
+
);
|
|
673
688
|
})
|
|
674
689
|
.catch((e) => setNote("保存失败:" + (e && e.message ? e.message : String(e))));
|
|
675
690
|
};
|
|
@@ -735,30 +750,22 @@ window.__ModuleLoader__.load({
|
|
|
735
750
|
.catch(() => {});
|
|
736
751
|
};
|
|
737
752
|
|
|
738
|
-
//
|
|
739
|
-
//
|
|
753
|
+
// v1.10.0: the harness model directory is the WHOLE list — Jev is no
|
|
754
|
+
// longer a judge provider (it judges the per-call review mode only),
|
|
755
|
+
// so there is no synthetic "typesafe" entry to prepend.
|
|
740
756
|
const providerOptions = [{ id: "", name: "默认(Harness 默认模型)" }].concat(
|
|
741
|
-
[{ id: "typesafe", name: "TypeSafe Jev(决策模型·直连 API)" }],
|
|
742
757
|
dir ? dir.providers : [],
|
|
743
758
|
);
|
|
744
|
-
const modelOptions =
|
|
745
|
-
provider ===
|
|
746
|
-
|
|
747
|
-
{ provider: "typesafe", id: "jev-latest", name: "jev-latest(跟随最新版本)" },
|
|
748
|
-
{ provider: "typesafe", id: "jev-1.13.0", name: "jev-1.13.0(锁定版本)" },
|
|
749
|
-
]
|
|
750
|
-
: [{ provider: "", id: "", name: "默认(Harness 默认模型)" }].concat(
|
|
751
|
-
dir && dir.models ? dir.models.filter((m) => m.provider === provider) : [],
|
|
752
|
-
);
|
|
759
|
+
const modelOptions = [{ provider: "", id: "", name: "默认(Harness 默认模型)" }].concat(
|
|
760
|
+
dir && dir.models ? dir.models.filter((m) => m.provider === provider) : [],
|
|
761
|
+
);
|
|
753
762
|
const defaultHint =
|
|
754
|
-
|
|
755
|
-
? "
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
dir.defaultSelection.model
|
|
761
|
-
: "未配置时使用 Harness 默认模型路由";
|
|
763
|
+
dir && dir.defaultSelection
|
|
764
|
+
? "未配置时使用 Harness 默认模型;当前默认路由:" +
|
|
765
|
+
dir.defaultSelection.provider +
|
|
766
|
+
" / " +
|
|
767
|
+
dir.defaultSelection.model
|
|
768
|
+
: "未配置时使用 Harness 默认模型路由";
|
|
762
769
|
|
|
763
770
|
return h(
|
|
764
771
|
"div",
|
|
@@ -766,13 +773,17 @@ window.__ModuleLoader__.load({
|
|
|
766
773
|
h(
|
|
767
774
|
"div",
|
|
768
775
|
{ className: "aapr-card" },
|
|
769
|
-
h("h3", null, "
|
|
776
|
+
h("h3", null, "自动审批与自动审查"),
|
|
770
777
|
h(
|
|
771
778
|
"div",
|
|
772
779
|
{ className: "aapr-muted" },
|
|
773
|
-
"
|
|
780
|
+
"两种互相独立的权限模式。",
|
|
781
|
+
h("br", null),
|
|
782
|
+
"① 自动审批:workspace-write 基线;工具请求提权时由 LLM 审批 Agent 自动裁决(v1.10.0 起判定模型只能是 LLM)。在输入框 /permission 菜单选择「自动审批」预设,或执行 /agent-approval on|off 为会话开启。",
|
|
774
783
|
h("br", null),
|
|
775
|
-
"
|
|
784
|
+
"② 自动审查:danger-full-access 基线;每个工具调用执行前经 TypeSafe Jev 判定一次(见下方独立卡片)。在 /permission 菜单选择「自动审查」预设,或执行 /agent-review on|off。",
|
|
785
|
+
h("br", null),
|
|
786
|
+
"每个会话的审批审计记录在该会话窗口顶部的「审批」标签页(轨迹旁),随会话保存。",
|
|
776
787
|
),
|
|
777
788
|
note !== "" ? h("div", { className: "aapr-muted" }, note) : null,
|
|
778
789
|
),
|
|
@@ -794,7 +805,7 @@ window.__ModuleLoader__.load({
|
|
|
794
805
|
value: provider,
|
|
795
806
|
onChange: (e) => {
|
|
796
807
|
setProvider(e.target.value);
|
|
797
|
-
setModel(
|
|
808
|
+
setModel("");
|
|
798
809
|
},
|
|
799
810
|
},
|
|
800
811
|
providerOptions.map((p) =>
|
|
@@ -812,7 +823,7 @@ window.__ModuleLoader__.load({
|
|
|
812
823
|
className: "aapr-select",
|
|
813
824
|
value: model,
|
|
814
825
|
onChange: (e) => setModel(e.target.value),
|
|
815
|
-
disabled: dir === null
|
|
826
|
+
disabled: dir === null,
|
|
816
827
|
},
|
|
817
828
|
modelOptions.map((m) =>
|
|
818
829
|
h("option", { key: m.provider + "/" + m.id, value: m.id }, m.id === "" ? m.name : m.name + "(" + m.id + ")"),
|
|
@@ -822,144 +833,33 @@ window.__ModuleLoader__.load({
|
|
|
822
833
|
h(ui.Button, { variant: "primary", size: "sm", onClick: saveModel }, "保存"),
|
|
823
834
|
),
|
|
824
835
|
h("div", { className: "aapr-muted" }, defaultHint),
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
"裁决方式:",
|
|
833
|
-
h(
|
|
834
|
-
"select",
|
|
835
|
-
{
|
|
836
|
-
className: "aapr-select",
|
|
837
|
-
value: judgeMode,
|
|
838
|
-
onChange: (e) => saveJudgeMode(e.target.value),
|
|
839
|
-
},
|
|
840
|
-
h("option", { value: "llm" }, "LLM 直连(默认,不创建子会话)"),
|
|
841
|
-
h("option", { value: "subagent" }, "隔离子代理(legacy,创建审批子会话)"),
|
|
842
|
-
),
|
|
843
|
-
),
|
|
844
|
-
)
|
|
845
|
-
: null,
|
|
846
|
-
provider !== "typesafe"
|
|
847
|
-
? h(
|
|
848
|
-
"div",
|
|
849
|
-
{ className: "aapr-muted" },
|
|
850
|
-
"LLM 直连与隔离子代理是同一套审批人格、提示词与裁决格式({decision, riskLevel, rationale}),仅调用方式不同:前者一次直连模型调用完成裁决、不启动审批子代理(零上下文污染);后者每次裁决创建一个独立子会话(v1.8.0 前的唯一行为)。TypeSafe Jev 后端不受此设置影响。",
|
|
851
|
-
)
|
|
852
|
-
: null,
|
|
853
|
-
),
|
|
854
|
-
provider === "typesafe"
|
|
855
|
-
? h(
|
|
856
|
-
"div",
|
|
857
|
-
{ className: "aapr-card" },
|
|
858
|
-
h("h3", null, "TypeSafe Jev 配置"),
|
|
859
|
-
h(
|
|
860
|
-
"div",
|
|
861
|
-
{ className: "aapr-muted" },
|
|
862
|
-
"Jev 是结构化决策模型(System One):审批时直连 TypeSafe API,不创建审批子会话,毫秒级返回带校准概率的裁决。审计「理由」由概率分布合成(Jev 本身不生成文字);置信度低于阈值时按 fail-closed 处理(记 unavailable,不批准也不记拒绝)。对中文任务上下文的准确率略低于英语。API Key 明文保存在本机 config.json;留空时使用环境变量 TYPESAFE_API_KEY。",
|
|
863
|
-
),
|
|
864
|
-
h(
|
|
865
|
-
"div",
|
|
866
|
-
{ className: "aapr-row" },
|
|
867
|
-
h(
|
|
868
|
-
"label",
|
|
869
|
-
null,
|
|
870
|
-
"API Key:",
|
|
871
|
-
h("input", {
|
|
872
|
-
className: "aapr-input",
|
|
873
|
-
type: "password",
|
|
874
|
-
placeholder: "TYPESAFE_API_KEY",
|
|
875
|
-
value: jevKey,
|
|
876
|
-
onChange: (e) => setJevKey(e.target.value),
|
|
877
|
-
}),
|
|
878
|
-
),
|
|
879
|
-
h(
|
|
880
|
-
"label",
|
|
881
|
-
null,
|
|
882
|
-
"模型:",
|
|
883
|
-
h("input", {
|
|
884
|
-
className: "aapr-input",
|
|
885
|
-
placeholder: "jev-latest",
|
|
886
|
-
value: jevModel,
|
|
887
|
-
onChange: (e) => setJevModel(e.target.value),
|
|
888
|
-
}),
|
|
889
|
-
),
|
|
890
|
-
),
|
|
891
|
-
h(
|
|
892
|
-
"div",
|
|
893
|
-
{ className: "aapr-row" },
|
|
894
|
-
h(
|
|
895
|
-
"label",
|
|
896
|
-
null,
|
|
897
|
-
"Endpoint:",
|
|
898
|
-
h("input", {
|
|
899
|
-
className: "aapr-input aapr-input-wide",
|
|
900
|
-
placeholder: "https://api.typesafe.ai/v1/systemone",
|
|
901
|
-
value: jevEndpoint,
|
|
902
|
-
onChange: (e) => setJevEndpoint(e.target.value),
|
|
903
|
-
}),
|
|
904
|
-
),
|
|
905
|
-
h(
|
|
906
|
-
"label",
|
|
907
|
-
null,
|
|
908
|
-
"置信度阈值:",
|
|
909
|
-
h("input", {
|
|
910
|
-
className: "aapr-input",
|
|
911
|
-
type: "number",
|
|
912
|
-
step: "0.05",
|
|
913
|
-
min: "0.01",
|
|
914
|
-
max: "0.99",
|
|
915
|
-
value: jevConf,
|
|
916
|
-
onChange: (e) => setJevConf(e.target.value),
|
|
917
|
-
}),
|
|
918
|
-
),
|
|
919
|
-
h(ui.Button, { variant: "primary", size: "sm", onClick: saveJev }, "保存"),
|
|
920
|
-
),
|
|
921
|
-
)
|
|
922
|
-
: null,
|
|
923
|
-
provider === "typesafe"
|
|
924
|
-
? h(
|
|
925
|
-
"div",
|
|
926
|
-
{ className: "aapr-card" },
|
|
927
|
-
h("h3", null, "自动审查(逐调用审查)"),
|
|
928
|
-
h(
|
|
929
|
-
"div",
|
|
930
|
-
{ className: "aapr-row" },
|
|
931
|
-
h(
|
|
932
|
-
"label",
|
|
933
|
-
null,
|
|
934
|
-
"逐调用审查:",
|
|
935
|
-
h(
|
|
936
|
-
"select",
|
|
937
|
-
{
|
|
938
|
-
className: "aapr-select",
|
|
939
|
-
value: reviewDefault ? "on" : "off",
|
|
940
|
-
disabled: state !== null && state.reviewAvailable !== true,
|
|
941
|
-
onChange: (e) => saveReviewDefault(e.target.value === "on"),
|
|
942
|
-
},
|
|
943
|
-
h("option", { value: "on" }, "开(新会话自动审查每个工具调用)"),
|
|
944
|
-
h("option", { value: "off" }, "关(新会话按默认预设)"),
|
|
945
|
-
),
|
|
946
|
-
),
|
|
947
|
-
),
|
|
836
|
+
h(
|
|
837
|
+
"div",
|
|
838
|
+
{ className: "aapr-row" },
|
|
839
|
+
h(
|
|
840
|
+
"label",
|
|
841
|
+
null,
|
|
842
|
+
"裁决方式:",
|
|
948
843
|
h(
|
|
949
|
-
"
|
|
950
|
-
{
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
: state.reviewAvailable === true
|
|
958
|
-
? "当前状态:Jev 判定可用。"
|
|
959
|
-
: "当前状态:Jev 判定不可用(缺 API Key),开关已禁用,新会话不会自动审查。",
|
|
844
|
+
"select",
|
|
845
|
+
{
|
|
846
|
+
className: "aapr-select",
|
|
847
|
+
value: judgeMode,
|
|
848
|
+
onChange: (e) => saveJudgeMode(e.target.value),
|
|
849
|
+
},
|
|
850
|
+
h("option", { value: "llm" }, "LLM 直连(默认,不创建子会话)"),
|
|
851
|
+
h("option", { value: "subagent" }, "隔离子代理(legacy,创建审批子会话)"),
|
|
960
852
|
),
|
|
961
|
-
)
|
|
962
|
-
|
|
853
|
+
),
|
|
854
|
+
),
|
|
855
|
+
h(
|
|
856
|
+
"div",
|
|
857
|
+
{ className: "aapr-muted" },
|
|
858
|
+
"LLM 直连与隔离子代理是同一套审批人格、提示词与裁决格式({decision, riskLevel, rationale}),仅调用方式不同:前者一次直连模型调用完成裁决、不启动审批子代理(零上下文污染);后者每次裁决创建一个独立子会话(v1.8.0 前的唯一行为)。",
|
|
859
|
+
h("br", null),
|
|
860
|
+
"自动审批的判定模型只能是 LLM:TypeSafe Jev 的结构化决策在此处不可选(它的风险判断比 LLM 弱,而这里正是人工审批原本要守住的路径)。Jev 只用于下方的「自动审查」模式,两者互不影响。",
|
|
861
|
+
),
|
|
862
|
+
),
|
|
963
863
|
h(
|
|
964
864
|
"div",
|
|
965
865
|
{ className: "aapr-card" },
|
|
@@ -977,6 +877,110 @@ window.__ModuleLoader__.load({
|
|
|
977
877
|
h(ui.Button, { variant: "primary", size: "sm", onClick: saveTimeout }, "保存"),
|
|
978
878
|
),
|
|
979
879
|
),
|
|
880
|
+
h(
|
|
881
|
+
"div",
|
|
882
|
+
{ className: "aapr-card" },
|
|
883
|
+
h("h3", null, "自动审查(逐调用审查)"),
|
|
884
|
+
h(
|
|
885
|
+
"div",
|
|
886
|
+
{ className: "aapr-muted" },
|
|
887
|
+
"自动审查是独立的第二种权限模式,与上面的自动审批互不相关:它不看提权,而是以 danger-full-access 为基线,对每个工具调用(含 PTC 内层调用,外层 run_code 传输除外)在执行前用 TypeSafe Jev 判定一次——风险调用直接拒绝、body 不执行、不转人工(fail-closed,拒绝即最终结论)。",
|
|
888
|
+
h("br", null),
|
|
889
|
+
"Jev 是结构化决策模型(System One):直连 TypeSafe API,不创建审批子会话,毫秒级返回带校准概率的裁决。审计「理由」由概率分布合成(Jev 本身不生成文字);置信度低于阈值时按 fail-closed 处理(记 unavailable,不放行也不记拒绝)。对中文任务上下文的准确率略低于英语。API Key 明文保存在本机 config.json;留空时使用环境变量 TYPESAFE_API_KEY。",
|
|
890
|
+
h("br", null),
|
|
891
|
+
"保存一个可用的 API Key 即完成配置,自动审查随之启用(新开会话自动进入);下面同一张卡片可以随时关掉它。",
|
|
892
|
+
),
|
|
893
|
+
h(
|
|
894
|
+
"div",
|
|
895
|
+
{ className: "aapr-row" },
|
|
896
|
+
h(
|
|
897
|
+
"label",
|
|
898
|
+
null,
|
|
899
|
+
"逐调用审查:",
|
|
900
|
+
h(
|
|
901
|
+
"select",
|
|
902
|
+
{
|
|
903
|
+
className: "aapr-select",
|
|
904
|
+
value: reviewDefault ? "on" : "off",
|
|
905
|
+
disabled: state !== null && state.reviewAvailable !== true,
|
|
906
|
+
onChange: (e) => saveReviewDefault(e.target.value === "on"),
|
|
907
|
+
},
|
|
908
|
+
h("option", { value: "on" }, "开(新会话自动审查每个工具调用)"),
|
|
909
|
+
h("option", { value: "off" }, "关(新会话按默认预设)"),
|
|
910
|
+
),
|
|
911
|
+
),
|
|
912
|
+
),
|
|
913
|
+
h(
|
|
914
|
+
"div",
|
|
915
|
+
{ className: "aapr-row" },
|
|
916
|
+
h(
|
|
917
|
+
"label",
|
|
918
|
+
null,
|
|
919
|
+
"API Key:",
|
|
920
|
+
h("input", {
|
|
921
|
+
className: "aapr-input",
|
|
922
|
+
type: "password",
|
|
923
|
+
placeholder: "TYPESAFE_API_KEY",
|
|
924
|
+
value: jevKey,
|
|
925
|
+
onChange: (e) => setJevKey(e.target.value),
|
|
926
|
+
}),
|
|
927
|
+
),
|
|
928
|
+
h(
|
|
929
|
+
"label",
|
|
930
|
+
null,
|
|
931
|
+
"模型:",
|
|
932
|
+
h("input", {
|
|
933
|
+
className: "aapr-input",
|
|
934
|
+
placeholder: "jev-latest",
|
|
935
|
+
value: jevModel,
|
|
936
|
+
onChange: (e) => setJevModel(e.target.value),
|
|
937
|
+
}),
|
|
938
|
+
),
|
|
939
|
+
),
|
|
940
|
+
h(
|
|
941
|
+
"div",
|
|
942
|
+
{ className: "aapr-row" },
|
|
943
|
+
h(
|
|
944
|
+
"label",
|
|
945
|
+
null,
|
|
946
|
+
"Endpoint:",
|
|
947
|
+
h("input", {
|
|
948
|
+
className: "aapr-input aapr-input-wide",
|
|
949
|
+
placeholder: "https://api.typesafe.ai/v1/systemone",
|
|
950
|
+
value: jevEndpoint,
|
|
951
|
+
onChange: (e) => setJevEndpoint(e.target.value),
|
|
952
|
+
}),
|
|
953
|
+
),
|
|
954
|
+
h(
|
|
955
|
+
"label",
|
|
956
|
+
null,
|
|
957
|
+
"置信度阈值:",
|
|
958
|
+
h("input", {
|
|
959
|
+
className: "aapr-input",
|
|
960
|
+
type: "number",
|
|
961
|
+
step: "0.05",
|
|
962
|
+
min: "0.01",
|
|
963
|
+
max: "0.99",
|
|
964
|
+
value: jevConf,
|
|
965
|
+
onChange: (e) => setJevConf(e.target.value),
|
|
966
|
+
}),
|
|
967
|
+
),
|
|
968
|
+
h(ui.Button, { variant: "primary", size: "sm", onClick: saveJev }, "保存"),
|
|
969
|
+
),
|
|
970
|
+
h(
|
|
971
|
+
"div",
|
|
972
|
+
{ className: "aapr-muted" },
|
|
973
|
+
"规则表与会话内信任缓存先行短路降噪;命中拒绝规则、Jev 判拒、低置信、超时、网络故障一律拒绝该调用(工具卡片显示 AGENT_REVIEW_DENIED 详情与风险理由)。",
|
|
974
|
+
h("br", null),
|
|
975
|
+
"已存在的会话不受此开关影响,可用 /permission 菜单「自动审查」或 /agent-review on|off 单独切换。审计逐调用记录在「审批」标签页(工具列标注「逐调用」)。",
|
|
976
|
+
h("br", null),
|
|
977
|
+
state === null
|
|
978
|
+
? "状态加载中…"
|
|
979
|
+
: state.reviewAvailable === true
|
|
980
|
+
? "当前状态:Jev 判定可用,自动审查已就绪。"
|
|
981
|
+
: "当前状态:Jev 判定不可用(缺 API Key),开关已禁用,新会话不会自动审查。",
|
|
982
|
+
),
|
|
983
|
+
),
|
|
980
984
|
h(
|
|
981
985
|
"div",
|
|
982
986
|
{ className: "aapr-card" },
|