DSH Plugins Marketplace

DSH Plugins

Plugins

/

dsh-better-chat

z

dsh-better-chat

Manifest valid

For DeepSeek Harness's "Pure Conversation" mode: chat just like the web version, but it reasons through multiple rounds on its own

hasBundlePatchMachine translated

dsh-better-chat

给 DeepSeek Harness 的「更好的对话模式」模式:像 DeepSeek 网页版一样聊天,但会自己多轮思考。

一个 DSH 本地 bundle,向 Web 端注册名为「更好的对话模式」的 agent preset,并附带一个 keep_thinking 工具。


它是什么

  • 一个模式(agent preset),在 DSH 的设置页出现,名字叫「更好的对话模式」
  • 一个工具 keep_thinking:纯文字的控制流工具,不碰文件、不联网、无任何副作用
  • 模型自己决定要想 1 轮还是 N 轮,自己判断什么时候该停
  • 只有一个硬上限(默认 10 轮)防止无限循环

「多轮思考」是怎么实现的

DSH 的 agent 循环规则是:让轮次继续的唯一燃料是「工具调用」或「steering」。没有工具调用,模型答完就结束。

所以这里给模型一个只用来表达"我还没想完"的空工具:

模型的行为循环的行为
调用 keep_thinking执行工具 → 再跑一步(又一次模型请求)
不调用、直接输出答案轮次结束

这就是 agent loop 原生的控制流,不需要监听轮次边界、不需要往对话正文里塞协议标记。「停止」就等于「不再调用」。

和 DeepSeek 网页版的对照

DeepSeek 网页版本模式
联网搜索✅ web_search + web_fetch
文件上传 / 文本提取✅ 全局附件能力(DSH 自带)
图片识图✅ 全局多模态输入 + read_image
语音输入✅ 取决于 profile 是否装了语音 bundle
深度思考✅ reasoningEffort(profile 级配置)
快速 / 专家模式✅ 就是 DSH 的 preset 概念,本模式是其中一个
——➕ 多轮纯文字思考(本模式独有)

挂载了哪些插件

全部内容就是 cordis.patch.yml 里的一个** preset 声明。**

宿主层(profile 层)

空的。 本 bundle 以前在这里放一行 think-better-tool,现在没有了——原因见 「为什么工具行在 preset 里面」。

preset 层(模式内部)

5 个官方工具行 + 本仓库自己的工具行,全部挂在 preset-chat 的 config.plugins 里:

行 id包配置提供的工具
think-better-toolthink_better(本仓库,按包名引入)maxRounds: 10keep_thinking
persona@deepseek-ai/dsh-persona见 persona—(系统提示词)
tool-web@deepseek-ai/dsh-tool-webfetch: true
searchTimeoutMs: 60000
web_search web_fetch
tool-ask-user@deepseek-ai/dsh-tool-ask-user—ask_user_question
tool-fs@deepseek-ai/dsh-tool-fs—read write edit read_image
tool-fs-search@deepseek-ai/dsh-tool-fs-searchsampleOverCapGlobResults: falseglob grep
present@deepseek-ai/dsh-tool-present—present

合计 11 个工具。 实测「更好的对话模式」一轮实际下发 25 个工具——多出来的 14 个来自宿主层的其它 bundle,见 已知限制。

因为工具行就在 preset 里,ctx.tools.register() 走的是 preset 的作用域:只有「更好的对话模式」的 agent 看得见它,其它模式不需要被摘掉任何东西。

刻意没有挂的

没挂原因
dsh-tool-goal + dsh-command-goalgoal-round-driver 在 base 中默认启用,模型一旦能建 goal 就会自动续轮,直接破坏"模型自己决定何时停"
dsh-tool-ralph天生自我循环
dsh-tool-subagent / dsh-tool-workflow / agent-team请求数成倍放大
dsh-tool-todo会把人格拉向"干活"
dsh-tool-pwsh / dsh-tool-bash / dsh-tool-jobs / dsh-tool-terminal重型,与"更好的对话模式"定位冲突
dsh-agent-instructions必须不挂,否则会去读 AGENTS.md,模型一上来就进入编码 agent 人格
dsh-plan-mode与更好的对话模式无关

不用挂、base 已经有的

dsh-compaction-basic(长对话自动压缩)、dsh-fs-observation-policy(先读后写门禁)、dsh-repeat-tool-reminder、dsh-tool-call-timeout-policy、dsh-session-title 系列。


配置参数

maxRounds —— 唯一需要理解的参数

