@max-null/dsh-memory 0.19.0 → 0.19.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/README.md +9 -6
- package/dist/backup.d.ts +29 -5
- package/dist/backup.js +107 -15
- package/dist/index.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,26 +106,29 @@ DSH 存储层的写入是「读—改—写全量覆盖」,且官方两个后
|
|
|
106
106
|
|
|
107
107
|
**未覆盖**:`workspace.json`(工作区登记)由 DSH 自己的 workspace 服务写,插件层够不着,仍会被双实例互相覆盖;根因处置在上游——[discussion #6882](https://github.com/deepseek-ai/deepseek-harness/discussions/6882)。当前防线是轮转快照备份(SSiD 侧 `shell/scripts/backup-storages.mjs` + 计划任务)。
|
|
108
108
|
|
|
109
|
-
## 定时备份(2026-10-18)
|
|
109
|
+
## 定时备份(2026-10-18;日记归档 2026-10-25)
|
|
110
110
|
|
|
111
111
|
**动机**:2026-10-18 的事故——`.dsh` 下的 storages 被外部整目录删除后,本插件的写入路径(读—改—写全量覆盖)在空磁盘上把内存里的 global 记忆覆盖成一条;而当时 `memory.json` **没有任何一份存在于 `.dsh` 之外**,无从恢复。
|
|
112
112
|
|
|
113
|
-
**机制**:插件启动后每小时(可配)把记忆文件**只读复制**到 `<home>/.dsh-backups/memory/<YYYYMMDD-HHmmss>/`,并写一份 `manifest.json` 记录来源与文件清单。**插件关闭(dispose)时再补一次**——定时器只在运行期间生效,关 DSH 前最后一段变更靠这一次兜住。保留最近 14
|
|
113
|
+
**机制**:插件启动后每小时(可配)把记忆文件**只读复制**到 `<home>/.dsh-backups/memory/<YYYYMMDD-HHmmss>/`,并写一份 `manifest.json` 记录来源与文件清单。**插件关闭(dispose)时再补一次**——定时器只在运行期间生效,关 DSH 前最后一段变更靠这一次兜住。保留最近 14 份(可配,**只作用于记忆快照**),超期自动清理。
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
**日记另走一条路**:`<DSH_HOME>/diary/*.md` 与记忆同一轮复制,但落在**固定目录** `~/.dsh-backups/memory/diary-archive/`,**只增不删**,不参与上面的轮转。两边的不对称是实的:记忆快照丢一份还有别的份(同一份数据的多个时间点副本),归档里每一篇都是日记的唯一副本,丢了没有任何地方能重建。日记那一侧改过就以日记为准覆盖(人可能在事后改过错字),归档里有而日记里已没有的篇目一律保留——那正是归档的全部价值。归档目录名不匹配轮转正则,因此不需要给清理逻辑开白名单。
|
|
116
|
+
|
|
117
|
+
**无界面**:这是行为类功能,不新增按钮、面板或设置项——行为效果即上面那段:部署后 `~/.dsh-backups/memory/` 下每小时多一个目录,目录里是当次复制出的 `memory.json`、`memory_project_*.json` 与 `manifest.json`;`diary-archive/` 里则按天累积 `YYYY-MM-DD.md`。
|
|
116
118
|
|
|
117
119
|
| 项 | 默认值 |
|
|
118
120
|
|---|---|
|
|
119
|
-
| 备份内容 | `memory.json`(global)+ `memory_project_*.json`(project
|
|
120
|
-
| 目标目录 |
|
|
121
|
+
| 备份内容 | `memory.json`(global)+ `memory_project_*.json`(project)+ `<DSH_HOME>/diary/*.md` |
|
|
122
|
+
| 目标目录 | 记忆:`~/.dsh-backups/memory/<时间戳>/`(**在 `DSH_HOME` 之外**);日记:`~/.dsh-backups/memory/diary-archive/` |
|
|
121
123
|
| 间隔 | 60 分钟(首次延迟 1 分钟);**插件关闭时另补一次** |
|
|
122
|
-
| 保留 |
|
|
124
|
+
| 保留 | 记忆:最近 14 份;日记:全部(只增不删) |
|
|
123
125
|
| 失败行为 | 只写日志,不影响记忆读写与启动 |
|
|
124
126
|
|
|
125
127
|
**边界**:
|
|
126
128
|
|
|
127
129
|
- 只备份**当前工作区**的 project 文件(`<cwd>/.dsh/storages/`);其它工作区的 project 记忆不在备份范围。
|
|
128
130
|
- 只读文件、只复制——**绝不复用引擎的写入路径**。写路径会先 `reload()` 再合并,磁盘缺失时它会把内存覆盖掉,正是这次事故的成因;备份不能走同一条路。
|
|
131
|
+
- 日记按**逐字节内容**判断是否已备份过,不按 mtime:复制与编辑都会动 mtime,内容才是事实。
|
|
129
132
|
|
|
130
133
|
## 使用流程(写入即生效,人为例外干预)
|
|
131
134
|
|
package/dist/backup.d.ts
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `DSH_HOME` 之外。
|
|
2
|
+
* 定时备份:把 `memory.json`(global)、`memory_project_*.json`(project)与
|
|
3
|
+
* `<DSH_HOME>/diary` 下的日记复制到 `DSH_HOME` 之外。
|
|
4
4
|
*
|
|
5
5
|
* 存在的理由(2026-10-18 事故):`.dsh` 下的 storages 被外部整目录删除后,dsh-memory 的
|
|
6
6
|
* 写入路径是「reload → 合并 → 写」——空磁盘让 reload 得到空库,写入把内存里的 global
|
|
7
7
|
* 记忆覆盖成一条;而当时 `memory.json` 没有任何一份存在于 `.dsh` 之外,无从恢复。
|
|
8
8
|
*
|
|
9
|
+
* 两类数据在同一个备份根下、用两种保留策略:记忆是**快照**(时间戳目录、只留最近 N 份),
|
|
10
|
+
* 日记是**归档**(固定目录、只增不删)——理由见 {@link archiveDiary}。
|
|
11
|
+
*
|
|
12
|
+
* 备份能力属于本插件而非宿主壳:上面两类数据都由本插件产生,换个宿主编排它同样该成立。
|
|
13
|
+
*
|
|
9
14
|
* 铁律:这里**只读文件、只复制**,绝不复用引擎的写入路径、不碰内存状态。备份失败只记
|
|
10
15
|
* 日志,绝不影响记忆本身的读写与启动。
|
|
11
16
|
*
|
|
@@ -28,6 +33,8 @@ export interface MemoryBackupOptions {
|
|
|
28
33
|
globalRoot: string;
|
|
29
34
|
/** project 记忆的根(`<cwd>/.dsh/storages`)。 */
|
|
30
35
|
projectRoot: string;
|
|
36
|
+
/** 日记的根(`$DSH_HOME/diary`);缺省不备份日记。 */
|
|
37
|
+
diaryRoot?: string;
|
|
31
38
|
/** 备份根目录。 */
|
|
32
39
|
targetRoot: string;
|
|
33
40
|
/** 保留份数(≥1)。 */
|
|
@@ -37,6 +44,13 @@ export interface MemoryBackupOptions {
|
|
|
37
44
|
/** 日志出口;缺省丢弃。 */
|
|
38
45
|
log?: (text: string) => void;
|
|
39
46
|
}
|
|
47
|
+
/** 日记归档的结果。 */
|
|
48
|
+
export interface DiaryArchiveRun {
|
|
49
|
+
/** 本次复制进归档的篇数(含内容变化后的覆盖)。 */
|
|
50
|
+
readonly copied: number;
|
|
51
|
+
/** 归档里已存在且与日记逐字节相同的篇数。 */
|
|
52
|
+
readonly kept: number;
|
|
53
|
+
}
|
|
40
54
|
/** 一次备份的结果。 */
|
|
41
55
|
export interface MemoryBackupRun {
|
|
42
56
|
/** 本次写入的目录;没有可备份文件时为 `null`。 */
|
|
@@ -45,18 +59,28 @@ export interface MemoryBackupRun {
|
|
|
45
59
|
readonly files: readonly string[];
|
|
46
60
|
/** 本次清理掉的旧目录名。 */
|
|
47
61
|
readonly pruned: readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* 日记归档的结果;入参没给 `diaryRoot` 时为 `undefined`(视为「不备份日记」)。
|
|
64
|
+
*
|
|
65
|
+
* 与 `files` 分开记:日记**不**落进本次的快照目录,它在固定的 `diary-archive/` 里
|
|
66
|
+
* 保持一份持续累积的副本,不做时间点切片。
|
|
67
|
+
*/
|
|
68
|
+
readonly diary?: DiaryArchiveRun;
|
|
48
69
|
/** 失败原因;成功时缺省。 */
|
|
49
70
|
readonly error?: string;
|
|
50
71
|
}
|
|
51
72
|
/**
|
|
52
|
-
*
|
|
73
|
+
* 备份一次:记忆走时间戳快照,日记走 {@link archiveDiary} 的累积归档。
|
|
53
74
|
*
|
|
54
75
|
* 目标目录按 `now` 命名;同名目录已存在时追加 `-1`、`-2`……每个文件独立复制,单个失败
|
|
55
76
|
* 只告警、不中断其余文件;`manifest.json` 记录本次来源与文件清单,便于事后核对。
|
|
56
77
|
* 没有可复制文件时返回 `dir: null`,不创建空目录。
|
|
57
78
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
79
|
+
* 日记归档先于「记忆文件是否存在」的判断执行:它落在固定的 `diary-archive/`,不依赖本次
|
|
80
|
+
* 快照目录,所以只剩日记可备份时照样该归档。
|
|
81
|
+
*
|
|
82
|
+
* @param options - 两个来源根、日记根、目标根、保留份数与时间注入点。
|
|
83
|
+
* @returns 本次目录、复制清单、清理清单、日记归档结果与失败原因。
|
|
60
84
|
*/
|
|
61
85
|
export declare function runMemoryBackup(options: MemoryBackupOptions): MemoryBackupRun;
|
|
62
86
|
/** 备份目录是否像一个已存在的备份树(测试与自检用)。 */
|
package/dist/backup.js
CHANGED
|
@@ -1,20 +1,40 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* `DSH_HOME` 之外。
|
|
2
|
+
* 定时备份:把 `memory.json`(global)、`memory_project_*.json`(project)与
|
|
3
|
+
* `<DSH_HOME>/diary` 下的日记复制到 `DSH_HOME` 之外。
|
|
4
4
|
*
|
|
5
5
|
* 存在的理由(2026-10-18 事故):`.dsh` 下的 storages 被外部整目录删除后,dsh-memory 的
|
|
6
6
|
* 写入路径是「reload → 合并 → 写」——空磁盘让 reload 得到空库,写入把内存里的 global
|
|
7
7
|
* 记忆覆盖成一条;而当时 `memory.json` 没有任何一份存在于 `.dsh` 之外,无从恢复。
|
|
8
8
|
*
|
|
9
|
+
* 两类数据在同一个备份根下、用两种保留策略:记忆是**快照**(时间戳目录、只留最近 N 份),
|
|
10
|
+
* 日记是**归档**(固定目录、只增不删)——理由见 {@link archiveDiary}。
|
|
11
|
+
*
|
|
12
|
+
* 备份能力属于本插件而非宿主壳:上面两类数据都由本插件产生,换个宿主编排它同样该成立。
|
|
13
|
+
*
|
|
9
14
|
* 铁律:这里**只读文件、只复制**,绝不复用引擎的写入路径、不碰内存状态。备份失败只记
|
|
10
15
|
* 日志,绝不影响记忆本身的读写与启动。
|
|
11
16
|
*
|
|
12
17
|
* @module dsh-memory/backup
|
|
13
18
|
*/
|
|
14
|
-
import { copyFileSync, existsSync, mkdirSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
19
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
15
20
|
import { join } from 'node:path';
|
|
16
21
|
/** 参与备份的名字:global 的 `memory.json` 与 project 的 `memory_project_*.json`。 */
|
|
17
22
|
const BACKUP_FILE = /^(?:memory\.json|memory_project_[A-Za-z0-9_-]+\.json)$/u;
|
|
23
|
+
/** 参与归档的日记篇目:与 `diary.ts` 的按天分文件一致(`YYYY-MM-DD.md`)。 */
|
|
24
|
+
const DIARY_FILE = /^\d{4}-\d{2}-\d{2}\.md$/u;
|
|
25
|
+
/**
|
|
26
|
+
* 日记归档目录名,固定在备份根下。
|
|
27
|
+
*
|
|
28
|
+
* 之所以是固定名而不是时间戳:它**必须不匹配** {@link pruneBackups} 的
|
|
29
|
+
* `^\d{8}-\d{6}(?:-\d+)?$`。记忆快照丢一份还有别的份(同一份数据的多个时间点副本),
|
|
30
|
+
* 日记归档里每一篇都是**唯一**的备份——丢了没有任何地方能重建它。只增不删因此是硬要求,
|
|
31
|
+
* 而靠这个名字天然躲开轮转,不必再给清理逻辑开白名单。
|
|
32
|
+
*/
|
|
33
|
+
const DIARY_ARCHIVE_DIR = 'diary-archive';
|
|
34
|
+
/** 从 `unknown` 错误里取可读信息(`catch` 绑定的类型是 `unknown`)。 */
|
|
35
|
+
function reason(error) {
|
|
36
|
+
return error instanceof Error ? error.message : String(error);
|
|
37
|
+
}
|
|
18
38
|
/** 备份目录名:`YYYYMMDD-HHmmss`,可排序、可读。 */
|
|
19
39
|
function stamp(now) {
|
|
20
40
|
const pad = (value) => String(value).padStart(2, '0');
|
|
@@ -29,29 +49,93 @@ function candidateFiles(root, log) {
|
|
|
29
49
|
return readdirSync(root).filter((name) => BACKUP_FILE.test(name));
|
|
30
50
|
}
|
|
31
51
|
catch (error) {
|
|
32
|
-
log(`memory backup: 无法读取 ${root}: ${
|
|
52
|
+
log(`memory backup: 无法读取 ${root}: ${reason(error)}`);
|
|
33
53
|
return [];
|
|
34
54
|
}
|
|
35
55
|
}
|
|
36
56
|
/**
|
|
37
|
-
*
|
|
57
|
+
* 把日记镜像进 `<targetRoot>/diary-archive/`。
|
|
58
|
+
*
|
|
59
|
+
* 保留策略与记忆快照相反:快照按时间戳分目录、只留最近 `keep` 份;归档**只增不删**,
|
|
60
|
+
* 理由见 {@link DIARY_ARCHIVE_DIR}。
|
|
61
|
+
*
|
|
62
|
+
* 三档处理:
|
|
63
|
+
* - 归档里没有 → 复制,计入 `copied`
|
|
64
|
+
* - 两边逐字节相同 → 跳过,计入 `kept`(绝大多数情况:日记写到归档之间没有变化)
|
|
65
|
+
* - 不同 → **以日记为准覆盖**,计入 `copied`:人可能在事后改过错字,日记那一侧才是当前的;
|
|
66
|
+
* 归档不是版本库,它只为「原件没了」这一件事存在
|
|
67
|
+
*
|
|
68
|
+
* 归档里独有、日记里已经没有的篇目**一律保留**——那正是归档的全部价值。
|
|
69
|
+
* 逐篇独立处理,单篇失败只告警、不抛出(模块头的铁律)。
|
|
70
|
+
*
|
|
71
|
+
* @param diaryRoot - 日记根(`$DSH_HOME/diary`)。
|
|
72
|
+
* @param targetRoot - 备份根;归档落在其下的 `diary-archive/`。
|
|
73
|
+
* @param log - 日志出口。
|
|
74
|
+
* @returns 写入与跳过的篇数;没有日记可归档时两项都是 0,且不创建任何目录。
|
|
75
|
+
*/
|
|
76
|
+
function archiveDiary(diaryRoot, targetRoot, log) {
|
|
77
|
+
const nothing = { copied: 0, kept: 0 };
|
|
78
|
+
if (!existsSync(diaryRoot))
|
|
79
|
+
return nothing;
|
|
80
|
+
let names;
|
|
81
|
+
try {
|
|
82
|
+
names = readdirSync(diaryRoot).filter((name) => DIARY_FILE.test(name));
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
log(`memory backup: 无法读取日记目录 ${diaryRoot}: ${reason(error)}`);
|
|
86
|
+
return nothing;
|
|
87
|
+
}
|
|
88
|
+
if (names.length === 0)
|
|
89
|
+
return nothing;
|
|
90
|
+
const archiveDir = join(targetRoot, DIARY_ARCHIVE_DIR);
|
|
91
|
+
let copied = 0;
|
|
92
|
+
let kept = 0;
|
|
93
|
+
for (const name of names) {
|
|
94
|
+
const from = join(diaryRoot, name);
|
|
95
|
+
const to = join(archiveDir, name);
|
|
96
|
+
try {
|
|
97
|
+
// 比字节而不是比 mtime:复制与编辑都会动 mtime,内容才是「是否已经备份过」的事实。
|
|
98
|
+
if (existsSync(to) && readFileSync(from).equals(readFileSync(to))) {
|
|
99
|
+
kept += 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
mkdirSync(archiveDir, { recursive: true });
|
|
103
|
+
copyFileSync(from, to);
|
|
104
|
+
copied += 1;
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
log(`memory backup: 归档日记 ${name} 失败: ${reason(error)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { copied, kept };
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* 备份一次:记忆走时间戳快照,日记走 {@link archiveDiary} 的累积归档。
|
|
38
114
|
*
|
|
39
115
|
* 目标目录按 `now` 命名;同名目录已存在时追加 `-1`、`-2`……每个文件独立复制,单个失败
|
|
40
116
|
* 只告警、不中断其余文件;`manifest.json` 记录本次来源与文件清单,便于事后核对。
|
|
41
117
|
* 没有可复制文件时返回 `dir: null`,不创建空目录。
|
|
42
118
|
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
119
|
+
* 日记归档先于「记忆文件是否存在」的判断执行:它落在固定的 `diary-archive/`,不依赖本次
|
|
120
|
+
* 快照目录,所以只剩日记可备份时照样该归档。
|
|
121
|
+
*
|
|
122
|
+
* @param options - 两个来源根、日记根、目标根、保留份数与时间注入点。
|
|
123
|
+
* @returns 本次目录、复制清单、清理清单、日记归档结果与失败原因。
|
|
45
124
|
*/
|
|
46
125
|
export function runMemoryBackup(options) {
|
|
47
126
|
const log = options.log ?? (() => { });
|
|
48
127
|
const now = options.now ?? new Date();
|
|
128
|
+
const diary = options.diaryRoot === undefined
|
|
129
|
+
? undefined
|
|
130
|
+
: archiveDiary(options.diaryRoot, options.targetRoot, log);
|
|
49
131
|
const globalFiles = candidateFiles(options.globalRoot, log).map(name => ({ name, from: join(options.globalRoot, name) }));
|
|
50
132
|
const projectFiles = candidateFiles(options.projectRoot, log).map(name => ({ name, from: join(options.projectRoot, name) }));
|
|
51
133
|
const sources = [...globalFiles, ...projectFiles];
|
|
52
134
|
if (sources.length === 0) {
|
|
53
135
|
log('memory backup: 没有可备份的记忆文件');
|
|
54
|
-
return
|
|
136
|
+
return diary === undefined
|
|
137
|
+
? { dir: null, files: [], pruned: [] }
|
|
138
|
+
: { dir: null, files: [], pruned: [], diary };
|
|
55
139
|
}
|
|
56
140
|
let base = join(options.targetRoot, stamp(now));
|
|
57
141
|
let dir = base;
|
|
@@ -61,7 +145,7 @@ export function runMemoryBackup(options) {
|
|
|
61
145
|
mkdirSync(dir, { recursive: true });
|
|
62
146
|
}
|
|
63
147
|
catch (error) {
|
|
64
|
-
const message =
|
|
148
|
+
const message = reason(error);
|
|
65
149
|
log(`memory backup: 无法创建 ${dir}: ${message}`);
|
|
66
150
|
return { dir: null, files: [], pruned: [], error: message };
|
|
67
151
|
}
|
|
@@ -72,7 +156,7 @@ export function runMemoryBackup(options) {
|
|
|
72
156
|
files.push(source.name);
|
|
73
157
|
}
|
|
74
158
|
catch (error) {
|
|
75
|
-
log(`memory backup: 复制 ${source.name} 失败: ${
|
|
159
|
+
log(`memory backup: 复制 ${source.name} 失败: ${reason(error)}`);
|
|
76
160
|
}
|
|
77
161
|
}
|
|
78
162
|
try {
|
|
@@ -81,14 +165,22 @@ export function runMemoryBackup(options) {
|
|
|
81
165
|
globalRoot: options.globalRoot,
|
|
82
166
|
projectRoot: options.projectRoot,
|
|
83
167
|
files,
|
|
168
|
+
// 日记不在本次快照目录里,manifest 只记「这一轮归档做了什么」,不列篇名。
|
|
169
|
+
...(diary === undefined ? {} : { diary: { root: options.diaryRoot, ...diary } }),
|
|
84
170
|
}, null, 2)}\n`, 'utf8');
|
|
85
171
|
}
|
|
86
172
|
catch (error) {
|
|
87
|
-
log(`memory backup: 写 manifest 失败: ${
|
|
173
|
+
log(`memory backup: 写 manifest 失败: ${reason(error)}`);
|
|
88
174
|
}
|
|
89
|
-
|
|
175
|
+
const pruned = pruneBackups(options.targetRoot, options.keep, log);
|
|
176
|
+
return diary === undefined ? { dir, files, pruned } : { dir, files, pruned, diary };
|
|
90
177
|
}
|
|
91
|
-
/**
|
|
178
|
+
/**
|
|
179
|
+
* 只保留最近 `keep` 份时间戳快照目录(按名字排序即时间序),多余的删除。
|
|
180
|
+
*
|
|
181
|
+
* 只认 `YYYYMMDD-HHmmss`(含 `-1` 这类冲突后缀):`diary-archive` 等固定名目录因此永远
|
|
182
|
+
* 不在清理范围内——日记只增不删是硬要求,不是配置项。
|
|
183
|
+
*/
|
|
92
184
|
function pruneBackups(targetRoot, keep, log) {
|
|
93
185
|
if (!existsSync(targetRoot))
|
|
94
186
|
return [];
|
|
@@ -97,7 +189,7 @@ function pruneBackups(targetRoot, keep, log) {
|
|
|
97
189
|
entries = readdirSync(targetRoot).filter((name) => /^\d{8}-\d{6}(?:-\d+)?$/u.test(name));
|
|
98
190
|
}
|
|
99
191
|
catch (error) {
|
|
100
|
-
log(`memory backup: 无法读取 ${targetRoot}: ${
|
|
192
|
+
log(`memory backup: 无法读取 ${targetRoot}: ${reason(error)}`);
|
|
101
193
|
return [];
|
|
102
194
|
}
|
|
103
195
|
entries.sort();
|
|
@@ -112,7 +204,7 @@ function pruneBackups(targetRoot, keep, log) {
|
|
|
112
204
|
pruned.push(name);
|
|
113
205
|
}
|
|
114
206
|
catch (error) {
|
|
115
|
-
log(`memory backup: 清理 ${name} 失败: ${
|
|
207
|
+
log(`memory backup: 清理 ${name} 失败: ${reason(error)}`);
|
|
116
208
|
}
|
|
117
209
|
}
|
|
118
210
|
return pruned;
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { runMemoryBackup } from "./backup.js";
|
|
|
13
13
|
import { ANCHOR_KINDS } from "./anchors.js";
|
|
14
14
|
import { mountMemoryApi } from "./routes.js";
|
|
15
15
|
import { SELF_DESCRIPTION } from "./self.js";
|
|
16
|
-
import { appendEntry, lastWriteAt, pointerLine, readDay, recentDates } from "./diary.js";
|
|
16
|
+
import { appendEntry, diaryDir, lastWriteAt, pointerLine, readDay, recentDates } from "./diary.js";
|
|
17
17
|
import z from '@deepseek-ai/schemastery';
|
|
18
18
|
import { DEFAULT_INJECTION_BUDGET, DEFAULT_SUMMARY_CHARS, candidateNotice, indexNotice, neighborNotice, neutralizeBraces, omittedNotice, renderInjection, staleNotice } from "./injection.js";
|
|
19
19
|
export { MemoryEngine } from "./engine.js";
|
|
@@ -992,6 +992,8 @@ export async function apply(ctx, config) {
|
|
|
992
992
|
const roots = {
|
|
993
993
|
globalRoot: config?.globalRoot ?? join(process.env.DSH_HOME ?? join(homedir(), '.dsh'), 'storages'),
|
|
994
994
|
projectRoot: config?.projectRoot ?? join(process.cwd(), '.dsh', 'storages'),
|
|
995
|
+
// 日记在 `storages` 之外,得单独给根——漏了这一项就等于「备份记忆、不备份日记」。
|
|
996
|
+
diaryRoot: diaryDir(),
|
|
995
997
|
};
|
|
996
998
|
ctx.effect(() => {
|
|
997
999
|
const first = setTimeout(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@max-null/dsh-memory",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.1",
|
|
4
4
|
"description": "Cross-session plaintext memory plugin for the DeepSeek Harness — deterministic BM25 recall, writes take effect immediately, signal-driven injection, no embeddings",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|