dsh-auto-memory 0.5.1 → 0.5.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 CHANGED
@@ -220,7 +220,6 @@ Override in your profile's `cordis.patch.yml` (config replaces wholesale):
220
220
  resume); everything flows through standard `tool/call` / `tool/result`.
221
221
 
222
222
  Deep dives: [design decisions](docs/design.md) ·
223
- [dsh source-level research](docs/api-reports.md) ·
224
223
  [postmortem: shipping a PR to awesome-dsh-plugin](docs/postmortem-pr-5696.md)
225
224
 
226
225
  ## Roadmap
package/README.zh.md CHANGED
@@ -187,7 +187,6 @@ dsh --profile demo # 重启 profile 生效
187
187
  一切走标准 `tool/call` / `tool/result`。
188
188
 
189
189
  深度内容:[设计决策](docs/design.md) ·
190
- [dsh 源码级调研](docs/api-reports.md) ·
191
190
  [复盘:向 awesome-dsh-plugin 提 PR](docs/postmortem-pr-5696.md)
192
191
 
193
192
  ## 路线图
package/lib/client.js CHANGED
@@ -51,6 +51,7 @@ window.__ModuleLoader__.load({
51
51
  const [groups, setGroups] = (0, react.useState)(null);
52
52
  const [maxBytes, setMaxBytes] = (0, react.useState)(4096);
53
53
  const [error, setError] = (0, react.useState)(false);
54
+ const [actionError, setActionError] = (0, react.useState)(null);
54
55
  const [editing, setEditing] = (0, react.useState)(null);
55
56
  const [busy, setBusy] = (0, react.useState)(false);
56
57
  const load = (0, react.useCallback)(async () => {
@@ -68,12 +69,15 @@ window.__ModuleLoader__.load({
68
69
  }, [load]);
69
70
  const act = async (fn) => {
70
71
  setBusy(true);
72
+ setActionError(null);
71
73
  try {
72
74
  const response = await fn();
73
- if (!response.ok) console.warn("[auto-memory] action failed", response.status);
74
- await load();
75
+ if (!response.ok) setActionError(response.status === 404 ? t("deleteFailed") : t("saveFailed"));
76
+ } catch {
77
+ setActionError(t("saveFailed"));
75
78
  } finally {
76
79
  setBusy(false);
80
+ await load();
77
81
  }
78
82
  };
79
83
  const cwd = (group) => group.scope === "project" && group.current === true ? currentCwd() : void 0;
@@ -120,13 +124,19 @@ window.__ModuleLoader__.load({
120
124
  style: { opacity: .75 },
121
125
  children: t("intro")
122
126
  }),
123
- /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
127
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
124
128
  onClick: () => {
125
129
  load();
126
130
  },
127
131
  disabled: busy,
128
132
  children: t("refresh")
129
- }) }),
133
+ }), actionError !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
134
+ style: {
135
+ marginLeft: "12px",
136
+ color: "#d97706"
137
+ },
138
+ children: actionError
139
+ })] }),
130
140
  total === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("empty") }),
131
141
  groups.map((group) => {
132
142
  const usage = group.indexBytes / maxBytes;
@@ -303,6 +313,7 @@ window.__ModuleLoader__.load({
303
313
  t,
304
314
  initial: editing.memory,
305
315
  initialBody: editing.body,
316
+ busy,
306
317
  onCancel: () => {
307
318
  setEditing(null);
308
319
  },
@@ -319,7 +330,7 @@ window.__ModuleLoader__.load({
319
330
  });
320
331
  }
321
332
  /** 单条记忆编辑器(标题/摘要/正文,保存即重建索引、下次注入生效)。 */
322
- function MemoryEditor({ t, initial, initialBody, onSave, onCancel }) {
333
+ function MemoryEditor({ t, initial, initialBody, busy, onSave, onCancel }) {
323
334
  const [title, setTitle] = (0, react.useState)(initial.title ?? "");
324
335
  const [description, setDescription] = (0, react.useState)(initial.description);
325
336
  const [body, setBody] = (0, react.useState)(initialBody);
@@ -383,6 +394,7 @@ window.__ModuleLoader__.load({
383
394
  style: { margin: "8px 0 0" },
384
395
  children: [
385
396
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
397
+ disabled: busy,
386
398
  onClick: () => {
387
399
  onSave({
388
400
  name: initial.name,
@@ -397,6 +409,7 @@ window.__ModuleLoader__.load({
397
409
  }),
398
410
  " ",
399
411
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
412
+ disabled: busy,
400
413
  onClick: onCancel,
401
414
  children: t("cancel")
402
415
  })
package/lib/index.js CHANGED
@@ -14,7 +14,7 @@ const USER_SCOPE_DIR = "_user";
14
14
  /**
15
15
  * 记忆存储层:类型化记忆文件 CRUD + MEMORY.md 索引维护。
16
16
  *
17
- * 关键设计决策(依据源码调研与对抗性审查,见 docs/api-reports.md):
17
+ * 关键设计决策(依据源码调研与对抗性审查,见 internal research notes):
18
18
  * 1. **必须用 node:fs 而非 ctx.fs**——默认部署的 fs-sandbox 可写根不含 $DSH_HOME,
19
19
  * 走 ctx.fs 会抛 FS_SANDBOX_DENIED;官方先例 skill-filesystem 访问 $DSH_HOME
20
20
  * 下的文件同样直接用 node:fs。
@@ -239,12 +239,54 @@ const SECRET_PATTERNS = [
239
239
  }
240
240
  ];
241
241
  /**
242
+ * GB11643 身份证校验位验证:18 位号码仅当末位与加权模 11 校验相符才算真身份证。
243
+ * 纯长度匹配会把订单号/雪花 ID 等任意 18 位数字一并误杀(#7)。
244
+ */
245
+ function isValidIdCard(candidate) {
246
+ if (!/^\d{17}[\dXx]$/.test(candidate)) return false;
247
+ const WEIGHTS = [
248
+ 7,
249
+ 9,
250
+ 10,
251
+ 5,
252
+ 8,
253
+ 4,
254
+ 2,
255
+ 1,
256
+ 6,
257
+ 3,
258
+ 7,
259
+ 9,
260
+ 10,
261
+ 5,
262
+ 8,
263
+ 4,
264
+ 2
265
+ ];
266
+ const CHECKS = [
267
+ "1",
268
+ "0",
269
+ "X",
270
+ "9",
271
+ "8",
272
+ "7",
273
+ "6",
274
+ "5",
275
+ "4",
276
+ "3",
277
+ "2"
278
+ ];
279
+ let sum = 0;
280
+ for (let i = 0; i < 17; i++) sum += Number(candidate[i]) * WEIGHTS[i];
281
+ return candidate[17].toUpperCase() === CHECKS[sum % 11];
282
+ }
283
+ /**
242
284
  * 写入前脱敏(纯函数,可测):把高置信密钥/手机号/身份证替换为 [REDACTED:kind]。
243
- * 应用于 description 与 body——所有写路径(工具/固化/面板)都经 store.write,单点拦截。
285
+ * 应用于 description/body——所有写路径(工具/固化/面板)都经 store.write,单点拦截。
244
286
  */
245
287
  function redactSecrets(text) {
246
288
  let out = text;
247
- for (const { kind, re } of SECRET_PATTERNS) out = out.replace(re, `[REDACTED:${kind}]`);
289
+ for (const { kind, re } of SECRET_PATTERNS) out = out.replace(re, (match) => kind === "id-card" && !isValidIdCard(match) ? match : `[REDACTED:${kind}]`);
248
290
  return out;
249
291
  }
250
292
  /**
@@ -280,9 +322,19 @@ function memoryScore(record, nowMs) {
280
322
  /**
281
323
  * 描述相似度(Jaccard 词集,纯函数,可测):固化防回声的查重依据——
282
324
  * "既有记忆的复述不是新信息",相似度过高的候选跳过而非堆积。
325
+ * CJK 感知:连续中文段按字符二元组(bigram)切分(检索领域标准做法)——
326
+ * 整段单 token 会让中文近重复仅得 ~0.4,防回声对中文完全失效(#5)。
283
327
  */
284
328
  function descriptionSimilarity(a, b) {
285
- const words = (text) => new Set(text.toLowerCase().split(/[^a-z0-9一-鿿]+/).filter((w) => w.length > 0));
329
+ const words = (text) => {
330
+ const tokens = text.toLowerCase().split(/[^a-z0-9一-鿿]+/).filter((w) => w.length > 0);
331
+ const out = /* @__PURE__ */ new Set();
332
+ for (const token of tokens) if (/^[一-鿿]+$/.test(token)) {
333
+ if (token.length === 1) out.add(token);
334
+ else for (let i = 0; i + 1 < token.length; i++) out.add(token.slice(i, i + 2));
335
+ } else out.add(token);
336
+ return out;
337
+ };
286
338
  const wa = words(a);
287
339
  const wb = words(b);
288
340
  if (wa.size === 0 || wb.size === 0) return 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-auto-memory",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Claude Code style auto-memory plugin for DeepSeek Harness: typed memory files + MEMORY.md index auto-injected into the system prompt",
5
5
  "type": "module",
6
6
  "license": "MIT",