dsh-biocore 0.0.0-stage → 0.1.0

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.
package/README.md CHANGED
@@ -1,3 +1,378 @@
1
- # Temporary Holding Version
1
+ # DSH 插件:真实会话状态(per-session)+ 右栏 tab「生物核」+ 独立仪器面板
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ host 侧按会话读写状态(`<dataDir>/sessions/<session_id>.json`),通过子进程
4
+ 调用 `dsh_adapter` 的 `load_state / one_turn / save_state`;client 侧右栏 tab
5
+ 每 5 秒带 `session` 轮询,切换会话即切数据。任务 194 之后状态会真实变化:
6
+ DSH `session/event` hook 每轮调用一次 `one_turn`,另有 `/bio sync <一句话>` 兜底。
7
+ 右栏 tab 是快览(含 24px sparkline),独立页是仪表盘(orb + 24h 曲线 + 记忆 +
8
+ 关系/叙事指标 + 人设编译 + 四个开关)。task-213 起有两个入口:左侧栏「BioCore」
9
+ (根作用域 main keyed slot 内嵌 iframe)与右栏 tab。
10
+
11
+ ## 文件
12
+
13
+ - `lib/index.js` host:`GET /api/biocore/state?session=<id>`(懒创建)、
14
+ `session/event` hook、`/bio sync` 命令、`/api/biocore/switch`(task-195)、
15
+ `GET/POST /api/biocore/persona`(task-208)。
16
+ - `lib/modules/state.js` 会话文件路径/校验、状态投影(v / intero 四通道 /
17
+ tone / relation.stage / autonomy.state / memory_count / updated_at)、16KB 上限。
18
+ - `lib/modules/data-dir.js`(task-197)唯一的 dataDir 解析:sessions 与 flags.json
19
+ 必须落在同一目录。
20
+ - `lib/py-bridge.js` + `lib/py/bridge.py` 子进程桥:`ensure` / `turn` / `default` /
21
+ `persona_compile`(文本走 stdin),只调用 dsh_adapter 与 mindbase 编译模块,
22
+ 不在 Node 里 import Python,不改 adapter 文件。
23
+ - `lib/modules/switch.js` task-195 四个开关(host settings 为权威)。
24
+ - `lib/client.js` 右栏 tab:心情主指标(大号 + 趋势箭头 + Δ)、四通道次指标、
25
+ 3 状态词、24px sparkline、四开关、底部「展开独立面板」;session 轮询。
26
+ 同一文件还注册左侧栏 `sidebar.panellist` 条目(图标 +「BioCore」)与根作用域
27
+ `main` keyed slot(key `biocore-panel`,iframe 指向独立页,带当前 session)。
28
+ - `lib/modules/panel-data.js` /state 的附加字段(memory_recent / relationship_metrics /
29
+ body_metrics / narrative / motivation / history)与独立页静态路由。
30
+ - `ui/panel.html` + `ui/panel.css` + `ui/panel.js` 独立仪器面板(原生 JS、
31
+ CSS 变量 + prefers-color-scheme 双主题、无外部依赖)。
32
+ - `test/selftest.mjs`(45 checks)、`test/hook-wiring.mjs`(2 checks)、
33
+ `test/bio-sync.mjs`(/bio sync 手动实测)。
34
+
35
+ ## 状态怎么变
36
+
37
+ 1) **DSH host hook `session/event`(可用)**:每条直接人类 `user/message` 都会
38
+ 触发一次 `one_turn` 并写回该会话状态。证据:
39
+ - `@deepseek-ai/dsh-session/lib/types/index.d.ts` 声明
40
+ `'session/event'(session, event)`;
41
+ - 参考插件 `dsh-biocore-memory/lib/modules/auto-memory.js` 已用同一 hook,
42
+ 并从 `event.data.content` 取文本、用 `source.kind === "user"` 过滤注入;
43
+ - 本仓库 `test/hook-wiring.mjs`:`apply()` 注册 hook 后,喂一条合成
44
+ `user/message`,真实 python bridge 写出 `sessions/hook-sess-1.json`,
45
+ `turn=1`(不是 mock)。
46
+ 运行时监听器只转发直接人类消息;assistant 消息、runtime-context 注入、
47
+ 非法 session id 都会被忽略(selftest 断言)。
48
+
49
+ 2) **兜底命令 `/bio sync <一句话>`**:给没有 hook 的宿主/手动调试用,
50
+ 调同一个 bridge 更新当前会话,返回一行状态变化摘要(不产生模型消息)。
51
+
52
+ ## 接口
53
+
54
+ `GET /api/biocore/state?session=<id>`
55
+
56
+ - 有会话:200 + 投影子集(含 `session_id`、`lazily_created` 当次懒创建)。
57
+ - 无 `session`:`{"available":false,"reason":"no_session"}`。
58
+ - session id 非法:`reason:"bad_session"`;文件坏:`reason:"bad_state"`;
59
+ 懒创建失败:`reason:"no_state_yet"`。
60
+ - 只读、无副作用、`cache-control: no-store`、载荷 ≤16KB。
61
+
62
+ 示例:
63
+
64
+ ```json
65
+ {"available":true,"session_id":"live-test-a","v":0.04,
66
+ "intero":{"tension":0.2982,"warmth":0.5979,"heaviness":0.2965,"clarity":0.7047},
67
+ "tone":{"label":""},"relation":{"stage":"熟悉"},"autonomy":{"state":"静"},
68
+ "memory_count":0,"updated_at":1793711000533}
69
+ ```
70
+
71
+ `GET/POST /api/biocore/switch`(task-195):四个开关,host settings 为权威,
72
+ 写入需要 `{key,value,confirm}`;详细键见 `lib/modules/switch.js`。
73
+
74
+ ## 诊断:这一轮为什么(task-210)
75
+
76
+ `GET /api/biocore/trace?session=<id>&limit=10`
77
+
78
+ - 返回 `{ok:true, rows:[...], session_id, limit}`;`rows` 按时间**新→旧**排序。
79
+ - 数据源(只读):`<store>/traces/<session_id>.jsonl`,由 Python
80
+ `biocore_runtime/pipeline.py` 在 `turn_end` 时写入,最多保留最近 50 条;
81
+ store 解析:`payload.store` > `BIOCORE_MEMORY_STORE` > `<plugin>/data/memory`。
82
+ - 文件不存在、不可读、单行坏 JSON:返回 `{ok:true,rows:[]}`(坏行跳过);
83
+ 缺 `session`:`{ok:false,reason:"no_session",rows:[]}`;非 GET/HEAD:405。
84
+ - `limit` 默认 10,范围 1..50;session 名按 Python 同规则 sanitize,禁止路径穿越。
85
+ - 本端点不调用模型、不写文件、不影响对话行为。
86
+
87
+ 单条 trace 字段清单(`rows[]`):
88
+
89
+ | 字段 | 类型 | 含义 |
90
+ | --- | --- | --- |
91
+ | `ts` | number | 写入时间(epoch 秒) |
92
+ | `session_id` | string | 会话 id |
93
+ | `state_delta` | array | 只列**真正变化**的数值:`{field,before,after,source}`;`source` = `user` / `bio_tick` |
94
+ | `intent` | object | `{kind,weight,reason}`(意图类别、权重、原因) |
95
+ | `constraints` | object | 本轮表达约束(`length_scale`/`initiative`/`tone`/`allow_correction`/`sources` 等) |
96
+ | `length` | object | `{scale,source}`(长度缩放 + 来源,如 `state` / `self_layer`) |
97
+ | `gate` | object | `{action,rule,label}`;`action=pass` 即未命中 |
98
+ | `memory` | object | `{recalled_ids:[], captured_id}`(召回 id 列表 / 本轮写入的记忆 id) |
99
+ | `modules` | object | `{ran:[], skipped:[]}`(本轮运行/跳过的模块) |
100
+
101
+ 独立页「这一轮为什么」:
102
+
103
+ - `ui/panel.html` 里可折叠的 `<details id="traceCard">`,默认收起;
104
+ - 拉 `/api/biocore/trace?session=<id>&limit=3`,每条显示 5 行:
105
+ **状态变化**(`field before→after`,最多 4 个,其余 `+N`)、**意图**、
106
+ **长度来源**、**闸命中**(`pass` 显示「未命中」)、**记忆**(召回 N / 写入 id);
107
+ - 缺失字段显示 `—`,`state_delta` 为空显示「无变化」;
108
+ - 空状态统一写「还没有记录」,请求失败写「读取失败」。
109
+
110
+ ## 两个入口(task-213)
111
+
112
+ - 左侧栏入口:客户端 `lib/client.js` 的 `apply()` 里注册
113
+ `sidebar.panellist`(id=`biocore-panel`,order 20,label `BioCore`,图标 SVG)
114
+ 与根作用域 `main` keyed slot(同 key)。点击左侧行后,layout 自动切到该 main
115
+ panel,渲染满高 iframe:
116
+ - 有当前会话:`/api/biocore-ui/ui/panel.html?session=<session-id>`
117
+ - 无会话:省略 query(独立页显示「没有会话参数」,不会报错)
118
+ - 当前会话来自 slot 标准 `useSessions` 选择器(优先 `retainedBy.mainView>0`,
119
+ 其次 updatedAt 最新的会话,再没有就省略)。
120
+ > 说明:DSH 的 slots 注册 API 在客户端插件(`ctx.slots`),与
121
+ > `dsh-biocore-memory` 同一套;host `apply()` 只负责 `/api/biocore-ui/ui/*`
122
+ > 静态路由(本次未改)。
123
+ - 右栏 tab:保留原样。底部「展开独立面板」改为 `ctx.layout.selectPanel('biocore-panel')`,
124
+ 切到同一个左侧入口(同一 iframe/同源 cookie/无弹窗拦截);若宿主没有 layout
125
+ 服务(或选择失败)则退回 `window.open` 新标签,行为与旧版一致。
126
+ - 静态路由未改。
127
+
128
+ ## 人设:提示词 -> 数值画像(task-208)
129
+
130
+ - 参考 AstrBot 侧格式:`persona_profile.json` = `{"persona": 原文, "profile": {...}}`,
131
+ profile 为 18 个扁平字段:O/C/E/A/N、stress_reactivity、recovery_rate、
132
+ social/energy/curiosity/safety baseline、da/oa/ht baseline、autonomy_gain、
133
+ baseline_v/baseline_a、learning_rate。注入顺序照 skill:
134
+ traits -> affect baseline -> base/autonomy/learning(白名单,不写记忆/事实/安全)。
135
+ - 编译用 `mindbase.persona_compile`(与 AstrBot 同字段/同顺序的确定性编译:
136
+ Big Five 后 `refresh_numbers()` 推导生理/驱力/自主/学习率);不新增 LLM 调用。
137
+ - 文件(与 flags/sessions 同 dataDir):
138
+ - `<dataDir>/persona.md` 原文
139
+ - `<dataDir>/persona_profile.json` 编译结果(含 version / persona_hash / compiled_at)
140
+ - 端点:
141
+ - `GET /api/biocore/persona` → `{ok,text,profile,updated_at,preview}`;
142
+ 缺文件 `ok:true, profile:null, reason:"no_persona"`;坏 JSON → `{ok:false,reason:"bad_profile"}`。
143
+ - `POST /api/biocore/persona {text, confirm:true}` → 编译并写两个文件,返回
144
+ profile + 9 项中文预览;错误:`empty_text` / `confirm_required` /
145
+ `text_too_long`(上限 20000 字)/ `bad_payload` / `compile_failed`。
146
+ - adapter 读取:adapter 本身没有 profile 参数,由桥 `lib/py/bridge.py` 读取
147
+ persona_profile.json,按白名单把 baseline_v/a、social/energy baseline、
148
+ recovery_rate(映射半衰期)应用到会话 state(新会话或 persona_version 变化时),
149
+ 并在 state JSON 里持久化 `persona_profile` / `persona_version`;
150
+ `turn` / `ensure` 都接受 profile 路径参数。
151
+ - UI:独立页「人设」区块 = 多行文本框(预填当前人设)+「保存并编译」
152
+ (二次确认)+ 9 项中文数值预览;状态:未保存 / 编译中… / 编译失败:原因 / 已编译。
153
+
154
+ ## 人设常驻:写入 DSH 预设(task-214)
155
+
156
+ - 调研结论(DSH 宿主代码 / profile 配置):
157
+ - 预设声明在 bundle 的 `presets/*.patch.yml`(`@deepseek-ai/dsh-agent-preset` 行);profile 的
158
+ `cordis.patch.yml` 按行 id 最后覆盖(`cordis.yml` 本身只是空数组)。
159
+ - 预设字段:`config {id,name,description,order,plugins[]}`;人设是预设 plugins 里的
160
+ `@deepseek-ai/dsh-persona` 行,字段 `{prefix,suffix,complete,includeRuntimeContext}`,
161
+ prefix 渲染为 `deployment:persona-prefix` 并**遮蔽**部署级 `system-prompt.personaPrefix`。
162
+ - 默认预设:registry 行 `agent-preset-registry.config {default, selectedDefault}`,
163
+ 生效默认 = `selectedDefault ?? default`(web-bio 当前是 `ptc`)。
164
+ - 优先级:bundle patches → profile `cordis.patch.yml`(同 id 后写覆盖)→ 会话选择的预设;
165
+ 编辑预设只影响**之后创建**的会话,已存在 Agent 保留旧 revision。
166
+ - 写入方式:宿主 `ctx.configEditor.edit(entry, change)`(校验 + 文件锁 + 原子写 profile
167
+ patch + Loader 热重载;profile 启动的进程才有该服务,headless 会返回 `host_unavailable`)。
168
+ - 端点 `/api/biocore/persona/apply`:
169
+ - `GET` → `{ok, applied, preset_id, patch_path, chars, matches_preset, injection, has_backup,...}`;
170
+ 未应用 `reason:"not_applied"`,预设在外部被改过 `reason:"drift"`。
171
+ - `POST {confirm:true}` → 读取 `<dataDir>/persona.md`(优先 persona_profile.json 原文),
172
+ 写入默认预设的 dsh-persona `prefix`;同时把 `dsh-biocore-memory` 行的
173
+ `personaFile` 置空以关闭它每轮的人设注入(原值进备份,避免重复)。
174
+ - `POST {confirm:true, rollback:true}` → 用备份恢复原预设 plugins 与原 personaFile。
175
+ - 占位符转换:写入前把 AstrBot 人设占位符转成字面文本——`{{user}}`→「主管」、`{{char}}`→「<角色名>」
176
+ (从 persona 标题「# 角色扮演:安吉拉(Angela)」解析,取不到用「她」)、其它 `{{xxx}}` 去掉大括号保留 xxx;
177
+ 转换后仍含 `{{`/`}}` 才拒绝(`template_unsafe`)。状态同时记录 `raw_chars` 与 `sanitized_chars`。
178
+ - 备份/状态(同 dataDir):`persona_preset_backup.json`(首次应用前的完整预设 config +
179
+ memory personaFile)、`persona_preset.json`(applied/preset/hash/chars/injection)。
180
+ - 面板「人设」卡新增「设为常驻模式」「恢复原人设」按钮(二次确认),并显示
181
+ 常驻状态/预设 id/字数/漂移提示。
182
+ - 说明:本插件当前没有每轮"人设文本注入"(只有 persona→数值画像,继续保留);真正每轮
183
+ 注入人设文本的是 `dsh-biocore-memory.personaFile`,应用时自动关闭,回滚自动恢复。
184
+
185
+ ## 独立页(仪器面板)
186
+
187
+ - 入口:右栏 tab 底部「展开独立面板」,或直接打开
188
+ `/api/biocore-ui/ui/panel.html?session=<session-id>`(同源 cookie)。
189
+ - 静态路由:`GET/HEAD /api/biocore-ui/ui/*`(prefix,`cache-control: no-store`,
190
+ 仅 `panel.html` / `panel.css` / `panel.js` / `trace-view.js`,拒绝穿越与 POST)。
191
+ - 布局:96px CSS orb +「她现在的样子」+ 当前动机;**心情为 hero**(大号数字 +
192
+ ↑/↓/→ 趋势箭头 + Δ);绷紧/暖/沉/清 为一行次指标(小号 + Δ);
193
+ 24h 心情曲线(canvas,单线 + 事件点,来自 /state 的 history,`/series` 不存在
194
+ 时自动回退);最近 10 条记忆(每条 ≤80 字);关系与叙事 5 个数字;
195
+ 底部四个开关(POST /api/biocore/switch 且 `confirm: true`)。
196
+ - 空状态三种文案(都带下一步建议):
197
+ - `no_state_yet` →「这个会话还没聊过」/「发一条消息后就有状态」
198
+ - 请求失败 →「连不上宿主」/「确认 3081 上的 DSH 与插件已加载」
199
+ - `bad_state` →「状态文件坏了」/「删掉该会话的 state 文件后重试」
200
+ - tab 侧 sparkline:高 24px、最多 60 个点(最近 60 个 v 样本);详细曲线只在
201
+ 独立页。
202
+ - 颜色:neutral-bluish token(浅色/深色两套,跟随 `prefers-color-scheme`,
203
+ 也可用 `html[data-theme]` 强制)。
204
+
205
+ ## 安装
206
+
207
+ ```powershell
208
+ cd C:\Users\Administrator\Desktop\deepseek
209
+ dsh plugin --profile web add file:C:/Users/Administrator/Desktop/deepseek/dsh-biocore-ui
210
+ # 再把 "dsh-biocore-ui" 追加进 profile 的 package.json dsh.profile.bundles
211
+ dsh --profile web --port 3080
212
+ ```
213
+
214
+ 本地开发(改代码即生效):把 profile 的 `node_modules/dsh-biocore-ui` 换成
215
+ 指向本仓库的 junction/symlink(pnpm 的 `file:` 会拷贝快照,不会跟随源码)。
216
+
217
+ 测试实例(不碰 3080):
218
+
219
+ ```powershell
220
+ dsh --profile web-test --port 3082 --no-open
221
+ ```
222
+
223
+ ## 数据目录(task-197 / task-201)
224
+
225
+ ```yaml
226
+ - insert:
227
+ - id: dsh-biocore-ui
228
+ name: dsh-biocore-ui
229
+ config:
230
+ dataDir: '' # 空 = 自动解析,见下
231
+ ```
232
+
233
+ `resolveDataDir(config)` 优先级(`lib/modules/data-dir.js`):
234
+
235
+ 1. `process.argv` 里的 `--profile <name>` / `--profile=<name>`
236
+ → `<homedir>/.dsh/profiles/<name>/data`(该 profile 目录存在时)
237
+ 2. `config.dataDir`
238
+ 3. 环境变量 `BIOCORE_UI_DATA_DIR`
239
+ 4. `<DSH_PROFILE_DIR>/data` —— **仅当** `DSH_PROFILE_DIR` 目录里确实有
240
+ `package.json` 或 `dsh.yml`(确认它是 profile 根)
241
+ 5. `<plugin>/data`(仓库/部署副本自己的兜底,不再用 cwd)
242
+
243
+ > task-201 起因:DSH 给子进程的 `DSH_PROFILE_DIR` 会指向默认 profile(web),
244
+ > 即使实例是 `--profile web-bio`,之前因此把会话写进了 `profiles\web\data`。
245
+ > 现在先看启动参数里的 `--profile`,argv 存在时以它为准。
246
+
247
+ 同一目录下的两个文件:
248
+
249
+ - `<dataDir>/sessions/<session_id>.json` 会话状态
250
+ - `<dataDir>/flags.json` 四个开关
251
+
252
+ 其它环境变量:`BIOCORE_ADAPTER_DIR`(adapter 目录,默认
253
+ `<home>/Desktop/deepseek/dsh_adapter`)、`BIOCORE_PYTHON`(默认 `python`)。
254
+ 端口/启停仍由 profile 管理。
255
+
256
+ 部署(`scripts\deploy_dsh_ui.ps1` 会同步 lib/package/cordis 并跑 selftest):
257
+
258
+ ```powershell
259
+ powershell -NoProfile -ExecutionPolicy Bypass -File scripts\deploy_dsh_ui.ps1 -Profile web-bio -Port 3081
260
+ dsh --profile web-bio --port 3081 --no-open
261
+ ```
262
+
263
+ 注意:从另一个 DSH shell 里启动测试实例时,`DSH_PROFILE_DIR` 可能继承成别的
264
+ profile;argv 里的 `--profile` 现在优先,因此直接
265
+ `dsh --profile <name> --port <port>` 启动即可正确落到该 profile 的 `data`。
266
+
267
+ ## 验证记录
268
+
269
+ ### task-208:人设写入窗口 + 编译(web-bio / 3081)
270
+
271
+ - deploy → 重启 3081 → 带 cookie 调端点。
272
+ - `GET /api/biocore/persona`(初始)→ `{"ok":true,"text":"","profile":null,"updated_at":0,"reason":"no_persona"}`。
273
+ - `POST` 「亲近、俏皮、温暖、先接情绪、好奇;说话直一点,不装。」+`confirm:true`
274
+ → 200;生成 `web-bio\data\persona_profile.json`(748 B,18 字段 + version)
275
+ 与 `persona.md`(80 B);预览 9 项(外向 0.65 / 宜人 0.65 / 情绪基线 0.04 /
276
+ 唤醒基线 0.52 / 压力反应 0.5 / 恢复速度 0.75 / 社交基线 0.66 / 自主增益 0.98 /
277
+ 学习率 0.059),与文件一致。
278
+ - adapter 读取:新会话 `persona-live-probe` 懒创建后 `v=0.04`(=baseline_v)、
279
+ social=0.66;state JSON 含 `persona_version`/`persona_profile`,
280
+ `valence_decay_minutes=104.9`。
281
+ - 错误:空文本 400 `empty_text`、缺 confirm 400 `confirm_required`、
282
+ >20000 字 400 `text_too_long`、坏 JSON GET → `{"ok":false,"reason":"bad_profile"}`。
283
+ - 截图:[人设面板截图](_persona_panel.png)(1170×1370,含文本框、已载入、保存并编译按钮、9 项预览)。
284
+ - selftest 45 checks(新增 7 条:GET 形状/POST 编译+文件一致/错误/坏 JSON/
285
+ adapter 读取/UI 分支/路由注册)。
286
+
287
+ ### task-213:左侧栏入口(未重启,Lead 验收)
288
+
289
+ - `lib/client.js` 注册 `main`(key `biocore-panel`)+ `sidebar.panellist`
290
+ (id 同 key,label「BioCore」);右栏 tab 保留;「展开独立面板」优先
291
+ `ctx.layout.selectPanel`,无 layout 时退回 window.open。
292
+ - selftest 48 checks(新增 3 条:注册被调用、iframe URL 带 session、无会话不崩)。
293
+ - 按约束未重启 3081;静态路由未改,验收由 Lead 刷新客户端后确认两个入口。
294
+
295
+ ### task-202:独立仪器面板(web-bio / 3081)
296
+
297
+ - deploy(脚本现在会同步 `lib/` + `ui/`)→ 重启 3081 → 带 cookie 打开
298
+ `/api/biocore-ui/ui/panel.html?session=session-e68776cb-...`。
299
+ - 静态资源 200:panel.html 3727 B、panel.css、panel.js,均为 `no-store`。
300
+ - 截图:[面板截图](_panel_shot2.png)(1170×1060)可见:orb、
301
+ 心情 hero 0.04 ↑ +0.02、绷紧/暖/沉/清 次指标行(Δ ±0.00)、24h 曲线、
302
+ 关系/叙事数字、四个开关。
303
+ - 真实会话 `session-e68776cb-...` 有 3 轮 history(300/600/900),Δ 与曲线
304
+ 均来自 /state 的 `history` 字段(无 /series 时不阻塞)。
305
+ - selftest 38 checks(含新增 4 条:主次/Δ/sparkline/三种空状态)。
306
+
307
+ ### task-201:dataDir 按 --profile 解析(web-bio / 3081)
308
+
309
+ - deploy 到 web-bio → 重启 3081 后端(启动参数 `--profile web-bio`,环境里
310
+ `DSH_PROFILE_DIR` 仍是默认 web,用于验证 argv 优先)。
311
+ - 真实网页操作:headless Edge 打开 3081,新建会话并发送
312
+ 「今天有点累,但谢谢你」,DSH 正常回复(1 轮)。
313
+ - 会话状态写入:`profiles\web-bio\data\sessions\session-3cb5d52b-...json`
314
+ (2981 B,mtime 21:32:30);另一条 `session-29e0d216-...json` 同样在
315
+ `web-bio\data`。
316
+ - 旧错误目录 `profiles\web\data\sessions` 未再新增文件(仍是修复前的
317
+ `ready-check.json` / `session-72c50b00-...json`)。
318
+ - 面板实测(右栏「生物核」):`已连接 · 9 秒前`,心情 0.02、绷紧 0.30、
319
+ 暖 0.60、沉 0.30、清 0.70、语气 正常、关系 熟悉、自主 静。
320
+ - `GET /api/biocore/state?session=session-3cb5d52b-...` → 上述数值;
321
+ `flags.json` 仍在该 dataDir(118 B)。
322
+ - selftest 29 checks(新增 `--profile` 解析 / argv 缺省回退 / profile 不存在回退)。
323
+
324
+ ### task-197:统一数据目录(web-bio / 3081)
325
+
326
+ - 部署:`scripts\deploy_dsh_ui.ps1 -Profile web-bio -Port 3081`
327
+ → 同步 lib/package/cordis + selftest 25 checks 全过。
328
+ - 重启 3081(`dsh --profile web-bio --port 3081 --no-open`,
329
+ 显式 `DSH_PROFILE_DIR` 指向 web-bio)。
330
+ - dataDir 实际解析:`C:\Users\Administrator\.dsh\profiles\web-bio\data`
331
+ - `GET /api/biocore/state?session=deploy-verify`(懒创建)→ available:true
332
+ → `web-bio\data\sessions\deploy-verify.json`(1410 B)
333
+ - `POST /api/biocore/switch {"key":"safety_enabled","value":true,"confirm":true}`
334
+ → 200,`switches.safety_enabled=true`
335
+ → `web-bio\data\flags.json`(118 B)
336
+ - 两个文件同处 `web-bio\data`:部署/备份只需这一个目录。
337
+
338
+ ### task-194:per-session / hook(早期 web-test / 3082)
339
+
340
+ - 无 session → `{"available":false,"reason":"no_session"}`;坏 JSON → `bad_state`;
341
+ `?session=..%2Fevil` → `bad_session`。
342
+ - `/bio sync 今天好累,但谢谢你` 实测:
343
+ `v 0.02→0.04 · 压力 0.20→0.24 · 长度 1.00 · 主动性 0.46 · 允许改口 否`;
344
+ 会话 A 更新、会话 B 不变。
345
+ - `node test/selftest.mjs` → 25 checks;`node test/hook-wiring.mjs` → 2 checks。
346
+
347
+ ## 自测
348
+
349
+ ```powershell
350
+ cd C:\Users\Administrator\Desktop\deepseek\dsh-biocore-ui
351
+ node test/selftest.mjs # 48 checks:投影/会话/数据目录/降级/命令/hook/开关/
352
+ # client/panel/主次指标/Δ/sparkline/空状态/人设编译/
353
+ # 双侧入口(main keyed slot + sidebar.panellist)
354
+ node test/hook-wiring.mjs # 2 checks:apply 真正注册 hook + 触发 one_turn
355
+ node test/bio-sync.mjs <session-id> "<一句话>" # 手动跑一次 /bio sync
356
+ ```
357
+
358
+ ## 已知限制
359
+
360
+ - hook 是 DSH 0.2.x 的 `session/event`;宿主版本变化需复核(类型文件在
361
+ `dsh-session/lib/types/index.d.ts`)。
362
+ - 单 host、单 python 进程桥:每次更新 spawn 一次 python(约 100-300ms),
363
+ 没做常驻服务;并发按 session 串行排队。
364
+ - tone / relation.stage / autonomy.state 仍是 JS 镜像投影(host 不跑 Python),
365
+ 与 adapter 的 Python 结果可能有小数级差异。
366
+ - 没有 per-session 的开关/设置(task-195 的开关是 host 级 settings)。
367
+ - 独立页曲线使用 /state 附带的 history(没有 /series 端点);history 上限 120 点。
368
+ - 独立页需要与宿主同源的 cookie;从别的域名直接打开会拿不到 /api 数据。
369
+ - 左侧栏 main panel 的 session 来自 `useSessions` 标准选择器;宿主未提供该
370
+ hook(或没有会话)时 iframe 省略 session 参数,独立页显示无会话提示。
371
+ - client tab 只展示快览(无记忆列表/诊断/详细曲线);每 5 秒轮询、隐藏不暂停。
372
+ - 会话切换依赖宿主 slot 传入的 `sessionId`;没有会话的界面显示「未连接」。
373
+ - `updated_at` 取状态文件 mtime。
374
+ - 人设编译用 mindbase 的确定性编译器(同字段/同注入顺序),AstrBot 侧 LLM 编译的
375
+ 个别数值可能略有差异;人设按 dataDir 存一份(非 per-session),变更后对新会话
376
+ 或 persona_version 变化的会话生效。
377
+ - hook 里 one_turn 失败只打一行 `__bioTurnErr` 到宿主 stderr,不打断回合;
378
+ 若看到该前缀,说明 python bridge 或 adapter 报错(会话状态会保持上一次)。
@@ -0,0 +1,22 @@
1
+ # dsh-biocore bundle layer: insert our plugin with its defaults.
2
+ - insert:
3
+ - id: dsh-biocore
4
+ name: dsh-biocore
5
+ config:
6
+ # Empty = auto: config > BIOCORE_UI_DATA_DIR > <DSH_PROFILE_DIR>/data > <plugin>/data
7
+ # Both sessions/ and flags.json live under this one directory.
8
+ dataDir: ''
9
+ # Memory (task-203): capture both sides, inject <=420 chars, 20/hour.
10
+ memoryEnabled: true
11
+ injectMaxChars: 420
12
+ hourlyCap: 20
13
+ # Proactive push (task-205): both switches must be true; default OFF.
14
+ # No timer / no delivery while proactiveEnabled=false.
15
+ proactiveEnabled: false
16
+ proactiveSend: false
17
+ proactiveIntervalMinutes: 30
18
+ proactiveMinGapMinutes: 180
19
+ proactivePerHour: 2
20
+ proactiveMinTrust: 0.6
21
+ proactiveMaxChars: 60
22
+ proactiveRequireOpenLoop: true