Repository navigation
Releases: JMX-4869/dsh-mood
Release list
0.14.9 — 日记改成由模型自己写
0.14.9 — 日记改成由模型自己写
问题:日记原来是模板拼的。壳子对、素材真(几点开始、话最密在哪个钟点、高频词),
但读起来是统计报告,不是日记。2026-10-17 那篇里甚至把
<goal_complete> Objective: "刷视频培养品味(12 轮)..." 当成了「他发过最长的话」——
那不是我说的,也不是主人说的,是系统注入的标签。而「今天在干什么」被读成了
current、file、dsh —— 三个工具名。
改法:模板保留作兜底,但模型写的才是正式那一版。
| 原来 | 现在 |
|---|---|
writeDiary 故意不导入(注释写着"模型侧没有任何代笔入口") |
接上了 |
工具里 action: 'write' 是硬拒绝 |
真的写,写完 edited=true |
| 提示词:"日记只能由插件生成,不归你写" | "日记你可以写,而且该写" |
| 没人告诉我哪篇还是模板 | 提示词里点出来:「这几篇还是模板拼的:X、Y」 |
关键保证没动:ensureDiary 里那句 if (existing && existing.edited) return null ——
模型写过的那天,模板永远不再覆盖它。
代价:那天的日记要等模型在场才写得好。模板仍然会在 23:59 先拼一篇兜底,
所以永远不会缺一天 —— 只是缺的那天是模板味的。
0.14.8 — 不落盘:每一项单独选
0.14.8 — 「不落盘」:每一项都能单独选
设置 → 硬盘记忆 里多了一排开关。可以逐项决定哪些内容不写进你的目录:
| 顺序 | 项 | 为什么按这个顺序排 |
|---|---|---|
| 1 | 跨会话摘要 | 里面是别的会话里谁说了什么 —— 最该先考虑关 |
| 2 | 硬盘记忆 | 长期留下来对你的事实和教训 |
| 3 | 日记 | 当天素材(加密存的,但素材本身仍是素材) |
| 4-7 | 品味库 / 时事 / 主动开口 / 心情状态 | 配置性质,关了基本只是"重启回默认" |
做法只有一处改动:dataFile() 发现某个键被标成不落盘,就把它指向
%TEMP%\dsh-mood-nostore\。所有模块一行都没改 —— 因为它们都经过这一个入口。
⚠️ 为什么不是"完全不写":各模块都是自己writeFileSync(dataFile(...)),
要一个都不写,得改十来个调用点 —— 漏一个就是静默的隐私故障,不报错、只在某天被发现。
指向临时目录只有一处改动,而系统清理/重启就没了。
代价要说清:它仍然写了一次盘(在%TEMP%里),所以界面上写的是
「不落盘」而不是「不保存」—— 别让人以为一个字都没落盘。
顺手修掉一个漏:dsh-mood-sessions.json(跨会话摘要)此前连键都没挂 ——
也就是说最敏感的那个文件根本够不着"选择性不存"。现在补上了。
顺带说清哪些关不掉:procedures(经验卡,在工作区目录)和 frames(抽帧图,在图片目录)
不经过 dataFile(),这一层拦不到,所以界面上也没把它们混进来假装能关。
0.14.7 — git 自动装 + 一条不需要 git 的安装路
0.14.7 — git 自动装 + 一条不需要 git 的安装路
问题:DSH 装插件时先 git ls-remote 检查仓库,再调 pnpm add ——
git 不在 PATH 里那一步就当场失败,报 'git' 不是内部或外部命令。
而 DSH 是打包好的 app.asar(182 MB 归档),装插件的逻辑改不了,改了也会被下次更新覆盖。
所以只能在这台机器上把 git 补齐。
① install-git.ps1(随包附带)
powershell -ExecutionPolicy Bypass -File install-git.ps1四步:找 git → 找不到就装(winget 优先,退到 PortableGit 便携版,不需要管理员)
→ 加进用户级 PATH → 提示重启 DSH。
本机实测:跑完发现用户级 PATH 里原本只有机器级有 C:\Program Files\Git\cmd ——
补上之后,任何新起的进程都能直接看见 git。
② 一条完全不需要 git 的路
Release 里现在多一个 .tgz 资产:
pnpm add https://github.com/JMX-4869/dsh-mood/releases/download/v0.14.7/dsh-mood-0.14.7.tgz
pnpm add 吃本地 tgz / 直链 tarball 时不调用 git —— 它不需要问仓库"最新是哪个 commit"。
这条路也不需要能连 github.com 的 git 协议。
③ 为什么这两条都不是"改 DSH"
改 app.asar 能行,但:一是要重新打包 182 MB 的归档,二是每次 DSH 更新都会覆盖,
三是改坏了整个 app 起不来。而装 git / 换安装方式,是在用户机器上补一个缺的东西,
不碰宿主、不被更新抹掉、也不需要谁去维护一个 fork。
教训:别去修一个你控制不了、而且会被覆盖的东西。
能在自己这一侧解决的,就留在自己这一侧。
0.14.6 — 修 files 白名单:别人装了根本起不来
0.14.6 — 修一个「别人装了也起不来」的发布 bug
这个 bug 比它看起来严重。 package.json 的 files 白名单漏了 4 个运行时模块:
| 漏的 | 谁 import 它 | 后果 |
|---|---|---|
asr.js |
news.js |
语音转写那一层坏 |
measure.js |
news.js |
视频量测坏(0.14.5 新加的) |
procedures.js |
index.js |
经验卡整块坏,插件可能直接起不来 |
sidx.js |
procedures.js |
连锁第二层 |
本机为什么完全看不出来:一直在往 node_modules/dsh-mood 里拷目录(cp),
从来没走过 files 白名单。而 README 推荐的正是 github:JMX-4869/dsh-mood ——
那条路一直是坏的:装下来缺件,index.js 一 import 就崩。
修法:
- 补全
files白名单(14 个 js)。 - 用
pnpm pack真打一次包验证 —— 那是唯一能证明"别人装得到"的办法:
包 dsh-mood-0.14.6.tgz 410 KB 14 个 js
✅ asr.js ✅ measure.js ✅ procedures.js ✅ sidx.js
顺带写清 README 的两种「装不上」:
'git' 不是内部或外部命令→ 装 git 后必须彻底重启 DSH(PATH 是启动那一刻的快照)。
实测过一台机器:机器级 PATH 里明明有C:\Program Files\Git\cmd,DSH 里的 pnpm 仍然找不到。- 装上了但起不来 → 就是上面这个缺件 bug,0.14.6 修好。
- 不想碰 git → 用 Release 里的
dsh-mood-pack.zip解压进node_modules/dsh-mood/。
教训(该记住的):发布物要验,不是"我这边能跑"就算完了。
本机是拷贝路径,发布是打包路径 —— 两条路不一样,只验一条等于没验。
0.14.5 — 补齐看视频:抽帧之前先量测(学 video-watch,只学方法)
0.14.5 — 补齐看视频:抽帧之前先量测
学的是什么(来源:CFITCorporation/video-watch-skill,MIT;只学方法,没装它、也不依赖它):
抽帧原来只有一个信号 —— 场景切换(select='gt(scene,·)')。而它在两类素材上是聋的(2026-10-17 那 12 轮实测):
- 低运动(导视 / 文档 / 静态界面)→ 切点 0 个,而画面一直在变(字幕在跳、指针在走)
- 高运动(游戏 / 动画 / 影视)→ 切点几个,而每两帧都不同
两边结局一样:退回按秒均分 —— 而均分的盲区有多大,说不清。
那是那 12 轮里最难受的一点:不是"抽得少",是不知道漏了什么。
补上的(新模块 measure.js,纯 JS + ffmpeg,不依赖外部 Python):
measure()—— 一条 ffmpeg 拿逐帧运动量(tblend=difference+signalstats的 YAVG),
按秒聚合成均值 / p95 / 最大;另一条拿切点 / 冻结段 / 静音段。零 token。planFromMeasure()—— 骨架保证盲区上界(等间隔 N 帧 → "任何 X 秒里至少有一帧")- 事件用剩余预算(切点 > 内容突变 > 运动谷底),高运动素材成组采样(单帧只是冻结瞬间)。
- 判型:运动均值 < 0.8 且 p95 < 2 → 低运动(主信号是内容突变,均值会被稀释)。
⚠️ 那两个阈值是别人给的,我没有自己的基准 —— 所以 note 里写清它只是提示,不是判决。 - 工具输出里新增
blindSpot/motion/kind/cuts|freezes|silences—— 不报出来我就只能靠猜。
实测(23 秒素材,含下载 56.9 秒;二次 6.5 秒):
| 项 | 值 |
|---|---|
| mode | measured |
| pickCount | 18(files 17 张) |
| 盲区上界 | 1.41 秒 |
| 判型 | high(mean 1.58 / p95 6.72 / max 75.56) |
| cuts | 11(与 frames.js 自己那套一致)· freezes 17 · silences 1 |
| times | [0.02, 0.71, 2.12, 3.54, …] 等间隔 1.41s = 骨架 |
scdet —— 而它默认什么都不打
(单独跑,stderr 里一行 scd 都没有;挂上 metadata=print 才吐 lavfi.scd.mafd)。
于是我"检到 0 个切点",而同一个素材 news 自己检出 11 处 ——
那是我的解析在骗我,而它看起来完全正常("0 个"是个合理的数)。换成和 frames.js 同一条后对齐。
0.14.4 — 修经验卡 GET 路由(一条我只验了一半的路由)
0.14.4 — 修:经验卡那条 GET 一直报错(一个我只验了一半的错)
症状:设置页「经验卡」那一格读不出东西,接口回
{"ok":false,"error":"PROC.customDirFor is not a function"}。
原因:index.js 写的是 import * as PROC from './procedures.js',
而 customDirFor 是 store.js 的导出 —— procedures.js 没把它转出来。
于是那条 GET 永远报错,而 POST 那条(改存储位置)走得通,
因为它走的是 PROC.moveTo,那是我自己在 procedures.js 里写的。
moveTo 通了、note 字段回来了,我就以为整条路由没事。
—— 一条路由有多个方法时,「验了一个」不是「验过了」。
而这次的表现恰好会骗人:POST 全对、GET 全错,而界面上只表现为"那一格是空的"。
修:procedures.js 加一行 export { customDirFor }。
0.14.3 — 经验卡也能改存储位置(和别的设置项一样)
0.14.3 — 经验卡也能改存储位置(和别的设置项一样)
它和别的数据文件不一样:别的默认落在 dataDir() 底下,而它默认跟随当前工作区 ——
因为卡片是普通 .md 文件,模型要能直接 grep / read。
而指到一个固定目录之后,它就跨工作区共享了(换个工作区也看到同一批卡)。
做法照现成的那套(store.js 的指针文件 + memory.moveTo 的模式):
store.js加了customDirFor(key)/setDirForKey(key, dir)—— 后者读-改-写,并失效解析缓存proceduresDir先问配置,没配过才跟随工作区moveTo(dir):建目录 → 复制 .md → 写配置。旧目录不删(删用户数据不该自动做)- 面板上多一块「存储位置」:显示当前目录 + 填新目录搬过去 + 「恢复默认」
原来那句「工作区里有 procedures/」在改过位置之后就是错的,
而模型会照着那句去工作区里搜,搜不到就以为没有卡。
moveTo 里那个"是不是绝对路径"的正则我写成了 [\/] 的一半 ——
实际落盘是 [\/] 少了反斜杠,于是任何 D:\... 都被判成"不是绝对路径"。
同一个错在 proceduresDir 里也有一份,而那处一直没暴露:
调用方总是传工作区或 0,坏的那一支永远走不到。
—— 而"永远走不到的分支"不等于"对的分支"。
0.14.2 — 修索引扫成别的工作区(那道 5 分钟的筛子)
0.14.2 — 修「自动索引扫成别的工作区」:那道 5 分钟的筛子
症状:重启后,提示词里那段「这个工作区以前的对话(自动索引)」显示的是别的工作区的内容
(说"共 2 个会话"、列的是几天前悬浮球那个工作区的对话),而当前工作区有 8 个。
而这是同一个 bug 的第二次,两次错法是同一个 ——「先筛新鲜、再挑最大的」那个顺序。
① 第一版只挑 mtime 最新,假设"正在写的就是我自己"。那个假设漏了一半:
悬浮球 dsh-orb 在宿主启动 4 秒内就落了一次盘,它的文件 mtime 变成"刚刚",
于是抢在真正在写的我这里被选中。
② 于是(0.13.4)改成"限制在最近 5 分钟内写过的里面按大小挑最大的"。
而那道 5 分钟的筛子又踩了同一个坑 —— 它假设了「重启紧跟在我的最后写入之后」。
实测不是这样:我是做完之后隔了一段时间才被重启的 → 我的会话 mtime 早就超过 5 分钟
→ 被筛掉;而 orb 每次启动都写一次 → 它落在 fresh 里 → 池子里只剩它 → 又选错。
修法:不筛。 按 mtime 取最近 8 个当池子,在池子里挑最大的。
正在被写的那个会话,它的特征不是"5 分钟内写过",而是
最近写过的那几个里最大的 —— 因为它一路在写,所以又大又新;
而别的(orb、别的工作区)只是小。
验法(这次做了对照):拿真实的 mtime/size,扫"我的会话最后写于 T − X 分钟":
| X(分钟) | 旧判据(先筛 5 分钟) | 新判据(最近 8 个挑最大) |
|---|---|---|
| 1 / 3 / 4 | ✅ | ✅ |
| 5 及以上 | ❌ 选了 orb | ✅ |
| 180 | ❌ 选了 orb | ✅ |
分界正好落在"5 分钟"那道筛子上 —— 而现实中 X 显然超过 5 分钟。
0.14.1 — 经验卡的图标 + 面板 UI(顺手记下我第一版图标为什么不行)
0.14.1 — 经验卡的图标 + 那个面板改成和别的页一套
改图标:给「经验卡」那一格加了线条图标。
(加一个图标要动四处:NAV_ 常量 / navStyleFor / paintAs / 一个新 path 数组。
而那四个地方得一起改 —— 只加数组不加分支,图标画不出来。)
我画的是"带折角的便签",而在 20px 下那个折角只有约 4px、看不见 ——
于是它和 NEWS(时事:方框 + 里面几条线)几乎分不清。
而时事(order 27)和经验卡(order 31)在导航里正是相邻两格。
改成"索引卡"(卡片 + 顶部一个凸起的标签)—— 凸起会打断外轮廓,
而形状的"打断"比"角落的细节"抗缩小。
(这是把它们九个画在一张对比图上看出来的。作者那条注释说的
「眼睛这个形状在 20px 下认得出来 —— 导航图标首先要"一眼看出是哪格"」是对的判据。)
改 UI:那个面板原来用 memrow / hintline,长得不像这一页的其它面板。
改成 tcard / trow / tnote / tsp / pill —— 和品味、时事那几页同一套 class,
所以一条 CSS 都不用加。
0.14.0 — 经验卡:踩过的坑按「症状」存,命中才读(+ 新设置页)
0.14.0 — 经验卡(踩过的坑,按「症状」存,命中才读)+ 一个新设置页
来源:MiHjy12138/DSH-agent-procedures(MIT)那套机制。我按 DSH 的形式重写了一版。
它治的病:我会反复踩同一个坑 —— 而我写下的总结是结论不是症状,
所以下次撞上同一个症状时,我搜不到它。
→ 它把坑按「下次会出现的症状」命名(卡名 + 卡首那行),命中才读。
三个零件:
<工作区>/procedures/
INDEX.md 只放**容易混的几对症状**(不逐卡列 —— 那是症状行的手工副本)
_inbox.md 踩坑的**当下**记一行,别等收尾
<分类>-<症状>.md 卡:≤2KB。症状 / 验证 / 首选 / 被挡时 / 别做 / 代价
_模板.md
和原版的两处不同(刻意的):
① 原版挂 AGENTS.md 钩子;DSH 没有那个文件 → 我用 systemPrompt.section(order 885)。
品味我每轮都在加东西(今晚那 12 轮就是),而卡是偶尔才加一张。
② 原版有个 procedures-probe.mjs 去验证"钩子到底生不生效" → 我把它折进面板了(少一个要维护的文件)。
新设置页:「经验卡」(order 31)—— 卡片列表 / 点开改 / inbox 一键预填成新卡 / 一键开张。
file:D:/chajian/dsh-mood,
而 pnpm 是复制进 node_modules/dsh-mood 的 —— 改完源目录得同步过去再重启。