shenkonghui/dsh-llm-acp 预览 preview

shenkonghui/dsh-llm-acp

DeepSeek Harness 的 ACP 客户端 LLM 适配器 + ACP 服务设置界面。通过外部 [Agent Client Protocol](https://agentclientprotocol.com) 服务器作为模型提供方接入 harness 的 LLM 层,并提供一个 Web 设置页面用于浏览 ACP 注册表和管理已配置的服务器。

Project Overview项目介绍

dsh-llm-acp is a dual-sided DeepSeek Harness plugin. The host side is an LLM adapter that launches long-lived ACP server processes via stdin/stdout and registers each as a provider route at acp-<server-id>, streaming agent_message_chunk updates to harness. The client side renders a web settings page for browsing the ACP registry and managing configured servers, including env vars and model selection. Use it to wire ACP agents like Claude, Codex, or OpenCode into harness as model providers. Caveat: harness tools are ignored, ACP servers handle their own tool execution, and no token usage is emitted.

dsh-llm-acp 是 DeepSeek Harness 的双面插件,作为 ACP 客户端 LLM 适配器,通过外部 Agent Client Protocol 服务器接入 harness 的 LLM 层,并为每个已配置服务器注册 acp-<server-id> provider 路由;客户端则提供 Web 设置页面,可浏览 ACP 注册表并管理 ACP 服务。适用于需要将 Claude、Codex、OpenCode 等 ACP agent 作为模型提供方接入 harness 的场景。注意:ACP 服务器自行执行工具,harness 的 tools 会被忽略,且无 token 用量统计。

Or use CLI install (for developers)或使用命令行安装(适合开发者)

CLI Install命令行安装

dsh plugin --profile my-acp add github:shenkonghui/dsh-llm-acp

shenkonghui/dsh-llm-acp 加入你的 DSH 配置(web profile)即可启用。

READMEREADME

@deepseek-ai/dsh-llm-acp

中文 | English

DeepSeek Harness 的 ACP 客户端 LLM 适配器 + ACP 服务设置界面。通过外部 Agent Client Protocol 服务器作为模型提供方接入 harness 的 LLM 层,并提供一个 Web 设置页面用于浏览 ACP 注册表和管理已配置的服务器。

本包是一个双面 dsh 插件:宿主端(lib/index.js)是传输适配器,在 ctx.llm 上注册 provider 路由;客户端(lib/client.js)是浏览器设置页面,让用户从 Web UI 浏览 ACP 注册表并添加/删除 ACP agent 服务器。

界面

ACP注册表添加acp-server

支持acp registry上的所有acp-server[https://agentclientprotocol.com/get-started/registry]

如claude,codex,opencode等 alt text

ACP服务管理

alt text

使用acp-server进行交互

alt text

安装

dsh plugin --profile my-acp add github:shenkonghui/dsh-llm-acp

或从本地目录安装:

dsh plugin --profile my-acp add ./dsh-llm-acp

构建产物(lib/)已提交到仓库,安装时无需运行任何构建脚本。

卸载

dsh plugin --profile my-acp remove @deepseek-ai/dsh-llm-acp

这会从 profile 中移除依赖和 bundle 层。

配置

安装后,在 Web UI 中打开 设置 → ACP 服务。浏览 ACP 注册表,在任意 agent(如 Devin、Codex、Claude Agent)上点击 添加,即可将其配置为 ACP 服务器。每个已配置的服务器会创建一个独立的 provider 路由 acp-<server-id>

我的服务 标签页中,点击任意已配置服务器上的 编辑 按钮,可以:

  • 设置环境变量用于认证(如 DEEPSEEK_API_KEYOPENAI_API_KEY)。每个服务器的环境变量会与插件级 env 合并,服务级优先。
  • 选择要启用的模型。从服务器发现的模型目录中多选要暴露的模型,不选则启用全部已发现的模型。

ACP server 不单独保存权限策略。它复用会话输入框中的权限列表:read-onlyworkspace-write 将敏感操作转发到 harness 审批界面,danger-full-access 自动允许。

也可以直接在 settings.yaml 中配置:

llm-acp:
  servers:
    devin:
      command: devin
      args:
        - acp
      name: Devin
      env:
        DEEPSEEK_API_KEY: sk-xxx
      models:
        - deepseek-chat
        - deepseek-reasoner

工作原理

整体流程

1. 插件加载阶段(apply()src/index.ts

dsh harness 启动
  └─ apply(ctx, config)
       ├─ 读取 llm-acp 设置命名空间 + 内联 config.servers,合并成服务器列表
       ├─ 对每个服务器 createServer():
       │    ├─ resolveNpxShortcut():npx -y <pkg> 若 bin 已在 PATH 则直接用 bin
       │    ├─ new AcpConnection():spawn 长生命周期子进程(stdin/stdout JSON-RPC)
       │    │    └─ initialize() 握手 → 仅配置了 API key 时才 authenticate()
       │    │       (无 key 直接走 env/缓存登录;session/new 失败才惰性补一次 authenticate)
       │    ├─ new AcpAdapter():构造时 discoverModels() 探测模型目录
       │    └─ ctx.llm.registerAdapter(['acp-<server-id>'], adapter)
       └─ reconcileDirectory():向设置页注册可配置 provider 目录

2. 模型调用阶段(每次 stream()src/adapter.ts

harness 请求模型
  └─ AcpAdapter.stream(options)
       ├─ await connection.ready(等 ACP initialize 完成)
       ├─ Session 决策:
       │    ├─ agent 支持 loadSession 且有 dsh sessionId
       │    │    → session/load 复用,只发增量用户消息(renderPromptDelta)
       │    │      失败/历史变短(compaction)→ 降级新建
       │    └─ 否则 session/new 新建,全量历史渲染成一条文本块(renderPrompt)
       ├─ setSessionModel():best-effort 设置所选模型
       ├─ session/prompt 流式循环:
       │    ├─ agent_message_chunk  → text-delta chunk
       │    ├─ agent_thought_chunk  → reasoning-delta(emitReasoning 开启时)
       │    ├─ 扩展进度通知          → reasoning-delta
       │    └─ stopReason 终态      → finish chunk(end_turn→stop 等)
       └─ 收尾:复用 session 记入 sessionMap 供下轮复用;一次性 session 关闭

权限请求(session/request_permission)按当前会话的权限预设路由:danger-full-access 自动 allow,否则弹 harness 的 approval UI。

3. 设置界面阶段(浏览器端,src/client/

Web UI「设置 → ACP 服务」
  ├─ 浏览内置 ACP 注册表(registry.json)→ 点「添加」写入 llm-acp.servers
  ├─ 宿主端监听 settings 变更 → reconcileServers() 增删/重建连接(指纹比对)
  ├─ 模型发现:registerModelDiscovery 路由 acp-<id> → 临时 session/new 读 configOptions
  ├─ acp-info-<id>:只读 initialize 身份(agent 名/版本),不建 session
  └─ acp-resolve-<bin>:探测 PATH,把 npx 形式改存本地 bin 路径

核心设计:每个 ACP 服务器 = 一个常驻子进程 = 一个 provider 路由 acp-<id>;工具由 ACP 服务器内部自己执行,适配器只透传文本/推理流,不接 harness 工具生态。

宿主端 — LLM 适配器

apply(ctx, config)llm-acp 设置命名空间读取已配置的服务器列表。对每个服务器,启动一个长生命周期的子进程,通过 stdin/stdout 建立 ACP ClientSideConnection,并在 ctx.llm 上注册路由为 acp-<server-id>AcpAdapter。每次模型调用会创建新的 ACP session,将完整对话作为一条用户消息发送,并将流式 agent_message_chunk 更新转换为 harness 的 StreamChunk

客户端 — 设置界面

浏览器端注册一个 settings.section slot,渲染 ACP 注册表浏览器和"我的服务"列表。添加服务器时会将其持久化到 llm-acp 设置命名空间;宿主端监听变更并同步更新 provider 目录。

注册表命令推导

ACP 注册表指定了不同的分发类型:

类型 命令
npx npx -y <package> ...args
uvx uvx <package> ...args
binary 取注册表 cmd 的 basename(如 ./bin/devindevin

binary 类型使用可执行文件的 basename,这样已安装到 PATH 的二进制文件可以直接找到,避免 spawn ./bin/devin ENOENT 错误。

配置项

配置 默认值 说明
emitReasoning true 是否将 agent_thought_chunk 和扩展进度通知转换为 reasoning-delta chunk。
defaultModelId devin ACP 发现未返回模型时的回退模型 ID。
defaultModelName Devin (ACP) 回退模型显示名称。
disposeEofGraceMs 6000 stdin EOF 后等待平台终止的宽限时间(毫秒)。
disposeGraceMs 3000 SIGTERM 后等待 SIGKILL 的 POSIX 宽限时间(毫秒)。
initTimeoutMs 120000 initialize 握手(含 keyed authenticate)的上限(毫秒)。
sessionTimeoutMs 60000 session/newsession/loadsession/listsession/set_config_option 的上限(毫秒)。
authTimeoutMs 15000 单次 authenticate 调用的上限(毫秒)。

协议契约

每次 stream() 调用:

  1. 创建新的 ACP session/new,使用配置的 cwd
  2. 将 harness 的 messagessystem prompt 渲染为一条 ACP 文本块。
  3. 发送 session/prompt,将流式 agent_message_chunk 更新作为 text-delta chunk 传输。
  4. emitReasoning 开启时,agent_thought_chunk 更新转换为 reasoning-delta chunk。
  5. session/prompt 响应的终态 stopReason 转换为 finish chunk。

工具调用增量不会被输出。ACP 服务器内部执行自己的工具。session/request_permission 复用当前会话的权限预设:danger-full-access 自动允许,其他预设通过 harness 一次性审批请求处理;审批不可用、失败或 ACP 未提供 allow_once 时拒绝执行。

停止原因映射

ACP Harness finish
end_turn stop
max_tokens max-tokens
refusal error(code REFUSAL
cancelled aborted
max_turn_requests / 未知 error

构建

pnpm install
pnpm build    # tsc -b && tsdown

构建产物已提交到仓库,用户安装时只需 pnpm install 即可。

已知限制与待办事项

  • 不支持 harness 工具生态 — ACP 服务器执行自己的工具;harness 的 GenerateOptions.tools 被忽略。
  • 无 session 复用 — 每次 stream() 调用创建新的 ACP session 并重新发送完整对话。
  • 无 token 用量 — ACP v1 不提供 token 计数;适配器不输出 usage chunk。
  • 系统提示在消息体内 — ACP session/new 没有 system 槽位,harness 的 system prompt 被拼接到用户消息文本前。
  • 全量历史重发 — 适配器将整个 messages 数组渲染为一条用户消息。
  • ACP v1(SDK 0.25.1) — 适配器使用 @agentclientprotocol/sdk 0.25.1,其 session/prompt 响应携带终态 stopReason(v1 契约)。
  • 扩展协议处理 — Devin 的 _cognition.ai/* 通知被静默消费(进度文本在 emitReasoning 开启时作为 reasoning 输出);其他非标准 ACP 扩展被吞掉以避免 SDK 错误日志。
  • 认证惰性化 — 未配置 API key 时不主动调用 authenticate:依赖 env 凭证或 CLI 缓存登录的 server 直接 session/new 成功;仅当 session/new/session/load 失败才执行一次有界(authTimeoutMs)的 authenticate 并重试。交互式浏览器登录只在确实需要时触发,URL 同时经警告日志与设置页 acp-auth-<id> 路由暴露。

许可证

MIT

上一个 Prev dsh-ventus-search 下一个 Next commandcode-dash