@dafantong01/dsh-memory-palace 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +314 -2
- package/cordis.patch.yml +12 -0
- package/lib/archive.js +483 -0
- package/lib/client.js +637 -0
- package/lib/index.js +2107 -0
- package/lib/route.js +296 -0
- package/package.json +55 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DaFanTong
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,315 @@
|
|
|
1
|
-
#
|
|
1
|
+
# dsh-memory-palace(记忆回廊)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
把会话原文按「一轮对话」为单位、分类归档到**当前工作区**的 `memory-palace/` 目录。
|
|
4
|
+
目的:在 DSH 自动压缩之前,先把较早的完整原文落到磁盘,压缩后还能查回原话。
|
|
5
|
+
|
|
6
|
+
## 工作方式
|
|
7
|
+
|
|
8
|
+
1. **触发检查**:每 `checkEveryTurns` 轮(默认 5),在回合收尾处检查一次。要同时满足两道门:
|
|
9
|
+
- **压力门**:上下文占用 ≥ `triggerPressure`(默认 60%)× 窗口。
|
|
10
|
+
占用取 **DSH 自己的计量** `ctx.tokenMeter.measure(session).totalTokens`——压缩后它会回落;
|
|
11
|
+
拿不到计量服务时才退回「日志轮次累加」,并在提示里标注「日志估算」。
|
|
12
|
+
- **批量门**:可归档轮数 ≥ `minBatchTurns`(默认 10)**或** 可归档文本量 ≥ 无需归档量 × `batchByteFraction`(默认 50%)
|
|
13
|
+
2. **提醒**:两道门都过,插件注入一条极短提示,请模型调用归档工具(`enableSteer:false` 可关掉,关掉就没有自动归档)。
|
|
14
|
+
3. **分类(逐轮判断)**:模型逐轮决定每轮属于哪个类别,用 `segments` 提交:
|
|
15
|
+
`[{category, fromTurn, toTurn}, …]`。同一类别可以拆成多段,最后合并成一个文件;
|
|
16
|
+
每个 turn 只能归一类,区间不许重叠。
|
|
17
|
+
4. **保留**:`无需归档的近期文本量 = 窗口 × retainFraction`(默认 12%),比这更新的轮次不动。
|
|
18
|
+
取整轮粒度、严格不超过该上限;只有单轮本身就超上限时,至少保留最后 1 轮。
|
|
19
|
+
12% 的用意:压得比 DSH 压缩尾部(约 16%)更小,压缩时不会碰到你还没归档的近期内容。
|
|
20
|
+
5. **写入**:插件从会话日志逐字取出每个类别的轮次,按时间正序写进该类别**一个文件**(超 `maxFileBytes` 才分卷)。模型不复述内容,不会截断或改写。
|
|
21
|
+
6. **覆盖去重**:已归档轮次记在 `.state.json` 的 `archivedRanges`;新请求先把已覆盖的轮次剔掉,只写剩下的。
|
|
22
|
+
7. **归档后压缩**:`compactAfterArchive` **默认关**。`compactNow()` 不接受范围参数,压多少由 DSH 自己决定,
|
|
23
|
+
可能压掉尚未归档的近期轮次(原文仍在会话日志里,可再归档)。默认不动它。
|
|
24
|
+
|
|
25
|
+
## 目录与文件名(会话级隔离)
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
<当前工作区>/
|
|
29
|
+
memory-palace/
|
|
30
|
+
.namespaces.json # 会话ID → 文件夹名 的映射(插件维护)
|
|
31
|
+
config.json # 新会话的默认值(旧版遗留,仅作默认)
|
|
32
|
+
<文件夹>/ # 记忆名:英文(大小写均可)+数字/空格/下划线/连字符
|
|
33
|
+
config.json # 本会话自己的开关/参数/记忆名
|
|
34
|
+
.state.json # 本会话水位、已归档区间
|
|
35
|
+
index.jsonl # 本会话的归档索引(路径相对本文件夹)
|
|
36
|
+
<YYYY-MM>/ # 月份容器:归档时按归档年月找/建
|
|
37
|
+
_summary.md # 该月摘要:只追加,每段对应一次归档里的一个类别
|
|
38
|
+
<category>/
|
|
39
|
+
<前缀>-<category>-<YYYYMMDD-HHmmss>.md
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- **文件夹名 = 页签里填的「记忆命名」**:英文(大小写均可)、数字、空格、下划线、连字符,≤32 位;**首尾不能有空格**,中文与路径分隔符会被拒绝。
|
|
43
|
+
- 没命名时用会话ID,且**不产生任何记忆文件**(只建 config/state)。
|
|
44
|
+
- 在页签里改名并保存 → 文件夹**就地改名**,索引里的路径是相对的,不需要跟着改。
|
|
45
|
+
- **文件夹即归属**:`memory-palace/<你的文件夹>/` 里的文件都属于那个会话;文件名前缀只是历史署名,不代表归属。
|
|
46
|
+
- **摘要每月一份**:`<YYYY-MM>/_summary.md`,只追加;同一次归档里每个类别写一段,段头是「归档日期 · [类别] 轮次范围」。
|
|
47
|
+
- 一次归档调用里,同一类别的所有轮次进同目录下的**一个文件**(超 `maxFileBytes` 才按轮分卷 `-p1/-p2`)。
|
|
48
|
+
- 不同类别 = 不同文件,即使来自同一次调用。
|
|
49
|
+
- 同秒冲突自动加 `-2`。分类目录强制英文小写。
|
|
50
|
+
|
|
51
|
+
### 从旧结构迁移
|
|
52
|
+
|
|
53
|
+
早期版本用过不同的目录结构。迁移脚本只用于本项目早期数据,**不包含在发布包中**;新用户从 0.1.0 开始使用无需迁移。
|
|
54
|
+
|
|
55
|
+
当前采用的结构是 `<会话>/<YYYY-MM>/<category>/`。
|
|
56
|
+
|
|
57
|
+
## 工具
|
|
58
|
+
|
|
59
|
+
| 工具 | 作用 |
|
|
60
|
+
|---|---|
|
|
61
|
+
| `palace_archive` | 归档轮次。**未命名会话会拒绝**。用 `segments` 逐段给类别:`[{category, fromTurn, toTurn}, …]`;并用 `summaries=[{category, text}, …]` 给**每个类别**写一段只概述该类别本次轮次的话(逐段追加到当月 `_summary.md`)。单类别时可用 `summary` 代替。可传 `selfName` 作文件名前缀。**超出保留边界或已归档的轮次会直接报错**(防止模型估错轮次号) |
|
|
62
|
+
| `palace_turns` | 列**轮次清单**:`turn` 号 + 发言者 + 开头 70 字,用于归档前确认每轮内容。`turn` 是日志事件序号(含系统指令/插件 steer/纯工具轮),**不要用“第几条消息”换算** |
|
|
63
|
+
| `palace_summary` | 读**月度摘要**:不指定 `month` 时列出最近几个月(摘要末尾预览 + 归档文件数);`month=YYYY-MM` 返回该月摘要全文;`category` 只看该月某类段落;`append` + `fromTurn/toTurn` 可给**还没归档的近况**补写一段摘要 |
|
|
64
|
+
| `palace_search` | 检索归档。默认**只查本会话**;`namespace` 查别的会话、`all:true` 扫全部命名空间(结果标来源)、`category` 只查某类。`keyword` 支持空格分隔的多关键词(AND,引号包短语)。默认**按轮聚合**(每轮一条代表命中 + 该轮命中数),`limitTurns` 控制轮数(默认取 `searchTurnLimit`=12);`groupByTurn=false` 回原始命中行(旧 `limit` 语义)。片段约 220 字符,总输出上限 8K,文件读失败会跳过并注明。**只搜归档正文,不搜 `_summary.md`**(摘要里的概述性说法要用 `palace_summary` 读);但原文 **0 命中**时会自动回查摘要并标注「摘要不是轮次」,可用 `includeSummary:true` 强制追加 |
|
|
65
|
+
| `palace_read` | 读归档。`fromTurn/toTurn` 返**完整原文**;`path` 可省略(按轮次自动找文件),也接受 search 的「文件夹/…」路径;可传 `namespace`。末尾附一行**回链**(提取 turn 引用/文件名/工具名,自动提取仅供参考) |
|
|
66
|
+
| `palace_config` | 查看/修改**本会话**配置(`get` 会同时返回 effective / 工作区默认 / 本会话配置) |
|
|
67
|
+
|
|
68
|
+
### 检索四步法(为省 token 设计)
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
0) 不知道在哪个月:palace_summary(limit=6)
|
|
72
|
+
→ 每月摘要末尾预览;找到大致年月
|
|
73
|
+
0b) palace_summary(month="2026-10")
|
|
74
|
+
→ 该月全部摘要段落(哪次归档、讲了什么)
|
|
75
|
+
1) palace_search(keyword="事故教训", limit=3)
|
|
76
|
+
→ 约 1 千字符:命中位置 + 轮次 + 行号 + 文件路径
|
|
77
|
+
2) palace_read(fromTurn=48, toTurn=48)
|
|
78
|
+
→ 该轮完整原文(思考、工具调用、返回都在)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- 单次读取默认上限 **60,000 字符**(约一本书的三分之一),硬上限也是 6 万。
|
|
82
|
+
- 单轮超过 6 万字符时,返回前 6 万并提示 `用 offset=N 继续`,可分段读完整个大轮(实测 12.7 万字符分 3 段读完)。
|
|
83
|
+
- 不给轮次、也不给 path 时只返回文件开头 6 万字符,并提示按轮次精确取。
|
|
84
|
+
|
|
85
|
+
## 开关(会话级,fail-closed)
|
|
86
|
+
|
|
87
|
+
真正生效要同时满足(现在收成**一个统一门** `gateState()`,页签会显示“为什么没生效”):
|
|
88
|
+
|
|
89
|
+
1. **全局总闸** `enabled`(默认开,只是允许插件工作)
|
|
90
|
+
2. **本会话开关**:`memory-palace/<文件夹>/config.json` 里 `enabled: true`,新会话缺省为关
|
|
91
|
+
3. **已命名**:记忆名为空时,即使开了开关也**不归档**(只建文件夹和配置)
|
|
92
|
+
4. **自动提示**:`injectNotice` / `enableSteer` 任一关掉 → 工具仍可用,但不会主动 steer(页签会写明)
|
|
93
|
+
5. **会话可见性**:`palace_archive` 被 capability-panel 关掉的会话整块静默
|
|
94
|
+
|
|
95
|
+
`GET /memory-palace-api` 额外返回 `gate: { active, steer, reasons[] }` 与 `events`(最近 20 条插件事件:归档/检索失败/门关闭等),页签直接展示,不用解压会话日志。
|
|
96
|
+
|
|
97
|
+
### 三个入口
|
|
98
|
+
|
|
99
|
+
- 聊天框 **`/palace on`**(`off` 关闭,`status` 查看)
|
|
100
|
+
- 页签「记忆回廊」里的滑块
|
|
101
|
+
- 让模型调用 `palace_config`
|
|
102
|
+
|
|
103
|
+
都只改**当前会话自己**那份配置——不碰 profile、不用重启。
|
|
104
|
+
|
|
105
|
+
## 会话页签(GUI)
|
|
106
|
+
|
|
107
|
+
会话头部会多一个「记忆回廊」页签,按**当前会话**显示与修改:
|
|
108
|
+
|
|
109
|
+
- 开关滑块(绿色=已开启、灰色=已关闭)
|
|
110
|
+
- 本会话文件夹(`memory-palace/<名字>`;未命名时显示会话ID并提示不会归档)
|
|
111
|
+
- 「记忆命名」输入框:保存即**改名文件夹**;只接受英文(大小写)/数字/空格/下划线/连字符、首尾不能空格;与同工作区其他会话重名会被拒绝
|
|
112
|
+
- 估算窗口 tokens(只读,自动探测)
|
|
113
|
+
- 触发阈值 %(滑块 + 输入框联动)
|
|
114
|
+
- 检查频率、最小归档批量、批量兜底阈值 %、无需归档比例 %
|
|
115
|
+
- 无需归档的近期文本量(只读,窗口 × 比例为 token 数)
|
|
116
|
+
- 单文件上限 MB
|
|
117
|
+
- 自动提示、收录深度思考、归档后压缩
|
|
118
|
+
- 最近 20 条归档列表
|
|
119
|
+
|
|
120
|
+
数据来自宿主路由 `GET/POST /memory-palace-api`(`kind: exact`;DSH 的 `/api` 是 RPC 通道,不能占用)。
|
|
121
|
+
非活跃会话通过会话日志头部的 `session` 事件取 `cwd`,不依赖 agent 是否在运行。
|
|
122
|
+
|
|
123
|
+
## 配置分层
|
|
124
|
+
|
|
125
|
+
1. **全局默认**:插件配置(profile 级,volatile)。
|
|
126
|
+
2. **工作区默认**:`<工作区>/memory-palace/config.json`,只作为新会话的初始值。
|
|
127
|
+
3. **本会话配置**:`memory-palace/<文件夹>/config.json`,页签与 `/palace` 实际改的是这份。
|
|
128
|
+
4. **按会话开关**:capability-panel 里关掉 `palace_archive`。
|
|
129
|
+
|
|
130
|
+
可被工作区覆盖:`enabled`、`triggerPressure`、`retainFraction`、`batchByteFraction`、`retainBytes`、
|
|
131
|
+
`maxFileBytes`、`checkEveryTurns`、`minBatchTurns`、`selfNameSlug`、`includeReasoning`、
|
|
132
|
+
`compactAfterArchive`、`injectNotice`。
|
|
133
|
+
|
|
134
|
+
## 配置项
|
|
135
|
+
|
|
136
|
+
| 项 | 默认 | 说明 |
|
|
137
|
+
|---|---|---|
|
|
138
|
+
| `enabled` | true | 全局总闸 |
|
|
139
|
+
| `triggerPressure` | 0.6 | 压力门:历史估算 ÷ 窗口 |
|
|
140
|
+
| `retainFraction` | 0.12 | 无需归档比例,占窗口比例(0.02–0.5,建议 < 16%) |
|
|
141
|
+
| `batchByteFraction` | 0.5 | 批量门兜底:可归档量达到无需归档量的该比例即放行 |
|
|
142
|
+
| `minBatchTurns` | 10 | 批量门:可归档轮数 |
|
|
143
|
+
| `checkEveryTurns` | 5 | 每几轮检查一次 |
|
|
144
|
+
| `maxFileBytes` | 5000000 | 单文件上限(十进制 MB,5,000,000 = 5.0 MB),超出按轮分卷 |
|
|
145
|
+
| `selfNameSlug` | assistant | 文件名前缀(与「记忆命名」无关,可分开设置) |
|
|
146
|
+
| `rootDirName` | memory-palace | 根目录名 |
|
|
147
|
+
| `includeReasoning` | true | 归档是否收录思考原文 |
|
|
148
|
+
| `compactAfterArchive` | false | 归档后是否触发压缩(默认关,见上) |
|
|
149
|
+
| `enableSteer` | true | 是否注入归档提醒 |
|
|
150
|
+
| `injectNotice` | true | 是否注入提示 |
|
|
151
|
+
| `enabledWorkspaces` | [] | 额外工作区白名单 |
|
|
152
|
+
| `enabledSessions` | [] | 额外会话白名单 |
|
|
153
|
+
| `contextWindowTokens` | 自动 | **只读**:从日志 `request/context` 探测,进程内缓存 |
|
|
154
|
+
| `searchTurnLimit` | 12 | `palace_search` 按轮聚合时默认返回多少轮(1–30 整数,服务端也校验) |
|
|
155
|
+
| `groupByTurn` | true | `palace_search` 是否默认按轮聚合(false 回原始命中行) |
|
|
156
|
+
| `recallStrength` | low | 回忆强度:`off` / `low` / `medium` / `high` / `forced`(见下节) |
|
|
157
|
+
| `recallBudgetRounds` | 1 | **建议预算**:最多几轮(1–5 整数,仅 high/forced 出现;插件不强制执行) |
|
|
158
|
+
| `recallBudgetKeywords` | 1 | **建议预算**:每轮最多几个关键词(1–5 整数;插件不强制执行) |
|
|
159
|
+
| `recursionMaxDepth/Width` | — | 旧键,等价于上面两个;读写时保持同步,仅为兼容保留 |
|
|
160
|
+
| `retainTurns` | — | **已废弃**:保留窗口只按比例算;写入时会被忽略并清理 |
|
|
161
|
+
|
|
162
|
+
会话配置里额外有 `name`(记忆命名,默认空);会话开关是会话配置里的 `enabled`。
|
|
163
|
+
|
|
164
|
+
## 回忆强度(五档)
|
|
165
|
+
|
|
166
|
+
| 档位 | 行为 |
|
|
167
|
+
|---|---|
|
|
168
|
+
| `off` 关 | **只归档,不读记忆**;不注入任何回忆/摘要提示 |
|
|
169
|
+
| `low` 低 | 默认行为:优先读摘要,模型觉得必要时才读原文;不递归 |
|
|
170
|
+
| `medium` 中 | 走正常路线:年月 → 摘要 → 检索 → 原文;**不递归** |
|
|
171
|
+
| `high` 高 | 在中基础上**允许**模型主动递归检索,受 `recallBudgetRounds/Keywords` 预算约束(预算只是建议,插件不强制执行) |
|
|
172
|
+
| `forced` 强制 | 高 + **每条用户消息后都发一条检索指令**;没有归档时插件返回「归档记忆为空」,模型必须如实回答,不得编造;检索后简短说明回忆到了什么 |
|
|
173
|
+
|
|
174
|
+
补充规则:
|
|
175
|
+
|
|
176
|
+
- **启动时不注入**:重启后会话历史本身就在上下文里,“上一轮聊到哪”不需要摘要补;只在**压缩后**注入一次归档摘要。
|
|
177
|
+
- **压缩后只注入“去读哪份摘要”的指引(硬触发)**:监听 `session/event` 的 `compaction/end`;每次上下文压缩完成后,下轮请求组装前注入一条**简短指引**(不注入摘要全文),要求先调 `palace_summary(month=…)` 读最近年月的归档摘要,再继续。月份取自**实际存在摘要的最新月份**(不是日历当前月,跨年也不会指向空月份);该月摘要不足 500 token 时,指引会额外给出上一个月的调用方式。优先级:**系统压缩摘要优先,它没覆盖到的内容用归档摘要补充**。这条指引是普通上下文消息,下次压缩自然消失——**不会每轮重复强调**。
|
|
178
|
+
- **事件失效是静默的**:`session/event` 属于首方内部用法,DSH 升级若改名不会报错、只会不收事件。插件会在“最近事件”里记 `compaction-detected` 与 `recall-after-compaction`,页签看不到这两条就说明链路没通。
|
|
179
|
+
- **预算是提示不是硬约束**:递归由模型执行,插件只把上限写进指令;超了要模型自己收敛。
|
|
180
|
+
- **强制档有成本**:每条消息多一次检索往返,长会话里 token 消耗明显。
|
|
181
|
+
- 所有档位都**不改变归档行为**(`off` 也照常归档原文)。
|
|
182
|
+
|
|
183
|
+
## 可靠性
|
|
184
|
+
|
|
185
|
+
- **原子写**:归档文件、`.state.json`、`config.json` 都是先写临时文件再 rename。
|
|
186
|
+
- **数据优先**:归档先写文件、再写摘要、最后写状态(摘要失败时 state 未更新,重试不会变成“已归档过”);索引追加失败写进 `.pending-index.jsonl`,下次归档前自动补写;追加前会补换行,避免半行粘连。
|
|
187
|
+
- **边界语义**:保留边界 `boundary = 0` 表示“全部在保留区、暂不可归档”,不是“无限制”;且**永远不归档最后一轮**(可能正在生成)。
|
|
188
|
+
- **覆盖集以索引为准**:判断“已归档”时按 `index.jsonl` 里真实存在的行展开,不用 `archivedThroughTurn` 单点最大值,避免区间空洞被填平。
|
|
189
|
+
- **不重复**:覆盖去重(区间剔除)+ 同秒 `-2` 兜底。
|
|
190
|
+
- **索引容错**:读 `index.jsonl` 前会剥掉 UTF-8 BOM(旧文件可能带 BOM,按行 parse 会全失败)。
|
|
191
|
+
- **读取限制**:`palace_read` 只能读本命名空间(或 legacy 根)下的归档,`.state.json`/`config.json`/`index.jsonl`/`.namespaces.json` 直接拒绝。
|
|
192
|
+
- **可观测**:`.state.json` 记 `lastCheckTurn / lastCheckAt / lastPressure / lastArchiveRange`,`/palace status` 可见。
|
|
193
|
+
- **解码缓存**:按日志 `mtime + size` 缓存(最多 8 个会话)。
|
|
194
|
+
- **失败静默**:钩子内 catch,不阻塞对话。
|
|
195
|
+
|
|
196
|
+
## 依赖与失败策略
|
|
197
|
+
|
|
198
|
+
- 读 DSH 的 `.zstd` 日志需要 `fzstd`(多帧解压;Node zlib 只解第一帧)。
|
|
199
|
+
- 注册一个 systemPrompt 段(order 160)用于常驻检索指引,并注册一个压缩后注入的动态上下文;不监听 `agent/pre-step`,避免与其他插件抢接缝。
|
|
200
|
+
|
|
201
|
+
## 兼容性
|
|
202
|
+
|
|
203
|
+
- 需要 `ctx.tools.register`、`agent/turn-stopping`、`agent.steer`、`ctx.webServer.register`、`ctx.compaction`(可选)。
|
|
204
|
+
- 日志按 DSH v4(`session.v4.jsonl.zstd`)解析;格式变了会少条目而不是报错。
|
|
205
|
+
|
|
206
|
+
## 图片、文件与联网结果
|
|
207
|
+
|
|
208
|
+
- 图片/文件**不存字节**:只写一行占位符(`[图片 sha256:… 1024×768|…]`),删了附件也不会崩。
|
|
209
|
+
- 联网搜索结果按工具返回文本归档,单块截断 40 万字符;用户/助手文本与思考各截断 20 万字符。
|
|
210
|
+
|
|
211
|
+
## 安装
|
|
212
|
+
|
|
213
|
+
**从 npm 安装(推荐)**
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
dsh plugin --profile <profile> add @dafantong01/dsh-memory-palace
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
**从 GitHub 安装(钉 commit,避免拉到未发布的中间状态)**
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
dsh plugin --profile <profile> add github:dafantong01/dsh-memory-palace#<commit>
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
**从本地 tgz 安装**
|
|
226
|
+
|
|
227
|
+
```
|
|
228
|
+
dsh plugin --profile <profile> add ./dsh-memory-palace-0.1.0.tgz
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
装好后在 profile 的 `dsh.profile.bundles` 里确认有 `@dafantong01/dsh-memory-palace`,然后重启 DSH。
|
|
232
|
+
|
|
233
|
+
> pnpm 对 `file:` 依赖会做 junction;如果插件树激活失败,检查 `node_modules/@dafantong01/dsh-memory-palace` 是否是真实目录。
|
|
234
|
+
|
|
235
|
+
## 兼容性与权限
|
|
236
|
+
|
|
237
|
+
- **DSH 版本**:`engines.dsh` 为 `>=0.2.1-rc.2 <0.3.0-0`,低于或高于此范围时市场会标记不兼容。
|
|
238
|
+
- **运行时权限**:插件在你的 DSH 进程内运行,权限等同你本人:
|
|
239
|
+
- 读 `$DSH_HOME/sessions/` 下的会话日志(归档原文来源)
|
|
240
|
+
- 读写当前工作区的 `memory-palace/`
|
|
241
|
+
- 注册一个本地 HTTP 路由 `GET/POST /memory-palace-api`(仅本机回环)
|
|
242
|
+
- **不联网**:不请求任何外部服务,数据全部留在本机。
|
|
243
|
+
- **不存附件字节**:图片/文件只记占位符与哈希,原文件删除也不影响归档。
|
|
244
|
+
|
|
245
|
+
## 常见问题
|
|
246
|
+
|
|
247
|
+
- **页签显示「session not found」**:路由拿不到该会话的 `cwd`,通常是日志被移走。
|
|
248
|
+
- **`/palace status` 显示未开启**:工作区 `config.json` 缺 `enabled:true`。
|
|
249
|
+
- **一直不触发**:检查压力是否到阈值、可归档区间是否为空(近期内容都在保留区内)。
|
|
250
|
+
|
|
251
|
+
## 常驻提示词
|
|
252
|
+
|
|
253
|
+
插件常驻一段 systemPrompt(order 160,约 60 token):
|
|
254
|
+
|
|
255
|
+
> 本工作区有 memory-palace 归档。需要更早的细节、旧决定、被压缩掉的历史,或用户问起“之前说过什么”时,
|
|
256
|
+
> 先用 `palace_search` 定位,再用 `palace_read` 按轮次取原文;不要凭印象编造过去的事。
|
|
257
|
+
|
|
258
|
+
工具描述也配合写成“先用它定位”。
|
|
259
|
+
|
|
260
|
+
## 按类别分段归档
|
|
261
|
+
|
|
262
|
+
模型逐轮判断类别,用一次调用提交多段:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
palace_archive({
|
|
266
|
+
"segments": [
|
|
267
|
+
{ "category": "dsh-plugin-dev", "fromTurn": 112, "toTurn": 114 },
|
|
268
|
+
{ "category": "bug-report", "fromTurn": 115, "toTurn": 115 },
|
|
269
|
+
{ "category": "dsh-plugin-dev", "fromTurn": 116, "toTurn": 116 }
|
|
270
|
+
]
|
|
271
|
+
})
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- 同一类别拆多段 → 合并写进**一个文件**,轮次顺序不变
|
|
275
|
+
- 每个 turn 只能归一类,区间重叠会报错
|
|
276
|
+
- 文件头会写实际轮次:连续 `turn 113–114`,跳号 `turn 113, 114, 116, 128`(4 轮,不连续)
|
|
277
|
+
- 索引行对不连续区间额外记 `turns: [...]`
|
|
278
|
+
- 归档正文的轮次标题改用 `## TURN n · 时间`,避免正文里“引用了旧归档标题”时把 `palace_read` 的切片切错;老文件仍按 `## 第 n 轮 ·` 兼容解析
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## English
|
|
282
|
+
|
|
283
|
+
A DSH plugin that archives older conversation turns, verbatim, into a workspace-local `memory-palace/` directory before context compaction — then lets the model search and read them back later.
|
|
284
|
+
|
|
285
|
+
**Why**: DSH compacts long contexts; after compaction the original wording is gone. This plugin keeps it on disk, searchable.
|
|
286
|
+
|
|
287
|
+
**Install**
|
|
288
|
+
|
|
289
|
+
```
|
|
290
|
+
dsh plugin --profile <profile> add @dafantong01/dsh-memory-palace
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
or pin a commit:
|
|
294
|
+
|
|
295
|
+
```
|
|
296
|
+
dsh plugin --profile <profile> add github:dafantong01/dsh-memory-palace#<commit>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Restart DSH afterwards. Enable per session via the Memory Palace tab or `/palace on`.
|
|
300
|
+
|
|
301
|
+
**What it does**
|
|
302
|
+
|
|
303
|
+
- Archives turns by category when context pressure crosses a threshold; never rewrites or truncates text.
|
|
304
|
+
- Stores `memory-palace/<session>/<YYYY-MM>/_summary.md` plus `<category>/<file>.md`.
|
|
305
|
+
- Tools: `palace_archive`, `palace_summary`, `palace_search`, `palace_read`, `palace_turns`, `palace_config`.
|
|
306
|
+
- Five recall levels (`off` / `low` / `medium` / `high` / `forced`); after compaction it asks the model to read the latest month's archive summary.
|
|
307
|
+
- Per-session configuration; data stays in the workspace.
|
|
308
|
+
|
|
309
|
+
**Requirements**
|
|
310
|
+
|
|
311
|
+
- DSH `>=0.2.1-rc.2 <0.3.0-0`.
|
|
312
|
+
- Runs inside your DSH process with your permissions: reads local session logs under `$DSH_HOME/sessions/`, reads/writes `memory-palace/` in the current workspace, and registers a loopback HTTP route `/memory-palace-api`.
|
|
313
|
+
- No network calls, no attached-file bytes stored (placeholders with hashes only).
|
|
314
|
+
|
|
315
|
+
**License**: MIT.
|
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Bundle patch layer: loads this package's main entry as a host-only Cordis plugin.
|
|
2
|
+
# No client half yet: DSH renders no automatic config form for bundle rows.
|
|
3
|
+
#
|
|
4
|
+
# 开启方式(工作区级,fail-closed):
|
|
5
|
+
# - 全局 enabled 是总闸,默认开;
|
|
6
|
+
# - 但插件只在「工作区里有 memory-palace/config.json 且 enabled=true」时才动作;
|
|
7
|
+
# - 人和模型都能开:聊天框输入 /palace on,或调用 palace_config。
|
|
8
|
+
- insert:
|
|
9
|
+
- id: dsh-memory-palace
|
|
10
|
+
name: '@dafantong01/dsh-memory-palace'
|
|
11
|
+
config:
|
|
12
|
+
enabled: true
|