dsh-daughter 0.1.0 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +151 -12
  2. package/bin/connector-token.mjs +78 -0
  3. package/config.yaml +141 -1
  4. package/dist/config.js +234 -4
  5. package/dist/host.d.ts +2 -0
  6. package/dist/host.js +241 -19
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.js +223 -19
  9. package/dist/runtime/connector-auth.d.ts +56 -0
  10. package/dist/runtime/connector-auth.js +148 -0
  11. package/dist/runtime/content.d.ts +11 -2
  12. package/dist/runtime/content.js +36 -6
  13. package/dist/runtime/desktop-input.d.ts +2 -0
  14. package/dist/runtime/desktop-input.js +3 -1
  15. package/dist/runtime/desktop-observe.d.ts +16 -0
  16. package/dist/runtime/desktop-observe.js +30 -8
  17. package/dist/runtime/douyin-link.d.ts +49 -0
  18. package/dist/runtime/douyin-link.js +102 -0
  19. package/dist/runtime/expression.d.ts +60 -0
  20. package/dist/runtime/expression.js +113 -0
  21. package/dist/runtime/filter.js +23 -0
  22. package/dist/runtime/maintenance.d.ts +0 -5
  23. package/dist/runtime/maintenance.js +59 -8
  24. package/dist/runtime/memory.d.ts +34 -2
  25. package/dist/runtime/memory.js +130 -18
  26. package/dist/runtime/motivation.d.ts +65 -0
  27. package/dist/runtime/motivation.js +282 -0
  28. package/dist/runtime/panels.js +34 -1
  29. package/dist/runtime/personality.js +3 -11
  30. package/dist/runtime/pet-link.d.ts +87 -0
  31. package/dist/runtime/pet-link.js +129 -0
  32. package/dist/runtime/reset.d.ts +40 -0
  33. package/dist/runtime/reset.js +253 -0
  34. package/dist/runtime/speak.d.ts +79 -0
  35. package/dist/runtime/speak.js +277 -0
  36. package/dist/runtime/state.js +66 -9
  37. package/dist/runtime/vpet-control.d.ts +2 -0
  38. package/dist/runtime/vpet-control.js +5 -3
  39. package/dist/store.d.ts +63 -1
  40. package/dist/store.js +233 -14
  41. package/dist/types.d.ts +253 -0
  42. package/dist/util.d.ts +10 -0
  43. package/dist/util.js +37 -0
  44. package/package.json +9 -2
package/README.md CHANGED
@@ -27,9 +27,20 @@
27
27
  └── DshDaughter\
28
28
  ├── info.lps # MOD 清单(vupmod#DshDaughter / author / gamever 11074)
29
29
  └── plugin\
30
- └── DshDaughter.dll # .NET 编译产物
30
+ ├── DshDaughter.dll # .NET 编译产物
31
+ └── token.txt # 连接器令牌(v0.6.1 起必需,见下)
31
32
  ```
32
33
 
34
+ **令牌怎么拿**:插件端点不再接受"本机来的就放行"(详见设计文档 §12.2)。先让插件跑起来一次(会生成 `brain/brainstem/connector_token`),然后:
35
+
36
+ ```bash
37
+ npm run connector:token -- --write-to "<VPet 安装目录>\mod\DshDaughter\plugin"
38
+ # 从 npm 安装的用户:
39
+ npx dsh-daughter-token --write-to "<VPet 安装目录>\mod\DshDaughter\plugin"
40
+ ```
41
+
42
+ 也可以手动把 `brain/brainstem/connector_token` 的内容复制成 `plugin/token.txt`,或设置环境变量 `DSH_DAUGHTER_TOKEN`。令牌缺失时 MOD 仍会启动,但请求会被 401 拒绝,日志里会写明原因。
43
+
33
44
  **怎么编译**(MOD 源码在 `connectors/vpet/mod-src/`,目标框架 `net10.0-windows`):
34
45
 
35
46
  ```powershell
@@ -74,7 +85,7 @@ dotnet build -c Release -p:VPetDir="E:\SteamLibrary\steamapps\common\VPet"
74
85
  | --- | --- | --- |
75
86
  | `connectors/android` | ADB | 受限动作 + 前台事件,需 `adb` 可用 |
76
87
  | `connectors/desktop` | `127.0.0.1:47614` | 窗口/前台/媒体查询、截图、抽帧、ffmpeg 推流、输入注入 |
77
- | 外部消息通用入口 | `POST /connectors/<source>/ingest` | QQ / 抖音 / Minecraft / 单机暂走这里,来源白名单校验 |
88
+ | 外部消息通用入口 | `POST /connectors/<source>/ingest` | QQ / Minecraft / 单机暂走这里,来源白名单校验;抖音改走共享记忆只读拉取(见「抖音联动」) |
78
89
 
79
90
  ## 能力
80
91
 
@@ -84,6 +95,8 @@ dotnet build -c Release -p:VPetDir="E:\SteamLibrary\steamapps\common\VPet"
84
95
  | 记忆 | 摘要层 + 全文层分离;SQLite FTS5 trigram + Dice 双路;五维加权(语义/近因/权重/情绪/链接);一跳二跳联想 + 概率联想;衰减、归档、情绪历史合并 |
