dsh-memory-palace 1.7.1 → 1.7.2-alpha.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  把 WorkBuddy 的文件式记忆系统移植进 [DeepSeek Harness](https://www.deepseek.com/harness/) —— 为 Harness 提供**跨会话持久化、人类可直接编辑的 Markdown 记忆**。
4
4
 
5
- [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com) [![npm](https://img.shields.io/npm/v/dsh-memory-palace.svg?label=npm&labelColor=000000&color=ff4b01)](https://www.npmjs.com/package/dsh-memory-palace) [![DeepSeek Harness:0.1.5-rc.1](https://img.shields.io/badge/DeepSeek%20Harness-0.1.5--rc.1-success.svg?labelColor=4D6BFE)](https://github.com/deepseek-ai/deepseek-harness)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![Awesome DSH Plugin](https://awesome-dsh-plugin.com/badge.svg)](https://awesome-dsh-plugin.com) [![npm](https://img.shields.io/npm/v/dsh-memory-palace.svg?label=npm&labelColor=000000&color=ff4b01)](https://www.npmjs.com/package/dsh-memory-palace) [![DeepSeek Harness:0.1.6-alpha.2](https://img.shields.io/badge/DeepSeek%20Harness-0.1.6--alpha.2-success.svg?labelColor=4D6BFE)](https://github.com/deepseek-ai/deepseek-harness)
6
6
 
7
7
  ## 特性
8
8
 
@@ -42,10 +42,12 @@
42
42
  **读取(每轮对话)**——两条通道并行:
43
43
 
44
44
  ```
45
- ① 长期记忆:全程常驻,每个步骤都可见;内容未变只写一次,压缩后自动重注
46
- ② 今日日志:随系统提示词按轮次注入(高频变化,不进对话历史以免膨胀)
45
+ ① 系统提示词(恒定):记忆公民指令 / 自定义指令等固定内容,前缀缓存友好
46
+ ② 对话历史投影(易变):用户级 + 项目级长期记忆、今日日志——按文件身份各注入一次,压缩后自动重注最新版
47
47
  ```
48
48
 
49
+ > **极简模式(静默预设)**:宿主的「模式」= agent 平面预设(`presets/*/agent.cordis.yml`),官方 minimal 用 persona `complete: true` 独占系统提示词,是刻意留白的「裸测环境」(跑分口径)。但本插件注册在 host 平面,各通道对**每个会话**都生效(`complete` 只吞 system 提示词、**不裁工具**),所以必须按会话自行静默。默认 `silentPresets = ["minimal"]` 已覆盖官方极简模式:命中预设的会话里,系统提示词、记忆投影、7 个记忆工具(**schema 一并隐身,零 token 足迹**)、turn-end 自动写入与 hybrid 记忆子代理**全部关闭**,语义等价于该会话没装本插件,不留半注入状态。判据取会话创建头 `agentPreset`(含未显式指定时的部署默认)与预设切换事件;判据缺失一律 fail-open(不静默),不会因识别不到而丢记忆。要静默其它预设,在设置页「静默预设」里加 id(逗号分隔)。
50
+
49
51
  **写入(每轮结束)**——经「防闲聊闸门」判定后异步追加:
50
52
 
51
53
  ```
@@ -72,13 +74,13 @@
72
74
 
73
75
  ## 安装
74
76
 
75
- 前置要求:已安装 DeepSeek Harness 及其 CLI(`dsh` 命令可用)。
77
+ 前置要求:已安装 DeepSeek Harness 及其 CLI(`dsh` 命令可用)。插件声明的 **dsh 下限为 `0.1.5-rc.1`**(写进 `peerDependencies` 门禁,低于此版本装载期即挡),**已核验至 `0.1.6-alpha.2`**(契约 1–12 与插件占用的宿主面全部通过)。
76
78
 
77
79
  方式一:直接通过 GitHub 安装(推荐,`lib/` 构建产物已随仓库分发,装即用)
78
80
 
79
81
  ```bash
80
82
  dsh plugin --profile web add github:lovezi0/dsh-memory-palace
81
- # 锁定版本:dsh plugin --profile web add github:lovezi0/dsh-memory-palace#v1.7.1
83
+ # 锁定版本:dsh plugin --profile web add github:lovezi0/dsh-memory-palace#v1.7.2-alpha.1
82
84
  ```
83
85
 
84
86
  方式二:clone 后本地安装(开发 / 修改源码场景)
@@ -119,6 +121,9 @@ dsh plugin --profile web remove dsh-memory-palace
119
121
 
120
122
  ## 版本历史
121
123
 
124
+ - **1.7.2**
125
+ - **1.7.2-alpha.1**
126
+ - 💪dsh Agent 预设[极简模式]时禁用记忆注入、记忆生成、记忆工具
122
127
  - **1.7.1**
123
128
  - 💪移除日志重注机制优化缓存命中降低堆积冗余
124
129
  - 💪记忆设置UI优化
package/lib/client.js CHANGED
@@ -27,6 +27,8 @@ window.__ModuleLoader__.load({
27
27
  bridgeHint: "项目已存在 .workbuddy / .codebuddy 记忆目录时直接读写,不再建 .deepseek-harness/memory/",
28
28
  dirsLabel: "buddy 记忆目录(逗号分隔,按序优先)",
29
29
  dirsHint: "例如:.workbuddy/memory, .codebuddy/memory",
30
+ silentPresetsLabel: "静默预设(逗号分隔)",
31
+ silentPresetsHint: "这些 agent 预设下本插件整体隐身:不注入系统提示词与记忆投影、记忆工具不对模型暴露、不写日志、不跑记忆子代理(官方极简模式即「裸测环境」跑分口径)。默认 minimal;留空=关闭该机制。切换预设需新建会话生效。",
30
32
  userPathLabel: "用户级记忆文件",
31
33
  userPathHint: "跨项目个人偏好记忆,默认 ~/.deepseek-harness/MEMORY.md",
32
34
  wsDirLabel: "工作区记忆目录(回退)",
@@ -110,6 +112,8 @@ window.__ModuleLoader__.load({
110
112
  bridgeHint: "Read/write directly into existing .workbuddy / .codebuddy memory dirs instead of creating .deepseek-harness/memory/",
111
113
  dirsLabel: "Buddy memory dirs (comma-separated, first wins)",
112
114
  dirsHint: "e.g. .workbuddy/memory, .codebuddy/memory",
115
+ silentPresetsLabel: "Silent presets (comma-separated)",
116
+ silentPresetsHint: "Under these agent presets the plugin goes fully invisible: no system-prompt or memory projection injection, memory tools hidden from the model, no logging, no memory sub-agent (the official minimal mode is the bare-benchmark preset). Default: minimal; leave empty to disable this rule. Switching presets takes effect in a new session.",
113
117
  userPathLabel: "User memory file",
114
118
  userPathHint: "Cross-project preferences, default ~/.deepseek-harness/MEMORY.md",
115
119
  wsDirLabel: "Workspace memory dir (fallback)",
@@ -186,6 +190,8 @@ window.__ModuleLoader__.load({
186
190
  return {
187
191
  bridgeBuddyMemory: v.bridgeBuddyMemory === false ? "false" : "true",
188
192
  buddyWorkspaceMemoryDirs: Array.isArray(v.buddyWorkspaceMemoryDirs) ? v.buddyWorkspaceMemoryDirs.join(", ") : ".workbuddy/memory, .codebuddy/memory",
193
+ // v1.7.2:静默预设名单(数组 ↔ 逗号串,与 buddyWorkspaceMemoryDirs 同款约定)
194
+ silentPresets: Array.isArray(v.silentPresets) ? v.silentPresets.join(", ") : "minimal",
189
195
  userMemoryPath: v.userMemoryPath || "~/.deepseek-harness/MEMORY.md",
190
196
  workspaceMemoryDir: v.workspaceMemoryDir || ".deepseek-harness/memory",
191
197
  dailyLogRetentionDays: v.dailyLogRetentionDays != null ? String(v.dailyLogRetentionDays) : "30",
@@ -352,6 +358,9 @@ window.__ModuleLoader__.load({
352
358
  }
353
359
  } else if (field === "buddyWorkspaceMemoryDirs") {
354
360
  section[field] = text.split(",").map((s) => s.trim()).filter(Boolean);
361
+ } else if (field === "silentPresets") {
362
+ // v1.7.2:数组型配置(同 buddyWorkspaceMemoryDirs)。空串 → 空数组 = 关闭该机制。
363
+ section[field] = text.split(",").map((s) => s.trim()).filter(Boolean);
355
364
  } else {
356
365
  if (text !== "") section[field] = text;
357
366
  }
@@ -649,7 +658,18 @@ window.__ModuleLoader__.load({
649
658
  ...num("buddyWorkspaceMemoryDirs"),
650
659
  type: "text",
651
660
  placeholder: ".workbuddy/memory, .codebuddy/memory"
652
- }), "buddyWorkspaceMemoryDirs")
661
+ }), "buddyWorkspaceMemoryDirs"),
662
+ // v1.7.2:静默预设(极简模式等)。命中后本插件在该会话整体隐身(提示词/投影/工具/写入/子代理)。
663
+ // 用普通文本输入(非 num —— 这里填的是预设 id 列表,不是数字)。
664
+ row(t("silentPresetsLabel"), t("silentPresetsHint"),
665
+ h("input", {
666
+ type: "text",
667
+ value: draft.silentPresets,
668
+ disabled,
669
+ style: { ...inputStyle, borderColor: "var(--dsw-alias-border-l2, #e5e7eb)" },
670
+ placeholder: "minimal",
671
+ onChange: (e) => edit("silentPresets", e.target.value)
672
+ }), "silentPresets")
653
673
  ]),
654
674
  // v1.7.1(特性3):自定义指令。非空时经 system prompt 追加到「记忆分工说明」之后;
655
675
  // 它是恒定内容(用户配置一次不变),故放 section 而非 E 投影 —— 零缓存成本。
package/lib/index.js CHANGED
@@ -42,6 +42,11 @@ const MEMORY_PALACE_SETTINGS_NAMESPACE = "memory-palace";
42
42
 
43
43
  export const Config = Schema.object({
44
44
  enabled: Schema.boolean().default(true).description("启用 memory-palace 记忆注入与每日日志写入。"),
45
+ // ---- v1.7.2:预设级静默(官方 minimal「裸测环境」口径)----
46
+ // 宿主把「模式」实现为 agent 平面的 preset:minimal 预设用 persona complete:true 独占 system prompt。
47
+ // 但本插件注册在 host 平面(profile bundle 的 insert),各通道对**每个 agent** 都生效,preset 并不会
48
+ // 把它摘掉——故须自行按会话判据静默,否则记忆正文仍会经 E 投影进历史(实测症状)。
49
+ silentPresets: Schema.array(Schema.string()).default(["minimal"]).description("在这些 agent 预设下本插件整体静默(等价 enabled=false):不注入系统提示词与投影、记忆工具对模型隐身、不写日志、不跑记忆子代理。默认 [\"minimal\"](官方极简模式);置空数组 = 关闭该机制。判据取会话创建头 agentPreset 与 agent-preset/selected 事件(宿主公开事实)。"),
45
50
  userMemoryPath: Schema.string().default("~/.deepseek-harness/MEMORY.md").description("用户级记忆文件路径(支持 ~ 展开)。"),
46
51
  workspaceMemoryDir: Schema.string().default(".deepseek-harness/memory").description("无 buddy 目录时使用的项目级每日日志目录;项目级 MEMORY.md 位于其同级(.deepseek-harness/MEMORY.md)。"),
47
52
  dailyLogRetentionDays: Schema.number().default(30).description("每日日志保留天数,过期日志会被蒸馏进 MEMORY.md。"),
@@ -128,6 +133,9 @@ export function apply(ctx, config) {
128
133
  lastSummarizedSeq: -1, // 智能模式增量摘要断点(session 事件 seq)
129
134
  summarySessionId: null,
130
135
  planModeActive: false, // 计划模式:禁写记忆(硬编码默认,无开关;仅经 session/event 的 plan/mode 翻转;未装 dsh-plan-mode 永不触发=不拦截)
136
+ // v1.7.2:会话 → agent 预设 id(仅"切换预设"事件写入;创建头另在 presetOfSession 里兜底读)。
137
+ // 只增不减:会话结束后残留几条字符串,代价可忽略;切回同一 session 断言预设不变(宿主侧保证)。
138
+ presetBySession: new Map(),
131
139
  // v1.7.0:injectedSessionIds 已删除——记忆正文改经 E 投影(src/projection.mjs)注入为
132
140
  // 常驻消息,compaction 后由投影自动重注,不再需要"仅首次注入"的会话级标记。
133
141
  };
@@ -137,6 +145,45 @@ export function apply(ctx, config) {
137
145
  const TURN_BUFFER_CHAR_CAP = 30000;
138
146
  const SETTLE_DELAY = 1500;
139
147
 
148
+ // ---------- v1.7.2:预设级静默判据(官方 minimal = 宿主给模型的"裸测环境") ----------
149
+ // 背景:宿主的「模式」= agent 平面的 preset(`packages/preset/agent-presets/presets/*/agent.cordis.yml`),
150
+ // minimal 预设以 persona `complete: true` 独占 system prompt 并关掉 runtime context——那是官方跑分口径
151
+ // 的刻意设计。而本插件是 host 平面插件(profile bundle insert),其 systemPrompt.section / agent/pre-step
152
+ // 投影 / 工具注册对**每个 agent** 都生效,preset 不会把它摘掉;`complete` 又只吞 sections、不裁 tools。
153
+ // 结果就是"指令没了、记忆正文照进、工具 schema 照发"的半残状态(实测:极简模式下投影仍在注入)。
154
+ // 因此必须自行判定会话预设并整体静默。
155
+ //
156
+ // 判据取宿主公开事实,不引入额外服务依赖:
157
+ // ① session.header.agentPreset —— 创建头,由 session-controller 的 composeAgent 写入**解析后**的
158
+ // 预设 id(未显式指定时即部署默认),故默认预设会话同样能识别;
159
+ // ② `agent-preset/selected` 事件 —— 仅"尚未产生任何消息"的空会话可切预设,切换时追加,
160
+ // 由下方 session/event 监听捕获。
161
+ // 二者合起来等价于宿主的 `agentPreset` 会话投影(init=header,apply=该事件),无需注入 sessionProjections。
162
+ // fail-open:判据缺失(老宿主 / 非 preset 部署)一律视为"不静默",绝不因判据缺失而丢掉记忆能力。
163
+ function presetOfSession(session) {
164
+ if (!session) return undefined;
165
+ const switched = state.presetBySession.get(session.id);
166
+ if (typeof switched === "string" && switched) return switched;
167
+ const header = session.header?.agentPreset;
168
+ return typeof header === "string" && header ? header : undefined;
169
+ }
170
+
171
+ /**
172
+ * 该会话是否应整体静默:`enabled=false`(全局停用)或命中「静默预设」名单。
173
+ * 命中后 section / E 投影 / turn-end 写入 / 记忆工具 / hybrid 子代理一并关闭——
174
+ * 语义等价于该会话里没装本插件。
175
+ * @param {object|undefined} session
176
+ * @returns {boolean}
177
+ */
178
+ function isSessionSilent(session) {
179
+ const cfg = source();
180
+ if (!cfg.enabled) return true;
181
+ const list = Array.isArray(cfg.silentPresets) ? cfg.silentPresets.filter((x) => typeof x === "string" && x) : [];
182
+ if (!list.length) return false; // 名单空 = 关闭该机制(只受 enabled 管)
183
+ const preset = presetOfSession(session);
184
+ return preset !== undefined && list.includes(preset);
185
+ }
186
+
140
187
  // ---- 装配:路径解析 / 记录读写 / 蒸馏 / 工具 / route ----
141
188
  const paths = createPaths(() => source(), () => state.activeCwd);
142
189
  const records = createRecords({ getConfig: () => source(), paths });
@@ -152,8 +199,75 @@ export function apply(ctx, config) {
152
199
  // 的 prompt 不引导使用,guards 白名单只拦 hybrid 工具)。
153
200
  const hybrid = registerHybrid({ ctx, getConfig: () => source(), paths, records, state });
154
201
  // v1.7.0 特性1:E 投影通道(记忆正文常驻注入)。与 hybrid 同理,无条件注册——
155
- // 其内部读热配置,无副作用(enabled=false 时 buildProjections 返回空)。
156
- registerProjection({ ctx, getConfig: () => source(), paths });
202
+ // 其内部读热配置,无副作用(enabled=false 或命中静默预设时 buildProjections 返回空)。
203
+ // v1.7.2:把 isSessionSilent 透传下去——投影按**会话**判定,故必须知道 agent 所属会话的预设。
204
+ registerProjection({ ctx, getConfig: () => source(), paths, isSilent: isSessionSilent });
205
+
206
+ // ---------- v1.7.2:静默会话(enabled=false 或命中静默预设)的工具 schema 隐身 ----------
207
+ // 背景:`complete: true` 只收缩 sections、**不裁剪 tools**——记忆工具的 schema 仍会经 toolProvider
208
+ // 进 assemble,在"裸测环境"里白吃 token 且可能被模型幻觉调用。故静默会话需显式隐身工具。
209
+ // 机制约束:① enabled / silentPresets 都是热配置,apply() 同步段读不到 → 不能条件注册(否则切回后
210
+ // 工具从未注册,同 v1.6.0 hybrid 踩坑);② tools.restrict() 要求 agent 作用域 ctx、静态名单、
211
+ // dispose 才解除;③ 宿主无"当前是哪个 preset"的装配期 API,但 agent/created 时 session 创建头已带
212
+ // agentPreset(composeAgent 写入解析后的 id),故此处判定可靠。
213
+ // 落法:挂 agent/created 建立掩码,并在**空会话切换预设**时同步(宿主允许"尚未产生任何消息"的会话
214
+ // 改预设;此时模型还没看过任何东西,掩码必须跟着变,否则切到极简后工具仍暴露)。
215
+ // 语义与 model selection 一致(能力集在 agent 装配前冻结):运行中热切 enabled / silentPresets
216
+ // 仅影响后续新建 agent,当前 agent 不变——可接受(execute 内已有 enabled 兜底,即便残留 schema 被调
217
+ // 也返回 disabled)。
218
+ // deny 名单含 4 基础工具 + 3 hybrid 工具;log_read_section / log_write_ops 是子 agent 内部工具
219
+ // (经 ctx.llm.stream 的 tools 参数、不入全局层),列进 deny 会触发 unknown-tool 抛错,故不含。
220
+ const MEMORY_TOOL_NAMES = [
221
+ "memory_note",
222
+ "memory_note_user",
223
+ "memory_read",
224
+ "memory_delete",
225
+ "memory_write",
226
+ "memory_update_section",
227
+ "memory_reorganize",
228
+ ];
229
+ // sessionId → agent(仅用于切换预设时重新同步掩码;agent/disposed 时清理,避免长跑进程里越积越多)
230
+ const agentBySession = new Map();
231
+ // sessionId → restrict() 的解除函数(掩码是否已戴,靠它判断 + 反向解除)
232
+ const toolMaskBySession = new Map();
233
+
234
+ /** 按会话当前预设同步工具掩码:静默→戴上(deny 全部记忆工具),非静默→摘下。幂等。 */
235
+ function syncToolMask(agent) {
236
+ const sessionId = agent?.session?.id;
237
+ if (!sessionId) return;
238
+ const masked = toolMaskBySession.has(sessionId);
239
+ const silent = isSessionSilent(agent?.session);
240
+ if (silent === masked) return;
241
+ try {
242
+ if (silent) {
243
+ const release = agent?.ctx?.tools?.restrict({ deny: MEMORY_TOOL_NAMES });
244
+ if (typeof release === "function") toolMaskBySession.set(sessionId, release);
245
+ } else {
246
+ toolMaskBySession.get(sessionId)?.();
247
+ toolMaskBySession.delete(sessionId);
248
+ }
249
+ } catch (error) {
250
+ // 绝不冒泡:restrict 抛错(如某工具因异常未注册命中 unknown-tool)时降级为"不隐身",
251
+ // 由 execute 的 enabled 兜底保证功能仍停用,只是没省掉 schema token。
252
+ console.error(`[memory-palace] tool-hide skipped: ${error?.message || String(error)}`);
253
+ }
254
+ }
255
+
256
+ ctx.on("agent/created", ({ agent }) => {
257
+ if (agent?.session?.id) agentBySession.set(agent.session.id, agent);
258
+ syncToolMask(agent);
259
+ });
260
+ ctx.on("agent/disposed", ({ agent }) => {
261
+ const sessionId = agent?.session?.id;
262
+ if (!sessionId) return;
263
+ try {
264
+ toolMaskBySession.get(sessionId)?.();
265
+ } catch {
266
+ /* scope 已在卸载,忽略 */
267
+ }
268
+ toolMaskBySession.delete(sessionId);
269
+ agentBySession.delete(sessionId);
270
+ });
157
271
 
158
272
  // ---- session/event 跟踪:更新 activeCwd/activeSession/增量断点 + 累积 turnBuffer ----
159
273
  // 关键:一次用户请求在 dsh agent 循环里会被拆成多个 turn(每个工具调用一轮),
@@ -169,6 +283,16 @@ export function apply(ctx, config) {
169
283
  state.lastSummarizedSeq = session.firstLiveSeq;
170
284
  }
171
285
  const type = event?.type;
286
+ // v1.7.2:预设切换(仅空会话可切;宿主在切换提交后追加该事件)。记进 Map 供 isSessionSilent 读取。
287
+ if (type === "agent-preset/selected") {
288
+ const id = event?.data?.agentPreset;
289
+ if (session?.id && typeof id === "string" && id) state.presetBySession.set(session.id, id);
290
+ // 掩码跟随:会话切进/切出静默预设时立即戴上或摘下(此时模型尚未看过任何内容,
291
+ // 不做这一步会出现"切到极简、工具 schema 仍暴露"的漏网)。
292
+ const owner = session?.id ? agentBySession.get(session.id) : undefined;
293
+ if (owner) syncToolMask(owner);
294
+ return;
295
+ }
172
296
  // 计划模式:禁写记忆(硬编码默认行为,无开关;不依赖 dsh-plan-mode 服务,未装则永不触发=安全降级=不拦截)
173
297
  if (type === "plan/mode") {
174
298
  state.planModeActive = !!(event?.data?.active);
@@ -223,9 +347,15 @@ export function apply(ctx, config) {
223
347
  ctx.systemPrompt.section({
224
348
  name: "memory-palace",
225
349
  order: 50,
226
- text: () => {
350
+ // v1.7.2:text 接收宿主 AssembleContext(`{ agent, scope, signal }`,见 agent 包 assembleContextFor),
351
+ // 故可按**会话**判定预设静默 —— 与投影/工具/写入同一判据,避免"half-injected"状态。
352
+ text: (context) => {
227
353
  const cfg = source();
228
354
  if (!cfg.enabled) return "";
355
+ // v1.7.2:极简(裸测)预设整体静默。注:minimal 预设的 complete:true 本来也会吞掉本节,
356
+ // 这里显式返回空串是为了① 覆盖"自定义静默预设未用 complete"的情形;② 让本节的去留与其余
357
+ // 通道严格一致,便于排障(不会出现"投影关了、section 还在"的错觉)。
358
+ if (isSessionSilent(context?.agent?.session)) return "";
229
359
  // v1.7.0:alreadyInjected / injectedSessionIds 已整体删除(记忆正文改走 E 投影)。
230
360
  // intro(含记忆公民指令)仍【始终】注入——若因记忆为空而整体返回空串,agent 将不知道
231
361
  // 记忆系统存在、不会主动记,形成「无记忆 → 无指令 → 永不记」死循环。
@@ -278,6 +408,9 @@ export function apply(ctx, config) {
278
408
  if (state.planModeActive) return;
279
409
  const cfg = source();
280
410
  if (!cfg.enabled) return;
411
+ // v1.7.2:静默会话(极简等预设)不写任何记忆——错误捕获 / 轻量兜底 / smart 摘要 / hybrid 子代理
412
+ // 全部在此一处提前返回(hybrid 分支在其下方,天然被拦)。
413
+ if (isSessionSilent(state.activeSession)) return;
281
414
  const dirs = paths.writeDirs();
282
415
  if (!dirs.length) return;
283
416
 
@@ -15,6 +15,9 @@
15
15
  // (此前日志在 section 里每步求值,一变就让序列最前的整段前缀作废)。
16
16
  // - compaction 把消息移出 surface 后,身份判据即判为缺失 → 自动重注**磁盘最新版**,
17
17
  // 顺带完成一次「刷新到最新」。
18
+ // - v1.7.2 起**静默会话整体不投影**(isSilent(session)):宿主「模式」是 agent 平面预设,
19
+ // 本插件却在 host 平面,投影对每个 agent 都生效——官方 minimal 预设(persona complete:true
20
+ // 独占 system prompt 的"裸测环境")下必须自行按会话静默,否则记忆正文仍会常驻历史吃 token。
18
21
  //
19
22
  // 实现照抄宿主 `packages/context/agent-instructions/src/index.ts` 三件套(API 全公开、无第一方特权):
20
23
  // 1. `inject: ['sessionProjections']`(服务随 dsh-base 默认装载)
@@ -86,12 +89,16 @@ function buildProjection(text, heading) {
86
89
  /**
87
90
  * 收集本步要投影的消息(用户级 / 项目级 MEMORY.md / 今日工作日志,各自独立一条)。
88
91
  * 拆成多条独立消息(v1.7.0 定案,v1.7.1 扩展):否则"某一条内容一改,其余连带重发"。
89
- * @param {{ getConfig: () => object, paths: object }} deps
92
+ * v1.7.2:新增 `session` + `isSilent` —— 静默会话(官方 minimal 等预设)整体不投影。
93
+ * @param {{ getConfig: () => object, paths: object, session?: object, isSilent?: (session?: object) => boolean }} deps
90
94
  * @returns {object[]} 按 [用户级, 项目级…, 今日日志…] 顺序的消息数组(可能为空)
91
95
  */
92
- export function buildProjections({ getConfig, paths }) {
96
+ export function buildProjections({ getConfig, paths, session, isSilent }) {
93
97
  const cfg = getConfig();
94
98
  if (!cfg.enabled) return [];
99
+ // v1.7.2:静默会话(极简模式等)不投影——投影消息是**常驻历史**,比 section 更"重":
100
+ // 它要一路吃 token 直到 compaction,正是"裸测环境"最不该有的东西。
101
+ if (isSilent?.(session)) return [];
95
102
  const out = [];
96
103
  // 用户级 MEMORY.md
97
104
  const userFile = expandHome(cfg.userMemoryPath);
@@ -129,9 +136,9 @@ export function buildProjections({ getConfig, paths }) {
129
136
 
130
137
  /**
131
138
  * 注册 E 投影钩子。
132
- * @param {{ ctx: object, getConfig: () => object, paths: object }} deps
139
+ * @param {{ ctx: object, getConfig: () => object, paths: object, isSilent?: (session?: object) => boolean }} deps
133
140
  */
134
- export function registerProjection({ ctx, getConfig, paths }) {
141
+ export function registerProjection({ ctx, getConfig, paths, isSilent }) {
135
142
  ctx.on("agent/pre-step", async ({ agent, messages, step, signal }, next) => {
136
143
  // 先让瀑布流继续,拿到宿主与其他插件合成后的决策——投影要折叠进它的 messages。
137
144
  const decision = await next();
@@ -139,7 +146,9 @@ export function registerProjection({ ctx, getConfig, paths }) {
139
146
  signal?.throwIfAborted?.();
140
147
  if (decision.kind !== "enter") return decision;
141
148
  const session = agent?.session;
142
- const desired = buildProjections({ getConfig, paths });
149
+ // v1.7.2:静默会话(极简模式等)不投影。放在 next() 之后、构造之前,成本最低。
150
+ if (isSilent?.(session)) return decision;
151
+ const desired = buildProjections({ getConfig, paths, session, isSilent });
143
152
  if (!desired.length) return decision;
144
153
  // 逐条去重(按**文件身份**、非内容):已被本步 messages 覆盖、或已在 surface 上的,跳过。
145
154
  const pending = desired.filter((msg) =>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-memory-palace",
3
- "version": "1.7.1",
3
+ "version": "1.7.2-alpha.1",
4
4
  "description": "把 WorkBuddy 的文件式记忆系统移植进 DeepSeek Harness:用户级 + 工作区的人类可读 Markdown 记忆(~/.deepseek-harness/MEMORY.md 与 <cwd>/.deepseek-harness/memory/)。",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -41,13 +41,13 @@
41
41
  },
42
42
  "peerDependencies": {
43
43
  "@deepseek-ai/cordis": "*",
44
- "@deepseek-ai/dsh-llm": "*",
45
- "@deepseek-ai/dsh-settings": "^0.1.2-alpha.2",
46
- "@deepseek-ai/dsh-tools": "^0.1.2-alpha.2",
44
+ "@deepseek-ai/dsh-llm": "^0.1.5-rc.1",
45
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.1",
46
+ "@deepseek-ai/dsh-tools": "^0.1.5-rc.1",
47
47
  "@deepseek-ai/schemastery": "^3.18.2"
48
48
  },
49
49
  "devDependencies": {
50
- "@deepseek-ai/dsh-settings": "^0.1.2-alpha.2"
50
+ "@deepseek-ai/dsh-settings": "^0.1.5-rc.1"
51
51
  },
52
52
  "scripts": {
53
53
  "build": "node scripts/build.mjs",