sage-mem 1.6.0 → 1.6.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
@@ -12,7 +12,7 @@
12
12
 
13
13
  **Cross-session memory for DeepSeek Harness — plain markdown files, no database, no worker.**
14
14
 
15
- **Current version 1.0.0 (the first stable release).** The public commitments, the capability boundaries and the list of things it explicitly cannot do are in the
15
+ **Current release: `1.6.2`** — the npm badge above is the dynamic source of truth. **Contract baseline: `1.0.0`** — the first stable release, whose public commitments, the capability boundaries and the list of things it explicitly cannot do are frozen in the
16
16
  [frozen contract](https://github.com/gezi-wen/sage-mem/blob/main/docs/1.0.0-contract.md).
17
17
 
18
18
  If it is useful to you, a Star is the most direct feedback.
@@ -84,6 +84,11 @@ The memory management page's file list also has an **Open memory folder** button
84
84
  memory directory in the system file manager (Explorer / Finder / the Linux file manager) through a
85
85
  detached host-side process and returns without waiting for the window.
86
86
 
87
+ While the memory directory is empty, the empty state carries a **Create index skeleton** button: one
88
+ click writes a starter `MEMORY.md` (index format plus the four memory categories). When `MEMORY.md`
89
+ already exists it only says so and changes nothing; it uses the existing reserved-file read/write
90
+ calls rather than a new remote.
91
+
87
92
  ## Quick Start
88
93
 
89
94
  Requires **DSH `0.1.2-rc.1` or higher** (see [Compatibility](#compatibility)) and **Node.js `>= 18`**.
@@ -459,7 +464,7 @@ Your memories are the files in that directory, openable at any time for confirma
459
464
 
460
465
  sage-mem is a pure DSH plugin and declares its host requirement through **`engines.dsh`** in `package.json`: `>=0.1.2-rc.1` — **a one-sided open interval with no upper bound**, so no future DSH version will be blocked by this declaration. `engines.node` requires `>= 18`.
461
466
 
462
- Version-by-version verification record:
467
+ Version-by-version compatibility record:
463
468
 
464
469
  | DSH version | Status |
465
470
  | --- | --- |
@@ -473,6 +478,9 @@ Version-by-version verification record:
473
478
  | 0.1.7-rc.2 | compatible |
474
479
  | 0.2.0-rc.1 | compatible |
475
480
  | 0.2.0-rc.2 | compatible |
481
+ | 0.2.1-rc.1 | compatible |
482
+
483
+ **How to read `compatible`.** It means this plugin's codec and manifest contract has been checked against that host generation (code-level comparison plus the unit/contract suite), **not** that an end-to-end probe was run on that exact host build. The table is a declaration; how each claim was checked is summarized here. Real-host probes so far: `headless` (2026-10-15) and `web` (2026-10-16); the `desktop` profile has **not** been probed yet (see [release-checklist.md](https://github.com/gezi-wen/sage-mem/blob/main/docs/release-checklist.md) §4b). CI runs only on Ubuntu + Node 24 against the `0.2.0-rc.1` peer generation; `engines.node` still declares `>=18`, but Node 18 is not part of the CI matrix.
476
484
 
477
485
  Two points worth spelling out:
478
486
 
package/README.zh.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  > File-based cross-session memory for DeepSeek Harness — every memory is a plain markdown file you can open, edit and delete. No database, no worker, no port.
16
16
 
17
- **当前版本 1.0.0(稳定版)** —— 公开承诺、能力边界与「明确做不到」的清单见
17
+ **当前发布版本:`1.6.2`** —— 顶部 npm 徽章是动态权威。**契约基线:`1.0.0`** —— 第一个稳定版,公开承诺、能力边界与「明确做不到」的清单冻结在
18
18
  [契约冻结](https://github.com/gezi-wen/sage-mem/blob/main/docs/1.0.0-contract.md)。
19
19
 
20
20
  如果它对你有用,给个 Star 是最直接的反馈。
@@ -85,6 +85,10 @@ sage-mem 补上这一层,做法是:**把该记住的事写成一张张卡片
85
85
  记忆管理页的「文件列表」头部还有一个「打开记忆文件夹」按钮:用系统文件管理器打开当前记忆目录
86
86
  (Windows Explorer / macOS Finder / Linux 文件管理器)。host 侧起一个 detached 进程就返回,不等窗口。
87
87
 
88
+ 记忆目录还是空的时候,空态里有一个「建立索引骨架」按钮:一键写入 `MEMORY.md` 的引导文件
89
+ (索引格式 + 四类记忆说明)。**已经存在 `MEMORY.md` 时只提示、不覆盖**;它不走新的 remote,
90
+ 用的是原有的保留名读取 / 写入接口。
91
+
88
92
  ## 快速开始
89
93
 
90
94
  需要 **DSH `0.1.2-rc.1` 或更高**(见[兼容性](#兼容性))、**Node.js `>= 18`**。
@@ -462,7 +466,7 @@ sage-mem 只做三件事,全都在你本机:
462
466
 
463
467
  sage-mem 是纯 DSH 插件,用 `package.json` 的 **`engines.dsh`** 声明宿主要求:`>=0.1.2-rc.1` —— **单边开区间、不设上界**,所以任何未来的 DSH 版本都不会被这道声明挡住。`engines.node` 要求 `>= 18`。
464
468
 
465
- 逐版本核对记录:
469
+ 逐版本兼容记录:
466
470
 
467
471
  | DSH 版本 | 状态 |
468
472
  | --- | --- |
@@ -476,6 +480,9 @@ sage-mem 是纯 DSH 插件,用 `package.json` 的 **`engines.dsh`** 声明宿
476
480
  | 0.1.7-rc.2 | compatible |
477
481
  | 0.2.0-rc.1 | compatible |
478
482
  | 0.2.0-rc.2 | compatible |
483
+ | 0.2.1-rc.1 | compatible |
484
+
485
+ **`compatible` 怎么读**:它表示本插件的 codec / manifest 契约在该宿主代上核对过(代码层比对 + 单元/契约测试),**不表示**在那个确切宿主构建上跑过端到端探针。这张表是声明,核对方式总结在下面。真实宿主探针目前只跑过 `headless`(2026-10-15)与 `web`(2026-10-16);`desktop` profile **还没跑过**(见 [release-checklist.md](https://github.com/gezi-wen/sage-mem/blob/main/docs/release-checklist.md) §4b)。CI 只在 Ubuntu + Node 24 上、对着 `0.2.0-rc.1` 一代 peer 跑;`engines.node` 仍声明 `>=18`,但 Node 18 不在 CI 矩阵里。
479
486
 
480
487
  两点值得说明:
481
488
 
package/lib/client.js CHANGED
@@ -3755,7 +3755,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
3755
3755
  // 只负责「按状态/筛选把卡片摆出来 + 四种空态」;状态、筛选语义、动作回调全部由 Section 传。
3756
3756
  // 抽出的判据:它是 Section 里最大的一块纯渲染,而卡片组件(Card / ArchivedCard)本来就在 50 共享层。
3757
3757
  function FilesView(props) {
3758
- const { loading, totalCount, openAdd, statusFilter, setStatusFilter, items, archiveItems, filtered, setTypeFilter, setTagFilter, setQuery, busy, restoring, archDelete, archEdit, archLoad, archSaving, archText, setArchText, openArchEdit, saveArchEdit, cancelArchEdit, restoreFile, setArchDelete, setArchEdit, deleteArchivedFile, expanded, deleting, archiving, archiveReason, setArchiveReason, setArchiving, setDeleting, confirmArchive, toggleLock, openEdit, confirmDelete } = props;
3758
+ const { loading, totalCount, openAdd, createIndexSkeleton, statusFilter, setStatusFilter, items, archiveItems, filtered, setTypeFilter, setTagFilter, setQuery, busy, restoring, archDelete, archEdit, archLoad, archSaving, archText, setArchText, openArchEdit, saveArchEdit, cancelArchEdit, restoreFile, setArchDelete, setArchEdit, deleteArchivedFile, expanded, deleting, archiving, archiveReason, setArchiveReason, setArchiving, setDeleting, confirmArchive, toggleLock, openEdit, confirmDelete } = props;
3759
3759
  let filesView;
3760
3760
  if (loading && totalCount === 0) {
3761
3761
  filesView = h("div", { className: "smem-skeleton", key: "loading", "data-loading": "1" }, [0, 1, 2].map(function (i) {
@@ -3765,6 +3765,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
3765
3765
  filesView = h("div", { className: "smem-empty", key: "empty" }, [
3766
3766
  h("div", { key: "t" }, "还没有记忆文件。对话中值得记住的事会写到这里,也可以手动写入。"),
3767
3767
  h("button", { type: "button", className: "smem-btn smem-btn-primary", key: "add", onClick: openAdd }, "+ 添加记忆"),
3768
+ h("button", { type: "button", className: "smem-btn", key: "idx", title: "写入一份 MEMORY.md 索引骨架(已有索引不会覆盖)", onClick: createIndexSkeleton }, "建立索引骨架"),
3768
3769
  ]);
3769
3770
  } else if (statusFilter === "archived" && archiveItems.length === 0) {
3770
3771
  filesView = h("div", { className: "smem-empty", key: "empty" }, [
@@ -4126,6 +4127,53 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
4126
4127
  setFormFm(null); setFormBaseType(""); setFormTags("");
4127
4128
  }
4128
4129
 
4130
+ // 空记忆库时「建立索引骨架」写进去的引导文件。它只是给人看的起点:
4131
+ // 索引本来就是手写维护的,这里不猜用户有什么记忆、也不生成假条目。
4132
+ const INDEX_SKELETON = [
4133
+ "# 记忆索引",
4134
+ "",
4135
+ "> 这是 sage-mem 的记忆索引骨架:每条记忆一行,链接到同目录下的一个 Markdown 文件。",
4136
+ "> 索引由人(或 agent)手写维护,不是自动生成的;改写前建议先备份。",
4137
+ "",
4138
+ "## 活动记忆",
4139
+ "",
4140
+ "<!-- 一条记忆一行:「- [标题](文件名.md) — 一句话描述」 -->",
4141
+ "",
4142
+ "## 四类记忆",
4143
+ "",
4144
+ "- `user_*`:关于用户的事实与偏好",
4145
+ "- `feedback_*`:用户给的工作方式指导",
4146
+ "- `project_*`:进行中的工作与状态",
4147
+ "- `reference_*`:外部信息在哪找",
4148
+ "",
4149
+ ].join("\n");
4150
+
4151
+ function createIndexSkeleton() {
4152
+ setError(null);
4153
+ setNotice("");
4154
+ if (!remote || typeof remote.readRaw !== "function" || typeof remote.writeRaw !== "function") {
4155
+ setError("宿主侧还没有 readRaw / writeRaw 接口(插件版本较旧)");
4156
+ return;
4157
+ }
4158
+ setBusy(true);
4159
+ // 先读再写:**已有索引绝不覆盖**。readRaw 对不存在的文件会 reject,正好当探针。
4160
+ remote.readRaw("MEMORY.md").then(function () {
4161
+ setBusy(false);
4162
+ setNotice("MEMORY.md 已经存在,没有改动它。");
4163
+ }, function () {
4164
+ return remote.writeRaw("MEMORY.md", INDEX_SKELETON).then(function (res) {
4165
+ const out = unwrap(res);
4166
+ setBusy(false);
4167
+ if (out && out.ok === false) { setError(out.error || "建立索引骨架失败"); return; }
4168
+ setNotice("已建立 MEMORY.md 索引骨架。");
4169
+ load();
4170
+ });
4171
+ }).catch(function (e) {
4172
+ setBusy(false);
4173
+ setError(String((e && e.message) || e));
4174
+ });
4175
+ }
4176
+
4129
4177
  function openMemoryFolder() {
4130
4178
  setError(null);
4131
4179
  if (!remote || typeof remote.openMemoryDir !== "function") {
@@ -4597,7 +4645,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
4597
4645
  // 「文件列表」这一屏:状态筛选决定看哪一批,灰色卡片与留痕块自带「退役」语义。
4598
4646
  // banner 只在**专门看归档**时出现,切到全部时不重复说一遍。
4599
4647
  const filesView = h(FilesView, {
4600
- loading: loading, totalCount: totalCount, openAdd: openAdd,
4648
+ loading: loading, totalCount: totalCount, openAdd: openAdd, createIndexSkeleton: createIndexSkeleton,
4601
4649
  statusFilter: statusFilter, setStatusFilter: setStatusFilter,
4602
4650
  items: items, archiveItems: archiveItems, filtered: filtered,
4603
4651
  setTypeFilter: setTypeFilter, setTagFilter: setTagFilter, setQuery: setQuery,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sage-mem",
3
3
  "description": "Cross-session memory for DeepSeek Harness — plain markdown files, no database, no worker.",
4
- "version": "1.6.0",
4
+ "version": "1.6.2",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -47,7 +47,8 @@
47
47
  "0.1.7-rc.1": "compatible",
48
48
  "0.1.7-rc.2": "compatible",
49
49
  "0.2.0-rc.1": "compatible",
50
- "0.2.0-rc.2": "compatible"
50
+ "0.2.0-rc.2": "compatible",
51
+ "0.2.1-rc.1": "compatible"
51
52
  }
52
53
  }
53
54
  },