dsh-instruction-memory 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +168 -0
- package/README.md +208 -14
- package/lib/client.js +1267 -10
- package/lib/index.js +1645 -90
- package/memory.schema.json +173 -0
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,174 @@
|
|
|
7
7
|
> 1.0.13 起由 CI 经 OIDC Trusted Publishing 发布(首个带 provenance 的版本)。
|
|
8
8
|
> 这些历史 tag **不应回填**——npm 上的版本号已被占用。
|
|
9
9
|
|
|
10
|
+
## [1.3.0] — 2026-10-15
|
|
11
|
+
|
|
12
|
+
两个审查修复与两项体验增强(F12 / F7)。
|
|
13
|
+
|
|
14
|
+
### 修复
|
|
15
|
+
|
|
16
|
+
- **切换配置集时关闭打开中的编辑草稿**:切换成功后同时关闭指令编辑器与用户形象编辑器
|
|
17
|
+
(并退出批量选择模式)。此前留在屏幕上的表单仍显示旧集合的内容,保存时会落进新集合
|
|
18
|
+
(服务端跨集合保护保证不覆盖、不丢数据,但表单本身就是误导)。若切换时确有草稿被关闭,
|
|
19
|
+
会在提示里说明。ui-test 新增回归:打开形象编辑器 → 切换集合 → 断言编辑器消失且有披露;
|
|
20
|
+
并钉住「条目编辑器为整页接管,打开时切换按钮不可达」这一结构事实。
|
|
21
|
+
- **导入回退到激活集合时按目标集合重新去重**:导入条目声明的集合不存在(或已达 20 个上限
|
|
22
|
+
无法创建)时会归入当前激活集合,但去重仍按**声明的集合**计算,可能把与激活集合已有条目
|
|
23
|
+
title+content 完全相同的内容再插一份。现在在提交阶段先确定每项的真实归属,再对回退项
|
|
24
|
+
按目标集合补一次查重(含同批其他回退项与正常落集项之间的互查):重复的不再插入并单独
|
|
25
|
+
计数披露(「另有 N 项因回退到当前配置集后与已有内容重复被跳过」);全部被跳过时保持
|
|
26
|
+
no-op 不写盘。回退目标中 id 已被占用而正文是新内容时改发新 id,不再把重复 id 写进同一
|
|
27
|
+
集合。正常路径(集合存在/成功创建)的幂等性不变——重新导入同一导出文件仍是 no-op。
|
|
28
|
+
|
|
29
|
+
### 新增
|
|
30
|
+
|
|
31
|
+
- **诊断与安全状态集中面板(F12)**:设置页新增可折叠「诊断信息」卡片(默认收起、纯展示、
|
|
32
|
+
无写路由),集中展示存储(路径/在盘/载入来源/最近保存/rev)、注入(注册状态、`pulls`
|
|
33
|
+
自启动计数、字符数/上限)、备份与历史(位置、`.bak`、历史目录与快照数、Schema 文件)与
|
|
34
|
+
全部错误/警告;「复制诊断信息」生成含 `navigator.userAgent` 的纯文本(**不含任何用户文本**,
|
|
35
|
+
剪贴板不可用时降级为手动全选文本框;序列化为导出纯函数 `__diagnosticsText` 并被
|
|
36
|
+
`verify.mjs` 锁定)。快照新增 `security`(`hostGuarded`/`boundHost`,与路由 Host 校验同源)与
|
|
37
|
+
`diagnostics`(`loadedFrom`/`rev`):绑定在非回环地址时页面顶部显示醒目橙色安全警告,
|
|
38
|
+
补上对外发布场景缺失的信号。`docs/demo.html` 桩同步补齐。
|
|
39
|
+
- **临时静音(F7)**:总开关旁「暂停注入…」——30 分钟 / 1 小时 / 到今天结束(本地时区
|
|
40
|
+
24:00),暂停期间指令与形象都不注入(系统提示词与未安装插件逐字节一致),到期由宿主侧
|
|
41
|
+
定时器自动恢复,**无需操作、不写盘(rev 不变)**;状态存于新增的**可选**顶层字段
|
|
42
|
+
`disabledUntil`(epoch 毫秒;0/缺失 = 未暂停;格式版本仍为 2,无迁移;跨重启保留)。
|
|
43
|
+
`set-options` 接受该字段:0 = 立即恢复,过去时间视为 0,超过 7 天上限钳到 now+7d 并披露;
|
|
44
|
+
手动切换总开关清零它(显式开关优先于定时)。注入渲染引入**可注入时钟**:`isMuted(data, now)`、
|
|
45
|
+
`planInjection(data, now = Date.now())`、`buildBlock(data, now = Date.now())`(默认参数仍是
|
|
46
|
+
当前时间,纯函数允许固定 now,字节结果可复现);到期决策 `muteExpired(data, now)` 由
|
|
47
|
+
`syncSection` 末尾的单一定时器消费(每次状态变化先清旧定时器,插件卸载时清理,`unref`
|
|
48
|
+
不阻退出)。客户端提供暂停菜单、剩余时间倒计时(1s interval,随暂停状态自动建立/清理)
|
|
49
|
+
与「立即恢复」;`memory.schema.json`、README、demo 桩同步更新。
|
|
50
|
+
|
|
51
|
+
### 测试
|
|
52
|
+
|
|
53
|
+
- contract-test:导入回退去重六组回归(激活集合重复 / 20 集上限 / 批内互查 / 形象重复 /
|
|
54
|
+
id 重发 / 跨集同文不误伤);先红后绿验证(回退修复后新用例失败)。
|
|
55
|
+
- ui-test:切换集合关闭草稿回归;同以 stash 验证测试有效性。
|
|
56
|
+
- smoke-test:isMuted 真值表与 muteExpired 到期决策;planInjection/buildBlock 注入时钟在
|
|
57
|
+
暂停前/后的字节一致性(暂停中 ≡ enabled:false;到期后 ≡ 未暂停);形象随暂停一起置空;
|
|
58
|
+
时钟参数可省(arity = 1)。
|
|
59
|
+
- contract-test(F7/F12):set-options disabledUntil 写入/披露/过去归零/7 天钳制/随总开关
|
|
60
|
+
清零/非法拒绝;暂停中快照 injection.muted=true、registered=false、preview 为空;存储文件/
|
|
61
|
+
导出/restore 往返/源码模式携带该字段;手改非法值载入时重置并披露;F12 security/diagnostics
|
|
62
|
+
字段存在且 hostGuarded 随绑定地址变化(回环 vs 0.0.0.0)。
|
|
63
|
+
- verify.mjs:__diagnosticsText 输出含关键字段、含环境信息、不含条目/形象/预演文本;
|
|
64
|
+
schema 记录 disabledUntil(integer ≥ 0);F12/F7 双语键组检查。
|
|
65
|
+
- ui-test(F12/F7):诊断卡片展开/收起与复制降级路径(无剪贴板 → 可手动全选的转储,断言
|
|
66
|
+
不含用户文本);非回环绑定警告出现/消失(第二宿主实例真实切换绑定);暂停菜单三档 →
|
|
67
|
+
状态条倒计时 → 立即恢复;50ms 真实定时器到期自动恢复(文件字节不变的到期语义)。
|
|
68
|
+
ui-test 垫片补上 React 的 effect cleanup 语义(重跑前先清理),倒计时 interval 不再泄漏到
|
|
69
|
+
后续断言。
|
|
70
|
+
|
|
71
|
+
## [1.2.0] — 2026-10-15
|
|
72
|
+
|
|
73
|
+
P2 进阶增强:多情境配置集、版本历史与回滚、手改友好化(JSON Schema)、源码模式、
|
|
74
|
+
按需指令的本地预演、自定义备份位置。
|
|
75
|
+
|
|
76
|
+
### 新增
|
|
77
|
+
|
|
78
|
+
- **多情境配置集(F13)**:一份存储文件内可维护多个命名集合(「工作」「个人」「某客户」…),
|
|
79
|
+
设置页顶部列出全部集合并可**一键切换**,注入**只渲染当前激活的那一套**。
|
|
80
|
+
(字段名用 `sets` 而非需求里的 "Profiles":`profiles` 已被 1.1.0 的「用户形象」占用,
|
|
81
|
+
两者含义完全不同。)
|
|
82
|
+
- 存储格式升到**版本 2**:新增 `activeSetId`、`sets`(每项仅 `id` + 名称),条目与用户形象
|
|
83
|
+
通过 `setId` 声明归属。切换只是改一个字段,**不移动任何用户文本**,因此不存在"切到一半"
|
|
84
|
+
的中间态;新建与复制**不会**自动激活。
|
|
85
|
+
- 版本 1 文件**平滑迁移**:条目与形象全部归入合成的「默认」集合,载入本身不写盘,
|
|
86
|
+
下次保存才写为版本 2,并明确提示。反过来,旧版插件打开版本 2 文件会**拒绝载入并暂停修改**
|
|
87
|
+
(原文件保留、数据完好),这是刻意设计——旧版看不到 `sets`,按 v1 读进去再保存就会抹掉配置集。
|
|
88
|
+
- **红线:绝不按进程 cwd 隐式建集或切换。** `activeSetId` 只由用户显式命令写入;这条纪律
|
|
89
|
+
来自早期"cwd 漂移导致记忆像消失"的真实事故(见 README)。
|
|
90
|
+
- 新路由:`save-set`(新建/重命名)、`delete-set`(拒绝删除激活集合;删除其他集合会连同其中
|
|
91
|
+
条目与形象一并删除并报数)、`set-active-set`(切换,写盘并推进 rev)、`duplicate-set`
|
|
92
|
+
(以全新 id 复制集合内容,用于模板派生)。上限:20 个集合;指令/形象仍按**全集合总量**计
|
|
93
|
+
(200 / 50)。
|
|
94
|
+
- 隔离是硬性的:注入计划、设置页快照、条目/形象的单条与批量增删改**全部**限定在激活集合。
|
|
95
|
+
特别地,提交一个属于**其他集合**的 id 时不再就地覆盖,而是改用新 id 在当前集合新建
|
|
96
|
+
(否则一次"保存"就能把别的集合的条目搬过来并改写内容)。
|
|
97
|
+
- 导出为**整份存储**(所有集合);导入时若文件带 `sets` 会按需创建对应集合并把条目放回原集合,
|
|
98
|
+
去重按**集合内**计算(同一句话可以同时存在于两个集合,且重复导入仍是幂等 no-op)。
|
|
99
|
+
|
|
100
|
+
- **版本历史与回滚(F14)**:每次成功保存后把该版本原样写入 `<备份位置>/history/` 下的
|
|
101
|
+
时间戳快照(文件名 `20261015T152355123Z-r12-laptop.json`:UTC 时间 + rev + 写入设备,
|
|
102
|
+
固定宽度所以按名排序即按时间排序)。
|
|
103
|
+
- 每台设备各保留最近 20 份,逐出最旧;**不改名、不碰**任何不符合命名的文件。放在
|
|
104
|
+
`history/` 子目录是为了避开监听 `memory.json` 的 `fs.watch`(子目录不在非递归监听范围内,
|
|
105
|
+
文件名过滤再兜底一层)。快照写失败不会让保存失败,只在面板说明。
|
|
106
|
+
- 设置页「版本历史」可展开(时间、rev、条目/形象数、体积、是否来自其他设备),一键回滚或删除。
|
|
107
|
+
回滚把快照内容作为**新的一版**写回(rev 只增不减),并且回滚自身也留快照——可以再回滚回来。
|
|
108
|
+
- 新增**「从备份恢复…」**入口:导入只能合并、无法还原整份,这个入口按确认后**整体替换**当前
|
|
109
|
+
记忆,替换前的内容仍在 `memory.json.bak` 与版本历史里。
|
|
110
|
+
- 新路由:`history-list`、`history-restore`、`history-delete`、`restore-data`。
|
|
111
|
+
|
|
112
|
+
- **手改友好化(F15)**:随包发布 `memory.schema.json`(draft-07),描述 v1/v2 两种形状与全部
|
|
113
|
+
长度上限;写出的文件带 `"$schema": "./memory.schema.json"`,宿主同时把该 Schema 的副本放到
|
|
114
|
+
`memory.json` 旁边,因此任何支持 JSON Schema 的编辑器打开存储文件即可获得校验与补全,
|
|
115
|
+
无需知道包的位置。`verify.mjs` 逐项断言 Schema 与代码中的上限一致,防止漂移。
|
|
116
|
+
另外**首次需要落盘前会先重读一次磁盘**,若文件在此期间出现(手放/别的工具恢复)就采用它,
|
|
117
|
+
不再用空存储覆盖。
|
|
118
|
+
- **CLI 管理命令不在承诺范围内**(依赖宿主是否开放插件命令能力,未经验证)——`tui`/`headless`
|
|
119
|
+
仍只能手改 JSON,这是当前官方路径。
|
|
120
|
+
|
|
121
|
+
- **设置页源码模式(F16)**:在文本框里直接编辑整份存储 JSON。输入时实时做语法校验(防抖 250ms)
|
|
122
|
+
并摘要条数;保存走与手改文件**完全相同**的路径(归一化 + 披露 + `rev` 冲突保护),披露包括
|
|
123
|
+
未能载入的条目、被修正的选项值、被截断的字段、指向不存在集合的 `setId`、未来格式版本拒绝。
|
|
124
|
+
一次拒绝**不会**丢失草稿;提交携带的是**打开源码时**读到的 rev,因此编辑期间的他人修改会被
|
|
125
|
+
如实判为冲突。与卡片编辑互斥(有草稿在编时按钮禁用)。新路由:`read-source`、`write-source`。
|
|
126
|
+
|
|
127
|
+
- **按需指令的本地预演(F17)**:设置页输入一句任务描述,前端**纯字面关键词**匹配各条「按需」
|
|
128
|
+
指令的适用场景/标题/内容(场景权重最高),列出重合度较高的条目并给高/中/低分档,同时在列表
|
|
129
|
+
里打标。中文用 2/3 字滑窗切词(无分词依赖),术语上限 40,纯本地计算、不发请求。
|
|
130
|
+
界面明确标注**「本地预演 ≠ 模型的真实判断」**,`verify.mjs` 断言该免责声明在两种语言中都存在;
|
|
131
|
+
它不参与注入,也不代表模型是否采纳。
|
|
132
|
+
|
|
133
|
+
- **自定义备份位置(F18,不含网络同步)**:可把备份指向用户指定目录(如本地云盘)。
|
|
134
|
+
影响 `history/` 快照目录、「导出到备份目录」的去向,以及**额外**一份 `memory.json.bak`;
|
|
135
|
+
**存储文件本身与它的跨进程锁不会移动**(崩溃恢复用的 `.bak` 必须留在存储目录)。
|
|
136
|
+
设置时当场校验(绝对路径、目录存在、探针写入成功),不满足即拒绝并说明原因;它是**本机设置**,
|
|
137
|
+
整体替换与回滚**不会**改写它。README 给出多设备指引(只同步导出文件与历史快照,
|
|
138
|
+
**不要**同步存储目录本身)。新路由:`export-to-dir`。
|
|
139
|
+
|
|
140
|
+
### 变更
|
|
141
|
+
|
|
142
|
+
- 存储格式版本 **1 → 2**(见上);导出信封 `version` 同步为 `2`。
|
|
143
|
+
- 版本拒绝文案改为列出**受支持**的版本(`1 / 2`)而不是"只支持版本 1"。
|
|
144
|
+
- 设置页新增「配置集」「按需指令本地预演」「版本历史」「备份位置」四块区域;「从备份恢复…」
|
|
145
|
+
与「源码模式」两个入口;`zh`/`en` 文案同步。
|
|
146
|
+
- `.gitignore` 的临时 harness 规则放宽为 `.test-dsh-home*/`,覆盖各套件各自的家目录变体
|
|
147
|
+
(清理由重命名/沙箱阻止时不再留下未跟踪噪声)。
|
|
148
|
+
|
|
149
|
+
### 测试
|
|
150
|
+
|
|
151
|
+
- `smoke-test.mjs` 新增:作用域解析(无集合=旧形状、未知 `activeSetId` 回退首个、无归属条目
|
|
152
|
+
跟随激活集合)、**只渲染激活集合**的隔离断言、`sanitizeSet`、`setId` 保留规则、
|
|
153
|
+
快照命名与解析往返、设备标签清洗、`LIMITS` 冻结与请求体上限下界。
|
|
154
|
+
- `contract-test.mjs` 新增独立宿主块(约 60 条断言):v1 → v2 迁移与披露、切换/新建/重命名/
|
|
155
|
+
复制/删除的全部分支与拒绝路径、跨集合删除与批量的 no-op、跨集合 id 不得覆盖、
|
|
156
|
+
快照目录与命名、回滚推进 rev、20 份保留上限、源码模式的采纳与全部拒绝分支(语法/空对象/
|
|
157
|
+
非对象/未来版本/过期 rev)、整体替换(含"本机设置不被采用")、导入的集合创建与回退计数、
|
|
158
|
+
备份位置校验与两个 `.bak` 副本、Schema 副本落盘。原「未来版本」场景的种子升到 `version: 3`。
|
|
159
|
+
- `verify.mjs` 新增:Schema 与 `LIMITS` 的逐项一致性(含 v1 仍可校验)、请求体上限断言改用
|
|
160
|
+
宿主发布的真实常量、`__relevanceTerms`/`__relevancePreview`/`__sourceParseVerdict` 的行为断言、
|
|
161
|
+
F13–F18 的双语文案键集与免责声明断言,以及**每个 `t('…')` 调用点都能在两种语言里解析到文案**
|
|
162
|
+
(键集相等不足以保证这一点:漏声明的键会在界面上直接显示成键名)**且没有从未使用的死文案**。
|
|
163
|
+
- **新增 `ui-test.mjs`**:用约 60 行的 React 垫片(保持 hook 状态、按依赖运行 effect、展开函数
|
|
164
|
+
组件)真实渲染设置页并**点按钮、填输入框**,路由直连真实宿主。覆盖:v1 迁移后的界面、
|
|
165
|
+
新建集合→切换→**注入内容随之清空**、新条目落在激活集合、预演命中、历史列表与回滚、
|
|
166
|
+
源码模式的实时校验与保存、备份位置应用与导出。同时断言**每次渲染的 hook 顺序稳定**
|
|
167
|
+
(能抓出条件式 hook)。已并入 `npm test`。面板语言在测试内**固定为 zh**:Node ≥ 21 的全局
|
|
168
|
+
`navigator` 会把 CI 运行器的英文语言带进面板,与断言中的中文文案冲突(本机 zh 通过、
|
|
169
|
+
CI 英文失败——已在 CI 上实测到并修复)。
|
|
170
|
+
- `docs/demo.html` 演示宿主补齐 F13–F18 的路由与快照字段(演示数据现在是两个集合,
|
|
171
|
+
切换能立刻看到注入内容变化)。
|
|
172
|
+
|
|
173
|
+
### 兼容性
|
|
174
|
+
|
|
175
|
+
- 宿主支持范围**不变**(`>=0.1.3-alpha.2` 起的各段,见 `host-range-test.mjs`)。
|
|
176
|
+
- 存储文件:v1 可读可迁移;v2 只能由本版本及以后读写(降级会被拒绝并暂停修改,数据无损)。
|
|
177
|
+
|
|
10
178
|
## [1.1.0] — 2026-10-13
|
|
11
179
|
|
|
12
180
|
### 新增
|
package/README.md
CHANGED
|
@@ -38,12 +38,14 @@ dsh plugin --profile web remove dsh-instruction-memory
|
|
|
38
38
|
| `lib/client.js` | Client 半:在 `settings.section` 注册「指令记忆」设置页(手写模块,无需构建) |
|
|
39
39
|
| `cordis.patch.yml` | bundle patch:把 Host 行插入宿主组合 |
|
|
40
40
|
| `install.mjs` | 开发安装脚本:从本仓库检出安装时,把包链接进 web profile 的 `node_modules`(junction)并注册 `dsh.profile.bundles`;`--remove` 完整回滚,改 profile 前自动备份 |
|
|
41
|
-
| `contract-test.mjs` |
|
|
42
|
-
| `
|
|
43
|
-
| `
|
|
41
|
+
| `contract-test.mjs` | 契约测试:驱动真实路由,验证两半之间的响应格式(含配置集、版本历史、源码模式、备份位置) |
|
|
42
|
+
| `ui-test.mjs` | 设置页行为测试:用一个 ~60 行的 React 垫片真实渲染面板、点按钮/填输入框,再断言注入内容随之变化(不出图、不依赖浏览器) |
|
|
43
|
+
| `verify.mjs` | 装载验证:按 DSH 的方式加载两半,并逐项断言 `memory.schema.json` 与代码中的上限一致 |
|
|
44
|
+
| `smoke-test.mjs` | 注入渲染逻辑的单元测试(含「只渲染激活配置集」的隔离断言与 cwd 红线) |
|
|
44
45
|
| `host-range-test.mjs` | 宿主版本范围契约测试:固定矩阵 × 两种 semver 判定模式,并把 `peerDependencies`(**被执行的那个**)、`dsh.engines.dsh`、`dsh.compatibility.dsh` 三处声明锁到同一个生成值 |
|
|
45
46
|
| `lint.mjs` | 语法门禁:`node --check` 解析所有随包发布与本仓库运行的 JS 文件,已并入 `npm test` |
|
|
46
47
|
| `docs/demo.html` | 离线演示页:桩掉 DSH 外壳、加载真实 Client 半渲染设置页(不随 npm 包发布) |
|
|
48
|
+
| `memory.schema.json` | 手改存储文件用的 JSON Schema(随包发布,并被宿主复制到 `memory.json` 旁边) |
|
|
47
49
|
|
|
48
50
|
## 生效方式
|
|
49
51
|
|
|
@@ -78,12 +80,26 @@ dsh plugin --profile web remove dsh-instruction-memory
|
|
|
78
80
|
总开关关闭时,该提示词段落(指令与用户形象)**整个注销**,系统提示词与未安装本插件时逐字节一致;
|
|
79
81
|
总开关开启但没有任何生效指令、也没有启用形象时,段落同样不出现。
|
|
80
82
|
|
|
83
|
+
### 临时静音(暂停注入一段时间,F7)
|
|
84
|
+
|
|
85
|
+
总开关旁提供「暂停注入…」:**30 分钟 / 1 小时 / 到今天结束**(本地时区 24:00)。暂停期间
|
|
86
|
+
指令与用户形象**都不注入**,系统提示词与「未安装本插件」**逐字节一致**;到期由插件内的
|
|
87
|
+
宿主侧定时器**自动恢复**——无需用户操作,也不产生写盘(存储文件与修订号不变)。暂停
|
|
88
|
+
状态保存在存储文件的 `disabledUntil` 字段里(仍是格式版本 2 的可选字段,缺失即未暂停),
|
|
89
|
+
因此**跨重启保留**。手动切换总开关会立即清除暂停(显式开关优先于定时)。设置页在暂停中
|
|
90
|
+
显示剩余时间倒计时,并可随时「立即恢复」;倒计时只是展示,权威判定始终在宿主快照
|
|
91
|
+
(`injection.muted`)里。到期判定与注入渲染都是**可注入时钟**的纯函数(`isMuted`、
|
|
92
|
+
`planInjection(data, now)`),相同输入永远得到相同字节。
|
|
93
|
+
|
|
81
94
|
## 存储
|
|
82
95
|
|
|
83
96
|
记忆保存在 **DSH 数据根目录**下(设置页会显示完整绝对路径):
|
|
84
97
|
|
|
85
98
|
```
|
|
86
|
-
<DSH_HOME>/instruction-memory/memory.json
|
|
99
|
+
<DSH_HOME>/instruction-memory/memory.json # DSH_HOME 默认为 ~/.dsh
|
|
100
|
+
<DSH_HOME>/instruction-memory/memory.json.bak # 上一版(崩溃恢复用)
|
|
101
|
+
<DSH_HOME>/instruction-memory/memory.schema.json # 手改用的 JSON Schema 副本
|
|
102
|
+
<DSH_HOME>/instruction-memory/history/ # 版本历史快照(见下文 F14)
|
|
87
103
|
```
|
|
88
104
|
|
|
89
105
|
路径优先级与官方 `@deepseek-ai/dsh-home-paths` 一致:显式 `$DSH_HOME` → `~/.dsh`。
|
|
@@ -93,6 +109,13 @@ DSH 就会得到第二份空记忆,看起来像"记忆消失"。
|
|
|
93
109
|
> 1.0.0 之前的构建曾把数据写在宿主进程 cwd 下(`.dsh-instruction-memory.json`)。
|
|
94
110
|
> 首次启动新版本时会自动迁移到上述位置,并把原文件改名为 `*.migrated` 保留。
|
|
95
111
|
|
|
112
|
+
**当前存储格式为版本 2**(1.1.0 及更早写的是版本 1,见下文「多情境配置集」)。
|
|
113
|
+
两者都能载入:版本 1 的文件会**平滑迁移**——其中的条目与用户形象全部归入一个名为
|
|
114
|
+
「默认」的配置集,文件在**下次保存时**写为版本 2(载入本身不写盘),并明确提示这一点。
|
|
115
|
+
反过来,**用 1.1.0 及更早的版本打开版本 2 的文件会被拒绝载入并暂停修改**(原文件保留、
|
|
116
|
+
数据完好),这是刻意设计:旧版看不到 `sets` 字段,若按版本 1 读进去,下一次保存就会把
|
|
117
|
+
配置集信息抹掉。升级回新版即可继续。
|
|
118
|
+
|
|
96
119
|
该文件可手工编辑,保存后已打开的设置页会**自动刷新**(见下文「外部改动自动刷新」);
|
|
97
120
|
流式更新不可用时「重新载入」按钮仍可手动读回。每次保存先写临时文件再原子替换,
|
|
98
121
|
进程崩溃不会留下损坏的半截 `memory.json`;保存前把上一版**复制**为 `memory.json.bak`,
|
|
@@ -151,11 +174,17 @@ DSH 就会得到第二份空记忆,看起来像"记忆消失"。
|
|
|
151
174
|
**暂停一切修改**(此时保存会用 v1 格式覆盖文件,可能损坏数据),直到你升级插件或把
|
|
152
175
|
文件改回版本 1 后点「重新载入」。
|
|
153
176
|
|
|
154
|
-
设置页支持**导出 / 导入**:导出为自带说明的 JSON
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
177
|
+
设置页支持**导出 / 导入**:导出为自带说明的 JSON 文件,导出的是**整份存储**(所有配置集,
|
|
178
|
+
不是只看当前激活的那一套)——备份只带走一半就没有意义了;导入采用**合并语义**——在**同一个
|
|
179
|
+
配置集内**按 id 与「标题+内容」去重,只新增、绝不覆盖或清空现有条目。导入文件里带
|
|
180
|
+
`sets` 时会**按需创建对应配置集**并把条目放回原集合(本插件的导出天然带上);指向不存在集合
|
|
181
|
+
的条目会归入当前激活集合,并在结果里说明有几项如此处理。要"整体还原"请用上文
|
|
182
|
+
「从备份恢复…」,导入不做覆盖。
|
|
183
|
+
|
|
184
|
+
请求体上限由各项上限推导(`(MAX_ENTRIES × (MAX_CONTENT + MAX_TITLE + MAX_WHEN + 8×24 + 200)
|
|
185
|
+
+ MAX_PROFILES × (MAX_PROFILE_CONTENT + MAX_PROFILE_NAME + 200) + MAX_SETS × (MAX_SET_NAME + 200))
|
|
186
|
+
× 2 + 100000`,另加形象与配置集的开销),保证**本插件导出的文件一定能被自己导回**——早先
|
|
187
|
+
写死的 1,000,000 小于满配导出(200 × 6000 = 1.2 M 字符),导致满配记忆导出后无法导入。
|
|
159
188
|
|
|
160
189
|
### 首次运行不写文件(惰性创建)
|
|
161
190
|
|
|
@@ -173,6 +202,121 @@ DSH 就会得到第二份空记忆,看起来像"记忆消失"。
|
|
|
173
202
|
**不受影响**:从 `.bak` 恢复、以及从旧位置迁移——这两条分支只有在**磁盘上已有真实数据**时才会走,
|
|
174
203
|
它们仍然会立即写回。
|
|
175
204
|
|
|
205
|
+
## 多情境配置集(配置集 / sets)
|
|
206
|
+
|
|
207
|
+
一份记忆装不下"工作 / 个人 / 某客户"三套互不相干的规则,所以设置页新增了**配置集**:
|
|
208
|
+
多个命名集合共存于**同一个 `memory.json`**,用户显式一键切换,**注入只渲染当前激活的那一套**。
|
|
209
|
+
|
|
210
|
+
> **命名**:存储字段叫 `sets`(配置集),不叫 `profiles`——`profiles` 已经被 1.1.0 的
|
|
211
|
+
> 「用户形象」占用了,两者是完全不同的东西:配置集是"记忆的分区",用户形象是"关于你本人的
|
|
212
|
+
> 画像"。文档里的 "Profiles" 指的就是配置集。
|
|
213
|
+
|
|
214
|
+
- 配置集本身只有 `id` 与名称;条目与用户形象通过各自的 `setId` 指向它。因此"切换"只是
|
|
215
|
+
改一个字段——**不会移动、复制或删除任何一条用户文本**,也不可能出现"切到一半"的中间态。
|
|
216
|
+
- 设置页顶部列出全部配置集(含各自的条目/形象数量),点击「切换」立即生效并写盘(重启后
|
|
217
|
+
保持);「新建」「重命名」「复制」「删除」齐备。**新建与复制都不会自动激活**——激活是
|
|
218
|
+
唯一会改变注入内容的动作,它始终是一次单独的、看得见的点击。
|
|
219
|
+
- **删除当前激活的配置集会被拒绝**(请先切换);删除其他配置集会连同其中的条目与形象一起
|
|
220
|
+
删除,确认框里写明数量。文件里至少要保留一个配置集。
|
|
221
|
+
- 「复制」用于从模板派生:条目与形象会以全新 id 复制到新集合,`客户A → 客户B` 就是这么来的。
|
|
222
|
+
- 全部配置集**合计**最多 200 条指令、50 个用户形象、20 个配置集。总开关与注入字数上限是
|
|
223
|
+
**全局**的,不分集合。
|
|
224
|
+
- 提示词里**不会**出现配置集名称或任何"当前属于哪套"的标记——注入的就是激活集本身,和只有
|
|
225
|
+
一套记忆时的文本逐字节一致。切换集合会改变提示词前缀,这是预期的(缓存会随之失效一次)。
|
|
226
|
+
|
|
227
|
+
> **红线:绝不按进程 cwd 隐式建集或切换。** 这条规则来自一次真实事故:早期构建把数据文件
|
|
228
|
+
> 解析到宿主进程的工作目录下,用户换个目录启动 DSH 就得到第二份空记忆,看起来像"记忆凭空
|
|
229
|
+
> 消失"。配置集沿用同一条纪律:`activeSetId` 只能由用户显式命令写入,载入时它只从文件里读,
|
|
230
|
+
> 任何"根据当前项目目录推断集合"的想法都不要加进来。
|
|
231
|
+
|
|
232
|
+
## 版本历史与回滚(F14)
|
|
233
|
+
|
|
234
|
+
现在只有一份 `.bak`,误改或批量误删之后回不到更早的状态。每次**成功保存**后,插件会把这一版
|
|
235
|
+
原样写入 `<备份位置>/history/` 下的一个快照文件:
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
20261015T152355123Z-r12-laptop.json # UTC 时间戳 - rev - 写入设备
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
- 命名是固定宽度的:按文件名排序就是按时间排序,列出历史只需读目录,不必逐个打开文件。
|
|
242
|
+
- 每台设备**各保留最近 20 份**(按文件名里的设备标签分组),逐出最旧的。两台机器共用一个
|
|
243
|
+
备份目录时,谁也不会把对方的历史剪掉。不符合该命名的文件(手工放的、别的工具写的、
|
|
244
|
+
云盘的冲突副本)**一律不碰**。
|
|
245
|
+
- **为什么放在 `history/` 子目录**:`memory.json` 所在目录被 `fs.watch` 监听以做外部改动
|
|
246
|
+
自动刷新,快照绝不能混进去触发重载;子目录不在非递归监听的范围内,文件名过滤也会兜底。
|
|
247
|
+
- 设置页「版本历史」里可展开列表(时间、rev、条目/形象数、体积、是否来自其他设备),
|
|
248
|
+
一键**回滚**或删除某一份快照。回滚是把快照内容作为**新的一版**写回(rev 只增不减),并且
|
|
249
|
+
它自己也会留下一份快照——所以回滚本身也能再回滚回来;被替换掉的内容仍然躺在
|
|
250
|
+
`memory.json.bak` 里。
|
|
251
|
+
- 快照写失败**不会**让这次保存失败(存储已经安全落盘),只会在面板上说明"历史快照未写入"。
|
|
252
|
+
|
|
253
|
+
另外,导入(见下文)是**只增合并**,无法还原整份记忆;因此另有一个**「从备份恢复…」**入口:
|
|
254
|
+
选择本插件导出的文件,确认后**整体替换**当前记忆。同样是"可反悔"的——替换前的内容已经在
|
|
255
|
+
`.bak` 与版本历史里。
|
|
256
|
+
|
|
257
|
+
## 手改与 JSON Schema(F15)
|
|
258
|
+
|
|
259
|
+
随包发布一份 `memory.schema.json`(JSON Schema draft-07),描述版本 1/2 两种文件形状与全部
|
|
260
|
+
长度上限。宿主还会把它的**副本写到 `memory.json` 旁边**,而 `memory.json` 里写死了:
|
|
261
|
+
|
|
262
|
+
```json
|
|
263
|
+
{ "$schema": "./memory.schema.json", "version": 2, … }
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
于是把 `memory.json` 拖进任何支持 JSON Schema 的编辑器(VS Code 等),就能直接得到字段校验、
|
|
267
|
+
补全与悬停说明,**不需要知道这个包装在哪里**。Schema 与代码的一致性由 `verify.mjs` 逐项断言
|
|
268
|
+
(上限、枚举、最大条数),因此它不会和实现悄悄漂移。
|
|
269
|
+
|
|
270
|
+
> **CLI 管理命令不在本次承诺范围内。** 它依赖宿主是否向插件开放命令能力,目前未经验证;
|
|
271
|
+
> `tui` / `headless` 环境下设置页不可用这一点没有改变,手改 JSON + 这份 Schema 是当前
|
|
272
|
+
> 官方支持的路径。
|
|
273
|
+
|
|
274
|
+
## 源码模式(F16)
|
|
275
|
+
|
|
276
|
+
设置页的「源码模式」把**整份存储文件**(与 `memory.json` 内容一致)放进一个文本框直接编辑:
|
|
277
|
+
|
|
278
|
+
- 输入时**实时做 JSON 语法校验**(防抖 250ms),并摘要显示"几条指令 / 几个形象 / 几个配置集";
|
|
279
|
+
语法不通过时保存按钮直接禁用。
|
|
280
|
+
- 保存走的是**与手改文件完全相同的一条路**:解析 → 归一化 → 披露(未能载入的条目、被修正的
|
|
281
|
+
选项值、被截断的字段、指向不存在配置集的 `setId`、未来格式版本拒绝),再按 `rev` 提交。
|
|
282
|
+
因此它比裸编辑器安全,也**不会绕过**任何既有校验。
|
|
283
|
+
- **草稿不会因为一次拒绝而丢失**:冲突(其他窗口/进程在你编辑期间提交过)、版本拒绝、
|
|
284
|
+
语法错误都只显示原因,文本框里的内容原样保留;点「重新载入最新内容」才会放弃本地修改。
|
|
285
|
+
- 提交时带的 `rev` 是**打开源码时**读到的那个,而不是当前面板上的最新值——这样"你编辑期间
|
|
286
|
+
别人改过"会被如实判为冲突,而不是把你的整份文本盖到别人的修改上。
|
|
287
|
+
- 与卡片编辑互斥:正在编辑某条指令/形象时不能进入源码模式(按钮禁用),避免两份草稿互相覆盖。
|
|
288
|
+
|
|
289
|
+
## 自定义备份位置(F18,不含网络同步)
|
|
290
|
+
|
|
291
|
+
默认一切都在存储目录里。设置页可以把**备份位置**指向别处(例如本地云盘文件夹):
|
|
292
|
+
|
|
293
|
+
- 影响三件事:`history/` 快照目录、「导出到备份目录」的去向、以及**额外**一份
|
|
294
|
+
`memory.json.bak` 副本。**存储文件本身与它的跨进程锁不会移动**——崩溃恢复用的
|
|
295
|
+
`memory.json.bak` 始终留在存储目录,因为那是"下次启动必须能找到"的东西。
|
|
296
|
+
- 设置时会**当场校验**:必须是绝对路径、目录存在、且实际写入一个探针文件成功;不满足就直接
|
|
297
|
+
拒绝并说明原因(例如"目录不存在或不可访问"),而不是等到某次保存悄悄失败。
|
|
298
|
+
- **它是本机设置**:从备份文件"整体替换"或回滚历史时**不会**被改写——否则一台机器上的路径
|
|
299
|
+
会被带到另一台机器上,把备份悄悄写到一个不存在的目录里。
|
|
300
|
+
- 多设备建议:把备份位置指向本地云盘目录,**只让导出文件与 `history/` 快照同步**;
|
|
301
|
+
**不要同步存储目录本身**(`memory.json`)——两台机器同时写入会互相覆盖,插件那套
|
|
302
|
+
rev 冲突检测与写锁都只在同一台机器内有效。换机器时用「导出 → 另一台导入/恢复」。
|
|
303
|
+
|
|
304
|
+
## 按需指令的本地预演(F17)
|
|
305
|
+
|
|
306
|
+
「按需」条目到底会不会被采纳,本质上是模型的判断,插件看不到也猜不准。设置页提供一个
|
|
307
|
+
**本地预演**:输入一句当前任务描述,前端**纯字面关键词**匹配各条按需指令的
|
|
308
|
+
「适用场景 / 标题 / 内容」(场景权重最高、正文最低),列出字面重合度较高的条目并给出
|
|
309
|
+
高/中/低分档,同时在列表里给这些条目打上「预演·高/中/低」标记。
|
|
310
|
+
|
|
311
|
+
它的用途只有一个:**帮你把「适用场景」写清楚、预判取舍**(例如"没写场景的条目只能靠正文
|
|
312
|
+
碰运气"、"这条优先级低、一旦超出字数上限会先被丢下")。因此:
|
|
313
|
+
|
|
314
|
+
- 措辞是克制的:界面上明确写着**「本地预演 ≠ 模型的真实判断」**,它不参与注入,也不代表模型
|
|
315
|
+
实际会不会采纳;`verify.mjs` 断言了这句免责声明在两种语言里都存在。
|
|
316
|
+
- 只有「按需」条目参与;「始终」条目无条件生效,与预演无关。
|
|
317
|
+
- 匹配是启发式的:中文按 2/3 字滑窗切词(无分词依赖),英文/数字按词切分,术语上限 40 个,
|
|
318
|
+
纯前端计算、不发任何请求。
|
|
319
|
+
|
|
176
320
|
## 无 Web 服务器时依然生效
|
|
177
321
|
|
|
178
322
|
`systemPrompt` 是本插件唯一的硬依赖。**Web 服务器不是前置条件**:设置页那部分路由通过
|
|
@@ -191,7 +335,7 @@ DSH 就会得到第二份空记忆,看起来像"记忆消失"。
|
|
|
191
335
|
`info`,过程细节写 `debug`(走宿主的 `ctx.logger`):
|
|
192
336
|
|
|
193
337
|
```
|
|
194
|
-
[instruction-memory] mounted: store=<DSH_HOME>\instruction-memory\memory.json from=memory.json onDisk=yes entries=2 enabled=true section=437 chars (pending assembly) pulls=0
|
|
338
|
+
[instruction-memory] mounted: store=<DSH_HOME>\instruction-memory\memory.json from=memory.json onDisk=yes entries=2 sets=2 activeSet=st-default enabled=true section=437 chars (pending assembly) pulls=0
|
|
195
339
|
```
|
|
196
340
|
|
|
197
341
|
挂载行含**数据来源**(`memory.json` / `memory.json.bak (main file missing)` / `memory.json.bak (main file corrupt)`
|
|
@@ -217,6 +361,26 @@ DSH 就会得到第二份空记忆,看起来像"记忆消失"。
|
|
|
217
361
|
**逐字节相同**(`verify.mjs` 有断言锁定这一点)。`debug` 级别由宿主日志级别过滤;
|
|
218
362
|
没有 logger 服务的环境下需要 `DSH_INSTRUCTION_MEMORY_DEBUG=1` 才会输出到标准错误。
|
|
219
363
|
|
|
364
|
+
### 诊断与安全状态面板(F12)
|
|
365
|
+
|
|
366
|
+
设置页的「诊断信息」卡片把散在快照里的排障信息集中展示(默认收起,纯展示、无写路由):
|
|
367
|
+
|
|
368
|
+
- **存储**:路径、是否已在磁盘、载入来源(`loadedFrom`:首次创建 / 采用现有文件 / `.bak`
|
|
369
|
+
恢复 / 旧版迁移等)、最近保存时间、修订号(rev);
|
|
370
|
+
- **注入**:是否已注册、`pulls` 拉取计数(**自本次插件启动**,含义见上文)、当前字符数 / 上限;
|
|
371
|
+
- **备份与历史**:备份位置、是否默认、`.bak` 覆盖副本路径、版本历史目录与快照数、Schema 文件位置;
|
|
372
|
+
- **错误与警告**:存储错误 / 存储警告 / 历史错误 / 注入错误原样显示。
|
|
373
|
+
|
|
374
|
+
「复制诊断信息」把以上内容序列化成纯文本(附 `navigator.userAgent`、语言与当前时间,
|
|
375
|
+
便于提 issue)。它**绝不包含条目正文、形象内容等用户文本**;剪贴板 API 不可用时降级为
|
|
376
|
+
`execCommand` 复制,再不可用则把文本放进可手动全选的文本框(序列化逻辑是导出的纯函数
|
|
377
|
+
`__diagnosticsText`,`verify.mjs` 断言其输出含关键字段且不含任何用户文本)。
|
|
378
|
+
|
|
379
|
+
卡片同时补上了此前缺失的唯一安全信号:**`security.hostGuarded`**。设置路由绑定在**非回环
|
|
380
|
+
地址**(主动对外发布)时,页面顶部会出现醒目橙色警告:同一网络内的设备可无鉴权访问此页面、
|
|
381
|
+
请勿在不可信网络暴露,并指向下节「经反向代理对外发布」。该判定与路由实际执行的 Host 校验
|
|
382
|
+
是**同一个开关**;快照同时携带 `boundHost`(路由注册时捕获的绑定地址),警告会点名具体地址。
|
|
383
|
+
|
|
220
384
|
## 请求来源防护(Host / Origin)
|
|
221
385
|
|
|
222
386
|
设置页路由之所以要求 `content-type: application/json`,是因为 HTML 表单无法伪造 JSON body,
|
|
@@ -438,15 +602,26 @@ Package → Settings → Trusted publishing → Add trusted publisher
|
|
|
438
602
|
|
|
439
603
|
## 数据格式
|
|
440
604
|
|
|
605
|
+
版本 2(当前写入的格式;版本 1 的文件同样能载入,见上文「存储」):
|
|
606
|
+
|
|
441
607
|
```json
|
|
442
608
|
{
|
|
443
|
-
"
|
|
609
|
+
"$schema": "./memory.schema.json",
|
|
610
|
+
"version": 2,
|
|
444
611
|
"rev": 3,
|
|
445
612
|
"enabled": true,
|
|
613
|
+
"disabledUntil": 0,
|
|
446
614
|
"budgetChars": 4000,
|
|
615
|
+
"backupDir": "",
|
|
616
|
+
"activeSetId": "st-default",
|
|
617
|
+
"sets": [
|
|
618
|
+
{ "id": "st-default", "name": "默认" },
|
|
619
|
+
{ "id": "st-work", "name": "工作" }
|
|
620
|
+
],
|
|
447
621
|
"entries": [
|
|
448
622
|
{
|
|
449
623
|
"id": "im-xxxx",
|
|
624
|
+
"setId": "st-default",
|
|
450
625
|
"title": "回答语言",
|
|
451
626
|
"content": "所有回答使用简体中文。",
|
|
452
627
|
"mode": "always",
|
|
@@ -460,6 +635,7 @@ Package → Settings → Trusted publishing → Add trusted publisher
|
|
|
460
635
|
"profiles": [
|
|
461
636
|
{
|
|
462
637
|
"id": "pf-xxxx",
|
|
638
|
+
"setId": "st-default",
|
|
463
639
|
"name": "职业背景",
|
|
464
640
|
"content": "有 8 年后端开发经验,技术问题可跳过入门概念。",
|
|
465
641
|
"enabled": true,
|
|
@@ -472,8 +648,26 @@ Package → Settings → Trusted publishing → Add trusted publisher
|
|
|
472
648
|
`mode` 为 `always` | `auto`;`priority` 为 `2` 高 / `1` 普通 / `0` 低。`rev` 是存储修订号,
|
|
473
649
|
由插件在每次成功写入时递增,**无需手填**(缺失视为 0)。`tags` 为可选的短标签字符串数组
|
|
474
650
|
(最多 8 个、单个不超过 24 字符,仅用于设置页筛选,不参与注入);缺失或非数组一律按无标签处理,
|
|
475
|
-
|
|
651
|
+
非法项会被剔除、超长会被截断、重复项会去重。
|
|
652
|
+
|
|
653
|
+
`sets` 为**配置集**列表(每项只有 `id` 与 `name`,单行不超过 30 字符;最多 20 个),
|
|
654
|
+
`activeSetId` 指向当前激活的那一个——**注入只渲染它**。每条 `entries` / `profiles` 用
|
|
655
|
+
`setId` 声明归属:指向不存在的集合时归入当前激活集合(并在设置页披露);留空视为"跟随当前
|
|
656
|
+
激活集合"。缺失 `sets` 字段即版本 1 文件,载入时会合成一个名为「默认」的集合。
|
|
657
|
+
|
|
658
|
+
`backupDir` 为**本机**的自定义备份位置(留空 = 与存储文件同目录,最多 512 字符);它不是
|
|
659
|
+
可移植内容,从备份恢复时不会被改写。
|
|
476
660
|
|
|
477
|
-
`
|
|
661
|
+
`disabledUntil` 为**临时静音**(F7)的到期时刻(epoch 毫秒):`0` 或缺失 = 未暂停;未来值 =
|
|
662
|
+
暂停中,到期由插件自动恢复(无需操作、不写盘、rev 不变),跨重启保留。手动切换总开关会
|
|
663
|
+
清零它(显式开关优先于定时)。这是格式版本 2 的**可选**字段:旧文件缺失即未暂停,不触发
|
|
664
|
+
迁移;手改成非法值(非数字/负数)会被重置为 0 并在设置页披露。
|
|
665
|
+
|
|
666
|
+
`profiles` 为可选的**用户形象**数组(缺失按空数组处理):`name` 单行不超过 40 字符,
|
|
478
667
|
`content` 不超过 4000 字符,最多 50 个;`enabled` 控制是否使用该形象。名称与内容同时为空的形象会在
|
|
479
|
-
载入时剔除并明确披露;名称为空时自动取内容前 20
|
|
668
|
+
载入时剔除并明确披露;名称为空时自动取内容前 20 字符。
|
|
669
|
+
|
|
670
|
+
**上限一览**(`memory.schema.json` 与 `verify.mjs` 保证它和代码一致):
|
|
671
|
+
指令最多 200 条(全部配置集合计)/ 标题 120 字符 / 内容 6000 字符 / 适用场景 200 字符 /
|
|
672
|
+
每条最多 8 个标签、单个 24 字符;用户形象最多 50 个 / 名称 40 字符 / 内容 4000 字符;
|
|
673
|
+
配置集最多 20 个 / 名称 30 字符;注入字数上限 800–40000 字符。
|