@max-null/dsh-memory 0.7.0 → 0.7.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 +12 -0
- package/dist/engine.d.ts +43 -0
- package/dist/engine.js +120 -4
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -55,6 +55,16 @@ npm install @max-null/dsh-memory
|
|
|
55
55
|
|
|
56
56
|
两个根都可用 config 覆盖(`globalRoot` / `projectRoot`)。`memory_list` / `memory_search` 不带 `namespace` 过滤时会同时查两层。旧版双重前缀文件名(`memory_project_memory_project_<hash>.json`)在打开时自动迁移为规范名。
|
|
57
57
|
|
|
58
|
+
## 多实例共存(0.7.1)
|
|
59
|
+
|
|
60
|
+
两个 DSH 实例(例如 DSH web 与 SSiD 桌面壳)可以同时运行、共用同一个 `DSH_HOME`,**记忆不再互相抹掉**。
|
|
61
|
+
|
|
62
|
+
DSH 存储层的写入是「读—改—写全量覆盖」,且官方两个后端都声明不做跨进程协调(`storage-json`:*writer per process and last-write-wins is correct*;`storage-sqlite`:*cross-process coordination is out of scope*)。插件层的处置是**写前重读**(等价于 update 前先 select):每次写入在串行区里先比对存储文件指纹(mtime + size),发现磁盘被别的实例动过就先重载再写。代价是磁盘没变时的一次 `statSync`。
|
|
63
|
+
|
|
64
|
+
残余窗口只剩两个实例在**同一瞬间**写——此时后写者赢;同时写**不同**记忆已不再互相影响。
|
|
65
|
+
|
|
66
|
+
**未覆盖**:`workspace.json`(工作区登记)由 DSH 自己的 workspace 服务写,插件层够不着,仍会被双实例互相覆盖;根因处置在上游——[discussion #6882](https://github.com/deepseek-ai/deepseek-harness/discussions/6882)。当前防线是轮转快照备份(SSiD 侧 `shell/scripts/backup-storages.mjs` + 计划任务)。
|
|
67
|
+
|
|
58
68
|
## 使用流程(写入即生效,人为例外干预)
|
|
59
69
|
|
|
60
70
|
提示词模板库(0.6.0):`prompt_search / prompt_get / prompt_list / prompt_add` 四个工具管理**模板库**——
|
|
@@ -102,6 +112,8 @@ memory_forget → 随时删除(删除始终是人的动作)
|
|
|
102
112
|
|
|
103
113
|
## SSID 系列
|
|
104
114
|
|
|
115
|
+
本插件是 **[SSID(思灵 · Seek Soul in Darkness)](https://github.com/Max-Null/seek-soul-in-darkness)** 全家桶的一员;也可以单独安装到任意 DSH profile——除设置页的记忆面板外不依赖其它同系列插件。
|
|
116
|
+
|
|
105
117
|
## 开发
|
|
106
118
|
|
|
107
119
|
```bash
|
package/dist/engine.d.ts
CHANGED
|
@@ -222,6 +222,10 @@ export declare class MemoryEngine extends Service {
|
|
|
222
222
|
/** backend 只注册一次(registry 重名抛 duplicate;reload 清缓存后不得重复注册)。 */
|
|
223
223
|
private registeredProjectBackends;
|
|
224
224
|
private facility?;
|
|
225
|
+
/** 各存储文件上次被我们确认过的指纹;用于识别「另一个实例写过」。 */
|
|
226
|
+
private readonly storeStamps;
|
|
227
|
+
/** 写入串行链:「刷新—读—改—写」必须整体互斥,否则并发写会在其间穿插。 */
|
|
228
|
+
private writeChain;
|
|
225
229
|
/** 会话启动维护只跑一次的标记(2026-09-15)。 */
|
|
226
230
|
private maintenanceDone;
|
|
227
231
|
constructor(ctx: import('@deepseek-ai/cordis').Context, config?: MemoryConfig);
|
|
@@ -248,7 +252,30 @@ export declare class MemoryEngine extends Service {
|
|
|
248
252
|
* 命中危险内容规则则隔离:`status` 留 `suggested`(天然不注入)**并且**置
|
|
249
253
|
* `quarantined`(不进检索),双保险,等待人工放行或删除。
|
|
250
254
|
*/
|
|
255
|
+
/** 文件指纹:mtime + size(size 兜住同一毫秒内的两次写)。 */
|
|
256
|
+
private static fileStamp;
|
|
257
|
+
/** 全局记忆的存储文件(JsonStorageBackend 的 unit 文件名 = domain 名)。 */
|
|
258
|
+
private globalStoreFile;
|
|
259
|
+
/** 某工作区记忆的存储文件。 */
|
|
260
|
+
private projectStoreFile;
|
|
261
|
+
/** 记录当前指纹,作为「这就是我们造成的状态」的基准(写入完成后调用)。 */
|
|
262
|
+
private noteStoreStamps;
|
|
263
|
+
/**
|
|
264
|
+
* 写前新鲜度门:存储文件被外部改过就重载,避免整份覆盖吃掉对方的写入。
|
|
265
|
+
* 必须在取表**之前**调用——{@link reload} 重开 facility,此前取到的 table 引用会失效。
|
|
266
|
+
*/
|
|
267
|
+
private refreshForWrite;
|
|
268
|
+
/**
|
|
269
|
+
* 一次写入的完整作用域:串行化 → 新鲜度门 → 执行 → 记指纹。
|
|
270
|
+
* 刷新与「读—改—写」必须同处一个互斥区,否则并发写会在两者之间穿插。
|
|
271
|
+
* @param projectCwd - 本次写入涉及的工作区;无则只覆盖全局存储。
|
|
272
|
+
* @param run - 实际的读改写;必须在内部重新取表(刷新后旧引用失效)。
|
|
273
|
+
* @returns `run` 的返回值。
|
|
274
|
+
*/
|
|
275
|
+
private writeScope;
|
|
251
276
|
remember(input: MemoryWrite, projectCwd?: string): Promise<MemoryRecord>;
|
|
277
|
+
/** {@link remember} 的实现体;必须经 `writeScope` 进入(多实例写入协调)。 */
|
|
278
|
+
private rememberInner;
|
|
252
279
|
list(filter?: MemoryFilter, projectCwd?: string): Promise<MemoryRecord[]>;
|
|
253
280
|
/**
|
|
254
281
|
* 关键词检索。每次成功返回的检索都记一条查询日志(⑥ 2026-09-15)——命中与否都记:
|
|
@@ -283,6 +310,8 @@ export declare class MemoryEngine extends Service {
|
|
|
283
310
|
private tableOf;
|
|
284
311
|
/** 命中标记:写回 lastUsedAt 与命中次数,达标即自动升常驻(2026-09-15)。 */
|
|
285
312
|
private markUsed;
|
|
313
|
+
/** {@link markUsed} 的实现体;必须经 `writeScope` 进入(`recordUse` 在此之内)。 */
|
|
314
|
+
private markUsedInner;
|
|
286
315
|
/**
|
|
287
316
|
* 命中记账 + 自动升级(2026-09-15 静默记忆机制):命中次数达阈值即自动打开常驻
|
|
288
317
|
* 注入——「被反复检索命中」是它值得每轮付费的唯一客观证据。统计性字段不 emit,
|
|
@@ -331,6 +360,8 @@ export declare class MemoryEngine extends Service {
|
|
|
331
360
|
* @returns 被撤下的条数(诊断与日志用)。
|
|
332
361
|
*/
|
|
333
362
|
demoteStale(projectCwd?: string, now?: number): Promise<number>;
|
|
363
|
+
/** {@link demoteStale} 的实现体;必须经 `writeScope` 进入。 */
|
|
364
|
+
private demoteStaleInner;
|
|
334
365
|
/**
|
|
335
366
|
* 锚点批量校验(2026-09-15):会话启动时调一次,把失效的条目标成 `stale`。
|
|
336
367
|
*
|
|
@@ -343,6 +374,8 @@ export declare class MemoryEngine extends Service {
|
|
|
343
374
|
* @returns 本次状态发生变化的条数。
|
|
344
375
|
*/
|
|
345
376
|
verifyAnchors(probes: AnchorProbes, projectCwd?: string): Promise<number>;
|
|
377
|
+
/** {@link verifyAnchors} 的实现体;必须经 `writeScope` 进入。 */
|
|
378
|
+
private verifyAnchorsInner;
|
|
346
379
|
/**
|
|
347
380
|
* 旧数据迁移(2026-09-15 静默记忆机制):一次性、幂等,会话启动维护时调用。
|
|
348
381
|
*
|
|
@@ -363,13 +396,21 @@ export declare class MemoryEngine extends Service {
|
|
|
363
396
|
quarantined: number;
|
|
364
397
|
counted: number;
|
|
365
398
|
}>;
|
|
399
|
+
/** {@link migrateLegacy} 的实现体;必须经 `writeScope` 进入。 */
|
|
400
|
+
private migrateLegacyInner;
|
|
366
401
|
forget(id: MemoryId, projectCwd?: string): Promise<boolean>;
|
|
402
|
+
/** {@link forget} 的实现体;必须经 `writeScope` 进入。 */
|
|
403
|
+
private forgetInner;
|
|
367
404
|
setStatus(id: MemoryId, status: MemoryStatus, projectCwd?: string): Promise<MemoryRecord>;
|
|
405
|
+
/** {@link setStatus} 的实现体;必须经 `writeScope` 进入。 */
|
|
406
|
+
private setStatusInner;
|
|
368
407
|
/**
|
|
369
408
|
* 注入维度开关(0.3.0):只改 injected,不动审核状态。供 UI 面板
|
|
370
409
|
* 「常驻注入」开关调用(remote.setInjected)。
|
|
371
410
|
*/
|
|
372
411
|
setInjected(id: MemoryId, injected: boolean, projectCwd?: string): Promise<MemoryRecord>;
|
|
412
|
+
/** {@link setInjected} 的实现体;必须经 `writeScope` 进入。 */
|
|
413
|
+
private setInjectedInner;
|
|
373
414
|
/**
|
|
374
415
|
* 修改一条记忆的内容/关键词(0.3.1 整理记忆用)。2026-09-15 起:改动**不再**
|
|
375
416
|
* 退回待审核——静默机制下记忆由系统与模型自行维护,更新即保持原有审核状态;
|
|
@@ -379,6 +420,8 @@ export declare class MemoryEngine extends Service {
|
|
|
379
420
|
content?: string;
|
|
380
421
|
keywords?: string[];
|
|
381
422
|
}, projectCwd?: string): Promise<MemoryRecord>;
|
|
423
|
+
/** {@link update} 的实现体;必须经 `writeScope` 进入。 */
|
|
424
|
+
private updateInner;
|
|
382
425
|
/**
|
|
383
426
|
* 预热某工作区的 project 表(异步打开并缓存)。system prompt 的
|
|
384
427
|
* `memory:recall` provider 是同步回调——只读已打开的表;预热让
|
package/dist/engine.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* is always created `suggested` and becomes effective only through `setStatus`.
|
|
6
6
|
*/
|
|
7
7
|
import { randomUUID } from 'node:crypto';
|
|
8
|
-
import { existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { existsSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { homedir } from 'node:os';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { Service } from '@deepseek-ai/cordis';
|
|
@@ -170,6 +170,10 @@ export class MemoryEngine extends Service {
|
|
|
170
170
|
/** backend 只注册一次(registry 重名抛 duplicate;reload 清缓存后不得重复注册)。 */
|
|
171
171
|
registeredProjectBackends = new Set();
|
|
172
172
|
facility;
|
|
173
|
+
/** 各存储文件上次被我们确认过的指纹;用于识别「另一个实例写过」。 */
|
|
174
|
+
storeStamps = new Map();
|
|
175
|
+
/** 写入串行链:「刷新—读—改—写」必须整体互斥,否则并发写会在其间穿插。 */
|
|
176
|
+
writeChain = Promise.resolve();
|
|
173
177
|
/** 会话启动维护只跑一次的标记(2026-09-15)。 */
|
|
174
178
|
maintenanceDone = false;
|
|
175
179
|
constructor(ctx, config = {}) {
|
|
@@ -273,7 +277,87 @@ export class MemoryEngine extends Service {
|
|
|
273
277
|
* 命中危险内容规则则隔离:`status` 留 `suggested`(天然不注入)**并且**置
|
|
274
278
|
* `quarantined`(不进检索),双保险,等待人工放行或删除。
|
|
275
279
|
*/
|
|
280
|
+
// ── 多实例写入协调(2026-09-17)────────────────────────────────────────
|
|
281
|
+
//
|
|
282
|
+
// 另一个 DSH 实例(web 与 SSiD 同时运行)共用同一个 DSH_HOME 时,它写的记忆只落在
|
|
283
|
+
// 磁盘上;我们的内存态若比磁盘旧,下一次整份覆盖就会把对方的新记忆抹掉。DSH 的
|
|
284
|
+
// 存储层明确不做跨进程协调(storage-json「writer per process and last-write-wins
|
|
285
|
+
// is correct」、storage-sqlite「cross-process coordination is out of scope」),
|
|
286
|
+
// 所以协调放在这里:开写前比对存储文件指纹,变过就重载——等价于「update 前先
|
|
287
|
+
// select」。残余窗口只剩两个实例真正同时写,而记忆写入是低频操作。
|
|
288
|
+
/** 文件指纹:mtime + size(size 兜住同一毫秒内的两次写)。 */
|
|
289
|
+
static fileStamp(file) {
|
|
290
|
+
try {
|
|
291
|
+
const stat = statSync(file);
|
|
292
|
+
return `${stat.mtimeMs}:${stat.size}`;
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
return 'missing';
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
/** 全局记忆的存储文件(JsonStorageBackend 的 unit 文件名 = domain 名)。 */
|
|
299
|
+
globalStoreFile() {
|
|
300
|
+
return join(this.config.globalRoot ?? globalRoot(), 'memory.json');
|
|
301
|
+
}
|
|
302
|
+
/** 某工作区记忆的存储文件。 */
|
|
303
|
+
projectStoreFile(projectCwd) {
|
|
304
|
+
const key = join(projectCwd);
|
|
305
|
+
return join(this.projectRootFor(key), `${this.projectBackendName(key)}.json`);
|
|
306
|
+
}
|
|
307
|
+
/** 记录当前指纹,作为「这就是我们造成的状态」的基准(写入完成后调用)。 */
|
|
308
|
+
noteStoreStamps(projectCwd) {
|
|
309
|
+
const global = this.globalStoreFile();
|
|
310
|
+
this.storeStamps.set(global, MemoryEngine.fileStamp(global));
|
|
311
|
+
const scopes = new Set(this.projectTables.keys());
|
|
312
|
+
if (projectCwd !== undefined && projectCwd !== '')
|
|
313
|
+
scopes.add(join(projectCwd));
|
|
314
|
+
for (const key of scopes) {
|
|
315
|
+
const file = this.projectStoreFile(key);
|
|
316
|
+
this.storeStamps.set(file, MemoryEngine.fileStamp(file));
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* 写前新鲜度门:存储文件被外部改过就重载,避免整份覆盖吃掉对方的写入。
|
|
321
|
+
* 必须在取表**之前**调用——{@link reload} 重开 facility,此前取到的 table 引用会失效。
|
|
322
|
+
*/
|
|
323
|
+
async refreshForWrite(projectCwd) {
|
|
324
|
+
if (this.storeStamps.size === 0) {
|
|
325
|
+
this.noteStoreStamps(projectCwd);
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const files = [this.globalStoreFile()];
|
|
329
|
+
if (projectCwd !== undefined && projectCwd !== '')
|
|
330
|
+
files.push(this.projectStoreFile(projectCwd));
|
|
331
|
+
for (const file of files) {
|
|
332
|
+
if (MemoryEngine.fileStamp(file) === this.storeStamps.get(file))
|
|
333
|
+
continue;
|
|
334
|
+
await this.reload();
|
|
335
|
+
this.noteStoreStamps(projectCwd);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* 一次写入的完整作用域:串行化 → 新鲜度门 → 执行 → 记指纹。
|
|
341
|
+
* 刷新与「读—改—写」必须同处一个互斥区,否则并发写会在两者之间穿插。
|
|
342
|
+
* @param projectCwd - 本次写入涉及的工作区;无则只覆盖全局存储。
|
|
343
|
+
* @param run - 实际的读改写;必须在内部重新取表(刷新后旧引用失效)。
|
|
344
|
+
* @returns `run` 的返回值。
|
|
345
|
+
*/
|
|
346
|
+
writeScope(projectCwd, run) {
|
|
347
|
+
const task = this.writeChain.then(async () => {
|
|
348
|
+
await this.refreshForWrite(projectCwd);
|
|
349
|
+
const result = await run();
|
|
350
|
+
this.noteStoreStamps(projectCwd);
|
|
351
|
+
return result;
|
|
352
|
+
});
|
|
353
|
+
this.writeChain = task.then(() => undefined, () => undefined);
|
|
354
|
+
return task;
|
|
355
|
+
}
|
|
276
356
|
async remember(input, projectCwd) {
|
|
357
|
+
return this.writeScope(projectCwd, () => this.rememberInner(input, projectCwd));
|
|
358
|
+
}
|
|
359
|
+
/** {@link remember} 的实现体;必须经 `writeScope` 进入(多实例写入协调)。 */
|
|
360
|
+
async rememberInner(input, projectCwd) {
|
|
277
361
|
const namespace = input.namespace ?? 'global';
|
|
278
362
|
const table = namespace === 'project'
|
|
279
363
|
? await this.projectTableFor(projectCwd)
|
|
@@ -479,13 +563,17 @@ export class MemoryEngine extends Service {
|
|
|
479
563
|
}
|
|
480
564
|
/** 命中标记:写回 lastUsedAt 与命中次数,达标即自动升常驻(2026-09-15)。 */
|
|
481
565
|
async markUsed(ids, projectCwd) {
|
|
566
|
+
return this.writeScope(projectCwd, () => this.markUsedInner(ids, projectCwd));
|
|
567
|
+
}
|
|
568
|
+
/** {@link markUsed} 的实现体;必须经 `writeScope` 进入(`recordUse` 在此之内)。 */
|
|
569
|
+
async markUsedInner(ids, projectCwd) {
|
|
482
570
|
if (ids.length === 0)
|
|
483
571
|
return;
|
|
484
572
|
const now = Date.now();
|
|
485
573
|
const global = this.requireTable('global');
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
574
|
+
// 取表走异步版:`writeScope` 可能在本次进入前刚 `reload()`(外部实例改过存储),
|
|
575
|
+
// 那时同步缓存已被清空——继续读缓存会把 project 侧的命中静默漏记。
|
|
576
|
+
const project = await this.projectTableFor(projectCwd);
|
|
489
577
|
for (const id of ids) {
|
|
490
578
|
const globalBlock = global.get(id);
|
|
491
579
|
if (globalBlock !== undefined) {
|
|
@@ -625,6 +713,10 @@ export class MemoryEngine extends Service {
|
|
|
625
713
|
* @returns 被撤下的条数(诊断与日志用)。
|
|
626
714
|
*/
|
|
627
715
|
async demoteStale(projectCwd, now = Date.now()) {
|
|
716
|
+
return this.writeScope(projectCwd, () => this.demoteStaleInner(projectCwd, now));
|
|
717
|
+
}
|
|
718
|
+
/** {@link demoteStale} 的实现体;必须经 `writeScope` 进入。 */
|
|
719
|
+
async demoteStaleInner(projectCwd, now = Date.now()) {
|
|
628
720
|
const threshold = now - AUTO_DEMOTE_DAYS * 24 * 60 * 60 * 1000;
|
|
629
721
|
const tables = [this.requireTable('global')];
|
|
630
722
|
if (projectCwd !== undefined && projectCwd !== '') {
|
|
@@ -664,6 +756,10 @@ export class MemoryEngine extends Service {
|
|
|
664
756
|
* @returns 本次状态发生变化的条数。
|
|
665
757
|
*/
|
|
666
758
|
async verifyAnchors(probes, projectCwd) {
|
|
759
|
+
return this.writeScope(projectCwd, () => this.verifyAnchorsInner(probes, projectCwd));
|
|
760
|
+
}
|
|
761
|
+
/** {@link verifyAnchors} 的实现体;必须经 `writeScope` 进入。 */
|
|
762
|
+
async verifyAnchorsInner(probes, projectCwd) {
|
|
667
763
|
const tables = [this.requireTable('global')];
|
|
668
764
|
if (projectCwd !== undefined && projectCwd !== '') {
|
|
669
765
|
const project = await this.projectTableFor(projectCwd);
|
|
@@ -713,6 +809,10 @@ export class MemoryEngine extends Service {
|
|
|
713
809
|
* @returns 迁移统计(放行 / 隔离 / 回填计数)。
|
|
714
810
|
*/
|
|
715
811
|
async migrateLegacy(projectCwd) {
|
|
812
|
+
return this.writeScope(projectCwd, () => this.migrateLegacyInner(projectCwd));
|
|
813
|
+
}
|
|
814
|
+
/** {@link migrateLegacy} 的实现体;必须经 `writeScope` 进入。 */
|
|
815
|
+
async migrateLegacyInner(projectCwd) {
|
|
716
816
|
const tables = [this.requireTable('global')];
|
|
717
817
|
if (projectCwd !== undefined && projectCwd !== '') {
|
|
718
818
|
const project = await this.projectTableFor(projectCwd);
|
|
@@ -778,6 +878,10 @@ export class MemoryEngine extends Service {
|
|
|
778
878
|
return stats;
|
|
779
879
|
}
|
|
780
880
|
async forget(id, projectCwd) {
|
|
881
|
+
return this.writeScope(projectCwd, () => this.forgetInner(id, projectCwd));
|
|
882
|
+
}
|
|
883
|
+
/** {@link forget} 的实现体;必须经 `writeScope` 进入。 */
|
|
884
|
+
async forgetInner(id, projectCwd) {
|
|
781
885
|
if (await this.requireTable('global').delete(id)) {
|
|
782
886
|
this.ctx.emit('memory/changed', { operation: 'forgotten', id });
|
|
783
887
|
return true;
|
|
@@ -790,6 +894,10 @@ export class MemoryEngine extends Service {
|
|
|
790
894
|
return false;
|
|
791
895
|
}
|
|
792
896
|
async setStatus(id, status, projectCwd) {
|
|
897
|
+
return this.writeScope(projectCwd, () => this.setStatusInner(id, status, projectCwd));
|
|
898
|
+
}
|
|
899
|
+
/** {@link setStatus} 的实现体;必须经 `writeScope` 进入。 */
|
|
900
|
+
async setStatusInner(id, status, projectCwd) {
|
|
793
901
|
const global = this.requireTable('global').get(id);
|
|
794
902
|
if (global !== undefined) {
|
|
795
903
|
const updated = {
|
|
@@ -830,6 +938,10 @@ export class MemoryEngine extends Service {
|
|
|
830
938
|
* 「常驻注入」开关调用(remote.setInjected)。
|
|
831
939
|
*/
|
|
832
940
|
async setInjected(id, injected, projectCwd) {
|
|
941
|
+
return this.writeScope(projectCwd, () => this.setInjectedInner(id, injected, projectCwd));
|
|
942
|
+
}
|
|
943
|
+
/** {@link setInjected} 的实现体;必须经 `writeScope` 进入。 */
|
|
944
|
+
async setInjectedInner(id, injected, projectCwd) {
|
|
833
945
|
const locate = async () => {
|
|
834
946
|
const global = this.requireTable('global').get(id);
|
|
835
947
|
if (global !== undefined)
|
|
@@ -872,6 +984,10 @@ export class MemoryEngine extends Service {
|
|
|
872
984
|
* `injected` 保留原值。若新内容命中危险规则则转为隔离(与写入路径同一判定)。
|
|
873
985
|
*/
|
|
874
986
|
async update(id, patch, projectCwd) {
|
|
987
|
+
return this.writeScope(projectCwd, () => this.updateInner(id, patch, projectCwd));
|
|
988
|
+
}
|
|
989
|
+
/** {@link update} 的实现体;必须经 `writeScope` 进入。 */
|
|
990
|
+
async updateInner(id, patch, projectCwd) {
|
|
875
991
|
const applyPatch = (block) => {
|
|
876
992
|
const normalized = normalizeBlock(block);
|
|
877
993
|
const content = patch.content ?? block.content;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@max-null/dsh-memory",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.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": {
|
|
@@ -22,7 +22,9 @@
|
|
|
22
22
|
"client.js",
|
|
23
23
|
"cordis.patch.yml",
|
|
24
24
|
"README.md",
|
|
25
|
-
"LICENSE",
|
|
25
|
+
"LICENSE",
|
|
26
|
+
"docs/shots"
|
|
27
|
+
],
|
|
26
28
|
"dsh": {
|
|
27
29
|
"bundle": {
|
|
28
30
|
"patch": "./cordis.patch.yml"
|