dsh-memory-palace 1.7.2 → 1.8.0-alpha.2
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 +38 -103
- package/lib/api.mjs +3 -5
- package/lib/client.js +39 -84
- package/lib/common/planmode.mjs +32 -0
- package/lib/common/prompts.mjs +6 -8
- package/lib/common/records.mjs +7 -68
- package/lib/common/sections.mjs +35 -11
- package/lib/common/text.mjs +8 -3
- package/lib/distill.mjs +9 -20
- package/lib/hybrid/index.mjs +3 -4
- package/lib/hybrid/subagent.mjs +423 -419
- package/lib/hybrid/tools.mjs +35 -28
- package/lib/index.js +172 -169
- package/lib/projection.mjs +7 -5
- package/lib/tools.mjs +10 -7
- package/package.json +1 -1
package/lib/hybrid/tools.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { join, dirname } from "node:path";
|
|
|
11
11
|
import { readFileSync } from "node:fs";
|
|
12
12
|
import { expandHome, toHomeShort, readMdSync, nowStamp, normLine } from "../common/text.mjs";
|
|
13
13
|
import { appendToSectionText, upsertSectionText, replaceSectionText, markEntryDeletedText, locateSection } from "../common/sections.mjs";
|
|
14
|
+
import { planModeOf } from "../common/planmode.mjs";
|
|
14
15
|
|
|
15
16
|
const REORG_MARK = "memory-palace:last-reorg:";
|
|
16
17
|
const REORG_MARK_RE = new RegExp(`\\n?<!--\\s*${REORG_MARK}[^>]*-->\\s*$`);
|
|
@@ -35,7 +36,7 @@ export function readLastReorg(md) {
|
|
|
35
36
|
export function checkReorgGate(memFile, cfg) {
|
|
36
37
|
const md = readMdSync(memFile);
|
|
37
38
|
const size = md.length;
|
|
38
|
-
const overBudget = size > (cfg.workspaceBudgetChars ||
|
|
39
|
+
const overBudget = size > (cfg.workspaceBudgetChars || 6000);
|
|
39
40
|
const last = readLastReorg(md);
|
|
40
41
|
const cooldownMs = (cfg.reorgCooldownDays ?? 7) * 86400000;
|
|
41
42
|
const cooled = Date.now() - last >= cooldownMs;
|
|
@@ -49,7 +50,7 @@ export function checkReorgGate(memFile, cfg) {
|
|
|
49
50
|
? cooled
|
|
50
51
|
? "ok"
|
|
51
52
|
: `重整冷却期内(上次重整 ${new Date(last).toLocaleString()},冷却 ${cfg.reorgCooldownDays ?? 7} 天),请用 memory_update_section 做章节级修正。`
|
|
52
|
-
: `MEMORY.md 当前 ${size} 字符未超出注入预算(${cfg.workspaceBudgetChars ||
|
|
53
|
+
: `MEMORY.md 当前 ${size} 字符未超出注入预算(${cfg.workspaceBudgetChars || 6000}),无需重整;请用 memory_write / memory_update_section 维护。`,
|
|
53
54
|
};
|
|
54
55
|
}
|
|
55
56
|
|
|
@@ -65,8 +66,8 @@ function sectionHasEntry(md, section, entry) {
|
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
69
|
+
* 注册记忆工具(MEMORY.md 章节化写入 / 整章节替换 / 全量重整)+ pre-execute 闸门。
|
|
70
|
+
* v1.8.0:由 index.mjs 无条件装配(原 hybrid 已成唯一写入路径)。
|
|
70
71
|
* @param {{ ctx: object, getConfig: () => object, paths: object, records: object, state: object }} deps
|
|
71
72
|
*/
|
|
72
73
|
export function registerHybridTools({ ctx, getConfig, paths, records, state }) {
|
|
@@ -118,7 +119,6 @@ export function registerHybridTools({ ctx, getConfig, paths, records, state }) {
|
|
|
118
119
|
render: (_args, value) => [{ type: "text", text: value?.message ?? "Saved." }],
|
|
119
120
|
},
|
|
120
121
|
async execute(args, exec) {
|
|
121
|
-
state.recentAgentWrote = true;
|
|
122
122
|
const c = cfg();
|
|
123
123
|
if (!c.enabled) return { ok: false, message: "memory-palace is currently disabled in settings." };
|
|
124
124
|
const section = String(args.section ?? "").trim();
|
|
@@ -127,28 +127,33 @@ export function registerHybridTools({ ctx, getConfig, paths, records, state }) {
|
|
|
127
127
|
const cwd = sessionCwd(exec);
|
|
128
128
|
const target = memFileOf(args.scope === "user" ? "user" : "project", cwd);
|
|
129
129
|
if (!target) return { ok: false, message: "No active workspace." };
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
130
|
+
try {
|
|
131
|
+
let wrote = 0;
|
|
132
|
+
let skipped = 0;
|
|
133
|
+
let flattened = false;
|
|
134
|
+
const files = target.dirs.length ? target.dirs.map((d) => paths.memoryFileOf(d, cwd)) : [target.file];
|
|
135
|
+
for (const file of files) {
|
|
136
|
+
const md = readMdSync(file);
|
|
137
|
+
if (sectionHasEntry(md, section, entry)) {
|
|
138
|
+
skipped++;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
const r = upsertSectionText(md, section, entry);
|
|
142
|
+
if (!r.ok) {
|
|
143
|
+
skipped++;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
await writeFile(file, r.text, "utf8");
|
|
147
|
+
wrote++;
|
|
148
|
+
flattened = flattened || r.flattened === true;
|
|
144
149
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
150
|
+
// v1.8.0-alpha.1 修复(D1):entry 经 flattenEntry 扁平化为单行——含内嵌换行/标题行
|
|
151
|
+
// 时不再原样落盘破坏结构,message 明示合并,不静默撒谎。
|
|
152
|
+
const flatNote = flattened ? "(entry 含多行,已合并为单行)" : "";
|
|
148
153
|
return {
|
|
149
154
|
ok: wrote > 0,
|
|
150
155
|
message: wrote > 0
|
|
151
|
-
? `Saved to ${wrote} MEMORY.md file(s) under 「${section}」${skipped ? ` (${skipped} duplicate skipped)` : ""}.`
|
|
156
|
+
? `Saved to ${wrote} MEMORY.md file(s) under 「${section}」${flatNote}${skipped ? ` (${skipped} duplicate skipped)` : ""}.`
|
|
152
157
|
: `Duplicate entry under 「${section}」; nothing written.`,
|
|
153
158
|
};
|
|
154
159
|
} catch (e) {
|
|
@@ -202,7 +207,6 @@ export function registerHybridTools({ ctx, getConfig, paths, records, state }) {
|
|
|
202
207
|
[{ type: "text", text: value?.actual ? `${value.message}\n\n当前实际内容:\n${value.actual}` : value?.message ?? "Done." }],
|
|
203
208
|
},
|
|
204
209
|
async execute(args, exec) {
|
|
205
|
-
state.recentAgentWrote = true;
|
|
206
210
|
const c = cfg();
|
|
207
211
|
if (!c.enabled) return { ok: false, message: "memory-palace is currently disabled in settings.", actual: "" };
|
|
208
212
|
const section = String(args.section ?? "").trim();
|
|
@@ -217,7 +221,10 @@ export function registerHybridTools({ ctx, getConfig, paths, records, state }) {
|
|
|
217
221
|
? markEntryDeletedText(md, section, oldText)
|
|
218
222
|
: replaceSectionText(md, section, oldText, args.newText);
|
|
219
223
|
if (!r.ok) {
|
|
220
|
-
|
|
224
|
+
const msg = r.reason === "heading-mismatch"
|
|
225
|
+
? `更新被拒绝(heading-mismatch):newText 的标题行与 section 参数「${section}」不一致(或缺少 ## 标题行)。请保持两者一致后重试;确需重命名章节,请同步修改标题行与 section 参数。`
|
|
226
|
+
: `更新被拒绝(${r.reason}):请先 memory_read 重读后再试。`;
|
|
227
|
+
return { ok: false, message: msg, actual: r.actual ?? "" };
|
|
221
228
|
}
|
|
222
229
|
await writeFile(file, r.text, "utf8");
|
|
223
230
|
return { ok: true, message: `Updated 「${section}」 in ${toHomeShort(file)} (${r.reason}).`, actual: "" };
|
|
@@ -258,7 +265,6 @@ export function registerHybridTools({ ctx, getConfig, paths, records, state }) {
|
|
|
258
265
|
render: (_args, value) => [{ type: "text", text: value?.message ?? "Done." }],
|
|
259
266
|
},
|
|
260
267
|
async execute(args, exec) {
|
|
261
|
-
state.recentAgentWrote = true;
|
|
262
268
|
const c = cfg();
|
|
263
269
|
if (!c.enabled) return { ok: false, message: "memory-palace is currently disabled in settings." };
|
|
264
270
|
const cwd = sessionCwd(exec);
|
|
@@ -310,7 +316,8 @@ export function attachHybridGuards(ctx, getConfig, paths, state) {
|
|
|
310
316
|
ctx.on("tools/pre-execute", async (exec, next) => {
|
|
311
317
|
const name = exec?.name;
|
|
312
318
|
if (!HYBRID_WRITE_TOOLS.has(name)) return next();
|
|
313
|
-
|
|
319
|
+
// 按**会话**判定(plan 模式是会话级状态,见 common/planmode.mjs)——别的会话进 plan 不该 deny 本会话
|
|
320
|
+
if (planModeOf(state, exec?.agent?.session)) {
|
|
314
321
|
return { kind: "deny", reason: "plan 模式下禁止写入记忆" };
|
|
315
322
|
}
|
|
316
323
|
if (name !== "memory_reorganize") return next();
|
|
@@ -327,7 +334,7 @@ export function attachHybridGuards(ctx, getConfig, paths, state) {
|
|
|
327
334
|
kind: "ask",
|
|
328
335
|
reason:
|
|
329
336
|
`⚠️ 项目级 MEMORY.md 全量重整确认\n` +
|
|
330
|
-
`当前 ${gate.size} 字符(已超预算 ${c.workspaceBudgetChars ||
|
|
337
|
+
`当前 ${gate.size} 字符(已超预算 ${c.workspaceBudgetChars || 6000}),冷却已满足(上次重整 ${gate.last ? new Date(gate.last).toLocaleString() : "从未"})。\n` +
|
|
331
338
|
`即将用 ${size} 字符的新全文覆盖 ${file}(原文件会自动备份)。\n` +
|
|
332
339
|
`请确认是否执行重整?`,
|
|
333
340
|
};
|