85
96
  | 认知滤网 | 写入前四类裁决(屏蔽 / 丢弃 / 合并 / 放行)+ 来源可信度降权 + 显著性 + 习惯化签名 + 定期忘却;存量治理脚本 |
86
97
  | 技能与资产 | `draft → active → frozen → deprecated`;按技能形态自动固化为 MAC / CON / CMB / MOT,失败两次自动回退 |
98
+ | 行为动机 | 人格双通路(降阈值 + 方向舵)、熟悉度倒 U、记忆三路径、情绪覆盖、情境乘子;分数与阈值比较后给出 `act / hold / avoid`;失败熔断;零信号恒不触发 |
99
+ | 主动开口 | **默认关闭**。开启后按思念/人格/情绪/关系决定要不要自己开口;欲言又止会攒劲;安静时段、冷却期、日上限兜底;模板文案经可插拔出口投递到桌宠气泡 |
87
100
  | 反射 | 关键词 / 正则 / 状态 / 事件四类匹配,毫秒级,不调用模型(短路默认关闭) |
88
101
  | 安全 | 三档降级 + 滞回 + 写类工具熔断 + 输出侧诊断词检查;只描述强度与持续时间 |
89
102
  | 注入预算 | 硬上限 3000 tokens,按优先级裁剪,`constraint` 永不裁 |
@@ -98,10 +111,14 @@ dotnet build -c Release -p:VPetDir="E:\SteamLibrary\steamapps\common\VPet"
98
111
  仓库自带一份 format 1 索引:[`market/index.json`](market/index.json)。在 DSH 里打开 **插件 → 发现 → 索引源 → 添加**,填入:
99
112
 
100
113
  ```text
101
- https://cdn.jsdelivr.net/gh/han0han0le0ni/dsh-daughter@main/market/index.json
114
+ https://fastly.jsdelivr.net/gh/han0han0le0ni/dsh-daughter@main/market/index.json
102
115
  ```
103
116
 
104
- 之后在「发现」里搜 `daughter` 安装即可。索引里固定了 commit,装到的是可复现的版本。
117
+ (`cdn.jsdelivr.net` 同路径也可;部分网络下它会被重置,`fastly` 节点更稳。)
118
+
119
+ 之后在「发现」里搜 `daughter` 安装即可。索引里每个版本都钉死了 commit,装到的是可复现的版本。
120
+
121
+ > 社区目录(`dsh-plugin-catalog`,也就是「发现」里默认那份)收录的是 npm 上的包,本插件发布到 npm 之后会被抓取;在那之前,请用上面的自带索引源。
105
122
 
106
123
  **2. 直接从 git 装**
107
124
 
@@ -114,8 +131,8 @@ dsh plugin add git+https://github.com/han0han0le0ni/dsh-daughter.git
114
131
  ```bash
115
132
  npm install
116
133
  npm run build # tsc → dist/
117
- npm test # 112 项
118
- npm run check:link # 适配性 / 联动性自检,18 项
134
+ npm test # 211 项
135
+ npm run check:link # 适配性 / 联动性自检,19 项
119
136
  ```
120
137
 
121
138
  > 仓库里提交了 `dist/`:市场与 git 安装没有构建步骤,必须带一份可直接加载的产物。改了 `src/` 记得 `npm run build` 再提交(`prepare` 脚本也会兜一层)。
@@ -124,19 +141,50 @@ npm run check:link # 适配性 / 联动性自检,18 项
124
141
  - 运行时数据写到 `data_root`(默认 `.`),目录按脑区划分:`brain/hippocampus`、`brain/neocortex`、`brain/prefrontal` 等。
125
142
  - 桌宠是可选件:没有 MOD、没有 bridge、没有 `vpet` 配置时,插件行为与"完全不知道桌宠存在"一致。
126
143
 