- id: preset-chat
  name: '@deepseek-ai/dsh-agent-preset'
  config:
    id: chat
    plugins:
      - id: think-better-tool
        name: 'think_better'
        config:
          maxRounds: 10      # ← 改这里,必须是 ≥1 的整数

maxRounds 是每个用户轮次允许的思考轮数上限(每个 turn/start 清零)。它的行为:

第几次调用工具返回循环行为
1 … maxRounds-1继续。(n/10,还剩 m 轮)再跑一步
maxRounds已达思考上限(10 轮)。现在必须直接给出最终答案,不要再调用本工具。再跑一步,模型在这一步作答
> maxRounds思考已达 10 轮上限,本次回答到此结束。 + concludeTurn()本轮立即强制结束

净效果:最多 maxRounds 轮思考 + 1 轮最终回答,绝对收敛。

maxRounds 的 schema 是 z.natural().min(1).default(10):非整数、0、负数都在加载时直接校验失败(ValidationError),不会静默退化成一个"一调用就结束本轮"的工具。

其它可调参数

行参数默认说明
think-better-toolmaxRounds10见上
personaprefix见下人设文本,可自由替换
personasuffix'当前工作目录是 {{cwd}}。'见 为什么 suffix 不能留空
personaincludeRuntimeContexttrue是否注入运行时上下文(时间等)。设 false 提示词更干净,但模型不知道当前日期
tool-webfetchtrue是否启用 web_fetch
tool-websearchTimeoutMs60000搜索超时
tool-fs-searchsampleOverCapGlobResultsfalse必填(该插件的 Config schema 要求)。控制超出结果上限时是"跨顶层条目抽样"还是"保留前 N 条"

改完 cordis.patch.yml 需要重启 DSH:HMR 只监听 profile 自己的 cordis.patch.yml 和 package.json,不监听 bundle 内部的 patch。


persona(可自行更换)

当前 persona.prefix 内容:

你是一位专业、耐心的对话助手,既能深入讨论专业问题,也能自然闲聊。
专业问题上:结论先行、依据清楚、不确定处直言不讳。
日常对话上:自然、简洁、不端着。
需要查证时使用联网搜索或文件工具,不做无意义的工具调用;简单问题直接回答。
推演不充分时调用 keep_thinking 继续思考,足够时立即作答。

这段文字可以按需要随意替换。 它就是 cordis.patch.yml 里 persona 行的 prefix,改完重启即可。

几点说明:

  • prefix 会遮蔽部署级人设(Web 端默认是 You are a coding agent powered by the {{model}} model.),所以人格是被替换而不是被追加。
  • 不要加 complete: true。complete 控制的是"除人设以外的东西还要不要":设了它,工具引导段落会被一并抹掉,模型更容易用错工具。这里要的是"换人格、留工具提示"。
  • prefix 支持模板变量,例如 {{model}}、{{cwd}}。
  • 想让它更像纯聊天机器人,可以把专业/严谨那两句删掉;想让它更严谨,可以加更多约束。

为什么 suffix 不能留空

suffix 默认是空字符串,而空后缀会遮蔽掉部署级的 Your working directory is {{cwd}}.。

运行时上下文里没有任何 cwd 提供方(packages/context/ 下只有 agent-instructions、file-reference、session-reference、time-context、tmux-context),也就是说 {{cwd}} 是工作目录进入模型视野的唯一通道。而本模式挂了 read / glob / grep / write 四个按会话 cwd 解析相对路径的工具——不知道 cwd,模型就只能等你给绝对路径。

所以这里要显式写一句:suffix: '当前工作目录是 {{cwd}}。'


安装

方式一:直接从 git 仓库安装(推荐)

dsh plugin --profile <你的profile> add https://github.com/zdjmrq/dsh-better-chat

DSH 的 install_bundle 原生支持 git 仓库 URL,pnpm 会克隆仓库并把它装进 profile,不需要先 clone、也不需要发到 npm。

想锁定版本就加 commitish:

dsh plugin --profile <你的profile> add "https://github.com/zdjmrq/dsh-better-chat#v0.1.0"

方式二:克隆后按本地路径安装

git clone https://github.com/zdjmrq/dsh-better-chat.git
dsh plugin --profile <你的profile> add "<clone 出来的绝对路径>"

适合要自己改代码的场景——就地改,重装一次即生效。

方式三:Web 插件页

侧边栏 插件 页 → 安装组合包 → 填上面任意一种 spec(git URL 或绝对路径)。

三种方式装完都需要重启 DSH 才会看到新模式。

本地路径方式会创建目录链接(junction / symlink),那个目录不要删除或移动,否则 profile 启动时会跳过这个 bundle。

本插件没有发布到 npm:git 安装方式已经覆盖同样的效果,没必要多维护一个发布渠道。


