- Accueil
- Plugins
- Améliorations UI
- dsh-chat-plus
dsh-chat-plus
kr-atg/dsh-chat-plus
Ensemble qui enrichit les conversations de DeepSeek Harness par injection de plugins, sans modifier le code source. Le tableau Seeker à deux colonnes affiche à droite quatre cartes (tâches, panneau d’opérations, sous-agents, livrables) et une mémoire épinglée en bas ; à gauche, une carte d’état sur une ligne et des cartes de réflexion et de questions-réponses intégrées au fil (la taille suit le réglage officiel « Paramètres → 字号 »). Le contenu peut inclure un bac html exécutant du vrai HTML/JS, des cartes diagram (organigrammes), la carte interactive native iu et proto-tabs. S’ajoutent des captures de conversation, un outil download, la conversion de chemins nus en liens et un centre de fournisseurs avec API Key, proxy réseau, génération et reconnaissance d’images, et génération vidéo.
Installer
dsh plugin --profile web add github:kr-atg/dsh-chat-plusREADME
dsh-chat-plus
DeepSeek Harness 的对话体验增强套件:让对话读得更顺、能出可交互的卡片、把执行过程翻译成人话, 再把记忆 / SKILL / 用量 / 画廊 / 档案 / 系统提示词 / 同步设置收成一排工作台。
零 DSH 源码改动 —— 全部能力以 Cordis 插件注入,官方升级不动它。
dsh plugin --profile web add github:Kr-ATG/dsh-chat-plus
装完重启 DSH 即可。本包声明了 dsh.bundle.patch,dsh plugin add 会自动加进 profile 的
bundles 层,不用手改配置文件。
目录
- 你可能最想要的
- Seeker 视图:双栏执行大盘
- 正文里能出卡片
- 对话读起来更顺
- 七个工作台
- 输入区与工具栏
- Agent 邮箱(只读卡片)
- 供应商中心(设置 → 供应商)
- 提示词通道:什么时候模型才知道这些围栏
- 安装与卸载
- 配置一览
- 常见问题
- 从旧版本升级
- 开发
- 许可
你可能最想要的
| 想要 | 在哪 |
|---|---|
| 看 AI 到底在干什么,但不想要一屏工具日志 | Seeker 视图右侧大盘;左栏只留一行「正在…」 |
| 让 AI 直接给个能点的小工具 / 可视化 / 演示页 | 正文里写 ```html 围栏 → HTML 卡片 |
| 让 AI 画流程图 / 架构图 / 时序图 | 正文里写 ```html 围栏,手写内联 SVG → HTML 卡片 |
| 快速取值换算 / 对比 / 清单,不要长篇大论 | 输入框发 /iu 开头 → iu 原生卡片 |
| 让 AI 知道「我现在开着什么」 | 输入框打 @ → 运行中的程序 |
| 给不会编程的人看「这轮做了什么」 | 右栏操作面板卡,中文人话时间线 |
| 让 AI 记住我的偏好和身份 | 工作台记忆 |
| 存账号密码 / 卡 / 证件照片,还要能自动进上下文 | 工作台**档案** |
| 管 API Key / 代理 / 生图模型 | 官方设置弹窗 → 供应商 |
| 看 Agent 收到什么邮件、等验证码 | Seeker 标签右侧的邮箱按钮(居中只读卡片,6 个 mail_* 工具) |
Seeker 视图:双栏执行大盘
会话头部标签栏多一个 「Seeker」(内部代号 KR),与官方「对话 / 轨迹」并列。点进去, 底层仍是官方 ChatView(多轮历史、虚拟滚动、Markdown、输入框全部原样),右边多一栏全高执行大盘。
┌─ 左栏(官方 ChatView)────────────┬─ 右栏 Seeker 大盘 ────────────┐
│ │ ┌ 子智能体 ────────────────┐ │
│ [你] … │ │ 固定 5 行,整行可点跳转 │ │
│ [AI] … │ └──────────────────────────┘ │
│ │ ┌ 操作面板 ────────────────┐ │
│ (Seeker 正在读取文件 ··· 12s) │ │ 任务动画 + 当前这一件事 │ │
│ ┌ 思考过程 ─────────────────┐ │ │ 默认收起,点开看完整流水 │ │
│ │ 贴在对话流里,跑时展开 │ │ └──────────────────────────┘ │
│ │ 收口自动折叠成一行 │ │ ┌ 任务 ────────────────────┐ │
│ └───────────────────────────┘ │ │ 只列本轮清单 │ │
│ ┌ 提问与回答 ───────────────┐ │ └──────────────────────────┘ │
│ │ 问了什么 / 你答了什么 │ │ ┌ 产出物 ──────────────────┐ │
│ └───────────────────────────┘ │ │ 本次做出的文件,可点预览 │ │
│ │ └──────────────────────────┘ │
│ ┌ 最终回答(总结卡)─────────┐ │ ┌ 记忆 ────────────────────┐ │
│ │ │ 钉在底部,默认折叠 │ │
└───────────────────────────────────┴──────────────────────────────┘
**为什么子智能体卡固定 5 行**:它顶在滚动区最上面、卡高与内容无关,所以紧贴
其下的「操作面板」永远停在同一位置 —— 子智能体多几个少几个都不会把操作面板
推走。没有子智能体时整卡不可见但位置照留(同样是为了不推动下面的卡)。
左栏干净:只有一行状态卡(头像 + 「Seeker 正在…」+ 末尾三点 + 用时读数)。工具明细不在这边堆。
顶栏标签行位置:
Seeker排在官方「对话 / 轨迹」左侧、紧贴右侧工具区,是插件用一组order规则把标签行插到标题与工具区之间实现的(零 DOM 改动,官方按tabs.length增删 标签行时不会崩)。DSH0.2.1-rc.3给标签行外面多包了一层.tabs容器,旧规则全部打在 内层 tablist 上导致标题被挤到最右、官方「…」跑到 Seeker 左边 —— 已按新结构补规则,两版 选择器靠:not([role='tablist'])区分,rc.2 / rc.3 都能正常显示。

