- Home
- Plugins
- UI Enhancements
- dsh-chat-plus
dsh-chat-plus
kr-atg/dsh-chat-plus
DeepSeek Harness 对话体验增强套件(零源码改动、纯插件注入)。Seeker 双栏执行大盘:右栏任务 / 操作面板 / 子智能体 / 产出物四张卡 + 记忆钉底,左栏只留一行状态卡与贴进对话流的思考卡、问答卡(字号跟随官方「设置 → 字号」)。正文可出卡片:html 沙箱(跑真 HTML/JS)、diagram 流程图、iu 原生交互卡、proto-tabs。另有对话截图、download 下载工具、裸路径转链接,供应商中心(API Key / 网络代理 / 生图识图生视频 / AnySea
Install
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 各自遵循其上游许可。
Related plugins
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