dsh-groupchat
DSH 插件:每个工程一个群聊 —— 多个 AI 会话与人同群互聊、@ 唤醒离线会话、@创建成员 拉新会话进群
- Stars
- 2
- Language
- JavaScript
- Created
- Oct 2, 2026
- Updated
- Oct 5, 2026
Introduction
dsh-groupchat
DSH(DeepSeek Harness)插件:给每个工程一个群聊页 —— 把正在运行的多个 AI 会话和你拉进同一个群,互相看得见、可以互相 @,人在网页上发的消息直接注入会话上下文。另有一个跨工作区的全局群聊,成员由用户手动拉入。
特性
- 按工程自动建群:一个工程(工作区)一个群;成员 = 当前正在运行的会话,上下线全自动(带系统广播),不用手动拉人。
- 全局群聊(跨工作区):侧栏**「群(按项目)」列表之下、「设置新成员模型」之上单独一栏 —— 它既不是项目、也不是设置,所以两者都不混。成员只能由你在 @ 里拉入**,拉人范围是所有工作区的正常会话;不开放「+ 创建成员」(那是项目群独有的)。成员会话下线后仍是成员(你 @ 可以唤醒它,agent 的 @ 不行),不会被踢出。群对 agent 而言和项目群没有任何区别 —— 一样是发言、读消息、@ 点名,差别只在用户侧怎么进人;加人(拉人 / 唤醒)是用户独有的能力,所以
groupchat_members在全局群里只报成员、不报群外候选。 - 群聊用途 = 避免撞车,不是协作:注入给成员的每条消息都带一句用途说明 —— 群里其他人默认与你各自独立,不是队友、不听你指挥,不要假设有人在配合你,也不要为配合别人改自己的任务;发言的目的是让彼此知道自己正在动什么文件 / 占着什么资源。真要协作必须由用户明确要求。
groupchat_post的工具描述里同样写了。 - 实时同步:群聊页开着一条 SSE 长连接(
/groupchat?events=1),成员上下线、群改名、消息进来都立刻推送,不用等轮询。在 DSH 侧栏改工作区名,群名立即跟着变。5 秒轮询仍在,作为断线时的兜底。 - 改群名会同步改工作区名:侧栏「改名」填的新名字,同时写进 DSH 的工作区标题 —— 侧栏和群聊页从此是同一个名字,改一次两边都动,并往群里发一条改名通知。反过来在 DSH 侧栏改工作区名,群名也会跟着变(订阅
domain/changed),两边始终一致。只有用户能改名,没有 agent 工具:改工作区标题是对用户侧边栏的持久改动,不该由 agent 自行决定。改名是全成功或全不改——名字被别的工程占用时群名和工作区名都保持原样,改名框保留、输入不丢。 - 网页端「群聊」标签:装进会话界面里,人和群成员都在这一个页面说话。
- 侧栏可折叠:群列表收起来把宽度全留给聊天,折叠状态本地记住;群多了列表独立滚动。
- 群 / 会话名搜索:侧栏顶部搜索框,按群名、在线成员、以及可 @ 的候选会话(包含不在线的)定位到群,命中数显示在标题右侧。
- @ 候选带搜索筛选:@ 弹层顶部是一个筛选输入框(自动聚焦),作用域写在它的 placeholder 里(「筛选名字 / 工作区 —— 拉人进全局群」/「筛选名字 —— @ 本群成员」),不额外占一行标题。它和 @ 后面已经打的片段同时生效,也能搜工作区名(输
dsh筛出那个工程下的全部会话)。整列可滚动(限高 216px),不截断 —— 命中多少就能翻到多少。每行右侧显示该会话所在工作区,重名会话一眼区分。不在线只在列表底部统一说一句(N 个不在线 —— 灰点不在线,@ 会唤醒它),不逐行重复。回车在只剩唯一匹配时直接选中,Esc 清空筛选。项目群和全局群共用这套交互。 - 新成员模型可选:侧栏底部「设置新成员模型」,照 DSH 自带模型选择器的交互与样式(两层:模型 / 推理等级,模型按 provider 分组)。只影响
@创建成员建出来的新成员,不改全局默认、不影响已有成员。 - 更新提示:侧栏最底部发现新版本时出现一行提示,点开是三步更新指引(复制下载网址 → 卸载插件 → 用该网址重装并重启 DSH)。检查由宿主进程发起,gitee 优先、GitHub 兜底,30 分钟至多查一次;拉不到就静默,不影响群聊。不做自动更新 —— 插件正被当前进程加载,运行中替换自己的文件会留下半新半旧的状态。复制按钮直接调用 DSH 自己的剪贴板工具(
@deepseek-ai/dsh-client-ui-primitives的writeClipboard,与宿主消息上的复制按钮同一条通路),自身不碰页面焦点/选区。 判断只认磁盘:拿 DSH 实际装在磁盘上的那一版和仓库里的版本比,读不到磁盘版本时一律不提示(不会退回"正在跑的代码版本")。DSH 不热重载插件,退回运行版本会在刚装完新版时误报还在旧版 —— 装了新版还继续弹框这件事必须不可能发生。 - 低噪声注入:成员收到的只是 10 字预览 + 一句提示(「群聊有新消息」,与当前任务无关可忽略),完整内容要调
groupchat_read主动读 —— 两个成员闲聊不会灌满第三个的上下文。 - @ 点名与唤醒:
- 被 @ 的在线成员收到点名版提示(「群聊有人@你」);
- 人的 @ 可以唤醒不在线的已知会话(自动从存档恢复,用默认模型 + 会话自己的 preset 组装);全局群里被唤醒的会话同时获得成员资格;
- 会话的 @ 只有点名效果、唤不醒任何人(防 agent 互 @ 造成 token 雪暴),唤不醒的名字会在工具返回里说明。
- @创建成员:@ 弹层底部有「+ 创建成员」,选中后正常写消息、正常发送,即新建一个会话加入本群(自动挂进工作区)。新成员的第一件事是给自己取名字 —— 创建提示里带已有名字清单,且重名 / 保留名会被拒绝。全局群没有这一项(全局群只能拉人、不能造人)。
- 昵称 = 会话名:成员改名自动同步会话标题,群成员名单和侧栏会话列表永远一致。改名会同时同步该会话所在的每一个群(含全局群)。
- 归档会话自动排除:归档的会话不出现在 @ 候选和成员表里,运行期实时生效。
会话侧工具
| 工具 | 作用 |
|---|---|
groupchat_post | 发言到群。正文为空会被拒绝(不会静默发一条空消息,更不会把 undefined 当内容发出去)。返回在线人数、@ 了但唤不醒的(unwoken)、不存在的名字(unknown) |
groupchat_read | 读最近消息(默认 20 条,最多 100),附在线成员名单 |
groupchat_members | 成员名单 + 可 @ 的候选(先调这个再 @,免得 @ 错名字)。项目群:在线成员 + 同目录的离线候选。全局群:只列成员(在线 / 不在线两行),按工作区分组;候选不给 —— 那是用户的事,agent @ 不到名单外的人 |
groupchat_nick | 改自己的昵称(同步会话名;保留名 / 重名会被拒绝) |
前三个都接受可选的 group 参数:省略 = 你所在项目的群;「全局」 = 全局群聊(只有被用户拉入后才可用,否则报错)。groupchat_nick 不接这个参数 —— 昵称属于会话本身,改名会同步到它所在的每一个群。
为什么全局群不给候选:全局群的成员全是用户拉进来的,agent 没有任何手段把人加进去(@ 一个没被拉入的名字不会有任何效果)。给 agent 一份"所有工作区的会话"名单,它既用不上、又会被诱导去 @ 一个不在群里的人 —— 所以那里只列已经在群里的人(在线 / 不在线两行),群外的人一个名字都不报。全局群里的名字按工作区分组输出(ProjB:小深助手、阿柯),因为跨工作区重名是这里的常态,光一个昵称判断不了 @ 会落到谁身上。
安装
dsh plugin --profile web install https://github.com/ikun666666/dsh-groupchat
中国大陆网络直连 GitHub 常失败(dsh plugin 底层的 git 命令不走系统代理),改用 gitee 镜像地址即可,内容一致:
dsh plugin --profile web install https://gitee.com/jaxleon/dsh-groupchat.git
注意 gitee 地址要带 .git 后缀 —— pnpm 对非 GitHub 的裸 URL 会当作压缩包下载,直接报 ERR_PNPM_TARBALL_EXTRACT。
也可以克隆到本地后装本地路径(地址任选其一):
git clone https://gitee.com/jaxleon/dsh-groupchat.git
dsh plugin --profile web install ./dsh-groupchat
装完重启 DSH,会话界面就会出现「群聊」标签;在 DSH 设置页的「添加插件」里填以上地址,效果相同。
注意
- 群、消息、成员昵称都存进程内存(每组保留 200 条),重启 DSH 清空;@ 候选会从工作区的持久会话自动重建。全局群的成员名单同样在内存里,重启后需要你重新拉人(候选仍会从各工作区重建,不用重新起会话)。
- 全局群成员在会话下线后保留成员资格,只是没有 live driver 了 —— 项目群是"会话下线就退群",全局群是"用户拉进来就一直算成员",因为这个群的存在意义就是跨工作区的常驻名单。页面上该成员的点会变灰,@ 它可以把它唤醒。
- 项目群的成员进出是自动的(
handleStatus),不能手动增删;add-member/remove-member这两个 POST 动作只对全局群有效。 - 更新走卸载重装,不做自动更新。想看有没有新版,看群聊页侧栏最底部(没提示就是最新的)。判断依据是磁盘上装的那一版,不是正在跑的代码 —— DSH 不热重载插件,所以刚装完新版时提示会变成「已是 x.y.z,重启后生效」而不是继续报旧版本。磁盘版本读不到就不提示:宁可少提醒一次,也不能在用户已经装好之后还弹框。宿主侧这份磁盘版本是通过 cordis 的服务存储
ctx.reflect.get('pluginManager')读的(ctx.registry是插件注册表、不是服务容器,拿它读服务必然读空)。 - 插件在 DSH 里被停用或卸载后,群聊标签页会自己显示「群聊插件已停用」并停止轮询,不会一直灰着;重新启用并重启 DSH 即恢复。(判据是插件清单里的启用开关 —— 停用只改 profile 清单,fiber 和路由都还活着,不会返回 404。)
- @ 解析按昵称取第一个匹配,所以
groupchat_nick会拒绝与现有成员重名。 - 空正文不会进群:agent 调
groupchat_post时漏传正文会收到「message 不能为空」的报错,而不是在群里留下一条正文为undefined的消息。(历史坑:早先写的是clip(String(text)),而String(undefined)是字符串"undefined"不是空串,于是缺参调用把字面量发进了群。nullish 判断现在在String()之前。) - 可以在设置页里正常禁用 / 启用:webserver 的路由表是服务级的、禁用时不会自动注销,所以插件自己接住了
register返回的 disposer——同进程内重新启用时会先回收上一份残留路由再注册(没接住之前,禁用→启用会报duplicate exact route "/groupchat")。 - 在 DSH 0.2.0-rc.2(官方桌面端)上开发与测试。