GDWhisper/dsh-ntr

dsh-ntr

把任意支持 ACP 的 Agent 接进 DSH,用 DSH 一个入口管理所有会话和任务。模型、思考等级和权限模式由 Agent 自己提供;工具调用显示在主聊天区,diff 汇总到独立的 NTR 页,权限询问出现在输入框。使用前需在本机安装支持 ACP 的 Agent 命令行工具。

待分类

dsh-ntr

English | 简体中文

在 DSH(DeepSeek Harness) 的网页/桌面端里,用你习惯的界面,直接和任意一个 ACP agent(CodeBuddy、Pi、Oh My Pi、Claude Code、OpenCode、Qoder 等)对话。

DSH 提供界面,agent 提供能力。 会话列表、输入框、侧栏、消息展示全部来自 DSH;推理、工具执行、文件读写、权限判断全部由你自己配置的那个 agent 完成,它跑在独立进程里,完全自治。这个插件只做一件事:把两边接通,并把 agent 的输出原样画到 DSH 的界面上。

模型选择、思考等级、工作模式都是 agent 自己的概念,不是 DSH 的模型目录。插件不替你换模型,也不会把 agent 的选择写进 DSH 的全局设置。

NTR 是什么

NTR = Negotiated Target Routing(协商式目标路由)。

一句话:你的宿主 Agent 作为 ACP 客户端,和目标 Agent 协商能力与权限,把这一轮任务交给它执行。

安装

本插件是一个 DSH bundle:package.json 的 dsh.bundle.patch 声明了随包分发的两层补丁——插件本体的 cordis.patch.yml,与 NTR 模式 preset 的 presets/acp/acp.patch.yml。用 dsh plugin 安装后,包会加入 profile 的 dsh.profile.bundles,补丁层自动生效,无需手动编辑任何配置文件。

方式一:从官方界面安装

打开 DSH 侧边栏的插件页,点添加插件,输入 dsh-ntr,点安装。装完刷新页面即可使用。

DSH 的 npm 渠道有 24 小时防范期:刚发布的版本 24 小时内不算「最新版」,只输 dsh-ntr 装到的可能还是旧版。想马上用新版,把版本号一起写上,例如 dsh-ntr@0.1.5;不急的话,等防范期过去再照常安装或升级。

方式二:从 npm registry 安装

dsh plugin --profile web add dsh-ntr@latest

升级重跑同一条命令即可。

add 时务必带 `@latest`:不带的话包管理器会一直复用首次写入的版本号,之后连 update 也升不上去。

方式三:经 dsh-market 插件市场安装

先装市场本体:

dsh plugin --profile web add dshmarket

重启 dsh web,打开插件页:dsh 0.2.1 起市场是「已安装」旁边的插件市场标签页;更早的版本在设置 → 插件市场。搜索 dsh-ntr,确认来源后一键安装,装完刷新页面即可使用;之后的更新检查与升级也在市场里完成。官方 DSH 桌面 App 自带这个市场,直接在 App 的插件页里搜索安装即可。

市场的插件清单来自 awesome-dsh-plugin 精选目录,每天自动刷新。若搜索结果里暂时没有 dsh-ntr,用方式二从 npm 安装,不影响使用。

方式四:从源码安装

需要 Node.js ≥ 22.12.0。

git clone https://github.com/GDWhisper/dsh-ntr.git
cd dsh-ntr
npm install        # 安装构建依赖(typescript 等)
npm run build      # 编译宿主 TS 与浏览器 Bundle 到 lib/(构建产物不入库,此步必须执行)
dsh plugin --profile web add ./

之后升级:git pull → npm run build → 重启 dsh web。add . 以 link: 方式安装,重建后重启即生效,不需要重新 add。

从源码安装切到 npm / 市场版前,先在插件管理器里卸载旧版,并删掉 profile 里的 node_modules/dsh-ntr。pnpm 的 hoisted 布局会把 link: 目标里的 node_modules 留在包目录内,旧依赖会遮蔽新装的 SDK,典型报错见「常见问题」里的 EPERM ... zod。
dsh plugin 是 pnpm 转发器,--profile 必填;add . 会把当前目录以 link: 方式装进 profile。官方桌面 App 的 profile 由它自己的插件管理器掌管,不要对着它的 profile 跑命令行。

快速开始

