DeepSeek Harness Hub
← 返回列表

HarnessDesk/dsh-acp

DeepSeek 客户端兼容 / 相关生态spec-screened在 GitHub 查看 ↗
⚠ 装前注意

dsh-acp — 一个完整的 DeepSeek Harness ACP 服务器

基本兼容但装前注意:未发布到 npm registry,仅可从源码安装 · 最近上游提交 2026/9/15 · 已提供中文文档

一个完整的 DeepSeek Harness 的 Agent Client Protocol 服务器——流式传输 harness 自身自动化桥接所隐藏的推理、工具调用、计划和 token 使用情况。

综合分
31.1
GitHub 分
31.1
用户评分
★ Stars
2
周下载量
兼容 / 相关生态插件(非 dsh 原生,请按其对应运行时安装)
git clone https://github.com/HarnessDesk/dsh-acp.git
数据截至 2026/9/19(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查⚠ 装前注意

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

npm 包@harnessdesk/dsh-acp(未发布到 npm,仅可源码安装)
Node 引擎要求 >=22.15 · 基线 Node 22.19 满足
dsh CLI 依赖未声明 dsh 版本约束
入口文件main/exports/bin 已声明

未发布到 npm registry,仅可从源码安装

验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/19 07:37:00

依赖的 DSH / Cordis 模块
@deepseek-ai/cordis
用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动

README

dsh-acp — 一个完整的 DeepSeek Harness ACP 服务器

CI
license

从任意 Agent Client Protocol 客户端驱动 DeepSeek Harness——并且真正看到它做了什么。

为什么会有这个项目

DeepSeek Harness 自带一个 ACP 服务器,@deepseek-ai/dsh-acp。它的源码直白地说明了其范围:

只发出已提交的助手文本。原始分块、推理、工具、计划、标题和重试标记属于展示或追踪数据,不进入自动化通道。

对于它被构建出来所服务的工作而言,这是正确的决定——它是一个自动化桥接层,它的 README 也是这么说的。但对于一个正在观看对话的人来说,这是错误的决定。通过它驱动时,一次运行了十五分钟、加载了两个技能、写了一个 822 行的文件、构建了一个 virtualenv 并运行了浏览器测试的过程,最终只呈现为两段散文。没有工具调用。没有推理。没有 token 计数,因此也没有上下文用量指示器。

这个适配器做出了相反的选择。同样的 harness,同样的会话,一切都在通道上。

自那段话写就以来,上游已经发生了变化。从 DeepSeek Harness 0.1.2-alpha.1(提交 511181684c,2026-08-22)起,它自己的服务器会流式传输推理、工具调用和 usage_update,并将模型和推理强度作为 configOptions 接收;它的 README 仍然说它“为自动化而构建……而非 DSH 用户界面”,它的 Known Limitations 仍然拒绝人们需要的那些动词。对照 v0.1.2-rc.1 测量:

| | @deepseek-ai/dsh-acp (rc.1) | 本项 |
|---|---|---|
| 助手文本 | ✅ 已提交消息 | ✅ 流式传输 |
| 推理 | ✅ agent_thought_chunk | ✅ agent_thought_chunk |
| 工具调用 | ✅ 通用生命周期 | ✅ tool_call / tool_call_update,带输出和每个 rc.1 工具一句话说明 |
| 计划(todo_write) | — | ✅ plan |
| Token 用量 | ✅ 仅 usage_update | ✅ usage_update + PromptResponse.usage(回合总计、缓存读取和写入) |
| 上下文构成 | — | ✅ 系统 / 工具 / 消息,来自 harness 自己的计量器 |
| 模型 / 强度 | ✅ session/set_config_option | ✅ ACP configOptions |
| 沙箱模式 | — | ✅ ACP configOptions(mode) |
| session/new 上的 MCP 服务器 | ✅ stdio + HTTP | — 拒绝,并明确说明(见下文) |
| 图像提示 | ✅ 带附件存储 | — |
| 对话列表 | ✅ session/list(已存储、分页) | ✅ 实时和已存储,带 harness 自己的标题 |
| 重新打开对话 | ✅ session/resume | ✅ session/resume——在它当时所用的模型上 |
| ……并看到它说了什么 | — session/load 被拒绝 | ✅ session/load 重放整个日志 |
| 另一个 agent 写了什么 | — | ✅ send_message 转发和结算通知,带归属 |
| 权限提示 | ✅ 允许 / 拒绝 | ✅ 允许 / 拒绝 |

什么算作一次对话
harness 会在 agent 被组合的那一刻写入会话头和 sandbox/mode——在任何人输入之前。因此,一个被打开后又废弃的会话仍会持久化,并在 session/list 中作为一行无人开始的无标题记录出现,每次应用启动都会产生一条。

已存储的会话只有在其中某样东西是人在说话时才会被列出:至少有一条 user/message,其来源为 user。在 harness 的模型中,工具结果也是 user 角色的消息,因此决定因素是来源而非角色——把它们计入会使每个碰巧运行过工具的废弃会话看起来像一段对话。

只有当某行的日志被读取并发现为空时,该行才会被隐藏。此 harness 拒绝解析的日志,或超出折叠预算的日志,都会被列出:没有任何东西检查过的行不是任何东西能评判的行,而隐藏一段真实对话远比显示一个废弃会话糟糕得多。

Node 24.0–24.11.1 与较旧的 harness

如果 harness 启动失败,且堆栈跟踪指向插件树而非你的组合,请先检查你的 Node 版本。

@deepseek-ai/cordis-plugin-loader 按主版本对 Node 的内部 ESM 加载器进行分类——>= 24 意味着“v2”——但 v2 直到 24.12.0 才落地。因此 24.0–24.11.1 中的每个加载器都被错误标记,resolveSync 被以颠倒的参数调用,这会在本适配器的任何代码运行之前就破坏插件树。该问题已在加载器 1.0.3 中修复,它随 DeepSeek Harness 0.1.2-alpha.2 及更高版本发布。

本适配器无法绕过它——失败发生在 harness 组合自身之时。要么升级 harness,要么运行 Node 24.12+ 或 Node 22。当启动在该窗口内失败时,本适配器会打印一条指向此处的说明。

engines 有意仍然允许该范围:适配器本身在那里没问题,足够新、带有已修复加载器的 harness 也没问题。直接排除它会拒绝那些可以正常工作的安装。

给从 0.4.0 或更早版本升级的人的一条说明

在 0.4.1 之前记录的对话无法重新打开,这是本适配器的错。当日志被读回时,harness 会验证每个消息事件——assertMessageEventShape 要求 id 是非空字符串——而本适配器生成用户消息时没有提供它。消息写入时没有任何报错;失败只在有东西尝试读回对话时出现,报告为 session event at seq N lacks an identified message。

本意是借用 harness 自己的消息工厂的动态 import() 也从未解析成功,因为 ESM import() 不会查询 NODE_PATH——而这正是像这样的插件获得 harness 包的方式。所以那个回退从来不是回退;它是唯一的路径。已在 0.4.1 中修复,它生成与 harness 相同的 crypto.randomUUID() id,并且现在会在使用自己的工厂时明确说明。

重新打开对话

ACP 区分两个听起来相似的动词,而这一区别决定了是否
客户端可以绘制任何内容:

- session/resume 将智能体重新置于已存储的会话上,并且明确不重放历史记录。
- session/load 会重放历史记录。

@deepseek-ai/dsh-acp 实现了 resume,并将 session/load 列在其拒绝暴露的接口之下,这对于一个不保留对话记录的自动化桥接来说是自洽的。对于一个人正在查看的客户端而言,这意味着重新打开一个对话会在一个空窗格之上得到一个实时智能体。

本适配器同时实现了两者。重放是一种折叠,而不是第二个映射器:存储保留与实时馈送所携带的相同的 SessionEvent 日志,因此事件会经过相同的投影返回,并作为客户端第一次本应收到的更新输出——包括用户轮次、推理、带有结果的工具调用、计划与标题。

这两项能力都是根据实际挂载的内容声明的。一个没有 @deepseek-ai/dsh-session-persistence 的组合会得到 loadSession: false 且没有 resume,并且只列出实时存在的内容——而不是本适配器无法兑现的承诺。

Token 用量遵循 ACP 的 Session Context Size and Cost RFD,包括其规则:缓存 token 仍然占用上下文窗口。

安装

npm install @harnessdesk/dsh-acp

你需要在旁边安装一个 DeepSeek Harness——一个全局的 @deepseek-ai/dsh,或者一个检出副本。适配器会按以下顺序查找它:从工作目录、从你指向的组合所在目录,或从它自己的包中。

使用

将插件挂载到 harness 组合中,与一个智能体主干并列:

- id: spine
name: '@deepseek-ai/dsh-agent-spine-demo'
config:
provider: deepseek-official
model: deepseek-flash

- id: acp
name: '@harnessdesk/dsh-acp'
config:
provider: deepseek-official
model: deepseek-v4-pro
models: [deepseek-flash, deepseek-v4-pro]

新会话使用 DeepSeek 当前的 deepseek-flash 路由,显示为 DeepSeek V4.1 Flash,与 DeepSeek V4 Pro 并列。旧版 deepseek-v4-flash 标识符在历史会话日志中仍可读取,但不再作为新会话选项对外公布。

官方 ACP 兼容性

DeepSeek 自己的 @deepseek-ai/dsh-acp 仍然是仅用于自动化的参考接口;HarnessDesk 继续使用本适配器处理面向渲染器的会话。可选启用的探测仅检查官方桥接的 initialize 和 session/new 自动化边界,不会提示模型,也不需要 API 调用。针对一个已初始化的官方 acp 配置文件运行它:

DSH_OFFICIAL_BIN=/path/to/deepseek-harness/apps/cli/lib/bin.js \
DSH_OFFICIAL_CWD=/path/to/deepseek-harness \
DSH_OFFICIAL_ARGS='["--profile","acp"]' \
npm run test:official

默认的 npm test 测试套件会跳过此探测,除非提供了 DSH_OFFICIAL_BIN,因此单元测试保持无需凭据且具有确定性。

然后将客户端指向该二进制文件:

harnessdesk-dsh-acp --config ./cordis.yml
Stdout 只承载 JSON-RPC 帧,不承载其他任何内容,因此该组合绝不能挂载
stdout 日志记录器——harness 自身的 ACP 组合出于同样的原因也省略了它。

在 Zed 中

{
"agent_servers": {
"DeepSeek Harness": {
"command": "harnessdesk-dsh-acp",
"args": ["--config", "/path/to/cordis.yml"]
}
}
}

在 HarnessDesk 中

向 ~/.harnessdesk/agents.json 添加一个条目:

{
"id": "dsh",
"name": "DeepSeek Harness",
"brand": "deepseek",
"command": "harnessdesk-dsh-acp",
"args": ["--config", "/path/to/cordis.yml"]
}

配置

| 键 | 含义 |
|---|---|
| provider | 所创建 agent 的 provider 路由,例如 deepseek-official |
| model | 所创建 agent 的模型,例如 deepseek-flash |
| models | 在选择器中提供的模型;少于两个则不提供任何模型 |
| efforts | 提供的推理级别;默认为 off, low, high, max |

设计

三条规则,每一条都源于一次具体的失败:

映射器是纯函数。 SessionProjection 接收 harness 事件并返回 ACP
更新,没有 I/O,也不导入 harness,因此整个词汇表可以在毫秒级内针对从真实会话日志
记录的事件进行测试,而不是针对作者凭空编造的事件。

harness 以结构化方式类型化,而非导入。 它的各个包沿着独立的版本线演进——
dsh-session 处于 0.0.1-rc.1,而 dsh-agent 处于
0.1.0-rc.6——因此,如果适配器将它们固定住,那么其中任何一个包发生变动时都需要
发布一个新版本。我们实际依赖的是会话事件词汇表,而 harness 自身将其视为兼容性
接口。没有任何内容被 vendored:适配器只是紧挨着你的 harness 的几百行代码,而不是
它的副本。

未知事件被忽略,绝不致命。 harness 的词汇表会不断增长。如果适配器遇到新的
事件类型就抛出异常,那么它本可以安然度过的升级也会导致其崩溃。

Token 计量

两个容易出错的细节,而且出错的方式看起来还挺合理:

上下文填充量是最新一次请求,而不是每一步的总和。 harness 在每一步都会发送
整个对话,因此对步骤求和会报告出一个远大于模型实际所见内容的会话规模。

ACP 的 inputTokens 是整个输入;harness 的则是未命中缓存的那部分。
将 harness 的数字原样传递,会让一个 12,574 token 的提示渲染成
157 token。适配器进行了转换,因此 cachedReadTokens 保持为 inputTokens
的一部分,并且“% cached”在这里的含义与其他所有 agent 相同。

上下文构成

一个挂载了 @deepseek-ai/dsh-token-meter 和
@deepseek-ai/dsh-session-projection 的组合,能获得两样事件流上任何东西都无法
提供的东西。

在压缩后依然存活的占用率。 meter 的 contextPressure 投影报告的是下一次
请求将花费多少——一个以 provider 为锚点的样本,针对对话自那以来所增加或减少的
一切重新定价。压缩不会报告任何用量,
它自身如此,所以仅由 assistant/chunk 构建的指示器会一直保持陈旧,直到下一次请求恰好运行;而这一指示器会在对话被压缩的瞬间消失。适配器优先使用它,并在没有挂载计量器时回退到按请求求和。

提示词由什么构成。 contextBreakdown 投影分别对系统提示词、工具模式和对话进行计价,适配器将这三者都携带在 usage_update._meta.harnessdesk.contextBreakdown 上,与它们所解释的占用情况并列:

{
"sessionUpdate": "usage_update",
"used": 43574, "size": 1000000,
"_meta": { "harnessdesk": { "contextBreakdown": {
"approximate": true,
"source": "DeepSeek Harness token meter",
"segments": [
{ "id": "system",   "label": "System prompt", "tokens": 44 },
{ "id": "tools",    "label": "Tool schemas",  "tokens": 771, "count": 3 },
{ "id": "messages", "label": "Messages",      "tokens": 325 }
]
} } }
}

approximate 不是装饰。这些分段来自计量器的固定密度估算,而 used 锚定于提供商实际收取的量,因此两者处于不同的真实单位,分段之和不会等于 used。harness 自己也是这么说的,而此适配器会传递这一警告,而不是将其丢弃。不会发送总计,也不应通过减法推断出总计——请将这些渲染为构成中的份额,而绝不是环的切片。

只有 count——请求信封中工具模式的数量——是精确的;它从 request/header 读取,适配器在其他方面不动它。在这里对这个信封计价将意味着要附带一个分词器并猜测 DeepSeek 的分词器,而 harness 已经用它对其他一切计价的估算器对它计价了。

本节中的所有内容都是可选的。ctx.inject(['sessionProjections'], …) 在没有注册表的情况下永远不会在组合中运行,而忽略 _meta 的客户端仍然会得到环。

状态

已针对真实 harness 端到端工作并测试——最近是 v0.1.2-rc.1,从源码构建并通过 stdio 驱动:流式传输、推理、带输出的工具调用、计划、用量、权限提示、三个会话控制、一个携带 harness 自身会话标题的对话列表、带重放的 session/load,以及用于模型、effort 和沙箱模式的 session/set_config_option。rc.1 的移除项——Session.events、SQLite 持久化后端、APIProxy——不触及此适配器注入的任何内容。

重新打开的对话会沿着其自身日志最后记录的路线继续(request/header 的调用配置,或之后的 model/selection),而绝不会沿着部署的默认路线:存储的会话头不携带提供商或模型,因此日志是答案唯一所在之处。选择器会显示该路线。

自 rc.1 起,子项通过 send_message 而不是单向的 report 工具回报,并且运行时会在子项
落定。两者都以 user 角色消息的形式出现在父级日志中,其来源为
agent-message 或 subagent-settled;此适配器将它们作为
user_message_chunk 发送,并标记 _meta.harnessdesk.notice: true,附带一个 from
来指明发送者,因此客户端会将它们绘制为该轮次上的通知,而非
此人的话语——并且两者都不会在对话列表中计为此人在发言。

列出的对话只有在组合生成名称时才会有名称。标题
服务及其提供者是独立的插件,挂载它们才能将一行
从截断的首个提示词变为“Maximum landing score breakdown”:

- id: session-title
name: '@deepseek-ai/dsh-session-title'
config: { fallbackMaxWords: 5, fallbackMaxBytes: 40, maxTitleBytes: 80 }

- id: session-title-llm
name: '@deepseek-ai/dsh-session-title-first-prompt-llm'
config:
targetWords: 5
targetCjkCharacters: 10
maxInputBytes: 4096
maxOutputTokens: 64
timeoutMs: 60000
provider: deepseek-official
model: deepseek-flash

仅靠该服务会给出一个按词数回退的标题;而提供者则用一条廉价路由
为对话命名。若两者都没有,session/list 仍会以 cwd 和预览作为响应,
且 title 为 null。

尚未实现:

- MCP 服务器透传——明确拒绝,并大声说明。 该 harness 确实托管 MCP
服务器,通过 @deepseek-ai/dsh-mcp-client,但它在
组合时连接它们:在 cordis.yml 中每个服务器对应一个插件实例。ACP 线路上
没有任何东西能向一个已经组合好的 harness 添加服务器,而此
适配器不会替某人编辑其组合。因此,session/new 上非空的
mcpServers 会被拒绝,并给出指明该参数的错误。

值得明确说明为什么这比另一种做法更好。
直到 0.3.0,此适配器接受该参数并忽略它,这让
客户端相信其工具已经到达模型。它们并没有,而
唯一的症状是一个 agent 说它无法做某件它被告知
可以做的事。指明 mcpServers 的拒绝是客户端唯一能
据以行动的答复:它会不带该服务器重试,并报告缺少哪些工具。

在 DeepSeek Harness 0.1.2-rc.1 上运行它

rc.1 移除了 packages/examples,随之移除了较旧组合所挂载的
dsh-agent-spine-demo。现在的捷径是随附的 acp 配置文件
加上一个覆盖层——profile/harnessdesk.patch.yml
——它会关闭 harness 自带的服务器,并将此服务器放到 stdio 上:

ln -s "$(pwd)" "$DSH_HOME/profiles/acp/node_modules/@harnessdesk/dsh-acp"
NODE_PATH="$DSH_HOME/profiles/node_modules" dsh --profile acp --patch profile/harnessdesk.patch.yml

NODE_PATH 很重要:适配器通过它访问 harness 自带的包以实现
模型选择耦合,没有它,模型和推理选择器会被诚实地
隐去。基础包挂载了此
适配器读取——持久化、标题、令牌计量器、会话
投影。

开发

npm install
npm run verify   # typecheck, test, build

许可证

MIT。

上游仓库有新提交时邮件通知你(每天最多一封,无更新不打扰),随时一键退订。

💬 加入 DPharness 群聊

插件用法、部署报错、新插件第一时间同步——群里问,比一个人翻文档快。

点击加入 QQ 群
DPharness 群聊二维码,手机 QQ 扫码进群
扫码进群