Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help


id: ADR-004 title: Tool-held shared state follows daemon-owned identity and handle ownership date: 2026-03-22 status: accepted relates-to:

  • https://github.com/zeroclaw-labs/zeroclaw/issues/4057
  • crates/zeroclaw-runtime/src/tools/mod.rs
  • crates/zeroclaw-tools/src/canvas.rs
  • crates/zeroclaw-tools/src/reaction.rs
  • crates/zeroclaw-api/src/tool.rs

ADR-004:工具持有的共享状态遵循守护进程拥有的标识和句柄所有权

这是一个恢复的追溯性记录。原始 ADR 添加在 docs/architecture/adr-004-tool-shared-state-ownership.md,并在 mdBook 迁移期间被移除。自原始记录以来,代码路径已迁移到 workspace crates;此恢复版本保持已接受的决策不变,同时在有用时更新路径引用。

上下文

ZeroClaw tools 在多客户端环境中运行,在这种环境下,单个守护进程可以为多个已连接的客户端和代理会话提供服务。某些工具需要长生命周期的共享状态:

  • 委托工具会保留对父工具的句柄;
  • 面向 channel 的工具会保留对 channel maps 的句柄;
  • canvas tooling 保持共享显示状态;
  • 未来的工具可能会保留速率限制器、连接池、凭据句柄或会话作用域缓存。

这些状态不能一概而论。有些是合法的共享显示或注册表状态。有些则对安全敏感,必须按客户端或会话隔离。

如果没有共享契约,新工具可能会引入重复状态、客户端之间的数据泄漏、重载后的陈旧状态,或者在错误的生命周期阶段阻塞启动验证。

决策

当工具遵循 handle 模式并尊重由守护进程拥有的身份、隔离、生命周期和重载规则时,它们可以拥有长生命周期的共享状态。

1. 所有权

当一个工具合法地拥有共享状态时,它会使用在构造时传入的可克隆句柄,通常是 Arc<RwLock<T>> 或其上的一个窄包装器。

当前工作区中的示例包括:

处理当前位置目的
DelegateParentToolsHandlecrates/zeroclaw-runtime/src/tools/mod.rs委托代理的父工具列表
PerToolChannelHandlecrates/zeroclaw-runtime/src/tools/mod.rs每个工具的通道映射句柄
ChannelMapHandle 别名crates/zeroclaw-tools/src/ask_user.rs, poll.rs, reaction.rs工具本地通道映射
CanvasStorecrates/zeroclaw-tools/src/canvas.rs共享画布帧

需要共享状态的工具必须:

  • 定义一个命名的句柄类型或包装器;
  • 在构造时接受 handle;
  • 记录并发和所有权约定;
  • 避免为每个请求或每个客户端数据使用全局可变状态。

2. 身份

该守护进程拥有客户端和会话标识。工具不得从传输细节(例如 IP 地址、标头、用户名或特定通道的发送者字符串)自行构造持久化的客户端标识键。

需要按客户端命名空间划分的工具会消费由守护进程分配的标识,或者接收一个已经作用域化的句柄。不需要按客户端隔离的工具可以忽略身份表面,但不能另行发明一个平行的身份表面。

3. 生命周期

工具生命周期有四个阶段:

  1. 构造:使用句柄和从配置派生的输入进行实例化。不要执行阻塞性的网络或文件系统验证。
  2. 注册:在工具注册表中注册。若在使用前需要验证,工具可执行启动验证。
  3. 执行:处理单个请求。避免在此路径中阻塞验证或注册表重建。
  4. 关闭:通过 Drop 或显式的 shutdown 方法清理所拥有的资源(当所有者提供此类方法时)。

从 config、credentials、policy 或外部资源派生的验证状态,在源发生变化时必须失效。非安全显示状态只有在 reload 不影响其有效性时,才可以在重载后保留。

4. 隔离

可能泄露凭据、策略、配额、用户数据或会话数据的状态,必须根据所属表面按客户端、代理或会话隔离。共享句柄不得存储每个客户端的密钥,除非密钥空间按守护进程拥有的身份进行作用域划分。

像广播显示状态、只读注册表数据或通道句柄这类天然共享的状态,可以在客户端之间共享。对于使用字符串键的情况,应支持命名空间前缀或跟踪元数据,以便运维人员仍可按客户端、代理、通道或会话进行筛选。

5. 重新加载语义

基于配置派生的校验和缓存,在相关的配置、凭据、策略、工作区或提供方源发生更改后即失效。工具必须在使用时从事实来源重新解析,或者从所有者处接收新的句柄/配置派生值。

重新加载规则是关于有效性,而不是注册表变更。只有当重新加载不会影响该状态的有效性时,工具才可以在多次重新加载之间保留非安全显示状态。

后果

积极后果:

  • 工具拥有的状态变得可发现且可审计。
  • 安全敏感数据有一个命名的隔离要求。
  • 运行时重新加载行为有明确的失效规则。
  • 新工具可以重用句柄模式,而无需发明全局状态。
  • 审阅者可以在接受新的工具字段或缓存之前要求提供事实来源。

负面后果:

  • 看起来像简单单例的工具仍然必须考虑客户端、代理和会话标识。
  • 当守护进程标识暴露面或重新加载模型发生变化时,一些较旧的句柄需要迁移。
  • 单靠句柄模式是不够的;在审查中仍然必须明确所有权和规范状态。

参考文献

  • 内置工具清单
  • 问题 #4057
  • AGENTS.md
  • crates/zeroclaw-runtime/src/tools/mod.rs
  • crates/zeroclaw-tools/src/ask_user.rs
  • crates/zeroclaw-tools/src/poll.rs
  • crates/zeroclaw-tools/src/reaction.rs
  • crates/zeroclaw-tools/src/canvas.rs
  • crates/zeroclaw-api/src/tool.rs
  • crates/zeroclaw-gateway/src/lib.rs