为什么工具行在 preset 里面

cordis.patch.yml 里只有一行(preset-chat);keep_thinking 是它 config.plugins 里的一个子行,按包名引入:

- id: think-better-tool
  name: 'think_better'
  config:
    maxRounds: 10

为什么包名能解析(这次是从源码定的)

0.1.x 用裸包名时失败过:

think-better-tool (think_better): never started

(never started 在 packages/preset/agent-preset-registry/src/mount.ts 中等价于「模块根本没解析成功」。)

当时的解释是「preset 的行挂在注册表拥有的内存子树里,profile 本地包名那条规则不适用」。这个解释站不住——把 packages/boot/app-boot 的解析器读完,四个条件现在全部成立:

条件源码位置本机取值
导入方 baseURL 落在 profile 目录里mountPreset 用 prepareProfileEntries(ctx, plugins, ctx.baseUrl)——注释原文是 "scope context inheriting the declaring Loader's resolution base"探针实测 file:///…/profiles/desktop/ ✅
拦截层由导入方路径决定,不由"谁建了这棵子树"决定findInterceptionLayer() → computeProfileLayer()路径在 active profile 之下 → { kind: 'profile', active: true } ✅
该层 active同上✅
包名在 localPackageNames 里profile manifest 的 dependenciesthink_better: link:… ✅

四条齐了,routeLocalPackage 就返回 route: { kind: 'native' },交给 Node 按普通 node_modules 解析。

所以当初那次失败最可能就是:think_better 还没写进 profile 的 dependencies,localPackageNames 里没有它,路由被拒——跟"preset 子树"没关系。

为什么不能发布绝对 file URL

绝对 file: URL 在本机能跑,但发布包不能带它。指南 R22 明确:「发布包不得含作者机器的绝对路径」。原因不只是"换台机器路径就没了"——preset 里任何一行失败 = 整棵子树失败:

// agent-preset-registry/src/mount.ts
const audit = await auditRows(tree)
if (audit.failed.length > 0) throw new Error(audit.failed.join("\n"))

也就是说别人装完拿到的不是「少一个工具」,而是一个挂不上的模式,报错还是不指向根因的 never started。这就是 0.4.0 改回包名的唯一理由。

万一它哪天又不解析了:本机临时把这一行改回绝对 file:// URL 去定位,但不要把那个改动提交/发布。

真正的收益是作用域

工具行挂在 preset 里,ctx.tools.register() 就落在 preset 的作用域上:只有从「更好的对话模式」组合出来的 agent 看得见 keep_thinking,其它模式什么都不用摘。插件因此没有任何可见性逻辑——不监听 agent/created、不推断当前预设、不调用 tools.restrict()。

