@nightpainters/dsh-ling 1.5.1
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 +119 -0
- package/CHANGELOG.md +627 -0
- package/DESIGN.md +461 -0
- package/LICENSE +201 -0
- package/NOTICE +7 -0
- package/README.md +236 -0
- package/cordis.patch.yml +16 -0
- package/lib/audit/access-log.js +533 -0
- package/lib/audit/index.js +92 -0
- package/lib/audit/patch-spec.js +195 -0
- package/lib/client.js +5069 -0
- package/lib/host/api.js +2584 -0
- package/lib/host/archive.js +194 -0
- package/lib/host/autotree.js +131 -0
- package/lib/host/backfill.js +267 -0
- package/lib/host/classify.js +10 -0
- package/lib/host/clock-skill.js +141 -0
- package/lib/host/clock.js +132 -0
- package/lib/host/corpus-suggest.js +139 -0
- package/lib/host/deepsummary.js +338 -0
- package/lib/host/dsweb-summary.js +225 -0
- package/lib/host/feedback.js +119 -0
- package/lib/host/freeze.js +144 -0
- package/lib/host/genesis.js +176 -0
- package/lib/host/guard.js +184 -0
- package/lib/host/habit-gen.js +273 -0
- package/lib/host/import-file.js +291 -0
- package/lib/host/import-log.js +129 -0
- package/lib/host/inject-common.js +25 -0
- package/lib/host/inject.js +285 -0
- package/lib/host/l1.js +282 -0
- package/lib/host/lifecycle.js +367 -0
- package/lib/host/memory.js +2389 -0
- package/lib/host/mode.js +101 -0
- package/lib/host/persona.js +214 -0
- package/lib/host/retitle.js +214 -0
- package/lib/host/rules.js +195 -0
- package/lib/host/scan-dsweb.js +111 -0
- package/lib/host/seal.js +17 -0
- package/lib/host/settings-file.js +49 -0
- package/lib/host/snapshot.js +168 -0
- package/lib/host/suggestions.js +47 -0
- package/lib/host/summarizer.js +103 -0
- package/lib/host/tone-advice.js +112 -0
- package/lib/host/tools.js +1067 -0
- package/lib/host/tree-ops.js +330 -0
- package/lib/host/util.js +165 -0
- package/lib/host/zstd.js +63 -0
- package/lib/index.js +263 -0
- package/package.json +69 -0
- package/tools/apply-access-log-patch.mjs +295 -0
package/DESIGN.md
ADDED
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
# dsh-ling — 「器灵」记忆与人格助手插件 · M1 设计文档
|
|
2
|
+
|
|
3
|
+
> 依据:立项阶段的一次接口探测 spike 结论(含本地文件:行号证据)。
|
|
4
|
+
> 本文档记录 M1 阶段的设计与决策;**实现以代码为准**(后续演进:存储 schema 已升到 v2、新增历史接入/深摘要/诞生仪式/语气成长等子系统,详见仓库代码与提交记录)。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 0. 已定决策记录(用户拍板 + spike 结论)
|
|
9
|
+
|
|
10
|
+
| # | 决策 | 内容 | 来源 |
|
|
11
|
+
| --- | --- | --- | --- |
|
|
12
|
+
| D1 | 按钮落位 | **方案 A**:官方 slot `conversation.session.header.utilities`(list,会话页顶栏右侧) | 用户拍板;spike ① |
|
|
13
|
+
| D2 | 模式↔全局默认 | **跟随**:最近一次模式切换成为"之后新会话"的默认(平台 selectModel 会 best-effort 写全局默认,此副作用即机制)。**2026-09-12 收敛:只同步推理等级,不改模型** —— provider/model 一律沿用当前值 | 用户拍板;report-03 |
|
|
14
|
+
| D3 | 人格承载 | **自建注入段 + 设置页维护结构化字段**(称呼/自称/语气等),dsh-persona 不复用 | 用户拍板;report-04 |
|
|
15
|
+
| D4 | 运行期冻结(新增) | **会话运行中(思考/任务进行中)不参与"人格更新层"的任何变化**:不打断、不新增设定、不中途改注入内容;更新请求一律排队,在该会话空闲边界才应用 | 用户本轮追加 |
|
|
16
|
+
| D5 | 记忆加载策略 | 三层记忆(L0 常驻人格 / L1 开场 Top-K / L2 运行时检索),不注入全部概述 | 早前拍板 |
|
|
17
|
+
| D6 | 模式语义 | 工作/生活 = 先验倾向,不硬过滤;差异 = **推理档位** + 记忆权重向量 + 风格块(不改模型) | 早前拍板 |
|
|
18
|
+
| D7 | 配置页 | 右键进入;内含"当前 L0/L1 现状"查看 | 早前拍板 |
|
|
19
|
+
|
|
20
|
+
**插件命名**:工程目录/包名 `dsh-ling`(器灵);包显示名 "dsh-ling · 器灵";UI 文本语言 zh。
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 1. 范围
|
|
25
|
+
|
|
26
|
+
**M1 交付**:
|
|
27
|
+
1. 可安装插件骨架(host `lib/index.js` + client `lib/client.js` + settings 命名空间 + 本地构建);
|
|
28
|
+
2. 人格子系统(D3):字段 → 组装器 → L0 文本 → 按会话快照注入;
|
|
29
|
+
3. 控制器按钮(D1):左键=切换本会话工作/生活;右键=自绘菜单(打开配置/L0·L1 现状/导出);
|
|
30
|
+
4. 模式切换链路(D2/D6):selectModel(max/low)+ 本会话模式标记 + 快照按模式重定稿;
|
|
31
|
+
5. 冻结原则(D4):运行中一切"人格更新层"变更排队,空闲边界应用;
|
|
32
|
+
6. 记忆存储底座:own DB + DSH 会话增量捕捉(事件管线) + L1 选择 v0(热度 Top-K);
|
|
33
|
+
7. 导出 v1(带 manifest)接口占位(UI 完整页在 M2,宿主 API 先行)。
|
|
34
|
+
|
|
35
|
+
**M2/M3(预留接口,不在 M1 实现)**:配置完整页(settings.section)与记忆树可视化、导出/合并 UI、概述器(LLM 摘要调度)、反馈成长回路、人格档案草稿脚本接入。
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## 2. 总体架构
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
┌─ 浏览器 (client) ───────────────────────────────────────────┐
|
|
43
|
+
│ slots: conversation.session.header.utilities 控制器按钮 │
|
|
44
|
+
│ onContextMenu→自绘菜单 / 左键→切模式 │
|
|
45
|
+
│ 设置页卡(plugin.item:<dsh-ling>)含 L0/L1 现状面板 │
|
|
46
|
+
└───────────┬──────────────────────────────┬──────────────────┘
|
|
47
|
+
│ fetch /api/dsh-ling/* │ ctx.remote.* (可选)
|
|
48
|
+
┌───────────▼──────────────────────────────▼──────────────────┐
|
|
49
|
+
│ Host 插件 (cordis apply) │
|
|
50
|
+
│ · settings ns "dsh-ling"(schema: 称呼/自称/语气/模式映射…) │
|
|
51
|
+
│ · ctx.systemPrompt.section("dsh-ling.persona") 全局注入 │
|
|
52
|
+
│ └ text 函数:运行期冻结门 → 会话快照缓存 → L0+L1 文本 │
|
|
53
|
+
│ · 事件监听:session-start(快照定稿)/ turn-end / agent-status │
|
|
54
|
+
│ (运行门)/ session-event(增量捕捉)/ session-disposed(归档) │
|
|
55
|
+
│ · mode 服务:selectModel + 本会话 mode 记录 + 队列(冻结) │
|
|
56
|
+
│ · HTTP 网关 /api/dsh-ling/* (webServer.register, 守卫) │
|
|
57
|
+
│ · 记忆存储:node:sqlite @ ~/.dsh/cache/dsh-ling/memory.db │
|
|
58
|
+
└───────────┬──────────────────────────────┬──────────────────┘
|
|
59
|
+
│ 事件订阅(ctx.on) │ node:fs / sqlite
|
|
60
|
+
┌───────────▼──────────────────────────────▼──────────────────┐
|
|
61
|
+
│ DSH 平台:session/* agent/* 事件、sessionQuery、settings、 │
|
|
62
|
+
│ llm(供 M2 概述器)、sessionController(selectModel) │
|
|
63
|
+
│ 磁盘语料:历史导出(ds-search 库) + ~/.dsh/sessions/*.jsonl.zstd│
|
|
64
|
+
└─────────────────────────────────────────────────────────────┘
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**分层原则(沿用评审定稿)**:身份层(L0)与情节层(概述/记忆)分离;模式是倾向不是闸门;注入内容会话内稳定(快照,保 KV 前缀);运行中冻结(D4)。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## 3. 目录与打包/安装
|
|
72
|
+
|
|
73
|
+
开发仓库:你的克隆目录(下方为作者本机的目录结构示例)
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
dsh-ling/
|
|
77
|
+
├─ DESIGN.md # 本文档
|
|
78
|
+
├─ package.json # name: dsh-ling; dsh.bundle.patch / dsh.client.platform=web
|
|
79
|
+
├─ cordis.patch.yml # - insert: {id: dsh-ling, name: dsh-ling}
|
|
80
|
+
├─ src/
|
|
81
|
+
│ ├─ host/
|
|
82
|
+
│ │ ├─ index.ts # apply(ctx): 全部注册(见 §5)
|
|
83
|
+
│ │ ├─ settings.ts # ns schema + 默认值
|
|
84
|
+
│ │ ├─ persona.ts # 字段→L0 组装器 + 快照缓存(会话 id→文本)
|
|
85
|
+
│ │ ├─ freeze.ts # 运行期冻结门(状态机, §7)
|
|
86
|
+
│ │ ├─ inject.ts # systemPrompt section 注册 + 过滤 + 降级(消息式预留)
|
|
87
|
+
│ │ ├─ lifecycle.ts # session/turn/status/event 监听 → 记忆管线
|
|
88
|
+
│ │ ├─ mode.ts # mode→(provider,model,effort) 映射 + selectModel + 跟随
|
|
89
|
+
│ │ ├─ memory.ts # sqlite 存储层(DDL §9)
|
|
90
|
+
│ │ ├─ l1.ts # L1 Top-K 选择(热度公式 §8)
|
|
91
|
+
│ │ └─ api.ts # /api/dsh-ling/* 路由(webServer.register)
|
|
92
|
+
│ ├─ client/
|
|
93
|
+
│ │ ├─ index.ts # apply(ctx) inject:["slots","locale","connection"]
|
|
94
|
+
│ │ ├─ button.tsx # header.utilities 按钮 + 状态图标 + 右键菜单
|
|
95
|
+
│ │ ├─ settings-card.tsx # 设置卡(plugin.item:<dsh-ling>)+ L0/L1 现状面板
|
|
96
|
+
│ │ └─ i18n.zh.ts
|
|
97
|
+
│ └─ shared/
|
|
98
|
+
│ ├─ settings-schema.ts # host/client 共用的字段定义(单一事实源)
|
|
99
|
+
│ └─ api-types.ts # /api 请求/响应类型
|
|
100
|
+
├─ lib/ # 构建产物(host: cjs/esm;client: __ModuleLoader__ bundle)
|
|
101
|
+
└─ build.mjs / tsdown.config # 本地构建脚本(无网环境:直接 tsc/esbuild?)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**安装(复用 install/INSTALL-PLAN 模式,含回滚)**:
|
|
105
|
+
1. `pnpm add file:<仓库路径>`(或等价 file: 依赖)进 `$DSH_HOME/profiles/web/package.json`(文中出现的 `E:\DSH\V1\...` 等均为作者本机路径,按你的实际路径替换);
|
|
106
|
+
2. `cordis.patch.yml` 追加行 `- insert: [{id: dsh-ling, name: dsh-ling}]`(loader id == 包名);
|
|
107
|
+
3. patchReload live → host 热载;浏览器硬刷载入 client 模块;失败即移除该行回滚(先备份两文件,参照 backup-20260906-170533 做法);
|
|
108
|
+
4. GUI 鉴权墙:安装后的功能验证需在用户已登录浏览器进行(本机端口 3080)。
|
|
109
|
+
|
|
110
|
+
**数据目录(插件本体外,更新不丢)**:`dshHomePath('cache','dsh-ling')` → `~/.dsh/cache/dsh-ling/`(memory.db、snapshots、exports)。
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## 4. Host 设计(apply 注册清单)
|
|
115
|
+
|
|
116
|
+
| 模块 | 注册内容 | 关键 API / 证据 |
|
|
117
|
+
| --- | --- | --- |
|
|
118
|
+
| settings | `ctx.settings.register('dsh-ling', schema, {base})` | settings ns 落 `$DSH_HOME/settings.yaml`(report-04) |
|
|
119
|
+
| inject | `ctx.systemPrompt.section({name:'dsh-ling.persona', order: ORDER, text: fn})` | order 取部署 persona(0)之后、agent-instructions 之前(如 1000~2000);全局注册 + text 内按会话过滤(仅顶层、非 subagent,否则返回空串);report-02/R5 |
|
|
120
|
+
| freeze | `ctx.on('agent/status')` 等维护每会话运行态 | idle|running 翻转事件(report-02) |
|
|
121
|
+
| lifecycle | `agent/session-start`(定稿快照;source=startup)、`session/event`(顶层会话增量捕捉)、`turn/end`、`session/disposed`(归档点) | report-02 |
|
|
122
|
+
| mode | `ctx.sessionController.selectModel({sessionId, provider, model, reasoningEffort})` —— **provider/model 必填(平台契约),故取当前选择原样带回,只替换 `reasoningEffort`;读不到当前模型则不调用**(宁可不切档位,也不替用户指定模型) | report-03;档位映射:工作=max / 生活=low(默认),存 settings 可编辑 |
|
|
123
|
+
| memory | sqlite 存储(见 §9);增量写 own 表 | ctx.sessionQuery 用于补读(可选) |
|
|
124
|
+
| api | `ctx.get?.('webServer').register({kind:'exact', path:'/api/dsh-ling/…'})` + `dsh-auth-` cookie 守卫 | ego-browser 先例(事实 9) |
|
|
125
|
+
| (预留) llm | M2 概述器:经 ctx.llm 走适配器(不冻结 loop 请求,只做离线摘要) | report-03(llm/stream 只读护栏只约束 loop) |
|
|
126
|
+
|
|
127
|
+
### 4.1 settings 命名空间 schema(`dsh-ling`)
|
|
128
|
+
|
|
129
|
+
```jsonc
|
|
130
|
+
{
|
|
131
|
+
"persona": {
|
|
132
|
+
"enabled": true, // 总开关;false=不注入任何人格/记忆段
|
|
133
|
+
"userTitle": "", // 对用户的称呼,默认空=称呼"你"
|
|
134
|
+
"aiName": "", // AI 自称,默认空
|
|
135
|
+
"aiTitle": "", // 定位自述(一句话)
|
|
136
|
+
"tone": "natural", // natural|literary|concise|playful
|
|
137
|
+
"language": "follow", // zh | en | follow
|
|
138
|
+
"hardRules": [], // 惯例(内部字段名沿用 hardRules;UI/注入显示为「惯例」)
|
|
139
|
+
"bottomLines": [], // 底线(≤5 条;定型锁开启后修改需亲手敲承诺句)
|
|
140
|
+
"toneWork": "", // 工作模式语气(''=跟随 tone;切换模式自动用对应档)
|
|
141
|
+
"toneLife": "", // 生活模式语气(''=跟随 tone)
|
|
142
|
+
"sealed": false, // 定型锁:true = 档案只读,改动需亲手敲一遍承诺句(禁粘贴)
|
|
143
|
+
"sealPhrase": "", // 定型承诺句(明文;只是庄重与自我提醒、不是安全边界——锁只造庄重,真正的门是本机信任与本地 cookie/令牌)
|
|
144
|
+
"extraLore": "" // 自由扩展设定(器灵世界观等)
|
|
145
|
+
},
|
|
146
|
+
"styles": {
|
|
147
|
+
"work": "克制、结构化、结论先行,少抒情",
|
|
148
|
+
"life": "更有温度,可沿用用户偏爱的文风,允许共情与适度调侃"
|
|
149
|
+
},
|
|
150
|
+
"mode": {
|
|
151
|
+
"mapping": { // 档位映射表(用户可改):只含推理等级,不含模型
|
|
152
|
+
"work": { "effort": "max" },
|
|
153
|
+
"life": { "effort": "low" }
|
|
154
|
+
},
|
|
155
|
+
"lastMode": "life" // 跟随(D2):最后使用的模式
|
|
156
|
+
},
|
|
157
|
+
"memory": {
|
|
158
|
+
"l0Always": true, "l1Enabled": true, "l1BudgetTokens": 1200,
|
|
159
|
+
"l1MaxItems": 8,
|
|
160
|
+
"weights": { // 模式→领域权重向量(D6)
|
|
161
|
+
"work": { "knowledge": 1.0, "daily": 0.25, "feeling": 0.1 },
|
|
162
|
+
"life": { "knowledge": 0.2, "daily": 0.6, "feeling": 1.0 }
|
|
163
|
+
},
|
|
164
|
+
"trackWorkspaces": ["*"] // 增量捕捉范围(默认全部工作区;'*')
|
|
165
|
+
},
|
|
166
|
+
"updates": { "applyWhileRunning": false } // D4 语义固化:默认冻结
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### 4.2 人格组装器(persona.ts)输出示例
|
|
171
|
+
|
|
172
|
+
空字段全部跳过;生成的 L0 文本 = `身份段 + 底线 + 规矩 + 习惯 + extraLore + 模式块`(模式块由本会话模式选择,内容来自 styles.*)。**段头一律是纯标签**(见下例):原先写在括号里的元描述(`[规矩](用户的指令,可直接追加)` 这种)是**写给主人看的说明**,不进模型上下文;段与段之间**空一行**,段内条目仍单换行 —— 段边界因此只剩「行首是不是 `[`」一条判据,**故条目正文里的半角方括号在渲染时转全角**(`[`→`[`、`]`→`]`,规矩 / 习惯 / 底线三类条目统一转);规矩 / 习惯另在渲染出口按同键各去一次重(保留首次出现,只作用于渲染,不回写存储)。`[规矩]` = 用户的指令,可直接追加;`[习惯]` = 器灵自己长出来的,新增需用户确认。填好后的例子:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
[身份·器灵]
|
|
176
|
+
你是"器灵";你称用户为"老板",<定位自述一句话>。
|
|
177
|
+
语气:亲切自然,不轻浮;专业问题先严谨再谈温度。
|
|
178
|
+
|
|
179
|
+
[底线]
|
|
180
|
+
- 绝不编造历史记忆
|
|
181
|
+
|
|
182
|
+
[规矩]
|
|
183
|
+
- 引用过往对话记忆时,注明来自哪个历史会话(标题+日期)
|
|
184
|
+
- 不知道的事明说不知道,不编造
|
|
185
|
+
|
|
186
|
+
[习惯]
|
|
187
|
+
- 先接住情绪再谈事
|
|
188
|
+
|
|
189
|
+
[设定]
|
|
190
|
+
(extraLore 原样内容——例如"器灵世界观"段落)
|
|
191
|
+
|
|
192
|
+
[生活模式]像老朋友一样接住情绪再谈事;允许文雅笔调;不急着给方案。
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
> **瘦身口径(2026-09-16)**:身份句不再重复宿主已声明的平台名(`DeepSeek Harness` 由宿主系统提示给出);模式块**合并为一行**(原为"段头 + 本会话为X模式:…"两行);语气标签由「语气基调」缩为「语气」。净字数变化见 `CHANGELOG.md` 1.1 节。
|
|
196
|
+
|
|
197
|
+
### 4.3 快照与注入时序(含 D4)
|
|
198
|
+
|
|
199
|
+
1. `agent/session-start`(source=startup,顶层会话):**定稿快照** —— 冻结门空闲时组装 `L0(+模式风格)+ L1(Top-K)` 文本,缓存 `会话id→{text, mode, personaVersion, memoryVersion}`;若运行中(理论上 start 时未运行)按门规则处理。
|
|
200
|
+
2. 每 step 组装时 section.text() 仅查缓存返回(纯读取,零副作用)。
|
|
201
|
+
3. 失效源:模式切换 / persona 字段保存 / 记忆热度重算 / 显式"立即刷新" —— 一律先过冻结门:目标会话 running → 记 `pendingVersion++`,空闲边界(agent/status idle 或 turn/end)重定稿并清 pending;idle → 立即重定稿。
|
|
202
|
+
4. **运行中绝不**:调 selectModel、改快照、注入新文本、向 agent 注入任何消息(不打断、不加设定)。
|
|
203
|
+
5. KV 语义:同文本重算前后缀稳定;每次"有意失效"至多一次前缀击穿(可接受)。
|
|
204
|
+
|
|
205
|
+
### 4.4 事件监听表(lifecycle.ts)
|
|
206
|
+
|
|
207
|
+
| 事件 | 动作 | 运行期门 |
|
|
208
|
+
| --- | --- | --- |
|
|
209
|
+
| `agent/session-start`(startup) | 定稿 L0+L1 快照;初始化该会话运行态=idle;mode 初值=settings.mode.lastMode | — |
|
|
210
|
+
| `agent/status` | 更新会话运行态(running/idle);running→idle 时:flush pending 更新(快照重定稿/排队 selectModel) | 门主体 |
|
|
211
|
+
| `session/event` | 顶层会话增量捕捉:user/assistant 消息全文→own 增量表(被动记录,不影响运行) | 记录不受门限制(不改注入、不打断) |
|
|
212
|
+
| `turn/end` | 轮次边界:若 pending,执行重定稿;触发"待概述"标记 | 边界点 |
|
|
213
|
+
| `session/disposed` | 归档点:该会话增量→cold 段;新会话 created+同 id 视作 clear(报告-02 推断规则) | 归档在拆毁后,天然安全 |
|
|
214
|
+
| `session/created` | 注册会话(若在 trackWorkspaces 范围);mode 记录表初始化 | — |
|
|
215
|
+
| (M2) messageFeedback 轮询 | 修订建议队列(用户确认才并入 persona) | 与注入无关 |
|
|
216
|
+
|
|
217
|
+
### 4.5 冻结门状态机(freeze.ts)
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
会话状态: idle ⇄ running(agent/status)
|
|
221
|
+
请求矩阵:
|
|
222
|
+
请求 idle 时 running 时
|
|
223
|
+
────────────────────────────────────────────────────────────
|
|
224
|
+
切换模式(含 selectModel) 立即:记录mode+selectModel 排队(pending.mode)
|
|
225
|
+
+重定稿快照 UI 显示"空闲后生效"
|
|
226
|
+
persona 字段保存(应用) 立即重定稿(仅空闲会话) 仅标记 personaVersion++
|
|
227
|
+
(新快照在边界/新会话生效)
|
|
228
|
+
显式"刷新记忆/立即应用" 立即 UI 提示运行中,已排队
|
|
229
|
+
导出/查看 L0/L1 状态 直接读 直接读(只读,不设限)
|
|
230
|
+
增量记忆捕捉 写库(旁路) 写库(旁路,不影响运行)
|
|
231
|
+
```
|
|
232
|
+
不变量:**注入面文本在任何 running 区间内逐字节不变**;selectModel 绝不会在 running 区间发出(平台侧也以"下一请求"生效,排队至空闲=无中间态漂移)。
|
|
233
|
+
|
|
234
|
+
> **出口中和口径(2026-09-18)**:凡进入系统提示的文本(快照成文 + 时间锚),必须先把相邻的两个 `{` 拆开(插入零宽空格)。原因是宿主模板对**未注册**的 `{{name}}` 直接抛异常、外层无兜底 —— 记忆里的一段对话原文(模板片段、代码、文档正文)即可让该会话**每一步模型调用都失败**。中和位点两处、覆盖全部注入路径:`snapshot.js` 的 `parts.join`(成文侧)与 `inject.js` 的 `withAnchor`(最终侧);实现见 `util.js` 的 `neutralizeMustache()`,验证见 `CHANGELOG.md` 对应版本节。
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 5. Client 设计
|
|
239
|
+
|
|
240
|
+
模块契约:`window.__ModuleLoader__.load({id:'dsh-ling', factory})`;导出 `{name:'dsh-ling', apply(ctx), inject:['slots','locale','connection']}`(settings 卡数据走 fetch `/api/dsh-ling/*`,同 ego 模式;`betterSidebar` 等社区服务一律不依赖)。
|
|
241
|
+
|
|
242
|
+
### 5.1 控制器按钮(D1 方案 A)
|
|
243
|
+
|
|
244
|
+
- 落位:`ctx.slots.inject('conversation.session.header.utilities', …)` → `slots.register({name:'conversation.session.header.utilities', key:'dsh-ling', order:…, locale:'dsh-ling'}, Component)`;组件在会话头右侧渲染。
|
|
245
|
+
- **交互(2026-09-07 用户修订:不用右键,防误触浏览器原生菜单)**:
|
|
246
|
+
- 左键单击:POST `/api/dsh-ling/mode/toggle`;running → 提示"运行中,空闲后生效"并排队(冻结门);响应 `{mode, appliedNow|queued}`;
|
|
247
|
+
- **鼠标悬停 1.5 秒**:打开 dsh-ling 菜单(切换模式/刷新记忆/导出记忆/状态与 L0·L1 现状);移出即取消定时;
|
|
248
|
+
- 键盘:Enter=切换,Shift+Enter=菜单;按钮上 contextmenu 一律 preventDefault(原生菜单永不弹出);
|
|
249
|
+
- 形态:小图标按钮「☾ 生活 / ⚙ 工作」+ tooltip(含运行中冻结提示)。
|
|
250
|
+
- **client 模块契约(实证教训):必须导出 `inject:['slots','locale','connection']`**(服务名声明缺失 → ctx.slots 为空 → 按钮静默不挂载)。
|
|
251
|
+
|
|
252
|
+
### 5.2 设置卡(settings-card.tsx)
|
|
253
|
+
|
|
254
|
+
- 座位:官方 `settings.plugin.item`(key `dsh-ling`;验证点 V1,见 §11;若该卡位不可用则回退 ego 式自建卡)。
|
|
255
|
+
- 冻结门可见性:卡内"L0/L1 现状"面板始终显示目标会话 running/idle 状态(来自宿主 agent/status 维护表),running 会话的编辑按钮标"空闲后生效"。
|
|
256
|
+
- 内容区(Tab):
|
|
257
|
+
1. **人格**:上表 persona 字段表单(逐字段;称呼/自称旁附"从历史语料建议"占位按钮(点击调 `/api/dsh-ling/persona/suggest`——M2 接离线脚本,先行返回 501));提交后 POST `/api/dsh-ling/persona`(宿主写 settings 并按冻结规则应用)。
|
|
258
|
+
2. **模式与推理等级**:mode.mapping 两行(仅 effort 档位选择);模型由用户自己在平台 UI 里选、插件不碰;mode.lastMode 只读显示;说明"新会话默认跟随最后使用的模式(D2)"。
|
|
259
|
+
3. **记忆**:l1BudgetTokens/l1MaxItems/weights(work/life 两行 × 三领域滑块 0..1)/trackWorkspaces;l0Always、l1Enabled 开关。
|
|
260
|
+
4. **L0/L1 现状**(D7):只读面板,GET `/api/dsh-ling/state?sessionId=…`:当前 L0 文本(渲染后)、本会话模式、L1 当前列表(每条:标题/日期/来源/得分)、pending 更新队列状态;运行中的会话标注"运行中(快照冻结)"。
|
|
261
|
+
5. **数据**:导出按钮(GET `/api/dsh-ling/export` → 下载 dsh-ling-memory-<date>.dshling.json,格式 §9);合并输入(文件选择 → POST `/api/dsh-ling/import`,冲突规则:同 conv_id 且同 source 跳过/覆盖由 manifest 决定,外来条目标 origin,persona 永不覆盖)。
|
|
262
|
+
- 保存语义:所有写操作返回 `{saved, appliedSessions: idle[], pendingSessions: running[]}` 供 UI 提示。
|
|
263
|
+
|
|
264
|
+
---
|
|
265
|
+
|
|
266
|
+
## 6. 记忆管线与 L1 选择 v0
|
|
267
|
+
|
|
268
|
+
### 6.1 数据源与入库
|
|
269
|
+
|
|
270
|
+
| 源 | 时机 | 处理 |
|
|
271
|
+
| --- | --- | --- |
|
|
272
|
+
| 网页端历史导出(1523 会话) | v0 一次性导入 | 复用 ds-search:离线脚本导出 `conversations_overview`(标题/日期/领域标签/概述/关键词)为 JSON → 宿主 `import` 端点写入(源='dsweb') |
|
|
273
|
+
| DSH 会话增量 | 持续 | `session/event` 顶层会话 → 追加 user/assistant 文本到 `dsh_turns_raw`;turn/end 后置 `needs_summary`;M1 仅存原文+计数,概述器(LLM)M2 接通;dispose 归档 |
|
|
274
|
+
| 领域/词条热度 | 实时更新 | 见 §8 热度公式;会话命中/检索命中回写(检索工具 M2) |
|
|
275
|
+
|
|
276
|
+
### 6.2 L1 选择算法 v0(l1.ts)
|
|
277
|
+
|
|
278
|
+
```
|
|
279
|
+
输入: 会话 mode(m), 首条用户消息关键词 K(若有), 预算 B(默认≤1200 token), 上限 N(8)
|
|
280
|
+
候选: conv_overview 中 is_overview_ok 且 source 范围内
|
|
281
|
+
打分: score(c) = w_domain(m,c) × heat(c)
|
|
282
|
+
heat(c) = α·recency(c) + β·freq(c) + γ·importance(c) (α,β,γ 默认 0.5/0.3/0.2,可配)
|
|
283
|
+
recency: 指数衰减(半衰期 90 天)
|
|
284
|
+
freq: 近 60 天命中/提问次数归一化
|
|
285
|
+
importance: 用户 pin(importance=1)或含"决策/偏好/纠正"标记
|
|
286
|
+
w_domain: c 的领域标签在 mode 权重向量(memory.weights)下的合计(多标签取 max 或加权和)
|
|
287
|
+
选取: 按 score 取 Top-N(同分取新);若 K 非空,先取 K 与 c.keywords 重合 ≥1 的候选并按 score 排序,
|
|
288
|
+
再补足至 N;与 L1 本会话已有文本按 conv_id 去重
|
|
289
|
+
输出行格式: 「[记忆·{领域}·{日期} {标题}] 概述 2~3 句 (来源: 会话/网页导出 id)」
|
|
290
|
+
裁减: 组装后超 B 则按 score 截断,截断行尾注"(已省略 N 条)"
|
|
291
|
+
```
|
|
292
|
+
不变量:同一(会话, mode, memoryVersion)输出确定(供 KV 与可复现)。
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
### 6.3 记忆树(2026-09-22 落地,D9-a/D9-b)
|
|
297
|
+
|
|
298
|
+
**问题**:平铺的记忆库没有"离我多近"的概念 —— L1 是全库裸排序,不区分"这轮做的工作"与"三个月前的闲聊";分叉会话更是**记忆真空**。
|
|
299
|
+
|
|
300
|
+
**结构**:`branch` 表组织**工作线**,而不是给记忆分类。
|
|
301
|
+
|
|
302
|
+
| 层级 | 含义 | 存储 |
|
|
303
|
+
|---|---|---|
|
|
304
|
+
| **主干** | 主线工作流,恒一条 | `branch.id = 'trunk'` |
|
|
305
|
+
| **枝** | 一条并行工作线(**比会话大**,一条枝可含多个会话) | `branch` 一行,`kind='branch'` |
|
|
306
|
+
| **主脉** | 由多条枝**归并**出的抽象节点 | `branch` 一行,`kind='vein'` |
|
|
307
|
+
| **连边** | 枝之间的横向关联(如 力学 ↔ 材料科学) | `vein_link` 表 |
|
|
308
|
+
|
|
309
|
+
**归属解析**(两条链并存):DSH 会话走 `session_meta.branch_id`;历史网页端/导入条目走 `conv_branch` 覆盖层;优先级 `conv_branch` > `session_meta` > 主干。**记忆行不冗余存 branch**,靠 `会话 → 枝` 推导。
|
|
310
|
+
|
|
311
|
+
**血缘加权**:检索时按"当前枝 → 目标枝"的相对位置取档 —— **同枝 1.0 / 祖先 0.7 / 旁系 0.4**,`score = 基础分 × 血缘系数 × 矛盾系数`;**枝系数为预留** —— 可写入并显示(复盘期可调,夹在 0~2),当前**恒按 1 计、不参与检索打分**,返回体里明确带 `applied:false`,别以为排序会跟着变。
|
|
312
|
+
|
|
313
|
+
> ⚠️ **不用连乘**:血缘系数是**单值**(不沿路径累乘),避免 `0.7^n` 随深度指数失真(五层后 `0.4^5 = 0.010`,枝等于从记忆里消失)。深树的路径搜索留作后续。
|
|
314
|
+
|
|
315
|
+
**结构不变量(硬性)**:
|
|
316
|
+
|
|
317
|
+
> **任何树操作都不改写任何一条记忆的内容。** 分枝 / 并脉 / 连边只增加"关系",从不"融合内容"。
|
|
318
|
+
|
|
319
|
+
> ⚠️ **例外只有一个**:主脉提炼被**再次采纳**时,会**覆盖该主脉记忆的上一版**(这一条记忆的内容确实被改写 —— 主脉记忆是一种概括,随主脉本身一起成长)。覆盖**之前**先**自动归档**旧版(kind `vein-overwrite`),**归档失败就拒绝覆盖**;注入面也会说明"旧版已归档"。除这一个例外,其余树操作(分枝 / 并脉 / 连边 / 删枝)都只动"关系"与归属,不改写任何一条记忆的内容。
|
|
320
|
+
|
|
321
|
+
这条不变量是记忆树区别于"合并式分支"的根本,也是跨源污染不可能在树里重演的原因。
|
|
322
|
+
|
|
323
|
+
**矛盾标记层**:一对记忆被判定重复/矛盾时,只**标记**不改写 —— 未复盘时检索**以较新的那条为准**(旧的一条降权 `×0.3`,不删除)。
|
|
324
|
+
|
|
325
|
+
**注入感知**:人工改树后,`branchLogDigest()` 把**器灵还没看过**的结构改动格式化成中文短句,由 `snapshot.js` 附在注入面末尾 —— 器灵下一次开口时就知道树变了。
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 7. 记忆存储 schema v1
|
|
330
|
+
|
|
331
|
+
> **现状说明(实现已升级)**:线上 schema 为 **v2** —— `conv_overview.source` 的 `CHECK` 已放开(支持 `dsweb` / `dsh` / `import` 三个来源,自动整表重建且数据无损),并新增 `session_meta.raw_seq`、`persona_suggestions` 等;下表为 v1 原始设计,保留作对照。
|
|
332
|
+
|
|
333
|
+
```sql
|
|
334
|
+
-- ~/.dsh/cache/dsh-ling/memory.db (node:sqlite)
|
|
335
|
+
PRAGMA journal_mode=WAL;
|
|
336
|
+
|
|
337
|
+
CREATE TABLE conv_overview (
|
|
338
|
+
conv_id TEXT NOT NULL, -- dsweb=导出会话 uuid / dsh=session id
|
|
339
|
+
source TEXT NOT NULL CHECK(source IN ('dsweb','dsh')),
|
|
340
|
+
title TEXT NOT NULL,
|
|
341
|
+
title_locked INTEGER NOT NULL DEFAULT 0, -- v3(D2):1=名字已定(主人手改或 AI 点名),概述器一律不覆盖
|
|
342
|
+
title_by TEXT NOT NULL DEFAULT '', -- v4(D2):定名者 'user'(主人手改)/ 'ai'(机器起名);界面统一显示「已定名」,归属只进悬浮说明
|
|
343
|
+
started_at TEXT, updated_at TEXT,
|
|
344
|
+
domain_tags TEXT NOT NULL DEFAULT '[]', -- JSON 数组,多标签
|
|
345
|
+
keywords TEXT NOT NULL DEFAULT '[]', -- JSON 数组
|
|
346
|
+
summary TEXT NOT NULL DEFAULT '',
|
|
347
|
+
heat REAL NOT NULL DEFAULT 0,
|
|
348
|
+
importance INTEGER NOT NULL DEFAULT 0, -- 1=用户 pin
|
|
349
|
+
last_hit_at TEXT, hit_count INTEGER NOT NULL DEFAULT 0,
|
|
350
|
+
overview_ok INTEGER NOT NULL DEFAULT 0, -- 概述质量可用(供 L1 候选)
|
|
351
|
+
PRIMARY KEY (source, conv_id)
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
CREATE TABLE dsh_turns_raw ( -- DSH 会话原文增量(旁路记录)
|
|
355
|
+
session_id TEXT NOT NULL, seq INTEGER NOT NULL,
|
|
356
|
+
role TEXT NOT NULL, ts TEXT, model TEXT,
|
|
357
|
+
text TEXT NOT NULL,
|
|
358
|
+
PRIMARY KEY (session_id, seq)
|
|
359
|
+
);
|
|
360
|
+
CREATE TABLE session_meta ( -- 会话运行态/模式/快照版本(进程内存为主,落盘作恢复)
|
|
361
|
+
session_id TEXT PRIMARY KEY,
|
|
362
|
+
mode TEXT, mode_updated_at TEXT,
|
|
363
|
+
snapshot_persona_version INTEGER DEFAULT 0,
|
|
364
|
+
snapshot_memory_version INTEGER DEFAULT 0,
|
|
365
|
+
pending INTEGER DEFAULT 0, -- 冻结门排队标记
|
|
366
|
+
archived INTEGER DEFAULT 0
|
|
367
|
+
);
|
|
368
|
+
CREATE TABLE kv (key TEXT PRIMARY KEY, value TEXT); -- meta/版本
|
|
369
|
+
CREATE TABLE feedback_queue ( -- M2:修订建议(用户确认才进 persona)
|
|
370
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
371
|
+
created_at TEXT, session_id TEXT, message_id TEXT,
|
|
372
|
+
rating TEXT, note TEXT, status TEXT DEFAULT 'new'
|
|
373
|
+
);
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
## 8. 导出/合并格式 v1
|
|
377
|
+
|
|
378
|
+
单文件 `dsh-ling-memory-<yyyy-MM-dd>.dshling.json`(UTF-8):
|
|
379
|
+
|
|
380
|
+
```jsonc
|
|
381
|
+
{
|
|
382
|
+
"format": "dsh-ling-memory",
|
|
383
|
+
"schemaVersion": 1,
|
|
384
|
+
"exportedAt": "2026-09-07T…+08:00",
|
|
385
|
+
"sourceFingerprint": "sha256(…)", // 导出时的数据指纹,供合并判重
|
|
386
|
+
"origin": "local-default", // 命名空间标记
|
|
387
|
+
"persona": { /* persona 字段副本 */ }, // 默认只读合并(不覆盖目标 persona)
|
|
388
|
+
"overviews": [ /* conv_overview 行 */ ],
|
|
389
|
+
"settings": { /* dsh-ling ns 用户可配项 */ },
|
|
390
|
+
"includeRawTurns": false // 默认不含 dsh_turns_raw;显式勾选才含
|
|
391
|
+
}
|
|
392
|
+
```
|
|
393
|
+
合并规则:同 (source, conv_id) 存在 → 跳过(或按 manifest 内 requestedAction: skip|overwrite);外来 overview 全部带 origin 标记;persona 只有在显式勾选"采纳其人格"时写入(置 status='imported-pending' 待用户确认)。
|
|
394
|
+
|
|
395
|
+
## 9. HTTP API 表(/api/dsh-ling/*)
|
|
396
|
+
|
|
397
|
+
| 方法/路径 | 用途 | 冻结门 |
|
|
398
|
+
| --- | --- | --- |
|
|
399
|
+
| POST `/mode/toggle` body{sessionId} | 切换模式(记录 + 只同步推理档位、不改模型 + 重定稿) | 运行中→排队返回 queued |
|
|
400
|
+
| GET `/state?sessionId=` | L0 文本/模式/L1 列表/队列状态(设置页与右键菜单用) | 只读 |
|
|
401
|
+
| GET `/state?sessionId=&l0Preview=1` | 人格字段实时预览(编辑中) | 只读 |
|
|
402
|
+
| POST `/persona` body=字段 | 写 persona(→settings ns;**字段白名单**,白名单外的键丢弃并回报 `ignored`)并触发应用 | 见冻结门 |
|
|
403
|
+
| GET `/persona/suggest` | 人格档案草稿(M2;先行 501) | — |
|
|
404
|
+
| POST `/memory/refresh` body{sessionId?} | 显式重定稿 | 运行中→排队 |
|
|
405
|
+
| GET `/memories/sources` | 各来源条数(D8;记忆中心筛选栏显示「历史网页端 (1523)」) | 只读 |
|
|
406
|
+
| POST `/memories/rename` body{source,conv_id,title} | 主人手改标题并**上锁**(`title_locked=1`,`title_by='user'`):概述器重建与批量重命名此后不再覆盖;锁住的是自动重写,**不锁主人** | 写库旁路,不碰注入 |
|
|
407
|
+
| POST `/memories/retitle` body{source,conv_id,engine?,heuristic?} | 给一条记忆起名(D2;**机器起的名字同样上锁** = 名字已定,概述器不得覆盖;已锁行仍可再次起名;`heuristic:true` 走纯启发式)。`engine`:`auto`(默认,小助手优先、不可用则全局大模型)/ `assistant` / `global`,实际用的引擎与原因写回 `engine`/`note` | 写库旁路,不碰注入 |
|
|
408
|
+
| GET `/assistant/config` | 小助手**生效地址与来源**(`settings` / `env` / `default`)+ 各级候选值 + 内置默认(S7;只读配置,**不做网络探测**,秒回) | 只读 |
|
|
409
|
+
| POST `/assistant/config` body{baseUrl?,model?,clear?} | 写小助手地址/模型(S7;只接受这两个键,不开放任意设置写入;`baseUrl` 必须 `http(s)://` 开头,留空 = 回落下一级;`clear:true` 清空回默认) | 写 settings,不碰注入 |
|
|
410
|
+
| POST `/assistant/test` body{baseUrl?,model?} | 探活(不改配置):返回 `ok` / `models` / 失败原因与排查提示;UI 的「测试连通」用它 | 只读(外呼一次) |
|
|
411
|
+
| GET `/export` | 下载记忆包(§8) | 只读 |
|
|
412
|
+
| POST `/import` (multipart) | 合并记忆包 | 只读(写库旁路,不碰注入) |
|
|
413
|
+
| GET `/health` | 存活与版本 | — |
|
|
414
|
+
|
|
415
|
+
守卫(2026-09-17 加固 ③④):**来源栅栏** —— Host 必须是 loopback(`127.0.0.0/8` / `::1` / `localhost`),或 `settings.guard.trustedHosts` 里**显式声明**的 authority(**本机 LAN 地址不再自动信任**;要整段 LAN 需 `guard.allowLan: true`);`Sec-Fetch-Site: cross-site` 直接拒;带 `Origin` 时其 host 须等于请求 Host(**带了但解析不出即拒;不带 `Origin` 时这一条跳过** —— 它不是"每个请求都必须带 Origin 且同源")。**身份对撞** —— 期望 cookie 名 = `dsh-auth-` + base64url(sha256(规范化 Host)),与 DSH 同法(不校验值的签名,故本机原生进程仍可调用)。**字段白名单** —— `/persona` 与 `/import` 只接受 `persona` / `styles` 的白名单字段,其余丢弃并在响应里回报 `ignored`。拒绝一律 403 + reason。
|
|
416
|
+
|
|
417
|
+
## 10. 里程碑与验证清单
|
|
418
|
+
|
|
419
|
+
- **M1a 骨架 + 本地构建(下一阶段,不触碰运行 profile)**
|
|
420
|
+
- [ ] package.json(dsh.bundle.patch / exports ./client / platform web)+ cordis.patch.yml
|
|
421
|
+
- [ ] host 各模块空实现 + settings ns 注册 + sqlite 建库(临时 DB 自检)
|
|
422
|
+
- [ ] client 按钮渲染在官方 slot(本地浏览器无法目检 → 静态验证 + 代码评审)
|
|
423
|
+
- [ ] 构建产物可被 __ModuleLoader__ 契约加载(结构自检)
|
|
424
|
+
- **M1b 安装验证(需用户配合:备份→加依赖→插行→热载→已登录浏览器目检)**
|
|
425
|
+
- [ ] 按钮出现在会话页顶栏;左右键行为;运行中点击提示"空闲后生效"
|
|
426
|
+
- [ ] 新会话 L0 注入(空称呼 / 已填写两种);KV 无异常重复
|
|
427
|
+
- [ ] 模式切换:**推理档位**实际变化(会话内档位选择处可见);**模型保持不变**;新会话默认跟随 lastMode
|
|
428
|
+
- [ ] 运行中(长任务)切模式/改人格:确认 running 区间快照与档位不变、空闲后生效
|
|
429
|
+
- [ ] 设置卡读写 settings.yaml;导出文件可导入回(合并规则生效)
|
|
430
|
+
- **回滚**:移除 patch 行 + 撤依赖 → host 侧 HMR 卸载;client 硬刷后消失;数据目录保留不影响回滚。
|
|
431
|
+
|
|
432
|
+
## 11. 实现期待验证点(进入编码前/编码初用最小探针确认)
|
|
433
|
+
|
|
434
|
+
| V | 问题 | 验证方式 | 若不符的降级 |
|
|
435
|
+
| --- | --- | --- | --- |
|
|
436
|
+
| V1 | `settings.plugin.item` 卡位在官方设置页的注册键与 props | 参考 ego(已实证可用)直接复用同款注册 | 沿用 ego 同款即最低风险 |
|
|
437
|
+
| V2 | host 运行 Node 版本下 `node:sqlite` 可用性 | 编码前 `node -e` 探针 | 纯 JSONL + 内存索引(量小可行) |
|
|
438
|
+
| V3 | 顶层/子代理会话判别字段(parentSession/delegationDepth)在 session header 的实际取值 | 读 ~/.dsh/storages session_projcache 样例(已见字段) | 多条件组合判定 |
|
|
439
|
+
| V4 | `agent/session-start` 时能否读到首条 user/message(session 种子) | 最小 host 探针插件日志 | L1 冷启动=纯 lastMode Top-K |
|
|
440
|
+
| V5 | 消息式注入降级路径(特殊 preset)在 M1 是否实现 | M1 先只实现 section 通道 + 检测告警 | 检测到 complete persona 时设置页提示,注入降级放 M2 |
|
|
441
|
+
| V6 | client 类型来源 | 从已安装 `@deepseek-ai/dsh-client-ui-*/lib/types` 拷贝所需 contract(conversation slots/settings)进 `src/client/types` | 手动 ambient .d.ts |
|
|
442
|
+
|
|
443
|
+
## 12. 与评审阶段呼应的要点(自检)
|
|
444
|
+
|
|
445
|
+
- 三层记忆、不硬过滤、遗忘曲线(αβγ 可调)、多标签领域、溯源标注、pin/删、纯本地、导出默认不含原文、外来合并不覆盖 persona、反馈要人审(D4 之外均沿用 REVIEW 定稿)。
|
|
446
|
+
- D4(本回合新增)已贯穿:§4.3/4.4/4.5 冻结门、§5.1 运行中提示、§9 接口排队语义、§10 M1b 验证项。
|
|
447
|
+
|
|
448
|
+
---
|
|
449
|
+
## 附录 A:M1a 落地偏差(2026-09-07 骨架)
|
|
450
|
+
|
|
451
|
+
1. **语言/构建**:无网络环境,全部以**纯 JS(ESM host + 单文件 client)编写,无 TS/构建步骤**;文件即产物,位于 `lib/`(host 各模块 `lib/host/*.js`,入口 `lib/index.js`;client `lib/client.js`)。类型化契约(slot/组装上下文)在 M1b 安装后用真实运行核对(V 系列验证点),类型资产后续按需从 `@deepseek-ai/*/lib/types` 拷入。
|
|
452
|
+
2. **设置持久化**:M1a/M1b 用自有 JSON(`~/.dsh/cache/dsh-ling/settings.json`),**官方 settings-ns 集成留后续**(ctx.inject(['settings']) register 模式已取证,见 session-archive)。HTTP 网关 `/api/dsh-ling/*` 是唯一读写面。
|
|
453
|
+
3. **注入段 text 签名**:按 REPORT-02 "组装上下文 {agent,scope,signal}" 实现 `text(assembly)`(agent→sessionId→快照),已在真机验证注册成功(kv: inject.section_registered=1);payload 字段名按真实事件自适应。
|
|
454
|
+
4. **client 状态轮询**:按钮运行态/当前会话用 3s 轮询 `/state`(宿主侧最近活跃启发式),后续换 connection/事件驱动。
|
|
455
|
+
5. **交互修订(用户 2026-09-07)**:不用右键打开菜单(防误触浏览器菜单)→ **左键=切模式;悬停 1.5 秒=菜单**(contextmenu 一律 preventDefault);client 模块必须导出 `inject:['slots','locale','connection']`(缺失则 ctx.slots 为空、按钮静默不挂载——M1b 实证教训,已修复)。
|
|
456
|
+
6. **mode 映射**:早期默认值为 provider=`deepseek`、model=`deepseek-v4-flash`、effort work=`max`/life=`low`(设置可改);**2026-09-12 起只保留 effort** —— 模式切换不再改模型,provider/model 取当前值原样带回(详见 §0 D2 与 §4 mode 行)。
|
|
457
|
+
7. **webServer 启动竞态**:registerApi 采用 svc→ctx.inject 子作用域→3s 定时重试三级获取,路由挂载成功才落 kv(api.registered=1,真机已验证)。
|
|
458
|
+
|
|
459
|
+
## 附录 B:历史会话接入蓝图(2026-09-08 方向已拍板)
|
|
460
|
+
|
|
461
|
+
→ **详见 `ACCESS-DESIGN.md`**(会话契约 v1、DSH 一键/文件导入双通道、轻量档支持、分页幂等、记忆中心「接入历史」页签与排期)。要点:绝不要求用户理解库结构;先对齐 DeepSeek 体系(DSH 存量 + 网页端文件/库);纯文本粘贴通道暂缓(理由见蓝图 §2)。
|