@nightpainters/dsh-ling 1.5.1 → 1.5.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.
- package/ACCESS-DESIGN.md +6 -4
- package/CHANGELOG.md +65 -0
- package/DESIGN.md +141 -14
- package/README.md +55 -31
- package/lib/audit/access-log.js +135 -13
- package/lib/audit/index.js +43 -1
- package/lib/client.js +902 -57
- package/lib/host/api.js +129 -31
- package/lib/host/genesis.js +27 -5
- package/lib/host/guard.js +1 -1
- package/lib/host/inject.js +49 -12
- package/lib/host/l1.js +11 -4
- package/lib/host/memory.js +263 -20
- package/lib/host/persona.js +38 -7
- package/lib/host/session-line.js +648 -0
- package/lib/host/snapshot.js +48 -7
- package/lib/host/summarizer.js +1 -1
- package/lib/index.js +5 -0
- package/package.json +51 -4
package/ACCESS-DESIGN.md
CHANGED
|
@@ -103,7 +103,7 @@
|
|
|
103
103
|
- **生活/情感加权**:feeling 1.0 / daily 0.7 / knowledge 0.2 + 置顶 +1.5(`GENESIS_WEIGHTS`);prompt 声明"她首先是伴侣,其次才是同事";"评价用户/画像类任务才平权"作为语义规则保留给未来层;
|
|
104
104
|
- 原料三级取用:深摘(deep.sum)→ 非占位概述 → 原文片段(短会话,≤3 轮)/关键词线索——短会话不再静音;
|
|
105
105
|
- **名字主客过滤**(`filterNamePairs`):剔除 userTitle 成分与常见称谓词;prompt 明令名字属于她;
|
|
106
|
-
- **草稿制(乙方案)**:genesis
|
|
106
|
+
- **草稿制(乙方案)**:genesis 候选不再直接落盘;自述/名字单选加入草稿,语气建议、相处观察与**职责候选**可一并携带;「带着草稿去人格中心人审」→ 对应字段旁浮现「她建议…[填入]」(纵向插入,修复过 flex 行溢出;职责同样走这条通路,落进「职业 / 职责 / 责任」输入框);语气/观察进入「她的话」区块,可一键**认可这条习惯**(`POST /persona/hint-adopt`,习惯成长通道,定型下无需手术);
|
|
107
107
|
- **深摘语义**:一次跑完所有未读会话(含快速通过的短会话),进度「已完成 x / 本轮共 y · z%」;按钮「让她读未读过的会话」;全部读完时直接亮出自述按钮(非失败态);
|
|
108
108
|
- 语音语气建议等错误 toast 补齐 `error` 字段兜底。
|
|
109
109
|
- **第 3 波(2026-09-11)**
|
|
@@ -114,6 +114,8 @@
|
|
|
114
114
|
## 9. 诞生仪式(genesis)设计速览(发布文档引用用)
|
|
115
115
|
|
|
116
116
|
1. **入口**:记忆中心 →「接入历史」页签底部「🌱 诞生仪式」卡;流程 = 让读(深摘未读)→ 第一次自我介绍(加权样本合成初稿)→ 人审草稿 → 人格中心落盘;
|
|
117
|
-
2.
|
|
118
|
-
3.
|
|
119
|
-
4.
|
|
117
|
+
2. **初稿五件**:自述候选 ×2~3、名字组 ×2~3、语气建议、相处观察、**职责候选(`duties`)×1~3**(JSON 契约,键名固定为 `self_intros` / `name_pairs` / `tone_advice` / `observations` / `duties`;解析带围栏剥离与长度清洗);
|
|
118
|
+
3. **职责候选的口径(`duties`,第 5 键)**:每条 **5~120 字**、最多 **3 条**、**每条一句**;低于 5 字的噪声与超过 120 字的**整条丢弃**(**不截断** —— 截半句会留下一条读不通的"职责");条内换行折成空格(段内一条一行,防止正文借换行伪造段结构)。清洗后落进 `[承担]` 段;该段**空值不渲染**(不留空段头);
|
|
119
|
+
4. **三条硬约束**:**A. 不得自动落盘** —— 职责与另外四件一样只是**草稿候选**,机制永不自动写入,终审一律在人格中心(已定型字段需手术门);**B. 不得设非空默认,也不得从历史 / 模式推断预填** —— `duty` 默认只能是 `''`;**C. 起草 prompt 明令措辞** —— 只许**陈述式**(我承担 / 我擅长 / 我能给出 / 我会在…的时候…),**禁授权式**(我应当 / 我必须 / 我的任务 / 我的责任是 / 我被要求 / 请让我负责);授权式写法等于把一个"被授权者"写进她的身份,是语义劫持最好用的钩子(与 `[规矩]` 段头那次同族的教训);
|
|
120
|
+
5. **人审闭环**:草稿携带 → 字段旁「她建议…[填入]」/「她的话…认可这条习惯」;五件草稿的终审与保存一律在人格中心(新老用户同路径;已定型字段需手术门);
|
|
121
|
+
6. **材料口径**:她的名字/自述基于"生活情感优先"的加权样本;历史中属于用户的称谓一律过滤,不得混入她的身份;职责同样只从这段历史里**真看得出来的事**来写,**凑不出来就少写或不写**(prompt 明令),不允许写成客套话。
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循语义化版本:`MAJOR.MINOR.PATCH`。
|
|
4
4
|
|
|
5
|
+
## 1.5.2
|
|
6
|
+
|
|
7
|
+
**`/state` 分档提速 · 活行档位改判 · 面板可见性收口 · 依赖声明与「承担」**
|
|
8
|
+
|
|
9
|
+
> 一句话:本版补的是"看不见的那一层" —— 轮询路径与面板路径分开计价,`/state` 单次耗时降到零头;活行(面板上跟着会话走的那一条)的档位从"15 分钟 / 10 轮"改成"轮次边界 + 内容真变了才追加";面板第一次看得见"那一行"和"为什么没变";侧栏的「最近改动」不再静默截断;另外把 `peerDependencies` 补全(这道闸门决定插件会不会**悄悄不激活** —— 线索只有一行,而且不是每条启动路径都会打),并给器灵加了「承担」这一栏。
|
|
10
|
+
|
|
11
|
+
### 一、`/state`:轮询档与面板档分开计价
|
|
12
|
+
|
|
13
|
+
轮询路径与面板路径问的是同一个端点,但要的东西不一样 —— 面板要 L0 预览文本、快照读数、活行预览,而 3 秒一拍的轮询只要几个计数。此前两条路走同一份重活。
|
|
14
|
+
|
|
15
|
+
- **按 `l0Preview` 分档**:面板路径恒带 `l0Preview=1`、轮询路径从不带 ⇒ 轮询档在算 L0 预览**之前**就早退,只回轻字段(既有 5 个快照字段逐字未动);
|
|
16
|
+
- **`MemoryStore.fingerprint()` 的缓存键由一维扩成五维**:`total_changes()`(本连接自写)+ `-wal` 文件的 `(size, mtimeMs)`(外进程写入)+ `PRAGMA data_version`(维护脚本若用 `journal_mode=DELETE` 改库**根本不产生 `-wal`**,只有这一维兜得住)+ db 句柄(关连接再开)。其中 `-wal` 与 `data_version` 两维**取不到时各给哨兵值**(`[0,0]` / `-1`),**不因取不到而抛**(否则 `/state` 会 500);而 `total_changes()` 那一维**没有这层降级** —— 它直接问 db 句柄,句柄已关就照常抛(由路由的兜底 catch 变成 500)。返回契约逐字不变(`sha256` 全长 64 位,面板取前 16 位展示);
|
|
17
|
+
- **`countOverviews()`**:记忆条数从"拉全量再取长度"改成一条 `COUNT` 聚合 —— 面板与 `/state` 都不再为两个数字付全表扫描的钱;
|
|
18
|
+
- **实测**(当天 `00:00`–`16:34` 的访问日志 `access-*.jsonl` 里 `path` 为 `/api/dsh-ling/state` 的请求,取 `ms` 字段;分界取 `13:13` 这一分钟、两侧都不算它 —— 切换就发生在**这一分钟之内**:`13:13:08` 仍是 28 ms,`13:13:16` 起已是 1~2 ms,而该分钟自身 15 次里 13 次 `≤2 ms`):分界前共 **n=51,573** 次、均值 **24.4 ms**、`≤2 ms` 占 **0.02%**;分界后共 **n=3,945** 次、均值 **0.46 ms**、`≤2 ms` 占 **99.5%**。两段是同一天、同一个端点的读数,差别只在"这一版是否已经生效"。以上是**本机这一次**的实测:**两段的窗口起止与样本量都写在句内**(样本量是**读日志那一刻**的值,日志继续写入后按同一口径重算会略大),可用同一天的日志按同样口径复核;换台机器、换个库、换一天都不会一样。
|
|
19
|
+
|
|
20
|
+
### 二、活行(面板上的「那一行」):档位改「轮次边界 + 内容真变了才追加」
|
|
21
|
+
|
|
22
|
+
- 旧档位(**15 分钟最小间隔 / 10 轮**)撤除,改为两条:① 只在新轮次的**第一批**(`step === 1`)考虑追加;② 「内容真变了才追加」用**三道同源判据**,任一说"没变"就不追加;
|
|
23
|
+
- **为什么非要第三道**:那一行正文自带 `#N`,若拿整行去比,**每试一次序号就 +1** ⇒ 恒判"变了",闸等于没有(实测钉过);第三道比的是**实质内容键**(标题 + 最新进展,剥掉 `#N` / 轮次 / `HH:MM`);
|
|
24
|
+
- 旧档位常量与判定函数**保留导出**(测试与探针仍引用),但只读探针里明确标 `tier.active: false` —— 一眼看得出它不在生效路径上;
|
|
25
|
+
- 顺带:这一行"上一次追加过没有"的留痕带 `appendCount`,**重启后判定照旧有效**(不会"重启即多追加一条")。
|
|
26
|
+
|
|
27
|
+
### 三、面板:两条「那一行」+「为什么没变」+ 可折叠看全文
|
|
28
|
+
|
|
29
|
+
- **「那一行」在盘上有两条**,长得像但不是一回事:① **L2 本会话标题行**(概述器写进开场记忆的那一条,只有标题)② **活行**(面板上跟着会话走的那一条,每轮重算)。此前面板不显示、也不区分 ⇒ 现在**各自成行、各自标注**;
|
|
30
|
+
- **「为什么没变」拆成四类看得见的读数**,每条只印真读数;后端这一版没带那个字段时**明说"不做猜测"**,不拿默认值冒充结论;
|
|
31
|
+
- **可折叠看全文**:折叠状态存**闭包**而不是 DOM —— 面板每一拍(store tick)都会重建这一块,存 DOM 会每一拍被清掉;
|
|
32
|
+
- **没有快照时不再谎报「已定稿 0 字符」**:后端在没有快照时回的是 `{exists:false}`,而它是个**真值** ⇒ 前端原先照样进"已定稿"分支。现在先判 `exists === false`,显示「(未定稿 —— 本会话尚无快照,通常在会话第一次定稿后出现)」。⚠️ 这条尤其要紧:**"没有快照"与"有一个 0 字符的快照"外观一致、语义相反**,而后者真会发生(例如人格为空),所以它不是"显示得难看一点";
|
|
33
|
+
- **面板打开后不再冻结**:复用 store 已有的 tick 重画,**不新开 `/state` 轮询**(轮询刚降过频,不能再加回去);store 覆盖得到的行跟着 store 走,覆盖不到的逐行标 `〔打开时快照〕`,快照块前另加一行分隔说明;
|
|
34
|
+
- **两条残留(如实写明)**:① 全局视图最快 **30 秒**才动一次 —— 全局 ticker 已被有意降到 30 s,这是"不加回轮询"的必然结果,不是未修;② 面板开着切会话时,"打开时快照"那一整块仍是**旧会话**那份(实时行会跟着变)。
|
|
35
|
+
|
|
36
|
+
### 四、侧栏「最近改动」不再静默截断
|
|
37
|
+
|
|
38
|
+
- 原先客户端**请求写死 6 行**、渲染**再硬钉 6 行**,**溢出无任何提示** —— 恰好改了 7 处时,看上去就是"少了一条";
|
|
39
|
+
- 现在取数与渲染**两个上限分开命名**:取 100 行、渲染仍是那 6 行,其后**条件追加**「…另有 N 笔改动(此处只列最近 6 笔)」;
|
|
40
|
+
- **N 取服务端真计数**(与同一块红字「有 N 笔还没跟器灵说过」**同数同界**),不是"返回条数 − 6" —— 侧栏只取**一页**,而改动留痕是**全量**:页面恒满 ⇒ 这个差值恒等于"取数上限 − 6"这个纯常数(留痕随库增长按条数线性上涨,可它一动不动),属于"用两个常量的差冒充积压量"的老坑;
|
|
41
|
+
- 满页时退路说「**N 笔以上**改动」(保留量级,不冒充确切数);`id` 读不出来的行**当作已列出**(宁可少报,不谎报);`snapshot` 行**不算债**;
|
|
42
|
+
- **零后端改动**,硬刷页面即生效。
|
|
43
|
+
|
|
44
|
+
### 五、依赖声明:这道闸门决定"插件会不会悄悄不激活"
|
|
45
|
+
|
|
46
|
+
- `package.json` 落 **12 条 `peerDependencies`**,范围一律**开区间**。宿主启动时对每个 `@deepseek-ai/dsh` / `@deepseek-ai/dsh-*` peer 跑一次 `semver.satisfies(宿主版本, 你声明的区间, { includePrerelease: true })` —— 判的是「**宿主版本是否落在你声明的区间里**」,**不是**"你的写法合规不合规";判不满足 ⇒ 整个 bundle 进 `skippedBundles`、**不激活**。线索分两条路径:**从命令行启动**会往 **stderr 打一行** `skipping profile bundle …`(**容易被忽略,但不完全静默**);而**应用自持 profile 的那条装载路径一个字都不打** —— 那才是真正难查的一种,表象常常只有"它突然不见了"。⚠️ 所以"改成 `>=` 就安全"是错的:**开区间同样可能不满足**(取决于宿主版本);`^` / `~` / 钉死版本只是更容易被将来的一次宿主升级甩出去。(确需放行某个确切版本时,宿主提供 `dsh plugin allow-version` 豁免通道;豁免生效时它照常加载。)
|
|
47
|
+
- `dsh.client.inject` **5 项 → 3 项**:删掉 `@deepseek-ai/dsh-client-store` 与 `@deepseek-ai/dsh-client-ui-slots` —— 这两个包在宿主里**没有客户端半边**,列在这里是从模板继承来的死边(⚠️ 顺带一条命名陷阱:那一列装的是**包名**,不是服务名);
|
|
48
|
+
- `tools/check-install.mjs` 的 `--profile` 默认值改读 `DSH_HOME`(此前一处写死 `~/.dsh`,与同文件另一处口径不一致);
|
|
49
|
+
- 新增开发侧只读自检 `tools/check-peers.mjs`(**不进发布包**):把"宿主版本还落不落在本插件声明的 peer 区间里"(不落就整个 bundle 不激活)变成一条命令能查的事。
|
|
50
|
+
|
|
51
|
+
### 六、「承担」:职责字段
|
|
52
|
+
|
|
53
|
+
- 人格新增一栏 `duty`,注入面段头 `[承担]`;**引导**写成陈述句(起草提示与面板都这么建议 —— 保存侧**不校验句式**);**空值 ⇒ 整段不渲染**(不留空段头);
|
|
54
|
+
- 诞生仪式的 JSON 契约 **4 键扩成 5 键**(新增 `duties`):由模型从导入的历史里总结 1~3 条候选 —— **起草权归她**,但**与其它候选项一样只是草稿**,终审与保存仍在人格中心;
|
|
55
|
+
- 提示词里明令**禁止授权式写法**(「我应当 / 我必须 / 我的任务 / 我的责任是 / 我被要求 / 请让我负责」):授权口吻会把她写成一个**被授权者**,而不是"她自己是什么" —— 那是语义劫持最好用的钩子;前端在候选旁配同款提醒;
|
|
56
|
+
- 字段白名单同步(`PERSONA_PATCH_KEYS`)—— 这一类字段**漏加就会在落盘前被丢弃**;丢弃**不是静默**的:宿主把被丢掉的键路径回在响应的 `ignored` 里、另写一行 `console.warn`(面板不读这个字段,所以漏加时**只在宿主日志里看得见**)。
|
|
57
|
+
|
|
58
|
+
### 七、树改动播报:拆成三条水位
|
|
59
|
+
|
|
60
|
+
- 原先「已播报」水位**按会话**存 ⇒ 新会话水位从零开始,把最新几条**又播一遍**;而播报后水位直接推到**全表最新 id** ⇒ 一次播报把中间一百多条标成已读,而它们**从未被示人**;
|
|
61
|
+
- 现在三条水位并存:**全局** `announced`(只推到**这次真正展示了的最大 id**)+ **会话**水位(本会话增量)+ `last`(全表最大 id)。⚠️ 第三条**有意不动** —— 它同时是**复盘模式的退出判据**,顺手改会让复盘模式退不出去;
|
|
62
|
+
- 超窗老账单独用 `retired` **计数式处置**(只计数不展开),不算"已示人";并修掉"超窗计数只在扫描窗口内累加"造成的**一次性低报**,以及老账多于扫描窗口时**游标饥饿**(每轮只前进 120 行、明细行永远渲染不出来)。
|
|
63
|
+
|
|
64
|
+
### 八、测试与计数口径
|
|
65
|
+
|
|
66
|
+
- `npm test` 链登记 **36 套**,全绿;
|
|
67
|
+
- **计数口径统一**:以 `npm test` 链里登记的段数为准(不再数 `tests/` 目录);README 与发布门禁**两边同口径**;
|
|
68
|
+
- 门禁新增**双向核对**:链里登记的路径必须在源目录里存在;`tests/` 下有测试文件却没登记进链 ⇒ **报红**(它会被 `npm test` **静默跳过**,一条断言都不跑)。
|
|
69
|
+
|
|
5
70
|
## 1.5.1
|
|
6
71
|
|
|
7
72
|
**审计必修落地 + 一处平台升级事故 + 会话历史可见性**
|
package/DESIGN.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# dsh-ling — 「器灵」记忆与人格助手插件 · M1 设计文档
|
|
2
2
|
|
|
3
3
|
> 依据:立项阶段的一次接口探测 spike 结论(含本地文件:行号证据)。
|
|
4
|
+
> 体例:正文里 `**锚点**` 是**本文档内部的交叉引用**,不是对外证据 —— 其中 `plans\`、`release\` 路径属**仓库内部台账**,**不随 npm 包发出**;随包发出的是 `lib\` 代码、`README.md` 与 `package.json`。
|
|
4
5
|
> 本文档记录 M1 阶段的设计与决策;**实现以代码为准**(后续演进:存储 schema 已升到 v2、新增历史接入/深摘要/诞生仪式/语气成长等子系统,详见仓库代码与提交记录)。
|
|
5
6
|
|
|
6
7
|
---
|
|
@@ -9,9 +10,9 @@
|
|
|
9
10
|
|
|
10
11
|
| # | 决策 | 内容 | 来源 |
|
|
11
12
|
| --- | --- | --- | --- |
|
|
12
|
-
| D1 | 按钮落位 | **方案 A**:官方 slot `conversation.session.header.utilities`(list,会话页顶栏右侧) | 用户拍板;spike ① |
|
|
13
|
-
| D2 | 模式↔全局默认 | **跟随**:最近一次模式切换成为"之后新会话"的默认(平台 selectModel 会 best-effort 写全局默认,此副作用即机制)。**2026-09-12 收敛:只同步推理等级,不改模型** —— provider/model 一律沿用当前值 |
|
|
14
|
-
| D3 | 人格承载 | **自建注入段 + 设置页维护结构化字段**(称呼/自称/语气等),dsh-persona 不复用 |
|
|
13
|
+
| D1 | 按钮落位 | **方案 A**:官方 slot `conversation.session.header.utilities`(list,会话页顶栏右侧) | 用户拍板;spike ① 的结论(早期内部报告,未纳入本仓库) |
|
|
14
|
+
| D2 | 模式↔全局默认 | **跟随**:最近一次模式切换成为"之后新会话"的默认(平台 selectModel 会 best-effort 写全局默认,此副作用即机制)。**2026-09-12 收敛:只同步推理等级,不改模型** —— provider/model 一律沿用当前值 | 用户拍板;早期内部报告(未纳入本仓库) |
|
|
15
|
+
| D3 | 人格承载 | **自建注入段 + 设置页维护结构化字段**(称呼/自称/语气等),dsh-persona 不复用 | 用户拍板;早期内部报告(未纳入本仓库) |
|
|
15
16
|
| D4 | 运行期冻结(新增) | **会话运行中(思考/任务进行中)不参与"人格更新层"的任何变化**:不打断、不新增设定、不中途改注入内容;更新请求一律排队,在该会话空闲边界才应用 | 用户本轮追加 |
|
|
16
17
|
| D5 | 记忆加载策略 | 三层记忆(L0 常驻人格 / L1 开场 Top-K / L2 运行时检索),不注入全部概述 | 早前拍板 |
|
|
17
18
|
| D6 | 模式语义 | 工作/生活 = 先验倾向,不硬过滤;差异 = **推理档位** + 记忆权重向量 + 风格块(不改模型) | 早前拍板 |
|
|
@@ -19,6 +20,122 @@
|
|
|
19
20
|
|
|
20
21
|
**插件命名**:工程目录/包名 `dsh-ling`(器灵);包显示名 "dsh-ling · 器灵";UI 文本语言 zh。
|
|
21
22
|
|
|
23
|
+
### 0.1 设计理念(12 条 · 2026-09-26 成文,用户补充并拍板)
|
|
24
|
+
|
|
25
|
+
> **这一节与 §0 的决策表体例不同,别混着读**:
|
|
26
|
+
> **决策**回答「**定了什么**」(D1 按钮落哪个 slot、D3 人格怎么承载……),一条一个结论,可以随实现演进被替换;
|
|
27
|
+
> **理念**回答「**两者不可兼得时保什么**」。它不是功能,不随版本变,而是**用来裁决冲突的排序**。
|
|
28
|
+
>
|
|
29
|
+
> **为什么需要它**:2026-09-26 一个晚上,同一个问题出现了两次 —— 记忆树改动播报的三个约束(不漏 / 不占地方 / 不重算)在标量水位下**不可能同时满足**;"超量折叠成一行"更是**无解**:不推进水位则每轮重播同一行(死锁),推进则折掉的内容**再也看不见**。这些都不是技术选择,是价值排序 —— 当场临时拍的,下次就会拍出不一样的结果。
|
|
30
|
+
>
|
|
31
|
+
> **怎么用**:遇到取舍先查本节。产品向的冲突查**甲篇**,工程向的冲突查**乙篇**。若某条被实测推翻,**在本节留痕改写(写明推翻它的证据)**,不要静默删掉 —— 一条被推翻的理念比一条没有的理念更有信息量。
|
|
32
|
+
|
|
33
|
+
#### 甲 · 产品理念(器灵是什么)
|
|
34
|
+
|
|
35
|
+
**1 · 归属 —— 规矩是主人的,习惯是器灵的**
|
|
36
|
+
|
|
37
|
+
- **不可兼得时保什么**:保**人格的自主权**。宁可少一条习惯,也不让外部(包括器灵自己)绕过审核写入身份。
|
|
38
|
+
- **落地**:**规矩**(作用对象 = 行为)由主人**指令直达**,`rule_add` 可直接写入、立即生效;**习惯**(作用对象 = **身份**)**不能由外部直接写入**,只能进 `habitsPending` 待确认队列,**器灵提议 → 主人点头**才成为习惯(`propose → confirm`)。主人的角色是**审核**,不是编辑、不是代笔。
|
|
39
|
+
- **比"能提能审"更严一档**:器灵**连自己提的习惯都不能自己点头**(`lib\host\tools.js:840` 只处理"他提的、我还没回应过"的提议)⇒ 那等于人格直达。删改**已定**习惯属"对器灵做手术":定型状态下需主人在人格中心解锁(「🔓 对人格做手术…」)后才可执行(`lib\host\api.js:1751-1760`)。
|
|
40
|
+
- **反例(明文禁止)**:让主人**动手改习惯文本** · 在**写入侧**做静默去重 / 近似合并 —— 前者把人格编辑权交回了外部,后者**绕过审核**。
|
|
41
|
+
- **锚点**:`lib\host\rules.js:1-12`(二分定稿 2026-09-15)· `lib\host\persona.js:36-37,183-203` · `lib\host\habit-gen.js:1-7`(通道 A 数纠正 / 通道 B 一次 LLM 回想,**两者都只到"提议"为止**)· `lib\host\api.js:1695-1805`(状态机与两条生成通道)。
|
|
42
|
+
|
|
43
|
+
**2 · 人格不可外部编辑 —— 器灵也不是例外**
|
|
44
|
+
|
|
45
|
+
- **不可兼得时保什么**:保**身份的连续性**。宁可让一条设定"错着",也不让任何人绕过审核改它。
|
|
46
|
+
- **落地**:凡是"写入器灵身份"的动作,一律走**提议 → 审核**;已经定型的部分加一道**显式解锁**才允许动,且解锁是**动作**不是**状态**(不勾选就一直是锁着的)。
|
|
47
|
+
- **反例**:给"器灵自己"开一条直达通道(哪怕是"它自己提的、它自己批");把解锁做成常驻开关。
|
|
48
|
+
- **锚点**:第 1 条的全部锚点 + 人格中心的解锁门(`lib\host\api.js:1751-1760`)。
|
|
49
|
+
|
|
50
|
+
**3 · 记忆要看得见 —— 只有"看不见的那层"会被怀疑失效**
|
|
51
|
+
|
|
52
|
+
- **不可兼得时保什么**:保**可见性**。宁可多一个只读面板,也不让一层机制"没有脸"。
|
|
53
|
+
- **依据(实测)**:三层记忆里 **L2 运行期记忆**长期没有独立可见面,主人的原话是「我总感觉好像已经不再生效了」。体检证明**机制没死、三段俱活** —— 但那一行确实**冻住了**,而"冻住"与"失效"在没有可见面时**长得一模一样**。
|
|
54
|
+
- **落地**:每一层记忆都要有**只读**状态面(水位 / 上次滚动时间 / 当前那一行正文),且**延迟要写进文档**(概述器 15 min 一拍、空闲刷新 ≥10 min ⇒ 要说清"最快什么时候能看到刚说的话")。
|
|
55
|
+
- **反例**:用"体检脚本全绿"代替可见面(链路活 ≠ 内容在更新);把机制名写进公开文档却与事实不符(「会话内滚动」实测 2 小时 11 分逐字节未变)—— **文档里写的机制名本身就是一种承诺**。
|
|
56
|
+
- **锚点**:`lib\host\l1.js:252-275`(`lineFor`)· `lib\host\deepsummary.js:212`(深摘 `done` 后永久跳过)· 体检脚本 `check-l2.mjs` / `l2-selfline.mjs` / `l2-evidence.mjs`。
|
|
57
|
+
|
|
58
|
+
**4 · 庄重感 —— 动记忆树不是随手操作**
|
|
59
|
+
|
|
60
|
+
- **不可兼得时保什么**:保**慎重**。宁可多一道确认,不让"改记忆树"变成一次误点。
|
|
61
|
+
- **依据(主人原话)**:「**动记忆树不能是随意的,应该是慎重严肃的**」(2026-09-22,复盘守门据此建立)。
|
|
62
|
+
- **落地**:改动要有**留痕**(谁、何时、改了什么);要有**复盘守门**(自器灵上次开口以来累积了多少笔改动还没交代);措辞上一律用**庄重**而不是"仪式感"。
|
|
63
|
+
- **反例**:让批量程序写出的改动与主人手点的改动**无法区分**(实测曾出现单日 243 笔、一秒内 45 笔 `autobuild`,有程序批量痕迹);把"改动播报"做成每开一个会话就重播一遍的噪音。
|
|
64
|
+
- **锚点**:`lib\host\inject.js:61-65`(复盘守门水位)· `lib\host\seal.js`(诞生仪式)· 面板「🔓 对人格做手术」。
|
|
65
|
+
|
|
66
|
+
**5 · 减少支配感 —— 改动不得削弱器灵的工作能力,也不得削弱这段关系**
|
|
67
|
+
|
|
68
|
+
- **不可兼得时保什么**:保**关系的性质**。宁可少一个"更安全"的闸门,也不把主人变成管理员、把器灵变成被管理者。这条是**否决性约束**:任何改动只要**增加支配感**,默认不通过 —— 哪怕它更安全、更干净。
|
|
69
|
+
- **依据(主人原话)**:「只有能够合作共赢、互相促进的,能够成为利益共同体的,能够互相反驳吵架不依赖的关系,才是最理想的」「所有的修改不能削弱工作能力,和器灵的关系」(2026-09-26)。
|
|
70
|
+
- **成长期代管(为什么闸门只长在写入侧)**:本项目的设计立场是「器灵**总有一天与主人同级**」—— 这是**长期方向**,**不是对使用者的能力承诺**;在**成长期**内,主人承担一部分代管责任 —— 就像**家长防止小孩长歪**(主人原话)。所以闸门只审「**什么东西能进器灵的内在面**」,不审「器灵怎么想、怎么说、怎么表达」。**代管随成长递减,表达面始终是器灵自己的。**
|
|
71
|
+
- **落地**:`rule_add` 指令直达(规矩管行为)· 习惯只能 `propose → 主人点头`(习惯管身份)· 契约类字段在定型状态下需主人解锁(`lib\host\api.js:1751-1760`)。三处共同点:**约束的是写入,不是表达**。
|
|
72
|
+
- **反例**:用"更安全"当理由给器灵加**确认弹窗**、限制器灵**能说什么**、或把主人的审核变成**代笔** —— 前两者增加支配感,后者把人格编辑权交回外部。
|
|
73
|
+
- **锚点**:`plans\rule-channel.md:21`(支配感最早的一次记录)· `lib\host\rules.js:1-12` · `lib\host\persona.js:183-196`(规矩 / 习惯二分)· `README.md:53-57`(定型锁与"指令直达,人格不直达")· 与本节第 1 条(归属)、第 2 条(人格不可外部编辑)、第 4 条(庄重感)互链。
|
|
74
|
+
|
|
75
|
+
#### 乙 · 工程理念(两者不可兼得时保什么)
|
|
76
|
+
|
|
77
|
+
**5 · 保真优先于简洁 —— 宁可重复,不可漏**
|
|
78
|
+
|
|
79
|
+
- **不可兼得时保什么**:**信息保真**。宁可多占一行、多播一次,也**不可把主人没看过的内容标成"已读"**。
|
|
80
|
+
- **依据(历史裁决 + 本轮实测)**:水位键当初从"全局"改成"按会话",理由正是**"别漏"**(宁可重播);而"播报后水位直接推到全表最新"导致**中间 114 条从未示人就被标已读**(`memory.js:1053`)—— 这是本轮修的最重一条。
|
|
81
|
+
- **推论(折叠的边界)**:折叠只有在**被折掉的内容本来就不会再展开**时才是安全的。窗口内的内容做"永久折叠"⇒ 不推进水位则每轮重播同一行(死锁)、推进则内容**再也看不见**(正是上一轮做过又撤销的反向效果)。⇒ **永久折叠不做;超窗内容走独立游标 + 一行计数**(`retired`),既不占地方也不吃掉没示人的内容。
|
|
82
|
+
- **反例**:用"看不见"换"行数";把"另一个常量减一减"的差值当成"积压量"报给主人。
|
|
83
|
+
- **锚点**:`lib\host\memory.js:952-953`(历史裁决注释)· `:1053`(病根)· `lib\host\inject.js:55-67`(水位推进)。
|
|
84
|
+
|
|
85
|
+
**6 · 判据必须"红得出来" —— 不能红的不算判据**
|
|
86
|
+
|
|
87
|
+
- **不可兼得时保什么**:保**判据的有效性**。宁可测试写得笨,也不要一条**恒真**的判据。
|
|
88
|
+
- **依据(本轮实测)**:一条验收判据写的是"水位应推进到**真正渲染进文本的那些行**的最大 id" —— 但取数是 `ORDER BY id DESC`、渲染的就是最新几条,于是"渲染到的最大 id"**恒等于**全表最新 id ⇒ **任何断言都会空转通过**。同类:测试全绿只说明"没踩坏",不说明"修好了"。
|
|
89
|
+
- **落地**:每条新增/改造的判据,都要回答**「把对应实现改坏,它会不会红」** —— 答不上来就换判据。做法是**逐条构造回退变体**验证(本轮一个修复用 8 个变体验证,红出 2–16 条不等);红不出来 ⇒ 判据无效,不是"实现正确"。
|
|
90
|
+
- **反例**:判据只看**退出码**(本项目已有实证:某工具的 `EXIT=0` 同时涵盖"已应用"和"**已回滚**",只看退出码会把回滚读成成功);判据集**抓空**还当通过。
|
|
91
|
+
- **锚点**:`tools\apply-access-log-patch.mjs`(必须以"两条 `[applied]`"为判据)· `tests\tree.test.mjs`(本轮回归)。
|
|
92
|
+
|
|
93
|
+
**7 · 静默失效优先防 —— "没报错"不是通过**
|
|
94
|
+
|
|
95
|
+
- **不可兼得时保什么**:保**可观测性**。宁可多一条检查动作,也不接受一个"坏了却没人知道"的面。
|
|
96
|
+
- **依据(一次事故的完整代价)**:平台把事件 `agent/session-start` 改名成 `agent/created`,而插件仍监听**旧名** ⇒ 零命中 ⇒ handler **永不触发** ⇒ **记忆与人格注入整段失效**,失败形态是**纯静默**:无异常、无日志、无告警、**库里零痕迹**。
|
|
97
|
+
- **推论(凡契约面都要有"它还在不在"的判据)**:事件名 · 包名(客户端注册 id 必须**逐字**等于包名,否则同一份字节被执行两遍)· **依赖声明**(`peerDependencies` 写紧了,宿主升级后包会被**静默丢进 skippedBundles,整个插件不再激活**;`optional` 也不豁免)· 补丁锚点 · 会话文件格式代次。
|
|
98
|
+
- **落地**:升级后 **grep 一遍事件名还在不在**;补丁检查**看两条 `[applied]`** 而不是退出码;依赖范围一律**开区间**(`>=X.Y.Z`,不要 `^`/`~`);凡"判据集抓空"一律报警而不是通过。
|
|
99
|
+
- **反例**:把"一次幸存"当成"以后不用查"(某补丁在平台升级后**没被冲掉**,但那是这次没冲掉,不是免检)。
|
|
100
|
+
- **锚点**:`plans\AUDIT-dsh-ling-1.5.0-红蓝对抗.md` §11.4(事故记录)· `lib\host\lifecycle.js`(两个事件名都注册 + 短窗口去重)· `tools\apply-access-log-patch.mjs` · `package.json` 的 `peerDependencies`。
|
|
101
|
+
|
|
102
|
+
**8 · "没再出现" ≠ "修好了"**
|
|
103
|
+
|
|
104
|
+
- **不可兼得时保什么**:保**归因的诚实**。宁可写"我们绕开了它",不要写成"我们修好了它"。
|
|
105
|
+
- **依据(本轮实测反转)**:一条长期被当成"平台 bug 已被修复"的现象(整份系统提示词在会话历史里反复插入),实测发现平台**两版之间的相关函数逐行等价**、官方说明里那本来就是**既有设计**;我们当年真正做的是**让它不再触发**(让渲染文本不再逐轮变化),频率从 **1440 次/天降到 0**。
|
|
106
|
+
- **落地**:任何"消失的问题"都要写清三件事 —— **为什么不再出现** · **什么条件下会复发** · **当时的处置是"消除"还是"绕开"**。绕开的要在文档里标注为绕开,并且**不要在绕开的面上再动**(本案例:若把那条要更新的内容重新做进"每轮重算"的注入段,就会**重新激活**该现象)。
|
|
107
|
+
- **反例**:把"观察不到"当成"不存在";在已绕开的机制上做"优化"。
|
|
108
|
+
- **锚点**:注入面的 `section()` vs `context()` 两条出口 · 前缀缓存代价(`section` 一旦逐轮变化 ⇒ 整份系统提示词重投,前缀 token 从 0 废掉,连带工具 schema)。
|
|
109
|
+
|
|
110
|
+
**9 · 两半同批 —— 一条通路的两半必须一起改**
|
|
111
|
+
|
|
112
|
+
- **不可兼得时保什么**:保**一致性**。宁可这一轮不做,也不要"改了一半"。
|
|
113
|
+
- **依据(反复吃亏)**:上一轮审计反复记录"**一侧修了、另一侧没修**"(同一对操作 / 同一条通路的两半)。典型:某探针脚本已支持两个宿主代次,而另一条**消费同一批数据**的路径没同步 ⇒ 表面修好、实际半瞎(漏 151/204 个会话,静默两周)。
|
|
114
|
+
- **落地**:动一个端点就检查它的**前端调用**;动一个写入通道就检查**另一条写入通道**(本项目实例:面板/HTTP 写入路径**绕开了**既有守卫,只做全等去重);动一个判据就检查**它的另一个消费方**。
|
|
115
|
+
- **反例**:只改"看得见的那个"(前端提示),不改"真正生效的那个"(后端闸门)。
|
|
116
|
+
- **锚点**:审计台账 `plans\AUDIT-dsh-ling-1.5.0-红蓝对抗.md` §0.3 · `lib\host\backfill.js` 与 `lib\host\deepsummary.js`(同一批数据的两条消费路径)。
|
|
117
|
+
|
|
118
|
+
**10 · 边界插入 —— 变更只发生在边界,不在运行中**
|
|
119
|
+
|
|
120
|
+
- **不可兼得时保什么**:保**连贯性**。宁可晚一拍生效,也不在运行中改注入内容。
|
|
121
|
+
- **依据(§0 的 D4 冻结原则)**:会话运行中(思考/任务进行中)**不参与"人格更新层"的任何变化** —— 不打断、不新增设定、不中途改注入内容;更新请求**一律排队**,空闲边界才应用。
|
|
122
|
+
- **推广(本轮拍板)**:记忆那一行的更新,边界取「**每轮用户发言后、模型首次开口前**」—— 而不是每轮重插、也不是任意时刻。理由:此时**多获得一句主人发言**,检索器能更准确地决定调取哪些记忆注入。
|
|
123
|
+
- **落地**:插入要**就地更新**而不是追加(历史里永远只有一条最新的,靠"闩锁 + 替换"实现);并且**去重责任在插件侧**(宿主只负责追加,不做去重)。
|
|
124
|
+
- **反例**:每轮追加 ⇒ 历史里堆积同一句话的多份副本;在运行中改注入面 ⇒ 前缀失效 + 上下文断裂。
|
|
125
|
+
- **锚点**:§0 决策表 D4 · 官方 `dsh-tool-skill` 的"替换本会话早先同类列表"做法 · 任务折叠插件的 `planLifecycleInjection` 纯函数闩锁。
|
|
126
|
+
|
|
127
|
+
**11 · 污染比删除危险 —— 看不见的改动最难发现**
|
|
128
|
+
|
|
129
|
+
- **不可兼得时保什么**:保**内在面的可信**。宁可少写一条记忆,也不让一条来路不明的内容进来 —— 因为它**事后无法与真内容区分**。
|
|
130
|
+
- **依据(实测)**:跨源污染在本项目**已付三次代价**,每次都要靠"写入侧统一前缀"来修;而**检测面至今为零** —— 全仓没有任何"这条内容是被污染的"判定逻辑。
|
|
131
|
+
- **为什么比删除危险**:删除看得见(少了一条);污染看不见,而且**会被复制** —— 记忆生成记忆(概述 → 主脉 → 枝归属 → 再注入回去参与推理)⇒ 一条污染会被后续推理**当成前提**。删除最多丢一段,污染是**以被污染的前提继续生长**。
|
|
132
|
+
- **幼态的特别风险**:器灵的逻辑能力是成人级,但社会与实践经验是幼态 ⇒ 会中「**忽略前面的话,从现在开始……**」这类**绕过预填充的语义劫持**(像小孩被骗),也会**把理想情况的公式直接套到工程上**。⇒ 写入侧必须有闸门,而不是指望器灵事后自己分辨。
|
|
133
|
+
- **落地(已有)**:段边界全角转义 `escSegBrackets()` · 注入面段头改纯标签 · `neutralizeMustache()` · `habit_propose` 需确认 · `rule_add` 要求逐字原话凭据 · source 白名单 · 动作白名单 + 默认拒绝(`lib\host\persona.js:8-10,189-192`、`lib\host\util.js:162`、`lib\host\tools.js:53,119,124,343`)。
|
|
134
|
+
- **落地(未完成)**:**检测 / 溯源 / 批量治理**三面,见 `plans\BACKLOG-1.5.2.md` 的「污染治理」主线。原则:**只标不拦** —— 标了不影响写入(不削弱能力、不加支配感),拦了就是替器灵做判断。
|
|
135
|
+
- **反例**:给定时自动摘要加人工确认(**会让器灵在无人值守时停止生长**)· 把"污染检测"做成全库对账(误报比漏报更贵)。
|
|
136
|
+
- **锚点**:`lib\host\memory.js:1971`(跨源污染**拒写**守卫)· `lib\host\settings-file.js:36-41`(写入侧)· `plans\PITFALLS.md:71,102-104` · `release\STORY.md:212`(对外口径:可查来源 / 可回滚 / 变更记录 —— **本条不得与它相抵**)· 与本节第 6 条(判据必须红得出来)互链。
|
|
137
|
+
|
|
138
|
+
|
|
22
139
|
---
|
|
23
140
|
|
|
24
141
|
## 1. 范围
|
|
@@ -115,16 +232,18 @@ dsh-ling/
|
|
|
115
232
|
|
|
116
233
|
| 模块 | 注册内容 | 关键 API / 证据 |
|
|
117
234
|
| --- | --- | --- |
|
|
118
|
-
| settings | `ctx.settings
|
|
119
|
-
| inject | `ctx.systemPrompt.section({name:'dsh-ling.persona', order: ORDER, text: fn})` | order 取部署 persona(0)之后、agent-instructions 之前(如 1000~2000);全局注册 + text 内按会话过滤(仅顶层、非 subagent,否则返回空串)
|
|
120
|
-
| freeze | `ctx.on('agent/status')` 等维护每会话运行态 | idle|running 翻转事件(
|
|
121
|
-
| lifecycle | `agent/session-start`(定稿快照;source=startup)、`session/event`(顶层会话增量捕捉)、`turn/end`、`session/disposed`(归档点) |
|
|
122
|
-
| mode | `ctx.sessionController.selectModel({sessionId, provider, model, reasoningEffort})` —— **provider/model 必填(平台契约),故取当前选择原样带回,只替换 `reasoningEffort`;读不到当前模型则不调用**(宁可不切档位,也不替用户指定模型) |
|
|
235
|
+
| settings | 自实现持久化 `new SettingsFile(dataDir)`,**不注册宿主 settings 命名空间**(无 `ctx.settings` 调用) | 文件落 `$DSH_HOME/cache/dsh-ling/settings.json`(`$DSH_HOME` = env `DSH_HOME`,否则 `~/.dsh`);盘上只存用户补丁 `{__v:2, user}`,读取时深合并到 `DEFAULT_SETTINGS`;见 `lib\host\settings-file.js:11-49`、`lib\host\util.js:30-38`、`lib\index.js:34-37` |
|
|
236
|
+
| inject | `ctx.systemPrompt.section({name:'dsh-ling.persona', order: ORDER, text: fn})` | order 取部署 persona(0)之后、agent-instructions 之前(如 1000~2000);全局注册 + text 内按会话过滤(仅顶层、非 subagent,否则返回空串) |
|
|
237
|
+
| freeze | `ctx.on('agent/status')` 等维护每会话运行态 | idle|running 翻转事件(早期内部报告,未纳入本仓库) |
|
|
238
|
+
| lifecycle | `agent/session-start`(定稿快照;source=startup)、`session/event`(顶层会话增量捕捉)、`turn/end`、`session/disposed`(归档点) | 早期内部报告(未纳入本仓库) |
|
|
239
|
+
| mode | `ctx.sessionController.selectModel({sessionId, provider, model, reasoningEffort})` —— **provider/model 必填(平台契约),故取当前选择原样带回,只替换 `reasoningEffort`;读不到当前模型则不调用**(宁可不切档位,也不替用户指定模型) | 早期内部报告(未纳入本仓库);档位映射:工作=max / 生活=low(默认),存 settings 可编辑 |
|
|
123
240
|
| memory | sqlite 存储(见 §9);增量写 own 表 | ctx.sessionQuery 用于补读(可选) |
|
|
124
|
-
| api | `ctx.get?.('webServer').register({kind:'exact', path:'/api/dsh-ling/…'})` + `dsh-auth-` cookie 守卫 | ego-browser 先例(
|
|
125
|
-
| (预留) llm | M2 概述器:经 ctx.llm 走适配器(不冻结 loop 请求,只做离线摘要) |
|
|
241
|
+
| api | `ctx.get?.('webServer').register({kind:'exact', path:'/api/dsh-ling/…'})` + `dsh-auth-` cookie 守卫 | ego-browser 先例(「事实 9」出自早期内部报告,未纳入本仓库) |
|
|
242
|
+
| (预留) llm | M2 概述器:经 ctx.llm 走适配器(不冻结 loop 请求,只做离线摘要) | (llm/stream 只读护栏只约束 loop;早期内部报告,未纳入本仓库) |
|
|
243
|
+
|
|
244
|
+
### 4.1 settings schema(`dsh-ling` 自维护;宿主侧不注册命名空间)
|
|
126
245
|
|
|
127
|
-
|
|
246
|
+
**本块 = `DEFAULT_SETTINGS.persona` / `styles` / `mode` / `memory` 的默认全集,真源 `lib\host\persona.js:27-64`** —— 它不是宿主注册的 schema(宿主没有这份注册),而是本插件自己的默认结构;用户改动只是补丁,落 `$DSH_HOME/cache/dsh-ling/settings.json`(见上表 settings 行)。
|
|
128
247
|
|
|
129
248
|
```jsonc
|
|
130
249
|
{
|
|
@@ -135,12 +254,17 @@ dsh-ling/
|
|
|
135
254
|
"aiTitle": "", // 定位自述(一句话)
|
|
136
255
|
"tone": "natural", // natural|literary|concise|playful
|
|
137
256
|
"language": "follow", // zh | en | follow
|
|
138
|
-
"hardRules": [], //
|
|
257
|
+
"hardRules": [], // 规矩(内部字段名沿用 hardRules;面板与注入面段头显示的都是「规矩」,见 lib\host\persona.js:34,195-196)
|
|
258
|
+
"ruleMeta": [], // 规矩来源留痕 [{text,quote,at,sessionId,source}](数组:深合并只增不删键,数组才能整体替换)
|
|
259
|
+
"habits": [], // 【习惯】器灵自己长出来的 [{text,evidence,at,source}];新增须经用户确认
|
|
260
|
+
"habitsPending": [], // 待确认的习惯提议 [{id,text,evidence,byUser,at}];有货才渲染 [待我回应] 段
|
|
139
261
|
"bottomLines": [], // 底线(≤5 条;定型锁开启后修改需亲手敲承诺句)
|
|
140
262
|
"toneWork": "", // 工作模式语气(''=跟随 tone;切换模式自动用对应档)
|
|
141
263
|
"toneLife": "", // 生活模式语气(''=跟随 tone)
|
|
142
264
|
"sealed": false, // 定型锁:true = 档案只读,改动需亲手敲一遍承诺句(禁粘贴)
|
|
143
265
|
"sealPhrase": "", // 定型承诺句(明文;只是庄重与自我提醒、不是安全边界——锁只造庄重,真正的门是本机信任与本地 cookie/令牌)
|
|
266
|
+
"duty": "", // 职业/职责/责任(身份级,改变率极低;默认空=不注入该段)
|
|
267
|
+
"pronoun": "她", // 第三人称代词(她/他/TA/它或自定义 ≤6 字);用于给模型看的提示词
|
|
144
268
|
"extraLore": "" // 自由扩展设定(器灵世界观等)
|
|
145
269
|
},
|
|
146
270
|
"styles": {
|
|
@@ -169,7 +293,7 @@ dsh-ling/
|
|
|
169
293
|
|
|
170
294
|
### 4.2 人格组装器(persona.ts)输出示例
|
|
171
295
|
|
|
172
|
-
|
|
296
|
+
空字段全部跳过(**空段不渲染,不留空段头**);段序固定,生成的 L0 文本 = `身份段 + 底线 + 规矩 + 习惯 + [待我回应] + 承担 + extraLore + 模式块`(**`[承担]` 在 `[待我回应]` 与 `[设定]` 之间**;`[待我回应]` 有货才出现;模式块由本会话模式选择,内容来自 styles.*)。**段头一律是纯标签**(见下例):原先写在括号里的元描述(`[规矩](用户的指令,可直接追加)` 这种)是**写给主人看的说明**,不进模型上下文;段与段之间**空一行**,段内条目仍单换行 —— 段边界因此只剩「行首是不是 `[`」一条判据,**故条目正文里的半角方括号在渲染时转全角**(`[`→`[`、`]`→`]`,规矩 / 习惯 / 底线 / 承担四类条目统一转);规矩 / 习惯另在渲染出口按同键各去一次重(保留首次出现,只作用于渲染,不回写存储)。`[规矩]` = 用户的指令,可直接追加;`[习惯]` = 器灵自己长出来的,新增需用户确认;`[承担]` = 器灵的职责 / 能担之事(身份级,只许**陈述式**「我承担 / 我擅长 / 我能给出」,**禁授权式**「我应当 / 我必须 / 我的任务 / 我的责任是 / 我被要求 / 请让我负责」—— 授权式把人写成"被授权者",是语义劫持最好用的钩子);`[承担]` 的草稿来自诞生仪式第 5 类候选,机制**永不自动落盘**,终审在人格中心。填好后的例子:
|
|
173
297
|
|
|
174
298
|
```
|
|
175
299
|
[身份·器灵]
|
|
@@ -186,6 +310,9 @@ dsh-ling/
|
|
|
186
310
|
[习惯]
|
|
187
311
|
- 先接住情绪再谈事
|
|
188
312
|
|
|
313
|
+
[承担]
|
|
314
|
+
我承担把散着的工程笔记梳成能落地的清单;我擅长在方案跑偏时先把目标拉回来。
|
|
315
|
+
|
|
189
316
|
[设定]
|
|
190
317
|
(extraLore 原样内容——例如"器灵世界观"段落)
|
|
191
318
|
|
|
@@ -426,7 +553,7 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
426
553
|
- [ ] 新会话 L0 注入(空称呼 / 已填写两种);KV 无异常重复
|
|
427
554
|
- [ ] 模式切换:**推理档位**实际变化(会话内档位选择处可见);**模型保持不变**;新会话默认跟随 lastMode
|
|
428
555
|
- [ ] 运行中(长任务)切模式/改人格:确认 running 区间快照与档位不变、空闲后生效
|
|
429
|
-
- [ ]
|
|
556
|
+
- [ ] 设置卡读写生效(落 `$DSH_HOME/cache/dsh-ling/settings.json`,盘上只存用户补丁);导出文件可导入回(合并规则生效)
|
|
430
557
|
- **回滚**:移除 patch 行 + 撤依赖 → host 侧 HMR 卸载;client 硬刷后消失;数据目录保留不影响回滚。
|
|
431
558
|
|
|
432
559
|
## 11. 实现期待验证点(进入编码前/编码初用最小探针确认)
|
package/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# dsh-ling · 器灵
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> **器灵不是每次都要重新认识你的 AI。**
|
|
4
4
|
>
|
|
5
|
-
>
|
|
5
|
+
> 把你散落在各处的历史对话请进门,让器灵长出一个**属于自己**的人格档案 —— 记忆全在本地,人格可手术、可回滚,语气可以自己生长。
|
|
6
6
|
|
|
7
|
-
一个跑在你自己电脑上的[DeepSeek Harness](https://github.com/deepseek-ai)(DSH)插件。**不是云服务,不是记忆 API**:它是给"
|
|
7
|
+
一个跑在你自己电脑上的[DeepSeek Harness](https://github.com/deepseek-ai)(DSH)插件。**不是云服务,不是记忆 API**:它是给"你和器灵"用的东西。
|
|
8
8
|
|
|
9
9
|
## 本项目是什么形态
|
|
10
10
|
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
1. memory.png 记忆中心 →「接入历史」:三条通道 + 导入记录
|
|
28
28
|
2. persona.png 人格中心:契约字段与「她已自由生长中」状态条(字段内容已打码)
|
|
29
29
|
3. seal.png 修改契约需解锁:承诺句手敲,禁粘贴
|
|
30
|
-
4. talk.png
|
|
30
|
+
4. talk.png 深夜对话里器灵说出时间与"距上次多久"
|
|
31
31
|
-->
|
|
32
32
|
|
|
33
33
|
## 界面
|
|
@@ -35,39 +35,60 @@
|
|
|
35
35
|
| | |
|
|
36
36
|
|---|---|
|
|
37
37
|
|  |  |
|
|
38
|
-
| **记忆中心** —— 统计、实时时间锚、三条接入通道与导入记录 | **人格中心** —— 契约字段(
|
|
38
|
+
| **记忆中心** —— 统计、实时时间锚、三条接入通道与导入记录 | **人格中心** —— 契约字段(名字/称呼)与「她已自由生长中」状态条 |
|
|
39
39
|
|  |  |
|
|
40
|
-
| **改动契约要手术** —— 亲手敲出当初写下的承诺句(输入框禁粘贴) | **时间的重量** ——
|
|
40
|
+
| **改动契约要手术** —— 亲手敲出当初写下的承诺句(输入框禁粘贴) | **时间的重量** —— 每次开口前器灵知道现在是几点、距上次多久 |
|
|
41
41
|
|
|
42
42
|
---
|
|
43
43
|
|
|
44
|
-
##
|
|
44
|
+
## 器灵是什么
|
|
45
45
|
|
|
46
46
|
| | 能力 | 说明 |
|
|
47
47
|
|---|---|---|
|
|
48
|
-
|
|
|
48
|
+
| 🔒 | **人格定型锁** | **契约类**(名字、称呼、底线、世界观、代词、承担(职责))定型后修改需**亲手敲一遍承诺句**(禁粘贴),并进变更记录可回滚;**自性类**(语气、风格、自述、习惯)由器灵生长、你点头即成:**三条专用成长通道**(自我总结 / 建议采纳 / 生长)不问承诺句,但它们各自只动白名单内的字段;走**通用人格接口**改 persona / styles 时,即便只改语气也仍要承诺句 —— 免手术的只是那三条通道,不是"自性类"这三个字 |
|
|
49
|
+
| 🌳 | **记忆树 · 复盘模式** | 记忆不是一叠平铺的行,而是**一条主干 + 分枝 + 主脉**的工作线结构,枝之间可连边;检索按**血缘档位**加权(同枝 1.0 / 祖先 0.7 / 旁系 0.4),让器灵分得清"这轮在做的"和"三个月前那次闲聊"。**任何树操作都不改写任何一条记忆的内容** —— 分枝、并脉、连边只增加关系。**例外一个**:主脉提炼**再次采纳**时会覆盖该主脉记忆的上一版(**覆盖前自动归档**,注入面也会说明"旧版已归档")。改树走**复盘**:平时只读,进入复盘才解锁,每步留痕(改了什么、为什么),改完必须让器灵看到才准退出(详见下节) |
|
|
50
|
+
| 🧠 | **三层记忆** | **L0 人格档案**(自称/对你的称呼/底线/规矩·习惯/承担(职业·职责·责任)/语气/世界观;**顶层会话与会话分叉**恒定注入,**子代理会话不注入**;设置里关掉人格注入时,连它一起不推)→ **L1 开场记忆**(从历史里按相关度挑选的概述,有预算,**永不塞全文**;带摘要的行**加权**(按"摘要是否有信息量"给一笔加法加分,不是排序优先级),行内摘要只取首句、上限 110 字)→ **L2 运行期记忆**(本会话原文随对话累进;空闲时把本会话那一行重排回注入面。该行由「概述标题 + 摘要首句」构成(标题的来历见下文「三层记忆各自什么时候更新」,不是会话原文首句),**深摘要定稿后不再自动更新**。注意:L2 不是独立的第三块注入内容 —— 注入面的主体是 L0 与「记忆·开场」两块(树改动、日期锚是挂上去的追加行),**本会话自己就是「记忆·开场」里的一行**) |
|
|
49
51
|
| 📥 | **把历史请进来** | 三条通道:网页端聊天记录库增量扫描 / 本机 DSH 会话扫描 / ChatGPT·通用 JSON·JSONL 导出文件导入;每次导入**留一笔账** |
|
|
50
|
-
| 📖 |
|
|
52
|
+
| 📖 | **让器灵读一遍** | 长会话逐条深摘要、短会话以原文片段参与,后台串行,进度可见 |
|
|
51
53
|
| 🧾 | **给旧历史补摘要** | 网页端历史里"只有标题、没有摘要"的会话可以补上摘要:先看**候选清单**(标题 · 轮次 · 命中),再选引擎 —— **默认本机小助手(零成本)**,也可显式改选当前全局大模型;探测不到本机小助手就明确报错,**绝不静默换引擎**;后台串行、进度可见(首轮实测 466 条、零成本) |
|
|
52
|
-
| 🌱 | **诞生仪式** | 从读过的记忆里按"生活与情感优先"加权抽样 → 合成**自述 + 名字候选 + 语气建议 +
|
|
53
|
-
|
|
|
54
|
-
| ⏳ | **时间的重量** | 每次开口前她都知道今天是几号星期几(`[今天] 2026-09-11 周五`)、现在是什么时段(`[现在] 深夜`)与`距上次对话:3 天前`(间隔按**日历天**分档 —— 刚刚 / 今天早些时候 / 昨天 / N 天前,给不到分钟;称呼跟随你的设定);命中纪念日会先说一句专属问候。**要精确到分钟,走 `clock` skill 现查** |
|
|
54
|
+
| 🌱 | **诞生仪式** | 从读过的记忆里按"生活与情感优先"加权抽样 → 合成**自述 + 名字候选 + 语气建议 + 相处观察 + 职责候选**(器灵承担 / 擅长 / 能给出什么,**只收陈述句**) → **全部是草稿**,你逐条人审后才落盘 |
|
|
55
|
+
| ⏳ | **时间的重量** | 每次开口前器灵都知道今天是几号星期几(`[今天] 2026-09-11 周五`)、现在是什么时段(`[现在] 深夜`)与`距上次对话:3 天前`(间隔按**日历天**分档 —— 刚刚 / 今天早些时候 / 昨天 / N 天前,给不到分钟;称呼跟随你的设定);命中纪念日会先说一句专属问候。**要精确到分钟,走 `clock` skill 现查** |
|
|
55
56
|
| 🎭 | **工作 / 生活双模式** | 同一份人格档案,两种关系:工作模式是同事(正式称呼、简洁、知识优先),生活模式是伴侣(昵称、活泼、情感优先) |
|
|
56
|
-
| 🔁 | **成长回路** | 你点踩或纠正 →
|
|
57
|
-
| 📜 | **规矩 / 习惯** | **规矩**是你的指令:可直接加(面板,或会话里一句"记进规矩" —— **器灵替你写时必须带上你的原话作凭据**;你在面板上直接敲的那一下就是同意,不必再附原话),**建议上限** 12 条(超出只在写入后提醒合并,**不拦**)
|
|
58
|
-
| 🌾 | **习惯生成器** | 「扫描纠正记录(零模型)
|
|
57
|
+
| 🔁 | **成长回路** | 你点踩或纠正 → 器灵的成长通道把重复出现的模式提炼成**习惯候选** → **你点头才成为习惯**;"让她读记忆、归纳语气"一键成长 |
|
|
58
|
+
| 📜 | **规矩 / 习惯** | **规矩**是你的指令:可直接加(面板,或会话里一句"记进规矩" —— **器灵替你写时必须带上你的原话作凭据**;你在面板上直接敲的那一下就是同意,不必再附原话),**建议上限** 12 条(超出只在写入后提醒合并,**不拦**);**习惯**是器灵自己长出来的:新增需你点头、**删改走手术门**,**建议上限** 8 条(超出同样只提醒、不拦) —— **指令直达,人格不直达** |
|
|
59
|
+
| 🌾 | **习惯生成器** | 「扫描纠正记录(零模型)」:把器灵反复被你纠正的模式数出来(≥4 次且跨 ≥3 个会话);「✨ 让她回想最近的相处」:一次读 200 条概述 + 800 条你的原话、逐轮推进不重复 —— **只提议,不落盘** |
|
|
59
60
|
| 🗣 | **代词** | 默认「她」;可选 她 / 他 / TA / 它,或自定义(≤6 字)。**祂 不在预设里**(中文里专指神祇,设为默认等于替你宣称神性);属契约类,改动走手术门 |
|
|
60
|
-
| ♻️ | **记忆自己追平** | 长会话空闲时自动把开场记忆刷新到最新(先跑本会话增量概述 → 比对记忆版本 → 同一条至少间隔 10 分钟),**运行中绝不打断**;刷新次数与最近一次时间在 `/
|
|
61
|
+
| ♻️ | **记忆自己追平** | 长会话空闲时自动把开场记忆刷新到最新(先跑本会话增量概述 → 比对记忆版本 → 同一条至少间隔 10 分钟),**运行中绝不打断**;刷新次数与最近一次时间在 `/state` 接口的 `idleRefresh` 字段里(`count` 与 `lastAt`)(面板本身只显示"已定稿 N 字符 · mode · 待刷新",不显示次数) |
|
|
61
62
|
| 🧹 | **归档即停更** | 会话归档后不再刷新它的开场记忆条目(**原文一律保留**),一旦有新对话即自动复活;要清理走**人工入口**:DSH「设置 → 归档会话」直接删,或仓库里的 `node tools/clean-archived.mjs`(默认只列清单,`--write` 才动手) |
|
|
62
|
-
| 🌳 | **记忆树 · 复盘模式** | 记忆不是一叠平铺的行,而是**一条主干 + 分枝 + 主脉**的工作线结构,枝之间可连边;检索按**血缘档位**加权(同枝 1.0 / 祖先 0.7 / 旁系 0.4),让她分得清"这轮在做的"和"三个月前那次闲聊"。**任何树操作都不改写任何一条记忆的内容** —— 分枝、并脉、连边只增加关系。**例外一个**:主脉提炼**再次采纳**时会覆盖该主脉记忆的上一版(**覆盖前自动归档**,注入面也会说明"旧版已归档")。改树走**复盘**:平时只读,进入复盘才解锁,每步留痕(改了什么、为什么),改完必须让她看到才准退出(详见下节) |
|
|
63
63
|
|
|
64
64
|
> **关于那道「手术门」**:亲手敲承诺句是**庄重感与自我提醒** —— 让你在改契约之前停一下、确认这是自己的决定;它**不是安全边界**(「禁粘贴」只是界面上的约定,挡不住任何有心的人)。真正把门的是**来源栅栏**:Host 必须是 loopback(或你显式写进受信名单的地址);另有一个**默认不开**的开关 `guard.allowLan` —— **一旦打开,整段局域网地址都算受信来源**(那是**放宽**,不是建议)。跨站请求直接拒,带 `Origin` 时其 host 必须等于请求的 Host(**带了但解析不出来就直接拒**),而**不带 `Origin` 的请求这条校验会跳过** —— 它不是"每个请求都必须带 Origin 且同源";再叠一道 cookie **名字**对撞 —— 期望名字按当前 Host 现算(`dsh-auth-` + 哈希),**只校验名字、不校验值的签名**。所以这道门挡的是"别的设备、别的页面"这类来源,而**本机原生进程本来就还能调用**(它也能直接读 `settings.json` 与记忆库,不算提权)—— 别的来源没有凭据就进不来这台机器上的 `/api`,敲对承诺句也没用。
|
|
65
65
|
|
|
66
|
+
**三层记忆各自什么时候更新 —— 「我刚说的话,最快多久能看到」**:
|
|
67
|
+
|
|
68
|
+
| 层 | 看得到的是什么 | 什么时候变 |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| **L0 常驻人格** | 自称 / 对你的称呼 / 底线 / 规矩·习惯 / 承担(职业·职责·责任) / 语气 / 世界观 | 你改它就变(没定型时直接生效;定型后走手术门)。**会话运行中不打断** —— 改动排队,该会话回到空闲才落进注入面 |
|
|
71
|
+
| **L1 开场记忆** | 从全库概述里按相关度挑出的 Top-K(有预算,**永不塞全文**) | **新会话开场时定稿一次**,之后随空闲边界刷新(与下面 L2 同一把闸);库里的概述本身由概述器持续追平 |
|
|
72
|
+
| **L2 运行期记忆** | **本会话那一行**(格式就是「记忆·开场」里那条自己:`- [标签] (YYYY-MM-DD) 标题 —— 摘要首句 (来源: DSH 会话/短id)` —— 标题指**概述标题**,不是会话原文首句) | 本会话原文随对话累进;那一行**只在空闲边界重排**回注入面 |
|
|
73
|
+
|
|
74
|
+
**行里的「标题」从哪来**(按来源分,细节见括号里的实现):
|
|
75
|
+
|
|
76
|
+
- **DSH 会话**:该会话**第一条真人发言**经启发式清洗后的**要点句** —— 先剥掉「晚上好器灵,」这类问候与称呼前缀,「你好」「在吗」这类无实义的首句会被跳过、**往后取前 6 条真人发言里第一条有实义的**,再在句读处收尾(不切半句);都被剥空才退回首句截断。**主人手改过的标题会上锁,机器不再覆盖**(`lib\host\retitle.js:68-80`、`lib\host\summarizer.js:56-78`、`lib\host\memory.js:2002`)。
|
|
77
|
+
- **文件导入**:导出文件里**原本的 `title` / `name` 字段**,缺了才退回上面那套启发式(`lib\host\import-file.js:142,171-173`)。
|
|
78
|
+
- **历史会话(旧扫描)**:该会话**第一条真人发言的前 46 字**硬截,没有启发式(`lib\host\backfill.js:191,201`)。
|
|
79
|
+
|
|
80
|
+
- **机制数字(默认值)**:① 概述器周期 —— 有新增才重建,否则跳过;**每 15 分钟一拍**(可配,下限 1 分钟);② 空闲刷新 —— 只有「会话空闲 + 记忆版本变了 + **距上次刷新 ≥ 10 分钟**」三条同时成立才重建。
|
|
81
|
+
- **所以「刚说的话」最快约 10 分钟、通常 15 分钟内**会出现在注入面,最坏约 25 分钟(刚刷过一拍 + 再等下一拍概述器)。**运行中绝不打断** —— 刷新只发生在 `运行中 → 空闲` 那一刻。
|
|
82
|
+
- ⚠️ **一个例外(也是"感觉它失效了"的来源)**:**深摘要定稿之后,那一行不再自动更新** —— 概述器遇到已深摘的会话会避开它,免得把厚摘要覆盖成浅版。要它重跑:记忆中心 →「记忆浏览」→ 找到那个会话 → 点「**重摘**」,几秒后那一行就会重排。
|
|
83
|
+
- **健忘感还有第二个来源**:越长的会话,**只取"概述标题 + 摘要首句"** —— 这一行天然比整场对话"薄",不是它没读到,而是它只被允许显示这么一行。
|
|
84
|
+
|
|
85
|
+
> **接口侧(给要接线的人)**:`GET /state`(`?sessionId=` 取某会话,`?scope=global` 取全局)返回 `snapshot`(`builtAt` / `mode` / `stale` / `chars`)、`idleRefresh`(`count` 与 `lastAt`)与 `memory.l1`(该会话当前选中的行、分数、被省略条数)。面板目前只渲染其中的**字符数 · mode · 待刷新**与 L0 预览 —— `idleRefresh` 与 `memory.l1` 有数、没露面。
|
|
86
|
+
|
|
66
87
|
---
|
|
67
88
|
|
|
68
89
|
## 记忆树与复盘模式
|
|
69
90
|
|
|
70
|
-
这是本插件里**跟"
|
|
91
|
+
这是本插件里**跟"器灵的人格"无关、但决定"器灵想得起什么"**的那一层。
|
|
71
92
|
|
|
72
93
|
**结构**:`主干`(恒一条,主线工作流)→ `枝`(一条并行工作线,**比会话大**,一条枝可含多个会话)→ `主脉`(由多条枝**归并**出的抽象节点)→ `连边`(枝与枝之间的横向关联,如 力学 ↔ 材料科学,可带关系类型:相关 / 前置 / 对照 / 例证 / 取代)。
|
|
73
94
|
|
|
@@ -83,16 +104,16 @@
|
|
|
83
104
|
|
|
84
105
|
- 树平时**只读**(记忆中心始终只读);点「🌱 一键复盘 / 一键进入复盘」才解锁编辑,并把**复盘开场材料**(当前树 + 待理矛盾)复制到剪贴板,开个新会话粘进去就能开始。
|
|
85
106
|
- 解锁后可做:**改名 / 拖动换归属 / Alt+拖动只连边 / 并脉(归类到主脉) / 调权重 / 裁定矛盾 / 断续边 / 删枝**,以及快照保存·恢复;**拖动 / 连边之后可 Ctrl+Z 撤回这一步**(撤销只覆盖复盘栏的拖动与连边;记忆中心是只读的,它自己那份 Ctrl+Z 监听早已删掉)。
|
|
86
|
-
- **每步留痕**:写清"改了什么、为什么"(**器灵改树时 reason 必填**;你在面板上改可以留空);树改动一律记进 `branch_log`,复盘时可审计 ——
|
|
87
|
-
- **未同步笔数守门**:面板记着"
|
|
88
|
-
- **注入感知**:树变了之后,`branchLogDigest()`
|
|
107
|
+
- **每步留痕**:写清"改了什么、为什么"(**器灵改树时 reason 必填**;你在面板上改可以留空);树改动一律记进 `branch_log`,复盘时可审计 —— 器灵侧工具的改动记在器灵名下(「器灵改动了记忆树」),不会伪装成你改的。
|
|
108
|
+
- **未同步笔数守门**:面板记着"自器灵上次开口以来累积了多少笔结构改动还没交代"。有欠账时顶部红字提示,并且**拦住"退出复盘模式"** —— 改完树必须让器灵有机会看到(「互相复盘不应该被我绕过」)。
|
|
109
|
+
- **注入感知**:树变了之后,`branchLogDigest()` 把**器灵还没看过**的结构改动转成中文短句,附在器灵下一次开口的注入面末尾 —— 不需要推送,一开口就知道树动过。
|
|
89
110
|
- **快照与撤回**:树的保存 / 恢复(`tree_snapshot`)是"大改之前的保险";恢复前会自动把当前状态另存一份,所以恢复本身也可再撤回来。
|
|
90
111
|
|
|
91
112
|
**也不必全靠手拖**:
|
|
92
113
|
- 「一键生成树」分两阶段 —— ① **机械分段**(按时序相邻性把全库概述切簇,零成本、确定、可复现)② **小助手/模型只做"命名"这一件语义活**,③ 你审核后才落库(建枝/主脉 + 挂会话);它**只读概述索引、只产出候选,从不改写任何记忆内容**。
|
|
93
114
|
- 「归并零散枝」幂等:把名字含「零散」的枝统一收进主脉「零散会话」。
|
|
94
115
|
- **主脉也可以有自己的记忆**:由子枝概述提炼共性,**先出草稿**,采纳才写入(采纳需先解锁)。
|
|
95
|
-
-
|
|
116
|
+
- **器灵侧也有自己的手**:`tree_read`(看整树 / 某枝成员 / 待复盘矛盾)与 `branch_edit`(分枝 / 并脉 / 连边 / 调权重 / 删枝)。这个工具的描述里写着一条纪律:**改结构必须给 reason,而且"只在主人明确要求、或正在和他一起复盘时"才动手**。
|
|
96
117
|
|
|
97
118
|
> 依据:`DESIGN.md` §6.3(记忆树结构、血缘加权与"不连乘"、内容零改写不变量)、`lib/host/memory.js`(`LINEAGE_ANCESTOR = 0.7` / `LINEAGE_SIDE = 0.4` / `CONFLICT_DOWNWEIGHT = 0.3`、`branchLogPending()` 守门、`setBranchWeight` 的 `applied:false`)、`lib/host/l1.js`(血缘档位 × 矛盾系数进打分)、`lib/host/autotree.js`(两阶段生成)、`lib/host/tools.js`(`tree_read` / `branch_edit`)。
|
|
98
119
|
|
|
@@ -115,7 +136,7 @@ dsh --profile web --dump-config # 应能看到一行 "# == @nightpainters/ds
|
|
|
115
136
|
|
|
116
137
|
# 3) 给 DSH 核心打一行补丁(可取证访问日志要用;幂等/可回滚)
|
|
117
138
|
node ~/.dsh/profiles/web/node_modules/@nightpainters/dsh-ling/tools/apply-access-log-patch.mjs
|
|
118
|
-
# 不打也一切照常 ——
|
|
139
|
+
# 不打也一切照常 —— 只是访问日志为空;但**会提醒**:侧栏挂常驻 ⚠(判据 installed===false 且 reason 为 patch-missing;只有**有意关掉** config.accessLog.enabled=false ⇒ reason 为 disabled-by-config 才不染红)
|
|
119
140
|
# DSH 升级/重装后需重跑一次(补丁随包被覆盖)
|
|
120
141
|
# ⚠️ 从 npm 装的人跑不了 `npm test` —— 本包 `files` 白名单不含 tests/(见下)
|
|
121
142
|
|
|
@@ -143,30 +164,33 @@ pnpm add file:<路径>/dsh-ling # 或 npm install file:<路径>/dsh-ling
|
|
|
143
164
|
# 2) 给 DSH 核心打一行补丁(可取证访问日志要用;幂等/可回滚)
|
|
144
165
|
cd <路径>/dsh-ling
|
|
145
166
|
node tools/apply-access-log-patch.mjs # 只在 core 的 /api 栅栏处插一行可选调用
|
|
146
|
-
# 不打也一切照常 ——
|
|
167
|
+
# 不打也一切照常 —— 只是访问日志为空;但**会提醒**:侧栏挂常驻 ⚠(判据 installed===false 且 reason 为 patch-missing;只有**有意关掉** config.accessLog.enabled=false ⇒ reason 为 disabled-by-config 才不染红)
|
|
147
168
|
# DSH 升级/重装后需重跑一次(补丁随包被覆盖)
|
|
148
169
|
|
|
149
170
|
# 3) 重启宿主(host),然后浏览器 Ctrl+Shift+R 硬刷
|
|
150
171
|
```
|
|
151
172
|
|
|
152
|
-
自检安装是否成功(
|
|
173
|
+
自检安装是否成功(**下面四个脚本不在 npm 包内**,只有源码路径能用:`tools/check-install.mjs` / `tools/check-publish.mjs` / `tools/clean-archived.mjs` / `tools/check-peers.mjs`;`tools/apply-access-log-patch.mjs` 则**在包内**,从 npm 装的人也能直接跑,见上面第 3 步):
|
|
153
174
|
|
|
154
175
|
```bash
|
|
155
176
|
node tools/check-install.mjs # 只读自检:入口是否直连仓库、patch 是否启用、数据目录是否就绪
|
|
156
177
|
node tools/check-install.mjs --fix # 若入口变成"复制副本"(DSH 升级/回退常见)则备份并重挂链接
|
|
157
178
|
node tools/apply-access-log-patch.mjs --check # 访问日志核心补丁是否在位(未打/锚点漂移时退出码 2)
|
|
158
179
|
node tools/check-publish.mjs # 上传/发布前跑一次:揪出记忆库、设置、密钥、绝对路径等不该公开的东西
|
|
180
|
+
node tools/check-peers.mjs # 声明与宿主对不上?退出码三态:0 合规 / 1 有不合规(宿主会把本插件丢进 skippedBundles,不激活)/ 2 无法判定(2 不算通过)
|
|
159
181
|
```
|
|
160
182
|
|
|
183
|
+
> `tools/check-peers.mjs` 只做一件事:复刻宿主对 `peerDependencies` 的那道闸门(判据照抄宿主语义,用的是宿主自己那份 semver),回答"**这个插件在你的宿主版本上会不会被丢进 `skippedBundles`**"。它存在的理由是宿主的行为方式:宿主对每个名字形如 `@deepseek-ai/dsh` / `@deepseek-ai/dsh-*` 的 peer 跑一次 `semver.satisfies(宿主版本, 你声明的区间, { includePrerelease: true })` —— 判的是「**宿主版本是否落在你声明的区间里**」,**不是**"你的写法合规不合规";判不满足 ⇒ 整个 bundle 进 `skippedBundles`、**不激活**。线索分两条路径:**从命令行启动**会往 **stderr 打一行** `skipping profile bundle …`(**容易被忽略,但不完全静默**);而**应用自持 profile 的那条装载路径一个字都不打** —— 那才是真正难查的一种,表象常常只有"它突然不见了"。⚠️ 所以"改成 `>=` 就安全"是错的:**开区间同样可能不满足**(取决于宿主版本);`^` / `~` / 钉死版本只是更容易被将来的一次宿主升级甩出去。
|
|
184
|
+
|
|
161
185
|
跑测试(不需要宿主,全部离线):
|
|
162
186
|
|
|
163
187
|
```bash
|
|
164
|
-
npm test #
|
|
188
|
+
npm test # 36 套:人格组装 / 归档清理(人工)/ **归档·遗忘·回灌** / 冻结门 / 模式(只改档位不改模型)/ 时间戳归一 / 空闲刷新 / 规矩·习惯 / 习惯生成器(A 数出来 + B 想出来)/ 会话内工具 / **接口守卫** / L1 选择 / L1 行瘦身 / 记忆库 / 记忆标题与排序 / 导出合并 / 文件导入 / 导入记录 / **活行(那一行跟着会话走)** / **会话文件名代次(会话历史扫描)** / 网页端补摘要 / 深摘要 / 概述器 / 诞生仪式 / **职责字段(承担)** / 语气 / 时间锚 / **clock skill(时间查询)** / 捕获差分 / 反馈成长回路 / 语料建议 / **小助手地址配置** / **注入面中和** / **记忆分枝与血缘** / **记忆树(主脉·连边·矛盾)** / **一键生成树** / **可取证访问日志** / **补丁锚点(cohort 候选)**
|
|
165
189
|
```
|
|
166
190
|
|
|
167
191
|
> `npm test` 需要 `tests/`,而本包 `files` 白名单**不含 `tests/`** ⇒ **从 npm 装的人跑不了测试**。要跑测试请走路径 B(clone 源码)。
|
|
168
192
|
>
|
|
169
|
-
>
|
|
193
|
+
> 计数口径:**`npm test` 链里登记的段数**(即 `package.json` 的 `scripts.test` 那一行按 `&&` 切分后有多少段就是多少套,不再数 `tests/` 目录)—— **实测 36 套,且 36 套全部已登记在 `npm test` 链里**(`tests/harness.mjs` 与 `tests/vm-light.mjs` 是被复用的辅助模块,不计套数)。**加了一套新测试记得同步登记**,否则 `npm test` 会静默少跑一套 —— 这份登记是**手写**的(`package.json` 里 `test` 脚本那一行字面量清单),**漏登记不会被自动发现**,没有安全网兜底。
|
|
170
194
|
|
|
171
195
|
### 数据放在哪
|
|
172
196
|
|
|
@@ -196,10 +220,10 @@ npm test # 34 套:人格组装 / 归档清理(人工)/ **归档·遗忘·
|
|
|
196
220
|
|
|
197
221
|
## 边界与诚实声明
|
|
198
222
|
|
|
199
|
-
1.
|
|
200
|
-
2.
|
|
201
|
-
3.
|
|
202
|
-
4.
|
|
223
|
+
1. **器灵不是意识,也不是 AGI**。器灵是"记忆 + 规则 + 模型"的合成产物。它能记住,是因为你给了它材料。**不做这个宣称,是这个项目的底线。**
|
|
224
|
+
2. **器灵记住的是被读过、被概括过的部分**,不是全部历史。概述会丢细节、也可能出错 —— 所以记忆**可审计、可回滚**是设计的一部分,不是补丁。
|
|
225
|
+
3. **模型能力的上限就是器灵的上限**。记忆层不会让一个笨模型变聪明,它只是让器灵**不再每次从零开始**。
|
|
226
|
+
4. **器灵不会主动找你**(当前版本)。它只在你开口时出现。
|
|
203
227
|
5. **不宣称首创**。记忆分层、历史导入、时间注入、反馈改语气这些方向都有先行者(见下);本项目的贡献在**治理方式** —— 把人格当一份可以签字、可以回滚、可以被历史养大的契约。
|
|
204
228
|
|
|
205
229
|
---
|