6.0 KiB
ADR-0133:不可变 Tool Registry 与 Policy-fenced Invocation Plan
- 状态:Accepted(profile-neutral ToolDefinition registry、受限 JSON Schema、 Project Policy 聚合、Agent Approval 判定和 digest-bound invocation plan 已实现; handler、StepRun、preview、ApprovalRequest publisher、MCP/HTTP adapter 与生产组合仍 未开放)
- 日期:2026-07-24
- 关联 RFC:QL-RFC-0001 D-08、D-09、D-131
背景
AI Agent、MCP、Package 和内置功能都需要调用 Tool。如果每个入口各自:
- 解析一份 Tool schema;
- 猜测权限和审批要求;
- 直接持有 handler;
- 在运行期注册或扫描 Tool;
就会出现权限旁路、不同输入语义、插件移除后的行为漂移和低配设备常驻开销。
现有 ProjectPolicyEngine 已经提供稳定 Principal、Project permission 和
Project/RoleBinding version fence;Tool 层必须复用它,不能建立第二套 RBAC。
决策
1. Tool Registry 留在 @qinglong/runtime-core
新增 tool-registry subpath,不新增 workspace package或第三方依赖。Registry 在受信
composition root 中一次性接收 0–128 个 definition,构造后冻结,不提供 register、
目录扫描、watcher、timer、socket、数据库或 handler 注入。
同一 (name, version) 只能出现一次。Tool name 必须是小写分段标识,version 必须
是 canonical SemVer。调用必须精确指定 version,不提供 latest、wildcard 或运行期
fallback。
2. 首版只实现有界 JSON Schema 子集
QL3 不在核心中接受任意 JSON Schema draft。首版只允许:
null、boolean;- 有显式
maxLength的string,可选minLength和有界 enum; - 有显式安全上下界的
number、integer; - 有显式
maxItems的 array,可选minItems、uniqueItems; additionalProperties: false的 object,显式 properties 和 required。
拒绝 $ref、oneOf、anyOf、任意 regex、默认值、coercion、未知 keyword 和
开放 additional properties。Schema 深度最多 8、节点最多 256、object properties
最多 64、enum 最多 64、array 最多 256 项。
Tool input 根必须是 object。canonical input 最大 64 KiB,output 最大 256 KiB。
没有 outputSchema 的 Tool 只能返回 null,不能把“未声明 output”解释为任意输出。
3. Registry 不拥有执行能力
Definition 只包含:
- name、version、description;
- input/output schema;
read | write | execute | externaleffect;low | medium | high | criticalrisk;- 最多 16 个 Project permission;
- 1–3600 秒 timeout。
Registry 只规范化 definition/input/output 和生成 invocation plan。Definition、
Registry 和 plan 均不包含 execute、handler、数据库 service 或网络 client。
Package、MCP 或 Agent 不能通过注册 Definition 把代码注入控制面。
4. 每次调用先 Policy,后解析输入
prepareToolInvocation 固定执行:
- 精确解析 request envelope 和 Tool identity;
- 验证当前 Principal;
- 对
tool.call:{name}和 Definition 的每项 required permission 调用既有ProjectPolicyEngine.authorize端口; - 任一 deny 立即停止,且不解析 Tool input;
- 所有非 deny decision 必须包含完全相同的 Project/RoleBinding fence;
- 任一 decision 为
require_approval时生成approval_requiredplan; - 只有之后才按 schema 规范化 input,并生成 input/action SHA-256。
Policy 存储异常、畸形 decision、空 fence 或不同 fence 混用全部失败关闭。这样多 permission Tool 不能把不同 Project 版本或 revoke 前后的授权拼成一个调用。
5. Action digest 是 Approval/Dispatch 的稳定输入,不是执行授权
action digest 绑定:
qinglong/tool-invocation@v1;- Project、稳定 actor subject;
- exact Tool name/version;
tool.call:{name}和 required permissions;- effect、risk、timeout;
- canonical input digest。
它不保存 input、Prompt、Secret 或 handler,也不等于已审批。approval_required
仍必须由后续 preview builder 产生 preview digest,再进入既有
ApprovalRequest/immutable dispatch/start barrier。ready plan 也必须由受审 handler
registry、StepRun/Trace/Audit 组合消费;本 ADR 不提供直接 execute seam。
影响
- AI Agent 通过真实 Project Policy 调用任意 Tool 时,
tool.call:*继续返回require_approval。 - MCP、Package 和内置 Tool 共用同一 schema、permission 和 fence 语义。
- edge 只在显式 Tool 调用时支付 schema/input 规范化成本;禁用 Tool 时零 timer、 socket、数据库和后台进程。
packages/数量不增加,runtime-core 只增加一个源码文件和 subpath。
未完成边界
以下内容仍需独立闭环:
- built-in/package Tool handler registry;
- ADR-0150 已完成 Package Tool JSON 到 ToolDefinition 的来源/PackageLock/ generation 绑定;全部 active generation 的全局 immutable snapshot 仍未完成;
- preview/dry-run 和 ApprovalRequest publisher;
- Approved Action handler、receipt/evidence provider;
- StepRun、Trace、redaction 和 durable audit;
- MCP Server、MCP Client、HTTP/API 与 Agent adapter;
- Tool version retirement 与历史 Run/PackageLock 解析。
在这些能力完成前,Tool Registry 保持 production unreachable。
验证
测试必须覆盖:
- immutable registry、exact version 和无 runtime register;
- root/subpath 导出一致;
- JSON Schema exact subset;
- schema depth/node/property 上限;
- canonical input/output、未知字段与 unique array;
- 无 output schema 时只允许 null;
- 同一 Policy fence 的 digest-bound ready plan;
- 真实 Project Policy 下 Agent Tool call 需要审批;
- deny 在 input 解析前短路;
- Policy unavailable、畸形 decision 和 mixed fence;
- 过期 Principal 与扩展 request 拒绝;
- canonical envelope byte budget;
- 无 filesystem/process/network/timer/execute authority。