144
+ ## 记忆体检
145
+
146
+ 记忆是「SQLite 索引 + 全文/归档文件 + 星图」三层,容易出现"表里删了、文件还在"这类对不上的状态。`npm run memory:doctor` 只读扫一遍,`--fix` 才动手修:
147
+
148
+ ```bash
149
+ npm run memory:doctor -- --root=<数据根> # 只报告
150
+ npm run memory:doctor -- --root=<数据根> --fix # 修可安全修的那几项
151
+ npm run memory:doctor -- --root=<数据根> --json # 给脚本用
152
+ ```
153
+
154
+ 检查项:SQLite 完整性、事件/归档计数、孤儿回看记录、FTS 索引是否真的查得到已有事件(外部内容表的 `COUNT(*)` 和 `integrity-check` 都看不出索引被清空,所以用真实标题做功能探针)、全文与归档文件的缺失/孤儿、字段越界、出边悬空/自环/重复/超过 12 条上限、星图边的引用一致性。
155
+
156
+ `--fix` 的动作都是可逆或安全的:孤儿文件移到 `logs/memory-doctor-trash/<时间戳>/`(不删)、缺失全文用摘要补一份、悬空边从出边里摘掉、越界值夹回 0..1、FTS 必要时 rebuild。
157
+
158
+ ## 重置数据
159
+
160
+ 清记忆 / 恢复出厂人格 / 清条件反射 / 清技能 / 恢复出厂设置,都走 `npm run reset`。默认只列清单(dry-run),加 `--apply` 才真的删,删完会读回真实状态复核,复核不过退出码非 0:
161
+
162
+ ```bash
163
+ npm run reset -- memory # 只报告:会清掉多少事件、回看、全文与归档
164
+ npm run reset -- memory --apply # 清记忆
165
+ npm run reset -- personality --apply # 人格恢复出厂 L1–L4
166
+ npm run reset -- reflex --apply # 反射规则与冷却状态
167
+ npm run reset -- skills --apply # 技能卡与动作模板
168
+ npm run reset -- all --apply --yes # 恢复出厂设置(多一道 --yes 确认)
169
+ ```
170
+
171
+ 可选:`--no-backup` 跳过备份、`--root=<路径>` 指定数据根。建议先停掉 DSH 再重置,否则插件会把内存里的状态写回去。
172
+
173
+ 被清掉的目录不会递归删除,而是整体移到 `logs/reset-trash/<时间戳>/`(想捞回来搬回去即可);备份在 `logs/reset-backup/<时间戳>/`。
174
+
127
175
  ## 目录
128
176
 
129
177
  ```text
130
- src/ 插件主体(21 个 TS 文件)
131
- runtime/ 记忆、人格、状态、安全、滤网、反射、可观测性
132
- scripts/ 自检与存量治理(link-check / filter-sweep)
178
+ src/ 插件主体(23 个 TS 文件)
179
+ runtime/ 记忆、人格、状态、动机、主动开口、安全、滤网、反射、可观测性
180
+ scripts/ 自检与存量治理(link-check / filter-sweep / reset / memory-doctor)
133
181
  connectors/
134
182
  vpet/ VPet MOD 源码(mod-src/,10 个 .cs + info.lps)与桥接脚本(bridge.mjs)
135
183
  pet-shell/ 独立 WPF 壳(读 VPet 素材,不启动游戏)
136
184
  android/ ADB 受限动作
137
185
  desktop/ 桌面观察与输入
138
186
  web/ 星图页面(纯静态)
139
- tests/ 14 个测试文件
187
+ tests/ 21 个测试文件
140
188
  docs/ 设计文档、实现核对、评估、检测报告
141
189
  ```
142
190
 
@@ -151,13 +199,104 @@ docs/ 设计文档、实现核对、评估、检测报告
151
199
 
152
200
  出生时五维按有界区间抽样,并把结果写入 `innate` 锚点;同角色同种子可复现。日常闲聊不动人格,只有累积/关键事件才按通道推动五维,任一项不超出锚点 `±0.6`。依恋底色出生只抽安全/焦虑/回避,混乱型只能靠经历演化。面板只展示证据与数值,不输出"你是 X 型"的结论。完整规则见设计文档 §4.8。
153
201
 
