dsh-pet-extras
给 DeepSeek Harness(DSH)的桌面桌宠 dsh-pet 加装的扩展能力。
它本身不提供桌宠。 没装
dsh-pet这个插件就没有使用意义——请先装dsh-pet,再加这个。这是个人开发者的业余项目,不是官方扩展,与
dsh-pet作者无关。 现在是0.2.3早期版本,作者第一次用 GitHub 发布插件,功能上仍可能有 bug,遇到问题欢迎提 issue,我会尽力修。
英文说明见 README.en.md。
⚠️ 先读这个:它会截你的整个屏幕
如果这一节看完你接受不了,请不要安装。
| 它做什么 | 你要知道的 |
|---|---|
| 按你设的间隔截取主显示器整屏 | 截图里可能出现聊天记录、密码框、别人的隐私 |
| 默认只存在本地磁盘 | $DSH_HOME/dsh-pet/screenshots/,插件自己不上传任何地方 |
| 只有在你自己打开「看屏幕」之后,图才会发给模型 | 发给你自己在 DSH 里配置的模型提供方,用你自己的 API Key,费用你自己承担 |
默认状态:「看屏幕」是关的——不截图、不联网、不调用任何模型。什么都不开的话,它只是个躺在磁盘上的插件。
不想让图离开电脑,就永远别打开 screenshotEnabled。
完整的数据流图表见下面的 隐私与数据处理。
接宿主智能体:这个插件的底座,不是可选项
先讲清楚原理,后面所有功能都建在这上面。
dsh-pet 原本有个自己的小脑:它拿一个默认模型单独聊,读的是自己那份精简记忆,跟你界面里那条会话完全是两个脑子。你说过的事它不知道,它说过的事你也查不到。
这个插件做的事,是把这个小脑拆掉,让它改用你界面里的真脑子。
你说的话 / 屏幕画面
↓
投递给 DSH 里的一条真实会话 ← 就是你平时在用的那条
↓
那条会话用它自己的上下文和记忆回答
↓
回答再回到桌宠,用它的声音逐句说出来
这样一来得到什么:
- 一个脑子。桌宠不会另开一套记忆,它说的就是你那条会话说的。你在界面里交代过的事,桌宠那边也知道。
- 能接着聊。桌宠不再是一问一答的玩具,它会带着前面几十轮的上下文接你的话,能延续、能追问。
- 能替你干活。投进去的是真消息,那条会话会真的去调工具、读文件、跑命令——桌宠于是从「会说话的挂件」变成了「能动手的入口」。
- 屏幕也归它看。看图播报是同一个机制:截图作为附件投进去,会话真的看见画面再回话,而不是拿一句话去猜。
代价也得说清楚:
- 桌宠每说一句、每看一眼,都占那条会话的 token。开得越勤,撞上下文上限越快。
- 投递是串行排队的:那条会话正在跑你的任务时,桌宠得等。
- 它是真的进你的会话,所以别指望它「只是个宠物」——它做的事跟你自己发消息是一回事。
这也是为什么下面「看屏幕」必须走这条路:如果只发一句「你抬头看一眼屏幕」却不带图,会话看不见画面,只能凭空编。要播报就是投图,不播报就别投。
和 dsh-pet 自带的对话有什么区别
dsh-pet 自带 |
开了接智能体后 | |
|---|---|---|
| 谁在回答 | 单独调的默认模型 | 你界面里那条真实会话 |
| 记忆 | 自己那份精简记忆 | 会话的完整上下文和记忆 |
| 你说过的事它知道吗 | 不知道 | 知道 |
| 能调工具干活吗 | 不能 | 能(那就是真消息) |
| 占谁的 token | 单独的调用 | 那条会话的额度 |
功能
| 功能 | 说明 | 默认 |
|---|---|---|
| 接宿主智能体 | 底座。桌宠的话投递给 DSH 里的一条真实会话,回答再用桌宠的声音逐句说出来。共享那条会话的上下文和记忆。关掉它,下面两个都用不了 | 关 |
| 看屏幕 | 一个开关管到底:每隔 N 秒抓一张主屏(本地 PNG),并把这张投给接的那条会话看一眼,会话按自己的上下文和记忆回一两句,显示在桌宠头顶气泡里。开了就一定会调模型、耗 token;关了就全停。没有「只截不投」这一档 | 关 |
| 视野档案 | 每次看图的原图 + 会话的原始分析存进 dsh-pet/视野/(按天分目录,留 7 天)。平时不进上下文,要查「昨天下午我在干嘛」时再翻 |
开 |
| 锁在屏幕内 | 把 confineToScreen 开关下发到 dsh-pet,防止宠物窗口跑出屏幕边界。默认不管:只有你在 /dsh-pet-extras/confine 里明确设过,插件才接管,不会装完就擅自改 dsh-pet 的配置 |
关(不管) |
设置页
装好后,DSH 设置里会多出一节 「桌宠扩展」,里面是插件自己的旋钮:看屏幕开关 + 看屏幕间隔(秒),改完点保存即刻生效。
为什么单独开一节:这些键存在本插件自己的配置文件里,dsh-pet 的设置页不认识它们。之前只能改 JSON 或 curl,现在有了图形入口。
注意:官方
dsh-pet的设置页里没有这个开关,别去找。以前这里写着「两个是同一个键、保存会同步」——那是我们早期往dsh-pet里塞私有键留下的错觉,官方版根本没有这些键,那套同步代码也删了。插件的旋钮只在这一节里。
安装
前置:先装 dsh-pet(本插件是它的外挂,不提供桌宠本身)。
桌面版:一键装(推荐,不用改任何 JSON)
- 打开 设置 → 插件 → 添加插件
- 在「包名或地址」里填包文件的绝对路径,二选一:
.tgz包:D:\某个目录\dsh-pet-extras-0.2.3.tgz- 解压出来的文件夹:
D:\某个目录\package
- 确认安装,按提示重启 DSH
这条路不需要手动改 package.json:DSH 的插件管理器在安装时会读包里的 dsh.bundle 字段,自动把包名写进 dsh.profile.bundles(dsh-plugin-manager 的 reconcile() 负责这一步)。
装完第一件事:设置 → 桌宠扩展 → 在「接哪条会话」里选一条会话,再打开「看屏幕」。
不选会话时插件不截屏、也不投递——这是故意的。早先版本会在你没选的时候「挑消息最多的那条会话」投递,实测会把整屏截图灌进一个毫不相干的会话里,所以现在改成:没选就什么都不做。
手动装(pnpm add / 离线 tgz):必须改两处
DSH 只加载 package.json 的 dsh.profile.bundles 里列出的包。pnpm add 只写 dependencies,不写 bundles——文件装好了、重启了,设置页里却什么都没有、接口全 404,就是这个原因。
以 profile 目录 $DSH_PROFILE(桌面版通常是 $DSH_HOME/profiles/desktop)为例,改 $DSH_PROFILE/package.json:
{
"dependencies": {
"dsh-pet": "0.3.8",
"dsh-pet-extras": "0.2.3" // ① pnpm add 会自动加这一行
},
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
// ... 其他 bundle
"dsh-pet",
"dsh-pet-extras" // ② 这一行必须手动加,否则插件不加载
]
}
}
}
改完重启 DSH。卸载时两处都要删。
怎么确认真的加载了
# 返回 ok/enabled/minGapSec 就是加载成功;404 就是没加载(bundles 没加、或没重启)
curl http://127.0.0.1:<PORT>/dsh-pet-extras/sight-share
预期返回 {"ok":true,"enabled":true,"minGapSec":60}。若是 404 / no such route,回到上面检查 bundles 那一行。
端口每次重启都会变,从 DSH 窗口标题栏或者启动日志里看。
这个版本不再改 dsh-pet 的任何文件
0.1.0/0.1.x 早期版本靠读 dsh-pet 配置文件里的 screenshotEnabled / agentBridgeEnabled 工作,而这几个键是我们自己塞进 dsh-pet 的私有配置里的——官方 0.3.8 根本没有,官方更新也会覆盖掉。别人装了之后开关永远是关的,就是因为这个。
现在插件的配置全部自给自足($DSH_HOME/dsh-pet/extras-config.json),不读 dsh-pet 的配置,也不需要改 dsh-pet 的 assets/config.jsonc。
唯一还会动 dsh-pet 的是「工作状态气泡」那个可选补丁,见 工作状态气泡与桌宠对话冲突。它打不上会自己跳过,不影响其它功能。
配置
推荐在 DSH 设置 → 桌宠扩展 里改(有开关和数字框,保存即生效)。
配置文件在 $DSH_HOME/dsh-pet/extras-config.json,也可以走 HTTP 接口:
# 看当前配置
curl http://127.0.0.1:<PORT>/dsh-pet-extras/config
# 改配置(只传要改的字段)
curl -X PUT http://127.0.0.1:<PORT>/dsh-pet-extras/config \
-H 'content-type: application/json' \
-d '{"screenshotEnabled": true, "sightShareMinGap": 60}'
<PORT> 是 DSH 的 Web 端口,和地址栏里的一致。
| 键 | 类型 | 默认 | 说明 |
|---|---|---|---|
targetSessionId |
string | "" |
接哪条会话(权威键):完整会话 id,如 session-1d86…。空 = 不投递、也不截屏。填错(那条会话已经关了)同样不投,不会偷偷改投别的会话 |
agentBridgeTarget |
string | "" |
兼容键:会话标题。设置页保存时会自动跟 targetSessionId 一起写。只在配置文件里根本没有 targetSessionId 这个键时(从老版本升上来的配置)才当兜底用;这个键一旦存在,标题就彻底不参与匹配——哪怕值是空串,也算你亲手清空的,空就是空 |
screenshotEnabled |
bool | false |
看屏幕总开关:开 = 定时截屏 + 每张交给接的那条会话(要投图,每张会花 token);关 = 全停。还需要在下面选一条目标会话,没选就什么都不投 |
sightShareMinGap |
number | 60 |
唯一的节奏:两次投图之间至少隔多少秒(最小 5)。这是闸门不是节拍器——实际触发还会被截图助手的 20 秒轮询对齐、会话生成耗时、以及偶发的跳过一格影响,详见下面「实际节奏」一节。想让节奏稳,设成 20 的整数倍 |
screenshotIntervalSec |
number | 20 |
已废弃:间隔统一由上面的 sightShareMinGap 决定。代码里留着这个默认值只为读老配置文件,改了没有任何效果 |
confineToScreen |
bool | true |
想让窗口锁在屏幕工作区时填的值。只有 confineManaged 是 true 才起作用(通过 /dsh-pet-extras/confine 设置会自动置上) |
confineManaged |
bool | false |
插件是否接管 dsh-pet 的 confineToScreen。默认 false = 不碰,装完绝不擅自改 dsh-pet 的配置 |
screenshotKeep |
number | 60 |
截图文件保留张数(1~2000),超了删最旧的 |
lookLearnEnabled |
bool | false |
看图播报的自学习(见下) |
lookRulesMaxChars |
number | 1200 |
学到多少字就停止采用(0~4000) |
| 看图播报的额外要求 | 文件 | — | 写在 $DSH_HOME/dsh-pet/看图播报要求.md 里(不是配置键;首次启用时自动生成一份说明) |
必须两件事都有:①「看屏幕」打开;②「接哪条会话」选了一条正在用的会话。只开开关不选会话,一张都不会投(这是故意的:宁可不动,也不往不相干的会话里塞图)。
以前这里写着「开关会同步到
dsh-pet的设置页」——那是不存在的功能,现已删除。这个插件的配置只住在自己的extras-config.json里,只在本插件的设置页(设置 → 桌宠扩展)里改,两边不会再打架。
HTTP 接口
全部挂在 /dsh-pet-extras 下:
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /ping |
探活,返回插件名、版本、DSH_HOME |
| GET | /config |
读当前配置 + 默认值 |
| PUT/POST | /config |
改配置(只传要改的键) |
| GET | /screenshot |
列出最近截图和开关状态 |
| POST | /screenshot |
立刻抓一张(会拉起截图 helper) |
| GET | /agent |
桥接状态:目标会话、队列深度、可选会话列表 |
| POST/PUT | /agent |
body {"sessionId": "..."} 换目标;{"text": "..."} 直接投递一句话;加 "async": true 立刻返回、后台播报 |
| GET | /confine |
读屏幕锁定开关 |
| PUT/POST | /confine |
body {"value": true},立刻下发给 dsh-pet |
| GET | /sights?limit=N |
取最近 N 条看图记录(默认 50,上限 500) |
| DELETE | /sights |
清空看图记录 |
| POST | /rate |
body {"index": 0, "rate": "good"|"bad"|""},给某条播报打标 |
| GET | /learn |
自学习状态与学到的规则 |
| POST | /learn |
跑一次自学习(需要 lookLearnEnabled: true) |
工作方式
截图
DSH 宿主进程拉不起 desktopCapturer(那是 Electron 渲染进程的能力),所以本插件会自己 spawn 一个最小化的 Electron 进程专门截图:
- 进程入口
runtime/electron-helper/main.cjs - 它在
127.0.0.1:随机端口起一个只监听本机的 HTTP 服务,启动时把地址打到 stdout - 宿主从 stdout 正则抓到端口,之后通过
POST /shot让它截图 - 图片落在
$DSH_HOME/dsh-pet/screenshots/,默认保留最近 60 张
为什么不用 IPC/stdin:实测 Electron 被 spawn 起来之后 stdin 不通,写进去的行收不到。所以走了本机 HTTP。
接智能体
投递不是「复制一遍上下文」,是真的进入那条会话:
- 按
agentBridgeTarget(或自动规则)挑一条宿主会话 - 等它空闲
- 把桌宠的话
followup投递进去 - 等这一轮跑完,从会话事件流里抽出
assistant/message的正文 - 用桌宠逐句气泡说出来
好处是共享那条会话的完整上下文和记忆,不需要额外维护一份。代价是必须排队——那条会话正在忙的时候,桌宠得等。
桌宠的回复怎么和你在界面里说的话区分开:投递的文本前面会加三个零宽空格作为围栏,扫回复时只认围栏之后的那一段,所以你自己的对话不会被误当成桌宠的回答念出来。
看图播报
看图播报和聊天是同一条路(这是本插件的核心要求,别改成两条):
有新截图 → 存成附件 → followup 投进接的那条会话(等于你自己贴了张图)
→ 会话按它自己的上下文和记忆回一句
→ 这句话显示在桌宠气泡里
好处是它说的就是会话说的,看的也是会话看的,不用额外维护第二份记忆。 代价是每看一次图都要占那条会话的 token——所以有间隔和「画面没变就闭嘴」两道闸。
具体规矩:
- 请求里明确要求不要用「屏幕上」「画面里」这类报幕腔开头,也不要每次同一个开头
- 认不准就笼统说,宁可笼统也不猜错
- 投之前先缩图:整屏原图 2561×1601 约 1.1MB,缩到长边 1568 后约 520KB(省一半以上),模型理解力不掉
- 缩图是零依赖自己实现的(
lib/png-shrink.js,只用 Node 自带 zlib),不引任何第三方库 sightShareMinGap(默认 60 秒)内不重复投- 投之前先在本地比像素:把画面压成 32×20 个格子算平均色,跟「上一次真正投出去的那一屏」比。变化格占比 < 5% 就判成同一屏,整轮跳过——不进会话、不花 token、不出声。鼠标移动、时钟跳字、弹幕、进度条这些只占很小一块,滤得掉;切窗口、翻页会让大面积同时变,占比一下就上去
- 本地判定跑在
lib/screen-diff.js(零依赖,复用png-shrink.js的像素解码),一张图约 100ms。阈值用 60 张真实截图 + 8 个合成场景校准:噪声最高 3.1%、真变化最低 9.8% - 兜底:万一会话还是回了「没变」这类短句,也闭嘴(主判据已经是本地像素比对,这道只防模型不听话)
- 代码层还有一道只删不改的过滤:整句里出现三个以上「截图 / 画面 / 右侧 / 弹幕 / 进度条」这类描述性词汇,这句丢掉不播;一句人话都没有时这一轮就闭嘴
气泡停留时间按字数算(5.5 字/秒 × 2.6 余量,夹在 845 秒),不是一刀切的固定时长。短句至少 8 秒,20 字左右的常规句约 1314 秒,长句最多 45 秒。
实际节奏:设的间隔 ≠ 两次播报的间隔
看屏幕间隔 是闸门(两次投图之间至少隔这么久),不是节拍器(每隔这么久必然播一次)。真实两次播报之间的时间 = 间隔 + 下面几项:
| 项 | 典型多出 | 说明 |
|---|---|---|
| 截图助手的轮询对齐 | 0~20 秒 | 助手每 20 秒检查一次有没有新图,所以实际触发点会对齐到下一个 20 秒刻度 |
| 会话生成回答 | 1~6 秒 | 会话要真的跑一轮才有话可说 |
| 跳过一格 | 最多一个间隔 | 某一轮被判 too-soon 或赶不上节奏时,会整体后移一格 |
所以设 120 秒,实际大约是 2 分 05 秒 ~ 2 分 25 秒一次;偶尔会遇到 3 分半以上的空档(跳过一格时)。设 60 秒则大约 1 分 05 秒 ~ 1 分 25 秒一次。
想让节奏更准,把间隔设成 20 的整数倍(20 秒是助手轮询的最小粒度),比如 60 / 120 / 180,抖动最小。
想看真实节奏,翻
$DSH_HOME/dsh-pet/extras.log,搜准备播报或这一轮跳过(too-soon),每行都带毫秒时间戳。
这一版之前的做法是把截图交给默认模型单独跑一次(
describeShot):那条链路和主会话毫无关系, 桌宠等于自带一套脑子,说的不是会话说的、记忆也不共享。2026-10-24 已整条拆掉。
看图播报的自学习(默认关)
打开 lookLearnEnabled 后,你可以用 POST /rate 给每次播报打「好 / 差」标记。之后 POST /learn 会把标记过的样本交给模型,总结出「什么样的说法像人话」,写进 $DSH_HOME/dsh-pet/look-rules.txt,后续播报会参考这些规则。
它不会联网学习,也不会改变桌宠的性格:
- 唯一的输入是本地
dsh-pet/视野/index.jsonl,不抓取任何外部内容 - 性格不由本插件决定:播报是从你那条会话里出来的,用的是什么人设、什么口吻,跟你在那条会话里聊天的调子一致。插件只负责「别说成说明书」这一层措辞,管不到人格——这一条是 2026-10-24 大改的结果,以前插件里写死过一段人格(
LOOK_PERSONA_CORE),已经删掉了 - 学到的规则里如果出现「你是谁 / 你的身份 / 你其实是」这类改写人格的内容,会被直接拒用,整份规则作废
- 规则超过
lookRulesMaxChars就整段不采用(不截断——截断留半句话更容易学歪)
兼容性
- 直接依赖:只有 Node.js 内置模块(
node:fs/node:http/node:child_process等),不 import 任何@deepseek-ai/*包,所以版本对不上也不会炸 - 通过
inject声明获取宿主服务:webServer、attachments、agentDefaultModel、agents、llm - 与 dsh-pet 的耦合只有 HTTP 接口:
POST /dsh-pet-7340/broadcast(播报)、POST /dsh-pet-7340/anim(动作)、GET /dsh-pet-7340/config(读宠物配置)。这些都是 dsh-pet 原版就公开的路由,不需要安装任何改版 - 路径全部走
DSH_HOME环境变量,没有写死任何绝对路径 - 窗口定位按
screen.getPrimaryDisplay().workArea实时计算
系统通知
dsh-pet 自带系统通知(对话完成 / 生成失败 / 输出截断 / 权限申请 / 需要你选择),
在窗口失焦时弹到右下角。
如果觉得烦,改 $DSH_HOME/dsh-pet/main-config.jsonc 里的 notificationsEnabled 为 false。
这是总开关,一次关掉全部通知。本插件没有自己的通知,也不改这个键。
隐私与数据处理(请务必读完这一节再安装)
这个插件会截你的整个主屏幕。截图里可能包含聊天记录、密码框、别人的隐私。请确认你能接受这几点。
截什么、存在哪
- 截的是主显示器整屏,按你设的间隔抓,存到
$DSH_HOME/dsh-pet/screenshots/下的 PNG - 默认只在本地落盘,插件不往任何服务器上传
- 文件数量按
screenshotKeep滚动,默认只留 60 张;随时可以手动删目录
什么时候数据会离开你的电脑
只有一个条件:你主动打开 screenshotEnabled(默认关闭)。
打开之后,每次产生新截图,插件会把它交给你自己在 DSH 里配置的模型分析一次。这意味着:
- 图片内容会作为请求发到那个模型的提供方(DeepSeek 或你配置的其它 provider)
- 用的是你自己的 API Key,计费和授权都由你承担
- 插件不经过第三方中转,没有任何隐藏上报端点(可自行
grep验证:全文只有本地127.0.0.1和宿主提供的ctx.llm调用)
不想让图离开电脑,就保持 screenshotEnabled: false。 此时截图只躺在本地磁盘里。
它还会写什么
| 路径 | 内容 | 控制 |
|---|---|---|
$DSH_HOME/dsh-pet/screenshots/*.png |
原始截图 | screenshotKeep 控制张数 |
$DSH_HOME/dsh-pet/视野/index.jsonl |
每次看图的索引:时间、原图路径、体积、会话的原始分析 | 保留 7 天,DELETE /sights 清索引 |
$DSH_HOME/dsh-pet/视野/YYYY-MM-DD/*.png |
当时的原图(整屏无损) | 超过 7 天的目录自动删 |
$DSH_HOME/dsh-pet/memory.json |
dsh-pet 自己的对话记忆,本插件不再往里写看图记录 | — |
$DSH_HOME/dsh-pet/extras-config.json |
插件自己的配置 | — |
$DSH_HOME/dsh-pet/look-rules.txt |
自学习写出的规则(没开自学习就没有这个文件) | lookLearnEnabled |
看图播报只有一条路:截图投给主会话。
| 投什么 | 主会话能看到什么 | 花多少 | |
|---|---|---|---|
(早期版本的 sightingInject,已删) |
一句话 | 转述(只知道「在刷抖音」,没看见画面) | 很少 |
| 现在的看图播报 | 截图本身 | 真的看见,能自己判断、能记进对话 | 每投一次约 7.6 万 token(其中 97% 是那条会话的历史上下文,图本身只占 1~2K) |
投之前有两道闸:距上次投图超过 sightShareMinGap 秒 + 本地判定这一屏跟上次不一样(像素对比,变化格占比 < 5% 就整轮跳过,不进会话也不出声)。
为什么没有「投图开关」了:早期有个
sightShareEnabled只管「投的时候带不带图」,关掉就变成「发一句话、不带图」——主会话收到「你抬头看一眼屏幕」却看不见屏幕,只能凭空编。开关合并后:要播报就是投图,不播报就别投,统一由screenshotEnabled决定。sightShareMinGap仍然留着,因为「多久看一次」是个真需要调的旋钮。
迁移说明:早期版本的
sightingInject/sightingKeep/lookPersonaOwner/sightShareEnabled四个键已经删除,screenshotWatchEnabled也随开关合并一起删了(2026-10-24:以前是「定时截屏」+「定时看图」两个开关,用户分不清该开哪个,现在合成一个screenshotEnabled,开 = 截屏 + 投图,关 = 全停)。老配置文件里留着也不会报错——插件启动时会自动把它们擦掉。
⚠️ 成本估算:每次约 7.6 万 token(见上表)。按间隔 120 秒、连续用 8 小时算,约 240 次 ≈ 1800 万 token;按 60 秒算翻倍到 ≈ 3600 万。这是「按小时」的实打实开销——间隔调大一倍,钱就省一半,比任何图片压缩都管用。
权限说明(供安全扫描参考)
- 用
child_process.spawn启动一个 Electron 子进程跑截图 helper。启动的只有本插件包内的固定文件runtime/electron-helper/main.cjs,不接受任何外部传入的命令或参数,不存在远程命令执行 - helper 自己起一个
127.0.0.1随机端口的 HTTP 服务,只监听本机回环地址,不对局域网开放 - 通过
inject申请的宿主服务是功能必需:attachments(存图)、llm(分析图)、agents(注入会话)、webServer(提供接口) - 不读环境变量里的密钥,不写注册表,不安装任何依赖,没有任何 postinstall 脚本
已知问题(老实说,这个版本不完美)
- 只在桌面模式(
display: desktop)有效。web 模式下没有 Electron 进程,截图功能不可用 - 改
sightShareMinGap之后自动生效——间隔一变 helper 自己重启,不用手动干预 - 接智能体是串行的:桌面宠物和你说话时,目标会话很可能正在忙(就是你自己正在用的那条),这时候桌宠要排队。聊天等空闲最多 90 秒,等不到就回一句「在忙」;看图只等 10 秒就当场让位(截屏 20 秒一张,等 90 秒会让攒下的图全被新图顶掉)。这是个设计取舍,不是 bug:想立刻有回应,就指定一条空闲会话
- 播报依赖
dsh-pet:没装或版本太老时,看图分析照跑但说不出话(返回pet-unavailable) 0.2.3版本,作者第一次用 GitHub 发布,代码里可能还有我没想到的边界情况。 遇到问题请提 issue,附上$DSH_HOME/dsh-pet/extras.log会好排查很多- 只测过 Windows + DSH 桌面版。其它平台理论可行(路径都走
DSH_HOME),但没有实测过
工作状态气泡与桌宠对话冲突
dsh-pet 投递消息时用的是普通用户消息形状(source.id 以 dsh-pet- 开头),宿主分不出「主人手敲」和「桌宠投的」,于是桌宠那几轮也会触发头顶气泡上的「思考中 / 工具调用」。
0.1.0/0.1.x 早期版本在别人的机器上报
anchor-miss:fence:它的锚点依赖一个只有我们本机那份被改过的dsh-pet才有的常量,官方 0.3.8 里根本没有。现已重写成 v2,锚点全部落在官方原版代码上,并在干净的 0.3.8 副本上跑过 12 项安装测试 + 10 项行为测试。
这个插件会改 dsh-pet 的文件——两段补丁,说清楚改了哪、为什么
先声明:本插件会修改你装的 dsh-pet 的一份源码文件(node_modules/dsh-pet/lib/index.js)。这一节把所有改动摊开讲。
为什么非得改它:工作状态(头顶那个「思考中 / 工具调用」气泡)是 dsh-pet 自己算的,它监听宿主所有会话事件,按事件类型往上翻状态。桌宠投递的消息形状是 {kind:"user", id:"dsh-pet-<时间戳>"},跟主人手敲结构上完全一样,dsh-pet 自己分不出来,于是桌宠那几轮也会被当成「正在干活」,气泡乱跳。插件这边没法拦——事件是宿主发给 dsh-pet 的,插件不在那条链路上。
一共两段补丁:
- 补丁 A|工作状态守卫(4 处插入)——不让桌宠的轮次点亮「思考中 / 工具调用」气泡
- 补丁 B|接智能体钩子(2 处插入)——让桌宠说的话能交给主会话回答
补丁 B 为什么必须有:插件改不到 dsh-pet 的 chatWithPet,所以聊天那条路只能在 dsh-pet 里留一个调用点。这个调用点做的事情非常小——把一句话 POST 给插件,插件回过来说了话就拿回来念。插件没装 / 补丁没打上 / 开关关着,dsh-pet 的行为跟官方原版一模一样(钩子返回空,直接走原来的 generateChat)。
补丁 A 的具体位置(4 处插入,全部锚在官方 0.3.8 的原有代码上):
| # | 锚点(官方原版就有) | 干了什么 |
|---|---|---|
| 1 | const dispose = ctx.on("session/event", …) 之后 |
新增 petPending / petTurn 两个记账变量 |
| 2 | if (type === "user/message") { 分支 |
认出桌宠投的消息(source.kind 或 source.id 以 dsh-pet 开头)就打标记;排除 runtime-context / time-context / dsh-mnemon 三种宿主注入后清标记 |
| 3 | if (type === "turn/start") { 分支 |
把「这一轮是桌宠发起的吗」记到一起,跟着排队语义走 |
| 4 | const next = reduceWorkStatus(…); 之前 |
桌宠那一轮的事件直接 return,不进工作状态;turn/end 时清标记并 workStatus.clear |
补丁 B 的具体位置(2 处插入):
| # | 锚点(官方原版就有) | 干了什么 |
|---|---|---|
| 1 | const chatWithPet = async (petId, text 那一行之前 |
插入 bridgeChatViaExtras():把话 POST 给插件的 /dsh-pet-extras/agent,返回会话的回答;连不上就返回空 |
| 2 | const generated = await generateChat(…) 那一行之前 |
先问插件;插件管就直接念答案(并且补写气泡,不然会话答了桌宠不出声);插件不管就往下走原来的 generateChat |
补丁 B 的锚点只认前半段(const chatWithPet = async (petId, text),因为官方 0.3.8 的签名是 (petId, text)、没有 images 参数。只看前缀是为了兼容带不带 images 的各版本。
没改的地方(一并说清):消息投递、截图、缩图、视野档案、会话内容、任何数据文件——一律不碰。补丁只动「事件该怎么算工作状态」这一件事。
怎么保证不打坏:
- 只在新版本第一次启动时动手,打完记进
$DSH_HOME/dsh-pet/extras-state.json,版本没变就不再介入 - 状态里还记了「补丁方案代号」(现在是
ws+bridge):方案升级(比如以后加了第三段补丁)会自动重跑一次,不会因为版本号没变就漏打 - 两段补丁各自独立判断:只缺一段就只补那一段,不会重复插
- 打之前备份成
lib/index.js.bak-workstatus-guard(只备份一次,留最原始那份) - 只在
0.x系列上打;结构大改导致锚点对不上就放弃并记日志,绝不硬改 - 改完强制校验指纹齐全(补丁 A 4 处、补丁 B 2 处),少一处就整体放弃、不写文件
- 改的是磁盘源码,下次启动 DSH 才生效
想撤销:
# 用备份覆盖回去,再删掉状态记录,下次启动就不打了
cp "$DSH_HOME/profiles/desktop/node_modules/dsh-pet/lib/index.js.bak-workstatus-guard" "$DSH_HOME/profiles/desktop/node_modules/dsh-pet/lib/index.js"
然后删掉 $DSH_HOME/dsh-pet/extras-state.json 里的 petWorkStatusPatch 那段(文件里只有这一项,直接删文件也行)。
不想让它改? 卸载本插件(或在 extras.log 里看补丁是否 anchor-miss 主动放弃)即可。如果自动补丁没生效,extras.log 里会有 dsh-pet 补丁没打上(原因) 这样一行说明原因。常见原因:anchor-miss:*(新版 dsh-pet 结构变了)、series-mismatch(不是 0.x)。
卸载
dsh plugin remove dsh-pet-extras
然后重启 DSH。
卸载不会自动删你的数据,想清干净就手动删这几个(都在 $DSH_HOME/dsh-pet/):
| 路径 | 是什么 |
|---|---|
screenshots/ |
截图 PNG,可能很大 |
视野/ |
视野档案:原图 + 会话原始分析(按天分目录) |
extras-config.json |
插件配置 |
extras.log |
运行日志 |
extras-state.json |
补丁器记录(哪个 dsh-pet 版本打过补丁) |
look-rules.txt |
自学习写出的规则 |
extras-state.json |
插件自己的状态(目前只记 dsh-pet 补丁打到哪个版本了) |
memory.json 是 dsh-pet 自己的,别删——里面是桌宠的对话记忆,跟本插件无关。
常见问题
看不见设置项?
本插件有自己的设置页:设置 → 桌宠扩展(dsh-pet-extras 提供的「看屏幕」开关和间隔;没装 dsh-pet 时这一节不显示)。
先分清是哪种「看不见」:
| 现象 | 原因 | 怎么办 |
|---|---|---|
| 设置里连「桌宠扩展」这一节都没有 | 插件根本没被加载(最常见:bundles 没加) |
翻回上面的 「⚠️ 手动安装」 那节,检查 dsh.profile.bundles 里有没有 dsh-pet-extras,改完重启 |
| 有「桌宠扩展」这节,但找不到「接哪条会话」 | 它在本插件设置页里,往下滚就能看到 | 设置 → 桌宠扩展,在「看屏幕」下面选一条会话;不选就不投 |
| 开关显示「关」,点开保存后又变回关 | 配置文件写不进去,或读的是另一份 $DSH_HOME |
看 $DSH_HOME/dsh-pet/extras-config.json 里的实际值;C 盘 C:\Users\<你>\.dsh 和 D 盘 D:\dsh-home 可能是两份不同的 home |
这些键只存在于本插件的
extras-config.json,跟dsh-pet的设置页没有任何关系(早期版本说「同一个键」是错的,相关代码已删除)。
开了「看屏幕」,截图目录没动静?
确认三点:①桌宠的显示模式是 desktop(web 模式没有 Electron 进程);②「接哪条会话」选了一条正在运行的会话——没选就不投;③GET /dsh-pet-extras/screenshot 返回的 enabled 是 true。间隔改完会自动重启 helper,不用手动干预。
两次播报间隔比设的时间长? 正常。设置的是闸门不是节拍器,实际会比设定值多出 5 秒到 1 个间隔。详见上面「看图播报」一节的 「实际节奏」。
桌宠说话很久才回,或者干脆回「在忙」? 接智能体是串行的:目标会话正在跑任务时,桌宠得排队。聊天等空闲最多 90 秒;看图只等 10 秒(截屏是 20 秒一张,等 90 秒会让中间攒的图全被新图顶掉,等于盯着屏幕什么都没看见)。想让它立刻有回应,就把目标会话改成一条空闲的。
清空了「接哪条会话」,图还在往旧会话里投?
0.2.0 早期版本有这个坑:配置里同时存着 targetSessionId(id)和 agentBridgeTarget(标题),只清 id 的话标题会静默接管,照样按标题匹配。现在改掉了——targetSessionId 这个键一旦存在(哪怕值是空串),标题就彻底退出匹配。如果你的配置是从老版本升上来的(文件里根本没有 targetSessionId 这个键),标题仍然会当兜底用,那是为了迁移;随便在设置页保存一次就会写上这个键,之后标题就退休了。
换了显示器 / 改了缩放,宠物窗口又跑到屏幕外了?
默认插件不管这事。想让它管,调 PUT /dsh-pet-extras/confine 传 {"value":true}——这样会置上 confineManaged,之后插件启动和改配置时才下发。不调它就完全不碰 dsh-pet 的 confineToScreen。
看图播报有一阵子不说话了? 这是故意的。模型偶尔会吐出「屏幕上是一张截图,画面里有…」这种说明书体,插件会把这句丢掉不播,而不是念给你听。连续几次都不说话,说明那段时间屏幕内容确实不好说人话。
换了显示器 / 改了缩放,宠物窗口又跑到屏幕外了?
把 confineToScreen 打开,插件会在启动时和每次改配置时把它下发到 dsh-pet。
致谢
- 桌宠本体来自 PC2005-cloud/dsh-pet(MIT)。本项目只是调用它的公开 HTTP 接口,没有复制它的代码。桌宠可爱是作者的功劳,本插件只是给它加了几件事
- 宿主框架 DeepSeek Harness
许可
MIT,见 LICENSE。
本项目与 dsh-pet 的作者没有隶属关系,也不代表其立场。dsh-pet 的版权归 PC2005-cloud 所有。
No comments yet. Be the first to write one.