03. 工具和权限
翻译说明: 本页是与 英文源规格 一一对应的机器辅助翻译。代码、协议字段和标识符保持原文;如翻译与英文源事实有歧义,以英文版本为准。
应用的决定:D003、D004、D006、D013、D015、D093、D114、D115、D181、D186、D636、 D189、D190、D195(ADR 0057)、D315、D384(ADR 0211)、ADR 0087
0. 冻结政策总结
并行文件批次
Read、Glob、Grep 是并行候选;只有 runtime 与实际本地 Host 协商文件批次协议版本 1 成功后,Write、Edit 才加入并行候选。能力在一个 runtime/连接内固定,后台子代理也 沿用它。旧 Host 和远程工作区保持 Write/Edit sequential,因此含写整批串行。 sidecar 不查询本机文件系统来判断远程路径。
仅含 Task 的批次保留扇出;Task 与任何文件工具混批仍整批串行。可写 Task 之后的 读取或搜索等待委托落定;写只等待 Task 返回启动通知。Bash、TodoWrite、未知、插件、 MCP 工具保留原有串行策略。只有选中的内建执行器接受原生预处理与批次绑定;同名 动态工具仍为串行。
符合条件的并行批次按源序校验参数、加载路径指令,再向 Host 登记每个文件调用。 登记包含 session/delegation、新批次 ID、原始序号、调用 ID、工具名和规范参数;Host 返回不透明的单次 ticket。登记不查询文件、不授予权限、不占执行容量。全部准备结束 后,各执行器共用一次封存,再携带 ticket 提交 tools.execute。串行批次不登记不完整 批次。改参、跨所有者使用、重放、封存前执行均失败。ticket 绑定异步执行上下文, 不使用全局调用 ID 映射,避免父子代理同名 ID 相互覆盖。
Host 保留现有模式和权限检查;只有已获授权的调用才通过 session/delegation 根和 外部路径解析器识别资源。待审批调用不占活动租约或容量。较早调用身份未知且可能 冲突时,同批后继保守等待;身份明确后只等待冲突前驱。前驱失败或拒绝不级联失败。 不同批次或会话只保证资源互斥和排队公平,不虚构全局模型源顺序。
| 工具 / 目标 | Host 资源声明 |
|---|---|
| 已存在常规文件 Read | 文件对象身份及解析地址共享 |
| 已存在常规文件 Write / 普通 Edit | 文件对象身份及解析地址独占 |
| Glob / Grep | Host 文件协调域共享 |
| 目标不存在、身份未知、文件系统不支持 | 读取持共享域;写入持独占域 |
| Edit MV / REM 或 CUT/PUT 命名寄存器 | 协调域独占 |
| review 回滚 | 协调域独占,保留写后内容哈希校验 |
Unix 对支持的本地文件系统使用已打开文件的 dev/ino;Windows 对本地磁盘使用卷标识 和 FILE_ID_INFO。句柄在排队及活动期间保持有效。不同名称、不同根的硬链接/符号链接 仍必须冲突。根决定权限和地址解析,不能把物理文件 ID 按根分区。basename、文件大小、 词法路径、单独 realpath 都不能证明独立。全域含已授权外部路径。两侧都是共享时不冲突; 一侧独占时,域声明与所有文件资源冲突,否则按相同对象身份或地址冲突。未知不退回 “推定独立”。
ToolBudget 一次授予全部资源和容量;等待前驱或资源不占槽位。后来的冲突调用不能 越过等待中的独占者;同批较早源序调用可越过后到达的后继,避免队列与依赖图死锁。 额度仍为总 16、读 8、写 2、每会话 4。同一会话最多两个写可以重叠,但两者都必须有 已验证的批次 ticket 和可信本地文件身份,且通过现有资源冲突检查。其他 mutation 保持 单写准入,并在两种到达顺序下都与协调写互斥。较早且源序已就绪的保守写等待者阻止 后来协调写持续补位;未就绪后继与源序前驱绕行仍保留依赖图例外。 准入等待 30 秒仍返回 HOST_OVERLOADED。 本地 audit/debug 记录总准入等待与初始原因 source_order、resource、capacity;不将它们 宣称为互不重叠的分段耗时。
拿到租约后在 review 快照前和实际 blocking 文件任务内重验身份与地址。绑定变化则 失败,不落盘、不自动重放。命名空间变化和创建目标使用全域独占屏障。before 快照、 执行、Hashline 更新、after 快照共用租约;后续 rollback 也获取租约,等待时不持 AppState 锁。取消 RPC future 不会停止 spawn_blocking,实际任务持有 permit 到执行结束。 未开始项可放弃;关闭批次不能释放活动磁盘任务。登记表限制为全局 512 调用、每批 128 调用、参数总计 32 MiB;未使用元数据 15 分钟过期,已提交的审批等待和实际任务 不靠 TTL 回收。
支持新协议的路径由 Host 资源协调替代 sidecar PathMutex;legacy 路径保留原锁。 权限、Hashline、变更恢复和结果语义保持。父子路径指令只更新各自提示词;pi 保持源序 提交工具结果。租约只覆盖单个 Host 内接入的文件工具和回滚,任意外部写者、Bash、 另一个 Host 进程不自动参与。
参见 有序准备 ADR 和 Host 准入 ADR。
| 主题 | 决定 |
|---|---|
| 默认模式 | Agent |
| Agent 工具 | 读取 / Glob / Grep / 写入 / 编辑 / Bash + 活动作用域授权下的 Computer + 已注册的插件工具 |
| Plan 工具 | 读取 / Glob / Grep / BrowserPreview / Bash / SubmitPlan + 声明 plan-safe 动作的插件工具 |
| Goal 工具 | 读取 / Glob / Grep / BrowserPreview / Bash / SubmitGoal + 声明 plan-safe 动作的插件工具 |
| Plan 和 Goal 硬拒绝 | 写入 / 编辑 / Computer / 没有 planSafeActions 的插件工具 / 未知工具 / 另一类的提交工具 |
插件 planSafeActions | 非空的 action 字符串数组;运行时在 Plan/Goal 中隐藏没有该列表的插件工具,host 放行已列出的工具,plugin-runtime 拒绝列表外的任何动作(ADR 0211) |
| 本地权限确认 | 无自动截止时间;必须明确决定或取消 |
| 允许会话范围 | 工具名称 |
| 重击风格 | 非交互式;具有流输出的选定主机目录外壳 |
| Edit 契约 | 行锚定操作 + 整文件 tag;不再有 old_string/new_string(ADR 0087) |
| 询问工具 | 交互式多问题工具;无有效期期限;跳过的答案变成空输出字段 |
1. 目标
让代理完成工作,但默认情况下仍处于控制之下。
2. MVP内置工具
| 工具 | 风险 | 描述 |
|---|---|---|
Read | 低 | 读取工作区中的文件;返回带行号的内容和 [path#TAG] 头 |
new_context | 低 | 在下一个回合边界处启动一个新的上下文窗口;不接受任何参数并且不改变环境状态 |
Glob | 低 | 按模式列出文件 |
Grep | 低 | 内容搜索;本机有 rg 时优先用,否则进程内搜索;为每个文件生成 tag |
BrowserPreview | 低 | 在随应用打包的浏览器插件中打开与工作区相关的预览(若 pi.browser 被禁用则失败) |
EnterPlanMode | 低 | 主机验证后,将相同的 Agent 从 Agent 移动到 Plan |
SubmitPlan | 低 | 在新的 .pi/plan/*.md 工件中保留精确的 Markdown 字节并请求批准 |
EnterGoalMode | 低 | 主机验证后,将相同的 Agent 从 Agent 移动到 Goal |
SubmitGoal | 低 | 在新的 .pi/goal/*.md 工件中保留精确的 Markdown 字节并请求批准 |
Write | 高 | Create/overwrite 文件;返回写入后的 tag |
Edit | 高 | 通过针对已校验 tag 的行锚定操作修改文件(18) |
Bash | 高 | 执行命令 |
Computer | 高 | 检查并操作用户授权的那一个原生窗口或应用;没有活动作用域授权时不可用(computer-use,ADR 0342) |
asktool | 低 | 询问一个或多个用户问题并将提交的答案作为工具输出返回 |
TodoWrite | 低 | 替换当前 Agent 会话清单;由主机校验并持久化完整有序快照 |
名称可以在实现过程中进行微调,但语义保持一致。
2. 1 延期辅助工具(D185、ADR 0048)
按照 pi 的编码代理默认值,第一个 Agent 请求仅激活 Read、Bash、Edit 和 Write; Glob 和 Grep 按需加载。 Plan 和 Goal 保留其 read/inspection 核心。Skill 有意不作延迟:/skill:<skill-id> 调用会指示模型调用它,而模式中不存在的工具根本无法被调用,因此只要技能目录非空, 它就会随第一个请求一起发送(D404、ADR 0230)。运行时还注册功能 无需预先发送其完整模式:
- Agent 模式下的
Glob和Grep BrowserPreviewPluginCheck、PluginScaffold和PluginPack- 插件声明的代理工具
这些工具出现在有界的 # On-demand tools 目录中,具有紧凑的结构 描述。该模型使用确切的名称调用本地 ToolSearch 工具或 能力查询;匹配的模式在下一个模型回合中可用。 sidecar 在每个新用户提示开始时重置此延迟集,并从有效会话上下文中的 成功证据重建:成功 ToolSearch 结果使用 canonical details.addedToolNames,成功的延迟工具结果使用其工具名。为兼容历史 数据,也接受 details.activated 和顶层 addedToolNames。失败、中断或 缺少结果的占位行以及助手/用户文本都会被忽略;恢复的名称必须仍在当前 模式的延迟目录中。主机权限、workspace/scratch 遏制、超时和审核规则 加载工具时不会改变。ToolSearch 本身从不执行工作区操作并且永远不会 绕过 host-core 策略。
3. 常用工具约束
每个非交互式执行工具都必须具有:
- JSON 架构/类型框参数定义 2、超时 3.工作空间路径验证 4.输出截断策略
- 跟踪ID
- 结构化结果
asktool 是交互式异常:它有一个类型化的请求事件,等待 渲染器响应没有过期,并返回有界结构化工具 结果。停止回合可以解决跳过的未决问题。
4. 路径规则
本机文件和搜索工具强制执行不同的路径形状(D208、ADR 0069):
Read.path是现有的常规文件。返回一个目录INVALID_ARGUMENT具有结构化Glob建议,而不是通用建议 执行失败。Glob.path是目录搜索根。Grep.path可以是一个文件或一棵目录树。直接命名的文件是 在没有步行兄弟姐妹的情况下进行搜索,而include仍然过滤其基础 名称和每个产出预算保持不变。本机 PATH(以及 Unix login PATH)上有rg时 Grep 优先调用它,缺失或失败则回退到进程内搜索(D315)。 面向模型的结果契约不变。
工具结果中的工作区相对路径使用 / 表示平台分隔符。在 POSIX 系统中,文件名里的字面量反斜杠保持不变,以确保结果可以回传给 Read 或 Edit;Windows 路径分隔符会被规范化为 /。
Agent 模式使 host-core/JSON 在 D185 下保持延迟。每个新用户提示都会重置 它们的激活,因此目录发现通过 ToolSearch 激活 Glob 对于该提示,而不是猜测文件名或在 目录。
运行时为每个规范参数名接受一个别名,并在主机看到调用之前把它折叠掉(D273):
| 工具 | 规范名 | 接受的别名 |
|---|---|---|
Read / Write / Edit / BrowserPreview | path | file_path |
Glob / Grep | pattern | query |
两种拼写在 schema 中都是可选的,运行时要求恰好提供其中一个;两个都没给的调用以 INVALID_ARGUMENT 失败。当一次调用同时带上两个时,规范名胜出。Bash.timeout 在 schema 中放宽到 100000000,好让毫秒值先通过校验:超过 21,600 秒上限的值按毫秒读取并换算成 秒,再夹到 21,600 秒(D273 / D329)。范围内的值(包括 600 和 1800)按秒读取。
- 对于持久的
sessionId,workspaceRoot是从该会话的 持久的项目绑定。它不是从可变的活动侧边栏读取的 执行时的选项卡。 - 默认情况下,所有文件路径均相对于已解析的
workspaceRoot - 标准化后,它们必须仍然驻留在工作空间内,除非 调用收到显式外部路径许可
..转义和符号链接转义是外部路径请求,不是隐式的 访问- 符号链接在获得许可后立即执行之前再次解析 批准,因此批准不能跳过规范化步骤
- 例外(D114):会话临时目录内的绝对路径是
sessionId/workspaceRoot/workspaceRoot的第二个合法根 — 请参阅§4b。两个根都运行 相同的词汇+符号链接遏制防御。sessionId/workspaceRoot/workspaceRoot/../Read只能通过权限来寻址两个根之外的显式路径 政策如下;拒绝或未经批准的请求将返回TOOL_DENIED。
4a。显式外部路径权限
显式 path 参数在会话工作区之外解析,并且 刮根是一项单独的功能。主机正常之前检查一下 低风险自动允许决策:
auto允许无卡外部路径;ask和accept-edits发出普通权限卡;allow-once仅执行当前调用,而allow-session紧随其后 现有的每个工具会话授予范围;- 拒绝或取消永远不会执行该操作;
- 相对
..转义和符号链接转义使用与绝对相同的规则 路径; - 成功的外部
../Read/Write结果携带root: "external"和绝对规范路径;外部../Read匹配是绝对的 因此访问在记录中仍然可见。
该例外仅适用于显式路径参数。它不扩展 工作区根目录、Bash 的工作目录或任何隐式目录路径。
4b.会话临时目录 (D114)
Temporary/intermediate 代理生成的文件(一次性脚本,下载 数据、草稿)不得弄脏用户的项目或其 git 状态。每个 session 在工作区之外获取一个临时目录:
<data_dir>/scratch/<sessionId>/粘贴到作曲器中的操作系统剪贴板文件和图像通过以下方式具体化 Electron 主要低于 <data_dir>/scratch/<sessionId>/pasted/ 之前的 绝对路径被捕获为瞬态输入框引用并序列化为 @ 及时发送参考。他们使用与其他会话相同的生命周期 暂存数据并且不输入工作区、工件或持久提示 作为二进制内容。
- 寻址。 该模型仅通过绝对路径寻址;路径 在系统提示中公布。相对刀具路径始终解析 反对工作区。
Bash还导出PI_SCRATCH_DIR。POSIX shell(包括 Windows 上的 Git Bash)收到提示中展示的正斜杠路径;PowerShell 和 cmd 保留原生路径格式。 - 遏制。
resolve_tool_path首先尝试工作空间根目录,然后 暂存根,应用相同的两层防御(词汇..规范化+规范化祖先符号链接检查)到每个。符号链接 植入内部的划痕无法到达工作区或其他任何地方。 - 权限。
Write/Edit,其path在词汇上位于 会话的临时根自动允许,无需权限卡 - 他们不能 触摸项目。词法检查仅跳过提示;执行仍在 经过完整的解析器,因此它不是逃逸向量。 Plan 和 Goal 可以 不公开 Write/Edit,因此临时自动允许规则无法使这些工具 两者均可使用。契约模式 Bash 调用仍可能创建或变异 当其权限模式允许时,擦除数据。 - 工件。 成功的暂存写入不会记录在
artifacts表;工件驱动的文件选项卡代表工作区 仅可交付成果,而文件界面仍可浏览活动的 工作区。工具结果携带root: "workspace" | "scratch"来实现此目的 决策和 UI 渲染显式。 - 工具覆盖率。
Write/Edit/path使用工作区和暂存根;Write/Edit默认情况下使用工作空间根目录,并且可以显式搜索 范围临时目录或明确批准的外部目录。的 模型应该使用有界本机搜索工具而不是 shell 目录 散步。BrowserPreview在 v1 中仍然与工作区相关。它的主进程处理程序 从原始持久会话中解析根,并且渲染器 事件携带sessionId;选定的前台工作区从未使用过 用于背景预览。 - 生命周期。 在第一个
Write/Edit/path上延迟创建或 会话的输入框剪贴板粘贴。使用session.delete删除。一个 启动扫描删除会话不再存在的暂存目录和目录 超过 7 天未受影响(crash/force-quit 后备;没有预定作业 需要)。 - 项目切换不会重定向或取消后台会话的工具; 会话 A 和 B 分别保留在项目 A 和 B 的沙箱中。
- Temporary/path-less 会话没有工作空间根目录,即使是另一个项目 是可见的。如果没有会话项目,高风险工具将不可用。
- 无法解析为持久会话的旧调用可能会使用选定的 仅在兼容性窗口期间托管工作区。
- 会话 lookup/storage 错误使工具请求失败;它绝不能是 被视为丢失的旧会话或重定向到选定的工作区。
4b.1 会话可视化目录(D671)
内联可视化(ADR gen-ui-inline-visualization)是模型写出、由转录区渲染的 HTML 片段。 它们放在第三个由宿主拥有的根目录中,与 scratch 分开,这样 scratch 保留 7 天清理, 而可视化与会话同生命周期:
<data_dir>/visualizations/<sessionId>/- 可用范围。 仅本地会话,且默认关闭:需要用户开启设置项
AppSettings.inlineVisualizations(缺省与false都表示关闭)。关闭时没有该目录、 没有提示行,技能目录、/菜单和Skill工具都不提供pi-desktop/visualize, 渲染层只隐藏引用行、不加载卡片。远程工作区会话同样没有这些内容。 - 寻址。 只接受绝对路径;路径在系统提示中与 scratch 目录一起给出。相对路径 永远不会解析到这里。
- 包含校验。
resolve_tool_path依次检查工作区、scratch、可视化三个根目录, 使用相同的词法 + 符号链接防护。 - 内容。
Write/Edit只接受以.html结尾的单层文件名;该根目录下的其他目标 返回工具错误。允许Read。 - 权限。 在该根目录内的
Write/Edit与 scratch 条件相同,免确认自动允许。 Plan 和 Goal 不提供 Write/Edit,因此合约模式无法写入这里。结果带root: "visualization",不记为工件,也不做审核快照。 - 生命周期。 首次写入时延迟创建。随
session.delete删除。启动清理只删除会话已 不存在的目录,从不按时间删除。 - 渲染。 代码块之外单独一行
::visualize{"path":"<绝对路径>","title":"…"}会在 回合完成后渲染为内联卡片。渲染层由路径后缀推导出pi-genui://session/<sessionId>/<file>,从不把绝对路径发给 Main。Main 只提供当前 数据目录下不超过 2 MB 的普通非符号链接片段,拒绝完整文档,并把片段包进带严格 CSP (无网络、子框架、表单)的宿主外壳;框架为sandbox="allow-scripts"。外壳在片段 之前预加载内置 D3 和viz图表辅助函数,并移除片段自带的固定版本 D3 标签。客户端运行时会把片段脚本的第一个未捕获错误报告给卡片;卡片保留框架,并以纯文本显示截断后的错误提示。对会话可视化目录下.html文件的成功Write,结果里还会附带一次静态检查:内联脚本只编译不执行,传给viz.render、viz.chart、getElementById或querySelector("#…")的字面量元素 id 必须存在;发现问题会列出来,让模型用一次Edit修好。
4c。消息拥有的审核快照和回滚
Write 和 Edit 是结构化审核边界。对于一个成功的 工作区根突变,host-core 在执行前捕获前一个文件 并向工具结果添加有界审查证据:
type ReviewChange = {
version: 1;
snapshotId: string;
messageId: string;
path: string;
operation: "write" | "edit" | "delete";
status: "added" | "modified" | "deleted";
state: "active" | "rolledBack";
additions: number;
deletions: number;
hunks: DiffHunk[];
binary?: boolean;
truncated?: boolean;
reversible: boolean;
};2
3
4
5
6
7
8
9
10
11
12
13
14
15
- 渲染器保留并显示此记录以及工具消息;它 不会从 Git、
HEAD或当前脏树重新计算 Review。 - 临时根、失败、拒绝和无法解析的写入没有
review记录。二进制或超大内容可能会省略大块并且是不可逆的。 - 回滚由主机拥有并受哈希保护。它恢复以前捕获的 字节,或删除新创建的文件,仅当当前内容仍然存在时 等于后工具哈希。稍后的编辑将返回
conflict而不进行触摸 文件。完成的回滚会使该路径的会话快照条目失效, 因此模型无法继续针对回滚已替换的 tag 进行编辑。 - 携带
MV DEST的Edit在一次工具调用下记录两条条目——源删除 和目标创建——回滚要么同时恢复两者,要么都不恢复。REM记录为一次删除,其回滚恢复已捕获的字节。哈希保护使用 完整摘要,而不是 16 位tag。 - 查看工作区之外的实时快照文件,并随其一起删除 会议;孤立会话目录在主机启动时被清除。
4d。突变排序和编辑恢复
Write 和 Edit 在每个会话中序列化。 Read/search 工具可能 并行继续,不同的会话可能会变异不同的根 同时,但一个会话永远不会有两个正在进行的突变。主持人持有 在消耗全局突变槽之前每个会话突变允许,所以 排队的突变在等待早期编辑时无法保留容量。
Edit 命名位置并且只提供新内容;它从不匹配已有文本。 每次调用都携带由最后显示该内容的工具生成的整文件 tag, 当 tag 无法哈希出实时文件、或锚点引用了本会话从未显示过的 行时,主机拒绝该调用。完整契约——tag 计算、会话快照存储、 操作语法、块解析、寄存器与漂移恢复——见 18-line-anchored-edit-contract;本节只保留 排序与循环保护规则。代理突变工作流程是:
- 当交付内容位于 广告中的工作空间。
- 如果专用工作树位于该根目录之外,请在以下目录中执行一项受保护的编辑 使用 Bash 构建该工作树并验证结果差异。
- 编辑或补丁检查失败后,对当前的文件执行一次新的
Read定位并重新生成一次更改。一旦某条路径用完它的恢复额度 (18-line-anchored-edit-contract §9.3),该提示符中针对该路径的下一次失败Edit——或第二个失败的 shell patch 命令(apply_patch、git apply或patch)——返回终止工具结果,因此代理 报告确切的不匹配后停止。不要手动编辑旧的统一差异 大块标头或继续修复循环。 - 保持一条路径的突变是连续的,即使 read/search 调用是 并行发行。
其 reveal 完整的 EDIT_LINES_UNSEEN 拒绝不受第 3 步重新读取的约束: 错误本身已经显示了缺失的行并将其并入会话来源集,因此原样重试 同一个 tag 即可应用。那次重试同时也是 EDIT_LINES_UNSEEN 在该路径上唯一的 宽限,因此第二次确实会计入保护限额。
序列化同时保护快照存储,生产者与 Edit 都会修改它:没有按会话的 突变许可,一次并发记录可能落在校验与写入之间。
在一个提示内同一路径累计三次失败后(见 18-line-anchored-edit-contract §9.3),第 3 次 计数的失败 Edit——或第 3 次失败的 shell 修补命令(apply_patch、git apply 或 patch)——返回带有错误专属恢复提示的终止工具结果,代理随后停止并报告准确的不匹配。 不要手动编辑旧的 unified-diff 块头,也不要继续修复循环。
4e. 隔离会话 worktree(ADR 0329)
在 git 项目中以 isolated: true 创建的会话拥有专属 worktree,位于 <data_dir>/worktrees/<project-key>/<id>,分支为 pi-desktop/<id>,从项目当前 HEAD 创建(项目中未提交的改动不会被复制;.worktreeinclude 列出的 git 忽略文件会被 复制,且不跟随链接)。对这类会话:
- worktree 取代项目路径成为 §4–§5 所有规则的
workspaceRoot,文件工具、Bash 工作 目录、审阅快照和产物都在其中解析;绝不回退到主检出(worktree 丢失时返回WORKTREE_*错误); AGENTS.md指令从 worktree 读取,而项目身份(记忆、技能、插件、MCP、子代理)仍以 项目路径为键;- Bash 获得
PI_PORT(41000–48999 中一个 10 端口块的首端口;只有整段 10 个端口都空闲时才分配该块)、PI_PROJECT_ROOT和PI_WORKTREE_ROOT; - 可选的项目初始化命令在每个新 worktree 中运行一次,带同样的变量,使用用户的默认命令 shell(
defaultCommandShell,未设置时用平台默认:Windows 上为 Windows PowerShell, 其他平台为 bash)。命令不会在方言之间转换,因此在 Windows 上必须使用 PowerShell 语法 ($env:PI_PORT;Windows PowerShell 5.1 用;而不是&&),除非默认 shell 配置为 Git Bash 等 POSIX shell; - 宿主在 worktree 中运行的 git 命令禁用 hooks 和 fsmonitor,删除时绝不跟随链接越出 worktree;
- 登记的 worktree 文件夹必须是真实目录:若它(或通往项目子目录的某一级目录)被替换为符号链接或 Windows junction,即视为缺失(
WORKTREE_MISSING),不会被用作工具根目录、合并或同步;删除会话时 只删除链接本身,绝不删除其目标。
宿主的工作区差异收集器会对其检查的所有仓库应用相同的 Git 加固措施,包括非隔离工作区。每次由宿主启动 Git 命令都会禁用仓库配置的 hooks 和 core.fsmonitor,并移除可将 Git 重定向到其他仓库或注入命令级配置的环境变量。差异命令也会禁用外部 diff 和文本转换辅助程序。
合并到项目是显式的用户操作(先预览,再携带预览令牌合并):先把项目分支同步进 worktree,项目只做快进,因此冲突留在 worktree 中。删除隔离会话即丢弃其 worktree 和分支。
5. Bash 规则
主机执行基线:
- 需要一个项目绑定的会话工作区
- 默认 cwd = 原始会话的
workspaceRoot - 默认需要确认
- 设置强制 60 秒超时;接受 1 秒至 21,600 秒覆盖(D329)
- 超时返回
TOOL_TIMEOUT,错误消息包含实际timeoutMs预算,并建议延长预算或拆分命令 - 分别流式传输 stdout 和 stderr,然后返回有界的最终输出
- 截断大输出而不混合两个流
- 没有交互式 TTY (MVP)
- 以非零值退出的命令返回
ok: false、isError: true和errorCode: TOOL_FAILED,同时保留其exitCode、stdout 和 stderr 在content中,以便代理可以诊断命令而无需盲目重试。
Shell 目录 (D190) 公开稳定 ID windows-powershell、windows-pwsh、cmd、 平台支持的 git-bash 和 bash。楼主坚持 defaultCommandShell;如果那个持续的选择后来变得不可用, 有效的目录选择有意回退到第一个可用的目录 平台外壳。一轮固定有效的 shell ID 和方言。 Bash 仍然存在 tool/protocol 名称,请求中单独携带固定的 shell ID。 主机核心在生成之前再次解析该条目并拒绝更改的 ID/dialect 与 COMMAND_SHELL_CHANGED;设置写入拒绝不可用或 COMMAND_SHELL_INVALID 的平台 ID 错误。没有任意可执行路径 或可执行路径哈希被接受作为 shell 标识。
PI_DESKTOP_BASHenv 覆盖(bash 可执行文件的路径)- Unix:众所周知的位置(
/bin/bash、/usr/bin/bash、/usr/local/bin/bash、Homebrew),然后是 PATH - Windows:来自 Git 的 Windows 的
bash.exe— 派生自 PATH 上的git,然后是标准安装目录,然后是排除System32中的 WSL 启动器的 PATH
- Unix 调用
bash -lc(登录 shell 为 Finder/Dock 启动保留配置文件路径);Windows 使用CREATE_NO_WINDOW调用bash -c。负责拥有命令树的隐藏 host-core runner 会使用CREATE_NO_WINDOW创建,并且仅当宿主所在的 job 授予JOB_OBJECT_LIMIT_BREAKAWAY_OK时才加上CREATE_BREAKAWAY_FROM_JOB,然后被分配到自己的 kill-on-close Job Object 中。按宿主自身 job 的能力请求 breakaway,使启动器或策略 Job 不会成为 Desktop 进程的终止范围。当该 job 未授予JOB_OBJECT_LIMIT_BREAKAWAY_OK时,CreateProcess会以ERROR_ACCESS_DENIED拒绝该 breakaway,因此不再带上该标志,runner 改以嵌套 job 的形式加入 host 拥有的 job;无论哪种情况,shell 本身都留在这个 host 拥有的 job 中,所以超时和中止仍会终止完整的命令树。 - 在 Unix 上,Bash 工具另外探测用户的登录 shell 以获取其信息 路径 —
$SHELL(回退/bin/zsh→/bin/bash→/bin/sh)-lic 'printf %s "$PATH"',5 秒范围内,每个进程缓存 — 并注入它 进入每个子流程。bash -lc仅提供 bash 配置文件;上 macOS 默认 shell 是 zsh,因此 nvm/pnpm/Homebrew 初始化于 否则,~/.zshrc/~/.zprofile对代理命令将不可见。 探测是尽力而为:缺少 shell、非零退出或超时回退 主机 PATH 不变。 Agent 命令保留 POSIX bash (D181 / ADR 0045)。 - 安装程序中没有捆绑 bash:Windows 的 Git 是 Windows 的先决条件(无论如何,该应用程序都需要 git)
- 解决失败返回稳定的
SHELL_NOT_FOUND并提供安装指导 - Windows PowerShell 和 cmd 使用其本机非交互式调用。
- PowerShell 输出经过 PowerShell 自身的格式化器,因此对象(表格、列表、哈希表、
Measure-Object结果)以控制台显示的形式交给 Agent,而不是类型名。原生命令 写入 stderr 的行会转发到 stderr,但本身不会使调用失败:调用报告原生命令的退出码。 其他 PowerShell 错误会使调用以退出码 1 失败。 - PowerShell 7 先解析
%ProgramFiles%\PowerShell\7(宿主进程为 32 位时为ProgramW6432)下的pwsh.exe,再回退到 PATH,覆盖机器级安装、Store、 用户级与便携安装。它与 Windows PowerShell 5.1 共用同一调用契约,且从不被 隐式选中,因此不会改变既有用户的默认 shell。解析失败返回SHELL_NOT_FOUND,并列出已搜索的位置。 - Git Bash 使用发现的 Git 来执行 Windows 可执行文件。
- Unix Bash 使用经过批准的系统 Bash 条目。
- 用户中止和超时在返回之前终止整个进程树。
runInBackground: true让命令脱离本轮:shell 一启动就返回backgroundTaskId, 不再套用超时,进程也不会被tools.abort/agent.abort终止。进程树归宿主 所有,通过输入框上方的共用容器展示,可由用户停止或随应用退出而结束。 每个输出流保留最后 256KB。- 后台任务归属启动它的会话。 会话 ID 在启动时固定,之后不随切换、重命名、 归档或恢复而改变。单个会话最多同时运行 10 个后台任务,拒绝时会指明这是 每会话上限。另有一个进程级总量上限,仅用于防止多个会话叠加压垮宿主,并以 独立原因上报,使两种拒绝可区分。会话已不存在时启动会被拒绝,任务不会落在一个 无法展示它的会话里。
- 后台任务先排空输出,再发布最终状态,保留跨管道读取边界的 UTF-8 字符; 退出和停止时已排队的输出不会丢失。关闭宿主时发送停止信号不依赖输出锁。
- Todo 与后台任务共用输入框上方的一个容器,位于 Plan/Ask 决策面与输入框之上。 容器只有一个头部,承载待办进度摘要、运行中任务数、tab 列表与展开控件;折叠 只隐藏面板,因此折叠时 tab 仍可点击。仅当两侧都有内容时才出现 tabs:只有一侧 有内容时直接渲染该面板、不渲染 tab 列表;两侧都为空时容器不渲染。面板在
min(200px, 24vh)内滚动,不随行数无限增高;审批和 Ask 对输入的既有约束不变。 - 当前显示的 tab 是按会话记的视图状态。 它记住用户自己的选择,容器自动选出的 tab 不会覆盖该选择;记忆的 tab 对应侧变空时回落到另一侧。容器沿用待办坞原有的 门控:需要存在 active 会话且允许本地工具。
- 新启动的任务不抢焦点。 当后台任务面板不是当前可见面板时,新任务只在其 tab 上 累加徽标;进入该 tab 即清零。折叠状态下点击 tab 会同时展开容器,否则点击看起来 没有效果。
- 终止的后台任务不长期驻留:渲染层观察到任务结束后保留 10 秒宽限窗以便读取退出码, 随后离开容器。渲染层首次加载时已经结束的任务属于注册表历史,不再出现。正在查看的 输出预览会钉住其行,避免日志在阅读时消失。
- 移除是视图动作,但对运行中任务有准入限制。 只有终止态(
exited、failed、stopped)可移除;运行中任务——包括已发出停止信号、但在 supervisor 落定前仍是running的任务——在状态层就被拒绝,而不只是隐藏按钮。宿主侧任务、输出与 id 均不受影响,因此按 id 仍可读取,直到宿主自身的保留策略淘汰。移除记录按会话记忆, 不持久化。 - 删除会话会停止它的运行中后台任务。 归档不会:归档是可恢复的标记,杀掉进程属于 破坏性副作用。删除只发停止信号即返回——不等待任务落定,也不因任务杀不掉而失败。 已删除会话中被停止的任务不出现在任何容器里,因为原本展示它的会话已不存在。
- 同时只展开一个输出预览;任务运行时,上次读取完成后每秒刷新一次,折叠的日志 不读取。预览最多展示 stdout 的 8,000 字符和 stderr 的 4,000 字符,忽略切换前 的过期响应。读取、停止或移除失败会显示错误,停止操作可以重试。
初始拒绝名单(可扩展):
- 直接reading/writing工作空间外的敏感路径
- 未经确认的破坏性操作(由权限层控制的策略)
6. 权限模型
风险级别
| 风险 | 示例 | 默认政策 |
|---|---|---|
| 低 | 会话根目录内的 Read/Glob/Grep | 自动允许 |
| 中等 | 低风险 network/metadata | 政策确认或允许 |
| 高 | Write/Edit/Bash | 默认确认 |
用户配置的 MCP 服务器提供的工具(mcp_<serverId>_<tool>)归类为 medium, 与未声明有效风险的插件工具相同。MCP 服务器自行声明的风险级别不被信任, 这与用户已接受的插件 manifest 中的风险不同。在 ask 和 accept-edits 下, MCP 工具调用会显示审批卡片,原因为 "MCP server tool requires approval"; allow-session 授权会在该会话内对该工具名不再提示(授权仅保存在内存中)。 auto 自动允许,Plan/Goal 合约模式的硬拒绝仍然生效(D640,ADR mcp-tool-approval-risk)。
决策类型
allow-onceallow-sessiondeny
稍后可能会添加:
allow-always-for-toolallow-always-for-command-pattern
权限模式 (D115/D132)
高风险工具调用如何获得批准由权限模式控制:
| 模式 | Write/Edit | Bash / 插件工具 |
|---|---|---|
ask(默认) | 确认 | 确认 |
accept-edits | 自动允许 | 确认 |
auto | 自动允许 | 自动允许 |
显式外部工作空间路径是低风险行的一个例外:它是 仅在 auto 中允许自动; ask 和 accept-edits 都证实了这一点。
每个工具调用的解析顺序 (host-core tools.execute):
- 会话的持久化
permission_mode,除非是inherit - 应用程序设置中的全局
defaultPermissionMode(ask/accept-edits/auto) 3.ask
规则:
- 会话值存储在
sessions.permission_mode中 (inherit | ask | accept-edits | auto,默认inherit,架构 v5)和 通过session.configurepermissionMode设置。 - Plan 的硬拒绝胜过 Write/Edit 以及缺少
planSafeActions的插件 工具的所有权限模式。auto无法重新启用隐藏或拒绝的工具。 - 会话根目录内的低风险工具(
allow-once/allow-session/deny)自动允许 每种模式都和以前一样。 BrowserPreview是显式只读 UI 检查功能,并且是 在两种操作模式下均可用。- Plan 保留权限模式选择器。 Bash 在
ask下得到确认并且accept-edits,并且在auto下自动允许;因此 Plan 正在规划 意图,而不是严格的只读安全配置文件。 allow-session赠款继续在ask下运作,范围仅限于 会议;在BrowserPreview/ask下,根本不需要它们。- 暂存目录写入 (D114) 在每种模式下都保持无提示。
Computer由它的作用域授权治理,而不只由模式治理:创建授权始终是桌面 UI 的显式决定,auto从不签发授权,该工具也不存在allow-session授权。 在活动授权内,检查类动作自动允许,输入遵循授权的confirm-each(ask下唯一可用的策略)或autonomous-in-scope策略。Plan 与 Goal 直接拒绝该 工具(computer-use)。- UI:设置→分段全局默认;输入框在中显示每个会话的芯片 Agent、Plan 和 Goal 的菜单提供了三种有效模式,无需 单独的 global-default/inherit 条目。芯片和选定的菜单项 显示有效模式;选择一个项目会存储该显式会话 覆盖。现有的继承会话继续通过 全局设置,直到用户选择一种模式。
- 强制执行仅适用于 host-core; sidecar/model 从未被告知 模式并且不能影响它。
7. 权限流程
tool call
→ policy.evaluate()
→ allow? execute
→ need confirm? push to UI
→ deny? return tool error result2
3
4
5
权限确认:
- 本地确认会一直保持待处理,直到允许一次、允许会话、拒绝、取消或主机/进程关闭。批准后仍执行工具自身的超时限制。
8. 工具结果对模型的可见性
- 成功结果:给予模型
- 失败结果:提供给模型(带有错误信息)
- 用户拒绝:给模型明确的“用户拒绝权限”
- 敏感信息:在 persisting/displaying 之前进行编辑
9. 审计
各工具调用记录:
- 会话ID
- 转号
- 工具呼叫ID
- 工具名称
- 参数哈希/预览
- 存在显式路径时的 externalPathPermission 分类
- 决定
- 持续时间
- 成功/错误代码
MVP 可以通过写入 SQLite 或日志文件来启动。
审计行可以保留既有的分段时长字段用于取证检查:prompted (是否出示许可卡)、permissionWaitMs、durationMs(工具体)、 overheadMs(主机簿记)和 totalMs。拒绝调用携带工具体为零的相同字段。 这些是结构化审计字段,不会作为 timing 日志行写入进程日志。关于当前的 关键日志策略,请参见 09.日志记录和可观测性。
10. 操作模式矩阵
模式是一份定义文档(ADR mode-definitions)。它的工具策略只有两档,由定义(或分阶段运行的当前阶段)决定,并由 host-core 根据持久化的定义快照解析,从不采信运行时自报的模式:
| 策略 | Read/Glob/Grep | BrowserPreview | Write/Edit | Bash | 插件 |
|---|---|---|---|---|---|
agent | 允许 | 允许 | 权限策略 | 权限策略 | 已注册的风险策略 |
contract | 允许 | 允许 | 拒绝 | ask/accept-edits:确认;auto:允许 | 仅 plan-safe 操作 |
内置模式:Agent 使用 agent;Plan 使用 contract;Goal 是分阶段运行,其 work 阶段使用 agent。没有快照的旧 Goal 会话,以及 Host 无法解析的模式 id,一律回退为 contract(失败即关闭)。
Prompt admission first claims an existing staged run through the Host and uses its persisted snapshot without consulting the current catalog. Editing, removing, or making the catalog definition simple does not change an existing run. Without a claim, simple custom modes carry the current catalog definition to the runtime. An unavailable mode closes the admission turn without persisting a user message or invoking the sidecar. Plan-state projection may rename only Agent/Plan sessions; an inactive projection preserves Goal and custom-mode identity unless a legacy Goal proposal was approved. Live approval and restoration of a pending legacy proposal then show Agent; unrelated historical approvals do not rename a newly selected staged Goal. New Goal/custom sessions pass the catalog snapshot in the single Host creation call. Host validation, snapshot storage and session insertion share a transaction; a rejected snapshot or failed insert leaves neither an incomplete session nor an orphan snapshot. Legacy callers without a snapshot keep their existing behavior.
说明
contract在权限 UI 之前硬性拒绝 Write/Edit 和未声明planSafeActions的插件工具;直接调用 host 也无法绕过该矩阵。声明了非空列表的插件工具会被放行,但运行器仍会拒绝列表之外的操作(ADR 0211)。agent对 Write/Edit/Bash 和已注册插件工具使用权限卡片或所选的自动策略。- 用户选择 Auto 时,
contract下的 Bash 可能修改工作区或 scratch 状态;UI 必须让这一取舍可见。 Computer和定时任务工具只在内置 Agent 模式中可用,用户自定义模式中永远不可用。- Host 为分阶段运行自行发起的回合(loop 续跑、
onFail.goto)会把auto按accept-edits处理。用户发起的回合,以及审批通过后的回合,保持会话的权限模式。 - allow-session 只按 toolName 在当前会话内记住
- 会话授权跟随
sessionId跨项目标签页切换,不会被其他会话或临时会话继承
10. 1 Plan 控制工具与阶段完成
new_context 在所有模式下可用且无需确认:它只是请求运行时在下一个回合边界压缩上下文,达到硬预算时 host 本来也会这样做(见 02-agent-runtime §5.1)。SubmitPlan 只在 Plan 中可用,并且必须是其助手批次中唯一的工具调用。它通过 host-core 把完整 Markdown 字节保存为 .pi/plan/*.md 下的一个新唯一产物,然后创建一个待审批项。EnterPlanMode 只在 Agent 中可用,并且必须是其批次中唯一的工具调用。host 在任何切换之前校验持久化模式、提案类型和活动回合/配置边界;可见的工具列表只是指引,不是安全边界。
不再提供 EnterGoalMode 和 SubmitGoal。host 以 PLAN_GOAL_ACTIVATION_DISABLED 拒绝新的 Goal 提案;已存储的 Goal 审批仍可读取,且永远不会被转换为自主执行授权。
CompleteStage { summary, artifacts? } 只在分阶段模式运行的回合中提供。Host 把声明的产物写入其声明的工作区路径(contract 阶段写这些产物不需要 Write 权限),然后按顺序评估阶段的退出检查。run 检查通过正常的 Bash 权限路径(tools.execute 上的 modeGate)执行定义中的原样命令;命令、运行、检查序号或回合声明不匹配的调用会被 Host 拒绝。
10. 2 委派和子代理工具范围(D201、ADR 0062)
Task 仅在 Agent 模式下可用,并且仅当会话至少有 一个子代理定义。 Plan 和 Goal 是只读合同协商,因此 具有 Bash、Edit 或 Write 的代表将直接穿过它们。
定义声明其委托可以调用的工具。默认名称仅来自七个工作工具 Read、 Glob、Grep、BrowserPreview、Bash、Edit 和 Write。未声明 tools 时得到 Read、Glob、Grep;tools: "*" 表示全部七个。 无法识别的名称会被删除并带有解析警告。
文档可以用 tools: inherit 或 tools: [inherit, Bash] 选择继承父会话的 实时工具目录(ADR 0246 / D415)。Task 启动时运行时把 toolCatalog (含延迟的插件/MCP 工具)与可分配的额外工具取并集,再去掉 Task / TaskWait / TaskList / TaskStop、EnterPlanMode / EnterGoalMode、 asktool、new_context 和 ToolSearch。内置定义不默认开启。inherit 写在 Markdown 和设置里;host-core 会保留该标记,因此只有 inherit 的文档 仍能加载。插件工具、Skill 和 MCP 工具只通过这一 opt-in 到达委托, 不能写进可分配白名单。
没有 tools: inherit 时,委托可用的工具来自其定义,而不是其会话:它 无法因为父级拥有某个工具而获得该工具,会话也不能把修改权限借给只读 委托。委托调用由会话运行时构建并通过相同的 tools.execute 路径,因此 路径规则(§4)、Bash 规则(§5)、权限模式(§6)、操作模式矩阵(§10) 和审计(§9)保持不变,并针对拥有该调用的会话评估。
内置定义与用户定义还可以声明 permission: inherit | ask | accept-edits | auto(ADR 0089,默认 inherit)。使用默认的 inherit(包括所有内置定义)时, sidecar 不附加覆盖,委托使用会话的有效权限模式;因此父会话为 auto 时,明确的 外部路径也不会再次弹出授权卡。项目定义随仓库一起到来,其 scope 会在解析时被丢弃 并留下警告,克隆仓库永远不会获得权限升级。只有合格的内置或用户定义显式声明非 inherit scope 时,sidecar 才会附加它,并由 host-core 在该模式下裁决。scope 只是 权限模式覆盖:契约模式的硬拒绝和外部路径门禁(§4.1)仍然生效;显式 accept-edits 仅自动允许工作区和 scratch 根内的 Write/Edit,外部路径及其他 工具仍按其正常审批边界处理。
来自代表的权限请求带有提出请求的代表的姓名,因此 卡可以说明哪位代表想要通话(请参阅 04-ux/03-permission-ux.md §6a)。 会话范围的 allow-session 拨款仍按 toolName 和每个会话进行: 一名代表对 Bash 的批准适用于整个会议,包括 家长和其他代表。
在隔离会话(§4e)中,工具包含 Write 或 Edit 的子代理在自己的 worktree 中运行: 先为会话的未提交编辑建立检查点,再从会话 worktree 分出分支;它的 tools.execute 调用携带委派链 id delegationId,路径规则和 Bash 因此以该 worktree 为根。运行完成后 用与“合并到项目”相同的算法合回;冲突、失败或停止时保留子代理分支,报告会提示父代理 续跑处理。只读和仅 Bash 的子代理,以及非隔离会话的所有子代理,仍共享所属会话的根目录。
11. 插件工具
插件可通过 agentTools 贡献工具。Agent 模式能看到每一个已注册的 插件工具。Plan 和 Goal 只看到 planSafeActions 列表 非空的工具(ADR 0211 / D384):
- 舱单声明
- 用户授予
agent.tool.register3.PluginManager将它们注册到ToolHost中 4.执行经过统一的permission/audit/timeout包装器
没有 planSafeActions 的插件工具在 Plan 和 Goal 中对模型隐藏。 直接尝试返回 PLUGIN_DISABLED_IN_PLAN — _IN_PLAN 代码由两个合约共享 模式而不是每种重复 - 并且作为合同模式政策进行审核 否认。当列表存在时,host-core 放行该工具,plugin-runtime 拒绝列表外的任何 action, 返回 PERMISSION_DENIED。对于 Agent,缺失或无效的插件风险默认为 medium,并且从不 仅凭风险授予合同模式访问权限。
命名:
- 内部全名:
plugin.<pluginId>.<toolName> - 暴露给模型的名称:强制前缀
plugin_<pluginIdSafe>_<toolName>(D015) 以避免冲突
12. 未来的扩展
- MCP 工具
- 工具组切换
- 命令允许列表/拒绝列表
- 空运行模式
- 预览后应用补丁
图片生成与编辑
GenerateImages 是仅供 Agent 使用的高风险能力,可信桌面执行请求前必须由 host-core 授权。即使处于 Auto,Plan/Goal 仍被拒绝。取消、限制和结果语义见图片生成规格。
模型访问后台命令
Agent 模式下,模型通过 TaskList、TaskWait、TaskStop 管理本会话的后台 Bash,桌面和模型读取同一 Host 作业记录。runtime 带入已绑定会话身份,模型参数不能选择其他会话。Host 要求会话存在、属于本地并处于 Agent 模式,按 sessionId 与 id 校验读取和停止。未知与其他会话的 ID 均返回 NOT_FOUND。
等待不会终止进程。单次 Host 观察最多一秒,可在新输入或取消时放弃,等待期间不持有全局状态锁。终态通知在输出收尾后发出。显式停止复用主管的进程树清理,并返回实际终态或带 stopRequested 的运行中快照。查询互不消耗日志。有限历史与 Host 重启仍限制旧 ID 可查询的时间。
在 macOS 上,如果 Host 已成功向进程组发送停止信号并回收了子进程,但退出清理被拒绝,Host 会在短时间内复查进程组是否仍存在。只有确认进程组已消失,才清除这次权限错误。