🎁 福利专区全网大模型免费应用 + 新用户福利 + 注册活动入口,低成本玩转 AI

ACP 增强桥接 grunmin/dsh-acp-enhanced

DeepSeek Harnessv0.11.1
grunmin11 天前有提交已提供中文文档扫描:含敏感能力code

为 DeepSeek Harnessdsh打造的增强版 Agent Client ProtocolACP服务器,面向…

40.2/100
综合分
7
GitHub 星标
688
npm 周下载
—/5
用户评分

数据截至 2026/10/23 · 周下载取自 npm 全网(含 CI / 镜像),非本站安装数;综合分由星标、活跃度与兼容性检查合成。

安装命令 · 需先装 dsh CLI 引擎
dsh plugin --profile web add dsh-acp-enhanced
npm 源 · dsh-acp-enhanced 的发布者账号已校验与本仓库一致

状态速览

判定依据:实装验证 · 静态安装检查 · 自动扫描 · 仓库维护状态
信任档位:已验证
是什么
dsh 原生插件 · market
装得上吗
本站已在 dsh 0.2.1-rc.5 下真实安装成功
非静态推断
L4 · 真实安装等级说明 →
安全吗
本站尚未对该插件做风险分级
暂未覆盖,不等同于无风险
还在维护吗
活跃:最近一次提交在 11 天前
档位由哪些信号合成

本站实装验证(真实安装 L4 覆盖全站;L5 为运行时冒烟或实机截图,覆盖范围小于 L4) · 验证所用 dsh 版本 · 静态安装检查 · 风险分级 · 仓库维护状态。 下方各区块是它的证据明细。

验证判据与等级说明 →
社区实装还没有人打卡 · 装过的第一个就是你
安装兼容性检查✓ 自动检查通过
展开查看检查明细 ↓

以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。

✓npm 包dsh-acp-enhanced @ 0.11.1
✓Node 引擎未声明 engines.node
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明

验证方式:npm registry 存在性 + package.json 静态校验 · 最后检查 2026/11/1 07:02:55

安全扫描自动 · 静态分析源码
v2.7.0
🔵 扫描:含敏感能力尚无人工评估
展开查看扫描说明与证据 ↓

静态扫描发现插件具备触达本地系统的能力:执行外部命令、读写/删除本地文件、读取剪贴板或密钥类环境变量(至少其一)。这是能力清单,不是危险判定——具体能力项与证据见下方,安装前请自行判断是否需要。