实测记录(把没验证的结论跟验证过的分开写)

  • 用一个一次性的探针预设实测:preset 子树里按 file URL 挂一行,IMPORTED 和 APPLIED 都会发生 ✅(file URL 这条路本身是通的,只是不能发布。)
  • 探针读到的 ctx.baseUrl 是 profile 目录(file:///…/profiles/<name>/),不是 app.asar——这正是上面那张表第一行的依据。
  • 未实测:0.4.0 换成包名之后的解析结果是从源码规则推出来的(四个条件逐条核过),不是重新探针测出来的。首次重启后请按本地自测那条清单确认一遍。

顺带记录:切换预设的窗口

  • 会话头里的 agentPreset 是创建时的冻结事实,不是当前值。agent-preset-registry/src/session.ts 的原文是:"reads the agentPreset Session projection, never the header alone"。
  • select() 会拒绝已经开过轮的会话(抛 agent-preset/locked: This session has already started)。预设只能在第一次对话之前选,一旦跑过一轮就冻结——所以在旧会话里换不到「更好的对话模式」,只能新建一个。

这两条在旧版本里曾经是个坑(那时可见性靠「按会话头 deny、切预设再解除」,还会踩到 select() 的时序)。现在工具跟着配方走,可见性已经与预设身份无关。


插件列表里的显示名

插件页显示的不是包名,而是一份可本地化的显示元数据。取法(packages/boot/app-boot/src/package-meta.ts 的 readPluginMeta):

  1. 先解析 <包名>/locale/en.json —— 这个文件是入口,没有它,其它语言文件根本不会被扫描;
  2. 再扫同目录下所有 <语言id>.json,文件名就是查表用的键(小写化:zh-CN.json → zh-cn);
  3. meta.title / meta.description 取到就用;取不到则回落到 package.json 的 name / description;
  4. 客户端 presentation.ts 是 title: pkg.meta?.title ?? pkg.name —— 所以包名和显示名是两回事。

⚠️ 文件名必须和 DSH 的语言 id 一模一样

这一步很容易错。 DSH 内置的语言 id 是 zh 和 en(packages/client/locale/src/locale-settings.ts:LOCALE_IDS = ['zh', 'en']),不是 zh-CN。

查表是按当前语言 id 做的(locale/src/client/index.ts):

resolveText(text) {
  return this.fallbackChain(this.snapshot.active).reduceRight(
    (resolved, locale) => text[localeKey(locale)] ?? resolved,   // localeKey = toLowerCase()
    text.en,
  )
}

所以文件名写成 zh-CN.json 时,键是 zh-cn,而当前语言是 zh → 查不到 → 静默回落到英文。页面就会显示英文标题和英文描述,而且不报任何错。

本仓库因此同时放两个,覆盖 zh 和可能的 zh-CN 语言包:

// locale/zh.json       ← 内置语言,主要就靠这个
// locale/zh-CN.json    ← 兼容自定义 zh-CN 语言包
{ "meta": { "title": "更好的对话模式", "description": "…" } }

// locale/en.json       ← 入口文件 + 英文兜底
{ "meta": { "title": "Better Chat Mode", "description": "…" } }

插件页上哪些能改、哪些改不了

页面元素来源能不能中文化
标题meta.title✅
描述meta.description✅
版本号package.json 的 version❌ 本来就不该翻译
包名(等宽字体那一行)package.json 的 name❌ 原样显示(PluginManagerPage.tsx 里是 <code>{pkg.name}</code>),而且 npm 包名不允许中文,它同时是 dsh plugin remove <name> 要用的句柄

两个前提,缺一不可:

  • package.json 的 exports 必须放行这个路径("./locale/*": "./locale/*")——解析走的是 Node 的 ESM resolver,exports 不放行就等于没有这个文件(报 ERR_PACKAGE_PATH_NOT_EXPORTED,被当成"没配元数据"静默回落);
  • files 里要有 locale,否则打包/安装时不会带上。

包名 think_better、行 id think-better-tool、模块名都不用动——显示名和它们是分开的两层。另外 package.json 顶层可以放 icon(相对路径,SVG/PNG/JPEG/WebP,≤256 KiB),会被内联成 data URL 显示在插件页。

元数据在启动时读取,改完要重启。


卸载

dsh plugin --profile <你的profile> remove think_better

本地自测

lib/index.js 的两个 harness import 被 test/stub-loader.mjs 顶替成桩,所以测试直接跑线上那个文件,不需要装 DSH:

pnpm test        # = node --import ./test/register.mjs test/behaviour.test.mjs

覆盖:只注册一个工具、从不调用 tools.restrict()(可见性归作用域管,插件不该有可见性逻辑)、轮数计数与 turn/start/agent/disposed 的重置、每会话独立计数、maxRounds 行为、三档返回文案、空 thought 被拒且不计轮、dispose 之后工具与监听器全部注销(HMR 安全)。

0.2.x 那版测的是「从会话日志推断当前预设 + 解除 deny」;那套逻辑已经删掉了,相应的断言也一起删了。

桩的边界(必须知道):stub-dsh-tools.mjs 的 defineTool 是原样返回,stub-schemastery.mjs 什么都接受。所以测试只能断言本模块声明出来的对象形状,不能证明真 defineTool 会接受它、也不能证明真 schemastery 会拒绝坏的 maxRounds。

这不是偷懒:本机普通 Node 解析不到 @deepseek-ai/*(ERR_MODULE_NOT_FOUND),连 import('think_better') 从 profile 目录都失败——宿主里能跑,是因为 DSH 注入了宿主基准解析。所以「真实入口冒烟」在这台机器上只能手动做:

手动冒烟清单(改完 cordis.patch.yml / parameters / output / Config 之后跑一遍)

  1. 重启 DSH,打开 插件 页 → 「更好的对话模式」的内置插件列表里,这一行应该显示**「更好的对话模式」/「Better Chat Mode」(因为 readPluginMeta 现在解析得到包名,能读到 locale/*.json),而不是**一条 file:///… 路径。显示路径 = 解析或元数据出了问题。

  2. 新建一个会话,模式选「更好的对话模式」,问一个需要多步推演的问题,确认模型会调用 keep_thinking,并且返回文案是「继续。(1/10,还剩 9 轮)」这一档。

  3. 想要硬证据就看会话日志里的 request/header——那是实际发给模型的 tools 数组:

    node tools/session-tools.mjs <会话 id 前缀> keep_thinking
    

    列表里有 keep_thinking,说明注册成功且作用域正确。


已知限制

  • keep_thinking 的可见性由作用域决定,不由插件决定:工具行挂在 preset-chat 里,所以只有「更好的对话模式」的 agent 看得见它。插件里没有任何可见性逻辑,也不需要。
  • 工具行按包名(think_better)引入,所以仓库放哪、装到谁的机器上都一样。历史上用绝对 file:// URL 的那几版换机器就装不上,详见为什么不能发布绝对 file URL。
  • 「更好的对话模式」的系统提示词不止人设那 5 行,工具也不止 11 个。 因为 persona.prefix 只遮蔽部署级人设、没设 complete: true,harness 的工具引导段落(read/grep/glob/web_search/present 的用法)会照常注入;Agent Teams 那一大段 POLICY 文字也会进来。实测一轮下发 25 个工具:本 preset 挂的 11 个,加上 agent-team 的 9 个(spawn_teammate/send_message/list_agents/wait_agent/interrupt_agent/team_task_*)、schedule_* 4 个、load_workspace_dependencies 1 个。
  • 这 14 个不是"忘了挂",是本插件收不掉。 tool-agent-team 和 schedule 都在 agent/created 时把工具注册进 agent 自己的作用域(tool-agent-team/src/index.ts:const scoped = agent.ctx;schedule/src/index.ts:registerScheduleTools(ctx, agent.ctx, agent)),而 tools.restrict() 只能遮蔽全局工具——拿作用域内的名字去 restrict 会直接抛 unknown global tool。所以「刻意没有挂的」那张表管不到它们,"把 deny 列表做成配置项"也解决不了;要让「更好的对话模式」真的干净,只能在整个 profile 层面不挂 dsh-experimental-agent-team-profile / dsh-experimental-schedule-bundle(所有模式一起去掉)。这 14 个里只有 load_workspace_dependencies 是全局注册。(本插件现在不做任何 restrict,所以这条只是说明"为什么连做成配置项也收不掉"。)
  • 已经被旧版本 deny 过的运行中会话,需要重启 DSH 后才会恢复(旧版把限制记录在插件内存里;现在改用作用域,重启后天然干净)。
  • tool-fs 带写权限。想要只读,把会话权限切到 profile 里的 read-only 预设。
  • keep_thinking 在对话记录里渲染为通用工具卡片。想做成定制的"思考"卡片,需要另写一个 Client 插件在 tool.call.toolview 槽位注册组件。
  • 插件依赖 DSH 内部插件 id(@deepseek-ai/dsh-*)。DSH 升级后若某个 id 改名,需要同步更新 cordis.patch.yml。

许可

MIT

Comments

Loading…

Similar plugins

dsh-fusion

by omdsh-dev

将多个 DeepSeek Harness 对话融合为一个可继续的会话,支持 Agent 智能剪枝、话题分组、内容排序和界面操作

Manifest valid

★ 3

MIT

TypeScript

Aug 14, 2026

dsh plugin --profile web add @deepseek-ai/dsh-fusion

by JinkaiLiu

Switch between Chat and Agent without leaving your DeepSeek Harness session.

Sessions & MessagesManifest valid

★ 3

↓ 153/wk

MIT

TypeScript

Sep 28, 2026

dsh plugin --profile web add dsh-autonomy

by exoticknight

One-click native conversations with independent workspaces for DeepSeek Harness

Terminal & ClientsUI & ExperienceDevelopment & InfrastructureManifest valid

★ 0

Apache-2.0

JavaScript

Sep 24, 2026

dsh plugin --profile web add dsh-just-chat

by xmuwenxiang

Deepseek Harness插件,可以直接在Deepseek Harness中使用网页版deepseek进行聊天,并将聊天过程直接迁移到Deepseek Harness进一步进行开发。主要目的是节省前期方案讨论时的token

Manifest valid

★ 11

↓ 115/wk

Apache-2.0

TypeScript

Sep 7, 2026

dsh plugin --profile web add dsh-webchat

by my-dsh-plugin

Switch the current session's agent preset (mode) after the model answers and keep chatting. DeepSeek Harness 插件:模型回答后切换模式,继续当前对话。

Manifest valid

★ 0

Apache-2.0

TypeScript

Sep 14, 2026

dsh plugin --profile web add dsh-agent-mode-switcher

by Iambatman1928

Chat agent for a DeepSeek Harness session: characters and personas, several archive threads per character, an undoable/rewindable local transcript, and an event book.

UI & ExperienceTools & CapabilitiesManifest valid

★ 0

MIT

JavaScript

Sep 25, 2026

dsh plugin --profile web add dsh-xingye