feat(capability,gateway): 工具策略落盘 + 主代理代审真接线(裁决 1.1 核对/1.3/10.4.2/13.4.3)
CI / Backend (build / vet / test / cross-compile) (push) Has been cancelled
CI / Frontend (tsc + vite build) (push) Has been cancelled

- 1.1(核对):registry 默认两布尔 false 已一致,零代码改动(设计文档已按裁决改写)
- 1.3 + 10.4.2:新增迁移 0012_tool_policy(tool_policies 表,表名复数 + idx_tool_policies_name);
  新增 registry.PolicyStore/SQLitePolicyStore,启动 Load 覆盖注册初值、SetPolicy 先落库后改内存
  (落盘失败保持旧值并报错);面板保存路径不变但从此重启仍在
- 13.4.3:代审真接线 —— restrict.SensitiveCall 结构化判定(terminal / file write / http POST)、
  restrict.Gate/Reviewer/Resolver(无代审者或代审报错一律 fail-closed 拦截),
  组合根装进 loop.Options.Hooks 的 CallAdmit;gateway.RecordApproval 落 audit_logs
  (who=角色名 / what=approval.<工具> / result=granted|denied)并广播 approval_notice 帧
- 前端:代审小字「xxx 想要干什么,我审查后批准了」进聊天流顶部(帧→store→组件,文案单一来源
  复用 audit.Approval.Explain()),附纯 node 逻辑测试
- 复核:迁移在 data/cyrene.db **副本**上 up/down/up 各一次通过;TestRegistry_PolicyStoreSurvivesRestart
  与 TestAdminSetPolicySurvivesRestart 证明「重启后策略仍在」;terminal 触发代审 + 审计有行有端到端断言
This commit is contained in:
AskaEth committed 2026-10-04 12:30:16 +08:00
1 parent ed5bd92e1b
commit 63236c9b00
20 files changed
+1709 -24

No files matched your search

@@ -0,0 +1,27 @@
import { streamFrameApplies } from "../../streamScope";
import type { FrameEvent, FramePayload, StoreState } from "./shared";
/** 代审帧处理组:approval_notice —— 主代理代审小字(design/11 §4,裁决 13.4.3)。
* 敏感操作(如 terminal)经代审后后端广播该帧,`explain` 是后端产出的固定文案
* 「xxx 想要干什么,我审查后批准了 / 拒绝了」,前端原样渲染成聊天流里的一行小字。
* 命中返回 true,未命中返回 false 交给下一组。 */
export function handleApprovalEvent(ev: FrameEvent, r: FramePayload, s: StoreState): boolean {
if (ev.type !== "approval_notice") return false;
// 与流式帧同一作用域规则:别的会话发生的代审不写进当前会话的小字。
if (!streamFrameApplies(ev, r, s.activeSessionId)) return true;
// 没有文案就没有可渲染的小字 —— 丢掉这帧,而不是编造一句。
const explain = typeof r?.explain === "string" ? r.explain : "";
if (!explain) return true;
s.setApprovalNotice({
explain,
actor: typeof r?.actor === "string" ? r.actor : "",
tool: typeof r?.tool === "string" ? r.tool : "",
op: typeof r?.op === "string" ? r.op : "",
detail: typeof r?.detail === "string" ? r.detail : "",
// 后端只在整条代审失败/拒绝时发 allowed=false;缺字段按「已批准」处理,
// 与后端 RecordApproval 写入的布尔一致。
allowed: r?.allowed !== false,
at: typeof r?.at === "string" ? r.at : "",
});
return true;
}