读写/删除本地文件
查看扫描证据(共 3 处,展示前 3 条)
  • 读取本地文件
    lib/index.js:86
    const parsed = JSON.parse(readFileSync(effortMemoryFile, 'utf8'))
  • 写入本地文件
    lib/index.js:100
    writeFile(effortMemoryFile, JSON.stringify(effortMemory, null, 2)).catch((error) => {
  • 写入本地文件
    lib/index.js:459
    writeFile(tracePath, `${JSON.stringify({ time: Date.now(), ...entry })}\n`, { flag: 'a' }).catch((error) => {
扫描于 2026/11/3(0 天前) · 范围 入口闭包 + 全包扫描 · 来源 npm 发布包

静态扫描只反映源码中出现的调用,不构成安全背书;人工评估与自动扫描相互独立、互不覆盖。

依赖的 DSH / Cordis 模块
@deepseek-ai/schemastery@deepseek-ai/cordis@deepseek-ai/cordis-plugin-include@deepseek-ai/cordis-plugin-loader@deepseek-ai/dsh@deepseek-ai/dsh-agent@deepseek-ai/dsh-agent-instructions@deepseek-ai/dsh-llm@deepseek-ai/dsh-mcp-client@deepseek-ai/dsh-permission-presets@deepseek-ai/dsh-session@deepseek-ai/dsh-session-query
给这个插件打分还没有人投票↓
用户评分
还没有人投票,来当第一个

界面预览

2 张 · 点击查看大图

功能亮点

  • 块级流式输出
  • 用量与统计遥测
  • 模型与推理强度切换
展开完整介绍(132 字)↓

增强版 ACP(Agent Client Protocol)服务器,专为 DeepSeek Harness(dsh)打造——Zed 编辑器的即插即用桥接:块级流式输出、用量与统计遥测、模型与推理强度切换、权限预设、会话恢复与归档。安装:dsh plugin add

v0.11.02026-10-22
· 支持 dsh 0.2.1,并修复 {{cwd}} 导致每轮失败的问题 dsh-acp-enhanced@0.11.0 在声明的版本范围里写入了 0.2.1 这一线,在 CI 里启动它,去掉 @deepseek-ai/dsh-invariants,并修复了让该版本线上每一轮都失败的缺陷。lib/ 代码没有改动:范围本来就包含 0.2.1,所以这次发布只是改了声明,外加支撑它的证据——还有一处不得不改的预设修复。 迁移记录:docs/plans/2026-10-18-dsh-0.2.1-support.md。 · 修复:{{cwd}} 让 0.2.1 上每一轮都失败 内联的 standard / ptc / cordis 预设带着 persona.config.suffix: … {{cwd}}。在 0.2.1 上,dsh-agent-loop 只把 provider 和 model 注册为提示词变量(cwd 在 0.1.7 之前一直有注册),而 dsh-system-prompt 严格插值——于是用这些预设组装的每个会话,在第一次模型请求之前就因未知提示词变量 "{{cwd}}" 而提示词组装失败。启动正常,挂载正常,每一轮都失败。 三行 persona 和参考配置档 cordis.yml 现在只带 model 前缀,与上游 0.2.1 自己的预设文件一致。 这事现在就要处理,不是以后。latest 在 2026-10-19 移到了 0.2.1-rc.2,而 0.10.0 既通过 ^0.2.0-rc.2 接纳该版本线,又仍然带着这个后缀——所以在默认的 npm i -g @deepseek-ai/dsh 下,0.10.0 第一轮就失败。如果你在该版本线上用着 0.10.0,升级到 0.11.0。 有一处代价,明说而不藏着:dsh-agent-loop 在 0.1.6-alpha.2 / 0.1.7-rc.2 / 0.2.0-rc.2 上仍注册 cwd,而这几条线的 dsh-base 包都没有工作目录行,所以那里的单根会话不再从提示词里得知自己的目录。多根会话通过桥接自带的 acp:workspace-roots 上下文保留它;0.2.1 在宿主侧提供(Current working directory: …)。给这句话加版本门控被否决了——为了一句话在启动路径上放三个 !!js 探测,而模型仍能用 pwd 读出自己的目录。逐线表格:迁移记录 §2.3。 · 新增:dsh 0.2.1 0.2.1 是宿主侧和 UI 的发布;这个桥接消费的东西没有被移除。两处用户可见的变化,都不需要这个桥接做任何事: · DeepSeek 搜索与抓取。dsh-base 注册一行 web-deepseek-api,通过 DeepSeek 专用的 Search 和 Fetch 端点提供 web_search / web_fetch,而不是发一次完整的辅助模型请求。凭据:DEEPSEEK_API_KEY。在 harness 自己的 web-search 设置里选择;不涉及这个桥接的任何行、对等依赖或导入。 · 一个模型选择器标签,接进 web/desktop 包层,ACP 配置档从不加载它。 · 变更:支持的版本线 | | | |---|---| | 之前 | ^0.1.6-alpha.1 \|\| ^0.1.7-alpha.1 \|\| ^0.2.0-rc.2 | | 现在 | ^0.1.6-alpha.1 \|\| ^0.1.7-alpha.1 \|\| ^0.2.0-rc.2 \|\| ^0.2.1-rc.2 | 新增的这一项没有收窄任何东西——^0.2.0-rc.2 的 caret 上限是 <0.3.0-0,本来就包含 0.2.1-rc.2。之所以声明它,是因为这个范围同时充当守卫检查的版本线清单,每个新增项都需要在 CI 里启动一次,并在旁边生成一次链接检查。 @deepseek-ai/dsh-invariants 已从 peerDependencies 和 devDependencies 移除:上游在 0.2.0-rc.2 之后不再发布它,所以 0.2.1 的依赖闭包不带它。 已记录,未采用:0.2.1-rc.2 重写了上游全部四个预设文件(新增 time-context、tool-schedule、tool-plugin-market、一个压缩组、tool-fs;改写了 plan mode 的措辞)。这个桥接仍内联自己的副本,所以 0.2.1 会话组装的是 0.2.0 那套。采用其余部分需要逐线声明,因为新增的两行提到的包只在 0.2.1 上存在,而安装路径会在每条声明的版本线上解析每个行名。 · 安装 /
v0.10.02026-10-01
· 支持 dsh 0.2.0,最低版本线移到 0.1.6-alpha.1 dsh-acp-enhanced@0.10.0 把 0.2.0 这条线加进声明范围,去掉 0.1.5-rc.2 这条线,它在 CI 里根本没法验证。 · 新增:dsh 0.2.0 0.2.0 是客户端 / UI 版本:这个桥接层用到的东西一样没删(0 个服务、0 个事件),@deepseek-ai/dsh-session 新增了 ToolCallRecovery。真正要紧的改动在宿主侧——dsh-app-boot 的对等版本校验现在会拒绝任何声明的 @deepseek-ai/dsh* 范围与运行中命令行不匹配的包,在启动路径上它的做法是悄悄丢掉整个包的补丁层。所以对等范围放宽到 ^0.2.0-rc.2,随包发布的辅助脚本的脱字符上限现在与 npm 自身的语义一致(无效项不再报 BOOT FAILED,跳过行不再压过真正的崩溃特征,启动器的跳过提示不再占用它唯一的致命提示位)。 · 安装 / 升级 npm install -g @deepseek-ai/dsh@0.2.0-rc.2 # 或新范围内的任意版本 dsh plugin --profile acp-enhanced add dsh-acp-enhanced@0.10.0 配置档的依赖是 0.x 上的脱字符,所以 dsh plugin update 不会带你跨小版本——升级桥接层要显式操作。 · 已验证 api-surface、boot-classify、support-claim(3 条声明线 / 3 个矩阵版本 / 3 代链接)、启动器契约、pack-check、8 个离线编解码套件、compat-check(3 代链接全部干净),以及在 dsh 0.1.6-alpha.2 / 0.1.7-rc.2 / 0.2.0-rc.2 上的无密钥启动冒烟测试(各 26 项通过,没有跳过包)。CI 在 Node 22 和 24 上按线跑同一层级,外加依赖模型的端到端环节。
v0.9.12026-09-25
0.9.1 — 支持 dsh 0.1.7(增量) 新增对 dsh 0.1.7 系列的支持,同时不放弃 0.1.5 和 0.1.6:最低版本仍是 0.1.5-rc.2,peer 范围变为 ^0.1.5-rc.2 || ^0.1.6-alpha.1 || ^0.1.7-alpha.1。这仍是补丁版本——0.1.7 只是重新打包了智能体预设清单,并未声明新的 API 系列。 兼容性 — dsh 0.1.7 · 智能体预设清单从 @deepseek-ai/dsh-agent-presets(内置随包发布的预设,只读系统根目录)迁到 @deepseek-ai/dsh-agent-preset-registry,外加每个预设一行 @deepseek-ai/dsh-agent-preset 声明。现在两行都会发布,各自带一道生成门,读取启动安装自身的 manifest 版本,因此只会激活其中一个,两条路径都不会导致启动失败。 · 四个随包发布的预设声明(standard / ptc / minimal / cordis)从 0.1.7 的 web-app 包(MIT)内联而来,带上旧清单里每个预设 preset.yml 提供的展示元数据——0.1.7 不再为随包发布的 id 公布名称。 · 可挂载解析走成员探测:有 presets.resolveMountable(id) 就用它(≤ 0.1.6),否则用 presets.resolve(id) 加上该行自带的损坏原因(0.1.7 会故意返回损坏行,之后再拒绝它)。 · 清单里已不存在的已配置预设不再关闭面板:空白会话会在某个提供的默认值下打开,替换情况报到 stderr。恢复一个已经跑过缺失预设的线程仍会失败,直到该行存在。 · 从 0.1.7 起,不再发现 $DSH_HOME/.agent-presets 下用户编写的预设;请在配置档里把它们声明为 @deepseek-ai/dsh-agent-preset 行。 修复 · session/list 不再解码每条存储日志。在大型 0.1.7 存储上,每条日志约 217 ms,超过了 30 秒的传输超时。现在列表从一次存储遍历中取大小和修订号,在预算内装饰最近头部(DSH_ACP_LIST_BUDGET_MS,严格解析——NaN/空值回退到 2500 ms 并记日志,而不是悄悄关掉保护),按 id@revision 缓存,其余标题以 session_info_update 流式发送。活跃会话自己的标题始终权威,绝不会被存储的标题覆盖。 · 恢复会话的上下文窗口从其日志折叠而来,因此当 harness 不重新发出 request/context 时,环仍保留分母。 · 恢复环保护不再能空泛通过。 · 工具结果的名称取自其调用表;工具结果 id 和正文按两种 dsh 形态读取,因此卡片在 0.1.7 上不再卡在「running」且无输出。 · 编辑器运行的命令以命令本身为标题,而不是「bash」。 · acp-doctor 报告降级启动(DEGRADED 加载器条目从未激活),而不是 READY。 发布与 CI 加固 · 承诺的跨代链接检查会运行(scripts/compat-check.mjs);CI 矩阵覆盖 Node 22/24 × dsh 0.1.5-rc.2 / 0.1.6-alpha.2 / 0.1.7-rc.2。 · scripts/dsh-acp-zed.sh 和 scripts/init-acp-home.sh 遵循 DSH_PATH 目录,启动器契约测试覆盖这些情况。 · 新增离线套件:stored-titles-test(列表卡顿回归保护,源码级检查列表处理器绝不用 stat() 重新探测)、session-facts-test、context-window-test、tool-result-test。 · 0.1.7 迁移记录在 docs/plans/2026-09-24-dsh-0.1.7-support.md。 说明 · npm latest 从 0.9.0 移到 0.9.1。 · 升级:dsh plugin --profile acp-enhanced add dsh-acp-enhanced@0.9.1。 · 已知 CI 注意事项:dsh 0.1.5-rc.2 矩阵腿在 GitHub runner 上任何桥接代码运行前就失败——runner 的全局 dsh 安装缺少 @deepseek-ai/dsh-sandbox-local,而命令行自身的 dsh-base 补丁会加载它,却只把它声明为 devDependency。同一命令行版本在正常安装下能把这个检出跑绿;0.1.6-alpha.2 和 0.1.7
数据来自仓库的 GitHub Release,可能与插件实际版本不一致。

README

由 DeepSeek 最新模型翻译生成
| English · Changelog dsh-acp-enhanced 为 DeepSeek Harness(dsh)打造的增强版 Agent Client Protocol(ACP)服务器,面向 Zed 这类 ACP 编辑器。它可以直接替换官方 @deepseek-ai/dsh-acp 桥接层:官方桥接层只推纯文本,这个版本把 Web GUI 的能力都搬到了 ACP 协议上——流式输出、遥测、模型与权限控制、会话管理、MCP。 功能 输出与遥测 - Token 级流式输出,默认开启:回复和模型的思考过程在还在写的时候就送到编辑器(agent_message_chunk / agent_thought_chunk,按 75 ms 定时器合并)。在文本块提交之前就发送,代价是块中途重试时收不回已经丢弃的部分:会显示一个 _[stream interrupted — retrying]_ 标记,把它和重试的那次分开。
展开完整文档(README 剩余 1 节)
| English · Changelog

dsh-acp-enhanced

为 DeepSeek Harness(dsh)打造的增强版 Agent Client Protocol(ACP)服务器,面向 Zed 这类 ACP 编辑器。它可以直接替换官方 @deepseek-ai/dsh-acp 桥接层:官方桥接层只推纯文本,这个版本把 Web GUI 的能力都搬到了 ACP 协议上——流式输出、遥测、模型与权限控制、会话管理、MCP。

功能

输出与遥测

- Token 级流式输出,默认开启:回复和模型的思考过程在还在写的时候就送到编辑器(agent_message_chunk / agent_thought_chunk,按 75 ms 定时器合并)。在文本块提交之前就发送,代价是块中途重试时收不回已经丢弃的部分:会显示一个 _[stream interrupted — retrying]_ 标记,把它和重试的那次分开。在 acp-enhanced 这一行设置 streamDeltas: false(DSH_ACP_STREAM_DELTAS=false),就回到旧的块粒度行为:取消或重试的尝试完全不会漏出残缺输出,但块提交之前什么也不渲染
- 完整遥测:上下文用量环,加上缓存命中率 / TPS / 输入-输出-推理 token 数 / 工具耗时 / 轮次数(usage_update._meta 带完整明细)。环的分母是路由到的模型声明的 contextWindow,从会话日志里折叠出来,所以恢复的线程也能保留(harness 只记录一次 request/context,路由没变就不再重发)。dsh 自带的 dsh-compaction-basic 行会在该窗口约 80% 时自动压缩,standard/ptc/cordis 预设会挂载它(minimal 从不挂载);/compact 手动强制压缩。如果路由到的适配器没声明 contextWindow,就没有分母,环会显示 used === size。
- 图片支持(多模态):dsh 组合挂载了附件存储(dsh-attachment-local,dsh-base 默认挂载)时,会声明 promptCapabilities.image,粘贴或上传的图片会写入 harness 的持久附件存储——支持视觉的模型(例如 deepseek-v4-flash-vision-exp)能原生读取,按传输顺序和周围文本一起处理。旧的技术栈(没有附件存储)会自动降级:不声明 image,图片提示词会被拒绝并给出明确错误。

模型与权限

- 模型切换:实时的 provider/model 目录下拉框(ACP 分组选择器的协议形态)
- 推理强度:reasoning_effort 下拉框——只在路由到的模型提供可选强度时出现;每个模型记住上次用的强度(按配置档持久化),切回来就恢复;第一次用的模型回退到自己的默认值——或者它提供的第一个强度——而不是空的「未知」选项
- 权限预设:只读 / 工作区可写 / 完全访问三种会话模式
- 审批:每次工具调用弹出原生「允许一次 / 拒绝一次」提示
- 智能体预设:按会话组合面向模型的内容(工具 + 提示词片段),取自 dsh 的智能体预设清单。standard 是完整编码智能体(默认), minimal(极简模式)是裸的持久 shell,不含子代理 / web / todo / plan 工具——宿主层的任何东西都不会泄漏进极简智能体;ptc 和 cordis 也一并提供。你自己的预设要在配置档里声明(见 Your own presets)——dsh 0.1.7 不再发现 ~/.dsh/.agent-presets。
通过 agent_preset 配置项、/preset 命令或 DSH_ACP_PRESET 环境变量(作为会话默认值)选择;只有会话仍为空(尚未跑过任何一轮)时才允许切换,因此历史记录绝不会横跨两套工具集。
配置的预设若已不在清单里,面板不会关闭:空会话按清单默认值组合,并在 stderr 上说明。

Zed 深度集成

- 工具卡片:折叠标题栏显示一行摘要——Read <path>、shell 命令用模型自己的意图行(description,Codex 风格——确切命令一键可达)、Search: <pattern>、 Fetch: <url> 等。卡片正文遵循 ACP 最佳实践:文件编辑渲染成真正的 diff,bash/pwsh 命令渲染成真正的终端卡片(codex-acp 线格式:命令行 + 输出 + 退出状态胶囊,都在终端面板里——不再有原始 JSON 卡片),其他执行器渲染成语法高亮的代码块,涉及的文件渲染成可点击位置,点击即打开文件——rawInput / rawOutput 一键可达,保证透明,另有按类型区分的图标和完整的「进行中 → 已完成/失败」状态生命周期
- Zed 文件与终端:zed_read_text_file / zed_write_text_file / zed_terminal 把文件编辑放进 Zed 的「已编辑文件」区域(diff + 接受/拒绝),把命令放进真正的 Zed 终端
- 原生表单提问:ask_user_question → elicitation/create 表单,点选选项——都不合适时也可以输入自定义答案:选项连同描述一起渲染,每个带选项的问题都有一个自由文本「自定义答案」字段,自定义答案会替换单选结果 / 与多选并存(语义与 dsh 原生问题卡片一致)
- 计划面板:计划模式开关 → Zed 状态栏显示「planning」

会话

- 恢复与归档:session/load 恢复过往线程(完整重放);session/list 列出线程归档(带标题,按最后活动排序);session/close 丢弃内存中的记录,之后 session/load 会从持久化日志恢复;标题实时更新。session/delete 刻意不对外声明——harness 没有公开的持久化删除接口(见 Compatibility)
- 多根工作区:声明了 sessionCapabilities.additionalDirectories,因此 Zed 不再显示「this agent doesn't currently support multi-root workspaces」,改为在 session/new / session/load 上传入每个工作区根目录。所有根目录都会在系统提示词中向模型描述,并在 session/list 上报告;沙箱仍以主 cwd 作为唯一可写根目录(见「已知限制」)

命令

- 斜杠命令:输入 / 会显示命令列表(available_commands_update):/status 显示路由和遥测信息,/model 列出或切换模型,/preset 列出或切换智能体预设(列表以等宽代码块渲染——一眼就能看清),其余命令(/compact /goal /permission /plan…)直接走 harness 命令注册表——全部不触发模型回合即可执行。每个用户可调用的技能也会作为命令暴露,因此 /ask-matt、/code-review、/tdd 等能到达桥接层,而不会被编辑器拒绝,技能说明会注入到消息中(即 @deepseek-ai/dsh-tool-skill 风格的用户调用方式)。
粘贴在斜杠行旁边的图片会作为命令附件一并提交(例如给 /goal 目标附上参考截图),与 Web 编辑器提交图片的方式相同

MCP

- MCP 服务器:session/new 的 mcpServers 可挂载任意 MCP 服务器(stdio + streamable HTTP);工具以 mcp__<server>__<tool> 形式加入;某个服务器出错不会拖垮整个会话

预览

在 Zed 的 AI Agent 面板中选择 dsh-acp-enhanced 后:

快速开始

需要 dsh ≥ 0.1.6-alpha.1(npm install -g @deepseek-ai/dsh@0.2.1-rc.2,或下方 peer 范围内的任意版本);桥接层在每个受支持版本线上都只消费一个已声明的 harness 接口——从 0.1.6-alpha.1 到 0.2.1——运行时绝不探测更早的版本。

本包遵循官方 dsh 插件约定(声明了 dsh.bundle),因此安装方式与任何官方 bundle 一致:一条命令——自动初始化配置档、安装包、追加 bundle 层;无需手写配置档 YAML。

安装(2 步)

第 1 步——安装(从 npm registry 安装;无需检出源码):

dsh plugin --profile acp-enhanced add dsh-acp-enhanced

改代码时,改用 link: 指向本地检出(实时生效):
dsh plugin --profile acp-enhanced add "link:/absolute/path/to/dsh-acp-enhanced"

第 2 步——在 Zed 中注册(写在 ~/.config/zed/settings.json 的 agent_servers 下;Zed 启动智能体时 PATH 很精简,所以用随包提供的启动脚本 scripts/dsh-acp-zed.sh,它会自己定位 node/dsh)

启动脚本随包提供。 它的绝对路径取决于你在第 1 步的安装方式:
- npm 安装(默认):$HOME/.dsh/profiles/acp-enhanced/node_modules/dsh-acp-enhanced/scripts/dsh-acp-zed.sh —— 把 $HOME 换成你的主目录(例如 /Users/you);Zed 不会展开 ~ 或环境变量,所以要写完整的字面路径。
- link: 开发安装:<your checkout>/scripts/dsh-acp-zed.sh。

最常见:DeepSeek 官方 API(默认路由)

{
// ...your existing settings...
"agent_servers": {
"dsh-acp-enhanced": {
"type": "custom",
"command": "/bin/bash",
"args": ["/absolute/path/to/dsh-acp-enhanced/scripts/dsh-acp-zed.sh"],
"env": {
"DSH_ACP_PROVIDER": "deepseek-official", // the official provider id
"DSH_ACP_MODEL": "deepseek-v4-flash", // the official model id
"DSH_ACP_PRESET": "standard", // optional: agent preset id (minimal / standard / code / cordis / yours)
"DSH_ACP_INCLUDE_ALL_PROVIDERS": "true" // optional: list every registered provider in the model dropdown
}
}
}
}

这两个环境变量与随附补丁的默认值一致,所以完全可以省略 ——写出来只是让路由更明确。DSH_ACP_PRESET 在名单一侧默认是 standard;想让每个新会话都从特定模式启动时再设置它。
API key 不必放在 Zed 里:存到 ~/.dsh/.credentials.yaml(DEEPSEEK_API_KEY),dsh 凭证服务会解析它;启动器还会回退到正在运行的 dsh web 进程的 key。

DSH_ACP_INCLUDE_ALL_PROVIDERS=true 会让模型下拉框列出 llm 服务注册的每一个服务商,
而不只是被路由的那个。它默认关闭是有原因的:没有可用凭证的服务商照样会出现,选中它会把会话停在一个无法派发的路由上 —— 切换失败,报 invalid-params 错误并指出该路由,会话则保留原来的路由。当一个配置档跨多个服务商路由、你想按会话挑选时,再打开它。

调试卡住的回合(是模型请求还是工具?):

"env": {
// ...existing vars...
"ACP_LOG": "/Users/you/.dsh/dsh-acp-enhanced.trace.jsonl" // append-only JSONL event trace
}

每行是一个会话事件,带墙钟 time(毫秒 epoch);事后就能判断一个看似挂起的回合卡在哪: 模型请求卡顿表现为 step/start 与第一个 assistant/chunk 之间出现长间隔, 工具执行卡顿表现为 tool/call 与 tool/result 之间出现长间隔(结果行带 elapsedMs)。
prompt/settled 行覆盖整条用户消息的往返(stopReason + elapsed)。

可选:固定面板的默认配置选项(都仍可在面板里改):

"dsh-acp-enhanced": {
// ...the type/command/args/env above...
"default_config_options": {
"model": "deepseek-official/deepseek-v4-flash",
"agent_preset": "standard",
"plan_mode": false,jsonc
"reasoning_effort": "high"
},
"favorite_config_option_values": {
"model": ["deepseek-official/deepseek-v4-flash", "deepseek-official/deepseek-v4-pro"]
}
}

扩展:走 OpenAI-Responses 网关(比如公司内部的模型网关)

安装路径不变,只改环境变量的值:换成网关暴露的 provider/model,再加上它要求的密钥环境变量: jsonc
"dsh-acp-enhanced": {
"type": "custom",
"command": "/bin/bash",
"args": ["/absolute/path/to/dsh-acp-enhanced/scripts/dsh-acp-zed.sh"],
"env": {
"DSH_ACP_PROVIDER": "<gateway-provider-id>", // provider id exposed by the gateway
"DSH_ACP_MODEL": "<gateway-model-id>", // model id exposed by the gateway
"<KEY_ENV_NAME>": "<key>" // the key env var the gateway reads
}
}

<KEY_ENV_NAME> 也可以省略,把密钥存到 ~/.dsh/.credentials.yaml 里。

Zed 会热重载设置。打开 AI Agent panel(Cmd+Shift+A)→ 在智能体选择器里选 dsh-acp-enhanced → 发第一条消息:回复实时流式返回,状态栏显示上下文用量,面板提供 Model / Permission preset / Plan mode 选项和三种模式,线程归档能列出并恢复过去的会话。

本地验证(不需要 Zed): sh
node <pkg>/scripts/acp-doctor.mjs # bundles + versions, the peer range, and one real boot
node scripts/acp-client.mjs # dev checkout only: full ACP e2e, expect ALL CHECKS PASSED
DSH_ACP_PROVIDER=... DSH_ACP_MODEL=... node scripts/acp-client.mjs # only for a custom route

Web 搜索

桥接层不带搜索服务商,也不管你用哪一个:面向模型的 web_search 工具挂在 web 接缝的 searchProvider 上,所以往配置档里挂任意 ctx.web 服务商即可——带 dsh.bundle 的包用 dsh plugin --profile acp-enhanced add <package>,普通包用你用户层的 insert 行(见下文)。你的 dsh 部署里有哪些服务商,是配置档的事,不是桥接层的事。

注意代价:服务商 bundle 位于每个 ACP 线程的启动路径上,一旦加载失败,整个配置档就挂了,Zed 只会显示一个看不出原因的卡死。插件只添加面向模型的工具时,优先用预设组合(见保持配置档精简);必须配置宿主 web 行的服务商,应该放进宿主组合(配置档)里——有意识地挂载,改完再跑一次 doctor。

管理配置档的插件

dsh-acp-enhanced 跑在自己的配置档里——acp-enhanced,创建于 ~/.dsh/profiles/acp-enhanced/——和 dsh web 在同一个 dsh home 下。配置档隔离的是组合,所以这里的插件改动永远不影响你的 web 环境,而凭据、设置、会话和预设仍然共享。

配置档的插件树由三个来源组合而成,每一层都给前一层打补丁:
1. 打包层 — 配置档 package.json 里的 dsh.profile.bundles:先放模板的 @deepseek-ai/dsh-base,再按数组顺序放每个声明了 dsh.bundle 的已安装包(比如 dsh-acp-enhanced)。
2. 你的用户层 — ~/.dsh/profiles/acp-enhanced/cordis.patch.yml:按 id 定位的行配置覆盖、disabled: true 行禁用,以及 insert 列表(没有 dsh.bundle 的包——比如手动挂载的自定义服务商——靠它挂载)。
3. 每次运行的覆盖层 — dsh --profile acp-enhanced --patch extra.yml。

用下面的命令调整这组层: sh
dsh plugin --profile acp-enhanced add <package> # install; a dsh.bundle package auto-joins the layer stack
dsh plugin --profile acp-enhanced remove <package> # uninstall; auto-leaves the stack
dsh plugin --profile acp-enhanced update [package] # update one/all, then reconcile
dsh --profile acp-enhanced --dump-config # inspect the composed tree (per-layer provenance)

dsh plugin 是 pnpm 的薄封装(在配置档目录里运行),每次运行后都会拿 dsh.profile.bundles 和已安装状态对账。有两点后果值得知道:

- 把某个包从 bundles 里删掉来禁用它,不会生效 — 这个包仍是已安装的依赖,下次运行 dsh plugin 又会把它加回来。想禁用某一行又不想卸载,就在用户层按行 id 定位它(不是包名——id 可以在 --dump-config 输出里找到):

yaml
- id: example-row
disabled: true

- 没有 dsh.bundle 的包自己不会加载任何东西 — 它只作为普通依赖安装(会有一条一次性警告),需要你在用户层自己写 insert 条目。要改已有行的配置,用 - id: <row> + config: 覆盖——补丁条目会替换整行配置,不做合并。

改动在下一个进程生效:Zed 会为每个智能体线程新起一个 dsh --profile acp-enhanced,所以改完配置档后要开新的智能体线程(或重启 Zed)。

保持配置档精简

配置档是单一故障域。cordis-plugin-loader 会等待每个条目,并重新抛出第一个拒绝,所以一行加载失败就会中止整棵插件树:进程甚至可能先应答 ACP 的 initialize 握手,然后立刻死掉,客户端只会报告为不明原因的挂起,而不是错误。

把 dsh.profile.bundles 保持在恰好两行,这两行不可能与自己的启动不匹配: json
"bundles": ["@deepseek-ai/dsh-base", "dsh-acp-enhanced"]

@deepseek-ai/dsh-base 随命令行一起发布,所以它的版本总与启动它的命令行一致;其他每个包都是第三方,依赖闭包可能漂移。把额外插件挂到失败只损失一个预设、而不是整个编辑器会话的地方:

- 插件只添加面向模型的工具/命令 → 在预设里声明它的行组合。在 0.1.7 线上,它是配置档用户层里的一行 @deepseek-ai/dsh-agent-preset(见你自己的预设);在 0.1.6 线上,它是 $DSH_HOME/.agent-presets/<id>/ 下的一个目录(组合放在 agent.cordis.yml,选择器标签放在 preset.yml)。两种方式下,ACP 的 agent_preset 下拉框都会列出它;组合加载失败的预设会被标记为损坏,直接不提供,而不是让进程挂掉。
- 插件必须配置宿主服务(例如某个搜索服务商覆盖宿主 web 行的 searchProvider)→ 它属于宿主组合,也就是配置档。这是有意的取舍:接受启动路径的风险,并在任何改动后重新运行 doctor。

信任结果之前先检查: sh
node <pkg>/scripts/acp-doctor.mjs # bundles + versions, the peer range, and one real boot
dsh --profile acp-enhanced --dump-config # where each row comes from

你自己的预设

从 0.1.7 起,名册是一个由声明行供给的注册表:它不扫描任何用户目录,所以 $DSH_HOME/.agent-presets/<id>/ 不再被发现,配置档的默认值若指向这类预设,每次 session/new 都会失败,报 Unknown agent preset: <id>。请在注册表读取的位置编写你的预设: yaml

~/.dsh/profiles/acp-enhanced/cordis.patch.yml

- insert:
- id: preset-my-agent
name: '@deepseek-ai/dsh-agent-preset'
config:
id: my-agent # the id DSH_ACP_PRESET / the dropdown uses
name: My agent # optional; 0.1.7 only localizes shipped ids
description: … # optional order: 5 # optional plugins: # the composition, in the shipped preset's shape
- id: persona
name: '@deepseek-ai/dsh-persona'

随附的 presets/.patch.yml 层(以及本桥接内联的那份副本)遵循两条规则,因为预设是加载器挂载的数据:

- 重述整个组合。 预设行携带完整的 config.plugins 列表——不存在「给已有预设的子项打补丁」这回事——所以 standard 的分叉必须重新基线化到新线的行集合上(0.1.6 用 workflow-ptc 运行时替换了 workflow-worker-thread)。如果差异只在配置(模型路由、审批、沙箱),优先用宿主平面:只有工具集不同时才分叉预设。
- 在对应线读取的位置设置默认值。 0.1.7 上是 agent-preset-registry.config.default,≤ 0.1.6 上是 agent-presets.config.default(或 Settings 界面,它会写入 selectedDefault)。配置档若指向一个它已不再声明的预设,对空白会话仍然可用——它会按名册默认值组合,并在 stderr 记录这次替换——但无法恢复一个已经运行过该预设的会话,因为组合一个不同的把工具集加到已有会话记录上,正是「只允许空白」规则禁止的做法。

兼容性

一个桥接二进制,一个声明的 harness 接口面:dsh ≥ 0.1.6-alpha.1(peer 范围
^0.1.6-alpha.1 || ^0.1.7-alpha.1 || ^0.2.0-rc.2 || ^0.2.1-rc.2)。该范围内的四条版本线
都经过启动验证——握手、配置档落定和一次真实的 session/new——每次 CI 运行都会跑,另外还有跨代链接检查和 scripts/support-claim-test.mjs 守卫,用 npm 自己的 semver 钉住本段的声明。桥接只消费 harness 的声明接口面:docs/capability-seams.md 里的服务、 docs/event-producer-consumer.md 里的事件、已发布的包导出。其他任何东西都会让 scripts/api-surface-check.mjs 失败(这是会阻断的 CI 步骤)。没有特性嗅探,也没有代际矩阵:0.1.7 只需要两处适配——一个成员探测(presets.resolveMountable,0.1.6 及以前是私有的,0.1.7 里已移除,改由
resolve() 加上该行自己的 broken 判定替代)和一个按代际门控的行,写在
cordis.patch.yml 里,对应重新打包的 agent-preset 名册,它读取正在启动的安装自己的 manifest 版本(见下文);0.2.0 不需要任何适配(桥接导入的每个包都只改了版本,唯一新增的导出是追加式的),0.2.1 也不需要——它是第一条范围备选没有收窄任何东西的版本线(见下文)。宿主缺少其中任一适配时回退到旧形态;两条路径都不会让启动失败。

唯一会静默失败的是宿主自己的 peer 门禁:从 0.1.7 起 @deepseek-ai/dsh-app-boot 会拿每个 bundle 声明的 @deepseek-ai/dsh peer 对照正在运行的 CLI 版本,不匹配时就丢掉该 bundle 的整个补丁层,只在 stderr 打一行(dsh: skipping profile bundle "…")。所以范围少写一行不是警告,而是桥接直接消失——这就是为什么上面的范围既声明了,又逐行做了启动验证。安装路径更严格:从 0.1.7 起,插件管理器还会解析 bundle 补丁文件里每一行的 name,对每个包跑同一道门禁,只要有一个不匹配就拒绝整个安装(incompatible-version)——哪怕那一行在该版本线上会被代际门控禁用。所以这个桥接只为它真正链接的包声明 peer,≤ 0.1.6 的名册行则交给 CLI 自己的安装去解析。

支持策略

| 桥接 | 支持的 dsh 版本线 | 变更内容 |
| 0.11.1 | ^0.1.6-alpha.1 \|\| ^0.1.7-alpha.1 \|\| ^0.2.0-rc.2 \|\| ^0.2.1-rc.2 | 范围未变;token 级流式输出成为默认(streamDeltas: false 可退回旧行为);includeAllProviders 可从随附的行访问;无法路由的模型选择会以 invalid params 失败,并指明路由 |
| 0.11.0 | ^0.1.6-alpha.1 \|\| ^0.1.7-alpha.1 \|\| ^0.2.0-rc.2 \|\| ^0.2.1-rc.2 | 0.2.1 线已命名并完成启动;范围没有放宽;移除 dsh-invariants;去掉 persona 的 {{cwd}} 后缀(它让每个 0.2.1 回合都失败) |
| 0.10.0 | ^0.1.6-alpha.1 \|\| ^0.1.7-alpha.1 \|\| ^0.2.0-rc.2 | 支持 0.2.0;下限提升到 0.1.6-alpha.1;新增范围/矩阵/代际守卫 |
| 0.9.1 | ^0.1.5-rc.2 \|\| ^0.1.6-alpha.1 \|\| ^0.1.7-alpha.1 | 0.1.7 名单重新打包,用一个补丁版本吸收 |
| 0.9.0 | ^0.1.5-rc.2 \|\| ^0.1.6-alpha.1 | 只按声明面重写;下限 0.1.5-rc.2;移除 session/delete |
| 0.8.x | ^0.1.0-rc.6 … ^0.1.6-alpha.1(未发布) | 0.1.3+ 实时流接缝;0.1.5 持久化句柄 API |
| 0.7.x 及更早 | ≤ 0.1.2-rc.1 | 运行时探测两代 API |

这张表背后的规则:

- dsh 每出一条新 API 线,就发一个桥接版本,而不是放宽运行时探测。 0.7.x 靠探测吃下 0.1.1 → 0.1.5,也正是这种支持悄悄腐坏的原因。0.1.7 是重新打包,不是新 API 线,所以 0.9.1 用一个补丁版本吸收它——需要的探测只是一次成员检查,不是代际矩阵。0.2.0 连这都算不上:每个被消费的包只改了版本号,所以这次发布只是改声明,外加一个守卫,让声明保持诚实。0.2.1 更进一步:它的备选项完全不改变解析结果(^0.2.0-rc.2 已经包含 0.2.1-rc.2),所以这次发布存在的意义是命名并启动这条线——没写进声明的线,守卫守不住;声明了却从没启动过的线,就是「支持悄悄腐坏」的含义。
- 下限只随桥接次版本移动,而且绝不悄悄移动:启动器在启动范围外的 CLI 之前会警告,doctor 会停下并报 RESULT FAIL — CLI too old(低于下限)或 RESULT FAIL — CLI line not supported by this bridge(超出上限)。
- 弃掉一条线,靠发布一个这么说的桥接版本;上一条线留在 feat/dsh-0.1.3-plus-support 分支上,供无法迁移的用户使用。
- 在下一线发布前就盯住它:定时 canary 工作流安装 alpha dist-tag,跑守卫、链接检查和启动冒烟,这样破坏性变更会以红色 canary 暴露,而不是变成用户故障。

每个版本的细节——改了什么、破坏了什么、吸收它付出了什么代价——都在 CHANGELOG.md 里,最新的在前,每条都链接到 docs/plans/ 里的长篇迁移记录。本页只保留兼容性面:桥接今天声明了哪些线,以及声明遵循的规则。

从已发布的 ≤ 0.7.0 升级

0.1.3 之前的 API 线止于 0.7.0,它的桥接和 CLI 必须一起移动——无论先动哪个,半升级的一对都是坏的:

| 顺序 | 你会得到 |
| 命令行先行,bridge 留在 0.7.0 | 配置档能启动,initialize 也成功,但每次 session/new 都失败,报 Internal error(tool-subagent: modelSelectionSettings requires … in the Host scope)。没有任何东西能提醒你:那份 bridge 副本已经装好了,而且它既没有 agent/assistant-stream 接缝,也没有 assistant/message 回退,所以回复同样渲染不出来 |
| bridge 先行,命令行落后 | 配置档在加载时就挂了(… subpath './model-selection-settings' is not defined by "exports")。启动器会先往 stderr 打警告,scripts/acp-doctor.mjs 随后停下,报 RESULT FAIL — CLI too old |
| 两者一起升级 | 受支持的状态 |

检查清单:

1. npm install -g @deepseek-ai/dsh@0.2.1-rc.2(或上面 peer 范围内的任意版本)。
2. dsh plugin --profile acp-enhanced add dsh-acp-enhanced@0.11.1。升级 bridge 要显式操作:配置档的依赖是对 0.x 的 caret 约束,所以 dsh plugin update 不会自己把你带到新的次版本。
3. 之前是从 checkout 启动配置档,或者设过 DSH_PATH?旧启动器会自己把你挪到 ~/.dsh-acp,现在不会了。在 Zed 的 agent_servers.env 里设 DSH_HOME=<that home>,或者在默认 home 里重建配置档。启动器在那里找到配置档时会提示你。
4. 照着旧 README 在配置档的用户层里种过 subagent-model-selection-settings?删掉那一行:bridge 的补丁现在自带它,重复的 id 会让启动中止。scripts/init-acp-home.sh 会帮你清掉。启动器会警告,doctor 会点名这个 id。
5. 确认配置档里每个第三方 bundle 都有你要迁到的那条线的发布版本(见 Upgrading to 0.1.7)——配置档是一个故障域。
6. 重启 Zed(或开一个新的智能体线程);node <pkg>/scripts/acp-doctor.mjs 会先验证整条链路,包括打开线程。

每个 home 只跑一代命令行

$DSH_HOME/profiles/node_modules 是单个依赖闭包,由该 home 下所有配置档共享,dsh 会把它修复成最后启动的那个命令行。所以:

这是旧版本线的行为。从 0.1.6-alpha.2 起,这个共享闭包根本不存在了(harness 从命令行自己的安装里解析;配置档的 node_modules 只放树外插件),所以启动器的漂移检查按版本线区分,路径不存在时静默跳过。

- 绝不要在同一个 home 下同时跑两代命令行。 第二次启动会在第一个进程底下翻转闭包,那个进程随后会在运行途中惰性解析到不匹配的模块。启动器会拿闭包的 dsh-agent 版本和它即将启动的命令行比对,不一致就往 stderr 打警告——这样启动之后,重启该 home 下其他 dsh 进程(dsh web 等)。
- 逃生口是命令行,不是 home。 用 DSH_PATH=<dsh> 固定启动器(或使用下方固定到仓库的版本):启动器只解析 用哪个 dsh,从不解析 用哪个 home。

当前解析结果始终可见: sh
node scripts/compat-check.mjs # dev checkout: installs the 0.1.6-alpha.2, 0.1.7-rc.2, 0.2.0-rc.2 and 0.2.1-rc.2 sets and imports the bridge from each
node <pkg>/scripts/acp-doctor.mjs # CLI + closure versions, bundles, and one real boot (shipped)

开发检出:固定到仓库的 CLI,共享 home

启动器从检出目录运行时(link: 安装),按以下顺序解析 dsh CLI:

1. $DSH_PATH —— 显式指定的 dsh 二进制文件,或某个目录,其 node_modules/.bin/dsh 下存放着该二进制文件

2. the repo-pinned CLI — <repo>/node_modules/.bin/dsh (this package's @deepseek-ai/dsh devDependency, currently 0.1.7-rc.2)
3. global fallback — dsh on PATH / npx cache / npm prefix (a fresh clone without
pnpm install degrades to it)

Whichever wins boots profile acp-enhanced in the home the launcher was started with
(${DSH_HOME:-$HOME/.dsh}). The home is never rewritten. If you want the bridge on its
own dependency closure, build a separate home and point the launcher at it explicitly:
sh
DSH_ACP_HOME=~/.dsh-acp scripts/init-acp-home.sh # optional, idempotent: creates + seeds an isolated home

then in Zed's agent_servers env: "DSH_HOME": "/Users/you/.dsh-acp"

The isolated home is a deliberate opt-in, not a default: your profile must exist in
whichever home the launcher boots, or it exits 127 with the exact dsh plugin … add link: command that creates it. init-acp-home.sh ports your old profile's user rows verbatim, copies credentials/settings, disables the DeepSeek plugin-package inventory reporter, and retires a legacy subagent-model-selection-settings row from the user layer — that host row now belongs to the bridge's bundle patch, and a second copy aborts the boot with duplicate loader entry id.

Sessions persist under $DSH_HOME/sessions/<slug>/<id>/session.jsonl.zstd in both homes;
nothing is copied between them by default, because the default home's tree also holds
every web-profile session. Pass --copy-sessions (or run the rsync the script prints)
when migrating an existing setup.

Troubleshooting

Start with the doctor: it boots the profile exactly as Zed does and names the failure
layer, the offending bundle and the fix.
sh
node <pkg>/scripts/acp-doctor.mjs # installed copy
node scripts/acp-doctor.mjs # from a checkout (npm run doctor)
node <pkg>/scripts/acp-doctor.mjs --profile <name> --home <dsh-home> --timeout 60000

It prints the CLI + version, the home, the profile, every bundle + version, the supported
peer range and the closure version, then classifies the boot into one of four layers:

| Layer | Signature in dsh's stderr | What it means | Fix |
| manifest-gate | skipping profile bundle "<name>": … peerDependencies {…} | the CLI's peer gate rejected a bundle's declared @deepseek-ai/dsh peers and dropped its whole patch layer before the loader ever saw it. When the name is dsh-acp-enhanced the bridge itself is gone and there is no ACP server at all; when it is a third-party bundle, that bundle's rows never mount — its capability is gone, the boot continues, and the doctor reports DEGRADED | The doctor's FIX names the skipped bundle — upgrade the CLI inside that* bundle's declared range, upgrade the bundle itself, or take it out of the profile. Upgrading this bridge cannot help when the skipped bundle is a third party's |
| link-time | does not provide an export named …, SyntaxError: The requested module … | the booting CLI's closure cannot satisfy an import this bridge performs | node <pkg>/scripts/acp-doctor.mjs — if it prints LAYER link-time, align the generation: restart every other dsh process under this home (the shared closure heals to whichever CLI booted last), or pin this launcher with DSH_PATH=<matching dsh> |
| mount-time | failed to apply loader entry …, … requires … in the Host scope, duplicate loader entry id: … | the loader rejected one entry and rethrew, so the whole plugin tree is down | node <pkg>/scripts/acp-doctor.mjs prints SUBJECT <entry> (<module>) — install the missing module, disable that row (- id: <entry> + disabled: true in the user layer), or trim dsh.profile.bundles to @deepseek-ai/dsh-base + dsh-acp-enhanced. For a duplicate id, delete the row from your user layer (the bundle patch owns it) |
| run-time | … is not a function after a successful handshake | the bridge reached a harness service this CLI generation does not provide | npm install -g @deepseek-ai/dsh@<version in the supported range> (see Compatibility) |

The launcher translates the same four signatures on stderr while Zed boots (stdout is
the ACP wire — and the manifest-gate line is the only one that is not a stack: the boot continues without that bundle, so the translated line is all the log gets), so the agent log already carries the layer and the fix.

| Symptom | Locate | Fix |
| Zed hangs with no output; the thread never answers | node <pkg>/scripts/acp-doctor.mjs | Prints BOOT FAILED + LAYER/SUBJECT/FIX; follow the FIX line. A profile that answers initialize and dies right after is reported as such |
| exec: dsh: not found (status 127) | which dsh | Use the shipped dsh-acp-zed.sh launcher (it locates node/dsh itself), or install the CLI |
| no API key for provider route "xxx" | ls -l $DSH_HOME/.credentials.yaml | Write ~/.dsh/.credentials.yaml, or set env.DEEPSEEK_API_KEY on the agent_servers entry |
| SyntaxError: … 'PresetMountError' | the bridge version in the agent log | You are running a pre-0.9.0 bridge copy against a ≤ 0.1.6 host — update this package |
| modelSelectionSettings requires … in the Host scope | dsh --profile acp-enhanced --dump-config \| grep subagent-model-selection | The host row the standard preset needs is missing — the bridge's bundle patch inserts it, so reinstall/upgrade the bridge (dsh plugin --profile acp-enhanced add dsh-acp-enhanced), and check that nothing in your user layer sets it disabled: true |
| duplicate loader entry id: <row> | the doctor prints LAYER mount-time and the id | Two layers ship the same row. Delete it from the profile's user layer ($DSH_HOME/profiles/acp-enhanced/cordis.patch.yml) — these host rows belong to the bundle patch. scripts/init-acp-home.sh retires the legacy subagent-model-selection-settings copy for you |
| Old threads start empty after a host upgrade | ls $DSH_HOME/sessions | The sessions live under $DSH_HOME/sessions/<slug>/; copy the old home's history in (scripts/init-acp-home.sh --copy-sessions) and the new host resumes them |
| An old thread fails to open: Internal error … dsh-session-format-v0-to-v1 refuses this format v0 Session: … source summary requires notice form … (raw log: <path>) | the raw log path in the error | The dsh CLI's frozen v0→v1 migration refusing data that an older third-party bundle wrote into the session — every dsh client hits it, not only ACP through this bridge, and the migration never rewrites the original file. The writers of that era injected form/summary messages or message-edit/version events; a later release of the writing bundle typically ships a repair script (writes a repaired copy; the original stays). Upgrade the writing bundle so new sessions stop carrying the shape |
| Cannot switch models | ACP_DEBUG=1 dsh --profile acp-enhanced, then try the switch | The carried reasoning_effort is unsupported on the target: the bridge remembers the last effort per model (per-profile JSON) and falls back to the model's default rather than failing the switch. Also check the route is real — phantom providers are filtered, only config.provider's models are advertised |
| Context usage missing | /status in the thread | A "phantom provider" route was picked; point the profile's provider at a real route |
| Turn settles with usage but no reply text (empty panel) | ACP_DEBUG=1 and look for agent/assistant-stream frame=chunk | From 0.9.0 the only live seam is the agent/assistant-stream frames event, with the committed assistant/message as the fallback whenever a step streamed nothing. Frames present but no text = a client-side render problem; no frames at all = the fallback path (upgrade the bridge if it is older) |
| Unknown agent preset: <id> after a dsh upgrade | ls $DSH_HOME/.agent-presets and the profile's cordis.patch.yml | That preset left the roster — from 0.1.7 $DSH_HOME/.agent-presets is no longer scanned. Declare it as a @deepseek-ai/dsh-agent-preset row (Your own presets); 0.9.1 opens blank sessions under the roster default meanwhile (with a stderr note), but resuming a thread that already ran it keeps failing until the row exists |
| A capability the profile used to have is silently gone | node <pkg>/scripts/acp-doctor.mjs — a degraded boot prints DEGRADED <n> inactive items with the entry names | An entry that fails to import does not take the profile down, it just is not there. The bundle was built for another line: it imports an export this line removed (0.1.7 replaced SettingsProvider in dsh-settings with SettingsForms). Upgrade that bundle for this dsh line, or remove it |
| dsh: skipping profile bundle "<name>": … peerDependencies {…} — a whole bundle is gone after a dsh upgrade | that stderr line, the agent's stderr, or acp-doctor.mjs (a skipped bundle reports LAYER manifest-gate when the boot is down for it — the self-skip — and DEGRADED when the boot still works; either way that bundle's capability is gone) | The CLI's peer gate: from 0.1.7 dsh-app-boot checks each bundle's declared @deepseek-ai/dsh peers against the running version and drops the bundle's entire patch layer when they do not match (the install path refuses it outright as incompatible-version). Only the bundle's publisher can widen the range — upgrade it (dsh plugin --profile acp-enhanced add dsh-acp-enhanced@0.11.1), or pin the CLI to a line that bundle declares. This bridge declares all four lines above; when the skipped bundle is a third party's, it is that bundle's range that stops short, and upgrading this bridge cannot help |
| Session sidebar empty / slow to fill, bridge ≤ 0.9.0 on dsh 0.1.7 | ls $DSH_HOME/sessions \| wc -l and the agent's stderr for timeout: session/list | The pre-fix list decoded every stored log (see What 0.1.7 changes about a large session store); a few hundred sessions made it exceed the 30s wire timeout. Upgrade the bridge to ≥ 0.9.1 — it answers in seconds and streams the remaining titles as session_info_update |
| Plugin edits seem ignored | the profile's cordis.patch.yml mtime | Changes apply to the next process: open a new agent thread (or restart Zed) |
| A thread is dead after a Stop: Internal error: prompt was not queued: Cannot read properties of null (reading 'kind'), and a config switch in the same thread fails with {"details":"Cannot read properties of null (reading 'kind')"} | ACP_DEBUG=1 — that thread's last turn/end carries reason=null, and loading it again fails the same way | Bridge ≤ 0.9.1 passed new Error(...) as the harness cancel cause, but AgentCancelCause is a closed union ({kind:'user'\|'parent'\|'disposed'\|'hook'}). The harness's own turn close-out then threw on assertNever and persisted turn/end {reason: null}; a session projection that folds reason.kind unguarded throws on it, so every projection read in that session (prompt, config switch, session/load) threw afterwards. The thread itself cannot be recovered — start a new one — and upgrading the bridge removes the cause (guarding reason?.kind downstream only stops the throw; the unreadable event stays in the log) |
| Need detailed diagnostics | — | ACP_DEBUG=1 (stderr lifecycle trace) and ACP_LOG=/tmp/acp.jsonl (per-event JSONL with timings) |

Development
sh
pnpm install # install dev dependencies (repo-pinned CLI and test scripts)
node scripts/compat-check.mjs # link check across the supported lines (0.1.6-alpha.2 / 0.1.7-rc.2 / 0.2.0-rc.2 / 0.2.1-rc.2 scratch installs)
node scripts/support-claim-test.mjs # the declared range, the CI matrix and the helper agree with npm semver (blocking CI step)
node scripts/docs-claim-test.mjs # the READMEs' quoted surface, the changelogs' versions and the two languages' heading parity (blocking CI step)
node scripts/boot-classify-test.mjs # a skipped bundle on a working boot is DEGRADED, not a failure; a crash outranks the skip (blocking CI step)
node scripts/api-surface-check.mjs # public-surface guard: no undeclared harness API (blocking CI step)
node scripts/pack-check.mjs # package integrity: entry points, modes, shipped-file references (blocking CI step)
node scripts/acp-client.mjs # end-to-end smoke (needs an API key)
node scripts/acp-client-tools.mjs # client-tool tests (mocks Zed fs/terminal/elicitation/plan)
node scripts/acp-mcp-test.mjs # MCP mount test (no model calls)
node scripts/acp-smoke-keyless.mjs # keyless boot smoke (CI)
node scripts/acp-resume-test.mjs # session resume test
node scripts/codec-image-test.mjs # image-codec unit tests (no network, fake store)
node scripts/terminal-codec-test.mjs # terminal-card codec unit tests (no network)
node scripts/stored-titles-test.mjs # session/list title reader: bounded reads, no per-session stat(), revision-keyed cache (no network)
node scripts/session-facts-test.mjs # session-log folds: runningPreset/isBlank, live and stored share one contract (no network)
node scripts/context-window-test.mjs # request/context fold: the resumed ring keeps its denominator (no network)
node scripts/tool-result-test.mjs # tool/result id + body extraction across the supported dsh shapes (no network)
node scripts/cancel-cause-test.mjs # cancel-cause contract: every agent.cancel() passes an AgentCancelCause union member (no network)
node scripts/replay-order-test.mjs # replay/fallback chunk order: reasoning precedes its reply (no network)
node scripts/acp-image-e2e.mjs # image capability e2e (vision-model leg needs an API key)
node scripts/acp-message-fallback-test.mjs # live seam + assistant/message fallback: a seam fired and the reply arrived exactly once
node scripts/acp-launcher-test.mjs # launcher contract: home never rewritten, drift warning, boot-failure translation
node scripts/acp-doctor.mjs # boot the profile once and name the failing layer/bundle
scripts/init-acp-home.sh # optional: bootstrap an isolated* home (the launcher never switches to it by itself)

DevDependency pins for the harness packages use the line's prerelease range (currently
^0.2.1-rc.2, which resolves to the exact closure the pinned @deepseek-ai/dsh CLI
itself declares), so the repo's tree and a fresh CLI install resolve one coherent family — exact patch pins here mixed with the CLI's range-resolved closure produce a split closure (two versions of one name) that breaks profile boots with export-not-found errors. After changing those pins, regenerate the
whole lockfile (rm -rf node_modules pnpm-lock.yaml && pnpm install): an incremental
install both leaves stale store entries poisoning the profile heal AND retains stale
lockfile peer resolutions — bumping 0.1.2-alpha.2 → 0.1.2-rc.1 in place left
dsh-session-persistence@0.1.2-alpha.3 (old-generation peers) wired into the rc.1
packages' snapshots, which passes boot and session/new and only breaks the first turn
with TypeError: Cannot read properties of undefined (reading 'length') from
PersistenceCoordinator.
pnpm-workspace.yaml approves the CLI closure's build scripts (node-pty prebuilds, koffi)
— they are runtime requirements when the repo CLI boots the profile.

Known limitations

Audio attachments are not supported (audio capability is not advertised), text streams
token-by-token by default (streamDeltas: false restores block buffering, see
Features), one in-flight prompt per session. MCP supports stdio and streamable HTTP
(legacy SSE / acp transports are not advertised).
session/fork / session/resume are not implemented (capabilities
undeclared, compliant clients will not call them). session/delete is not advertised
either: the harness declares no public persistence delete, so persisted sessions are
never removed by the bridge (see Compatibility).

Multi-root workspaces are advertised and all roots are visible to the model, but dsh's
sandbox policy resolves one writable root per session (the primary cwd, i.e.
session.header.cwd) and the local sandboxes bind exactly that root for writes. Reads
work in every root; under workspace-write a write under an additional root is denied
first and needs escalation/approval, while danger-full-access writes everywhere.
True multi-root write enforcement belongs in dsh core (dsh-sandbox-policy /
dsh-sandbox-local would need a root list instead of a single root).

Agent presets take over the model-facing rows: the shipped cordis.patch.yml disables
the dsh-base rows a preset owns (tool-bash/fs/subagent/todo/web/… — exactly the official dsh-web-app/tui list minus hmr, kept version-agnostic across generations: a row a given generation does not ship is warned and skipped by the patch applier) and mounts the agent-presets roster (standard default; ptc/minimal/cordis ship with the dsh CLI; your own presets are declared as @deepseek-ai/dsh-agent-preset rows from 0.1.7 on — see Your own presets). The bundle's own patch applies
automatically (package.json dsh.bundle.patch) — do not copy it into the profile's
user-layer cordis.patch.yml, or the loader rejects the duplicate entry ids at boot.
When upgrading a profile that already carries a customized user-layer patch, keep
only your custom row configs there (e.g. includeAllProviders: true on the
acp-enhanced row, restating provider/model/preset since patch entries replace whole
rows, they do not merge). A session resumes under the preset its own log records
(the last agent-preset/selected event, else the creation header); only a log that
recorded none falls back to the roster's default.

License

MIT — see LICENSE.
预览分享卡片长什么样 ↓grunmin/dsh-acp-enhanced
分享 · DeepSeek Harness Hub
grunmin/dsh-acp-enhanced

为 DeepSeek Harnessdsh打造的增强版 Agent Client ProtocolACP服务器,面向…

这是你的仓库?认领条目

回到顶部