202
+ ## 行为动机
203
+
204
+ 决策层不再用"文本相似度过线"就执行技能,而是给每个技能算一个动机分,再和阈值比。两层结构,职责不重叠:
205
+
206
+ ```text
207
+ 阈值侧(够不够力) skills.min_main_score + 人格偏移 + 情绪偏移 → 不低于 min_threshold
208
+ 分数侧(想不想走) signal ×(熟悉度)×(记忆)×(情境)×(方向舵) → 0..1
209
+ 决策 熔断 → avoid;零信号 → avoid;score ≥ 阈值 → act;≥ 0.7×阈值 → hold
210
+ ```
211
+
212
+ **人格走两条独立通路**,这是这套设计的关键:
213
+
214
+ | 通路 | 参数 | 作用 | 类比 |
215
+ | --- | --- | --- | --- |
216
+ | 阈值偏移 | `trait_threshold_gain` | 五维按通道映射成阈值的升降 | 门有多重 |
217
+ | 方向舵 | `trait_signal_gain` | 五维按通道映射成分数乘子 `1±g` | 更想走哪条路 |
218
+
219
+ 只有阈值通路时,人格无法影响"几个候选技能里选哪个",只能影响"一个都别选"——所以补了方向舵。方向舵是**乘子不是加项**:`signal=0` 时乘什么都不会高于 0,零信号永远不会被凭空变成行动。
220
+
221
+ 其余因子:
222
+
223
+ - **熟悉度倒 U**:`exposure = uses/(uses+k)`,太少犹豫、太多厌倦,峰值在 `peak`。
224
+ - **记忆三路径**:结果预期(按 `lastUsedAt` 半衰)/情绪记忆(近 `lookback_days` 天相关事件的愉悦度)/自我效能(L3 自我价值),权重归一化。
225
+ - **情绪覆盖**:安全感低、压力高、心情低、疲劳高 → 更谨慎;高唤醒 → 更容易动。总偏移被 `clamp_min/max` 截断。
226
+ - **情境乘子**:关系深度 + 子人格披露度,社交通道吃满,其余通道折半。
227
+ - **失败熔断**:连续失败 `fail_streak` 次进入指数冷却,期间只 `avoid`,成功即清零。
228
+
229
+ 每个技能的因子都留在 `MotivationBreakdown` 里(`signal / personalityOffset / emotionOffset / threshold / familiarityGain / memoryGain / situationMultiplier / traitSignalGain / score / decision`),面板与日志可以逐项复算。`motivation.enabled = false` 时退回旧的相似度打分,便于对照。数值含义与取值范围见 `config.yaml` 注释。
230
+
231
+ ## 主动开口
232
+
233
+ 没人说话的时候,她会不会自己开口。这条链和上面的动机层是分开的:动机回答"这个技能该不该用",需要用户输入;主动开口回答"这会儿想不想说话",信号来自沉默时长而不是文本。
234
+
235
+ ```text
236
+ 强度侧(想不想) 思念累积 + 人格 + 情绪 + 关系 + 上一轮攒下的"憋劲"
237
+ 门槛侧(合不合适) 基础阈值 + 安静时段 + 压力
238
+ 决策 冷却期/日上限 → silent;urge ≥ 阈值 → act;≥ 0.75×阈值 → hold
239
+ ```
240
+
241
+ 拟人靠三件事:
242
+
243
+ - **思念有饱和**:`idle / (idle + 180min)`。沉默一小时只有 0.25,十小时到 0.77——不会两小时没回就急,但一整天没动静会明显想说话。
244
+ - **欲言又止**:`hold` 不是失败。它把"这次的憋劲"累进 `carry`,攒够下一次才真开口。所以会看到"她想了好几次,最后才说一句"。
245
+ - **时机不对就不说**:安静时段(默认 0–7 点)抬门槛 0.35,压力大再抬 0.06;刚说过话有 90 分钟冷却;一天最多 5 次。她说完话也算"刚联系过",不会因为对方没回就靠沉默累积刷屏。
246
+
247
+ 文案按动机分类取池(想念 / 关心 / 分享 / 不安 / 无聊),最近 5 句会被避开。**目前是模板,不是模型生成**——接模型后可替换这一层,其余决策逻辑不用动。
248
+
249
+ **默认是关闭的**(`speak.enabled: false`)。"她会自己开口"属于自主行为,装完插件不该自动开始——要用就改成 `true`,并确认桌宠在跑。
250
+
251
+ **能看见屏幕时会先看场景。** `connectors/desktop` 桥接在跑时,每次评估先读一眼前台窗口,按打扰成本抬门槛:开会 ×0.85、游戏 ×0.7、写代码 ×0.4、其它 ×0.15(再乘 `busy_penalty 0.25`)。桥接没启动就静默跳过——不会因为探测失败变成一整天不说话的哑巴。
252
+
253
+ 投递走可插拔的表达出口(`expression.sinks`,类型 `ExpressionSink`):`pet` 投给 DSH 内置桌宠 dsh-pet(气泡走 `/dsh-pet-7340/broadcast`,情绪动作走 `/anim`),`vpet` 把文本塞进 VPet 的 `/vpet/events` 队列(`kind: reply`),`http` 把事件 JSON POST 给任意应用,`file` 追加 JSONL 供本地 `tail -f`。多个出口可同时开,并发投递、单个出口抛错不影响其他;决策层只负责"想说什么",往哪发由配置决定,加一个出口不用动决策。
254
+
255
+ **换了桌宠不用改代码。** `pet` 出口的地址由宿主自己拼(`webServer` 端口 + dsh-pet 的路由段),不需要填端口;出厂配置只有 `vpet` 时,启动会探一下 dsh-pet 在不在,在就让它接管(`prefer_pet: true`),免得两套桌宠把同一句话各弹一遍。VPet 卸掉之后,`vpet` 出口只是往没人消费的队列里写,不会再出现在屏幕上。
256
+
257
+ **干活时桌宠也会动。** dsh-pet 接管后,她的 thinking / tool / reply / error 会点播成 dsh-pet 内置的工作状态动画(`events.workStatus`):思考冒泡、忙碌点按、清点归档、垂头叹气冒汗。这些动画是 dsh-pet 自带的,不用从 VPet 搬素材——两边的动作词汇表不通用,VPet 认 `GraphType`(Say/Touch_Head/Sleep),dsh-pet 认它素材目录里的中文名,所以按接管方各选各的。
258
+
259
+ **说过的话留痕。** 星图面板的「主动开口」分组能翻到:今天开口几次、几次欲言又止、憋着多少劲、最近说过哪几句。没有这块的话,她说什么只存在于气泡队列里,测完什么都看不到。
260
+
261
+ 没有桌宠在轮询时,消息只会入队、不会出现在聊天记录里——插件没有往宿主会话里插消息的权限。
262
+
263
+ ## 抖音联动
264
+
265
+ 装了 `dsh-douyin-browser-local` 时,她能知道你刷了什么。不是靠对方推送——那个插件没有对外事件或 webhook——而是**只读**它的共享记忆库(`~/.dsh/douyin-browser/memory/shared.sqlite`)里 `origin='video'` 的条目,把新出现的记录写成自己的一条事件(`source: douyin_content`),之后走同一套认知滤网、衰减与联想。
266
+
267
+ - **只读连接**:不写对方的库,观看记录也不会被反向同步回去
268
+ - **游标在内存里推进**:首次启动与长时间关机后只补最近 24 小时,不把上周刷的当成"刚看到的"
269
+ - **失败即静默**:库不存在、表结构变了、运行环境没有 `node:sqlite`,都只是"这次没有新内容",不影响其它功能
270
+ - 参数在 `config.yaml` 的 `douyin` 段:`enabled` / `memory_db` / `poll_interval_min`(默认 10 分钟)/ `max_items`(单次最多 5 条)
271
+
154
272
  ## 当前状态
