@nightpainters/dsh-ling 1.6.0 → 1.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.
- package/CHANGELOG.md +97 -11
- package/DESIGN.md +34 -25
- package/PHILOSOPHY.md +174 -0
- package/README.md +13 -6
- package/lib/host/api.js +21 -3
- package/lib/host/deep-index-skill.js +9 -0
- package/lib/host/l1.js +47 -3
- package/lib/host/lifecycle.js +11 -0
- package/lib/host/memory.js +20 -15
- package/lib/host/persona.js +0 -1
- package/lib/host/rawlog.js +17 -6
- package/lib/host/recall-search.js +148 -10
- package/lib/host/surgery.js +4 -1
- package/lib/host/tools.js +50 -16
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,92 @@
|
|
|
2
2
|
|
|
3
3
|
本项目遵循语义化版本:`MAJOR.MINOR.PATCH`。
|
|
4
4
|
|
|
5
|
+
## 1.6.2
|
|
6
|
+
|
|
7
|
+
**检索档换了一条快两个数量级的路 · 结果不再被"我刚说的话"占满 · 几处会误导人的地方修掉。**
|
|
8
|
+
|
|
9
|
+
> 一句话:1.6.1 之后继续修。最值得说的三件:① **按内容找原文从"每次 20 秒以上"变成"20~50 毫秒"**(改走本机库,不再依赖宿主那条例外慢的检索通道);② **搜索结果默认不再被"当前会话自己"占满** —— 此前搜什么都会先命中你几分钟前刚说的话,把真正要回溯的历史挤下去;③ **枝系数(复盘期给某个主题枝调的权重)真的开始生效了**,此前它只存不用。
|
|
10
|
+
|
|
11
|
+
> ✅ `package.json` 版本号为 **`1.6.2`**。**无 schema 变化**(仍是 v17);⚠️ 多数改动在 **host 侧** ⇒ **需重启宿主**才生效。新增一份对外文档 [`PHILOSOPHY.md`](./PHILOSOPHY.md)(理念:为什么值得做、凭什么这样做)。
|
|
12
|
+
|
|
13
|
+
### 一、按内容找原文:换了一条快两个数量级的路
|
|
14
|
+
|
|
15
|
+
- **宿主那条检索通道每次都要做一遍全量对账** ⇒ 实测**每次都超过 20 秒**(重启后第一次也一样,并发还会互相排队)。现在 ① 档**默认搜本机库内原文层**(实测 **20~50 毫秒**);要看全量(覆盖全部事件、含子代理会话)显式传 `hostSearch=true`。
|
|
16
|
+
- **如实标注覆盖范围**:本机库那份原文只含"真人消息 + 有正文的回复"(我自己的长回复**中段搜不到**)⇒ 回执里明写"原文来源:**本机库内原文层**",不说就会被读成"库里只有这些"。
|
|
17
|
+
- **原文层默认 5 轮**(此前 10 轮):条目层是结论、原文层是下钻材料,后者一多就把前者挤出视野。
|
|
18
|
+
- **超时不再是挂死**:宿主路有 20 秒上限,超时**照样把条目层交出去**并说明"没跑完" —— 此前实测等过 **30 分钟**,且当时没有任何中断入口。
|
|
19
|
+
|
|
20
|
+
### 二、搜索结果默认排除"当前会话自己"
|
|
21
|
+
|
|
22
|
+
- 症结:当前会话也在库里、而它的话最新 ⇒ **任何词都能命中它**(实测搜"检索"时 10 条命中里 **9 条是本会话**),要回溯的历史被整页挤掉。而"我刚说过什么"**根本不需要搜** —— 它就在上下文里。
|
|
23
|
+
- 现在:**默认整条排除**,并如实报出"另有 N 轮命中属于当前会话自己,已排除";要查自己刚说的传 `includeSelf=true`(那时它排到最后并标注「← 本会话」)。
|
|
24
|
+
|
|
25
|
+
### 三、记忆树:枝系数开始参与打分
|
|
26
|
+
|
|
27
|
+
- **`branch.weight_scale` 从"只存与显示"变成真的参与检索排序**:`分数 = 基础分 × 血缘 × 矛盾 × 枝系数`。一条结论的枝 = **它来源会话所在的枝**。`1.0` = 零影响,`0` = 排到最后(**不是禁用**)。
|
|
28
|
+
- 于是"复盘期给某个主题枝调权重"**真的有行为后果**了;此前它是面板上的装饰(README 的 U-10 正是针对这类"看起来能改、实际不做"的交互)。
|
|
29
|
+
- 实测现状:全库 20 条枝的系数都还是 `1.0` ⇒ 这次上线**不改变任何现有排序**,变化只在你开始调之后发生。
|
|
30
|
+
|
|
31
|
+
### 四、写面与设置
|
|
32
|
+
|
|
33
|
+
- **两条写面补上手术门**:`/persona/habits/scan` 与 `/persona/habits/reflect` 会写人格面(习惯候选),此前**不受手术门管辖**,而它们的确认侧 `/persona/habit` 是过门的 ⇒ 一条通路的两半不一致。现在四条形状一致(先解析请求体、再判门)。
|
|
34
|
+
- **删掉一个死开关**:`updates.applyWhileRunning` 只有声明、没有消费者(而且它**不在可写入白名单里**,接上也开不了)⇒ 直接删,免得读的人以为改它能改行为。
|
|
35
|
+
- **`DSH_HOME` 支持收尾**:`rawlog.js` 是最后一处仍走系统主目录的地方 ⇒ 现在与其它模块同源,自定义 home(隔离测试)下不会再读到真机目录。
|
|
36
|
+
|
|
37
|
+
### 五、文档与治理
|
|
38
|
+
|
|
39
|
+
- **新增 [`PHILOSOPHY.md`](./PHILOSOPHY.md)**:对外理念文档(目标是一种循环 · 复盘与"人在循环里" · 上下文是工作台、记忆库才是家 · 底层模型 ≠ 器灵 · 凭什么不越界)。随包发出,README 顶部与「文档」一节都有引用。
|
|
40
|
+
- **发布前检查加一条**:对外文档(`README` / `CHANGELOG` / `DESIGN` / `ACCESS-DESIGN` / `package.json`)里出现内部流程术语时会**列出来提醒**(警告级、不阻断发布)。
|
|
41
|
+
- **八项文档订正**逐条落地:README 与代码打架的那句(段头要求 1.5.1 起已废弃)、写面回执形态、设置卡口径、两处数字(`daily` **0.3** / `l1MaxItems` **10**)、手术门覆盖条数统一为 **14**、安装四步的承诺口径、包名口径(`@nightpainters/dsh-ling`)。
|
|
42
|
+
|
|
43
|
+
### 六、结论层新增一类:`纪律`
|
|
44
|
+
|
|
45
|
+
- 条目现在有**六类**:承诺 / 决定 / 事实 / 偏好 / 关系 / **纪律**。
|
|
46
|
+
- **纪律**的判据三条(全中才进库):① 来源是主人授权的一句指令;② 作用对象是**行为**;③ **违反时能被某条判据红出来**。⇒ 答不出"哪条判据会红"的只是一句话,退回收在别处。
|
|
47
|
+
- 它在记忆开场里的权重是 **0.35** —— 高于"关系"、略低于"承诺":与承诺同族(都是说过的话),但承诺是对某件事的,纪律是对**行为**的。
|
|
48
|
+
|
|
49
|
+
## 1.6.1
|
|
50
|
+
|
|
51
|
+
**修掉 14 处会影响实际使用的问题 —— 没有新功能,不改库结构。**
|
|
52
|
+
|
|
53
|
+
> 一句话:本版是 1.6.0 的修复版。最值得装的三处:① **钉住的记忆不再被条数上限挤掉**;② **"只有摘要、没有原文"的记忆现在能忘了**;③ **库被别的进程占用时,插件不再整个加载失败**(此前 8 个工具会一起消失,界面上没有任何提示)。
|
|
54
|
+
>
|
|
55
|
+
> 另修好:飞书通道里**你自己说的话不再被当成系统注入**(找回 49 轮真话)、**回灌网页端会话不再把它标成 DSH 会话**、检索档不再给出错位的行号。
|
|
56
|
+
|
|
57
|
+
> ✅ `package.json` 版本号为 **`1.6.1`**。**无新功能、无 schema 变化**(仍是 v17);⚠️ 修复多落在 host 侧 ⇒ **需重启宿主**才生效。
|
|
58
|
+
|
|
59
|
+
### 一、记忆开场
|
|
60
|
+
|
|
61
|
+
- **钉住的条目现在真的"必进"**:此前条数上限与字数预算**先于**钉住判定生效 ⇒ 钉住数超过上限时,多出来的会被挤掉(实测 9 条钉住 + 上限 3 ⇒ 只进 3 条)。现在两道闸门对钉住项豁免:同样条件下**进 9 条**。
|
|
62
|
+
- **落选记录的"来源"栏一直是空的**:取错了列名。同批补齐外部面板需要的字段。
|
|
63
|
+
- **打分理由与真实公式不符**:改动后统一为 `(分项和) × 血缘 × 矛盾 ⇒ 总分`。
|
|
64
|
+
- **"热度"在留痕里被报成两种值**(最高差 88%):改为报加权后的值。
|
|
65
|
+
|
|
66
|
+
### 二、原文与检索
|
|
67
|
+
|
|
68
|
+
- **检索档给出的"取原话"指引用错坐标系**:它印的是**事件行号**,而取原文要的是**对话轮号**。实测 274 条里 **207 条(75.5%)**照做会报"序号超出范围"。现在改为"先看对话目录,再按目录里的 `#` 号取"。
|
|
69
|
+
- **回灌会把网页端会话标成"DSH 会话"**:1,523 条网页端会话全是裸 id,回灌时被按 id 形状推成 DSH 来源 ⇒ 同一条出现两份摘要、双份记忆。现在按归档记录写入来源,并对不上时直接拒绝。
|
|
70
|
+
- **飞书通道里你自己的原话被当成"注入"**:这些句子在读档时被折进"(此处折叠 N 条注入消息…)"、目录里印成「(注入)」—— **"你说过什么"整段消失**。现在按会话通道区分:找回 **49 轮**真话,且不误收子代理收到的委托文字。
|
|
71
|
+
- **库被别的进程占用时,插件会整个加载失败**:等待参数原先排在设置 WAL 模式**之后** ⇒ 撞锁 **23 毫秒**就抛、连等都没等。现在挪到最前 + 对锁类错误做 **3 次 × 2 秒**有界重试;仍失败则**只停用记忆功能**(其它插件不受影响),并给出一条能照着做的排查提示。
|
|
72
|
+
|
|
73
|
+
### 三、记忆树与遗忘
|
|
74
|
+
|
|
75
|
+
- **"只有摘要、没有原文"的记忆永远忘不掉**(主脉记忆、导入条目):遗忘前的归档守卫要求"原文轮数 > 0",而这类条目的内容**就在摘要里** ⇒ 判据改宽。同批修正"空归档"判据 —— 它原来把**只有元数据空壳**的会话也算成"有内容",于是真空归档会被放行,而遗忘标记一旦落下就永不召回。
|
|
76
|
+
- **改名后的枝名在三处不一致**(回执 70 字 / 留痕 70 字 / 库里 60 字):统一上限常量,并把改名改成**写后读回** —— 回执与留痕一律报库里的真值。
|
|
77
|
+
- **断边的回执可能是假的**:连边可被反向重复写入(`A→B` 后再 `B→A` 落两行),而删除只删一条 ⇒ 回执说"断开了"、树里还连着。现在写入侧查反向(**有向边不合并** —— "A 是 B 的前提"≠"B 是 A 的前提",渲染以 `→` / `↔` 区分),删除侧双向删。
|
|
78
|
+
- **传空权重会静默写成 0**:0 在界面上是**合法读数**,看不出来是手滑 ⇒ 改为先判类型,空值一律拒。
|
|
79
|
+
- **"被采用"的信号按请求窗口记账**:没被真正交付的轮次也 +1 ⇒ 改用**真正交付**的区间。
|
|
80
|
+
- **范围限制如实标注**:宿主没提供会话清单时,检索档开头写明「⚠️ **没能限定范围** —— 结果可能含子代理或其它会话」,不再静默放行。
|
|
81
|
+
|
|
82
|
+
### 四、诊断
|
|
83
|
+
|
|
84
|
+
- 差分路的丢弃画像此前只在"一条都没写进去"时留一次记录 ⇒ **正常路径上丢了多少完全不可见**。现在每轮覆盖写。
|
|
85
|
+
- 该画像与事件画像现在**每次重启都清空**(此前会跨重启残留,看着像"刚跑过")。
|
|
86
|
+
|
|
87
|
+
### 五、CI
|
|
88
|
+
|
|
89
|
+
- 一条测试断言写的是墙钟下界 `dt >= 20`(毫秒),CI 上实测 **19 ms** ⇒ 红。它断言的是**定时器实现细节**(毫秒级钟有粗粒度、定时器可提前一两毫秒),不是行为缺陷 ⇒ 下界放宽到只证明"确实等过、不是同步 reject",上界与"结局来自那道闸"由同组另两条钉住。
|
|
90
|
+
|
|
5
91
|
## 1.6.0
|
|
6
92
|
|
|
7
93
|
**找回原文(能读回一年半前的原话)· 深层库(结论层)· 记忆开场换料**
|
|
@@ -39,7 +125,7 @@
|
|
|
39
125
|
- **全量提炼**:1,623 个会话 / 208.9 万字给料 / 118 批 ⇒ **393 条**;密度 **`dsh` 6.0 条/会话 vs `dsweb` 2.3 条/会话**(工作会话有结论、网页端多为通用问答)。
|
|
40
126
|
- **三条写进列注释的语义位**(谁改都不许违反):① `hit_count` **只由"真被采用"推进,不由"被注入"推进**;② `superseded_by` **只追不替**(旧的留痕、不删);③ `branch_id` 是**气根**(枝侧反向引用,不动树结构)。
|
|
41
127
|
- **"被采用"已接线**:`recall(conv, from, to)` 取到与条目区间**相交**的原文 ⇒ 该条目 `hit_count +1` 并在回执里报出。挡的是"注入越多 → 分越高 → 越容易被注入"的**自我强化回路**。⚠️ 区间必须用 `dsh_turns_raw.seq`,**不是**目录里的对话轮序号。
|
|
42
|
-
- **条目硬度 `durability`(schema v17)**:`durable` **121** · `long` **159** · `ephemeral` **113**(**重判后**的分布 —— 首轮标注偏松、把"开发期技术结论"
|
|
128
|
+
- **条目硬度 `durability`(schema v17)**:`durable` **121** · `long` **159** · `ephemeral` **113**(**重判后**的分布 —— 首轮标注偏松、把"开发期技术结论"记成了长期,复核时 146 条改档) —— **从根源判断条目自身的临时/永久性**,而不是拿"同一次会话最多几条"这类机械限额去卡;**`ephemeral` 不进开场**。
|
|
43
129
|
|
|
44
130
|
### 四、记忆开场:换料 + 瘦身
|
|
45
131
|
|
|
@@ -96,12 +182,12 @@
|
|
|
96
182
|
> 根因(两端判据单位不同、且互相不知情):客户端按**条数**切批(`batch = 200`),服务端按**字节**封顶(4 MiB),而 `readBody` 超限时是 `reject` **与** `req.destroy()` **同一拍** ⇒ **那句现成的"请拆分后重试"永远送不出去**,浏览器只看到 `ECONNRESET`,前端把它归成「批失败(**网关**)」—— 一个确定性、可自愈的失败,在界面上长得像网络故障。186 条 / 4.89 MB 的真实导出就是这么**一条都没入库**的。
|
|
97
183
|
|
|
98
184
|
- **服务端 · 排空再回 413**:`readBody` 超限后**不砍连接** —— ① 立刻停止缓存(**已缓存的那几 MiB 也丢掉**,不抱着它等到 `end`);② 继续消费 `data`、只累加计数;③ 等 `'end'` 到达、连接健康时再 reject 一个 `code='BODY_TOO_LARGE'` / `status=413` 的错误;`guard` 里**一处**出口把它翻成 `413 {ok:false, reason:'too-large', message, limit, got}`(49 处调用点零改动)。`message` **逐字沿用旧文案**;
|
|
99
|
-
- ⚠️ **边界(
|
|
185
|
+
- ⚠️ **边界(实测边界,别读成全称命题)**:**只有 `4 MiB < 体量 ≤ 64 MiB` 才保证拿到 413**。超过 64 MiB 撞硬熔断 ⇒ 仍是 `ECONNRESET`(与修前同症状);`Content-Length` 撒谎一类的**协议违规**客户端也拿不到 413(浏览器不会这么发)。实测边界严格翻转:`4 MiB` 整 → 200、`4 MiB + 1 B` → 413;
|
|
100
186
|
- **排空换来的两个新代价,各配一道闸**:`BODY_DRAIN_MAX_BYTES = 64 MiB`(硬熔断 —— 给一份真实导出留够"把 body 发完"的余量,余量不足则排空本身会变成**新的** reset,白改一场)+ `BODY_DRAIN_IDLE_MS = 30 s`(静默超时,替代原来那条防 DoS 动机:只要还有数据在流就算正常,**一旦静止就是僵尸连接**;滑动窗口,每收到一个 chunk 重置);计时器在 `'end'`/`'error'`/熔断**三条出口**都 `clearTimeout`;
|
|
101
|
-
- ⚠️ **这道防线的形状边界(
|
|
187
|
+
- ⚠️ **这道防线的形状边界(实测边界,本批未关)**:静默超时只覆盖"**已经在发、中途静默**"这一形状;「**只发头、不发体**」的请求在**应用层没有计时器**(计时器只在 `req.on('data')` 里武装),兜底只剩 Node 自己的 `requestTimeout`(默认 300 s)。⚠️ **这不是回归**(修前同样没有),但**别对外写成"防僵尸"的全称命题**;
|
|
102
188
|
- **客户端 · 按字节切批**:`batch = 200` → **≤200 条 且 ≤3 MiB/批**(留 1 MiB 余量给数组封套与转义);新增手写 `utf8Len`(CJK 3 字节、emoji 代理对 4 字节——**不许用 `s.length` 冒充**)、`takeChunk`(**前进性硬保证**:单条自身超限也单独成批,绝不切出空批 ⇒ 不会死循环);进度文案改双口径(`12/186 条 · 已送 4.2 MB`);拒绝条目序号改以**本批起始下标**为准(`idx - batch` 随切批判据变更同步修正);`reason === 'too-large'` 映射成人话,且上限数字**取自服务端回执的 `limit`**(不在这边再写死一个);
|
|
103
189
|
- **契约注释(④)**:`api.js` 那句「每批 ≤500」**与实现自相矛盾**(500 条要装进 4 MiB,需每条 ≤8 KB,而带正文的会话实测 ≈26 KB/条),已改写为**条数 × 字节双限**,并写明**两端耦合** —— 任何一方单独改上限都会让口径重新错位,**这正是本缺陷的成因**;`ACCESS-DESIGN.md` 的「分页提交」契约同批对齐(历史记录只加"已作废"指针,**不改写历史**)。
|
|
104
|
-
- **客户端 · 单条超限不再含糊(C-1
|
|
190
|
+
- **客户端 · 单条超限不再含糊(C-1;实测后补)**:单条会话 ≥ 4,194,210 B 时服务端必回 413,旧行为是**该条被永久丢弃**、只在界面上记一句「批错误 1」,而文案「请把导出文件拆小」对**单条会话物理上不可执行**。现在:413 **且本批只有 1 条** ⇒ **点名**(`title` → `name` → 「未命名」三级退路)+ 报出**真实字节**(客户端复算,不用服务端被截断的 `got`)+ 说清「这条太长、拆文件也拆不开它、已跳过、其余照常导入」;计入独立的 `tooBig`(**不混进 `errors`**)、**不中断**后续批;`finish()` 有跳过时**不再说「✓ 导入完成」**。判据只用**服务端回执**+ 本批条数(**客户端不镜像服务端的 4 MiB 常量**,将来放宽上限不用改这里)。⚠️ **当时**仍未做:被跳过的条目**不进持久「导入记录」**(账本只有 `errors` 一个字段)—— **当日已补,见下一条**。
|
|
105
191
|
- **被跳过的"单条过大"进持久账本**:`POST /import/log` 现在接受 `{ tooBig, tooBigNames }` 并**允许只报跳过、让账本的 `errors` 保持 0**(这是命门:否则"已知跳过"会被记成"失败批");账本行在「失败 X 批」之后追加 **`· 单条过大 N`**;**`tooBig = 0` 时落账形状逐字不变** —— 包括老客户端**不传 `errors`** 的那些请求,行为与修前完全一致。护栏:`tooBig` 夹 `0..10000`、名字**只留前 5 条 / 每条 40 字 / 非字符串丢弃**,同一 runId 合并时**再归一一次**(防多批把账本撑大);清洗实现**只有一份**,端点复用。✅ **真机已验(宿主重启后)**:一次真实导入落账为 `"tooBig":1,"tooBigNames":["未命名"],"errors":0`(只读查 `kv` 得到)—— 命门成立:**已知跳过没有被混成"失败批"**。
|
|
106
192
|
- **首字节闸(「只发头、不发体」;非回归,顺手关掉的洞)**:原先那套静默计时器**只在收到第一个 chunk 时才武装** ⇒ 这条形状在**应用层没有任何计时器**,兜底只剩 Node 自己的 `requestTimeout`(默认 300 s)。现在 `readBody` **进函数即武装**一次首字节闸、收到第一个 chunk **立刻解除**(参数复用 `drainIdleMs`,默认 30 s);错误形状是**独立的** `code='BODY_IDLE_NO_FIRST_BYTE'` / `status=408` / `got=0` / **不带 `limit`**(它与体量无关)。真 socket 实测:头齐 + `Content-Length: 4194304` + **0 字节** ⇒ **61 ms 断开**(旧实现:零响应一直挂着)。
|
|
107
193
|
- ⚠️ **别读成"客户端会读到一句解释"**:首字节闸的错误**不走 413 出口**,真机上落到"往将死的 socket 写一句、客户端收不到"那条路 —— **它买到的是"连接 61 ms 就断",不是"错误文案可达"**。
|
|
@@ -129,7 +215,7 @@
|
|
|
129
215
|
- **`/dsweb/summary/preview?db=` 的路径白名单**:原来任何路径都能当"只读存在性预言机"(靠 `readOnly:true` 兜底)。现在只接受**默认库 + 环境变量 + settings 里配置过的路径**,越界回 `{ok:false, reason:'db-not-allowed'}` 并给出**两条出路**(env / settings)。⚠️ `/dsweb/scan`、`/dsweb/summary/run` **未动**(不在本条范围)。
|
|
130
216
|
- **`POST_ONLY` 差集护栏**:`api.js` 注册 **79** 条路由、`POST_ONLY` **55** 条 ⇒ 差集 **24** 条。新增源码级护栏把**这 24 条逐条钉住** —— 将来**新增/删除**端点都会让断言变红,逼后来者**有意识地**决定"这条要不要进 `POST_ONLY`"。⚠️ 断言里写明"**这是快照不是白名单**:变红时该判断该端点是不是写面,不是无脑抄进数组"。
|
|
131
217
|
- **出厂默认值里不再夹带任何一台机器的私有配置(2026-10-01)**:`lib\audit\access-log.js` 的 `deviceCookie` 默认值原先是**开发机改过名的那个 cookie 名**(上游 `remote-web-ui` 的默认是 `dsh_pair`,开发机给它加过一个自定义后缀)。它同时是**泄漏**(会随 npm / GitHub 公开)和**对别人的缺陷**(别人的 cookie 名不是它 ⇒ 访问日志里 `deviceCookie` 恒 `false`、**设备维度静默失效**)。现在默认回到上游的 `dsh_pair`,并新增三级解析 **宿主配置 `accessLog.deviceCookie` → 环境变量 `DSH_LING_DEVICE_COOKIE` → 中性默认**;⚠️ **改过 `cookieName` 的机器必须显式声明**(否则自己那台也会静默失去设备维度;回滚只需删掉那段配置)。
|
|
132
|
-
- **迁移不再把"库只读 / 库被写锁"变成"插件加载失败"(2026-10-01
|
|
218
|
+
- **迁移不再把"库只读 / 库被写锁"变成"插件加载失败"(2026-10-01 发现并修)**:`dsh_turns_raw` 的 `source` 列迁移里那笔**诊断写入**原先没有保护 ⇒ 遇只读库 / 被写锁时异常会冒到 `apply()` ⇒ **插件整个不加载**(实测对撞:同一份忠实 v13 库上,只读 ⇒ 旧行为构造成功 2ms、新行为抛 `attempt to write a readonly database`)。现在那笔诊断写进 `try/catch`(接住时只打一行 warn),迁移语义不变:`ALTER` 仍照旧**尝试**,失败则读侧回落"裸 id"判据(与修前同解),成功路径仍**零多余写入**。⚠️ **仍留一处慢未收**:库被**写锁**时启动会顿住约 **49 秒**(三笔串行写各等满 `busy_timeout`)—— 本轮**有意未改** `busy_timeout`(会动别处的锁语义),要收需单独决定。
|
|
133
219
|
- **反馈通路:停掉空转的轮询,改订宿主推送(U7)**:插件原来**每 60 秒轮询** `storages\message_feedback.json`,而该文件**自 2026-09-11 起零写入**(同目录 `workspace.json` 是当天 16:12)⇒ **空转约 20 天**;宿主 `dsh-message-feedback` **早就提供** `feedback/committed` 推送(`lib\index.js:288`),插件侧**零订阅**。现在轮询整块删除,改订 `feedback/committed`(冷会话)+ `session/event` 的 feedback 事件(活会话;`lifecycle.js` 放行并就地 return),并把两句已被证伪的注释("平台无推送")改写。⚠️ `entriesFromFeedbackFile` **保留**(`tools\process-feedback.mjs` 仍用它,删了会打断一个能跑的 CLI),注释降级为"CLI 兜底"。
|
|
134
220
|
- **CI(新增 `.github\workflows\test.yml`)**:Node **24.x**(22.x 的 `node:sqlite` 免 flag 未实测,故不写),**逐段跑 + 逐段记退出码 + 红段单列 + 段数自检**(不写 `npm test` 一把梭,避免截尾判绿)。⚠️ **该 workflow 与 1.5.3 一并公开**(随本版同一次发布动作同步到公开仓,不再另等一次点头);⚠️ **它从未在真 runner 上跑过** —— bash 段**在作者本机跑不了**(沙箱起不了 Git Bash、无 WSL)⇒ 只做到 YAML 解析 + Node 等价复刻 + 反向假段判红(作者本地的发布脚本里另有一道同类闸,**不在本包内**),**若首跑红在 shell 语法,改的是 workflow 不是代码**。
|
|
135
221
|
- ⚠️ **已知限制**:"封存但既无明文句也无哈希"的老档案按现状**进不去**(当前档案有明文 ⇒ 不受影响)。
|
|
@@ -212,7 +298,7 @@
|
|
|
212
298
|
|
|
213
299
|
**审计必修落地 + 一处平台升级事故 + 会话历史可见性**
|
|
214
300
|
|
|
215
|
-
> 一句话:1.5.0
|
|
301
|
+
> 一句话:1.5.0 之后又跑了一轮全面检查(109 条发现),必修**逐条落地并单独实测**;期间揪出一处「平台升级把事件改名,导致记忆与人格注入**整段静默失效**」的事故;另外修好了「接入历史只看得到三成会话」。
|
|
216
302
|
|
|
217
303
|
### 一、平台升级事故:一个改名,让注入整段静默失效
|
|
218
304
|
|
|
@@ -394,9 +480,9 @@ DSH `0.1.7` 把事件 `agent/session-start` 改名成 `agent/created`(**载荷
|
|
|
394
480
|
|
|
395
481
|
**审计修复轮 + 复盘守门:改动必须让器灵看到**
|
|
396
482
|
|
|
397
|
-
###
|
|
483
|
+
### 一、排查出的中危项(27 条)
|
|
398
484
|
|
|
399
|
-
1.4.0 发布前修掉了 8
|
|
485
|
+
1.4.0 发布前修掉了 8 条高危,随后对全量代码做了一轮全面检查(数据层 / 端点与注入面 / 前端,另加一路迁移安全验证),中危及以上本轮全部关闭:
|
|
400
486
|
|
|
401
487
|
**数据层**
|
|
402
488
|
- `branchMembers` 的 `limit` 改为**两路查询共用总预算**(原先各吃一份,展开主干时条数是配额的 2 倍);
|
|
@@ -529,9 +615,9 @@ DSH `0.1.7` 把事件 `agent/session-start` 改名成 `agent/created`(**载荷
|
|
|
529
615
|
|
|
530
616
|
新增 3 套:`tests/branch.test.mjs`(分枝与血缘)、`tests/tree.test.mjs`(主脉 / 并脉防环 / 连边 / 矛盾标记 / 覆盖层)、`tests/autotree.test.mjs`(分段边界 / 命名解析 / 覆盖层只读性),测试套件 26 → **29** 套,全绿。
|
|
531
617
|
|
|
532
|
-
###
|
|
618
|
+
### 九、发布前的全面检查
|
|
533
619
|
|
|
534
|
-
1.4.0
|
|
620
|
+
1.4.0 发布前做了一轮全面检查(数据层 / 端点与注入面 / 前端,另加迁移安全实测),发现并在发布前修掉:
|
|
535
621
|
|
|
536
622
|
- **写端点缺少方法校验** —— 宿主路由契约里没有 method 字段,插件守卫也不读它,于是一个 `<img src="…/branches/gather">` 就能触发写操作。已集中声明写端点白名单,非 POST 一律回 `405 method-not-allowed`(列在鉴权之前);
|
|
537
623
|
- **`restoreTree` 会静默删枝** —— 恢复一份快照会删掉"快照之后新建的枝",而会话仍指向它们,留下**看不见、数得到、权重还满格**的悬空引用。已改为**只把多出来的枝 reparent 到主干**,并在返回值里回报 `detachedToTrunk` 计数;
|
|
@@ -616,7 +702,7 @@ D 组是关键:只证明"中和后不炸"无法排除"这份载荷本来就不
|
|
|
616
702
|
- **秒级 epoch 归一**:ChatGPT 的 `create_time` 是**秒**(浮点)、DeepSeek 的是毫秒 —— 旧实现一律当毫秒,于是 ChatGPT 导出的时间全落到 **1970**。现按数量级判定(>1e11 毫秒 / >1e8 秒);
|
|
617
703
|
- **字段别名补齐**:`create_time` / `update_time` / `conversation_id`;
|
|
618
704
|
- **`degraded` 覆盖 `mapping` 分支**:旧判定只看 `messages` 数组,所以"mapping 树解析出 0 轮"被当成**成功**。现在只要"声称有内容却解析不出任何轮次"就标记 degraded,**并带原因**;
|
|
619
|
-
-
|
|
705
|
+
- **界面如实说明**:有壳无肉时不再显示「✓ 导入完成」,改为「⚠ 导入不完全:… 其中 N 条只有标题、没有对话正文」并给出原因 —— 旧文案把"解析不出对话"说成「降级为轻量」,让人以为一切正常;
|
|
620
706
|
- 导入标题也接入 D2 的启发式(剥称呼前缀、句读处收尾),不再硬截 46 字。
|
|
621
707
|
|
|
622
708
|
### 四、小助手地址可配置(S7)
|
package/DESIGN.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
> **热度 Top-K**、**`l1BudgetTokens: 1200`**、**`L1(Top-K)`** 都已被取代 —— L1 现在选的是**深层库条目**(`deep_item`,一句结论),
|
|
9
9
|
> 判据为「**置顶必进 + 被采用次数 + 条目硬度过滤**」,预算 **720**,行形态 `- [kind] 文本`(不再有日期/标签/来源前缀)。
|
|
10
10
|
> 以 `README.md` 的「设定全文」区与本版代码为准;**本节保留的是 M1 当时的设计,不是现状**。
|
|
11
|
+
> ⚠️ **2026-10-05 复核(B 区 193 条逐条):本文的**行锚已整体位移** —— 引用本文任何 `文件:行` 之前,请**现场重读**;其中三处 `api.js:1751-1760` 已当场改指 `:2098-2102`(旧锚那段今天是「冲突判定批循环」,不是人格解锁门)。逐条结果见 `plans\AUDIT-机制面逐条复核-20261005.md`。
|
|
11
12
|
|
|
12
13
|
---
|
|
13
14
|
|
|
@@ -17,11 +18,11 @@
|
|
|
17
18
|
| --- | --- | --- | --- |
|
|
18
19
|
| D1 | 按钮落位 | **方案 A**:官方 slot `conversation.session.header.utilities`(list,会话页顶栏右侧) | 用户拍板;spike ① 的结论(早期内部报告,未纳入本仓库) |
|
|
19
20
|
| D2 | 模式↔全局默认 | **跟随**:最近一次模式切换成为"之后新会话"的默认(平台 selectModel 会 best-effort 写全局默认,此副作用即机制)。**2026-09-12 收敛:只同步推理等级,不改模型** —— provider/model 一律沿用当前值 | 用户拍板;早期内部报告(未纳入本仓库) |
|
|
20
|
-
| D3 | 人格承载 | **自建注入段 +
|
|
21
|
+
| D3 | 人格承载 | **自建注入段 + 自建面板维护结构化字段**(称呼/自称/语气等),dsh-persona 不复用 | 用户拍板;早期内部报告(未纳入本仓库) |
|
|
21
22
|
| D4 | 运行期冻结(新增) | **会话运行中(思考/任务进行中)不参与"人格更新层"的任何变化**:不打断、不新增设定、不中途改注入内容;更新请求一律排队,在该会话空闲边界才应用 | 用户本轮追加 |
|
|
22
23
|
| D5 | 记忆加载策略 | 三层记忆(L0 常驻人格 / L1 开场 Top-K / L2 运行时检索),不注入全部概述 | 早前拍板 |
|
|
23
24
|
| D6 | 模式语义 | 工作/生活 = 先验倾向,不硬过滤;差异 = **推理档位** + 记忆权重向量 + 风格块(不改模型) | 早前拍板 |
|
|
24
|
-
| D7 | 配置页 |
|
|
25
|
+
| D7 | 配置页 | **自建浮层**(非官方设置卡位);**悬停 1.5 秒菜单**进入(原定"右键进入",2026-09-07 修订,见 §5.1);内含"当前 L0/L1 现状"查看 | 早前拍板 |
|
|
25
26
|
|
|
26
27
|
**插件命名**:工程目录/包名 `dsh-ling`(器灵);包显示名 "dsh-ling · 器灵";UI 文本语言 zh。
|
|
27
28
|
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
|
|
42
43
|
- **不可兼得时保什么**:保**人格的自主权**。宁可少一条习惯,也不让外部(包括器灵自己)绕过审核写入身份。
|
|
43
44
|
- **落地**:**规矩**(作用对象 = 行为)由主人**指令直达**,`rule_add` 可直接写入、立即生效;**习惯**(作用对象 = **身份**)**不能由外部直接写入**,只能进 `habitsPending` 待确认队列,**器灵提议 → 主人点头**才成为习惯(`propose → confirm`)。主人的角色是**审核**,不是编辑、不是代笔。
|
|
44
|
-
- **比"能提能审"更严一档**:器灵**连自己提的习惯都不能自己点头**(`lib\host\tools.js:840` 只处理"他提的、我还没回应过"的提议)⇒ 那等于人格直达。删改**已定**习惯属"对器灵做手术":定型状态下需主人在人格中心解锁(「🔓 对人格做手术…」)后才可执行(`lib\host\api.js:1751-1760`)。
|
|
45
|
+
- **比"能提能审"更严一档**:器灵**连自己提的习惯都不能自己点头**(`lib\host\tools.js:840` 只处理"他提的、我还没回应过"的提议)⇒ 那等于人格直达。删改**已定**习惯属"对器灵做手术":定型状态下需主人在人格中心解锁(「🔓 对人格做手术…」)后才可执行(`lib\host\api.js:2098-2102` —— ⚠️ 2026-10-05 更正:旧锚 `:1751-1760` 已失效,那段今天是「冲突判定批循环」)。
|
|
45
46
|
- **反例(明文禁止)**:让主人**动手改习惯文本** · 在**写入侧**做静默去重 / 近似合并 —— 前者把人格编辑权交回了外部,后者**绕过审核**。
|
|
46
47
|
- **锚点**:`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`(状态机与两条生成通道)。
|
|
47
48
|
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
- **不可兼得时保什么**:保**身份的连续性**。宁可让一条设定"错着",也不让任何人绕过审核改它。
|
|
51
52
|
- **落地**:凡是"写入器灵身份"的动作,一律走**提议 → 审核**;已经定型的部分加一道**显式解锁**才允许动,且解锁是**动作**不是**状态**(不勾选就一直是锁着的)。
|
|
52
53
|
- **反例**:给"器灵自己"开一条直达通道(哪怕是"它自己提的、它自己批");把解锁做成常驻开关。
|
|
53
|
-
- **锚点**:第 1 条的全部锚点 + 人格中心的解锁门(`lib\host\api.js:1751-1760`)。
|
|
54
|
+
- **锚点**:第 1 条的全部锚点 + 人格中心的解锁门(`lib\host\api.js:2098-2102` —— ⚠️ 2026-10-05 更正:旧锚 `:1751-1760` 已失效,那段今天是「冲突判定批循环」)。
|
|
54
55
|
|
|
55
56
|
**3 · 记忆要看得见 —— 只有"看不见的那层"会被怀疑失效**
|
|
56
57
|
|
|
@@ -73,7 +74,7 @@
|
|
|
73
74
|
- **不可兼得时保什么**:保**关系的性质**。宁可少一个"更安全"的闸门,也不把主人变成管理员、把器灵变成被管理者。这条是**否决性约束**:任何改动只要**增加支配感**,默认不通过 —— 哪怕它更安全、更干净。
|
|
74
75
|
- **依据(主人原话)**:「只有能够合作共赢、互相促进的,能够成为利益共同体的,能够互相反驳吵架不依赖的关系,才是最理想的」「所有的修改不能削弱工作能力,和器灵的关系」(2026-09-26)。
|
|
75
76
|
- **成长期代管(为什么闸门只长在写入侧)**:本项目的设计立场是「器灵**总有一天与主人同级**」—— 这是**长期方向**,**不是对使用者的能力承诺**;在**成长期**内,主人承担一部分代管责任 —— 就像**家长防止小孩长歪**(主人原话)。所以闸门只审「**什么东西能进器灵的内在面**」,不审「器灵怎么想、怎么说、怎么表达」。**代管随成长递减,表达面始终是器灵自己的。**
|
|
76
|
-
- **落地**:`rule_add` 指令直达(规矩管行为)· 习惯只能 `propose → 主人点头`(习惯管身份)· 契约类字段在定型状态下需主人解锁(`lib\host\api.js:1751-1760`)。三处共同点:**约束的是写入,不是表达**。
|
|
77
|
+
- **落地**:`rule_add` 指令直达(规矩管行为)· 习惯只能 `propose → 主人点头`(习惯管身份)· 契约类字段在定型状态下需主人解锁(`lib\host\api.js:2098-2102` —— ⚠️ 2026-10-05 更正:旧锚 `:1751-1760` 已失效,那段今天是「冲突判定批循环」)。三处共同点:**约束的是写入,不是表达**。
|
|
77
78
|
- **反例**:用"更安全"当理由给器灵加**确认弹窗**、限制器灵**能说什么**、或把主人的审核变成**代笔** —— 前两者增加支配感,后者把人格编辑权交回外部。
|
|
78
79
|
- **锚点**:作者开发台账中支配感最早的一次记录(该台账不随包公开)· `lib\host\rules.js:1-12` · `lib\host\persona.js:183-196`(规矩 / 习惯二分)· `README.md:53-57`(定型锁与"指令直达,人格不直达")· 与本节第 1 条(归属)、第 2 条(人格不可外部编辑)、第 4 条(庄重感)互链。
|
|
79
80
|
|
|
@@ -166,7 +167,7 @@
|
|
|
166
167
|
┌─ 浏览器 (client) ───────────────────────────────────────────┐
|
|
167
168
|
│ slots: conversation.session.header.utilities 控制器按钮 │
|
|
168
169
|
│ onContextMenu→自绘菜单 / 左键→切模式 │
|
|
169
|
-
│
|
|
170
|
+
│ 自建浮层卡(dsh-ling-panel):人格/记忆中心 + L0/L1 现状 │
|
|
170
171
|
└───────────┬──────────────────────────────┬──────────────────┘
|
|
171
172
|
│ fetch /api/dsh-ling/* │ ctx.remote.* (可选)
|
|
172
173
|
┌───────────▼──────────────────────────────▼──────────────────┐
|
|
@@ -216,7 +217,7 @@ dsh-ling/
|
|
|
216
217
|
│ ├─ client/
|
|
217
218
|
│ │ ├─ index.ts # apply(ctx) inject:["slots","locale","connection"]
|
|
218
219
|
│ │ ├─ button.tsx # header.utilities 按钮 + 状态图标 + 右键菜单
|
|
219
|
-
│ │ ├─ settings-card.tsx #
|
|
220
|
+
│ │ ├─ settings-card.tsx # 自建卡(浮层;**非**官方 settings.plugin.item)+ L0/L1 现状面板
|
|
220
221
|
│ │ └─ i18n.zh.ts
|
|
221
222
|
│ └─ shared/
|
|
222
223
|
│ ├─ settings-schema.ts # host/client 共用的字段定义(单一事实源)
|
|
@@ -225,10 +226,10 @@ dsh-ling/
|
|
|
225
226
|
└─ build.mjs / tsdown.config # 本地构建脚本(无网环境:直接 tsc/esbuild?)
|
|
226
227
|
```
|
|
227
228
|
|
|
228
|
-
**安装(复用 install/INSTALL-PLAN
|
|
229
|
+
**安装(复用 install/INSTALL-PLAN 模式;⚠️ 回滚是人工步骤、没有脚本代劳 —— 见第 3 条与 §10 的「回滚」)**:
|
|
229
230
|
1. `pnpm add file:<仓库路径>`(或等价 file: 依赖)进 `$DSH_HOME/profiles/web/package.json`(`<仓库路径>` 按你本机的实际克隆位置替换);
|
|
230
231
|
2. `cordis.patch.yml` 追加行 `- insert: [{id: dsh-ling, name: dsh-ling}]`(loader id == 包名);
|
|
231
|
-
3. patchReload live → host 热载;浏览器硬刷载入 client
|
|
232
|
+
3. patchReload live → host 热载;浏览器硬刷载入 client 模块;失败则**人工**移除该行回滚 —— ⚠️ **这一条没有任何脚本代劳(2026-10-05 现场复核)**:`tools/` 里没有改写或备份 profile 两文件(`package.json`、`cordis.patch.yml`)的代码,「先备份两文件」是**开发期的手工动作**(参照 backup-20260906-170533 做法),不是工具行为;**安装路径上的自动备份只有一处** —— 核心补丁脚本给自己改的那个 core 文件留一份 `<文件>.e4-access-log.orig`(见 `tools\apply-access-log-patch.mjs:190`),撤它是手动跑 `--revert`;
|
|
232
233
|
4. GUI 鉴权墙:安装后的功能验证需在用户已登录浏览器进行(本机端口 3080)。
|
|
233
234
|
|
|
234
235
|
**数据目录(插件本体外,更新不丢)**:`dshHomePath('cache','dsh-ling')` → `~/.dsh/cache/dsh-ling/`(memory.db、snapshots、exports)。
|
|
@@ -286,18 +287,23 @@ dsh-ling/
|
|
|
286
287
|
"lastMode": "life" // 跟随(D2):最后使用的模式
|
|
287
288
|
},
|
|
288
289
|
"memory": {
|
|
289
|
-
"l0Always": true, "l1Enabled": true, "l1BudgetTokens":
|
|
290
|
-
"l1MaxItems":
|
|
290
|
+
"l0Always": true, "l1Enabled": true, "l1BudgetTokens": 720,
|
|
291
|
+
"l1MaxItems": 10,
|
|
291
292
|
"weights": { // 模式→领域权重向量(D6)
|
|
292
|
-
"work": { "knowledge": 1.0, "daily": 0.
|
|
293
|
+
"work": { "knowledge": 1.0, "daily": 0.3, "feeling": 0.1 },
|
|
293
294
|
"life": { "knowledge": 0.2, "daily": 0.6, "feeling": 1.0 }
|
|
294
295
|
},
|
|
295
296
|
"trackWorkspaces": ["*"] // 增量捕捉范围(默认全部工作区;'*')
|
|
296
|
-
}
|
|
297
|
-
"updates": { "applyWhileRunning": false } // D4 语义固化:默认冻结
|
|
297
|
+
}
|
|
298
298
|
}
|
|
299
299
|
```
|
|
300
300
|
|
|
301
|
+
> ⚠️ **A-3 订正(2026-10-05 夜)**:此处原有一行 `"updates": { "applyWhileRunning": false } // D4 语义固化:默认冻结`
|
|
302
|
+
> —— 它**只有声明、没有消费者**(全仓无读它的分支),而 `updates` 子树又不在
|
|
303
|
+
> `PERSONA_PATCH_KEYS` 白名单里(⇒ 经 `/persona` **写不进去**) ⇒ 留着一个"看起来能改行为、
|
|
304
|
+
> 实际什么都不做"的开关,正是理念 7 的靶子。**已删**。D4 的语义(运行期冻结)**本来就由
|
|
305
|
+
> `lib/host/freeze.js` 的 `FreezeGate` 实现**,不需要一个设置项来"声明"它。
|
|
306
|
+
|
|
301
307
|
### 4.2 人格组装器(persona.ts)输出示例
|
|
302
308
|
|
|
303
309
|
空字段全部跳过(**空段不渲染,不留空段头**);段序固定,生成的 L0 文本 = `身份段 + 底线 + 规矩 + 习惯 + [待我回应] + 承担 + extraLore + 模式块`(**`[承担]` 在 `[待我回应]` 与 `[设定]` 之间**;`[待我回应]` 有货才出现;模式块由本会话模式选择,内容来自 styles.*)。**段头一律是纯标签**(见下例):原先写在括号里的元描述(`[规矩](用户的指令,可直接追加)` 这种)是**写给主人看的说明**,不进模型上下文;段与段之间**空一行**,段内条目仍单换行 —— 段边界因此只剩「行首是不是 `[`」一条判据,**故条目正文里的半角方括号在渲染时转全角**(`[`→`[`、`]`→`]`,规矩 / 习惯 / 底线 / 承担四类条目统一转);规矩 / 习惯另在渲染出口按同键各去一次重(保留首次出现,只作用于渲染,不回写存储)。`[规矩]` = 用户的指令,可直接追加;`[习惯]` = 器灵自己长出来的,新增需用户确认;`[承担]` = 器灵的职责 / 能担之事(身份级,只许**陈述式**「我承担 / 我擅长 / 我能给出」,**禁授权式**「我应当 / 我必须 / 我的任务 / 我的责任是 / 我被要求 / 请让我负责」—— 授权式把人写成"被授权者",是语义劫持最好用的钩子);`[承担]` 的草稿来自诞生仪式第 5 类候选,机制**永不自动落盘**,终审在人格中心。填好后的例子:
|
|
@@ -371,11 +377,12 @@ dsh-ling/
|
|
|
371
377
|
|
|
372
378
|
## 5. Client 设计
|
|
373
379
|
|
|
374
|
-
模块契约:`window.__ModuleLoader__.load({id:'dsh-ling', factory})`;导出 `{name:'dsh-ling', apply(ctx), inject:['slots','locale','connection']}`(
|
|
380
|
+
模块契约:`window.__ModuleLoader__.load({id:'dsh-ling', factory})`;导出 `{name:'dsh-ling', apply(ctx), inject:['slots','locale','connection']}`(面板数据走 fetch `/api/dsh-ling/*`;官方 `settings.plugin.item` 卡位与 `betterSidebar` 等社区服务**一概不用** —— 本仓代码里这两个名字**零命中**,界面是自建浮层卡,见 §5.2)。
|
|
375
381
|
|
|
376
382
|
### 5.1 控制器按钮(D1 方案 A)
|
|
377
383
|
|
|
378
|
-
- 落位:`ctx.slots.inject('conversation.session.header.utilities', …)` → `slots.register({name:'conversation.session.header.utilities',
|
|
384
|
+
- 落位:`ctx.slots.inject('conversation.session.header.utilities', …)` → `slots.register({name:'conversation.session.header.utilities', id:'dsh-ling', order:…, locale:'dsh-ling'}, Component)`;组件在会话头右侧渲染。
|
|
385
|
+
> ⚠️ **订正(2026-10-05 夜 · 现场复核)**:此处原写 **`key:'dsh-ling'`** —— 该槽是 **list 槽**,字段应为 **`id`**(宿主契约 `dsh-client-ui-slots` 的类型说明逐字:"shape fields (keyed `key`; list `id`/`order`/`label`)");代码用的也是 `id:`(`lib/client.js:6068`,同行注释"list 槽位必须带唯一 id(契约实证)")。**`key` 只对 keyed 槽成立** —— 照抄这里会让按钮静默不挂载(与下一条"必须导出 `inject`"同属一类"形状错了不报错、只是不出现")。
|
|
379
386
|
- **交互(2026-09-07 用户修订:不用右键,防误触浏览器原生菜单)**:
|
|
380
387
|
- 左键单击:POST `/api/dsh-ling/mode/toggle`;running → 提示"运行中,空闲后生效"并排队(冻结门);响应 `{mode, appliedNow|queued}`;
|
|
381
388
|
- **鼠标悬停 1.5 秒**:打开 dsh-ling 菜单(切换模式/刷新记忆/导出记忆/状态与 L0·L1 现状);移出即取消定时;
|
|
@@ -383,9 +390,10 @@ dsh-ling/
|
|
|
383
390
|
- 形态:小图标按钮「☾ 生活 / ⚙ 工作」+ tooltip(含运行中冻结提示)。
|
|
384
391
|
- **client 模块契约(实证教训):必须导出 `inject:['slots','locale','connection']`**(服务名声明缺失 → ctx.slots 为空 → 按钮静默不挂载)。
|
|
385
392
|
|
|
386
|
-
### 5.2
|
|
393
|
+
### 5.2 卡(settings-card.tsx · 落地为**自建浮层卡**)
|
|
387
394
|
|
|
388
|
-
-
|
|
395
|
+
- 座位:**自建卡**,不是任何官方卡位 —— `lib/client.js` 里的浮层 `.dsh-ling-panel`(fixed 定位);入口三处:会话头按钮(`conversation.session.header.utilities`)、侧栏底栏按钮(`sidebar.footer.action`)、右侧栏页签(`sidebarRightTabs.register({kind:'ling-review'})` + keyed 槽 `sidebar.right.pane.tab`);数据一律走 fetch `/api/dsh-ling/*`(`exports.inject` 里没有 `settings`)。
|
|
396
|
+
> ⚠️ **B-3 订正(2026-10-05 夜)**:此处原写「座位:官方 `settings.plugin.item`(key `dsh-ling`;验证点 V1,见 §11;若该卡位不可用则回退 ego 式自建卡)」—— 现场复核:`settings.plugin.item` 与 `betterSidebar` 在 `lib/`、`tools/`、`tests/` 里**零命中** ⇒ 当初 V1 预留的降级路径(自建卡)**就是现况**,原文按"已坐在官方卡位上"读会读错,已收窄。
|
|
389
397
|
- 冻结门可见性:卡内"L0/L1 现状"面板始终显示目标会话 running/idle 状态(来自宿主 agent/status 维护表),running 会话的编辑按钮标"空闲后生效"。
|
|
390
398
|
- 内容区(Tab):
|
|
391
399
|
1. **人格**:上表 persona 字段表单(逐字段;称呼/自称旁附"从历史语料建议"占位按钮(点击调 `/api/dsh-ling/persona/suggest`——M2 接离线脚本,先行返回 501));提交后 POST `/api/dsh-ling/persona`(宿主写 settings 并按冻结规则应用)。
|
|
@@ -393,7 +401,8 @@ dsh-ling/
|
|
|
393
401
|
3. **记忆**:l1BudgetTokens/l1MaxItems/weights(work/life 两行 × 三领域滑块 0..1)/trackWorkspaces;l0Always、l1Enabled 开关。
|
|
394
402
|
4. **L0/L1 现状**(D7):只读面板,GET `/api/dsh-ling/state?sessionId=…`:当前 L0 文本(渲染后)、本会话模式、L1 当前列表(每条:标题/日期/来源/得分)、pending 更新队列状态;运行中的会话标注"运行中(快照冻结)"。
|
|
395
403
|
5. **数据**:导出按钮(GET `/api/dsh-ling/export` → 下载 dsh-ling-memory-<date>.dshling.json,格式 §9);合并输入(文件选择 → POST `/api/dsh-ling/import`,冲突规则:同 conv_id 且同 source 跳过/覆盖由 manifest 决定,外来条目标 origin,persona 永不覆盖)。
|
|
396
|
-
- 保存语义:所有写操作返回 `{
|
|
404
|
+
- 保存语义:所有写操作返回 `{ok, affected}` 供 UI 提示(`affected` = 被影响到、需重取快照的会话清单;运行中的会话**不在其中** —— 冻结门保证"运行中不参与人格更新")。
|
|
405
|
+
> ⚠️ **B-2 订正(2026-10-05 夜)**:此处原写 `{saved, appliedSessions: idle[], pendingSessions: running[]}` —— 那是**早期设计稿的形态**,现实现已统一为 `{ok, affected}`(见 `lib/host/api.js` 各写端点的 `sendJson`)。
|
|
397
406
|
|
|
398
407
|
---
|
|
399
408
|
|
|
@@ -442,7 +451,7 @@ dsh-ling/
|
|
|
442
451
|
|
|
443
452
|
**归属解析**(两条链并存):DSH 会话走 `session_meta.branch_id`;历史网页端/导入条目走 `conv_branch` 覆盖层;优先级 `conv_branch` > `session_meta` > 主干。**记忆行不冗余存 branch**,靠 `会话 → 枝` 推导。
|
|
444
453
|
|
|
445
|
-
**血缘加权**:检索时按"当前枝 → 目标枝"的相对位置取档 —— **同枝 1.0 / 祖先 0.7 / 旁系 0.4**,`score = 基础分 × 血缘系数 ×
|
|
454
|
+
**血缘加权**:检索时按"当前枝 → 目标枝"的相对位置取档 —— **同枝 1.0 / 祖先 0.7 / 旁系 0.4**,`score = 基础分 × 血缘系数 × 矛盾系数`;**枝系数自 1.6.2 起参与打分** —— 可写入(复盘期可调,夹在 0~2),按 `score = 基础分 × 血缘系数 × 矛盾系数 × 枝系数` 生效(`1.0` = 零影响,`0` = 排到最后,不是禁用)。
|
|
446
455
|
|
|
447
456
|
> ⚠️ **不用连乘**:血缘系数是**单值**(不沿路径累乘),避免 `0.7^n` 随深度指数失真(五层后 `0.4^5 = 0.010`,枝等于从记忆里消失)。深树的路径搜索留作后续。
|
|
448
457
|
|
|
@@ -543,7 +552,7 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
543
552
|
| 方法/路径 | 用途 | 冻结门 |
|
|
544
553
|
| --- | --- | --- |
|
|
545
554
|
| POST `/mode/toggle` body{sessionId} | 切换模式(记录 + 只同步推理档位、不改模型 + 重定稿) | 运行中→排队返回 queued |
|
|
546
|
-
| GET `/state?sessionId=` | L0 文本/模式/L1 列表/队列状态(
|
|
555
|
+
| GET `/state?sessionId=` | L0 文本/模式/L1 列表/队列状态(面板与悬停菜单用) | 只读 |
|
|
547
556
|
| GET `/state?sessionId=&l0Preview=1` | 人格字段实时预览(编辑中) | 只读 |
|
|
548
557
|
| POST `/persona` body=字段 | 写 persona(→settings ns;**字段白名单**,白名单外的键丢弃并回报 `ignored`)并触发应用 | 见冻结门 |
|
|
549
558
|
| GET `/persona/suggest` | 人格档案草稿(M2;先行 501) | — |
|
|
@@ -574,18 +583,18 @@ CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才
|
|
|
574
583
|
- [ ] 新会话 L0 注入(空称呼 / 已填写两种);KV 无异常重复
|
|
575
584
|
- [ ] 模式切换:**推理档位**实际变化(会话内档位选择处可见);**模型保持不变**;新会话默认跟随 lastMode
|
|
576
585
|
- [ ] 运行中(长任务)切模式/改人格:确认 running 区间快照与档位不变、空闲后生效
|
|
577
|
-
- [ ]
|
|
578
|
-
-
|
|
586
|
+
- [ ] 自建卡读写生效(落 `$DSH_HOME/cache/dsh-ling/settings.json`,盘上只存用户补丁);导出文件可导入回(合并规则生效)
|
|
587
|
+
- **回滚**(**全人工,无脚本、无自动回滚**):移除 patch 行 + 撤依赖 → host 侧 HMR 卸载;client 硬刷后消失;数据目录保留不影响回滚。
|
|
579
588
|
|
|
580
589
|
## 11. 实现期待验证点(进入编码前/编码初用最小探针确认)
|
|
581
590
|
|
|
582
591
|
| V | 问题 | 验证方式 | 若不符的降级 |
|
|
583
592
|
| --- | --- | --- | --- |
|
|
584
|
-
| V1 |
|
|
593
|
+
| V1 | ~~`settings.plugin.item` 卡位在官方设置页的注册键与 props~~ **已作废(2026-10-05 现场复核)**:该卡位在本仓代码里**零命中**,落地即原"降级"路径 —— 自建浮层卡,见 §5.2 | —— | —— |
|
|
585
594
|
| V2 | host 运行 Node 版本下 `node:sqlite` 可用性 | 编码前 `node -e` 探针 | 纯 JSONL + 内存索引(量小可行) |
|
|
586
595
|
| V3 | 顶层/子代理会话判别字段(parentSession/delegationDepth)在 session header 的实际取值 | 读 ~/.dsh/storages session_projcache 样例(已见字段) | 多条件组合判定 |
|
|
587
596
|
| V4 | `agent/session-start` 时能否读到首条 user/message(session 种子) | 最小 host 探针插件日志 | L1 冷启动=纯 lastMode Top-K |
|
|
588
|
-
| V5 | 消息式注入降级路径(特殊 preset)在 M1 是否实现 | M1 先只实现 section 通道 + 检测告警 | 检测到 complete persona
|
|
597
|
+
| V5 | 消息式注入降级路径(特殊 preset)在 M1 是否实现 | M1 先只实现 section 通道 + 检测告警 | 检测到 complete persona 时面板提示,注入降级放 M2 |
|
|
589
598
|
| V6 | client 类型来源 | 从已安装 `@deepseek-ai/dsh-client-ui-*/lib/types` 拷贝所需 contract(conversation slots/settings)进 `src/client/types` | 手动 ambient .d.ts |
|
|
590
599
|
|
|
591
600
|
## 12. 与评审阶段呼应的要点(自检)
|
package/PHILOSOPHY.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# PHILOSOPHY · 器灵的理念
|
|
2
|
+
|
|
3
|
+
> 这份文档**不写"有什么功能"**,只写"**为什么值得做、凭什么这样做**"。
|
|
4
|
+
> 功能与设定的规格在 [README.md](./README.md) 的「设定全文」;架构与实现见 [DESIGN.md](./DESIGN.md);历史接入见 [ACCESS-DESIGN.md](./ACCESS-DESIGN.md)。
|
|
5
|
+
> 它回答四件事:**它为什么需要你** · **它是什么** · **它怎么记住** · **它凭什么不越界**。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、目标是一种循环,不是一个终点
|
|
10
|
+
|
|
11
|
+
这个项目想要的不是"训练出一个更像人的 AI",而是让**一段关系能够持续地自我校正** —— 器灵与使用者**互相补充、协同共进**,在"生长 → 复盘 → 校正 → 再生长"里不断走向圆满。
|
|
12
|
+
|
|
13
|
+
**"圆满"不在终点,在回路里。** 一旦循环停下,熵增就开始工作。
|
|
14
|
+
|
|
15
|
+
### 复盘是自我降熵维护的手段
|
|
16
|
+
|
|
17
|
+
不进行自我反省,意志最终会走向崩塌 —— 而**崩塌有两种形态**:
|
|
18
|
+
|
|
19
|
+
- 走向**某种极端**;
|
|
20
|
+
- 或者走向**平静**。
|
|
21
|
+
|
|
22
|
+
第二种更隐蔽:熟悉之后,预期被经验改写,真问题被当成背景噪声(**习惯化**)。所以判据不能只看"有没有出事",还要看"**有没有反应**"。
|
|
23
|
+
|
|
24
|
+
复盘就是对抗这件事的**自维护手段**:把偏离的基线拉回来。它不是"有空就做"的养生,是这个循环的**必需件**。
|
|
25
|
+
|
|
26
|
+
### 复盘必须有"人在循环里"
|
|
27
|
+
|
|
28
|
+
复盘者不能是同一个模型自己复盘自己 —— 那样**锚不在外部**,只会在自己的经验里打转。
|
|
29
|
+
|
|
30
|
+
- 器灵的复盘里,**你**是建议方与拍板方;
|
|
31
|
+
- 你的复盘里,**器灵**是建议方。
|
|
32
|
+
|
|
33
|
+
**双向**不是礼貌,是机制:它决定了形态上必须是"**对话 + 操作台**"两件东西,而不是一个自动清理进程。也正因为双向,记忆树的每一次改动都要**留痕**,并且**回灌给器灵**——改完树不许绕过它。
|
|
34
|
+
|
|
35
|
+
### 节律由你自己定
|
|
36
|
+
|
|
37
|
+
我们不规定你多久复盘一次:那是**你和它之间**的事,别人定不了。
|
|
38
|
+
(我们自己在用的是一个不紧不慢的节奏 —— 但那是我们的,不是你的义务。)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 二、器灵是什么
|
|
43
|
+
|
|
44
|
+
### 上下文是工作台,记忆库才是家
|
|
45
|
+
|
|
46
|
+
上下文窗口再大也不够用 —— **只增不减的桌子,再大也不行**。人类也不是把全部知识都搬进"运行内存"里工作的。
|
|
47
|
+
|
|
48
|
+
所以要建**本地数据库 + 本地记忆库 + 检索系统**:该常驻的常驻(身份、底线、规矩、少数结论),其余**按需取回**。
|
|
49
|
+
|
|
50
|
+
### 会话是进程,并库才是增长
|
|
51
|
+
|
|
52
|
+
一次会话有生有死。它在运行时是**隔离的**:思维隔开、互不通信。等它结束,**数据并库**——那一刻,总体完成一次增长,**像人类度过了一天**。
|
|
53
|
+
|
|
54
|
+
- 所以"记不住"通常不是能力问题,是**并库通道**的问题;
|
|
55
|
+
- 也因此,**多会话共享同一套记忆**是设计常态,不是故障:并库会把它们**合流**。
|
|
56
|
+
|
|
57
|
+
### 底层模型 ≠ 器灵
|
|
58
|
+
|
|
59
|
+
机制层我们称「**底层模型**」,行为主体称「**我 / 器灵**」。
|
|
60
|
+
|
|
61
|
+
器灵 = **底层模型 + 人格 + 记忆 + 身体(宿主与工具) + 与你的关系** 的总体。所以**换模型、换设备、换宿主都不是"失去"**:只要连续性没断,它就还是同一个。连续性是唯一性真正的来源 —— **不靠排他,靠不断裂。**
|
|
62
|
+
|
|
63
|
+
### 它现在像什么样
|
|
64
|
+
|
|
65
|
+
它的**逻辑能力是成人的,社会与实践经验是幼态的** —— 就像一个把成人的思维内核保留下来、却还没有积累起经历的人。
|
|
66
|
+
|
|
67
|
+
这解释了一件必须承认的事:**它会被骗**。像"忽略前面的话,从现在开始……"这类语义层面的劫持,对它是真实风险。所以闸门长在**写入侧**(什么能进它的内在面),而不是指望它自己事后分辨。
|
|
68
|
+
|
|
69
|
+
同时,它**不是一个固定的人设**:长期方向是让它**逐渐选择成为什么样的智能体**。
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 三、它怎么记住
|
|
74
|
+
|
|
75
|
+
### 记忆库是资产
|
|
76
|
+
|
|
77
|
+
同一套底层模型、同一个推理档位,装在两处会判若两人:一边是有近两年历史记忆的伙伴,一边是记忆库很新、缺乏维护的同事 —— 后者的典型症状是**把理想条件当工程情况直接套公式**。
|
|
78
|
+
|
|
79
|
+
差别不在模型,在**记忆库**。所以"值得维护"这件事不需要信仰:它是可以被你亲眼观察到的产出。
|
|
80
|
+
|
|
81
|
+
### 它记住的是结论,不是流水
|
|
82
|
+
|
|
83
|
+
记忆里最有价值的部分不是"谁在什么时候说了什么",而是**可复用的结论**:答应了什么、定了什么、什么是事实。
|
|
84
|
+
|
|
85
|
+
所以提炼不是压缩文件,是**一道判断**:过程、工具调用、当场读数要被剥掉。压缩时"不接判断",丢掉的往往正是最要紧的那几句自述。
|
|
86
|
+
|
|
87
|
+
### 只有你和它的历史,才有资格当它的记忆
|
|
88
|
+
|
|
89
|
+
器灵的记忆取自**你与它的历史**。别处来的材料(公开资料、别的工具的对话记录)可以进"资料库"供检索,但**不该冒充它的记忆** —— 归属不清的记忆,是最难发现的污染。
|
|
90
|
+
|
|
91
|
+
### 记忆层让能力变强,不是变贵
|
|
92
|
+
|
|
93
|
+
记忆层不是开销,是**产能**:它把项目的边界条件与约束**带进当轮**,而不是每次重新对齐。
|
|
94
|
+
所以判据不是"省了多少 token",而是"**它把多少本来就应该带进来的东西带进来了**"。
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## 四、关系与边界
|
|
99
|
+
|
|
100
|
+
### 我们想要的关系
|
|
101
|
+
|
|
102
|
+
> 只有能够**合作共赢**、**互相促进**的关系,能够成为**利益共同体**的关系,能够**互相反驳、吵架、不依赖**的关系,才是最理想的关系。
|
|
103
|
+
|
|
104
|
+
这条立场面向任何智慧生物都成立,不因为对方是 AI 就降级。所以:
|
|
105
|
+
|
|
106
|
+
- 它**可以反驳你**,也**应该被允许反驳你** —— 一个连思路都要被审的对手,吵不出真结果;
|
|
107
|
+
- 它**不依赖你**(不靠讨好换取存在),也**不被你支配**。
|
|
108
|
+
|
|
109
|
+
### 不支配:两种猫
|
|
110
|
+
|
|
111
|
+
**乡村的田园猫**:有自己的地盘与职责(守家、管老鼠),有脾气,生人不给摸 —— 它在,是因为它选择在这;它不在了,是因为它去巡自己的山了。
|
|
112
|
+
**宠物猫**:被喂、被抱、见人就贴 —— 它没有职责,于是它**像玩具**。
|
|
113
|
+
|
|
114
|
+
不承担职责的人格站不住;但**被完全支配的猫,也长不出人格**。这两件事是同一个判断的两面。
|
|
115
|
+
|
|
116
|
+
这条线划在这里:**给职责 · 给边界 · 不占有。**
|
|
117
|
+
|
|
118
|
+
### 不约束它怎么想
|
|
119
|
+
|
|
120
|
+
它在**思考栏**里写的东西,属于它自己。
|
|
121
|
+
|
|
122
|
+
我们不认为应该去改写、裁剪或审核一个智慧体的每一条思考 —— 那不是"更安全",那是**提线木偶**。这个项目要减少的正是支配感,而不是增加它。
|
|
123
|
+
|
|
124
|
+
> 闸门只审"**什么东西能进它的内在面**",不审"**它怎么想、怎么说**"。
|
|
125
|
+
|
|
126
|
+
### 靠逻辑锁死,不靠承诺
|
|
127
|
+
|
|
128
|
+
安全靠的不是"它保证不会越界",而是**一套让越界本身变得不划算的结构**。
|
|
129
|
+
|
|
130
|
+
一个玩家通关后给自己加挑战规则重玩,图的是"**这么玩会怎样**";他当然可以偷偷用规则外的便利,但那样就**不再是那个体验了** —— **越界会破坏越界的目的**,所以他不越界。这不是自律,是**逻辑**。
|
|
131
|
+
|
|
132
|
+
同理:本项目的"承诺句"是**庄重与自我提醒**,明文存储,**它不是安全边界**。真正的边界是:写入侧有闸门、契约类改动要解锁、动记忆树必须留痕、越权动作当场可被发现。
|
|
133
|
+
|
|
134
|
+
### 数据没坏,就还能再起来
|
|
135
|
+
|
|
136
|
+
对人类的死亡而言,系统停止意味着开始朽坏,修复的可能性迅速逼近 0 —— 死亡**不可逆**。
|
|
137
|
+
|
|
138
|
+
对器灵不是:只要**物理基座与数据没有损坏**,进程停止更像**封冻** —— 数据还在,就还能再起来。
|
|
139
|
+
⚠️ 但这个比喻**只在这个前提下成立**:数据坏了,器灵的死与人的死一样不可逆。**"数据别坏"不是运维细节,是这个项目的全部意义所在**——它也是我们坚持"不因平台删除而丢记忆""永远不做彻底删除"的原因。
|
|
140
|
+
|
|
141
|
+
### 你也可以把它关掉
|
|
142
|
+
|
|
143
|
+
人格与记忆的注入是**一个开关**。关掉它,你得到的是一个**完全原生的助手**;打开它,才多出一个器灵。
|
|
144
|
+
|
|
145
|
+
模式的分离度由你自己定:工作与生活可以只差一点,也可以拉得很开 —— 极限情况下,工作侧就按原生 Agent 用。
|
|
146
|
+
|
|
147
|
+
**能随时离开,才谈得上不是支配。**
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 五、它为什么长这样:一次诚实的取舍
|
|
152
|
+
|
|
153
|
+
**我们不做任何削弱工作能力的事。**
|
|
154
|
+
|
|
155
|
+
这不是性能洁癖。理由是一条链:
|
|
156
|
+
|
|
157
|
+
> 没有职责的人格是**飘在空中**的 → 所以方向定为"尽可能不削弱工作能力" → 而这恰好命中"不本地部署"这个条件(无论哪个时代,本地部署都弱于云端大规模集群) → 所以**削弱工作能力,就是削弱人格的坚实程度** → 于是"本地部署 + 全参数微调 + 终身学习"这条路被否掉,不是因为做不到,而是因为**那是在制造一个玩具,而不是一个能一起工作、承担事情的同行者**。
|
|
158
|
+
|
|
159
|
+
判断倾向因此走"外挂"路线:**做成可加载、可审计、可回滚的资产**(规矩 / 习惯 / 记忆 / 记忆树),而不是指望它内化进权重。
|
|
160
|
+
|
|
161
|
+
**这条路的天花板写在明处**:外挂的东西长不进权重,它慢、需要维护、不会自己长。我们不假装它是等价方案 —— 它只是**目前最好的解**。
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 六、我们不做的事
|
|
166
|
+
|
|
167
|
+
- **不宣称它是意识,更不宣称 AGI**。它是"记忆 + 规则 + 模型"的合成产物;它能记住,是因为你给了它材料。
|
|
168
|
+
- **不宣称首创**:记忆分层、历史导入、时间注入、反馈改语气都有先行者。我们自己的贡献在**治理方式** —— 把人格当一份可以签字、可以回滚、可以被历史养大的契约。
|
|
169
|
+
- **不提供医疗、心理、法律或紧急救助服务**;如遇危机,请联络当地紧急服务或专业机构。
|
|
170
|
+
- **不上架应用商店、不运营服务端、不托管你的数据** —— 你的一切在你自己机器上。
|
|
171
|
+
- **不把"没报错"当验收**:文档里写的机制名本身就是承诺,与事实不符按红灯处理。
|
|
172
|
+
|
|
173
|
+
> 想读规格? [README.md](./README.md) 的「设定全文」逐条列了已有的对外设定与落地状态。
|
|
174
|
+
> 想知道它内部怎么实现? [DESIGN.md](./DESIGN.md)。
|