site logo

Marico's space

深入探索 Open Agent SDK(第 5 部分):会话持久化与安全性

算法解析 2026-04-27 18:01:49 3

译者前言:Open Agent SDK 是一个 Swift 写的本地 AI Agent 开发框架,设计思路相当扎实——SessionStore 的 actor 隔离、PermissionPolicy 的可组合策略、路径规范化和段边界匹配防止目录遍历、HookRegistry 的 20+ 生命周期事件。这篇深入分析四个子系统的实现细节,是难得一见的深度技术文。

会话持久化:SessionStore

每次 Agent Loop 运行都会产生一个 messages 数组。如果没有持久化,进程退出时这些数据就会丢失。SessionStore 将对话历史持久化到磁盘,以便下次启动时恢复。

五个核心操作

save — 保存会话。将 messages 数组和元数据序列化为 JSON 并写入磁盘。存储结构:~/.open-agent-sdk/sessions/my-session/transcript.json,文件权限为 0600,目录权限为 0700——只有当前用户可以读写。

load — 加载会话。从磁盘读取并反序列化为 SessionData。load 支持分页参数 limit 和 offset,用于在不需要完整历史时仅加载尾部。

list — 列出所有会话,按 updatedAt 降序排列(最新的在前)。

fork — Fork 会话。将现有会话的 messages 复制到新会话,可选指定截断点。

delete — 删除整个会话目录。

三种会话恢复模式

Specified sessionId Recovery:给定一个会话 ID,Agent 在启动时加载历史消息。

continueRecentSession:当你不知道会话 ID 时,让 SDK 自动查找最新的。

forkSession + resumeSessionAt:从现有会话 fork 一个新分支,可选在特定消息处截断。

SessionStore 安全细节

SessionStore 在会话 ID 验证中包含路径遍历防护:会话 ID 不能包含 /、\ 或 ..——防止攻击者利用构造的 ID 读写意外路径。

权限控制:PermissionPolicy

六种 PermissionMode

  • default:每次工具执行前询问用户
  • plan:只读工具直接执行;写操作需要确认
  • auto:自动执行所有工具,危险操作除外
  • acceptEdits:文件编辑自动执行;其他操作需要确认
  • dontAsk:不询问用户;根据上下文自动判断
  • bypassPermissions:跳过所有权限检查

canUseTool 回调:比 PermissionMode 更细粒度

permissionMode 是一个全局开关,粒度较粗。要按工具名称或属性进行细粒度控制,请使用 canUseTool 回调。canUseTool 返回 CanUseToolResult?。返回 nil 表示回调没有意见,交由下一个检查。

CanUseToolResult 有三个工厂方法:allow() 允许、deny("reason") 拒绝、allowWithInput(modifiedInput) 允许但修改输入参数。allowWithInput 很少见但很实用——你可以在权限检查期间修改工具输入参数,例如将文件写入路径重定向到安全目录。

Policy 模式:可组合的权限规则

编写闭包很灵活但不可复用。SDK 提供了 PermissionPolicy 协议,将权限判断封装为可组合的策略:

  • ToolNameAllowlistPolicy:白名单,只允许指定的工具
  • ToolNameDenylistPolicy:黑名单,拒绝指定的工具
  • ReadOnlyPolicy:只允许只读工具
  • CompositePolicy:组合多个策略,按顺序评估

沙箱机制:SandboxSettings + SandboxChecker

SandboxSettings 配置

权限控制管理"这个工具能否执行"。沙箱管理"这个操作是否在允许范围内"。

路径和命令各有两种模式:路径的 allowedReadPaths/allowedWritePaths 是白名单(空 = 允许所有),deniedPaths 是黑名单(优先级更高);命令的 allowedCommands 是白名单,deniedCommands 是黑名单,allowedCommands 优先于 deniedCommands。

SandboxChecker 执行逻辑

SandboxChecker 是一个无状态的 enum,提供 isPathAllowed、checkPath、isCommandAllowed、checkCommand 静态方法。

路径检查使用前缀匹配和段边界保证:关键是 SandboxPathNormalizer——先规范化路径(解析 ..、.、符号链接),然后在段边界期间确保尾部 / 进行前缀比较。路径遍历攻击会被规范化后被 deniedPaths 捕获。

命令检查有三个阶段:Shell 元字符检测(识别绕过模式如 bash -c "cmd"、$(cmd)、`cmd`)、基名提取(从 /usr/bin/rm -rf /tmp 提取 rm)、白名单/黑名单匹配。

Hook 系统:20+ 生命周期事件

24 个生命周期事件

前三个系统解决的是"能否做"。Hook 系统解决的是"何时做完"和"事前干预"。SDK 定义了 24 个生命周期事件:

  • preToolUse / postToolUse / postToolUseFailure:工具执行前/后/失败后
  • sessionStart / sessionEnd:Agent 会话开始/结束
  • stop:Agent Loop 停止
  • subagentStart / subagentStop:子 Agent 启动/完成
  • userPromptSubmit:用户提交提示词
  • permissionRequest / permissionDenied:权限检查发生/被拒绝
  • taskCreated / taskCompleted:任务创建/完成
  • configChange / cwdChanged / fileChanged:配置变更/工作目录变更/文件变更
  • notification:通知事件
  • preCompact / postCompact:对话压缩前/后
  • setup:Agent 初始化
  • worktreeCreate / worktreeRemove:工作树创建/移除

Function Hooks vs Shell Hooks

Hook 有两种实现方式:函数回调和 shell 命令。Function Hook 是 Swift 闭包,适合进程内逻辑;Shell Hook 是外部命令,适合集成非 Swift 脚本。

Shell Hook 通过 ShellHookExecutor 执行:使用 /bin/bash -c 启动进程,将 HookInput 序列化为 JSON 传递到 stdin,从 stdout 读取 HookOutput JSON。Shell Hook 环境变量包括 HOOK_EVENT、HOOK_TOOL_NAME、HOOK_SESSION_ID、HOOK_CWD。

HookRegistry 注册和执行

HookRegistry 是一个 actor,内部维护 HookEvent 到 HookDefinition 数组的映射。每个 HookDefinition 可以有一个 matcher(正则表达式)。执行时,input.toolName 会与 matcher 检查;不匹配的 Hook 会被跳过。

超时处理:Function Hook 使用 withThrowingTaskGroup 实现超时;Shell Hook 使用 DispatchQueue.asyncAfter 实现超时,超时时终止进程。同上事件上的 Hook 按注册顺序串行执行。

HookOutput 能力

HookOutput 可以 block: true 阻止工具执行;permissionUpdate 在 Hook 执行期间动态修改工具权限;updatedInput 替换工具输入参数。

实践组合:构建安全的 Agent

四个子系统各司其职:SessionStore 记住对话历史、PermissionPolicy 控制工具是否可以执行、SandboxSettings 限制操作范围、HookRegistry 审计和拦截。

多层防御的好处:即使某一层有配置漏洞,其他层也能提供备份。例如,如果你不小心把 Bash 加到了白名单,Hook 的 matcher 仍会拦截它。即使 Hook 也漏掉了,沙箱的命令过滤仍会阻止它。

总结

SessionStore、PermissionPolicy、SandboxSettings 和 HookRegistry——四个系统各管一摊,组合起来形成完整的安全框架:SessionStore 的 actor 隔离和会话 ID 验证确保存储安全、PermissionPolicy 的可组合策略提供灵活的权限管理、SandboxChecker 的路径规范化和段边界匹配防止目录遍历、HookRegistry 的 matcher 过滤和超时机制确保 Hook 系统可靠。