155
273
 
156
- P0–P8 已落地并核对(当前 `npm test` **112 项全绿**,`check:link` PASS 18);P9 外部连接 3/7(VPet 桌宠 / Android / 桌面已通,QQ、抖音、Minecraft、单机待适配器)。已知短板与后续优先级见上面两份文档。
274
+ P0–P8 已落地并核对(当前 `npm test` **211 项全绿**,`check:link` PASS 19);P9 外部连接 4/7(VPet 桌宠 / Android / 桌面已通,抖音走只读共享记忆拉取,QQ、Minecraft、单机待适配器)。已知短板与后续优先级见上面两份文档。
275
+
276
+ ## 发布(维护者)
277
+
278
+ **两个渠道必须一起更新**:只 push GitHub 不发布 npm,插件市场「发现」里装到的仍是旧版。
279
+
280
+ ```bash
281
+ # 1. 版本 + 构建 + 校验
282
+ npm version <ver> --no-git-tag-version
283
+ npm run build && npm test && npm run check:link
284
+
285
+ # 2. 推送 GitHub,并把市场索引钉到本次发布提交
286
+ git add -A && git commit -m "feat(...): ..."
287
+ git push origin main
288
+ # 编辑 market/index.json:新增 { version, publishTimestamp, source.commit = 发布提交号 }
289
+ git add market/index.json && git commit -m "chore(market): 索引锁定到 <ver>(<sha>)" && git push origin main
290
+
291
+ # 3. 发布 npm(社区目录靠它抓取)
292
+ npm publish
293
+ ```
294
+
295
+ 发布后可用 `npm view dsh-daughter version` 与 `npx dsh-daughter-token --json` 验证;jsDelivr 对 `@main` 有缓存,索引更新后可请求 `https://purge.jsdelivr.net/gh/han0han0le0ni/dsh-daughter@main/market/index.json` 刷新。
157
296
 
158
297
  ## 隐私
159
298
 
160
- `brain/`、`logs/`、`archive/`、`star_map/`、`docs/screenshots/`、`dist/` 与 C# 的 `bin/obj` 均排除在版本库之外:前四者包含真实对话记忆、人格状态与界面截图,只在本地生成。
299
+ `brain/`、`logs/`、`archive/`、`star_map/`、`docs/screenshots/` 与 C# 的 `bin/obj` 均排除在版本库之外:前四者包含真实对话记忆、人格状态与界面截图,只在本地生成。`dist/` 例外——**故意提交**,因为市场与 git 安装没有构建步骤。
161
300
 
162
301
  ## License
