sage-mem 0.9.8 → 0.9.9

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.
Files changed (3) hide show
  1. package/README.md +9 -1
  2. package/lib/client.js +195 -55
  3. 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
+ ![锁定:按钮在「编辑」与「归档」之间,锁上后归档被禁用](https://raw.githubusercontent.com/gezi-wen/sage-mem/main/docs/images/lock-card.png)
57
+
58
+ *锁定 —— 重要记忆(人设、偏好这类)点一下就锁上:**自动归档的候选里不再出现它,手动点归档也会被宿主拒绝**。锁存在状态目录里,不写进记忆自己的 frontmatter —— 那会被自动整理重写掉,锁一丢它又会回到候选里。*
59
+
56
60
  ![体检:独立一页,硬问题按类报红分节,「指向已归档」只作提示](https://raw.githubusercontent.com/gezi-wen/sage-mem/main/docs/images/audit.png)
57
61
 
58
- *体检(排在「自动做梦」之后的第 4 个页签)—— 索引悬空、漏索引、断链这类硬问题按类报红;「指向已归档」只作提示、不计入问题,也不提供一键修补按钮(改哪条由你决定)。*
62
+ *体检(独立成一个页签)—— 索引悬空、漏索引、断链这类硬问题按类报红;「指向已归档」只作提示、不计入问题,也不提供一键修补按钮(改哪条由你决定)。从档案馆恢复一条记忆时,插件会顺手把它补回索引 —— 以前只搬文件、不补条目,于是顶层有文件、`MEMORY.md` 里没有,体检一直报「漏索引」。*
59
63
 
60
64
  ![更新提示:只在真有新版本时出现一条横幅](https://raw.githubusercontent.com/gezi-wen/sage-mem/main/docs/images/update-banner.png)
61
65
 
@@ -73,6 +77,10 @@ sage-mem 换一条路:**每条记忆就是一个 Markdown 文件**。用编辑
73
77
 
74
78
  *自动做梦 · 设置 —— 运行 / 设置 / 记录 二级分组;五个注入参数两列排开,并写清「当前生效」与「重启后」,不会让人误以为改完立刻生效。*
75
79
 
80
+ ![自动归档:独立一页,先说清它做什么,三档策略与三个闲置阈值都在这儿](https://raw.githubusercontent.com/gezi-wen/sage-mem/main/docs/images/archive-settings.png)
81
+
82
+ *自动归档(独立成一个页签,排在「自动做梦」与「体检」之间)—— 它和自动做梦不是一件事:归档是**确定性规则**(按访问台账的闲置天数算,不调模型),混在模型那套设置里既难找、也容易让人以为归档会花钱。这一页决定「多久算闲置」,以及要不要真的动手;阈值**保存即生效**,不用重启。*
83
+
76
84
  ## 快速开始
77
85
 
78
86
  需要 DSH `0.1.2-rc.1` 或更高(见[兼容性](#dsh-兼容性)),Node.js `>= 18`。
package/lib/client.js CHANGED
@@ -2007,6 +2007,8 @@ const StarmapRuntime = (function (require, _react) {
2007
2007
  ".smem-tool-panel--full{width:100%;align-self:stretch;box-sizing:border-box;}",
2008
2008
  // 已锁定的记忆:卡片上那个按钮要一眼看出「这条现在拒绝归档」,用警示色
2009
2009
  ".smem-btn-locked{border-color:var(--dsw-alias-state-warning-primary,#d97706);color:var(--dsw-alias-state-warning-primary,#d97706);}",
2010
+ // 「自动归档」页签里的天数输入框:窄一点,别让三个数字各占一整行
2011
+ ".smem-num{width:88px;}",
2010
2012
  // 体检 / 候选是「翻清单」的面板:定高 + 内部滚动,列表不被顶出首屏。
2011
2013
  // 原样编辑器(.smem-ar-editor)不设上限 —— 那是专心改一份文件的界面,给它完整高度。
2012
2014
  ".smem-tool-panel--cap{max-height:320px;overflow-y:auto;}",
@@ -2556,8 +2558,8 @@ const StarmapRuntime = (function (require, _react) {
2556
2558
  };
2557
2559
  const autoArchiveOf = (v) => (v === "off" || v === "auto" ? v : "report");
2558
2560
 
2559
- /** 头部带上的「我在哪」:四个 tab 的中文名(与 .smem-tabs 上的字面一致)。 */
2560
- const TAB_LABEL = { files: "文件列表", star: "记忆星图", autodream: "自动做梦", audit: "体检" };
2561
+ /** 头部带上的「我在哪」:五个 tab 的中文名(与 .smem-tabs 上的字面一致)。 */
2562
+ const TAB_LABEL = { files: "文件列表", star: "记忆星图", autodream: "自动做梦", archive: "自动归档", audit: "体检" };
2561
2563
 
2562
2564
  /**
2563
2565
  * 工具面板头部的「收起」按钮:抽屉里开的工具要能一键收回去 ——
@@ -2836,7 +2838,6 @@ const StarmapRuntime = (function (require, _react) {
2836
2838
  const sMemSet = react.useState(null); const memSet = sMemSet[0], setMemSet = sMemSet[1];
2837
2839
  const sLimitsDraft = react.useState(null); const limitsDraft = sLimitsDraft[0], setLimitsDraft = sLimitsDraft[1];
2838
2840
  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
2841
  const sReservedNew = react.useState(""); const reservedNew = sReservedNew[0], setReservedNew = sReservedNew[1];
2841
2842
 
2842
2843
  // 派生量放在 effect 之前:下面几个 effect 的依赖数组要在渲染期就能取到值。
@@ -2936,35 +2937,6 @@ const StarmapRuntime = (function (require, _react) {
2936
2937
  }, function (e) { setErr(detail(e)); });
2937
2938
  }
2938
2939
 
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
2940
  /** 保存 5 项上限。越界/非整数**本地先拦**(host 也是直接拒绝),别让它白跑一趟。 */
2969
2941
  function saveLimits() {
2970
2942
  if (!memRemote || typeof memRemote.setSettings !== "function") { setErr("宿主侧还没有 setSettings 接口(插件版本较旧)"); return; }
@@ -3262,28 +3234,9 @@ const StarmapRuntime = (function (require, _react) {
3262
3234
  ]),
3263
3235
  ]) : null,
3264
3236
  segOf === "settings" ? h("div", { className: "smem-seg", key: "seg-set" }, [
3265
- h("div", { className: "smem-autodream-sec", key: "autoarchive" }, [
3266
- h("div", { className: "smem-autodream-row", key: "r" }, [
3267
- h("span", { className: "smem-autodream-key", key: "k" }, "自动归档"),
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
- ]),
3237
+ // 自动归档那一段(三档策略 + 三个阈值)**已挪到「自动归档」页签**(v0.9.9):
3238
+ // 归档是确定性规则、不调模型,和这一页的「让模型整理」不是一件事;
3239
+ // 同一件事只在一处设定,这里不再重复摆一份。
3287
3240
  h("div", { className: "smem-autodream-sec", key: "limits" }, [
3288
3241
  h("div", { className: "smem-autodream-row", key: "h" }, [
3289
3242
  h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "记忆注入参数"),
@@ -3499,6 +3452,187 @@ const StarmapRuntime = (function (require, _react) {
3499
3452
  ]);
3500
3453
  }
3501
3454
 
3455
+ /**
3456
+ * 「自动归档」面板 —— 独立成一屏(v0.9.9)。
3457
+ *
3458
+ * 为什么独立出来(用户 2026-10-11):这一块原来塞在「自动做梦 → 设置」里,
3459
+ * 他自己的原话是「扔这里连我都找不到」。而且它确实是**另一件事**:
3460
+ * 自动做梦 = 让模型读记忆、整理、改写(要调模型、有 token 成本)
3461
+ * 自动归档 = 按「多久没被注入过」把记忆移进 archive/(纯确定性规则、不调模型)
3462
+ * 混在模型那套设置里,既难找、也容易让人以为归档会花钱。
3463
+ *
3464
+ * 这一页管两件事:
3465
+ * 1. **三档策略**:关闭 / 只出报告 / 满足条件自动跑
3466
+ * 2. **三个阈值**(项目 / 参考 / 用户):多少天没被注入就算闲置
3467
+ *
3468
+ * 数据全走 `remote.autodream` 的 `getConfig` / `setConfig` —— 阈值本来就在配置里
3469
+ * (`lib/autodream/config.js` 的 archiveAfterDays*),只是以前**界面够不着**。
3470
+ * 所以这一版没有新增任何 remote。
3471
+ *
3472
+ * 两处诚实标注:阈值**存完立即生效**(不经过模块加载期固化,与注入参数不同);
3473
+ * 保存后**一定回读**(`setConfig` 对非法值是「忽略并保留原值」,只信返回值会骗人)。
3474
+ */
3475
+ function ArchiveSettingsPanel(props) {
3476
+ const remote = props.ctx && props.ctx.get ? props.ctx.get("remote.autodream") : null;
3477
+ const sCfg = react.useState(null); const cfg = sCfg[0], setCfg = sCfg[1];
3478
+ const sDraft = react.useState(null); const draft = sDraft[0], setDraft = sDraft[1];
3479
+ const sErr = react.useState(null); const err = sErr[0], setErr = sErr[1];
3480
+ const sNotice = react.useState(""); const notice = sNotice[0], setNotice = sNotice[1];
3481
+ const sBusy = react.useState(false); const busy = sBusy[0], setBusy = sBusy[1];
3482
+
3483
+ const TH_KEYS = ["archiveAfterDaysProject", "archiveAfterDaysReference", "archiveAfterDaysUser"];
3484
+ const TH_LABEL = { archiveAfterDaysProject: "项目", archiveAfterDaysReference: "参考", archiveAfterDaysUser: "用户" };
3485
+ const TH_HINT = {
3486
+ archiveAfterDaysProject: "project_* 连续这么多天没被注入 → 进候选",
3487
+ archiveAfterDaysReference: "reference_* 同上(资料一般比项目留得久)",
3488
+ archiveAfterDaysUser: "user_* 同上(人设这类动它要格外谨慎)",
3489
+ };
3490
+
3491
+ /** 阈值 → 输入框草稿。存成字符串:number input 清空时立刻变 0 会很难用。 */
3492
+ const draftOf = function (c) {
3493
+ const src = c || {};
3494
+ const out = {};
3495
+ for (const k of TH_KEYS) out[k] = String(src[k] == null ? "" : src[k]);
3496
+ return out;
3497
+ };
3498
+
3499
+ react.useEffect(function () {
3500
+ if (!remote || typeof remote.getConfig !== "function") {
3501
+ setErr("宿主侧没有 autodream 配置接口(插件版本较旧)");
3502
+ return;
3503
+ }
3504
+ remote.getConfig()
3505
+ .then(function (r) {
3506
+ const d = unwrap(r);
3507
+ const c = d && d.config ? d.config : null;
3508
+ if (!c) { setErr("读不出自动做梦配置"); return; }
3509
+ setCfg(c);
3510
+ setDraft(draftOf(c));
3511
+ })
3512
+ .catch(function (e) { setErr("读配置失败:" + ((e && e.message) || e)); });
3513
+ }, []);
3514
+
3515
+ const c = cfg || {};
3516
+ const mode = autoArchiveOf(c.autoArchive);
3517
+
3518
+ /**
3519
+ * 存完回读。
3520
+ * @param {Object} patch 要写的配置
3521
+ * @param {string} okMsg 成功提示
3522
+ * @param {Function} failOf (got) => string|null —— 返回 null 表示这次改动真的落进去了
3523
+ */
3524
+ function save(patch, okMsg, failOf) {
3525
+ setBusy(true); setErr(null); setNotice("");
3526
+ remote.setConfig(patch)
3527
+ .then(function () { return remote.getConfig(); })
3528
+ .then(function (r) {
3529
+ const d = unwrap(r);
3530
+ const got = d && d.config ? d.config : null;
3531
+ if (got) { setCfg(got); setDraft(draftOf(got)); }
3532
+ const bad = failOf(got);
3533
+ if (!bad) { setNotice(okMsg); return; }
3534
+ setErr("保存没生效:" + bad + "。setConfig 对非法值会忽略并保留原值 —— 请重试,或看 DSH 启动日志。");
3535
+ })
3536
+ .catch(function (e) { setErr("保存失败:" + ((e && e.message) || e)); })
3537
+ .then(function () { setBusy(false); });
3538
+ }
3539
+
3540
+ function saveMode(v) {
3541
+ save({ autoArchive: v }, "自动归档已设为「" + AUTO_ARCHIVE_LABEL[v] + "」", function (got) {
3542
+ const g = got ? autoArchiveOf(got.autoArchive) : null;
3543
+ return g === v ? null : ("配置里仍是「" + (g ? AUTO_ARCHIVE_LABEL[g] : "读不出来") + "」");
3544
+ });
3545
+ }
3546
+
3547
+ /** 越界/非整数**本地先拦**(host 也会拒),别让它白跑一趟。 */
3548
+ function saveThresholds() {
3549
+ const d = draft || {};
3550
+ const patch = {};
3551
+ for (const k of TH_KEYS) {
3552
+ const n = Number(d[k]);
3553
+ if (!Number.isFinite(n) || n < 1 || n > 3650) {
3554
+ setErr(TH_LABEL[k] + "的阈值要是 1~3650 之间的天数(现在是「" + d[k] + "」)");
3555
+ return;
3556
+ }
3557
+ patch[k] = Math.round(n);
3558
+ }
3559
+ save(patch, "归档条件已保存", function (got) {
3560
+ const bad = TH_KEYS.filter(function (k) { return !got || Number(got[k]) !== patch[k]; });
3561
+ if (!bad.length) return null;
3562
+ return bad.map(function (k) { return TH_LABEL[k] + " 仍是 " + (got ? got[k] : "读不出来"); }).join("、");
3563
+ });
3564
+ }
3565
+
3566
+ return h("div", { className: "smem-seg" }, [
3567
+ // ── ① 它做什么(这一屏存在的理由写在最前面)──
3568
+ h("div", { className: "smem-autodream-sec", key: "what" }, [
3569
+ h("div", { className: "smem-autodream-row", key: "h" }, [
3570
+ h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "自动归档做什么"),
3571
+ ]),
3572
+ h("div", { className: "smem-autodream-note", key: "p1" },
3573
+ "闲置太久没被注入过的记忆,会被移进 archive/ —— 不删除,随时能恢复。这一页决定「多久算闲置」,以及要不要真的动手。"),
3574
+ h("div", { className: "smem-autodream-note", key: "p2" },
3575
+ "它不调模型:候选完全按访问台账的天数算,同一份输入每次算出来都一样。"),
3576
+ h("div", { className: "smem-autodream-note", key: "p3" },
3577
+ "想现在看看会被收走哪些:文件列表 →「工具 ▾」→ 归档候选。"),
3578
+ ]),
3579
+ // ── ② 三档策略 ──
3580
+ h("div", { className: "smem-autodream-sec", key: "mode" }, [
3581
+ h("div", { className: "smem-autodream-row", key: "r" }, [
3582
+ h("span", { className: "smem-autodream-key", key: "k" }, "自动归档"),
3583
+ h(Seg, {
3584
+ key: "v", value: mode,
3585
+ options: [
3586
+ { value: "off", label: AUTO_ARCHIVE_LABEL.off },
3587
+ { value: "report", label: AUTO_ARCHIVE_LABEL.report },
3588
+ { value: "auto", label: AUTO_ARCHIVE_LABEL.auto },
3589
+ ],
3590
+ onChange: function (v) { if (v !== mode) saveMode(v); },
3591
+ }),
3592
+ busy ? h("span", { className: "smem-status", key: "s" }, "保存中…") : null,
3593
+ ]),
3594
+ h("div", { className: "smem-autodream-note", key: "cost" }, AUTO_ARCHIVE_COST[mode]),
3595
+ mode === "auto" && !c.apply
3596
+ ? h("div", { className: "smem-autodream-note smem-ad-warn", key: "deg" },
3597
+ "注意:「自动做梦 → 改动方式」现在是只出报告 —— 这一档会自动降级成只出报告,不会动文件。")
3598
+ : null,
3599
+ ]),
3600
+ // ── ③ 三个阈值 ──
3601
+ h("div", { className: "smem-autodream-sec", key: "th" }, [
3602
+ h("div", { className: "smem-autodream-row", key: "h" }, [
3603
+ h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "闲置多少天算可以归档"),
3604
+ ]),
3605
+ draft
3606
+ ? h("div", { key: "rows" }, TH_KEYS.map(function (k) {
3607
+ return h("div", { className: "smem-autodream-row", key: k }, [
3608
+ h("span", { className: "smem-autodream-key", key: "k" }, TH_LABEL[k]),
3609
+ h("input", {
3610
+ className: "smem-input smem-num", key: "i", type: "number", min: 1, max: 3650,
3611
+ value: draft[k],
3612
+ onChange: function (e) {
3613
+ const nx = Object.assign({}, draft);
3614
+ nx[k] = e.target.value;
3615
+ setDraft(nx);
3616
+ },
3617
+ }),
3618
+ h("span", { className: "smem-muted", key: "u" }, TH_HINT[k]),
3619
+ ]);
3620
+ }))
3621
+ : h("div", { className: "smem-muted", key: "loading" }, "读配置中…"),
3622
+ h("div", { className: "smem-autodream-row", key: "btns" }, [
3623
+ h("button", {
3624
+ type: "button", className: "smem-btn smem-btn-primary", key: "save",
3625
+ disabled: busy || !draft, onClick: saveThresholds,
3626
+ }, busy ? "保存中…" : "保存归档条件"),
3627
+ h("span", { className: "smem-muted", key: "n" }, "保存即生效,不用重启 DSH"),
3628
+ ]),
3629
+ h("div", { className: "smem-autodream-note", key: "note" },
3630
+ "天数按访问台账算(这条记忆上次被注入给模型是什么时候)。feedback_* 永不入选;锁定的记忆也不会被选中。"),
3631
+ ]),
3632
+ err ? h("div", { className: "smem-err", key: "err" }, "⚠ " + err) : null,
3633
+ notice ? h("div", { className: "smem-ok", key: "ok" }, "✓ " + notice) : null,
3634
+ ]);
3635
+ }
3502
3636
  function Section(props) {
3503
3637
  const remote = props.ctx.get("remote.memory");
3504
3638
  const sItems = react.useState([]);
@@ -4180,7 +4314,11 @@ const StarmapRuntime = (function (require, _react) {
4180
4314
  h("button", { type: "button", className: "smem-tab" + (tab === "files" ? " smem-tab--on" : ""), key: "f", onClick: function () { setTab("files"); } }, "文件列表"),
4181
4315
  h("button", { type: "button", className: "smem-tab" + (tab === "star" ? " smem-tab--on" : ""), key: "s", onClick: function () { setTab("star"); } }, "记忆星图"),
4182
4316
  h("button", { type: "button", className: "smem-tab" + (tab === "autodream" ? " smem-tab--on" : ""), key: "d", onClick: function () { setTab("autodream"); } }, "自动做梦"),
4183
- // 体检排在「自动做梦」之后、独立成一屏:它只报告不修改,和「文件列表」里的
4317
+ // 「自动归档」排在自动做梦之后:归档是**确定性规则**(按闲置天数算,不调模型),
4318
+ // 和「让模型整理记忆」是两件事。塞在自动做梦的设置里用户找不到
4319
+ // (2026-10-11 用户原话:「扔这里连我都找不到」)。
4320
+ h("button", { type: "button", className: "smem-tab" + (tab === "archive" ? " smem-tab--on" : ""), key: "ar", onClick: function () { setTab("archive"); } }, "自动归档"),
4321
+ // 体检再排在「自动归档」之后、独立成一屏:它只报告不修改,和「文件列表」里的
4184
4322
  // 增删改是两件事,塞在工具抽屉里会让人以为它是个动作按钮。
4185
4323
  h("button", { type: "button", className: "smem-tab" + (tab === "audit" ? " smem-tab--on" : ""), key: "a", onClick: openAuditTab }, "体检"),
4186
4324
  ]),
@@ -4387,6 +4525,8 @@ const StarmapRuntime = (function (require, _react) {
4387
4525
  : tab === "star"
4388
4526
  ? h("div", { className: "smem-starmap", key: "starmap" },
4389
4527
  h(StarmapRuntime.StarMap, { ctx: props.ctx }))
4528
+ : tab === "archive"
4529
+ ? h(ArchiveSettingsPanel, { key: "archive", ctx: props.ctx })
4390
4530
  : tab === "audit"
4391
4531
  // 体检面板在上面已经渲染过了 —— 这里必须显式给 null,
4392
4532
  // 否则会落进下面那个 `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.8",
4
+ "version": "0.9.9",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {