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 +0 -1
- package/README.zh.md +0 -1
- package/lib/client.js +18 -5
- package/lib/index.js +56 -4
- package/package.json +1 -1
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
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)
|
|
74
|
-
|
|
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.
|
|
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
|
-
* 关键设计决策(依据源码调研与对抗性审查,见
|
|
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
|
|
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) =>
|
|
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