163
302
 
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * 连接器令牌工具:打印、导出、轮换。
4
+ *
5
+ * 桥接端点(47612)与桌面 / 安卓连接器(47614 / 47613)都要求令牌,
6
+ * VPet MOD、独立壳、第三方脚本需要拿到同一个值才能连上。
7
+ *
8
+ * 这里直接用 dist/ 里的实现,所以 npm 安装的用户也能用:
9
+ * npx dsh-daughter-token
10
+ * npx dsh-daughter-token --write-to "<MOD 目录>"
11
+ * npx dsh-daughter-token --rotate
12
+ *
13
+ * 参数:
14
+ * --write-to <目录> 同时写入 <目录>/token.txt(MOD / 壳直接读)
15
+ * --rotate 删掉旧令牌重新生成(所有连接器都要换新值)
16
+ * --json 机器可读
17
+ *
18
+ * 从源码仓库运行时等价于 `npm run connector:token`。
19
+ */
20
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
21
+ import { dirname, join, resolve } from 'node:path';
22
+ import { fileURLToPath } from 'node:url';
23
+ import { loadConfig, resolveDataRoot } from '../dist/config.js';
24
+ import { CONNECTOR_TOKEN_FILE, ensureConnectorToken } from '../dist/runtime/connector-auth.js';
25
+ import { silentLogger } from '../dist/util.js';
26
+
27
+ const args = process.argv.slice(2);
28
+ const has = (name) => args.includes(`--${name}`);
29
+ const value = (name, fallback) => {
30
+ const index = args.indexOf(`--${name}`);
31
+ return index >= 0 && args[index + 1] ? args[index + 1] : fallback;
32
+ };
33
+
34
+ // 插件根目录:bin/ 的上一级
35
+ const pluginRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
36
+
37
+ const config = loadConfig(pluginRoot, silentLogger);
38
+ const dataRoot = resolveDataRoot(pluginRoot, config);
39
+ const tokenPath = join(dataRoot, CONNECTOR_TOKEN_FILE);
40
+
41
+ if (has('rotate')) {
42
+ if (existsSync(tokenPath)) rmSync(tokenPath, { force: true });
43
+ console.error('已删除旧令牌,下面输出的是新生成的值。');
44
+ }
45
+
46
+ const state = ensureConnectorToken(dataRoot, config.security?.connector_token);
47
+ if (!state.token) {
48
+ console.error(`无法取得连接器令牌:${state.path}`);
49
+ console.error(' 检查目录是否可写,或在 config.yaml 的 security.connector_token 里显式指定(≥16 字符)。');
50
+ process.exit(1);
51
+ }
52
+
53
+ const writeTo = value('write-to', '');
54
+ const written = [];
55
+ if (writeTo) {
56
+ const target = resolve(writeTo, 'token.txt');
57
+ mkdirSync(dirname(target), { recursive: true });
58
+ writeFileSync(target, `${state.token}\n`, { encoding: 'utf8', mode: 0o600 });
59
+ written.push(target);
60
+ }
61
+
62
+ if (has('json')) {
63
+ console.log(JSON.stringify({ token: state.token, source: state.source, path: state.path, files: written }, null, 2));
64
+ } else {
65
+ console.log(`令牌来源:${state.source}`);
66
+ console.log(`令牌文件:${state.path}`);
67
+ console.log('');
68
+ console.log(state.token);
69
+ for (const file of written) console.log(`\n已写入:${file}`);
70
+ console.log('');
71
+ console.log('把它交给连接器:');
72
+ console.log(' · VPet MOD:放到 MOD 的 plugin 目录下的 token.txt,或设置环境变量 DSH_DAUGHTER_TOKEN');
73
+ console.log(' · 独立壳 / 桥接脚本:--token <值>,或设置 DSH_DAUGHTER_TOKEN');
74
+ console.log('提示:令牌只在本机使用,不要提交进仓库。');
75
+ }
76
+
77
+ // 打印完立即退出,避免残留句柄把命令挂住
78
+ process.exit(0);
package/config.yaml CHANGED
@@ -23,6 +23,13 @@ retrieval:
23
23
  emotion_match: 0.10
24
24
  link_proximity: 0.10
25
25
  min_score: 0.12
26
+ # 直接命中要求的最低语义分:语义为 0 的事件不算"相关记忆",交给联想通道(§8.1)
27
+ min_semantic: 0.01
28
+ # 回忆重构的门槛:当前情绪与事件快照的 PAD 距离(满量程 1)到这个值才记一条"回看感受"。
29
+ # 真实库标定:事件两两距离 p50≈0.04、p90≈0.14;0.08 约对四分之一的召回生效,调小则更密。
30
+ reconstruct_distance: 0.08
31
+ # 同一条记忆的回看冷却(分钟):距离门槛挡不住高频召回,一轮十几条两轮就能写满
32
+ reconstruct_cooldown_minutes: 360
26
33
  # 命中事件若在全文层有更完整记录,就在摘要行后补一句摘录(§4.4)
27
34
  expand_fulltext: true
28
35
  fulltext_excerpt_chars: 120
@@ -31,6 +38,9 @@ association:
31
38
  max_depth: 2
32
39
  max_injected: 2
33
40
  hop_decay: 0.6
41
+ # 一条字面都没命中时,从"当下情绪最像、时间最近"的记忆顺一跳(§8.2);
42
+ # 换对话框后的第一句也靠它续接"上一个对话的结尾"。关掉即完全沉默。
43
+ fallback_when_no_match: true
34
44
 
35
45
  skills:
36
46
  max_main: 1
@@ -42,6 +52,106 @@ skills:
42
52
  # 一轮里至少用到几个工具,才把这次工具序列沉淀成技能草稿(§9.1 draft)
43
53
  min_steps_to_learn: 2
44
54
 
55
+ # 行为动机:决策层把"文本相似度"换成动机评分
56
+ # - 人格只调信号通道的阈值,不直接改分数
57
+ # - 熟悉度倒 U、记忆三路径、情绪覆盖、情境乘子共同决定最终分数
58
+ # - enabled=false 时退回旧的相似度打分(便于对照)
59
+ motivation:
60
+ enabled: true
61
+ # 人格对阈值的最大影响强度(0 = 人格不影响决策)
62
+ trait_threshold_gain: 0.25
63
+ # 阈值下限:外向/宜人拉满也只能把阈值压到这里,避免"零信号也能 act"
64
+ # 0 = 不设下限(不推荐)。不能大于 skills.min_main_score
65
+ min_threshold: 0.05
66
+ # 人格对分数的增益(方向舵):乘子范围 [1-g, 1+g]
67
+ # 与 trait_threshold_gain 分工——那个决定"够不够力",这个决定"更想走哪条路"。
68
+ # 只影响通道间的比分排序,不会凭空造出信号:signal=0 时分数恒为 0
69
+ trait_signal_gain: 0.2
70
+ # 熟悉度倒 U:exposure = uses / (uses + k)
71
+ familiarity:
72
+ k: 3
73
+ peak: 0.45
74
+ sigma: 0.35
75
+ # 记忆三路径:结果预期 / 情绪记忆 / 自我效能,权重内部会归一化
76
+ memory:
77
+ outcome_weight: 0.4
78
+ emotion_weight: 0.3
79
+ efficacy_weight: 0.3
80
+ half_life_days: 30 # 结果预期按上次使用时间衰减的半衰期
81
+ lookback_days: 30 # 情绪记忆只看最近多少天的事件
82
+ # 习得性无助:连续失败到阈值后熔断,冷却按 2 的指数增长
83
+ breaker:
84
+ fail_streak: 3
85
+ base_cooldown_min: 10
86
+ max_cooldown_min: 1440
87
+ # 情绪覆盖:命中条件时抬升/降低阈值,总偏移会被 clamp
88
+ emotion_override:
89
+ safety_low: 0.35
90
+ safety_low_offset: 0.10 # 安全感低 → 更谨慎
91
+ stress_high: 0.7
92
+ stress_high_offset: 0.08 # 压力高 → 更谨慎
93
+ pleasure_low: -0.25
94
+ pleasure_low_offset: 0.06 # 心情低 → 更谨慎
95
+ arousal_high: 0.4
96
+ arousal_high_offset: -0.05 # 唤醒高 → 更容易行动
97
+ fatigue_high: 0.8
98
+ fatigue_high_offset: 0.08 # 疲劳高 → 更谨慎
99
+ clamp_min: -0.15
100
+ clamp_max: 0.20
101
+ # 情境乘子:社交通道按亲密度放大,其余通道只吃一半影响
102
+ situation:
103
+ min_multiplier: 0.85
104
+ max_multiplier: 1.15
105
+ # score ≥ ratio × threshold 时为 hold(保留但不主动)
106
+ hold_ratio: 0.7
107
+
108
+ # 主动开口:没人说话时,她什么时候会自己开口
109
+ # - 强度侧(想不想)吃人格、情绪、关系与沉默时长;门槛侧(合不合适)只吃时段与压力
110
+ # - 想说但忍住会累积"憋着"的压力,攒够了下一次才会真开口
111
+ # - 冷却期与日上限兜底,避免刷屏
112
+ speak:
113
+ # 主动开口是自主行为,代码默认关闭;这里显式开启(桌宠没跑的话消息只会落进面板)。
114
+ enabled: true
115
+ check_interval_min: 3 # 多久评估一次,不是多久说一次
116
+ # 活跃档:安静 25 分钟就进入考虑区间,思念 2 小时到半饱和。
117
+ # 早期 45/180/0.62 在"写代码"场景下要沉默四五个小时才开口,实际等于不会说话。
118
+ min_idle_min: 25 # 至少安静这么久才开始考虑
119
+ idle_half_min: 120 # 思念的半饱和时间:沉默 2 小时约 0.5
120
+ trait_urge_gain: 1 # 人格对强度的放大倍数,0 = 人格不影响
121
+ threshold: 0.52 # 开口门槛
122
+ threshold_min: 0.40 # 门槛下限,防止被压成"见谁都说话"
123
+ refractory_min: 60 # 说过后强制安静
124
+ max_per_day: 8 # 一天最多主动开口几次
125
+ quiet_hours: [0, 7] # 安静时段,支持跨午夜如 [23, 7]
126
+ quiet_penalty: 0.35 # 安静时段抬升的门槛
127
+ # 看得见屏幕时按场景抬门槛(需要 connectors/desktop 桥接在跑):
128
+ # 会议 0.85 / 游戏 0.7 / 写代码 0.4 / 其它 0.15,乘以这个系数。
129
+ # 别为了"活跃"把这个调太小:沉默够久时,会议也拦不住。
130
+ busy_penalty: 0.25
131
+ hold_ratio: 0.75 # 到门槛的这个比例算"想说但忍住"
132
+ carry_step: 0.05 # 一次忍住累积多少
133
+ carry_decay: 0.02 # 没到 hold 时自然消退
134
+ carry_max: 0.25 # 累积上限
135
+ jitter: 0.04 # 抖动幅度,避免像定时任务
136
+
137
+ # 表达出口:她主动说的话发给谁
138
+ # - 多个出口可同时开,并发投递;任一出口失败不影响其它出口
139
+ # - 留空或全部非法时退回桌宠,避免"开了却没人收到"
140
+ # - 可用类型:
141
+ # pet DSH 内置桌宠 dsh-pet(蓝鱼):气泡 + 情绪动画,宿主自己拼端口,无需填地址
142
+ # vpet 旧版 VPet 游戏的事件队列,需要 MOD 轮询(默认)
143
+ # http 把事件 JSON POST 给任意应用:{ type: http, url: http://127.0.0.1:8080/hook, timeout_ms: 3000 }
144
+ # file 追加 JSONL,任何本地进程都能 tail -f:{ type: file, path: logs/expression.jsonl }
145
+ # - pet 出口可选字段:pet(宠物 id,缺省 main)、duration_ms(气泡停留毫秒,0 = 只要动画)、
146
+ # route(路由段覆盖,缺省 /dsh-pet-7340)、url(缺省用宿主自己的 http://127.0.0.1:<port>)
147
+ expression:
148
+ enabled: true
149
+ sinks:
150
+ - type: vpet
151
+ # 出厂只配了 vpet 时,若探测到 dsh-pet 在跑就让它接管(两套桌宠各弹一遍没意义)。
152
+ # 守着 VPet 就改成 false。
153
+ prefer_pet: true
154
+
45
155
  reflex:
46
156
  enabled: true
47
157
  max_match_ms: 5
@@ -79,6 +189,14 @@ storage:
79
189
  # true = 人格/技能/资产/L5/日志写入走文件锁 + 原子写(§5.3);false 会退化为无锁写并告警
80
190
  single_writer: true
81
191
 
192
+ # 本机连接器认证(§12.2):桥接端点(47612)与桌面连接器(47614)都要求令牌。
193
+ # - 留空 = 首次启动自动生成 brain/brainstem/connector_token(0600),不会退化成无认证
194
+ # - 显式填写(≥16 字符)= 固定用它,多机/容器部署时用;填写过短会被忽略并告警
195
+ # - 任何情况下,浏览器来源(带 Origin / Sec-Fetch-*)的请求一律拒绝,令牌正确也不行
196
+ # MOD 与独立壳需要这个令牌:跑 `npm run connector:token` 可打印并写入指定目录
197
+ security:
198
+ connector_token: ""
199
+
82
200
  # 认知滤网:写入门控 / 习惯化 / 认知屏蔽 / 忘却(§8.5)
83
201
  filter:
84
202
  enabled: true
@@ -101,6 +219,13 @@ filter:
101
219
  habituation:
102
220
  window_minutes: 30
103
221
  max_repeat: 3
222
+ # 同源爆发抑制:外部来源在窗口内最多留几条,超出即丢弃。
223
+ # 习惯化只认"内容很像",而抖音这种模板相同、条目不同的输入躲得过去,会一条条写满事件库。
224
+ # 对话来源不设限:那是真实发生的事,不该被限流。
225
+ burst:
226
+ window_minutes: 60
227
+ max_keep: 3
228
+ exempt_sources: [chat, qq, douyin_dm]
104
229
  salience:
105
230
  drop_below: 0.2 # 显著性低于此值且来源可信度 ≤0.5 时直接丢弃(0.12 时"前台切换"这类系统事件会漏进来)
106
231
  # 忘却:低显著性 + 从未被想起 + 长期闲置 → 归档(不物理删除)
@@ -158,11 +283,26 @@ personality:
158
283
  avoidant: 0.12
159
284
  evidence_cap: 200 # 证据链保留条数(面板只给事实,不给类型标签)
160
285
 
161
- # 桌面连接器(P9,尚未实现):打开只会多一条告警,默认关闭
286
+ # 桌面连接器:注册 desktop_observe(看屏幕/截图)与 desktop_input(鼠标键盘窗口)。
287
+ # 光开这里不够,还要把桥跑起来:node connectors/desktop/bridge.mjs(默认 47614)。
288
+ # 写类动作(点击/按键)仍受安全档位熔断,档位 ≥2 时只读放行。
289
+ # 桌面连接器(可选,默认关闭):开启后她可以截屏、看前台窗口、注入键鼠。
290
+ # 这是本插件里权限最高的一项,只在你明确需要时才打开。
291
+ # 桥接服务本身也要令牌(见 security 段),两者缺一不可。
162
292
  desktop:
163
293
  enabled: false
164
294
  bridge_url: http://127.0.0.1:47614
165
295
 
296
+ # 抖音联动(可选):只读 dsh-douyin-browser-local 的共享记忆库,
297
+ # 把新刷到的视频记成她的一条事件(source = douyin_content)。
298
+ # - 全程只读,绝不写对方的库;没装该插件或库还没建时静默跳过
299
+ # - 首次启动与长时间关机后只补最近 24 小时,不把上周刷的当成"刚看到"
300
+ douyin:
301
+ enabled: true
302
+ memory_db: "~/.dsh/douyin-browser/memory/shared.sqlite"
303
+ poll_interval_min: 10 # 多久拉一次新记录
304
+ max_items: 5 # 单次最多入库几条,避免一口气灌满事件流
305
+
166
306
  # 桌宠联动(可选):任何一项关掉/缺失,插件行为都与"没有桌宠"一致
167
307
  # 没装桌宠也不用改这里——没有心跳时 body_link 自动失效
168
308
  vpet: