sage-mem 0.9.8 → 0.9.10
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 +9 -1
- package/lib/client.js +210 -56
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -53,9 +53,13 @@ sage-mem 换一条路:**每条记忆就是一个 Markdown 文件**。用编辑
|
|
|
53
53
|
|
|
54
54
|
*工具抽屉 —— 关着时只占一个按钮,展开后两项各带状态:归档候选 2 条 / 保留名 3 个。(体检不在这里:它自己有一页。)*
|
|
55
55
|
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
*锁定 —— 重要记忆(人设、偏好这类)点一下就锁上:**自动归档的候选里不再出现它,手动点归档也会被宿主拒绝**。锁存在状态目录里,不写进记忆自己的 frontmatter —— 那会被自动整理重写掉,锁一丢它又会回到候选里。*
|
|
59
|
+
|
|
56
60
|

|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
*体检(独立成一个页签)—— 索引悬空、漏索引、断链这类硬问题按类报红;「指向已归档」只作提示、不计入问题,也不提供一键修补按钮(改哪条由你决定)。从档案馆恢复一条记忆时,插件会顺手把它补回索引 —— 以前只搬文件、不补条目,于是顶层有文件、`MEMORY.md` 里没有,体检一直报「漏索引」。*
|
|
59
63
|
|
|
60
64
|

