Zed 交互式 ACP 插件方案
September 10, 2026 · View on GitHub
目标
提供一个独立的 dsh-acp-interactive Cordis 插件,让 Zed 通过 Agent Client Protocol(ACP)获得接近 Codex ACP 和 Claude ACP 的交互体验。Zed 负责会话界面、流式内容、工具卡片、审批控件和计划展示;DeepSeek Harness(dsh)继续负责模型调用、agent loop、工具执行、工作目录、沙箱、权限策略和生命周期。
这不是 Codex 插件,也不是 Zed 扩展。协议适配器作为 dsh 插件运行,通过 JSON-RPC stdio 与 Zed 通信。
本项目采用“组合优先、上游贴合”的产品方向,不以追赶其他社区实现的功能数量为目标。Harness 已发布的领域插件继续拥有行为,ACP 层只承担适配、协商、投影和精确生命周期绑定;公开 CI、固定/最新上游兼容信号和真实 Zed 验证优先于新增功能。完整原则与实施顺序见上游贴合、公开 CI 与 Zed 验证规范。
是否需要官方支持
运行不需要 Zed 为 DeepSeek 或 dsh 增加专用内置支持。只要当前 Zed 版本支持在 agent_servers 中启动自定义 ACP agent,就可以把 dsh 的启动命令配置进去。Zed 官方支持会影响非标准 _meta 扩展和新 ACP 功能的兼容速度,但不是第一阶段的前置条件。
DeepSeek 官方同样不需要修改 API。适配器连接的是 dsh 的 agent 和事件模型;实际模型由外围 Cordis 组合注册的 provider 选择。组合 dsh-settings-file、dsh-credentials-local 和 dsh-llm-pi-ai 后,ACP server 可以读取当前用户 dsh home 中与 Pi Agent 桌面版相同的 provider profile 和凭据引用,同时保留各进程与 session 的隔离。
包边界
@deepseek-ai/dsh-acp保持 automation-only,继续服务程序化 ACP 客户端和进程外 subagent。dsh-acp-interactive是面向编辑器和人的独立 UI transport,不被 automation ACP 的最小协议面约束。- 本仓库发布
dsh-acp-interactivelauncher 与config/cordis.yml完整组合;Zed 直接启动安装后的命令,不依赖 DeepSeek Harness 源码 checkout。 - ACP 只投影 dsh 已经拥有的状态,不让 Zed 代替 dsh 执行工具或扩大文件访问范围。
独立 launcher 使用经过评审的 editor profile,不自动复制官方完整 profile。config/editor-profile.json 固化准入规则、选入能力、暂缓能力和官方参考快照;对账检查只报告候选包、人类命令、必要 provider 与关键 consumer 的变化,必须经显式评审后才修改 config/cordis.yml。当前选入本地 filesystem search 及其 cooperative timeout policy、web,以及进程内 subagent;LSP、持久终端、后台/continuable subagent 与 workflow 暂缓。相关决定见 Editor Profile Agent Note 与 In-Process Subagents Agent Note。
自包含启动器的长期约束记录在 Agent Note。
数据流
Zed Agent Panel
| ACP JSON-RPC over stdio
v
dsh-acp-interactive
| create/followup/cancel + session event projection
v
dsh agent loop -> selected provider (DeepSeek or user-configured route)
|
+-> dsh tools -> sandbox / subprocess / filesystem
+-> approval/request -> ACP permission dialog -> decision returned to dsh
工具展示不按工具名分支。插件读取工具注册表中的 presentCall、presentResult 和持久化的 presentationMeta,再按 generic、terminal、diff 等工具自有展示意图映射到 ACP。
第一阶段
第一阶段交付一个可由 Zed 启动的新会话通路,并覆盖最影响编码体验的投影:
session/new、session/prompt、session/cancel和精确的单次请求结算;- assistant 文本增量映射为
agent_message_chunk; - reasoning 增量映射为
agent_thought_chunk; tool/call/tool/result映射为带状态、位置和内容的工具卡片;- 文件修改映射为 ACP diff,文件位置保留给 follow-along;
- 终端展示意图映射为 Zed terminal card;不支持该能力的客户端得到文本回退;
approval/request映射为session/request_permission,只提供单次允许或拒绝;todo/write映射为完整 ACP plan;session/title映射为session_info_update;独立 launcher 组合 Harness 的 first-prompt LLM provider,在即时确定性回退之后通过主请求已记录的精确 route 异步生成并持久化标题,ACP transport 不实现标题领域逻辑;- 请求上下文与用量映射为
usage_update; ctx.commands映射为available_commands_update,并支持直接执行斜杠命令。
第一阶段的普通 prompt 只接受 ACP text block。图片、音频、embedded resource、额外工作目录和客户端提供的 MCP server 会被明确拒绝,不会静默丢弃。
第二阶段
第二阶段交付持久会话发现、恢复与关闭:
session/list从ctx.sessionQuery的 live 优先语料库返回按创建时间倒序排列的会话,支持精确 cwd 过滤与日志标题;缺少绝对 cwd 的会话不进入 ACP 结果;session/load先校验持久会话及其 cwd,再通过ctx.agents.resume()恢复 agent,并重放已组装的用户/assistant 消息、reasoning、工具卡片、最后的 plan、标题、用量和命令目录;session/resume恢复相同的 dsh 上下文,但不重放历史,只发送当前命令目录;session/close取消正在执行的命令或模型轮次,等待 agent、ACP 输出队列和 continuable 后代完全停稳,通过标准 session flush durability barrier,再释放精确归属的AgentHandle;checkpoint 失败仍完成在线资源释放并明确失败;- 恢复历史包含 ACP 无法无损表示的丰富内容时明确失败,不会静默丢弃内容;
- 示例组合加入 JSONL persistence、checkpoint policy 和每进程内存 SQLite session query,并由真实 Loader 快照覆盖 list/load;并发编辑器 server 只共享 JSONL 真源,不共享单 owner 的派生索引。
本阶段不声明 session/delete。SessionPersistence 尚未提供跨后端删除操作;transport 直接删除 JSONL 文件或修改 SQLite 私有表会绕过持久化所有权与对账。session/list 目前返回完整单页且不伪造 updatedAt,稳定 cursor 和低成本最后活动时间由 session-query 能力提供后再接入。
第三阶段
第三阶段交付 ACP session config options 形式的模型与权限控制:
session/new、session/load和session/resume返回完整configOptions,模型按 provider 分组,权限来自ctx.permissionPresets的部署配置;session/set_config_option的模型值编码完整 provider/model route,只接受当前目录公布的值,并通过 agent-scoped model selection 在下一次 prompt assembly 生效;- 恢复会话以最后一条
request/header的 route 作为当前模型;目录不再公布该 route 时仍显示一个 current-only 选项,不会把历史选择改写成默认值; - 权限 selector 复用
/permission的唯一在线写路径,把 preset、sandbox mode 和 approval policy 一起持久化;运行中的 session 也接受切换,事件立即落账,对后续受限调用与审批请求生效; - LLM adapter topology 变化后发送完整
config_option_update,直接执行/permission后也刷新 selector; - selector 请求按 session 串行化,prompt 不会越过尚未结算的配置切换。
本阶段不增加独立 reasoning-effort selector。选择模型时采用 adapter 为该模型解析出的默认 effort。
第四阶段
第四阶段补齐编辑器内的协作与丰富输入:
- 组合
ctx.planMode时,session/new、session/load和session/resume返回原生 ACPdefault/planmodes,session/set_mode委托标准 plan-mode 服务,plan/mode事件发布current_mode_update; - 所选精确模型公布 reasoning efforts 时增加
thought_levelselector,切换在下一次 prompt assembly 生效,切换模型清除显式 effort 并采用新 route 默认值,恢复会话保留最后 request header 中的精确 effort; - baseline
resource_link变成持久用户消息中的明确引用;组合 attachment store 后公布 inline image 能力,图片在消息排队前校验模型 route、批量持久化并替换为 durable reference,加载历史时重新校验并投影图片字节; - 组合
ctx.userQuestions后,为本连接精确拥有的根 agent 注册稳定 ACP form elicitation provider,结构化投影普通问题、多选、自由文本和 plan-review detail,并区分用户关闭、turn 取消与 request cancellation; - 示例组合加入 attachment store、plan mode、user-questions 与
ask_user_questionconsumer,并公布可选 vision route。
该版本阶段尚未接入 MCP server;0.8.0 的当前设计见下文第六阶段。Additional directories 的多根沙箱与跨能力强制执行已划归独立的 dsh-additional-directories DSH 插件项目,本仓库在其完整能力闭包可用前继续明确拒绝非空值。音频、embedded resource 和工具结果图片卡片明确失败或保持文字投影;form elicitation 只在客户端声明对应稳定 ACP capability 时启用。
第五阶段
第五阶段把 user-invocable skill 接入 Zed 的斜杠菜单,同时保留真实命令的直接执行语义:
- ACP 目录合并精确 agent scope 下的
ctx.commands与按 session cwd 发现的userInvocableskills,真实命令与 skill 同名时由命令胜出; commands/change与skills/change分别刷新各 session 的完整目录,skill provider 返回不完整结果或失败时保留上一次完整 skill 目录,连接关闭会取消进行中的发现;- 真实命令继续由
commands.execute()直接执行;精确匹配的 user-invocable skill 作为普通用户消息进入 agent,由dsh-tool-skill完成已落账的agent/pre-step内容注入;未知斜杠名称保持 unknown command,不会进入模型; - 每次目录查询和显式 skill 解析都携带精确 agent scope、cwd 与请求取消信号,不共享 session 间的目录或调用状态。
第六阶段:Session-scoped MCP(0.8.0)
- ACP v1 stdio 是基线能力,初始化仅额外公布真实组合支持的 HTTP capability;SSE、ACP 代理和未知 transport 明确拒绝;
- new/load/resume 请求中的完整 MCP 配置先完成校验,再在尚未发布的 agent setup 中安装;stdio executable 与 argv 直接传递,env 与 HTTP headers 不写 session log、不进入普通诊断或模型上下文;
- 每个 session 使用私有 Cordis root 运行已发布的
dsh-mcp-client,tools.register通过窄 adapter 委托给精确 agent scope。这样activeServerNames保持逐私有 root 隔离,同名 server 可跨 session 并存,模型可见工具名仍是稳定的mcp__<server>__<tool>; - 初始连接和工具发现采用 strict startup,任一 server 失败会销毁整个私有 host 并回滚先前 server。Load/resume 不持久化、不继承历史进程或认证配置,只使用当前请求;
- 取消、session close、连接 teardown 与创建中断等待工具调用、transport、重连 timer、工具注册和 stdio 子进程静止。MCP 工具仍通过 Harness registry 的展示函数进入通用 ACP tool card 投影。
第七阶段:进程内 subagent(1.3.0)
- 组合已发布的
dsh-subagent注册表、spawn/fork进程内 backend 与两条dsh-tool-subagent行(subagent、subagent_fork)。委托语义完全由 Harness 拥有:子 agent 是同一注册表上的普通 Agent,拥有自己的 Session、origin: subagent与parentSession血统,父 agent 只以工具结果收到子 agent 的最终输出; - 每次委托都在父 turn 内前台等待(
enableRunInBackground: false)。session/cancel、session/close与连接拆卸沿父 agent 的取消信号先停掉子 agent 再到达静止,父 loop 的调度器在 turn 结算前等待每个进行中的工具调用,因此 ACP 层不需要第二套生命周期;并行委托保留,因为工具本身声明并发安全; - 子 agent 继承父 session 的 sandbox 覆盖,approval policy 固定为
never,并通过工具行的toolFilter移除ask_user_question,因此子 agent 不会向编辑器发起 permission request 或提问;bridge 的审批与提问 listener 本来也只回答精确归属的根 agent,升级路径仍归父 agent; - ACP 层只做归属与投影。seam 不携带父工具调用 id,按时间或 label 归属会让并行委托串卡,因此每个
tools/execute分发都在自己的 delegation origin(NodeAsyncLocalStorage)中运行,subagent/start在同一异步链上发布,从而精确归属到各自的tool/call;父 session 的持久subagent/catalog事件提供 label;子 session 及其后代的tool/call、tool/result、assistant/message与subagent/end折叠为父卡片内有界的 transcript,并以替换 content 的tool_call_update整体重发(Zed 按替换处理卡片 content);父 agent 自己的tool/result经通用工具投影结算卡片并把 transcript 保留在结果之前,结算后卡片解除链接,迟到的子事件被丢弃;不投影子 agent 的 reasoning 与实时增量,只以已落账的assistant/message为来源; - 卡片
_meta.dsh_subagent记录子 session id、provider、label 与最终 stop reason;不发送 Zed 私有的subagent_session_info,因为 Zed 没有为外部 agent 消费该键的路径,而未来的消费者可能试图通过本连接打开子会话;session/list排除origin: subagent或带parentSession的会话,session/load/session/resume明确拒绝它们,与官方dsh-acp一致; - 后台 job、continuable child、
send_message/interrupt_agent/list_agents与进程外 ACP/Codex/Claude Code backend 暂缓:它们的生命周期越过父 turn,稳定 ACP v1 尚无对应表达(上游 subagents RFD agentclientprotocol/agent-client-protocol#1992 仍未合并,claude-code-acp的嵌套 transcript 也依赖它)。
后续阶段
后续开发按协议基线、Editor Profile 与 ACP 投影闭包、session-scoped MCP、完整 Session 管理、丰富内容与实时 UI 五个阶段推进。Additional directories 由独立 DSH 插件项目负责,不属于本仓库的交付路线。每个阶段的交付范围、验收条件和先后依赖见后续开发路线图。
Zed 连接方式
全局安装后,在 Zed 的 settings 中登记一个自定义 agent server。Windows 示例:
{
"agent_servers": {
"dsh-acp-interactive": {
"type": "custom",
"command": "C:/Users/you/AppData/Roaming/npm/dsh-acp-interactive.cmd",
"args": []
}
}
}
启动命令加载包内 config/cordis.yml,不依赖 DeepSeek Harness 源码 checkout。stdio 的 stdout 只承载 ACP 帧;诊断必须写 stderr。
第一阶段验收
- Zed 能创建 session、提交文字任务并取消正在运行的任务。
- 文字与思考在生成时分别流式出现,不等待最终消息。
- 读文件、修改文件和 shell 命令使用工具声明的卡片、位置、diff 与终端内容展示。
- dsh 的 ask 策略在 Zed 中出现审批控件,拒绝不会被解释成允许。
- todo、标题、命令目录与上下文占用变化能刷新 Zed UI。
- 多个 Zed session 之间不串流、不串审批、不互相取消。
- 插件卸载或 stdio 断开时,所有由它创建的 agent 先停止并达到静止,再解除注册。
第二阶段验收
- Zed 能发现同一 cwd 下的持久会话,并显示日志中已有的标题。
session/load只重放组装后的消息一次,同时恢复工具卡片、计划、标题、用量与命令目录。session/resume恢复模型上下文但不重复发送历史。session/close在 prompt、斜杠命令、恢复中、连接断开及并发关闭场景下都能达到完全停稳,成功返回前已完成标准持久化 checkpoint,且不会释放其他连接拥有的 agent。- 无法无损投影的持久内容和不受支持的分页 cursor 明确失败。
- 真实 Loader 组合能在新进程中列出并加载磁盘上的 JSONL 会话。
第三阶段验收
- Zed 新建、加载或恢复 session 后能看到按 provider 分组的模型和当前权限 preset。
- 模型切换只影响下一次进入 prompt assembly 的 step,运行中的 step 保持已组装 route。
- 未公布的模型值、未知权限 preset 和未知 config id 明确失败,不改变当前选择。
- 权限切换写入 preset、sandbox mode 和 approval policy 的标准 session 事件,运行中的 session 也允许切换,切换从后续受限调用与审批请求开始生效。
- 多个 session 的模型选择、权限选择和串行化队列互相隔离。
- 真实 Loader 快照覆盖新建与加载响应中的完整 config options。
第四阶段验收
- Zed 新建、加载和恢复 session 后能看到
default/planmode,切换只通过ctx.planMode生效且日志事件会刷新当前 mode。 - 支持 reasoning effort 的模型显示
thought_levelselector;effort 切换影响下一次 step,模型切换重置显式 effort,恢复保留已记录 effort。 - Resource link 作为明确引用进入模型历史;inline 图片只在 attachment store 和精确 route 都支持时准入,取消不会排入迟到消息,持久历史能重放图片。
ask_user_question与 plan review 通过 ACP form elicitation 往返选项、多选与自由文本;外部 agent、不支持 elicitation 的客户端、关闭和取消都明确失败。- 多个 session 的 mode、effort、图片准入和 elicitation 不串流,连接拆卸会注销 provider 并等待正在进行的准入静止。
- 真实 Loader 快照覆盖 modes、reasoning selector、图片能力、plan 与 ask 命令目录、
session/set_mode更新,以及一次经 ACP form elicitation 完成的两步模型/工具往返。
第五阶段验收
- 真实 filesystem provider 发现的 user-invocable skill 出现在菜单中,
/<skill-name>经标准 pre-step 注入完成一个模型轮次;model-only skill 不出现。 - 同名 command/skill 由 command 胜出;未知名称明确报错,不会成为普通 prompt。
- skill 注册、注销和 provider invalidation 会刷新对应 session 的 ACP 目录;不完整观察不清空最后一次稳定目录。
- 显式 skill 解析可取消,两个 session 的 scoped skill 目录与调用互不影响。
第六阶段验收
- 真实 stdio 与 HTTP MCP 完成配置映射、启动、工具发现、调用、通用结果投影和关闭;初始化不公布 SSE 或 ACP transport。
- 两个 session 可同时安装同名 server,工具注册、执行结果、失败、取消与关闭互不影响,恢复后的模型可见名称保持稳定。
- new/load/resume 的任一 startup 或响应组装失败都会回滚所有已启动 server;resume 移除、更换或失败时不复用旧连接。
- prompt 取消、session close、连接断开和创建中断均不产生迟到工具更新,并在返回前确认 transport、timer、注册和子进程完全静止。
- Packed install 在 Harness checkout 外解析 MCP runtime closure 并通过真实 launcher ACP 会话。
第七阶段验收
- 真实 launcher 在
read-onlypreset 下完成一次subagent委托:卡片以 description 为标题,_meta.dsh_subagent指向子 session,结算时 transcript 在结果之前,且没有任何 permission request。 - 同一步骤内并行的两次委托各自归属到自己的卡片,transcript 与结果互不串流;嵌套委托在直接子 agent 之下缩进呈现。
session/cancel与 bridge 释放都在父 turn 内停掉子 agent,prompt 以cancelled结算,卡片状态为failed且 transcript 以Subagent aborted结尾;结算后的子事件不再产生更新。- 子 session 不出现在
session/list,session/load/session/resume明确拒绝;子 agent 文本不会成为顶层agent_message_chunk。