ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

oh-my-pi 的 xd://resolve 决议设备机制:基于纯文本 write 的暂存预览确认协议解析

2026/9/11 17:29:55 拓冰建站 浏览量
oh-my-pi 的 xd://resolve 决议设备机制:基于纯文本 write 的暂存预览确认协议解析 oh-my-pi 的 xd://resolve 决议设备机制基于纯文本 write 的暂存预览确认协议解析【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi导读本文围绕 oh-my-pi⌥ Coding agent with the IDE wired in中resolve-device-reminder系统提醒模板展开深入剖析其背后的暂存预览staged preview→ 纯文本决议协议当工具如 AST 编辑产出预览而非直接改文件时Agent 只需通过write向xd://resolve或xd://reject写入一句话理由即可应用或丢弃。读者读完将掌握该协议的设计动机、调用形状、软性工具约束SoftToolRequirement的注入机制、调度与渲染链路以及测试用例所验证的行为边界。1. 一切从一个 3 行的系统提醒开始在packages/coding-agent/src/prompts/system/resolve-device-reminder.md中存放着整个 resolve 机制的模型可见入口——一个只有 3 行的系统提醒模板system-reminder {{toolName}} result above: PREVIEW — no files changed. Finalize now with write: write a one-sentence plain-text reason to xd://resolve to APPLY, or xd://reject to DISCARD. /system-reminder虽然文字简短但它精确地定义了整套交互协议的关键语义{{toolName}}是运行时占位符由buildResolveReminderMessage在注入前用产生预览的那个工具名替换如ast_edit见 resolve.ts 中的实现PREVIEW — no files changed明确告知模型上一轮工具结果只是暂存提案文件系统尚未被修改避免模型误读预览为已落盘的结果Finalize now with write协议规定确认动作必须通过write工具完成——不是新工具、不是特殊 JSON 协议而是把纯文本理由写给xd://虚拟 URLone-sentence plain-text reason理由必须是一句纯文本它会被记录到决议结果中作为审计信息xd://resolve应用 /xd://reject丢弃两条路径构成完整的通过/否决二态决策。该模板的完整生命周期由源码支撑它由resolve.ts的buildResolveReminderMessage渲染为一条role: custom、customType: resolve-reminder的隐藏消息display: false并通过会话的软性工具约束注入模型上下文详见第 4 节。2. xd:// 协议会话绑定的虚拟工具设备要理解xd://resolve为何能无需工具 schema 即可调用需要先看 xd-protocol.ts 定义的内部 URL 协议协议前缀常量XD_URL_PREFIX xd://parseXdUrl(input)解析xd://后的设备名对包含/?#的畸形目标返回null防止路径注入XdProtocolHandler注册scheme xd且immutable trueresolve/write都要求当前会话挂载了xd上下文xd:// is not mounted in this session.即设备是会话绑定的不能脱离会话凭空调用。在resolve.ts中定义了三个决议设备名与对应的虚拟路径常量设备名路径写入内容语义resolvexd://resolve一句话应用理由APPLY 暂存的预览把暂存动作落盘rejectxd://reject一句话丢弃理由DISCARD 暂存的预览不落盘proposexd://propose计划slug匹配local://slug-plan.md仅计划模式plan mode下提交计划待审批对应实现见 resolve.ts 的常量定义。isResolutionDeviceName用于识别这三个名字resolutionDeviceUsage为每个设备生成一行使用说明是read xd://device时返回的唯一文档——设计上有意不让协议文档长期占据系统提示而是在相关时刻才教给模型调用形状。值得注意的设计取舍源码注释明确写出Nothing rides the system prompt——决议流程的所有调用形状都不是常驻在系统提示里的而是由产生暂存动作的流程如预览提醒、计划模式提示在相关时机即时教导从而避免系统提示膨胀与缓存抖动。3. 暂存预览工具如何先提案、后落盘以 ast-edit 工具提示词 为例可以直观看到暂存语义在真实工具上的落地Matches are STAGED as a proposal, not applied: finalize by writing a one-sentence reason toxd://resolve(apply) orxd://reject(discard).也就是说ast_edit的结构化改写不直接修改文件而是产生一个暂存提案。源码层面该机制的核心常量是 PREVIEW_PENDING_NOTICEStaged as a proposal — files NOT modified yet. To apply: write a one-sentence reason to xd://resolve. To discard: write to xd://reject.这条模型可见的横幅会前置到暂存预览的工具结果文本上。源码注释解释了两个关键原因TUI 中的⟨proposed⟩徽标不会传给模型且预览 diff 与已应用编辑的输出字节级一致——如果没有这行提示模型会把结果误读成已经应用必须显式声明文件尚未修改这是暂存语义的诚信基础。4. 软性工具约束SoftToolRequirement零 tool_choice 开销的提醒注入暂存预览一旦存在Agent 循环必须温和地提醒模型去决议但又不能强制——agent-session.ts的nextToolChoiceDirective()实现见 agent-session.ts实现了三级优先级HARD 强制选择用户强制、eager-todo 等消费队列生成器否则若存在非强制暂存预览的头部返回一个SoftToolRequirement——这是一个 PEEK不推进/不弹出队列toolName: writesatisfies: isPreviewResolutionToolCallreminder: [buildResolveReminderMessage(head.sourceToolName)]否则返回undefined。isPreviewResolutionToolCallresolve.ts是合规性判定函数只有write且目标路径解析后设备名为resolve或reject才满足约束——一个写到别处的普通write是绕路不满足该约束。这套设计带来的关键收益源码注释与测试双重印证合规轮次零 tool_choice 变更模型只要乖乖写xd://resolve/xd://reject就不会触发tool_choice变化从而不使 prompt-cache 的 messages-cache 失效提醒是中途追加而非主机侧 steer提醒作为稳定的 mid-history 追加注入一次每个暂存头部一次不会搅动已缓存的前缀仅当模型拒绝配合时才升级为强制write非强制在先、强制兜底兼顾效率与可靠性。测试 agent-session-resolve-reminder.test.ts 验证了这一点注册暂存处理器后nextToolChoice()返回undefined不是硬强制而nextToolChoiceDirective()返回isSoftToolRequirement true的软约束其satisfies对xd://resolve与xd://reject均返回 true、对/tmp/out.md返回 false且 steer 队列长度为 0。5. 调度执行从一句话理由到 apply/reject 回调模型写入xd://resolve reason后由write工具的 xd 分发路径write.ts 中的 dispatch 逻辑调用dispatchResolutionDevice(session, resolve, reason)。核心实现在 resolve.ts 的 dispatchResolutionDevice对xd://propose要求计划模式激活且存在planProposalHandler否则抛出ToolError对resolve/reject先取队列中的暂存 invokerpeekQueueInvoker优先其次peekPendingInvoker无 pending 动作时reject被视为成功取消返回Nothing to reject; no pending action remains.而apply会报错No pending action to apply — xd://resolve is only valid while a staged preview is pending.并附上计划模式提示有 pending 动作时构造{ action: apply | discard, reason }调用 invoker结果携带ResolveDetailsaction、reason、sourceToolName、label 等回传给渲染层与事件控制器。5.1 暂存处理器的注册与唯一性任何希望获得预览/应用语义的工具都通过 queueResolveHandler 注册queueResolveHandler(session, { label: AST Edit: 1 replacement in 1 file, sourceToolName: ast_edit, apply: async (reason) ({ content: [{ type: text, text: Applied }] }), reject: async (reason) { /* 可选 */ }, });关键实现细节唯一 idpending-action:${sourceToolName}:${pendingPreviewSeq}其中pendingPreviewSeq是单调递增后缀保证堆叠的多个预览互不覆盖非强制注册注册进 tool-choice 队列的 pending-invoker 注册表不改动tool_choiceapply 失败不丢失暂存onApplyError钩子会在apply()抛错例如ast_edit重叠替换时以同一 id 重新注册pending invoker让模型可以否决或修复后重试——这正是runResolveInvocationresolve.ts与 rethrow 配合的效果错误重新抛出成功路径上的移除被跳过恰好消费一次apply 成功或 discard 完成后调用removePendingInvoker(id)暂存动作只执行一次。5.2 决议结果的数据形状resolveDispatchDetailsresolve.ts从write执行结果的details.xdev信封中解析出ResolveDetailsaction、reason、可选的sourceToolName、label、sourceResultDetails。这套一致形状让 TUI 渲染器、事件控制器如计划审批钩子都能消费。6. TUI 渲染决议动作的流式预览与结果块决议设备在 TUI 侧也有完整的渲染支持resolve.ts 的渲染部分调用预览renderResolutionDeviceCall流式安全首行截断到 72 字符标题分别为Resolve/Reject/Propose图标为pending结果渲染resolveRenderer.renderResult反色整行块徽标区分三种状态——proposed - resolved成功绿、proposed - rejected警告黄、Failed错误红下方以斜体展示模型写下的一句话理由内联合并inline: true, mergeCallAndResult: true调用与结果合并展示减少界面噪音。渲染器通过 write 工具的 xd 委托机制renderers.ts以resolve和reject为键注册驱动使得设备写入与旧式 resolve 工具转录绘制出同一区块。7. 行为边界与会话生命周期测试给出的保证agent-session-resolve-reminder.test.ts 用四个用例锁定了协议的行为契约提醒经非强制软约束投递第 101-123 行暂存存在时tool-choice 队列不产生硬强制软约束的satisfies只认xd://resolve/xd://reject提醒是customType: resolve-reminder的自定义消息steer 队列为空。会话边界清理第 125-138 行new/switch/branch三种逻辑会话转换后pending invoker 与软约束指令均被清空——暂存预览不会跨会话泄漏。生产链路分发与闸门排空第 140-156 行dispatchResolutionDevice(session, resolve, looks correct)恰好执行一次apply随后nextToolChoiceDirective()返回undefined——暂存闸门被排空。幽灵闸门排空第 158-176 行当 invoker 全部不可见facade 模拟时reject不报硬错误而是成功返回并清空残留状态——与第 5 节reject 是请求到达无暂存终态的设计一致。8. 设计要点小结与实践建议从这份 3 行提示模板出发oh-my-pi 的 resolve 机制沉淀出的设计原则值得复用提示词即协议入口细节在源码{{toolName}}占位符 一句话指令承载模型可见的全部协议其余机制全部隐藏在resolve.ts、agent-session.ts与xd-protocol.ts中系统提示保持极简普通工具 虚拟 URL而非新协议决议动作复用write工具、复用xd://内部 URL 协议packages/coding-agent/src/internal-urls/xd-protocol.ts没有引入新的 JSON 工具调用形状天然兼容既有工具生态软约束优先、强制兜底SoftToolRequirement 让合规轮次零tool_choice开销保住 prompt-cache仅在模型不配合时才升级为强制write暂存动作恰好执行一次pending-invoker 的唯一 id 与消费后移除保证堆叠预览不串扰、不重复执行错误可恢复apply 失败时暂存保留在同一 id 下模型可否决或修复重试会话边界干净切换/分支/新建会话都会清除残留暂存杜绝幽灵闸门。对于希望在自有 Agent 中实现先预览后确认的工具作者可直接复用queueResolveHandler这一规范入口——只需传入label、sourceToolName与apply/reject回调即可获得完整的提醒注入、软约束合规、调度分发与 TUI 渲染能力而无需在会话层新增任何抽象。相关参考文件提醒模板packages/coding-agent/src/prompts/system/resolve-device-reminder.md协议核心实现packages/coding-agent/src/tools/resolve.tsxd:// URL 协议packages/coding-agent/src/internal-urls/xd-protocol.ts软约束注入点packages/coding-agent/src/session/agent-session.tswrite 工具分发packages/coding-agent/src/tools/write.ts暂存语义工具示例packages/coding-agent/src/prompts/tools/ast-edit.md行为契约测试packages/coding-agent/test/agent-session-resolve-reminder.test.ts【免费下载链接】oh-my-pi⌥ Coding agent with the IDE wired in项目地址: https://gitcode.com/GitHub_Trending/oh/oh-my-pi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考