|
|
61
65
|
|
|
@@ -73,6 +77,10 @@ sage-mem 换一条路:**每条记忆就是一个 Markdown 文件**。用编辑
|
|
|
73
77
|
|
|
74
78
|
*自动做梦 · 设置 —— 运行 / 设置 / 记录 二级分组;五个注入参数两列排开,并写清「当前生效」与「重启后」,不会让人误以为改完立刻生效。*
|
|
75
79
|
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
*自动归档(独立成一个页签,排在「自动做梦」与「体检」之间)—— 它和自动做梦不是一件事:归档是**确定性规则**(按访问台账的闲置天数算,不调模型),混在模型那套设置里既难找、也容易让人以为归档会花钱。这一页决定「多久算闲置」,以及要不要真的动手;阈值**保存即生效**,不用重启。*
|
|
83
|
+
|
|
76
84
|
## 快速开始
|
|
77
85
|
|
|
78
86
|
需要 DSH `0.1.2-rc.1` 或更高(见[兼容性](#dsh-兼容性)),Node.js `>= 18`。
|
package/lib/client.js
CHANGED
|
@@ -1930,6 +1930,15 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1930
1930
|
const CSS = [
|
|
1931
1931
|
".smem-root{display:flex;flex-direction:column;gap:12px;font-size:13px;line-height:1.55;color:var(--dsw-alias-label-primary);max-width:760px;}",
|
|
1932
1932
|
".smem-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}",
|
|
1933
|
+
/**
|
|
1934
|
+
* 头部带里的「弹簧」:吃掉本行的剩余空间,把后面的主操作顶到右边。
|
|
1935
|
+
*
|
|
1936
|
+
* 为什么不用 `margin-left:auto`:那个写法在**换行之后依然把它推到最右**,
|
|
1937
|
+
* 于是窗口一窄(或状态徽标一多)主操作就被挤到下一行的右端,看着像跑出了格子。
|
|
1938
|
+
* 弹簧换成 `flex:1 1 auto` 之后 —— 放得下时它撑开(主操作靠右),
|
|
1939
|
+
* 放不下时主操作换行、而弹簧留在上一行,**下一行就是左顶格**。
|
|
1940
|
+
*/
|
|
1941
|
+
".smem-head-spacer{flex:1 1 auto;min-width:0;}",
|
|
1933
1942
|
".smem-title{font-size:15px;font-weight:600;}",
|
|
1934
1943
|
".smem-dot{width:8px;height:8px;border-radius:50%;background:var(--dsw-alias-state-success-primary,#22c55e);flex:none;}",
|
|
1935
1944
|
".smem-dot--bad{background:var(--dsw-alias-state-error-primary,#ef4444);}",
|
|
@@ -1998,7 +2007,8 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
1998
2007
|
".smem-status--bad{color:var(--dsw-alias-state-error-primary,#dc2626);font-weight:600;}",
|
|
1999
2008
|
// 头部带:三个 tab 共用的一条「我在哪 + 现在什么状态 + 主操作」。
|
|
2000
2009
|
".smem-head-job{font-size:12.5px;font-weight:600;}",
|
|
2001
|
-
|
|
2010
|
+
// 头部带主操作:**不用 margin-left:auto**(见 .smem-head-spacer 的注释)
|
|
2011
|
+
".smem-head-actions{display:flex;align-items:center;gap:6px;}",
|
|
2002
2012
|
".smem-tool-panel{border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1);padding:10px 12px;display:flex;flex-direction:column;gap:8px;}",
|
|
2003
2013
|
// 独立成屏的工具面板(现在的「体检」):显式占满**可用宽度**。
|
|
2004
2014
|
// 整个面板的宽度上限在 `.smem-root`(760px)上,这里只要确保它在任何父容器里
|
|
@@ -2007,6 +2017,8 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2007
2017
|
".smem-tool-panel--full{width:100%;align-self:stretch;box-sizing:border-box;}",
|
|
2008
2018
|
// 已锁定的记忆:卡片上那个按钮要一眼看出「这条现在拒绝归档」,用警示色
|
|
2009
2019
|
".smem-btn-locked{border-color:var(--dsw-alias-state-warning-primary,#d97706);color:var(--dsw-alias-state-warning-primary,#d97706);}",
|
|
2020
|
+
// 「自动归档」页签里的天数输入框:窄一点,别让三个数字各占一整行
|
|
2021
|
+
".smem-num{width:88px;}",
|
|
2010
2022
|
// 体检 / 候选是「翻清单」的面板:定高 + 内部滚动,列表不被顶出首屏。
|
|
2011
2023
|
// 原样编辑器(.smem-ar-editor)不设上限 —— 那是专心改一份文件的界面,给它完整高度。
|
|
2012
2024
|
".smem-tool-panel--cap{max-height:320px;overflow-y:auto;}",
|
|
@@ -2556,8 +2568,8 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2556
2568
|
};
|
|
2557
2569
|
const autoArchiveOf = (v) => (v === "off" || v === "auto" ? v : "report");
|
|
2558
2570
|
|
|
2559
|
-
/**
|
|
2560
|
-
const TAB_LABEL = { files: "文件列表", star: "记忆星图", autodream: "自动做梦", audit: "体检" };
|
|
2571
|
+
/** 头部带上的「我在哪」:五个 tab 的中文名(与 .smem-tabs 上的字面一致)。 */
|
|
2572
|
+
const TAB_LABEL = { files: "文件列表", star: "记忆星图", autodream: "自动做梦", archive: "自动归档", audit: "体检" };
|
|
2561
2573
|
|
|
2562
2574
|
/**
|
|
2563
2575
|
* 工具面板头部的「收起」按钮:抽屉里开的工具要能一键收回去 ——
|
|
@@ -2836,7 +2848,6 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2836
2848
|
const sMemSet = react.useState(null); const memSet = sMemSet[0], setMemSet = sMemSet[1];
|
|
2837
2849
|
const sLimitsDraft = react.useState(null); const limitsDraft = sLimitsDraft[0], setLimitsDraft = sLimitsDraft[1];
|
|
2838
2850
|
const sSaveBusy = react.useState(false); const saveBusy = sSaveBusy[0], setSaveBusy = sSaveBusy[1];
|
|
2839
|
-
const sAutoSaving = react.useState(false); const autoSaving = sAutoSaving[0], setAutoSaving = sAutoSaving[1];
|
|
2840
2851
|
const sReservedNew = react.useState(""); const reservedNew = sReservedNew[0], setReservedNew = sReservedNew[1];
|
|
2841
2852
|
|
|
2842
2853
|
// 派生量放在 effect 之前:下面几个 effect 的依赖数组要在渲染期就能取到值。
|
|
@@ -2936,35 +2947,6 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2936
2947
|
}, function (e) { setErr(detail(e)); });
|
|
2937
2948
|
}
|
|
2938
2949
|
|
|
2939
|
-
/**
|
|
2940
|
-
* 自动归档三档:保存后**回读校验**。
|
|
2941
|
-
*
|
|
2942
|
-
* `setConfig` 对非法值沿用的是「忽略、保留原值」的语义(没有 `{ok:false}` 通道),
|
|
2943
|
-
* 所以不能只看它返回的 config —— 必须再 `getConfig()` 一次,确认那个值真的落在
|
|
2944
|
-
* 配置里。没变就报错:**不能让用户点了保存以为成了、实际什么都没发生**。
|
|
2945
|
-
*/
|
|
2946
|
-
function saveAutoArchive(v) {
|
|
2947
|
-
setErr(null); setNotice(""); setAutoSaving(true);
|
|
2948
|
-
callRemote("setConfig", [{ autoArchive: v }], function (d) {
|
|
2949
|
-
if (d && d.config && d.config.autoArchive === v) {
|
|
2950
|
-
setCfg(d.config);
|
|
2951
|
-
setAutoSaving(false);
|
|
2952
|
-
setNotice("自动归档已设为「" + AUTO_ARCHIVE_LABEL[v] + "」");
|
|
2953
|
-
return;
|
|
2954
|
-
}
|
|
2955
|
-
// 返回的 config 里没看到新值 → 回读一次再定论
|
|
2956
|
-
callRemote("getConfig", [], function (d2) {
|
|
2957
|
-
const next = d2 && d2.config ? d2.config : null;
|
|
2958
|
-
setAutoSaving(false);
|
|
2959
|
-
if (next) setCfg(next);
|
|
2960
|
-
const got = next ? autoArchiveOf(next.autoArchive) : null;
|
|
2961
|
-
if (got === v) { setNotice("自动归档已设为「" + AUTO_ARCHIVE_LABEL[v] + "」"); return; }
|
|
2962
|
-
setErr("保存没生效:配置里仍是「" + (got ? AUTO_ARCHIVE_LABEL[got] : "读不出来") +
|
|
2963
|
-
"」。setConfig 对非法值会忽略并保留原值 —— 请重试,或看 DSH 启动日志。");
|
|
2964
|
-
}, function (m) { setAutoSaving(false); setErr("保存后回读失败:" + m); });
|
|
2965
|
-
}, function (m) { setAutoSaving(false); setErr(m); });
|
|
2966
|
-
}
|
|
2967
|
-
|
|
2968
2950
|
/** 保存 5 项上限。越界/非整数**本地先拦**(host 也是直接拒绝),别让它白跑一趟。 */
|
|
2969
2951
|
function saveLimits() {
|
|
2970
2952
|
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr("宿主侧还没有 setSettings 接口(插件版本较旧)"); return; }
|
|
@@ -3262,28 +3244,9 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
3262
3244
|
]),
|
|
3263
3245
|
]) : null,
|
|
3264
3246
|
segOf === "settings" ? h("div", { className: "smem-seg", key: "seg-set" }, [
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
h(Seg, {
|
|
3269
|
-
key: "v", value: autoArchiveOf(c.autoArchive),
|
|
3270
|
-
options: [
|
|
3271
|
-
{ value: "off", label: AUTO_ARCHIVE_LABEL.off },
|
|
3272
|
-
{ value: "report", label: AUTO_ARCHIVE_LABEL.report },
|
|
3273
|
-
{ value: "auto", label: AUTO_ARCHIVE_LABEL.auto },
|
|
3274
|
-
],
|
|
3275
|
-
// 点一档就存一档,但**存完要回读**:setConfig 对非法值是静默忽略,
|
|
3276
|
-
// 只信返回值不够(见 saveAutoArchive 的注释)。
|
|
3277
|
-
onChange: function (v) { if (v !== autoArchiveOf(c.autoArchive)) saveAutoArchive(v); },
|
|
3278
|
-
}),
|
|
3279
|
-
autoSaving ? h("span", { className: "smem-status", key: "s" }, "保存中…") : null,
|
|
3280
|
-
]),
|
|
3281
|
-
h("div", { className: "smem-autodream-note", key: "cost" }, AUTO_ARCHIVE_COST[autoArchiveOf(c.autoArchive)]),
|
|
3282
|
-
autoArchiveOf(c.autoArchive) === "auto" && !c.apply
|
|
3283
|
-
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "deg" },
|
|
3284
|
-
"注意:现在「改动方式」是只出报告 —— 这一档会自动降级成只出报告,不会动文件。")
|
|
3285
|
-
: null,
|
|
3286
|
-
]),
|
|
3247
|
+
// 自动归档那一段(三档策略 + 三个阈值)**已挪到「自动归档」页签**(v0.9.9):
|
|
3248
|
+
// 归档是确定性规则、不调模型,和这一页的「让模型整理」不是一件事;
|
|
3249
|
+
// 同一件事只在一处设定,这里不再重复摆一份。
|
|
3287
3250
|
h("div", { className: "smem-autodream-sec", key: "limits" }, [
|
|
3288
3251
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
3289
3252
|
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "记忆注入参数"),
|
|
@@ -3499,6 +3462,187 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
3499
3462
|
]);
|
|
3500
3463
|
}
|
|
3501
3464
|
|
|
3465
|
+
/**
|
|
3466
|
+
* 「自动归档」面板 —— 独立成一屏(v0.9.9)。
|
|
3467
|
+
*
|
|
3468
|
+
* 为什么独立出来(用户 2026-10-11):这一块原来塞在「自动做梦 → 设置」里,
|
|
3469
|
+
* 他自己的原话是「扔这里连我都找不到」。而且它确实是**另一件事**:
|
|
3470
|
+
* 自动做梦 = 让模型读记忆、整理、改写(要调模型、有 token 成本)
|
|
3471
|
+
* 自动归档 = 按「多久没被注入过」把记忆移进 archive/(纯确定性规则、不调模型)
|
|
3472
|
+
* 混在模型那套设置里,既难找、也容易让人以为归档会花钱。
|
|
3473
|
+
*
|
|
3474
|
+
* 这一页管两件事:
|
|
3475
|
+
* 1. **三档策略**:关闭 / 只出报告 / 满足条件自动跑
|
|
3476
|
+
* 2. **三个阈值**(项目 / 参考 / 用户):多少天没被注入就算闲置
|
|
3477
|
+
*
|
|
3478
|
+
* 数据全走 `remote.autodream` 的 `getConfig` / `setConfig` —— 阈值本来就在配置里
|
|
3479
|
+
* (`lib/autodream/config.js` 的 archiveAfterDays*),只是以前**界面够不着**。
|
|
3480
|
+
* 所以这一版没有新增任何 remote。
|
|
3481
|
+
*
|
|
3482
|
+
* 两处诚实标注:阈值**存完立即生效**(不经过模块加载期固化,与注入参数不同);
|
|
3483
|
+
* 保存后**一定回读**(`setConfig` 对非法值是「忽略并保留原值」,只信返回值会骗人)。
|
|
3484
|
+
*/
|
|
3485
|
+
function ArchiveSettingsPanel(props) {
|
|
3486
|
+
const remote = props.ctx && props.ctx.get ? props.ctx.get("remote.autodream") : null;
|
|
3487
|
+
const sCfg = react.useState(null); const cfg = sCfg[0], setCfg = sCfg[1];
|
|
3488
|
+
const sDraft = react.useState(null); const draft = sDraft[0], setDraft = sDraft[1];
|
|
3489
|
+
const sErr = react.useState(null); const err = sErr[0], setErr = sErr[1];
|
|
3490
|
+
const sNotice = react.useState(""); const notice = sNotice[0], setNotice = sNotice[1];
|
|
3491
|
+
const sBusy = react.useState(false); const busy = sBusy[0], setBusy = sBusy[1];
|
|
3492
|
+
|
|
3493
|
+
const TH_KEYS = ["archiveAfterDaysProject", "archiveAfterDaysReference", "archiveAfterDaysUser"];
|
|
3494
|
+
const TH_LABEL = { archiveAfterDaysProject: "项目", archiveAfterDaysReference: "参考", archiveAfterDaysUser: "用户" };
|
|
3495
|
+
const TH_HINT = {
|
|
3496
|
+
archiveAfterDaysProject: "project_* 连续这么多天没被注入 → 进候选",
|
|
3497
|
+
archiveAfterDaysReference: "reference_* 同上(资料一般比项目留得久)",
|
|
3498
|
+
archiveAfterDaysUser: "user_* 同上(人设这类动它要格外谨慎)",
|
|
3499
|
+
};
|
|
3500
|
+
|
|
3501
|
+
/** 阈值 → 输入框草稿。存成字符串:number input 清空时立刻变 0 会很难用。 */
|
|
3502
|
+
const draftOf = function (c) {
|
|
3503
|
+
const src = c || {};
|
|
3504
|
+
const out = {};
|
|
3505
|
+
for (const k of TH_KEYS) out[k] = String(src[k] == null ? "" : src[k]);
|
|
3506
|
+
return out;
|
|
3507
|
+
};
|
|
3508
|
+
|
|
3509
|
+
react.useEffect(function () {
|
|
3510
|
+
if (!remote || typeof remote.getConfig !== "function") {
|
|
3511
|
+
setErr("宿主侧没有 autodream 配置接口(插件版本较旧)");
|
|
3512
|
+
return;
|
|
3513
|
+
}
|
|
3514
|
+
remote.getConfig()
|
|
3515
|
+
.then(function (r) {
|
|
3516
|
+
const d = unwrap(r);
|
|
3517
|
+
const c = d && d.config ? d.config : null;
|
|
3518
|
+
if (!c) { setErr("读不出自动做梦配置"); return; }
|
|
3519
|
+
setCfg(c);
|
|
3520
|
+
setDraft(draftOf(c));
|
|
3521
|
+
})
|
|
3522
|
+
.catch(function (e) { setErr("读配置失败:" + ((e && e.message) || e)); });
|
|
3523
|
+
}, []);
|
|
3524
|
+
|
|
3525
|
+
const c = cfg || {};
|
|
3526
|
+
const mode = autoArchiveOf(c.autoArchive);
|
|
3527
|
+
|
|
3528
|
+
/**
|
|
3529
|
+
* 存完回读。
|
|
3530
|
+
* @param {Object} patch 要写的配置
|
|
3531
|
+
* @param {string} okMsg 成功提示
|
|
3532
|
+
* @param {Function} failOf (got) => string|null —— 返回 null 表示这次改动真的落进去了
|
|
3533
|
+
*/
|
|
3534
|
+
function save(patch, okMsg, failOf) {
|
|
3535
|
+
setBusy(true); setErr(null); setNotice("");
|
|
3536
|
+
remote.setConfig(patch)
|
|
3537
|
+
.then(function () { return remote.getConfig(); })
|
|
3538
|
+
.then(function (r) {
|
|
3539
|
+
const d = unwrap(r);
|
|
3540
|
+
const got = d && d.config ? d.config : null;
|
|
3541
|
+
if (got) { setCfg(got); setDraft(draftOf(got)); }
|
|
3542
|
+
const bad = failOf(got);
|
|
3543
|
+
if (!bad) { setNotice(okMsg); return; }
|
|
3544
|
+
setErr("保存没生效:" + bad + "。setConfig 对非法值会忽略并保留原值 —— 请重试,或看 DSH 启动日志。");
|
|
3545
|
+
})
|
|
3546
|
+
.catch(function (e) { setErr("保存失败:" + ((e && e.message) || e)); })
|
|
3547
|
+
.then(function () { setBusy(false); });
|
|
3548
|
+
}
|
|
3549
|
+
|
|
3550
|
+
function saveMode(v) {
|
|
3551
|
+
save({ autoArchive: v }, "自动归档已设为「" + AUTO_ARCHIVE_LABEL[v] + "」", function (got) {
|
|
3552
|
+
const g = got ? autoArchiveOf(got.autoArchive) : null;
|
|
3553
|
+
return g === v ? null : ("配置里仍是「" + (g ? AUTO_ARCHIVE_LABEL[g] : "读不出来") + "」");
|
|
3554
|
+
});
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
/** 越界/非整数**本地先拦**(host 也会拒),别让它白跑一趟。 */
|
|
3558
|
+
function saveThresholds() {
|
|
3559
|
+
const d = draft || {};
|
|
3560
|
+
const patch = {};
|
|
3561
|
+
for (const k of TH_KEYS) {
|
|
3562
|
+
const n = Number(d[k]);
|
|
3563
|
+
if (!Number.isFinite(n) || n < 1 || n > 3650) {
|
|
3564
|
+
setErr(TH_LABEL[k] + "的阈值要是 1~3650 之间的天数(现在是「" + d[k] + "」)");
|
|
3565
|
+
return;
|
|
3566
|
+
}
|
|
3567
|
+
patch[k] = Math.round(n);
|
|
3568
|
+
}
|
|
3569
|
+
save(patch, "归档条件已保存", function (got) {
|
|
3570
|
+
const bad = TH_KEYS.filter(function (k) { return !got || Number(got[k]) !== patch[k]; });
|
|
3571
|
+
if (!bad.length) return null;
|
|
3572
|
+
return bad.map(function (k) { return TH_LABEL[k] + " 仍是 " + (got ? got[k] : "读不出来"); }).join("、");
|
|
3573
|
+
});
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
return h("div", { className: "smem-seg" }, [
|
|
3577
|
+
// ── ① 它做什么(这一屏存在的理由写在最前面)──
|
|
3578
|
+
h("div", { className: "smem-autodream-sec", key: "what" }, [
|
|
3579
|
+
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
3580
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "自动归档做什么"),
|
|
3581
|
+
]),
|
|
3582
|
+
h("div", { className: "smem-autodream-note", key: "p1" },
|
|
3583
|
+
"闲置太久没被注入过的记忆,会被移进 archive/ —— 不删除,随时能恢复。这一页决定「多久算闲置」,以及要不要真的动手。"),
|
|
3584
|
+
h("div", { className: "smem-autodream-note", key: "p2" },
|
|
3585
|
+
"它不调模型:候选完全按访问台账的天数算,同一份输入每次算出来都一样。"),
|
|
3586
|
+
h("div", { className: "smem-autodream-note", key: "p3" },
|
|
3587
|
+
"想现在看看会被收走哪些:文件列表 →「工具 ▾」→ 归档候选。"),
|
|
3588
|
+
]),
|
|
3589
|
+
// ── ② 三档策略 ──
|
|
3590
|
+
h("div", { className: "smem-autodream-sec", key: "mode" }, [
|
|
3591
|
+
h("div", { className: "smem-autodream-row", key: "r" }, [
|
|
3592
|
+
h("span", { className: "smem-autodream-key", key: "k" }, "自动归档"),
|
|
3593
|
+
h(Seg, {
|
|
3594
|
+
key: "v", value: mode,
|
|
3595
|
+
options: [
|
|
3596
|
+
{ value: "off", label: AUTO_ARCHIVE_LABEL.off },
|
|
3597
|
+
{ value: "report", label: AUTO_ARCHIVE_LABEL.report },
|
|
3598
|
+
{ value: "auto", label: AUTO_ARCHIVE_LABEL.auto },
|
|
3599
|
+
],
|
|
3600
|
+
onChange: function (v) { if (v !== mode) saveMode(v); },
|
|
3601
|
+
}),
|
|
3602
|
+
busy ? h("span", { className: "smem-status", key: "s" }, "保存中…") : null,
|
|
3603
|
+
]),
|
|
3604
|
+
h("div", { className: "smem-autodream-note", key: "cost" }, AUTO_ARCHIVE_COST[mode]),
|
|
3605
|
+
mode === "auto" && !c.apply
|
|
3606
|
+
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "deg" },
|
|
3607
|
+
"注意:「自动做梦 → 改动方式」现在是只出报告 —— 这一档会自动降级成只出报告,不会动文件。")
|
|
3608
|
+
: null,
|
|
3609
|
+
]),
|
|
3610
|
+
// ── ③ 三个阈值 ──
|
|
3611
|
+
h("div", { className: "smem-autodream-sec", key: "th" }, [
|
|
3612
|
+
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
3613
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "闲置多少天算可以归档"),
|
|
3614
|
+
]),
|
|
3615
|
+
draft
|
|
3616
|
+
? h("div", { key: "rows" }, TH_KEYS.map(function (k) {
|
|
3617
|
+
return h("div", { className: "smem-autodream-row", key: k }, [
|
|
3618
|
+
h("span", { className: "smem-autodream-key", key: "k" }, TH_LABEL[k]),
|
|
3619
|
+
h("input", {
|
|
3620
|
+
className: "smem-input smem-num", key: "i", type: "number", min: 1, max: 3650,
|
|
3621
|
+
value: draft[k],
|
|
3622
|
+
onChange: function (e) {
|
|
3623
|
+
const nx = Object.assign({}, draft);
|
|
3624
|
+
nx[k] = e.target.value;
|
|
3625
|
+
setDraft(nx);
|
|
3626
|
+
},
|
|
3627
|
+
}),
|
|
3628
|
+
h("span", { className: "smem-muted", key: "u" }, TH_HINT[k]),
|
|
3629
|
+
]);
|
|
3630
|
+
}))
|
|
3631
|
+
: h("div", { className: "smem-muted", key: "loading" }, "读配置中…"),
|
|
3632
|
+
h("div", { className: "smem-autodream-row", key: "btns" }, [
|
|
3633
|
+
h("button", {
|
|
3634
|
+
type: "button", className: "smem-btn smem-btn-primary", key: "save",
|
|
3635
|
+
disabled: busy || !draft, onClick: saveThresholds,
|
|
3636
|
+
}, busy ? "保存中…" : "保存归档条件"),
|
|
3637
|
+
h("span", { className: "smem-muted", key: "n" }, "保存即生效,不用重启 DSH"),
|
|
3638
|
+
]),
|
|
3639
|
+
h("div", { className: "smem-autodream-note", key: "note" },
|
|
3640
|
+
"天数按访问台账算(这条记忆上次被注入给模型是什么时候)。feedback_* 永不入选;锁定的记忆也不会被选中。"),
|
|
3641
|
+
]),
|
|
3642
|
+
err ? h("div", { className: "smem-err", key: "err" }, "⚠ " + err) : null,
|
|
3643
|
+
notice ? h("div", { className: "smem-ok", key: "ok" }, "✓ " + notice) : null,
|
|
3644
|
+
]);
|
|
3645
|
+
}
|
|
3502
3646
|
function Section(props) {
|
|
3503
3647
|
const remote = props.ctx.get("remote.memory");
|
|
3504
3648
|
const sItems = react.useState([]);
|
|
@@ -4180,7 +4324,11 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
4180
4324
|
h("button", { type: "button", className: "smem-tab" + (tab === "files" ? " smem-tab--on" : ""), key: "f", onClick: function () { setTab("files"); } }, "文件列表"),
|
|
4181
4325
|
h("button", { type: "button", className: "smem-tab" + (tab === "star" ? " smem-tab--on" : ""), key: "s", onClick: function () { setTab("star"); } }, "记忆星图"),
|
|
4182
4326
|
h("button", { type: "button", className: "smem-tab" + (tab === "autodream" ? " smem-tab--on" : ""), key: "d", onClick: function () { setTab("autodream"); } }, "自动做梦"),
|
|
4183
|
-
//
|
|
4327
|
+
// 「自动归档」排在自动做梦之后:归档是**确定性规则**(按闲置天数算,不调模型),
|
|
4328
|
+
// 和「让模型整理记忆」是两件事。塞在自动做梦的设置里用户找不到
|
|
4329
|
+
// (2026-10-11 用户原话:「扔这里连我都找不到」)。
|
|
4330
|
+
h("button", { type: "button", className: "smem-tab" + (tab === "archive" ? " smem-tab--on" : ""), key: "ar", onClick: function () { setTab("archive"); } }, "自动归档"),
|
|
4331
|
+
// 体检再排在「自动归档」之后、独立成一屏:它只报告不修改,和「文件列表」里的
|
|
4184
4332
|
// 增删改是两件事,塞在工具抽屉里会让人以为它是个动作按钮。
|
|
4185
4333
|
h("button", { type: "button", className: "smem-tab" + (tab === "audit" ? " smem-tab--on" : ""), key: "a", onClick: openAuditTab }, "体检"),
|
|
4186
4334
|
]),
|
|
@@ -4207,6 +4355,10 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
4207
4355
|
}, (pendingDiff ? (pendingDiff + " 项") : "") + "待重启生效")
|
|
4208
4356
|
: null,
|
|
4209
4357
|
// 主操作:只放当前 tab 的那一个(星图/自动做梦的主操作在各自面板里,不重复摆一份)。
|
|
4358
|
+
// 弹簧:把主操作顶到右边。**换行时它留在上一行**,主操作于是落到下一行、
|
|
4359
|
+
// 左顶格 —— 这正是用 `margin-left:auto` 做不到的(那会把换行后的主操作
|
|
4360
|
+
// 也推到最右,看起来像"跑出格子了")。
|
|
4361
|
+
h("span", { className: "smem-head-spacer", key: "sp" }),
|
|
4210
4362
|
h("span", { className: "smem-head-actions", key: "act" }, tab === "files" ? [
|
|
4211
4363
|
h("button", { type: "button", className: "smem-btn", key: "refresh", disabled: busy || loading, onClick: load }, "刷新"),
|
|
4212
4364
|
h("button", { type: "button", className: "smem-btn smem-btn-primary", key: "add", onClick: openAdd }, "+ 添加记忆"),
|
|
@@ -4387,6 +4539,8 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
4387
4539
|
: tab === "star"
|
|
4388
4540
|
? h("div", { className: "smem-starmap", key: "starmap" },
|
|
4389
4541
|
h(StarmapRuntime.StarMap, { ctx: props.ctx }))
|
|
4542
|
+
: tab === "archive"
|
|
4543
|
+
? h(ArchiveSettingsPanel, { key: "archive", ctx: props.ctx })
|
|
4390
4544
|
: tab === "audit"
|
|
4391
4545
|
// 体检面板在上面已经渲染过了 —— 这里必须显式给 null,
|
|
4392
4546
|
// 否则会落进下面那个 `filesView` 兜底分支,变成「体检 + 一整套文件列表」同屏。
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sage-mem",
|
|
3
3
|
"description": "File-based memory plugin for DeepSeek Harness (DSH) — cross-session memory in plain markdown files, with a memory star-map view, type/tag filtering, retrieval aliases, and an optional autodream pass that consolidates memories on demand or on a schedule — with per-run rollback points, a per-file change declaration (what changed and why), and a separately selectable model for the consolidation pass",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
7
|
"exports": {
|