大盘设置:「Seeker」标签右侧有一枚齿轮按钮,点开在视口中央弹出设置卡片,里面是 「提示词通道」六条(中文优先 / 对话内交互卡片 / 团队协作 / Agent 邮箱 / step 纪律 / 回合结束自动收口)、「展示」三条(总结卡外框 / 工具调用卡 / 操作面板), 以及下方通栏的「工作区记忆」。齿轮与卡片 联动(再点收起、Esc 关闭、点遮罩关闭),改完立刻生效、刷新后保留。
卡片约 1000×512,排版在 2026-10-25 按「拥挤、排版不合理」重做过一轮:
- 上方两栏:左「提示词通道」4 行(中文优先 / 对话内交互卡片 / 团队协作 / Agent 邮箱), 右「展示」3 行 +「任务清单」2 行(step 纪律 / 回合结束自动收口),中间一道发丝线。
- 任务清单组里的两行没有合并成一个开关:step 纪律往提示词里写一条行为约束(让模型
少往返),回合结束自动收口是 host 侧监听器(
agent/turn-stopping把残留的in_progress改写成pending),一个字都不进提示词。两者作用机理相反,绑成一个开关 会强迫用户做无意义的交易 —— 想关掉「末项别再转圈」就得连带关掉「少往返」。归到同一 组只负责「看起来归一类」。 - 下方通栏:「工作区记忆」独占一整条,带极淡底色与上方分隔线(不套卡片外框 ——
面板里再套一层圆角卡就是卡中卡,本仓库老坑)。它从右栏搬下来是因为三个性质都对不上:
它是列表(长度跟工作区数量走)、是唯一按工作区存的一组、行结构是「名称 + 路径 +
两枚迷你开关」横排 —— 挤在 392px 右栏里路径会被截成
D:\AI\E…,而路径正是「哪个工作区」 唯一无歧义的标识。 - 行内三列 grid:标签定宽 132px / 说明弹性 / 开关定宽 40px。原来是 flex,开关跟着文字 长度浮动,左右两栏停在两条不同的竖线上,扫「开没开」时每换一栏都要重新找位置。
- 说明常态一行:短句只讲「这一行与别行有什么不同」,共性提到组头右侧(「每会话首步 ·
存于 host」「本机浏览器 · 不进提示词」)。全文挂在
title上,鼠标悬停 / 键盘聚焦 / 触摸 点按任一方式都能展开 —— 是按需展开,不是删字。 - 窗口窄于 820px 回退单列;窄于 560px 时行降成两列(标签+开关一行、说明独占下一行)。
「工作区记忆」列出本机全部工作区,每个工作区两枚开关:记录(要不要往这个工作区攒新记忆 —— 关掉后不再自动提取、模型也不能手动记)与不注入(要不要把已有记忆读进它的会话)。 两件事语义相反、互不影响:关掉「记录」不影响已有记忆照常注入,打开「不注入」也不影响继续攒。 当前会话所在的工作区带「当前」角标与左侧主色竖条;列表限高可滚,工作区多了不会把下面顶出卡片。
右栏五张卡:
| 卡 | 讲什么 |
|---|---|
| 子智能体 | 整场会话派出去的独立会话,一行一个、整行可点跳到那个子会话。不按轮次切分(切到哪一轮都看得全);分两段、各带段标题——上面主色的「正在执行 N」(呼吸点 + 计数,无底色),下面带对勾的「已完成 N」折叠带(灰底、整条可点、带 chevron)。行首用形状图标分状态(进行中转圈 / 已结束对勾),状态同时留在 aria-label 与悬停说明里。固定 5 行窗口:卡高与内容无关,多出来的行在窗口内滚动(这是「操作面板位置恒定」的保证);没有子智能体时整卡不可见但占位保留,不会让下面的卡上移 |
| 操作面板 | 把工具调用翻成中文人话(「查看文件 KrAgentPanel.tsx」而不是 read_file(path=…))。卡头是任务动画(鲸鱼娘,9 段按状态切换的透明底 WebM,见任务动画)+ 「Seeker」+ 当前这一件事(新动作把它顶掉,最多两排,再长抹省略号),默认收起,点卡头展开完整时间线;卡头那行的耗时隐藏,明细行里的耗时保留。措辞不出现程序、终端、命令行这类词,也不印「在电脑上操作」这类复述图标的话 —— 终端族只留对象(模型写的 description),行首图标已经说明是哪类动作(workflow → 「执行自动流程」) |
| 任务 | 只列本轮 todo_write / 提交的计划,本轮没写就是空(发一条新消息不会挂着上一轮的清单)。只有回看历史轮次时才回溯到会话最近一次,并标注「会话清单」 |
| 产出物 | 本次对话做出来的文件,整行可点,按类型分流:官方右侧栏能正常展示的(图片 / 文档 / PDF / Office / 表格 / 页面 / 音视频)就地进右栏看;侧栏展示不了的(3D 模型、压缩包、avif/tif/heic)交给系统默认应用(Blender、解压软件、看图器) |
| 记忆 | 钉在底部;按需展开,只列本会话新增的条目(按写入来源判定,不按时间) |
思考过程卡贴在对话流里(不在右栏):左侧一条 2px 竖线,无底色无投影,跑时展开跟随、 收口自动折叠成一行让位给正式回答。与它配套的还有提问与回答卡,紧贴在下方, 同一时机一起收成一行——回复落定后不再占屏,点标题行随时能再展开重读。
与官方「设置 → 字号」联动:左栏两张 + 右栏四张卡的字号、行高、图标全部跟随官方字号设置。 字号 14(默认)时与你之前的观感逐像素一致,调大调小会一起变。
右栏被挤压时(窗口窄 / 记忆卡内容多),操作面板与记忆卡会自动缩一档。 「思考 / 提问 / 产出物」等卡片各有独立开关,见 配置一览。
卡头 Git 状态与一键提交
操作面板卡头上,人物右侧那排按钮里有一枚 Git 菱形徽章(在邮件与设置按钮旁边), 把「这个工作区现在有没有没提交的东西」直接摆在眼前:
| 看什么 | 说明 |
|---|---|
| 最近一次提交 | 时钟图标 + 相对时间(「3小时前」「2天前」),悬停看提交主题与短 hash |
| 待提交 | 分支/提交图标 + 待提交文件数;干净时换成对勾 |
| 点它 | 打开提交弹窗:AI 依据真实 diff 生成规范 commit message,确认后执行 add / commit / push |
数据来自 host 侧 GET /api/chat-flow/git-status:分支、最后提交、改动列表(按 M/A/D/R/?/U 分类)、
staged/unstaged 计数、ahead/behind,以及一个 needsMerge 判定——「这个工作区现在不适合
直接提交」的三种情形(有冲突 / 落后远端 / 正在合并)会被标出来,而不是让你在冲突中途提交。
提交走严格
execFile、参数定长、不经过 shell,天然免疫命令注入;所有 Git 异常与分支 判断都平稳兜底,路由绝不 500。跨站请求同样过rejectCrossSite闸门。
旁边还有一枚入口打开**「工作区与自制插件 Git 大盘」**(GET /api/chat-flow/git-roster):
把工作区本身和每个自制插件仓库各自的改动状况收在一张表里,一眼看出哪个仓库有东西没提交。
任务动画:卡头那 9 段
右栏卡头(以及空白新对话页的 Hero 卡头)那块 92×164 的立绘位,2026-10-26 起不再 是静态图片,而是一组按状态切换的透明底 WebM。用户给了 9 个本地 GIF,换成播放器 之后,一整轮对话里她都是「一直在动」的。
| 状态 | 片段 | 时长 | 什么时候播 |
|---|---|---|---|
| 刚发出消息、还没有任何动作 | thinking 在思考 | 19.8s 循环 | running 但一步都还没有 |
| 第一个真动作刚开始 | start-op 开始操作 | 10s,播一次 | 从「思考」或空闲进入「操作」的那一刻 |
| 有动作在进行(默认) | working 操作中 | 19.8s 循环 | 其余所有 running 时刻 |
| 正在看图片 / 文件预览 | photo 查看照片 | 10s 循环 | 进行中那一步的图标是 image / fileView / eye |
| 你发来图片(图或图 + 文字) | viewing 查看照片中 | 10s 原地重播 | 本轮用户输入里含 image 内容块,且还没有任何动作在跑 |
| 动作都做完了、在收尾 | end-op 操作结束 | 5s,播一次 | 本轮还在跑、但没有一步在跑,且连续 2.2 秒 |
| 本轮收口 | done 完成 | 10s,播一次 | 亲眼看到 running 从 true 翻 false |
| 空闲 11–13 点 | meal 吃饭中 | 19.8s 循环 | 本轮没在跑,且此刻是饭点 |
| 空闲 19–5 点 | moon 观月中 | 19.8s 循环 | 本轮没在跑,且在夜里 |
| 空闲 5–11 / 13–19 点 | nap 小憩中 | 19.8s 循环 | 本轮没在跑,白天 |
几条刻意的取舍:
- 允许打断(用户点名要的):任何时刻只有一段是激活的,新状态直接顶掉旧的,不等
前一段播完。回看历史轮次时,
running是 false,所以看到的是空闲那三段之一。 - 收尾的判据天生有歧义:「本轮还在跑、但没有任何一步在跑」在两次工具调用之间同样
成立(模型在想下一步)。所以要求它连续 2.2 秒才认,而且
end-op演完回「操作中」 而不是冻在最后一帧 —— 万一误报,后果是「她做一个收尾手势又继续干活」,不是卡住。 - 切片段不卸载
<video>:9 个一次性挂满、永不卸载,切状态只改 CSS 类名与 play / pause。面板每 250 毫秒重渲染一次,按需挂载会让浏览器反复中断请求, 表现是「每次都要闪一下才动起来」。 preload="none"+ 首帧海报:9 段合计 2.6 MB,全量预载等于每次打开大盘先拉 一遍;真播起来之前看到的是同片段的首帧 WebP,不会有一闪的黑块。- 系统「减少动态效果」时不播,只把画面切到该状态对应的静止片段(循环段的首帧)。 滚出视口、标签页切到后台同样暂停解码;新对话页与右栏两个卡头同时存在时各播各的。
- 「你发了图」单独立一档:
photo是「某个读图工具正在跑」,演完就回「操作中」;viewing是「你刚把图发过来、她正在看」,一条工具都还没跑(模型看完经常直接作答), 所以要停在看图这一幕反复重播,直到真的开始动工具。判据只看输入侧的 image 内容块(turn-input-images.ts),两处取数部位data.content与payload.content都认 —— 写死一处会静默失效、界面上只是「不演了」,没有任何报错。 - 深色主题给人物补光:这套素材是白底 GIF 抠的,皮肤本身是冷调中低明度(L ≈ 101),
白蕾丝与高光在 L ≈ 220 以上。白底上这是「白底上一段灰」,深底(L ≈ 24)上它就成了
整块画面里最暗的一层,看起来像脸发黑。
.kr-stage在body[data-ds-dark-theme]下统一提亮brightness(1.12) contrast(0.94)并在背后加一圈中性浅光(实测脸部 L 101 → 114)。提亮幅度必须留在 1.05–1.2:再高白蕾丝先过曝,人会「发光」而不是变清楚。
素材是那 9 个 320×640 的原 GIF,在 Node 里按边沿洪泛把近白底抠成透明,再编码成
VP9 yuva420p(crf 38)配一张首帧 WebP。走 host 路由
/api/chat-flow/motion/<片段 id>.webm|.webp(cache-control: immutable),不内联
进 client 产物 —— 内联会让每个用户冷启动多下 3.4 MB base64,关掉动画的人也照下。
正文里能出卡片
流程图卡片(历史兼容)
早期版本用 ```diagram 围栏画流程图,要模型手工给出每个节点的坐标与折线点——
画歪是常态,且这个能力有独立开关、默认关,模型多半不知道它存在,常年闲置。
现在流程图 / 架构图 / 时序图 / 任何示意图,一律用 ```html 围栏手写内联 SVG
(见下一节)。iu 曾有的 graph / arch / sequence 三个图形 kind 已整块下线:
它们只能画算法排版的拓扑图,视觉上限被组件锁死,而图形恰恰最吃设计感。
能力文档随「html 卡片」通道默认注入,直接说「画个架构图」即可。
```diagram围栏的渲染器仍然保留:历史消息里的老围栏照常渲染成 SVG 卡片 (节点 / 连线 / 分支标注 / 三档比例),不会变成一坨 JSON。只是不再往提示词里注入 它的规范,模型也不会再主动产出——那个独立开关早已移除。
HTML 沙箱卡片(能跑真 HTML/JS)
```html
<h1>计数器</h1>
<button id="b">+1</button><p id="v">0</p>
<script>
let n = 0
document.getElementById('b').onclick = () => { document.getElementById('v').textContent = ++n }
</script>
```
对话流里出现的就是这张卡本身,没有任何常驻控件(2026-10-19 起把标题栏、源码/复制、 缩放、重载、全屏整组砍掉):卡片是正文的一部分,不是嵌进来的组件。 里面是真在跑的 HTML/JS —— 小工具、计算器、图表、可视化、单页原型都能直接交付。
内置设计系统(kit):正文里出现 k- 类名或 --k- 变量时自动注入,不写 CSS 也能
得到与宿主同一套视觉语言 —— .k-title / .k-lede / .k-h 排版,.k-num 等宽数字,
.k-rule 发丝分隔线,.k-kv 键值两栏,.k-grid / .k-cols 自适应多列,.k-bar /
.k-track 条状图元,.k-swatch + .k-a1~.k-a4 色标。尺寸全部跟随官方「设置 → 字号」
缩放(内部是 --k-scale),不写死 px。
内置交互(按需注入,不用自己写 JS):data-tooltip="…" 提示气泡(hover / 键盘聚焦 /
触摸点按,Escape 关闭);.k-tabs + role="tablist" 页签(点击与方向键已接管);
__dshState.set({…}) / __dshState.ready(fn) 把交互状态持久化,刷新不丢;
window.__dshFill(text) 把结果回填输入框,由你确认后发送。
安全:卡片跑在独立 iframe 里,只给 allow-scripts、不给 allow-same-origin ——
卡片里的脚本读不到你的会话数据与页面 DOM。高度自适应上报;点卡片里的链接会开新标签页。
流式期先出占位卡:模型逐字吐 HTML 时先显示「正在预渲染…」,围栏闭合后原地变成真卡片。
空内容、只有注释、语言标记不是精确的 html,都会回退成代码块(你能看到原文,
而不是一张空白框)。
这个能力默认开启 —— 它是交付形态本身,不注入模型就永远想不起来用。
iu 原生交互卡片
输入框以 /iu 开头,这条消息会连 /iu 一起原样发给模型,模型据此出卡。十二种:
| kind | 用途 | 样子 |
|---|---|---|
slider | 取值换算(按人数算用量、按面积算预算…) | 拖动滑块,右侧实时出各项结果 |
table | 数据行列 | 可排序表格:点表头切换升 / 降序,数字列右对齐 |
checklist | 待办清单 | 可勾选,带进度条 |
tabs | 方案对比 | 页签切换 |
kanban | 任务分列 | 拖拽看板:卡片可跨列拖动,也有点选 →「移到这里」的触摸 / 键盘通路 |
form | 收集输入 | 文本 / 数字 / 下拉 / 勾选 / 多行五类字段,必填项失焦才提示,填完一键回填输入框 |
timeline | 时间安排 | 竖向 / 横向时间线,节点三态(完成 / 进行中 / 待办),点击展开详情 |
diff | 前后修改 | 行级对比(自算 LCS),统一 / 并排两种视图,可只看变更行 |
tree | 层级结构 | 可折叠树,点箭头展开 / 收起子树 |
gauge | 完成度 / 指标 | 半圆仪表或圆环进度,可带阈值变色(绿 / 黄 / 红),最多 4 个并排 |
quiz | 考察理解 | 单选 / 多选自测卡,答题后揭示对错与解析,答完出得分 |
piano | 钢琴 | 点键或用键盘 A W S E D F T G Y H U J K 演奏,可把弹过的音写成音名+简谱回填输入框 |
原生卡(不走 iframe)体积小、不白屏,还能把结果一键回填输入框由你确认后发送。 卡片状态会记住:勾过的清单、拖过的滑块、移过的看板卡片、切过的页签、填了一半的表单, 都存本地(localStorage),刷新页面不丢。
图形不再走 iu:
graph/arch/sequence三个图形 kind 已整块下线,图表 kindchart也已下线 —— iu 只能画算法排版的拓扑图,而图形最吃设计感。现在任何示意图都写```html围栏手写内联 SVG(见上一节),文档随「html 卡片」通道默认注入。 历史消息里的```graph/```diagram围栏仍照常渲染(渲染器保留,只是模型不再产出)。
每张 iu 卡右上角只有全屏(Esc 退出,全屏态不再持久化——否则刷新后整页滚不动)。 缩放档位按钮已按用户要求移除:卡片字号统一跟随官方「设置 → 字号」,不再自成一档。
细节记不住没关系 —— 直接说「用 /iu 给我算一下」「用 /iu 做个对比」即可。
@ 不会把普通中文变成文件芯片
官方 @ 文件引用的判据只要求「@ 前面是空白、后面跟一串非空白字符」,不检查那串
东西像不像路径。中文里不打空格,于是「@的时候不是实时获取的」这种写法会被认成一个
叫「的时候不是实时获取的」的文件,文件当然不存在 —— 那段文字就渲染成一枚蓝色虚线芯片。
这里用一条 order 更小的 user-text 规则抢在官方前面,只把不像路径的 @ 盖回普通文字。
判据:带引号 @"..." / 含 / 或 \ / 含扩展名 / 纯 ASCII 且至少一个字母 —— 满足任一才算
路径。所以 @的时候、@帮我看看、@3.5 都是纯文字,而 @src/main.ts、@README.md、
菜单里选的 @package.json 照旧是芯片。
可交互卡片(proto-tabs)
```tabs 围栏 → 可点的页签卡片,适合并列展示几套方案。
运行中的程序:打 @ 直接挑窗口
输入框打 @,候选里除了官方的文件 / 会话,最上面多一组 「运行中的程序」 —— 你此刻
真的开着的窗口,每条带真实进程图标 + 窗口标题,右侧一行灰字是程序名(浏览器窗口写
Chrome / Edge)。前台窗口排第一,最小化的排最后。
选中后草稿里落一枚 @ 窗口标题 的芯片,模型收到的是结构化上下文:
[运行中的程序] 程序:chrome;窗口标题:Example Domain - Google Chrome;
路径:C:\Program Files (x86)\Google\Chrome\Application\chrome.exe;状态:当前前台窗口
于是「帮我把这个页面整理成笔记」「这个报错窗口是什么问题」这类话不用再说程序名 —— 指一下就够。
怎么采集的:host 半身用一个常驻 PowerShell 子进程枚举可见顶层窗口(标题 / 进程 /
图标 / 前台 / 最小化),暴露只读路由 GET /api/chat-flow/running-apps。首次冷启动约
600~700ms,之后每次往返 2~4ms(客户端还有 30s 缓存 + 后台刷新)。
过滤掉什么:托盘浮层、输入法候选窗、Progman / Shell_TrayWnd 这类桌面外壳、
Chrome 的「翻译此页?」气泡、TextInputHost 等系统提示宿主 —— 都有实测依据,见
src/running-apps/collect.ts 的注释。
失败面:采集器不可用时整组静默消失,官方的文件 / 会话候选照常工作,界面上不报错。
客户端只保留两层不算缓存的机制:并发去重(同一时刻一个请求)与失败兜底(用上一次成功的结果)。
宿主被强杀(任务管理器结束进程 / 崩溃)时,采集器由 C# 看门狗线程 TerminateProcess
自我终止,不会在机器上留下孤儿 pwsh。
对话读起来更顺
| 能力 | 说明 |
|---|---|
| 思考行 / 工具行 | 与官方同款的折叠行,实时走秒 + 分步跟随滚动 |
| 步骤卡 / 总结卡 | 回合中间是步骤卡,最终回复独立成总结卡。总结卡外观可在工作台记忆页里改成纯正文(去框去影) |
| 共享活动抽屉 | 点思考 / 工具行打开,思考按语义分组、工具调用成树 |
| 裸路径自动变链接 | 正文里出现的文件路径自动变成可点链接,点了在右栏预览;整段只有一个图片路径时直接渲染成图 |
| 对话滚动守卫 | 你手动往上翻时不被新内容拽回底部 |
| 重试行影子 | 模型重试时显示官方同款试次行 |
| 生图画廊条 | 生图结果收成一条画廊,不用一屏一屏刷 |
动效(移植自 aa2246740/dsh-better-display,MIT):思考两行步进跟随、流式新文字淡入、
忙碌标签微光、展开收起过渡。全部只动 opacity / transform,并在系统开启
「减少动态效果」时自动关闭。
侧边栏:工作区 ⇄ 操作间
侧边栏「更多」下面只有一行切换菜单,两屏互斥:
| 当前显示 | 那一行写 | 点它之后 |
|---|---|---|
| 工作区(会话 / 工作区列表) | 操作间 | 工作区收起,下面列出记忆 / SKILL / 用量 / 画廊 / 档案 / 系统提示词 / 同步设置 |
| 操作间(七个工作台入口) | 工作区 | 那七个条目收起,会话列表回来 |
也就是「这一行永远写着点下去会去哪」。当前停在哪一屏会记住(刷新不跳屏), 从任何一个工作台页回到会话时自动切回工作区——否则在操作间里点「新会话」 会看不到会话列表。
侧栏折叠成 56px 图标态时,这条二级导航整块让位,官方「工作台」图标行接管 (那里放不下一整屏二级导航),入口在任何形态下都不会丢。
二级菜单:手风琴
记忆(工作区)、画廊(八个类别)、档案(场景)这几行展开后列出的子项,是手风琴:
- 点开「画廊」,之前展开的「记忆」在同一拍收起,同一时刻只有一组子项展开;
- 展开态记在 localStorage 的
dsh-sidebar-expanded-menu,刷新后仍是上次那一行; - 实现是「展开 = 覆盖成单值」(
writeExpandedMenus),渲染层只判断自己那行, 所以互斥是数据结构自带的,不需要每次点开头再遍历兄弟行去收一遍。
旧版本允许多行同时展开,存量数据里可能还留着
memory,gallery这种多值。readExpandedMenus只认第一个,任何一次展开/收起都会把它写回单值——升级后 不会出现「一进来所有行全撑开」。
加一个新菜单(给二次开发)
侧栏二级导航是注册表驱动的:src/client/triad/hub/nav-registry.ts 里两级数据
——区域(section)与条目(entry),侧栏只按表渲染。加一个菜单不需要碰挂载器、
样式或显隐逻辑:
// 打开某个官方 main 面板(例:加一个「任务」菜单)
ctx.effect(() => navMenuItem({
id: 'tasks', label: '任务', icon: MyTasksIcon, panelId: 'schedule',
}), 'dsh-chat-plus: 任务菜单')
// 打开工作台的某个分类
ctx.effect(() => navMenuItem({
id: 'gallery-entry', label: '画廊', icon: MyGalleryIcon, workbenchTab: 'gallery',
}), 'dsh-chat-plus: 画廊菜单')
// 自定义动作(弹浮层、发命令……)
ctx.effect(() => navMenuItem({
id: 'custom', label: '随便什么', icon: MyIcon, run: () => { /* ... */ },
}), 'dsh-chat-plus: 自定义菜单')
// 再加一整屏(第三个区域,按 order 轮转)
ctx.effect(() => navSection({
id: 'tools', label: '工具间', icon: MyIcon, order: 30,
}), 'dsh-chat-plus: 工具间区域')
条目缺省落在操作间(section 不写就是它),order 决定行序(工作台七个
条目占 10–70)。注册返回的 dispose 交给 ctx.effect 回收,热重载不残留;
同 id 重复注册 = 覆盖,不会叠出两份。
七个工作台
侧边栏工作台,每页独立(邮箱不在其中,它的入口在 Seeker 标签右侧,见 邮箱):
记忆
自动沉淀的长期记忆:每轮对话后由模型提炼值得跨会话保留的事实(你的偏好、项目约定、踩过的坑), 按项目 / 全局分层。可在面板里增删改、置顶、按标签检索。在左栏选中某个项目后,列表头下方 出现一条项目上下文条,可改别名、可清空该项目的全部记忆(置顶记忆会保留)。
两个按工作区的开关(记录要不要往这个项目里攒新记忆、不注入这个项目的记忆要不要 进上下文)不在这个面板里 —— 它们在 Seeker 的 大盘设置 卡片里,按工作区逐个列出、一次能拨完好几个。一处入口、一份真相。

SKILL
管理技能(Skill):安装 / 停用 / 查看说明 / 归入技能包 / 设置分类。技能包与状态筛选在同一行, 分类在左、启用状态在右。

观测 / 用量
整页是一块观测大盘:顶部范围胶囊(今日 / 昨日 / 近 7 天 / 近 30 天 / 本月 / 上月 / 今年 / 全部 / 自定义)+ 自动刷新间隔;第二行供应商 / 模型下拉、请求状态分段(全部 / 失败 / 重试后成功)、关键字搜索与 CSV 导出。
四张 KPI 卡:Token(输入 / 输出 / 缓存拆分)、请求(失败数、成功率、重试后成功)、 缓存命中率(带进度条)、输出速度(token/秒 + 首字平均)。中段按日堆叠柱状图, 四档口径 Token / 请求 / 速度可切,悬浮出卡片式 tooltip、点某天下钻;右栏是「拆分维度」 列表(模型 / 供应商两个维度,同色系与柱图对齐)。底部 52 周活动热力图。
版面铺满可用宽度:KPI 用 auto-fit 栅格(宽屏均分、窄屏折行),右栏宽度 clamp 到 22%, 热力图格子按容器实测宽度在 12–28px 之间自适应,断点用容器查询而不是视口查询——工作台 既可能全屏也可能嵌在窄栏里。

画廊
所有对话生成的图片 / 网页 / 演示 / 文档 / 表格 / 音视频一页看全:跨会话增量索引、 类别筛选、搜索、跳回来源会话。
点任意一张卡片 = 用系统默认应用打开这个文件(2026-10-20)—— 图进看图器、视频进
播放器、PPT 进 PowerPoint,与你双击资源管理器里的图标完全一致。走官方 open-in-app
那条 shell-open 链路(remote.session.openWorkspacePath),不自建路由。
产出物卡的口径与画廊不同(2026-10-22):侧栏能正常展示的类型走侧栏(图片 / 文档 / PDF / Office / 表格 / 页面 / 音视频),展示不了的才交给系统默认应用。 判据逐扩展名核验过(含
ods的接管、avif/tif/heic这类"看着像图但官方不认"的 坑),见src/client/sidebar-display.ts。
本机没有桌面时(手机远程访问 / 纯服务端部署)自动退回内置预览:可内联的进就地 弹窗(图片缩放、视频播放、html 沙箱 iframe、PDF / Office 页图翻页),不可内联的走 官方右栏,行为与升级前一致。那个弹窗右上角也留着一枚「用默认应用打开」,本地看到 想拿去用的那一刻就能直接送出去。
生图与生视频的结果原本只存在于 spill 的文本里,打开前会先由 host 落成真正的
png / mp4 —— 直接把 .txt 交给系统只会弹记事本。
每个类别标签上写着该类的真实条目数(图片几百张就显示几百,不做 99+ 之类的截断)。
时间轴视图(默认视图)的方格在 2026-10-24 整体放大约 21%:最小列宽 150 → 182px、 gutter 10 → 14px,缩略图与占位图标同步加法,格子仍是 1:1 正方形。四种格式 (图片 / 网页 / 视频 / 音频)共用同一套方格尺寸,换类别不会跳变。
同一次改动里去掉了方格右上角的类型徽标(分类已经在工具条上选定,每格再挂一枚纯属 噪声);2026-10-25 又把文件名与时间从常驻改成 hover 浮层:默认格子是干净的正方形 缩略图,鼠标移上来(或键盘聚焦)时以自下而上的渐变浮层淡入在缩略图内部的下缘, 压图显示。一屏因此多放一整行(实测 6 行 → 缩略图 187×187、日期分组间距压到 2px)。 网格视图的文件名仍是常驻,未受影响。
删除有两条路,互不干扰:
- 按类别整批删:每个类别标签带一枚删除键 —— 点它,标签原地变成「删除 N 项?不可恢复 确认 / 取消」,确认后把这个类别下的条目连同磁盘文件一起删掉(不可恢复,所以有 这一步二次确认;按 Esc 或点别处即取消,几秒不操作也会自动收回)。
- 单条删(2026-10-24):鼠标移到任意卡片上,右上角浮现一枚垃圾桶 ——
第一次点变成红底「删除?」,再点一次才真删。键盘用户聚焦卡片后按
Delete/Backspace走同一套两拍语义。确认态没有自动回退(视线就在这张卡上,自动收回 反而像静默失败),取消出口是点别处 / Esc。
两条路都先播淡出退场再发请求;若有个别文件删不掉(被占用、已不存在),它们会留在 画廊里并给出原因。删除只作用于画廊索引里的普通文件,符号链接与目录一律拒删,也不会 连带删除目录内容。

图库缩略图是真实首页位图而不是类型图标。PDF / Office 由插件的侧栏渲染器接管 (官方内嵌 pdf.js 对带 /ID 的 PDF 会崩),音视频由插件的媒体渲染器接管 —— 官方把 mp4 / mp3 这类后缀列为「没有渲染器」,不接管的话侧栏只会说一句「不支持预览」。
档案
「不管工作区关不关记忆,它都会自动记录并自动注入」——这是档案与记忆最根本的区别。
记忆库的两道闸门(按工作区「记录 / 不注入」)对档案完全不生效:档案有自己的
autoRecord / autoInject 开关,默认全开,且注入以 prepend 优先级注册,
跑在记忆库那两道闸门之前。
一页管完六类结构化信息(对齐 Chrome 密码管理器那种分组):账号密码 / 邮箱与登录 / 支付方式 / 联系方式 / 证件 / 其它,分组可改名、可加自定义分组。每条记录是一个实体 (一个站点账号、一张卡、一份证件),字段级就地更新——「密码从 A 变成 B」是一次字段 patch 加一行历史,不是一个追加句子。证件这类场景可以挂图片(正面 / 反面 / 补充), 浏览器侧压成三档再传上来。
四条不变量(改代码时不要破坏)
| # | 不变量 | 为什么 |
|---|---|---|
| 1 | 敏感值落盘必加密(密文带 secret:v1: 前缀,AES-GCM,密钥是 key 文件里的 32 字节随机数、权限 0600) | 任何备份 / 同步 / 误打包都等于明文泄露 |
| 2 | 敏感值绝不进上下文:注入里只有「密码:已加密」,连长度都不给 | 注入内容会发给模型厂商;长度本身能缩小暴力空间 |
| 3 | 列表响应里敏感值是掩码(八个星号),明文只经 POST /reveal 单字段取一次并写审计 | 否则「打开档案页」就等于「整库明文进浏览器内存」 |
| 4 | 提交回来的掩码不会被当成新值 | 一次保存就把真密码写成六个星号,且不可逆 |
模型那侧只有 3 个工具(vault_search / vault_get / vault_remember),
查得到「有没有这个账号」,拿不到密码。要明文必须用户自己在页面上点眼睛。
谁来写:主 agent 当场写,后台抽取兜底
2026-10-26 起写入以主 agent 自己调 vault_remember 为主:用户这一轮说出新的账号 /
密码 / 证件信息,模型当场写进去,当轮即时生效(用户下一句就能用)。turn/end 的
后台抽取退为兜底——模型漏调时仍能捡到,不会丢数据。两条路写同一份 store,靠
「场景 + 归一化标题」去重、patchField 的「值没变就不写」保证重复写幂等,不需要额外互斥。
这么改的原因很实际:抽取是回合结束之后跑的,写入永远滞后一轮。原来的注入规则 甚至明说「正常对话即可,回合结束会自动抽取入库」,等于劝住模型别自己写——于是用户 刚给完密码紧接着说「那你去登一下」,模型手上还是旧值。
自动抽取的结果也直接生效,没有「待确认」环节。 写进去是什么样,模型下一轮就读到
什么样:记录上没有「待确认」状态、页面没有确认 / 驳回按钮、也没有 POST /confirm 路由。
这里原本有一套「抽取结果挂 pending、等用户点确认/驳回才算数」的机制,2026-10-27 用户 明确要求档案不需要确认,整套退役(
VAULT_SCHEMA_VERSION1→2)。理由是那道闸门 同样把「当场写、当场用」拖住了:模型明明在会话里听见「密码改了」,档案里却还是旧值、 页面还堆着一栏待确认。现在护栏只剩三条——update 只改模型明确点名的字段、create 先按 「场景 + 归一化标题」查重、patchField值没变就不写——改错了也只错在那一个字段上, 下一轮抽取还能改回来。老库里已经挂着的 pending 标记由migrate的 v1→v2 一级 主动删掉(不是「从此不再写」),否则会留下一条永远挂着、页面上却没有入口能处置的 假状态。
⚠ 这条路上绕不开的一件事:模型自写意味着明文要经过模型厂商一次。官方的
tool/call事件保存的是模型产出的原始argumentsJSON 串(dsh-session类型里原话: the raw arguments JSON string exactly as the model produced it),而ToolCallBlock属于ContentBlockMap,会随 assistant message 在后续每一轮重发给 provider。所以明文 与其他对话内容同级别地走了厂商、并留在会话历史里。后台抽取同样要发给模型才能抽出 字段(只是不落在主会话历史里)。两条路都做不到「模型不看见明文」——真正的零暴露 只有用户自己在页面手录。代码里因此把「参数里传一次是必须的,但不要在回复正文里 复述」写成硬纪律,把凭空多出来的那一份副本消掉。
存储与占用
<DSH_HOME>/memories/dsh-chat-vault/
vault.json 记录 + 分组(几 KB~几百 KB,原子写 tmp+rename)
key 32 字节 AES-256 密钥(0600)
blobs/<2位>/<sha256> 图片字节,内容寻址:同样的字节只存一份
blobs/index.json blob 索引 + 引用计数
revisions/<日期>.json 每日快照(「回到昨天」)
audit.jsonl 敏感字段明文查看审计
占用的三档压缩实测(同一张 2.1MB 原图,JPEG q75):
| 档位 | 长边 | 体积 | 占比 | 用在哪 |
|---|---|---|---|
| thumb | 512 | 36KB | 1.7% | 列表 / 网格 |
| display | 1600 | 270KB | 12% | 详情 / 灯箱 |
| original | 原样 | 2100KB | 100% | 默认不存(keepOriginal) |
配额默认 512MB,超出时按 original → display 顺序淘汰,缩略图和记录文本永不删
(照片全没了,账号密码也还在——这是「有损但不会丢命」的方向)。
到期提醒:它会提前告诉你哪几样东西要失效
页面右上角有一枚**「提醒」按钮**(显示有几项要到期),点开是一个独立的窗口, 按紧急度分三档列出 90 天内到期、以及已经过期的记录:
| 档位 | 判据 | 例子 |
|---|---|---|
| 已经过期 | date 字段早于今天(永远报,没有阈值) | 过期的身份证、旧银行卡 |
| 30 天内到期 | 剩余天数 ≤ 30 | 下个月要续的证书 |
| 90 天内到期 | 剩余天数 ≤ 90 | 半年后的护照 |
数据来自 GET /api/dsh-vault/reminders。三条设计约束:
- 每条记录只看最近的那一个日期,不把「签发日期 / 到期日期」都列出来——用户要 动的手只有一处(去换掉那张证),同一件事报两遍只是噪音;
- 只读日期字段,不读任何明文。这条路由用的是掩码版
listRecords,整个窗口 连一次解密都不做; - 与每轮对话注入行尾那句「有效期还剩 89 天」是同一个函数(
reminders.ts的expiryNoteOf/nearestExpiryOf)——两处各算一遍,迟早会出现「窗口里没有、但 模型被提示了」。
早先这里还有一整套「密码健康审计」:0–100 的分数、弱口令 / 复用 / 明文落盘 / 同名 重复四类问题,以及一个「一键批量换密」(
GET /health+POST /rotate)。 用户 2026-10-27 明确要求删掉:那一整套里只有日期会自己变坏,其余都是用户自己 造出来的状态,报出来只是替他数落一遍,而每一条都得他去各个站点手动改密。 路由、引擎、页面入口与对应测试一并删除,别照着旧文档加回来。
实战中的几个小工具
- TOTP 动态验证码:存了「二次验证」种子后,详情页点一下就能出 6 位码 + 倒计时环,
到期自动换。种子支持裸 base32、
otpauth://链接、带空格 / 短横线的分组写法三种形态。 实现按 RFC 6238,测试里直接对官方测试向量(T=59s 出94287082)做了断言。 - 强口令生成:服务端
crypto.randomInt(不是Math.random),保证含大小写与数字、 去掉l/O/0/1这类易混字符,附带熵值显示。 - 加密备份:用你自己的口令把密钥 + 全部记录打包成
.dshvault文件 (口令经 scrypt 派生,AES-GCM 加密后再 gzip)。换电脑 / 重装系统 / 改了DSH_HOME时, 只要还记得那个口令就能整库救回——这是唯一的换机通道,因为密钥只存在本机。 恢复的顺序是「先解包校验 → 再换密钥 → 最后覆盖数据」:口令打错一次就覆盖掉现有 密钥,会让原本能用的数据当场全部解不开。 - 云端自动备份(每 5 小时):页面右上角「云端」按钮里填你自己的 GitHub 私有仓库地址
(形如
https://github.com/你/仓库名)、一个同步口令和一枚有contents: write权限的 访问令牌。之后每 5 小时(可改 1–168)自动把加密包推到那个仓库的dsh-vault-backup.dshvault,数据没变时不产生新提交(按内容哈希短路,不打扰你的提交历史)。 三条硬约束:① 只认私有仓库——先探GET /repos/{owner}/{repo}确认private: true才上传, 公开仓库直接拒绝(备份包里含本机密钥,一旦公开等于把整库交给全世界的离线暴力); ② 令牌与口令在磁盘上都是加密存储,接口只回「来源 + 末四位」,任何日志与响应里都不出现明文; ③ 令牌可留空,此时回退到本机gh auth token,适合已经把gh登录好的机器。 别人装了这个插件,页面会常驻一条提示条让他去填自己的仓库地址——不会、也不该指向 作者或其他人的仓库:那个仓库他没有写权限,地址填错还会把档案推进别人的私库。 恢复走官方备份通道:从仓库把.dshvault下载下来,用同一个同步口令导入即可。 - 「到底传全了没」看哪:右上角「云端」按钮上挂一枚状态点——绿=已全部上传,黄=有改动尚未上传
(没配置过时是那枚
!)。点开弹窗还有一条状态条,连同条数一起说清: 「已全部上传 · 3 条记录 · 内容与云端一致」/「有改动尚未上传 · 当前 4 条、云端 3 条」。 判据是内容指纹(明文数据的哈希)是否等于上次成功上传时记下的那个,跟"这次跑没跑过"无关: 刚改完档案还没到下次定时,也会如实变黄,而不是沿用上一次的"已上传"。 从未成功传过时不给假绿点,只说「尚未上传」;换了仓库会清掉旧指纹,同样回到待上传态。
隐私边界
所有 /api/dsh-vault/* 路由自带两道闸门:只接受 loopback 来源(远程访问由 DSH
自己的鉴权挡住),并且拒绝跨站请求(浏览器跨站时 IP 与 Host 都满足 loopback,
只看 IP 是不够的)。图片直链只服务索引里登记过的 sha256,杜绝路径穿越。
档案不替代密码管理器:它没有浏览器插件、不会自动填表、不会在钓鱼站上识别 「这个域名和记录里的不一样」。它解决的是「AI 需要知道你有没有这个账号」以及 「换机之后还记得住」。
系统提示词
看这一次发送会话时,模型真正收到的那段系统提示词。这个面板只读,但它是唯一能看到
「组装完之后长什么样」的地方——提示词不是浏览器侧拼出来的,而是 host 在每个 step 之前
把「全局段 + 作用域段 + 运行时上下文 + 工具清单 + 变量」组装出来、再经
system-prompt/assemble 这条 waterfall 逐层改写(本插件的工具闸门就在这条链上删工具、
删提示词段、追加「哪些能力关着」的说明),最后渲染成一段纯文本交给 provider。
所以快照是在 waterfall 的出口抓的:抓到的是最终值,任何插件改写之后的形态。
会话事件里回放的 system/message 做不到这一点——那是「过去某一步的渲染结果」,不带分段,
也看不到「关掉工具组之后剩什么」。
| 看什么 | 说明 |
|---|---|
| 概览 | 字符数、估算 token、工具个数、工具 schema 体积 |
| 分段 | 按段名归组(身份 → 工具 → … → 其它),每段可单独展开,看它贡献了多少字 |
| 变量 | 哪些变量真的被替换、哪些没解析成功(未注册的原样保留,不吞也不编) |
| 会话切换 | 下拉选会话 / 点刷新现场重算(?fresh=1 跳过缓存) |
两条设计取舍:
- 不落盘。工作目录、模型、各开关状态、时间都在里面,每次组装都会变;落盘等于存一堆 只有几分钟价值的副本,还要处理清理、迁移与(更麻烦的)加密边界。所以只留最近几个会话 各一份,进程重启即清空——页面会如实说明这件事,而不是给一个「空的但不知道为什么空」的页面。
- 绝不抛穿。出口 hook 抛错会毁掉用户真实的那一轮请求,路由抛错会变成页面上的红屏。
所以记录逻辑整段 try/catch,路由一律 200 +
ok字段,失败只是「这一次没抓到快照」。 新会话一个字都没发时,路由会现场跑一次 assemble(同一套链、同一个出口),结果与真要发出去的 一致;只有「活会话」才有 agent,历史会话直接如实回「不在本进程内」,不编一份看起来像真的 提示词。
数据源:GET /api/chat-flow/system-prompt(?session= 指定会话,缺省 = 最近一次组装;
?fresh=1 跳过缓存现场重算)。
同步设置
把工作台资产(档案 / 记忆 / 技能)加密后推到你自己的 GitHub 私有仓库,用于换机、 多机漫游。跟档案页那个「云端」按钮是一套思路,覆盖面从「档案」扩到「工作台全部资产」。
设置三项:仓库地址(owner/repo)、同步口令(≥8 位)、访问令牌(可留空走 gh auth token)
同步范围:档案 / 记忆 / 技能,三档独立开关
主次优先级:本地为主(主力机) / 智能双向(默认) / 云端为主(从属机)
自动节奏:自动上传 + 自动同步,间隔 1 / 2 / 5 / 12 / 24 小时(默认 5 小时)
几条硬约束,与档案云备份同一套:
- 只认私有仓库:先探
GET /repos/{owner}/{repo}确认private: true,公开仓库直接拒绝 ——包里含本机密钥,一旦公开等于把整库交给全世界的离线暴力; - 令牌与口令加密落盘,接口只回「来源 + 末四位」,任何日志与响应里都不出现明文;
- 端侧加密后才出门:上传前在本地打包加解密,推上去的是一个
.dshsync加密包, 不是可读的 JSON; - 内容没变就不产生新提交(按内容哈希短路),不打扰你的提交历史;
- 同步/上传结束后,侧栏「操作间」那行文字后面会挂一枚小字(「已同步」/「已上传」), 停留 1 分钟后淡出;刷新页面在有效期内仍会继续显示。
同步完成后记忆引擎会热重载(MemoryStore.reload()),不需要重启 DSH 才看到还原的条目。
Agent 邮箱(只读卡片)
腾讯 Agent Mail 的专属邮箱(与你的个人邮箱完全隔离)。入口在 Seeker 标签右侧的邮件按钮, 紧跟「大盘设置」齿轮 —— 点开在视口中央弹出只读卡片(约 1080×760,四周留边、点外面或按 Esc 收起):左列文件夹(收件箱 / 已发送 / 回收站 / 垃圾邮件 + 只看未读 / 只看附件)、 中间的邮件列表、选中后的正文与附件。窗口压窄到卡片不足 620px 时,自动切成 「列表 ↔ 正文」单栏,点邮件看内容、点返回回到列表。
只看,不发:面板里没有写信、回复、转发、移入回收站、永久删除的入口。
模型那侧同样只剩 6 个只读工具(mail_account / mail_list / mail_search /
mail_read / mail_download_attachment / mail_wait_code)—— 它能自己查收件箱、
读信、找邮件、下附件、等验证码,但发不了信也删不了信。写类那 5 个工具已下架。
邮箱地址 / 授权状态 / 额度、开关(启用邮箱工作台、对话里自动声明邮箱能力)、 附件保存位置、CLI 与数据目录这些设置仍在面板左下角的「设置」里。
「新邮件实时监听」已于 2026-10 整块移除:它靠一个常驻的
agently-cli message +watch长轮询进程,产出只有「面板开着时弹一下」,面板关掉后什么都没有;而且事件缓冲从不清空, 每次打开面板都会把历史事件当新邮件误报一遍。未读数本来就由列表自带,代价大于收益。
输入区与工具栏
| 位置 | 能力 |
|---|---|
| 输入框上方 | 供应商标签 / 模型选择器 / 推理等级 / 优化提示词 四个座位 |
| 输入框左侧 | 工具闸门(记忆注入开关 2026-10-23 已删,见设置里能改的) |
| Seeker 标签右侧 | 大盘设置齿轮 + 邮箱按钮(都是视口居中弹出的卡片),见 Seeker 视图 |
| 消息操作行 | 对话截图(无头浏览器渲染 markdown / 代码高亮 / mermaid,可长图)、下载 |
优化提示词:点输入框右侧的 ✨ 图标,用当前选中的模型把草稿改写成更清晰的提示词。
| 项 | 行为 |
|---|---|
| 三档风格 | 均衡(补齐目标/约束/输出格式,长度大致不变)/ 精简(压到最短,要求一个不丢)/ 详尽(补上下文、输入输出格式与验收标准);换档立即重写,自动中止上一次 |
| 结果先预览 | 原文与优化结果并列展示,点「应用到输入框」才写回草稿——不会偷偷改你的输入 |
| 关掉不丢 | 按 Esc 或点外面关面板,结果按会话留一份;重开时草稿没变就直接恢复,不重复烧 token |
| 设为长任务目标 | 可选包成 /goal <正文>,交给官方 /goal 命令创建目标。保留原有换行与代码块(不再压平成一行) |
| 输出预算 | 按「原文长度 × 2 + 16K 思考余量」给 maxTokens,用模型声明的上限封顶——推理模型的长思考不会把正文挤成截断 |
| 提示语言 | 指令全部中文撰写;输出语言跟随原文(原中文出中文、原英文出英文) |
结果由共享清洗器(prompt-optimize-clean.ts,host / client 同一份)收敛成可直接发送的正文:去掉围栏、去「优化后的提示词」小标题、去结尾「主要改动」段落。两条安全线:正文里的代码块不会被当成答案(草稿含代码时不会只剩几行 SQL)、用户自己写的「备注:」约束不会被当成改动说明删掉。输出顶到长度上限时会明确提示,不会把半截结果当成功。
download 工具:模型可以把文件下给你,带实时进度条 / 速度 / ETA。
对话截图:把这条回复渲染成图片,代码块带语法高亮、mermaid 出真图。
面板里的选项:范围(本条回复 / 这一轮问答 / 整段会话)、宽度(540 / 720 / 960 / 1200 / 1440 / 1920,或填 360~2560 的自定义值)、画质(1080P / 2K / 4K)、主题(浅色 / 阅读版 / 深色 / 玻璃 / 玻璃深色)、标题与徽章。 六项全部记住:改完下次打开面板还是这套设置,刷新页面不丢。标题按消息记(换一条消息回落该条对话标题),主题在你没手动选过之前跟随界面深浅色。
输出像素宽 =(宽度 + 画布留白 ×2)× 画质倍率,画质倍率按 1080P/2K 为 2、4K 为 3(宽度 ≤640 的窄档更高)。 实测:1920 档在 1080P / 2K 下出 4000 px 宽,4K 下出 6000 px 宽。 超长内容会被像素预算截断,这是既有的内存保护:宽度越大越早触顶,长会话建议配 2K 档。
供应商中心(设置 → 供应商)
官方设置弹窗里多一个「供应商」页(该页自动加宽到 min(1680px, 100vw-48px),
其余设置页维持官方 800×800 原规格):
- 左:供应商列表(可增删、可拖动排序)
- 右:API Key、Base URL、协议、模型列表、一键获取可用模型、检测推理等级
- 底部三块:辅助视觉(识图)/ 生图 / 生视频的模型配置
- 网络代理(全宽区块):总开关 + 代理地址 + 连通性自检 + 生效范围(全局 / 仅选中), 逐供应商开关在供应商卡片上
API Key 存哪:写入 DSH 凭据密钥环(.credentials.yaml),可切换回明文,
密钥永不过浏览器。换供应商不需要改任何环境变量。
相关工具(模型可见):generate_image、generate_video、vision_describe。
网页搜索走 AnySearch(注册为官方 ctx.web provider,不是硬编码抓取)。
提示词通道:什么时候模型才知道这些围栏
有些能力是「呈现形态」,模型不知道就不会主动用。它们由一组内置提示词通道控制, 在 Seeker 标签右侧那枚齿轮 → 大盘设置卡片里逐个开关(「工作区记忆」那组是逐工作区的列表),每会话只注入首步,走独立 user message:
| 通道 | 默认 | 内容 |
|---|---|---|
| 中文偏好 | 开 | 中文思考与输出(含「代码/路径/报错不翻译」例外);工具调用里会被直接上屏的说明性参数(description、子任务标题…)与任务清单条目(todo_write 的 content)也归对外输出,必须中文 —— 官方 pwsh / bash / run_code 的 description 文档要求「英文、active voice、5-10 词」、todo_write 的 content 文档是 "What the task is — a short imperative line.",而 DSH 把它们原样印在右栏操作面板与「任务概览」卡上(plain-language.ts 里那三条规则取 description 当 detail 且带 hideVerb,抹掉动词后这一行只剩它),不写这条就会得到「中文对话 + 英文面板/待办」 |
| 团队协作 | 开 | 什么时候该派子智能体、多代理怎么分工 |
| step 纪律 | 开 | 两条行为纪律装在一次注入里:① 一次调用装多少动作 —— 无数据依赖的探查合并进一次调用(实测 3 条命令拆 3 步 25.7s → 合并 1 步 5.3s,省 79%),并行不是杠杆(PTC 里 Promise.all 实测仅 0.76%),含反判据(真依赖 / 需独立审批 / 输出会超限时不合并);② 任务清单纪律 —— 开工先列、过程中逐项推进、收尾清零,pending 不等于「等待用户」,交棒事项(等审阅 / 等重启)不进清单,收口动作本身不占条目。第 ② 条原先住在记忆库的一条 pinned 记忆里( todo_write 完整纪律,importance 19.6),2026-10-25 并入本通道并软废弃那条记忆:它是行为纪律而不是「用户说过什么」,住在记忆库里会被检索 / 预算机制影响,而纪律必须每次都成立。它与「回合结束自动收口」是同一件事的两半 —— 纪律让模型做对,收口在没做到时兜底;两条通道的开关没有合并(作用机理相反:一个写提示词,一个是 host 监听器)。子代理 A/B 实测:PTC 下 step −39.5%、墙钟 −58.7%;native 下 step −25.4% 但墙钟仅 −3.9%( dsh-tool-pwsh 未声明 isConcurrencySafe,native 并行分发对 shell 任务失效)——详见 docs/step-rule-ab.md |
| 记忆 | 开 | 检索到的相关记忆 |
| html 卡片 | 开 | 告诉模型有 ```html 围栏(含内置 kit 样式与运行时),同时交代 /iu 的 12 种 kind 语义 |
| Agent 邮箱 | 开 | 本 Agent 有专属邮箱(kratgone@agent.qq.com 这类地址),注册第三方服务、收验证码时直接用它,不问你 |
⚠ 关掉「html 卡片」通道,
/iu xxx会以普通文本到达模型 —— 不报错、也不出卡。 排查「/iu没反应」先看这里。旧的「diagram(对话内流程图)」开关已移除:图形能力并入 html 卡片通道,改用
```html手写内联 SVG,不再需要单独打开。
Agent 邮箱通道(跨模块,与邮箱工作台解耦)
「Agent 邮箱」这一行与其余通道不同源:它由邮箱模块承载,卡片只是 把它转出来,让你不必为了拨它再跑一趟邮箱卡片。
它和邮箱工作台(卡片里的邮箱设置页)的总开关互不影响:
| 邮箱工作台 | 这一行 | 结果 |
|---|---|---|
| 开 | 开 | 模型有 6 个 mail_* 工具,声明点名工具 |
| 开 | 关 | 工具还在,但模型不知道有这个邮箱 |
| 关 | 开 | 照常声明,模型改走 agently-cli 命令行 |
| 关 | 关 | 什么都没有 |
第三行是刻意的:关掉工作台通常只是想省下那些工具每轮占的 schema,不该因此
让模型忘掉自己有个邮箱(那样每次注册又得你提醒一遍)。所以工具不在时注入文本会
换成命令行版本,点名 agently-cli message +list / +read / +search 等实测可用的只读命令。
模型这一侧没有发信能力:mail_send / mail_reply / mail_forward / mail_trash /
mail_delete 五个写类工具已下架,注入文本也不再提发信、回信、转发的命令 ——
给模型的操作说明只要写了发信命令,就等于把写信能力又交回去了。邮箱里的事由你自己看。
邮箱尚未授权(没跑过 agently-cli auth login)时会写明这一点:注入此时会静默跳过
(把「你有邮箱」说成假的比不说更糟)。
邮箱工作台开关是运行时生效的,拨完立即起作用,不需要重启 DSH。
回合结束自动收口(同一张卡里的第 6 个开关)
这张卡里还有一枚**「回合结束自动收口」开关,默认开**。它不是提示词通道 (一个字都不进 prompt),只是同属「插件内置、全局单值」这一类能力才并排放在一起。
它治的是任务清单的收尾:DSH 的任务投影是整表快照(turn/end 保留、下一轮
turn/start 才清空),模型漏写最后一次清单时,那条「进行中」会一直挂在右栏
「任务概览」卡上——回合早结束了,卡片却还在转圈。
开启后,插件在每个回合收口时把清单里残留的「进行中」项降级为「待办」,卡片 相应地把它们标成**「未完成」**:是一个诚实的待办,而不是一个永远转圈的假状态。 只在真有残留时才动手,没有残留的回合零写入。
关掉它就回到原样:残留项保持「进行中」不变。
安装与卸载
安装
dsh plugin --profile web add dsh-chat-plus
npm 上是 dsh-chat-plus;直接跑 GitHub 源也一样:
dsh plugin --profile web add github:Kr-ATG/dsh-chat-plus
重启 DeepSeek Harness。
卸载
dsh plugin --profile web remove dsh-chat-plus
本地开发安装
New-Item -ItemType Junction -Path "$env:USERPROFILE\.dsh\profiles\web\node_modules\dsh-chat-plus" -Target D:\AI\Dsh\dsh-chat-plus
并在 ~/.dsh/profiles/web/cordis.patch.yml 追加(同 id 按 last-write-wins 合并):
- insert:
- id: dsh-chat-plus
name: dsh-chat-plus
本地产物改完 node build.mjs 后刷新页面即可(client 半身热重载);host 半身改动需要重启 DSH。
配置一览
视图与卡片开关
编辑 src/client/kr-chat/enabled.ts 后重新 build。都是隐藏而非删除,改回 true 即恢复:
| 开关 | 默认 | 控制 |
|---|---|---|
KR_CHAT_ENABLED | true | 整套 Seeker 视图(标签 + 右栏 + 专属样式) |
KR_PANEL_HEADER_VISIBLE | false | 右栏顶栏(头像 + 标题 + 统计 + 截图 / 收起按钮) |
KR_PLAIN_TIMELINE_CARD_VISIBLE | true | 「操作面板」卡 |
KR_SUBAGENTS_CARD_VISIBLE | true | 「子智能体」卡 |
KR_ASK_CARD_VISIBLE | true | 「提问与回答」卡 |
KR_OUTPUTS_CARD_VISIBLE | true | 「产出物」卡 |
KR_MEMORY_CARD_VISIBLE | true | 「记忆」卡 |
KR_TOOL_CALLS_CARD_VISIBLE | true | 「工具调用」卡 |
「操作面板」「工具调用卡」另有运行时开关(大盘设置 → 展示),与上表是叠加关系: 构建开关决定这张卡还存不存在,运行时开关决定此刻要不要看。
设置里能改的
- 设置 → 通用 → 界面字体:整站字体两档下拉(微软雅黑 / 霞鹜新致宋)。默认微软雅黑零下载;选霞鹜新致宋时从本插件的 host 路由按需拉一次 4.99 MB 全字集 woff2(之后走 immutable 缓存),下拉框旁会显示「加载中 5.0 MB」或「加载失败,已回退系统字体」。选择存 localStorage,首帧即生效(不等 React 挂载,不会先闪一下系统字体)。与官方「更多字体设置」并存:你在官方那边显式填了正文字体时以官方为准
- 设置 → 供应商:上面那一节的全部内容
- 输入框左侧:工具闸门
- Seeker 标签右侧的齿轮(大盘设置):提示词通道六条(含 step 纪律)+ 工作区记忆一条 + 展示三条(含总结卡外框开关,关掉后总结卡不留边框与阴影,退回纯正文)
记忆注入的开关不在输入框里。它原先也挂在输入框左端(一枚大脑图标 + 悬浮卡),2026-10-23 按用户要求删除;同一个开关住在上面的「大盘设置」抽屉里,读写的 还是同一份
/inject-state。
浏览器按需拉起
浏览器操作工具(mcp__playwright-mcp__*)需要一个开着 CDP 端口的 Chrome 作为 attach 目标。
本插件会在第一次调用浏览器工具时才把它拉起来,之后空闲 5 分钟自动关掉 ——
不用再让计划任务开机常驻一个 Chrome(实测常驻约占 1GB 内存)。
- 平时不占内存:没调用浏览器工具时,没有这个 Chrome 进程。
- 首次调用会多等约 0.3 秒:冷启动耗时,之后每次调用都很快。
- 关掉后下次再调用会自动重新拉起,不需要重启 DSH,也不需要手动做什么。
- 只关自己拉起的那个:你自己手动开的 Chrome 不会被碰。
- 登录态保留:用的是独立 profile(
%LOCALAPPDATA%\dsh-chrome),cookie 与登录态持久化, 关掉再拉起不会掉登录。
默认值(cordis.patch.yml 的 browserOnDemand 段可改):
| 项 | 默认 | 说明 |
|---|---|---|
port | 9222 | attach 端口,要与 browser-use-playwright-mcp 的 endpoint 一致 |
idleMs | 300000 | 空闲多久关掉(毫秒),0 = 不自动关 |
enabled | true | 总开关,关掉则退回「自己保证 9222 上有 Chrome」 |
浏览器操作标志
哪个会话正在跑浏览器操作,左侧会话列表一眼就能看出来:该会话行右端一枚会呼吸的 小浏览器图标(蓝色,跟随主题)。
位置在行右端、时间戳旁边:右侧本来就承载「这个会话最近怎么样」(相对时间 / 进行中 的 pending 标签),浏览器活动放这里读起来是一路的;行首那 16px 还给官方状态点 (转圈 = 正在生成,与「正在操作浏览器」不是同一件事,挤一格会互相盖)。图标占位时 行会向内让出一格(8px → 25px,带过渡),时间戳被轻轻推开、不会与图标重叠;鼠标 悬停时官方把时间戳换成行操作按钮,图标位置不动。
数据来自 host 半身对 tools/execute 的观测(GET /api/chat-flow/browser-activity)。
几处刻意的取舍:
- 1500ms 尾迹:一次网页操作通常是
navigate→snapshot→click→type一串调用, 每两次之间模型还在想。没有尾迹,图标会在调用间隙频闪;有尾迹,一串相邻调用被缝成 一段连续的「正在操作浏览器」。 - 两条判据,都只认「真的在操作浏览器」:
- MCP 工具:工具名按
mcp__<service>__<tool>切词元后匹配 (playwright / browser / chrome / devtools / stagehand / ego / puppeteer / selenium / cdp), 不用裸子串 —— 否则list_categories里藏着ego会被误判。够长的词还认复合词 (browseros_neo、browserbase),而ego/cdp这类短词仍只做精确匹配,egosystem、cdpdriver不会误判。 - 命令行 CLI:ego-browser 这类工具不是 MCP 工具,是用
pwsh跑的命令行程序, 所以额外看arguments.command里可执行位置上的 CLI 名 (ego-browser / ego / playwright / stagehand / puppeteer / selenium)。grep ego-browser、echo "ego-browser"、Get-Content ego-browser.md只是在谈论它, 不会点亮图标;带引号路径、.exe后缀、&调用运算符、管道或分号后的命令段都认。
- MCP 工具:工具名按
- 转后台的调用继续亮到进程真的结束:
pwsh跑浏览器 CLI 时,前台等待超时会把它 转成后台 job 并立刻返回(kind: promoted),显式run_in_background更是从一开始 就只回 jobId(kind: background)。这两种情况下工具调用已经结束、但浏览器进程还在跑, 所以观测器认出这两种结果后不立即收尾,改由ctx.jobs的settled/removed事件来收(另有一层 6 小时看护定时器兜底,防事件丢失导致图标永不灭)。 - 只观测、不改行为:观测器全程 try/catch,任何异常的结果都只是「图标不亮」; 观测器拿不到会话归属时直接跳过这次记录,绝不会把图标算到别的会话上。
- 路由不通就静默:host 半身未装 / 未重启时路由 404,客户端一律当「无活动」, 不重试、不报错、不影响对话与列表。
- 不用等真调用也能看:浏览器控制台执行
__dshBrowserFlagPreview([{sessionId, active:1, service:'playwright-mcp', tool:'browser_click', since:Date.now(), lastAt:Date.now(), lingering:false}])即可在页面上看到标志与动效;__dshBrowserFlagPreview(null)清空。 - 排障:
GET /api/chat-flow/browser-activity?debug=1额外回最近 20 次浏览器调用的 接缝摘要(有没有 callId、sessionId 从哪条路径解析),只含工具名与来源标量, 不含任何工具参数原文。

环境变量
| 变量 | 用途 |
|---|---|
DSH_CHECKOUT | 构建时若本目录没有 esbuild,去这个 checkout 的 pnpm store 里借 |
DSH_HOME | DSH 数据根(默认 ~/.dsh) |
常见问题
「卡片是空白的」 模型可能写了个只有注释的围栏。现在客户端会把它回退成代码块(你能看到原文);如果仍遇到, 把那条围栏内容贴出来。
「/iu 没反应」
去 Seeker 标签右侧的齿轮 → 大盘设置,「对话内交互卡片」那条是不是被关了 —— 它同时负责交代 /iu 语义。
「模型不画流程图 / 架构图 / 时序图」
图形走 ```html 围栏(手写内联 SVG),能力文档随「html 卡片」通道注入——
先确认那条通道是开的(同上一条),再直接说「画个流程图」。旧的独立
diagram 开关、以及 iu 的 graph / arch / sequence 三个 kind 都已移除。
「思考卡 / 提问卡不见了」 它们只在 Seeker 视图渲染,且都在回合内。普通「对话」视图里是官方原生样式。
「右栏卡片显示不全」 窗口太窄触发了挤压自适应(操作面板展开后的列表会从 13 行降到 9 行)。拉宽窗口或收起记忆卡。
「操作面板的位置还是会变」 它只有两个会动的前提,都在子智能体卡上: ① 那张卡必须固定 5 行(不是「按内容自适应」)—— 改回自适应,子智能体多几个, 操作面板就往下挪几行; ② 那张卡必须排在操作面板上面,且没有子智能体时要不可见占位(不是整卡不渲染、 也不是 display:none —— 后两者都会把盒子抽掉、让下面的卡上移)。 两条都在 scripts/smoke-client.mjs 里钉着,改坏了冒烟会红。
「截图里的代码块没有高亮」 高亮引擎只内联了 34 种常用语言,未注册的语言回落纯文本(不报错)。需要更多语言见 开发一节的说明。
「装了没反应」
重启 DSH 一次(host 半身不支持热重载)。若仍无,检查 profile 的 bundles 里是否有
dsh-chat-plus:dsh plugin --profile web list。
从旧版本升级
从 dsh-webui 过来
dsh-webui 已退役,其对话增强 / 工具聚合 / 思考隔离能力全部在本插件内独立实现,
不要再同时挂 dsh-webui(座位会重复注册)。卸掉它即可。
从 dsh-triad 过来
已融合,dsh-triad 退役。 记忆 / 用量 / SKILL 工作台整体并入本插件 座位、路由前缀、设置命名空间、数据目录全部原样保留,所以:
- 你原有的记忆条目、用量统计继续可用,不需要迁移
- 请把
dsh-triad从 profile 的 bundles 里去掉 —— 两套同时挂会因 slot id 与路由前缀 相同而重复注册
定时任务不再由本插件提供(已交给官方 @deepseek-ai/dsh-experimental-schedule-bundle)。
旧的定时任务不会被自动接管,需要重建。
从 dsh-provider-hub 过来
已融合,dsh-provider-hub 退役。 供应商配置、网络代理、模型能力开关、生图 / 识图 / 生视频 工具、提示词优化、AnySearch 搜索、凭据密钥环全部原样保留,路由前缀与设置命名空间零迁移。 官方「模型」设置页的导航项会隐藏(供应商页接管了模型目录编辑)。
安全模型
本机 HTTP 路由的跨站闸门
插件注册的 /api/* 路由比官方的 /api 前缀更长,而 DSH 的 webServer 是
longest-prefix-wins 派发 —— 官方 handler 里那道 connection.requestRejection
(含 Origin / sec-fetch-site 校验)对插件路由根本不执行。所以每一条插件路由
都必须自己补来源校验,src/shared/csrf.ts 就是这层公共闸门。
判据(只拒明确跨站,其余放行):
| 请求形态 | 判定 |
|---|---|
sec-fetch-site: cross-site | 拒 |
origin 与 host 主机名不同 | 拒 |
origin: null(sandbox iframe / file://) | 拒 |
同源(same-origin / 同主机名同端口) | 放行 |
无 Origin(curl、<img>、Node 脚本) | 放行 |
刻意不要求「Origin 必须 loopback」:那会把 http://192.168.x.x:3080
(手机远程配对、局域网访问)一起拒掉。判据按同源比对,对任何 host 都成立。
覆盖范围:截图、download、open-path、open-file、office、记忆、邮件、技能开关。
用默认浏览器打开文件(open-file,2026-10-21)
POST /api/chat-flow/open-file { path, cwd? } → { ok, via, path }:把「产出物 / 画廊
/ 弹窗里点一下打开文件」这件事交给系统,浏览器文档(.html/.htm/.xhtml/.svg/.mht/.mhtml)
交给系统默认浏览器,其余交给文件关联。
它替换掉官方 remote.session.openWorkspacePath 作为主出口,原因有两条(都是实测):
- 官方那条按 host 进程 cwd 解析相对路径。产出物卡里
present交付的路径是相对 会话工作区的(deliverables/x.html),被解析成dsh-runtime\deliverables\x.html; 而verifyDesktopPath不 stat 文件本体,Explorer 收到不存在的 file URL 也不报错, 只弹出「文档」文件夹 —— 界面上就是「点了没反应」。本路由先按请求带来的 cwd 绝对化, 再 stat 文件本体; - 官方 opener 在 Windows 上从不把 .html 交给浏览器(
openInBrowser的 win32 分支 直接return false,association意图更是从设计上跳过浏览器)。本路由读UrlAssociations\https\UserChoice找真正的默认浏览器,解析出 exe 后直接拉起它。
安全面三道闸门:自有 CSRF 校验(跨站 403)、必须是已存在的普通文件、可执行类扩展名 整类拒绝(.exe/.bat/.ps1/.lnk…)。文件不存在返回 400 而不是静默成功。
它现在只服务"侧栏展示不了"的那一半(2026-10-22):产出物卡点击先过
src/client/sidebar-display.ts 的 canShowInSidebar,能正常展示的走官方右栏
(sidebarRight.openResource),这条路只接 3D 模型、压缩包、avif/tif/heic 这类
侧栏打不开的文件。画廊与弹窗仍是它为主出口(那两处要的是"拿去用")。
截图引擎的 CDP 端口
截图用的常驻无头 Chrome 带 --remote-debugging-port。不要给它加
--remote-allow-origins=*:WebSocket 不受 CORS 约束,加了之后任意网页的 JS 都
能 new WebSocket('ws://127.0.0.1:9400/...') 连上来,然后 Page.navigate 到
file:// 读本机任意文件。现在默认不传该 flag(Chromium 111+ 默认拒绝一切
带 Origin 的连接),而 Node 侧 CDP 客户端不带 Origin,照常工作。
已知的破坏性操作
- 画廊「按分类删除」删的是当前筛选后的该分类条目,不是全量。确认前请注意筛选条件。
开发
node build.mjs # esbuild 双 bundle:lib/index.js + lib/client.js
产物必须随仓库提交(pnpm ≥ 10 不跑 git 依赖的 prepare,安装方不构建)。
.gitattributes 锁死 lib/*.js 换行 LF。
冒烟测试
node scripts/smoke-client.mjs # 对话增强 + iu / html / diagram 卡片
node scripts/smoke-host.mjs # host 路由 + download 工具
node scripts/smoke-triad-host.mjs # 工作台 host:路由 + 记忆工具 + agent 钩子
node scripts/smoke-triad-client.mjs # 工作台 client 纯逻辑
node scripts/test-system-prompt.mjs # 系统提示词观测(组装 / 分段 / 变量 / 快照折叠 / 路由)
node scripts/test-workbench-sync.mjs # 工作台云端同步(打包加密 / 还原 / 主次优先级)
node scripts/test-session-isolation.mjs # 跨会话隔离(抽源码求值,真跑行为)
node scripts/test-mail-two-phase.mjs # 邮箱两阶段重放(host 能力)
node scripts/test-mail-drawer.mjs # 邮箱卡片真浏览器 E2E(顶栏按钮 / 居中几何 / 开合 / 只读)
node scripts/test-settings-card.mjs # 大盘设置卡片真浏览器 E2E(portal / 居中 / 两栏 / 三条关闭通路)
node scripts/test-skill-manager.mjs
node scripts/test-skill-toggles.mjs
node scripts/test-subagent-catalog.mjs
或一把梭:npm run smoke(四个冒烟 + 会话隔离 + 邮箱两阶段 + 邮箱卡片 E2E + 设置卡片 E2E + 档案引擎与档案路由)、npm test(三个纯逻辑测试)。
改动前建议先跑一遍确认基线是绿的。冒烟用 node:vm 假出 window.__ModuleLoader__ 与 DOM
跑真正的产物,或用桩 ctx 驱动 host 的 apply()。
产物体积
随包发布约 25.9 MB(files 字段里的 lib + assets + scripts + build.mjs):
| 组成 | 大小 | 说明 |
|---|---|---|
lib/index.js | 4.5 MB | host 半身,自包含 |
lib/client.js | 3.0 MB | 浏览器半身 |
lib/client.js.map | 5.7 MB | 浏览器半身的 source map(断点用) |
assets/skills/ | 2.8 MB | 随包内置技能,启动时物化到 ~/.dsh/skills/ |
assets/vendor/ | 0.9 MB | mermaid 引擎,预压缩 |
assets/motion/ | 2.6 MB | 卡头任务动画:9 段透明底 WebM + 9 张首帧海报(走 host 路由,不进产物) |
scripts/ | 0.5 MB | 冒烟与测试脚本(已安装位置跑用) |
两处刻意的取舍:
- host 不出 source map(Node 不带
--enable-source-maps就不读它) - shiki 走 fine-grained:只内联 34 种常用语法与 2 个主题,而不是全量 ~220 种(约 10 MB)。
用纯 JS 正则引擎而非 oniguruma wasm,产物保持单文件自包含。代价是首次高亮慢一些。
加语言在
src/shot/markdown.ts的 import 列表里补一行。
build.mjs 内置 assertHostExternals():host 产物里出现任何需要运行时解析的
@deepseek-ai/* 就直接构建失败(已安装位置解析不到这些包)。
目录结构
src/
├── host.ts host 半身入口
├── client/ 浏览器半身
│ ├── index.ts 装配入口(各子模块独立 try/catch)
│ ├── kr-chat/ Seeker 双栏大盘(五张卡 + 控制器 + 样式 + 设置卡片 + 只读邮箱卡片)
│ │ 卡头动画:stage-mode.ts 判状态 / motion-clips.ts 片段表 / SeekerStage.tsx 播放器
│ ├── thinking/ 思考行 / 工具行 / 步骤卡 / 总结卡
│ ├── tool-summary/ 工具调用聚合与活动抽屉
│ ├── iu/ iu 原生交互卡片(kinds/ 每种卡一对文件:纯逻辑 + React 体)
│ ├── html-embed/ html 沙箱卡片
│ ├── diagram/ 流程图卡片
│ ├── proto/ proto-tabs 卡片
│ ├── shot/ 对话截图(markdown + shiki + mermaid)
│ ├── download/ download 工具前端
│ ├── provider/ 供应商中心 + 生图/识图/生视频 + 优化提示词
│ ├── triad/ 工作台(记忆 / 用量 / SKILL / 画廊 / 档案 / 系统提示词 / 同步设置)与邮箱面板
│ ├── tools-gate/ 工具闸门
│ ├── browser-flag/ 浏览器操作标志(会话行右端的呼吸图标)
│ ├── running-apps/ 「@ 运行中的程序」@ 源 + 图标组件 + 样式(数据层纯判据可测)
│ ├── sidebar-doc/ 侧边栏文档预览(pdf / Office 接管)
│ ├── sidebar-media/ 侧边栏音视频预览(原生 video / audio)
│ ├── ui-font/ 界面字体(设置 → 通用 的两档下拉 + 覆盖 CSS 变量)
│ └── sidebar/ documentPreviews 就绪等待(官方服务晚于插件 apply)
├── mail/ 邮箱 host(路由 / CLI 封装 / 注入通道)
├── provider/ 供应商 host(代理 / 密钥环 / 搜索)
├── triad/ 工作台 host(记忆引擎 / 用量 / 技能 / 档案 / 工作台同步)
├── browser/ Chrome 进程管理(启动 / CDP)
├── browser-on-demand/ 浏览器按需拉起(调用时启动 + 空闲关停)
├── browser-activity/ 浏览器活动观测(会话级活动表 + 只读路由)
├── running-apps/ 「运行中的程序」采集(常驻 PowerShell 子进程 + 只读路由)
├── git-status/ 工作区 Git 状态 / 大盘 / AI 生成提交备注 / 一键提交推送(只读路由 + execFile)
├── system-prompt/ 系统提示词观测(assemble 出口抓最终快照 + 只读路由,不落盘)
├── spill/ spill 准入 + 生图落盘(materialize 路由)
├── fonts/ 界面字体资产路由(/api/chat-flow/fonts/*,woff2 + 许可)
├── motion/ 任务动画资产路由(/api/chat-flow/motion/*,9 段 WebM + 首帧海报)
├── office/ Office / PDF 页图与 PDF 渲染
└── shot/ 截图引擎(无头浏览器)
许可
MIT。动效部分移植自 aa2246740/dsh-better-display(MIT),
mermaid 引擎与 shiki 各自遵循其上游许可。
Plugins associés
dsh-web (dsh-task-board)
zhu1090093659/dsh-web
dsh-web (dsh-web-all)
zhu1090093659/dsh-web
dsh-web-ui (dsh-task-board)
zhu1090093659/dsh-web-ui
dsh-web-ui (dsh-web-ui-all)
zhu1090093659/dsh-web-ui