你需要什么

  1. 一个已经装好的 DSH(Web 或桌面版)。
  2. 至少一个支持 ACP 协议的 agent,用它的终端版本(CLI / TUI)即可。例如 Qoder CLI(qodercli)、CodeBuddy Code(codebuddy)、Claude Code、OpenCode——插件内置了这些常见预设;其他 agent 只要能以类似 xxx --acp 的方式从命令行启动,也可以直接填。

三步接入

  1. 打开 设置 → NTR。
  2. 在「Agent 列表」里选一个预设一键填入,或点「从零新建自定义 Agent」手填启动命令。
  3. 点「测试」。成功会显示 agent 的名字和握手耗时;失败会显示可操作的错误和 stderr 尾部。

保存后,这个 agent 就出现在输入框行尾的 NTR 底座里。

开始对话

  1. 新建一个会话,在模式选择里选「NTR 模式」。
  2. 在输入框行尾的 NTR 底座里选一个 agent。
  3. 直接说话。

agent 的回复和思考显示在主聊天区。工具活动按发生顺序插在它们中间:一行 🛠️ **工具名** · 参数,输出跟一个代码块。想看完整的工具输出、计划和 diff,切到对话头部的 NTR 标签页。

你会用到的界面

NTR 底座(输入框行尾)

点一下,选哪个 agent 服务这条会话。这个菜单里只有 agent 列表——一个控件一件事。agent 自己的模型和思考等级是同一行里各自独立的芯片,各开各的菜单;如果 agent 还发布了额外配置项(温度、供应商开关等),模型芯片左边会多一个「高级」底座,点开逐项设置。首轮之前 agent 底座固定在行尾最右侧;锁定后它退场,模型和思考等级接管右端。那些清单由 agent 自己上报,不是你熟悉的 DSH 模型列表。

底座只出现在 NTR 模式的会话里。普通会话里看到的是 DSH 原生的模型选择器。

一个会话一个 agent。 首条消息发出前,底座一直在,你可以先选好、也可以反悔改选。首轮开始时会话就锁定在这个 agent 上:底座粉碎,名字变成输入框卡片上边缘的小挂牌(NTR · ,骑在顶边上),挂牌只显示、不能点击——换 agent 请新建会话。模型、思考等级和「高级」芯片留在原位,它们属于同一个 agent,不涉及上下文迁移。如果首轮还没产生任何可见输出就失败/取消,锁定会自动回滚,底座回来,可以直接重选。

/ 命令属于 agent

在 NTR 会话里输入 /,弹出的是 agent 自己的命令清单——它通过 ACP 声明的那些(例如 compact、session、model),不是 DSH 的命令。选中一项只是把命令名填进输入框;回车后整行原样发给 agent,由它执行。清单在第一条消息之前就能用:宿主记住了这个 agent 上次会话声明的命令(从没跑过的 agent 要等第一轮之后才有)。

DSH 自己的 /model、/permission、/export 等在这一模式下不会被宿主截走:同名命令也交给 agent,宿主命令菜单与技能补全不会出现。普通会话的斜杠行为完全不变。

审批与 agent 自己的权限模式

agent 要动敏感的东西时会问你。询问卡出现在对话里、聊天输入框上方(插件自己的卡片),选项就是 agent 给的那些,你点哪个它照着做;设置页的「待处理权限请求」是跨会话镜像,对话没打开时在那里回答。

每个 agent 问法不一样:有的给一组选项,有的只给允许/拒绝,有的会附上它要执行的具体命令。插件把这些原样呈现给你,不预先替你决定。

权限模式由 agent 自己定义。 每个 agent 在输入框工具行给出它自己的模式清单(例如 Always Ask、Accept Edits、Plan、Bypass Permissions),选项文字就是 agent 下发的原文。你切换模式,agent 就按新模式决定什么时候来问你。插件不另设一套权限档,也不替你翻译这些模式名。

注意:DSH 自己的「访问模式」(只读 / 工作区写入 / 完全权限)管不到外部 agent。那个 agent 在自己的进程里,DSH 的文件沙箱对它没有作用力。真正生效的是 agent 自己的权限模式,以及它向你发起的那些审批。

NTR 标签页

对话头部的 NTR 标签页显示 agent 的原始流:

  • 计划和待办清单
  • 每次工具调用的名称、状态、输出(默认折叠,点开看全部)
  • 代码改动的 unified diff,带 +N -M 行数统计
  • 用量读数

这个标签页是只读的:它不发消息、不取消任务、不启动进程。实时跟随,不需要手动刷新。

主聊天区和 NTR 标签页的分工:人和 agent 的对话在主聊天区,工具活动也按时间顺序内联在那里;NTR 标签页放主聊天区装不下的东西——计划、完整输出、diff、用量。

设置 → NTR

  • 已接入的 NTR 智能体:每个 agent 一行,标着进程状态。点开「展开配置与模型」看全部细节。
  • `★ 默认`:新建 NTR 会话默认用哪个 agent。
  • 每张 agent 卡片:显示名、启动命令、npm 包名、环境变量、工作目录,全部直接摆在你面前,没有藏起来的默认值。
  • 启动命令是一整行:程序加参数,例如 codebuddy --acp。含空格的路径用引号包住。输入框下方会实时显示插件解析出的「程序 / 参数」。
  • 测试按钮:用当前表单里的值拉起一个一次性进程做握手,不保存、不注册。适合在改配置前先验证。
  • 工作区目录:agent 在哪个目录里干活。
  • 对话中可见的模型:agent 能报几百个模型时,这里挑你想在模型芯片菜单里看到的。没勾的仍然可用,只是不列出来。

你的工作区

在对话里起的 NTR 会话,工作目录就是你在这个 DSH 会话里选中的那个目录(侧栏点的那个)。对端 agent 的读文件、搜索、执行命令都在那里,它自己的进程也从那里启动。

有一类 agent 在进程启动那一刻就把工作区定死了,只改会话参数它不听。插件在它没有活动会话时会把它搬到新目录;有会话在进行时就保持原进程,不打断你的对话。

常见问题

会被平台风控吗?会封号吗?是反代吗?

不会,也不是反代。dsh-ntr 与 agent 之间的通信走 ACP——这是 agent 官方支持的协议入口。插件只负责启动你本地的 agent 进程并和它对话,全程不接触平台 API、不转发任何请求。模型调用由 agent 自己完成,跟你在终端里直接用它没有区别。在 NTR 提供的能力范围内合理使用,是官方允许的做法,不会因此封号。

为什么选完 agent 之后,输入框里没看到 DSH 的模型选择器?

那是设计如此。NTR 会话里,模型是 agent 自己的概念,所以那个位置换成了 NTR 底座。切回普通模式的会话,原生选择器就回来了。

为什么首轮开始后,输入框行尾不显示 agent 名字了?

一个会话一个 agent。首轮之前底座留着让你改选;首轮一开始,会话就锁在这个 agent 上,名字变成挂在输入框卡片上边缘的 NTR · 只读小挂牌。换 agent 会丢掉它这边的上下文,所以确认锁定后只能新建会话。模型、思考等级和高级配置芯片不受影响。首轮没有任何可见输出就失败/取消时,锁定会回滚,底座回来。

锁定后想换 agent 怎么办?

新建一个会话重新选。已锁定的会话不会接受 session/chat-bind 的改绑(宿主返回 agent_locked),这是刻意的:新 agent 没有这段对话的上下文,假装切换只会得到一段失忆的对话。首轮没有产生任何可见输出就失败的情况例外:锁定会自动回滚,可以直接重选。

顶部看不到 NTR 标签页?

每条会话都有这个标签页。这条会话还没绑过 agent 时,它是空的,会提示你去选一个。先在模式选择里选「NTR 模式」,再在 NTR 底座里选一个 agent。

agent 卡住不动了?

可以放心点停止。插件会取消这一轮,并在 agent 不理会取消时强制收尾,会话不会永久卡住。agent 自己长时间无动静时,卡住的那一轮也会被收口(见 AGENTS.md 约束 23):声明了 session/load 的 agent,下一次发送会自动重启并恢复会话;没声明的会被重置,上下文丢失。卡的若是这条会话的首轮、且什么都没输出,锁定会回滚、该轮作废,下一条消息直接新建会话。agent 进程崩溃同理——支持恢复的会自动拉起并回放历史,不支持的要新建会话。

agent 只回了一个空气泡?

一部分 agent 会把上游失败(认证、计费、配额)当成「正常结束」。插件检测到这种情况时会补一条告警,不让失败静默。

agent 没反应,也没报错?

看设置页的测试按钮。它会在宿主侧跑一次完整的握手,把真正的错误(包括 stderr 尾部)显示出来。

模型列表太长或太短?

模型芯片的菜单没有搜索框,所以模型是按需勾选的。去设置页那张卡片里勾你想看的。不勾就没有那些条目,避免几百个模型刷屏。

为什么 NTR 标签页里没有我发的消息?