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

140 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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。首版只允许:
- `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 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。