dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Discussion mode for DeepSeek Harness (dsh) - a sidebar-toggled design conversation where the agent consults as a domain expert, acting tools are denied, and only the user can end the mode. 讨论模式
在动手开发之前,先和一位行业专家把想法讨论清楚——而不是被一个急于执行的助手敷衍过去。
DSH 里模型天然是任务导向的:它倾向于尽快收敛成方案、尽快开始写代码。平时这没错,但在"我还不知道要做什么"的阶段,这种倾向恰恰是最没用的。这个插件给会话加一个讨论模式:模型换成资深从业者的姿态,动手类工具被直接拒绝,而退出讨论模式只能由你发起。
装好并重启后,左侧栏会多出一个「讨论模式」图标(排在自带的 Plugins、Schedule 之后)。点它打开面板:
区域 · 作用 状态 · 实时显示当前会话是否处于讨论模式 按钮 · 进入 / 退出。点完自动回到对话——因为进入模式之后,你要做的正是说出那个还没想清楚的想法 策略原文 · 把 AI 在此模式下被告知的内容摊开给你看 什么时候用它 · 三种典型场景
面板上的按钮不是另一套状态机:它执行的就是下面这条命令本身。
/discuss 进入讨论模式
/discuss <你的想法> 进入,并把这句话作为讨论的开场
/discuss off 退出命令也会出现在 Web 输入框的 / 菜单里,所以在对话中直接敲更顺手——进出都不必离开输入框。
输入框上方的模式下拉框——和「标准模式 / PTC 模式 / 极简模式 / 创造模式」同一处——里也有一条讨论模式。那是 DSH 的 Agent 预设:一条预设定义的是一整个会话的插件组装,所以它的语义和 /discuss 不完全一样:
· 模式下拉框 · /discuss、侧边栏面板 作用对象 · 新会话(选的是"这条会话用什么组装") · 当前会话 生效时机 · 一进去就在讨论模式里,不用先敲命令 · 敲下去那一刻 能否改主意 · 会话一旦开始就不能换预设(DSH 拒绝给已开始的会话换组装) · 随时进、随时出 退出之后 · 就是完整的标准会话——组装本来就是标准模式那套工具面 · 同左
所以它是入口,不是第二套状态机:退出仍然只有你能发起,/discuss off 与面板按钮在这条会话里照常管用。
这条预设的组装是标准模式工具面的快照(presets/discuss.patch.yml),理由就是上表最后一行——出讨论模式之后你还得能干活。DSH 没有"引用另一条预设"的写法,所以那份清单是抄来的:它抄自安装里 @deepseek-ai/dsh-web-app@0.2.1-rc.2 的 presets/standard.patch.yml,夹在文件里的 BEGIN/END 生成标记之间。这次对齐还修掉一处会炸的差异——0.2.1 移除了 {{cwd}} 提示词变量,而旧快照里 persona 的 suffix 用了它,讨论模式的会话会因此连第一次请求都发不出去。
升级 DSH 之后想让两边一致,重抄也是命令化的:
$env:DSH_APP_ASAR='D:\DSHD\resources\app.asar'
node tools/sync-preset.mjs # 就地重写生成区
node tools/sync-preset.mjs --check # 只比对:过期时 exit 1,一致时 exit 0--asar 也可以代替 DSH_APP_ASAR;工具只改写生成区,文件顶部的说明注释和生成标记保持原样。拿不准两边是否一致时先跑 --check。
讨论快到头的时候,它先摆出一版 MVP 草案:做什么、不做什么、最大风险在哪,外加它替你补的假设和自己拿不准的地方;然后停住:「范围我理解对了吗?你还有没有没说完的?」——你明确点头之后,才交出一段交接 prompt,单独一个代码块,可以直接粘进一个全新的会话。新会话里的执行者看不到你们说过的任何一句话,所以那段 prompt 要求自足:目标与验收、范围、已经定下的决定、已知的坑、从哪下手。
这是刻意的分工:讨论留在这里,动手去那边做。讨论模式的价值在于判断没有被"顺手开始实现"冲掉;把结论写成一段能带走的东西,比让同一个会话继续跑下去更贴近这个目的——没有它,讨论的产物就只是聊天记录。而"聊完了没有"这个判断交回给你:草案只是提案,点头才算成交。
plan mode 让模型调用 exit_plan_mode 来结束规划。它仍然是一个任务形态:模型在为你交回一份可执行的计划。
讨论模式反过来:模型没有任何结束这个模式的工具。唯一的出口是你在面板上点按钮,或在输入框敲 /discuss off。
理由很直接:如果一个"我要开始实现了"的动作可供调用,一个急于执行的模型就会去抓它。把可供性拿掉,急迫感在结构上就没有落脚点——这比在提示词里写一百遍"不要着急"都管用。模型仍然可以用自然语言说"我觉得聊得差不多了",但决定权在你手里。
这是设计上最容易搞错的一点。讨论模式不是"慢下来",而是收敛到别的东西:
· 终点 默认(无模式) · 代码 讨论模式 · 一个有依据的判断 + MVP 范围 plan mode · 一份已批准的计划
所以要防的是三种失败,而不只是一种:
前两种的机制不一样:动手是工具调用,所以能被 ctx.tools.guard 硬拦;交付是生成的文本,不经过任何工具,没有可供拦截的调用点,只能在话轮协议里把"什么时候算聊完"交回用户。策略文本里写死了两件事:"不允许连续两轮只提问不给判断",以及收敛的先后顺序——判断随时给 / 对齐(草案 + 两条确认 + 停)/ 用户明确点头后才交付 prompt。
只靠提示词约束行为是不可靠的。模式激活时,一个单调的 ctx.tools.guard() 会拒绝动手类工具,并把拒绝理由返回给模型:
write、edit、pwsh、bash、subagent、subagent_fork、workflow、spawn_teammate、send_message、create_goal、update_goal、job_kill、schedule_*、agent_teams_*、team_task_*read、glob、grep、read_image、web_search、web_fetch、ask_user_question、todo_write、skill、各类只读的 list/status 工具只读探索是故意保留的:讨论已有项目时,策略要求它先读相关代码再开口。
"单调"是 DSH 的机制保证——拒绝一旦成立,任何后续监听器都无法把它翻回允许。拒绝理由会作为 Error: 内容回到模型上下文里,所以模型能立刻改道,而不是反复撞墙。
模式状态是一个 sessionProjections 单元,从两处日志折叠出来:/discuss 命令自己的 command/run / command/done 记录,以及会话一开始就记着的 agentPreset(表头字段与 agent-preset/selected 事件)。这带来三个白拿的好处:会话恢复、fork、重放都能自动还原状态。
这也是必须的做法:DSH 不允许插件 append 自定义类型的事件——不认识该类型的读取方会拒绝重新打开会话。
两个入口在折叠时是有先后的,不是并列的:只要有一次 /discuss 跑完并落进日志,之后这条会话就听那个决定——否则在预设会话里敲 /discuss off 会被预设当场撤销。在那之前,会话的预设说了算,所以预设入口不需要任何命令就生效。
侧边栏面板读的就是这个投影的 wire.view,并通过执行同一条命令来改变它,所以界面不可能和日志脱节——预设入口也是同一个 view,面板显示的"已开启"和你看到的一致。
作为 bundle 安装时,默认值是内置的,无需任何配置。要覆盖,在 profile 的 cordis.patch.yml 里重述你保留的字段(patch 是整体替换 `config`,不是深合并):
- id: discuss-mode
name: 'dsh-discuss-mode'
config:
blockedTools: ['write', 'edit', 'pwsh', 'bash', 'subagent']字段 · 默认 · 含义 section · 内置中文策略文本 · 讨论模式激活时渲染为 discuss:policy 提示词段落的内容 denial · 内置中文拒绝语 · 工具被拒绝时返回给模型的理由;{tool} 会替换为工具名 blockedTools · 见上文清单 · 被拒绝的工具;精确名,或以 * 结尾表示前缀匹配(如 agent_teams_*) presetId · discuss · 哪条 Agent 预设的会话一进去就在讨论模式里;写 null 关掉这个入口,只留 /discuss
未知的配置键会在激活时报错,而不是被静默忽略——cordis.patch.yml 里的笔误会立刻暴露。
策略文本是可以改的,而且应该改。 它才是这个插件的正文;代码只是把它接到正确的时机上。注意面板里显示的是随插件发布的默认文案,若你覆盖了 section,以你的版本为准。
本版本(0.2.1)针对 DSH v0.2.1-rc.2 编写:
声明 · 值 engines.dsh · >=0.2.1-rc.2 <0.3.0 五个 @deepseek-ai/dsh-* peer · 0.2.1-rc.2 dsh.manifestVersion · 1
DSH 的插件兼容性预检(v0.2.1-alpha.3 起)先读 manifest 自己的 engines.dsh,没有才拿运行时版本逐个匹配 @deepseek-ai/dsh-* peer(includePrerelease 语义)。把范围写进 manifest,装错宿主版本时由预检直接拒绝,而不是等运行到一半才炸。
DSH 0.2.0-rc.2 及更早不再支持:旧清单把五个 peer 固定为 0.2.0-rc.2,精确范围不满足运行时 0.2.1-rc.2,预检会判为不兼容并拒绝加载插件的行。
要求 DSH v0.2.1-rc.2(见上文「兼容性」)。本包是一个 bundle(package.json 里的 dsh.bundle.patch 指向两个补丁层:cordis.patch.yml 挂插件行,presets/discuss.patch.yml 声明那条 Agent 预设),安装它就会自动激活,无需手工编辑 profile:
& 'D:\DSHD\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop add 'D:\OneDrive\Desktop\Code\dsh\dsh-discuss-mode'装完需要完全退出并重启 DeepSeek Harness。两个原因:本机正在运行的进程早于这次安装;而宿主侧与浏览器侧的新模块都需要在启动时重新组装。
升级时注意:profile 默认跑 HMR,往已装过的包上再执行一次 add 会被以 already-installed 拒绝(换版本、换路径也一样)。要换,先移除再装:
& 'D:\DSHD\resources\runtime\cli\bin\dsh.cmd' plugin --profile desktop remove dsh-discuss-mode而这个仓库是以 linked checkout(本地目录)方式挂进 profile 的:只是更新了检出内容时不必重装,完全退出并重启 DSH 即可。
重启后侧边栏出现「讨论模式」图标即安装成功。也可以在 Plugins 页确认并开关。
run_code 的运行时有裸的文件系统访问,那是一个缺口。native 模式(默认)下不存在这个工具。plan:policy,然后是 550 的 discuss:policy)。实际使用中你不会想同时开两个。retainedBy.mainView > 0——与 DSH 自带的 Workspace、Open-in-app 插件用的是同一个办法。没有打开会话时按钮禁用。agent-preset/locked)。所以"当前会话进讨论模式"永远走 /discuss 或面板,下拉框只负责开一条新的。@deepseek-ai/dsh-web-app@0.2.1-rc.2 的 presets/standard.patch.yml:这一版的标准模式多了 time-context、tool-schedule、tool-plugin-market(最后一个 disabled),少了 tool-subagent-codex、tool-subagent-claude-code、tool-ralph 三行以及 backgroundMode 配置。升级 DSH 后标准模式再变时,讨论模式预设里那份不会自己跟着长——跑 node tools/sync-preset.mjs 重抄;node test/verify.mjs 会检查生成标记、{{cwd}} 缺席与这些关键行的有无,tools/sync-preset.mjs --check 则直接告诉你它是否已经过期。宿主没有"预设继承另一条预设"的写法,这是唯一的选择。presetId,默认 discuss):只装插件、不装这条预设的部署完全不受影响——没有哪条会话的 agentPreset 会等于它,模式照旧只由 /discuss 开关。write / edit / pwsh 那些。想要更严格的立场,把它加进 blockedTools 即可。没有构建步骤:两个手写的普通 JS(宿主半 + 浏览器半)、两层补丁(插件行 + Agent 预设声明)、图标与两份 locale;Agent 预设那份快照由仓库里的 tools/sync-preset.mjs 重抄(开发期工具,不随包发布)。
关注点 · 机制 兼容性 · package.json 声明 engines.dsh: '>=0.2.1-rc.2 string \ · undefined),按会话状态条件判断 斜杠命令 · ctx.commands.register({ name: 'discuss', input: { hint: '[off\ · message]', attachments: true } }) 状态 · ctx.sessionProjections.register({ key: 'discuss', apply, wire }),stateVersion: 2 预设入口 · package.json 的 dsh.bundle.patch 是有序数组:cordis.patch.yml 之后追一层 presets/discuss.patch.yml,后者插一行 @deepseek-ai/dsh-agent-preset(config.id: discuss) 预设会话的默认状态 · 投影 init(header) 读表头的 agentPreset,apply 再折叠 agent-preset/selected;一旦有 /discuss 的 settled 决定,决定优先于预设 侧边栏入口 · ctx.slots.inject('sidebar.panellist', ...),order: 20 面板页面 · ctx.slots.inject('main', ...),keyed,key 同为 discuss-mode 面板 → 宿主 · ctx.remote.commands.execute(sessionId, '/discuss' \ · '/discuss off', []) 宿主 → 面板 · sessions.binding(id).session.projections.faceOf('discuss') 生命周期 · 每个注册都包在 ctx.effect(...) 里——服务侧注册挂在服务自己的 fiber 上,不包的话插件重载会留下陈旧段落并导致重名冲突
客户端不引入任何 DSH 客户端包(它们随版本变化,而手写 JS 没有类型检查),只用模块表里的 react,样式走 --dsw-alias-* 主题变量并带兜底值。
参考实现:@deepseek-ai/dsh-plan-mode(同体系的姊妹模式)、@deepseek-ai/dsh-persona(ctx.effect 包装的写法)、dsh-mobile-remote(侧边栏面板 + sidebar.panellist)。
本机到 github.com:443——也就是 git push 用的那个端点——不稳定,实测会连接超时或被重置;而 gh 走 api.github.com 完全正常。所以仓库带了一个走 Git Data API 的推送脚本:
powershell -ExecutionPolicy Bypass -File tools\push-via-api.ps1它不是复制:git 的对象 id 是内容的纯函数,上传同样的 blob / tree / 作者 / 提交者 / 时间 / 提交信息必然得到同一个 SHA。脚本在每一步都比对 git 自己的答案,对不上就拒绝移动 ref——所以本地和远端不会悄悄分叉,git status 保持准确,等网络恢复后 git push 也没有多余的事可做。
前提是远端仓库已有至少一个提交(GitHub 对完全空的仓库会以 409 Git Repository is empty 拒绝创建 blob/tree)。
test/verify.mjs 是随附的隔离测试:给 @deepseek-ai/dsh-llm 打桩后真实导入宿主侧代码,用假 ctx 跑通 apply() 的接线,并检查状态机(两个入口的先后、/discuss off 不会被预设撤销)、配置校验、工具匹配器、命令分支、两层补丁的清单与形状、客户端契约(含两半策略文本必须逐字相同的一致性断言)。0.2.1 适配后它还盯住兼容性声明与预设快照:version 是 0.2.x、engines.dsh 的范围、五个 @deepseek-ai/dsh-* peer 固定 0.2.1-rc.2、dsh.manifestVersion === 1,以及生成标记存在、{{cwd}} 缺席、time-context / tool-schedule / tool-plugin-market 在列、codex / claude-code / ralph 行与 backgroundMode 不在列。设了 DSH_APP_ASAR 时它还会顺带跑一次 sync 的 --check,没设就跳过,不算失败。
node test/verify.mjs不需要安装任何依赖——桩模块由脚本自己生成在 test/.sandbox/(已 gitignore)。应输出 N passed, 0 failed(N 为检查项数)并以 0 退出。
tools/sync-preset.mjs 回答的是另一个问题:生成区是否还等于安装里的标准预设。
$env:DSH_APP_ASAR='D:\DSHD\resources\app.asar'; node tools/sync-preset.mjs --check