Files
qinglong/docs/adr/ADR-0133-immutable-tool-registry-and-policy-fenced-invocation-plan.md

6.0 KiB
Raw Permalink Blame History

ADR-0133:不可变 Tool Registry 与 Policy-fenced Invocation Plan

  • 状态:Acceptedprofile-neutral ToolDefinition registry、受限 JSON Schema、 Project Policy 聚合、Agent Approval 判定和 digest-bound invocation plan 已实现; handler、StepRun、preview、ApprovalRequest publisher、MCP/HTTP adapter 与生产组合仍 未开放)
  • 日期:2026-07-24
  • 关联 RFCQL-RFC-0001 D-08、D-09、D-131

背景

AI Agent、MCP、Package 和内置功能都需要调用 Tool。如果每个入口各自:

  • 解析一份 Tool schema
  • 猜测权限和审批要求;
  • 直接持有 handler
  • 在运行期注册或扫描 Tool

就会出现权限旁路、不同输入语义、插件移除后的行为漂移和低配设备常驻开销。 现有 ProjectPolicyEngine 已经提供稳定 Principal、Project permission 和 Project/RoleBinding version fenceTool 层必须复用它,不能建立第二套 RBAC。

决策

1. Tool Registry 留在 @qinglong/runtime-core

新增 tool-registry subpath,不新增 workspace package或第三方依赖。Registry 在受信 composition root 中一次性接收 0128 个 definition,构造后冻结,不提供 register、 目录扫描、watcher、timer、socket、数据库或 handler 注入。

同一 (name, version) 只能出现一次。Tool name 必须是小写分段标识,version 必须 是 canonical SemVer。调用必须精确指定 version,不提供 latest、wildcard 或运行期 fallback。

2. 首版只实现有界 JSON Schema 子集

QL3 不在核心中接受任意 JSON Schema draft。首版只允许:

  • nullboolean
  • 有显式 maxLengthstring,可选 minLength 和有界 enum
  • 有显式安全上下界的 numberinteger
  • 有显式 maxItems 的 array,可选 minItemsuniqueItems
  • additionalProperties: false 的 object,显式 properties 和 required。

拒绝 $refoneOfanyOf、任意 regex、默认值、coercion、未知 keyword 和 开放 additional properties。Schema 深度最多 8、节点最多 256、object properties 最多 64、enum 最多 64、array 最多 256 项。

Tool input 根必须是 object。canonical input 最大 64 KiBoutput 最大 256 KiB。 没有 outputSchema 的 Tool 只能返回 null,不能把“未声明 output”解释为任意输出。

3. Registry 不拥有执行能力

Definition 只包含:

  • name、version、description
  • input/output schema
  • read | write | execute | external effect
  • low | medium | high | critical risk
  • 最多 16 个 Project permission
  • 13600 秒 timeout。

Registry 只规范化 definition/input/output 和生成 invocation plan。Definition、 Registry 和 plan 均不包含 execute、handler、数据库 service 或网络 client。 Package、MCP 或 Agent 不能通过注册 Definition 把代码注入控制面。

4. 每次调用先 Policy,后解析输入

prepareToolInvocation 固定执行:

  1. 精确解析 request envelope 和 Tool identity
  2. 验证当前 Principal
  3. tool.call:{name} 和 Definition 的每项 required permission 调用既有 ProjectPolicyEngine.authorize 端口;
  4. 任一 deny 立即停止,且不解析 Tool input;
  5. 所有非 deny decision 必须包含完全相同的 Project/RoleBinding fence
  6. 任一 decision 为 require_approval 时生成 approval_required plan
  7. 只有之后才按 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。

验证

测试必须覆盖:

  1. immutable registry、exact version 和无 runtime register
  2. root/subpath 导出一致;
  3. JSON Schema exact subset
  4. schema depth/node/property 上限;
  5. canonical input/output、未知字段与 unique array
  6. 无 output schema 时只允许 null
  7. 同一 Policy fence 的 digest-bound ready plan
  8. 真实 Project Policy 下 Agent Tool call 需要审批;
  9. deny 在 input 解析前短路;
  10. Policy unavailable、畸形 decision 和 mixed fence
  11. 过期 Principal 与扩展 request 拒绝;
  12. canonical envelope byte budget
  13. 无 filesystem/process/network/timer/execute authority。