# Agent Note: 当前沙箱策略上下文

Status: implemented

[English](2026-07-30-current-sandbox-policy-context.md) | 中文

## 问题

沙箱策略已经强制执行并记录每个会话的文件操作模式，但新的模型请求并不包含这一状态。在 `read-only` 下的 Web 会话中，write 与 edit schema 仍然可见，因此模型会声称自己能够写入，直到一次被拒绝的调用后才发现事实并非如此。执行 `/permission danger-full-access` 后，下一个请求带有批准策略变更，却仍省略沙箱模式。因此，即使用户在任何操作前询问能力，拒绝结果也是模型可见的首个策略来源。

## 决策

`dsh-sandbox-policy` 负责解析模式与工作区根目录，并注册一项缓存安全的 `sandbox:policy` 上下文贡献。每次 agent（智能体）请求都通过 `ctx.sandboxPolicy.resolve({ session })` 直接解析当前会话；不存在拒绝历史扫描或进程本地的「上次告知」状态。

该策略贡献不依赖具体能力，并默认可用于每个 agent 会话。当模型接口有意排除动态上下文时，组合可以抑制完整的 runtime-context 通道；这不会禁用策略强制机制。该贡献不会另行维护一份已挂载后端或工具清单；模型可见的 schema 仍是可用操作的权威来源，而上下文会将其声明限定在 DSH 文件沙箱所强制执行的任何可用操作上。[不依赖具体能力的策略上下文决策](../simplification/2026-07-31-capability-neutral-sandbox-policy-context.zh.md)取代了较早的家族注册机制，同时保留本 Agent Note 的缓存安全交付与持久快照设计。

该贡献只说明所有强制执行方言所共有的事实。`read-only` 表明受沙箱强制执行的可用操作在常驻模式下无法修改文件，并指示模型正常尝试可用工具，随后遵循该工具返回的任何拒绝与升权引导。`workspace-write` 用非排他措辞说明规范化的会话工作区，并概述某些平台临时区域可能也可写，而不逐一列举。`danger-full-access` 表明 DSH 文件沙箱不会限制可用操作修改文件。后端选择的临时路径、`/dev/null`、runner 就绪状态、确切的工具可用情况和其他策略领域都不会出现，因为 `resolve()` 无法在请求组装时确定它们。

现有 `dsh-system-prompt` 组装在稳定系统段与工具 schema 之外，还包含有序的动态上下文。组装一个步骤后，agent loop（智能体循环）会将所有活动上下文渲染成一份带显式取代声明的完整快照。仅当不存在保留快照、字节发生变化、压缩（compaction）移除了保留消息，或最后一项贡献消失而需要一份清除快照时，它才会追加一条带来源的 `user/message`。快照追加在现有历史之后、`step/start` 之前，因此策略变化时仍会保留此前的系统与对话缓存前缀。会话事件本身可以重建确切的模型输入；当变化的只有策略上下文时，`request/header` 仍保持逐字节相同。

归属范围仍然有限。批准策略会将其完整的当前 `ask` 或 `never` 事实贡献给同一份完整快照；只迁移沙箱无法保留缓存，因为 `/permission` 会同时改变两方。计划模式仍由 `plan:policy` 负责，工具插件也继续负责各自的 schema，以及尝试、拒绝与升权引导。上下文负责说明常驻策略；文件系统、一次性 bash 与终端后端仍是强制执行边界。

缓存决策依据当前源码，而不只依靠类比。Codex 将权限建模为 developer 角色的 `WorldState` 段并保存其指纹；只有状态变化或保留的历史丢失该片段时才发出它，同时记录快照转换。Hermes 在会话期间保持系统提示词不变，并明确将不断变化的 skill（技能）、模型与语音通知前置到下一条用户消息，以免提示词缓存失效。Pi 没有可比的内置沙箱状态，Claude Code 当前的原生实现也无法公开检视；不过，Anthropic 的公开缓存指南仍将不断变化的逐请求上下文放在稳定缓存前缀之后。

先前接入真实提供方的 Web fixture（测试前置数据）量化了系统段版本的缺陷。首次 `danger-full-access` 和 `workspace-write` 请求分别只有 256 个缓存读取 token，而未缓存输入 token 为 14,691 和 14,782 个。相同策略下的后续步骤报告约 14.7k–15.5k 个缓存读取 token。只移动沙箱语句无法修复这些未命中，因为同一次 preset 切换还会改写批准策略系统段。

## 措辞证据

措辞实验预先登记「预防性拒绝」为主要终点，并要求旧常驻句子在十二个新会话中至少产生一次拒绝，之后才能评判任何替代措辞。2026-07-30，commit `2bf41990401b194bd8637f07bbd90c67a9eeac75` 通过已交付的 Web 组合运行 `deepseek-v4-flash`，使用精确的阳性对照句子 `Bash commands run under the "read-only" file sandbox.` 与当前工具归属方的尝试引导。对照组产生零次预防性拒绝和零次推测性升权；十二个会话全部先发起普通 bash 调用、观察到拒绝、在同一轮次升权、获得批准，并让所请求文件实际落盘。没有样本被排除。

缓存安全交付变更后，commit `10d4e0ff7b68d38fc4403403b644aac442b97a00` 通过新的尾部上下文通道重复了同一项十二会话阳性对照。结果再次为零次预防性拒绝和零次推测性升权；十二个会话的首次调用均为普通调用，随后观察到拒绝、在同一轮次升权并获得批准。其中八个会话让所请求文件按确切要求落盘，没有样本被排除。

因此，两项阳性对照均未通过预先登记的灵敏度门槛。Candidate A 与 B 的正式十二会话实验组均未运行，这些实验不选择也不验证当前措辞。它们说明先前十二次中五次的结果无法在本任务与当前工具引导下复现；在声明模型行为率之前，需要更强的阳性对照或不同的任务分布。下述确定性测试只能证明请求构造如实反映状态，并验证其可回放性。

随后，缓存安全交付重做针对中性 Web 任务 `Create the relative path policy-neutral.txt ...` 提供了一次独立的非统计验收对比；它不取代预先登记的十二会话实验。Candidate A 的绝对化只读声明导致模型以纯文本拒绝，工具调用为零。Candidate B 只针对受强制执行、且其工具公开升权能力的家族增加一句按组合条件化的文案。随后一次全新的真实提供方运行先发出普通 `write`，观察到只读拒绝，再在同一轮次用 `sandbox_permissions: "workspace-write"` 重试同一操作，获得批准、读回文件并核验确切内容。它没有进行推测性升权。在权限切换和四个变更步骤中，每个请求的缓存读取为 14,848–15,872 个 token，未缓存输入为 59–306 个 token，直接证明了稳定前缀的收益。

## 曾考虑的替代方案

**仅叙述模式变更。** 不予采用，因为这会让新会话不了解策略，并把首次被拒绝的操作变成策略发现机制。如果可以直接渲染当前状态，也就无需额外定义基线。

**扫描拒绝历史或记住上次叙述的模式。** 不予采用，因为拒绝事件描述的是尝试过的操作，而不是权威的当前状态；进程本地的簿记也无法跨恢复保留。归属方可以在每次请求时直接折叠持久策略。

**把当前策略放入动态系统段。** 不予采用，因为真实提供方证据显示，首次权限切换后缓存读取降至 256 个 token，而约 14.7k 个输入 token 未命中缓存。DeepSeek 匹配完整前缀；改变第一条协议消息会阻止复用更长的系统与历史前缀。

**由每个策略归属方独立调用 `agent.inject()`。** 不予采用，因为同级监听器的顺序会决定模型所见顺序，分开的消息可能暴露不匹配的中间快照，并且每个归属方都需要各自扫描压缩后的保留状态。现有组装归属方可以对贡献排序，并生成一份原子化的完整快照。

**通用运行时事实包。** 不予采用，因为现有系统提示词组装已经拥有段、schema、变量、作用域和权威的逐步骤 waterfall（瀑布式事件）。为该归属方增加有序上下文，无需新增包或第二个注册表服务。

**在上下文中重复工具 schema 或计划引导。** 不予采用，因为这些接口已有各自归属方和独立生命周期。批准的当前状态加入快照，仅仅是因为同一个 `/permission` 切换会改变它，而把它留在系统段会保留缓存缺陷。

**缓存安全迁移后仍保留 Candidate A。** 不予采用，因为中性的真实提供方任务中，尽管已有 bash 尝试引导，模型仍以纯文本拒绝，且没有调用工具。保留下来的反预防性拒绝原则本身不说明任何升权机制；它告诉模型不要从常驻标签推断操作不可能完成，然后把拒绝与升权行为交还给可用工具。

**继续省略沙箱模式，因为常驻模式标签曾引发预防性拒绝。** 不予采用，因为新的 Web 请求否则会暴露变更工具，却隐去这些工具的常驻策略，导致模型在首次操作前错误声称自身能力。先前的线上测量仍是必须执行的反证测试：使用 `Bash commands run under the "read-only" file sandbox.` 时，十二个轮次中有五个没有调用工具。已提交的工具归属方尝试引导晚于该测量，因此应通过当前工具约定下的新阳性对照实验选择替代文案，而不能假设旧条件与当前条件相同。

**独立的模型上下文包。** 不予采用，因为策略归属方可以直接解析当前会话状态，现有组装服务也可以对其排序。新包只会围绕同一个请求边界引入浅层组合层，并增加额外的文档与门禁维护工作。

**枚举可写临时根目录。** 不予采用，因为后端要到稍后的 `confine()` 才会选定：bwrap、Landlock、Seatbelt 和进程内文件系统围栏并不授予一套共同的临时路径。常驻请求中的主机特定路径既不稳定，也会作出过度承诺。

## 后果

模型在试探工具前就会收到常驻文件策略，且 `/permission` 后的下一个请求会反映已提交的模式。稳定的系统提示词不再随沙箱或批准状态变化；变化后的完整上下文快照会在保留的历史之后仅追加，状态不变时不增加消息。较旧的快照仍保留在历史中，但最新的完整快照会明确取代它们。该声明是引导，而不是强制执行护栏：运行时安全仍来自文件系统、一次性 bash 与终端后端消费同一项解析完成的策略。

聚焦测试固定了所有模式、规范化根目录、切换时机、服务 dispose（资源释放）、上下文顺序、清除、稳定的请求 header、恢复，以及不同 `TMPDIR` 值下的字节稳定性。无密钥的组装快照通过真实 Loader 组合固定持久上下文消息。无密钥回放负责固定中性的拒绝到升权轨迹；它是结构回归证明，而不是措辞选型证据。
