sage-mem 1.8.0 → 1.8.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 +3 -1
- package/README.zh.md +3 -1
- package/icon.png +0 -0
- package/lib/client.js +145 -8
- package/package.json +4 -2
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 release: `1.8.
|
|
15
|
+
**Current release: `1.8.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.
|
|
@@ -82,7 +82,9 @@ The UI also carries four more capabilities (all of them optional): the memory ma
|
|
|
82
82
|
|
|
83
83
|
**Since 1.8.0, memory management is also reachable from the sidebar**: a 「记忆管理」(memory management) icon appears in the sidebar and switches the central panel to this page (with a "back to conversation" control at the top).
|
|
84
84
|
It can be turned off in the settings page under 「记忆管理 → 自动做梦 → 设置」(on by default; applies immediately, no restart);
|
|
85
|
+
**since 1.8.2 the same toggle also lives on the plugin's detail page** (「插件 → sage-mem」card) and applies immediately too;
|
|
85
86
|
**turning it off only removes the sidebar icon — the 「设置 → 记忆管理」 entry always stays**.
|
|
87
|
+
Since 1.8.2 the plugin also ships its own icon (a golden stack of memory cards) instead of the generic square.
|
|
86
88
|
|
|
87
89
|
The memory management page's file list also has an **Open memory folder** button: it opens the
|
|
88
90
|
memory directory in the system file manager (Explorer / Finder / the Linux file manager) through a
|
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.8.
|
|
17
|
+
**当前发布版本:`1.8.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 是最直接的反馈。
|
|
@@ -84,7 +84,9 @@ sage-mem 补上这一层,做法是:**把该记住的事写成一张张卡片
|
|
|
84
84
|
|
|
85
85
|
**1.8.0 起,记忆管理还能从侧栏直接进**:侧栏多一个「记忆管理」图标,点它把中央主区切到本页(页顶有「返回会话」)。
|
|
86
86
|
设置页「记忆管理 → 自动做梦 → 设置」里可以关掉这个入口(默认开,立即生效、不用重启);
|
|
87
|
+
**1.8.2 起,插件详情页也有同一个开关**(「插件 → sage-mem」那张卡),改完立即生效;
|
|
87
88
|
**关掉只影响侧栏图标,「设置 → 记忆管理」这个入口永远保留**。
|
|
89
|
+
从 1.8.2 开始,sage-mem 在插件列表里有了自己的图标(金色记忆卡片),不再是默认的通用方图标。
|
|
88
90
|
|
|
89
91
|
记忆管理页的「文件列表」头部还有一个「打开记忆文件夹」按钮:用系统文件管理器打开当前记忆目录
|
|
90
92
|
(Windows Explorer / macOS Finder / Linux 文件管理器)。host 侧起一个 detached 进程就返回,不等窗口。
|
package/icon.png
ADDED
|
Binary file
|
package/lib/client.js
CHANGED
|
@@ -2231,6 +2231,16 @@ const StarmapRuntime = (function (require, _react) {
|
|
|
2231
2231
|
".smem-page-top{display:flex;align-items:center;}",
|
|
2232
2232
|
".smem-page-back{display:inline-flex;align-items:center;gap:5px;border:0;background:transparent;color:var(--dsw-alias-label-secondary);font-family:inherit;font-size:12.5px;line-height:1.4;padding:3px 6px 3px 2px;border-radius:6px;cursor:pointer;}",
|
|
2233
2233
|
".smem-page-back:hover{color:var(--dsw-alias-label-primary,#1f2329);background:var(--dsw-alias-bg-layer-2);}",
|
|
2234
|
+
// ── 1.8.2:插件详情页的「侧栏入口」卡片 ──
|
|
2235
|
+
// 融入宿主插件页:标题 13px/600、说明 12px 次要色,圆角与分隔线只用宿主 token。
|
|
2236
|
+
// 不抄设置页的大标题 —— 这一块是插件页里的一张紧凑卡片,不是另一个 settings section。
|
|
2237
|
+
".smem-plugin-card{display:flex;flex-direction:column;gap:8px;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;padding:12px 14px;background:var(--dsw-alias-bg-layer-1,#fafbfc);box-sizing:border-box;}",
|
|
2238
|
+
".smem-plugin-card-head{display:flex;align-items:center;gap:8px;flex-wrap:wrap;}",
|
|
2239
|
+
".smem-plugin-card-title{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary,#1f2430);line-height:1.4;}",
|
|
2240
|
+
".smem-plugin-card-status{font-size:12px;color:var(--dsw-alias-label-secondary,#6b7280);line-height:1.4;}",
|
|
2241
|
+
".smem-plugin-card-check{font-size:12.5px;}",
|
|
2242
|
+
".smem-plugin-card-note{font-size:12px;line-height:1.55;color:var(--dsw-alias-label-secondary,#6b7280);}",
|
|
2243
|
+
".smem-plugin-card-err{font-size:12px;line-height:1.5;color:var(--dsw-alias-state-error-primary,#ef4444);overflow-wrap:anywhere;}",
|
|
2234
2244
|
// ── 底部一行仓库链接(引流,不抢视线)──
|
|
2235
2245
|
".smem-star{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--dsw-alias-label-tertiary,#8b93a1);padding:2px 2px 0;}",
|
|
2236
2246
|
".smem-star-link{color:var(--dsw-alias-brand-primary,#4d76e6);text-decoration:underline;}",
|
|
@@ -5131,32 +5141,136 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5131
5141
|
/** 该槽位的清理句柄数组(null = 当前没注册)。 */
|
|
5132
5142
|
let sidebarDispose = null;
|
|
5133
5143
|
|
|
5134
|
-
/**
|
|
5144
|
+
/**
|
|
5145
|
+
* 侧栏图标:内联 SVG、currentColor、细线 —— 不引外部图片/字体,跟随宿主明暗主题。
|
|
5146
|
+
*
|
|
5147
|
+
* 1.8.2 起字形与仓库根的 `icon.png` 同源:**叠层记忆卡片**(后面一张只描边、低透明,
|
|
5148
|
+
* 前面一张压在左下),不再是「文档三行」。单色而不是品牌金:侧栏图标必须跟随
|
|
5149
|
+
* 宿主的 currentColor,深色 / 浅色主题下才都可读。
|
|
5150
|
+
*/
|
|
5135
5151
|
function SidebarEntryIcon(props) {
|
|
5136
5152
|
const edge = typeof props.size === "number" && props.size > 0 ? props.size : 16;
|
|
5137
5153
|
return h("svg", {
|
|
5138
5154
|
width: edge, height: edge, viewBox: "0 0 16 16", fill: "none",
|
|
5139
5155
|
"aria-hidden": "true", focusable: "false",
|
|
5140
5156
|
}, [
|
|
5157
|
+
// 后面那张卡:错开、只描边、低透明 —— 表达「一叠记忆」而不是两张孤立的卡。
|
|
5141
5158
|
h("rect", {
|
|
5142
|
-
key: "
|
|
5143
|
-
stroke: "currentColor", strokeWidth: "1.2",
|
|
5159
|
+
key: "back", x: "4.4", y: "2.2", width: "9.4", height: "9", rx: "1.8",
|
|
5160
|
+
stroke: "currentColor", strokeWidth: "1.2", opacity: "0.45",
|
|
5144
5161
|
}),
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5162
|
+
// 前面那张卡:压在左下,实心描边。
|
|
5163
|
+
h("rect", {
|
|
5164
|
+
key: "front", x: "2.2", y: "4.8", width: "9.4", height: "9", rx: "1.8",
|
|
5165
|
+
stroke: "currentColor", strokeWidth: "1.2",
|
|
5148
5166
|
}),
|
|
5149
5167
|
h("path", {
|
|
5150
|
-
key: "
|
|
5168
|
+
key: "l1", d: "M4.7 8.2h4.4",
|
|
5151
5169
|
stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round",
|
|
5152
5170
|
}),
|
|
5153
5171
|
h("path", {
|
|
5154
|
-
key: "
|
|
5172
|
+
key: "l2", d: "M4.7 10.6h3.1",
|
|
5155
5173
|
stroke: "currentColor", strokeWidth: "1.2", strokeLinecap: "round",
|
|
5156
5174
|
}),
|
|
5157
5175
|
]);
|
|
5158
5176
|
}
|
|
5159
5177
|
|
|
5178
|
+
/** 卡片里的错误文案:remote 可能 reject 一个字符串 / 对象 / Error,尽量展开成人话。 */
|
|
5179
|
+
function pluginCardErr(e) {
|
|
5180
|
+
if (!e) return "保存失败(未知原因)";
|
|
5181
|
+
if (e instanceof Error) return e.message || String(e);
|
|
5182
|
+
try {
|
|
5183
|
+
const s = JSON.stringify(e);
|
|
5184
|
+
return s && s !== "{}" ? s.slice(0, 200) : String(e);
|
|
5185
|
+
} catch (_) {
|
|
5186
|
+
return String(e);
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
|
|
5190
|
+
/**
|
|
5191
|
+
* 插件详情页的「侧栏入口」卡片(1.8.2)。
|
|
5192
|
+
*
|
|
5193
|
+
* 槽位 `plugins.bundle.config` 是 keyed 槽(key = 包名),宿主在 sage-mem 的插件页里、
|
|
5194
|
+
* description 与「包含的组件」行之间渲染它,ownerProps 为 `{ view: "summary" | "page" }`:
|
|
5195
|
+
* - summary:标题下的一块摘要位 —— 这里返回 null,不在摘要位重复渲染;
|
|
5196
|
+
* - page:详情页本体(本卡片自己写设置,所以宿主给的是 page)。
|
|
5197
|
+
*
|
|
5198
|
+
* 数据与设置页同一个来源(remote.memory 的 getSettings / setSettings),切换成功后调
|
|
5199
|
+
* 模块级 `setSidebarEntry`:**立即**增删侧栏图标,不需要刷新 / 重启。
|
|
5200
|
+
* 勾选态一律由服务端回传的 settings 派生 —— 失败时什么都不改,于是自动回到原值。
|
|
5201
|
+
*/
|
|
5202
|
+
function SidebarEntryConfigCard(props) {
|
|
5203
|
+
const ctx = props && props.ctx;
|
|
5204
|
+
// 必须走 stableRemote:真实宿主里 ctx.get() 每次返回新对象,进依赖数组会无限重跑。
|
|
5205
|
+
const remote = stableRemote(ctx, "remote.memory");
|
|
5206
|
+
const sOn = react.useState(true); const on = sOn[0], setOn = sOn[1];
|
|
5207
|
+
const sBusy = react.useState(false); const busy = sBusy[0], setBusy = sBusy[1];
|
|
5208
|
+
const sErr = react.useState(""); const err = sErr[0], setErr = sErr[1];
|
|
5209
|
+
|
|
5210
|
+
react.useEffect(function () {
|
|
5211
|
+
if (!remote || typeof remote.getSettings !== "function") return undefined;
|
|
5212
|
+
let alive = true;
|
|
5213
|
+
Promise.resolve(remote.getSettings()).then(function (out) {
|
|
5214
|
+
if (!alive) return;
|
|
5215
|
+
const loaded = unwrap(out) || {};
|
|
5216
|
+
// 旧宿主没有 ui 字段 / 值不是布尔 → 保持默认「开」,不弹错、不阻塞界面。
|
|
5217
|
+
if (loaded.ui && typeof loaded.ui.sidebarEntry === "boolean") setOn(loaded.ui.sidebarEntry);
|
|
5218
|
+
}, function () {
|
|
5219
|
+
// 读设置失败也按「开」显示:这里只是插件页的一张卡片,不该因为它报红。
|
|
5220
|
+
});
|
|
5221
|
+
return function () { alive = false; };
|
|
5222
|
+
}, [remote]);
|
|
5223
|
+
|
|
5224
|
+
// summary 位不渲染(hooks 必须在上方无条件调用完再返回,规则与顺序都不变)。
|
|
5225
|
+
if (props && props.view === "summary") return null;
|
|
5226
|
+
|
|
5227
|
+
function toggle(next) {
|
|
5228
|
+
if (busy) return;
|
|
5229
|
+
if (!remote || typeof remote.setSettings !== "function") {
|
|
5230
|
+
setErr("宿主侧还没有 setSettings 接口(插件版本较旧)");
|
|
5231
|
+
return;
|
|
5232
|
+
}
|
|
5233
|
+
setBusy(true); setErr("");
|
|
5234
|
+
Promise.resolve(remote.setSettings({ ui: { sidebarEntry: !!next } })).then(function (out) {
|
|
5235
|
+
setBusy(false);
|
|
5236
|
+
// setSettings 是 { ok, settings, restartRequired } 形状,不要过 unwrap。
|
|
5237
|
+
if (out && out.ok === false) {
|
|
5238
|
+
setErr(out.error || "保存被拒绝");
|
|
5239
|
+
return;
|
|
5240
|
+
}
|
|
5241
|
+
const settings = out && out.settings ? out.settings : null;
|
|
5242
|
+
const applied = settings && settings.ui && typeof settings.ui.sidebarEntry === "boolean"
|
|
5243
|
+
? settings.ui.sidebarEntry
|
|
5244
|
+
: !!next;
|
|
5245
|
+
setOn(applied);
|
|
5246
|
+
// 写盘成功之后才动槽位:设置没落盘时界面不该先变一个样。
|
|
5247
|
+
setSidebarEntry(!!next);
|
|
5248
|
+
}, function (e) {
|
|
5249
|
+
setBusy(false);
|
|
5250
|
+
setErr(pluginCardErr(e));
|
|
5251
|
+
});
|
|
5252
|
+
}
|
|
5253
|
+
|
|
5254
|
+
return h("div", { className: "smem-plugin-card" }, [
|
|
5255
|
+
h("div", { className: "smem-plugin-card-head", key: "h" }, [
|
|
5256
|
+
h("span", { className: "smem-plugin-card-title", key: "t" }, "侧栏入口"),
|
|
5257
|
+
h("span", { className: "smem-plugin-card-status", key: "s" }, on ? "已开启" : "已关闭"),
|
|
5258
|
+
]),
|
|
5259
|
+
h("label", { className: "smem-check smem-plugin-card-check", key: "row" }, [
|
|
5260
|
+
h("input", {
|
|
5261
|
+
type: "checkbox", key: "c",
|
|
5262
|
+
checked: on,
|
|
5263
|
+
disabled: busy,
|
|
5264
|
+
onChange: function (e) { toggle(!!(e.target && e.target.checked)); },
|
|
5265
|
+
}),
|
|
5266
|
+
h("span", { key: "l" }, "在侧栏显示「记忆管理」入口"),
|
|
5267
|
+
]),
|
|
5268
|
+
h("div", { className: "smem-plugin-card-note", key: "n" },
|
|
5269
|
+
"默认开启;改动立即生效,不需要重启。关闭后「设置 → 记忆管理」入口仍然保留。"),
|
|
5270
|
+
err ? h("div", { className: "smem-plugin-card-err", key: "e" }, err) : null,
|
|
5271
|
+
]);
|
|
5272
|
+
}
|
|
5273
|
+
|
|
5160
5274
|
/**
|
|
5161
5275
|
* 返回会话:宿主 layout 服务的 `selectPanel(null)` 就是把中央主区还给 Conversation。
|
|
5162
5276
|
* 取不到 layout(旧外壳 / 测试环境)时是**空操作**,不影响页面其它部分。
|
|
@@ -5288,6 +5402,29 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5288
5402
|
sidebarEnabled = true;
|
|
5289
5403
|
}
|
|
5290
5404
|
setSidebarEntry(sidebarEnabled);
|
|
5405
|
+
|
|
5406
|
+
// ── 1.8.2 插件详情页的「侧栏入口」卡片 ──────────────────────────────
|
|
5407
|
+
// `plugins.bundle.config` 是 keyed 槽,key = 本 bundle 的 **package name**(sage-mem)。
|
|
5408
|
+
// 宿主在插件页的 description 与「包含的组件」行之间渲染它;ownerProps 形如
|
|
5409
|
+
// `{ view: "summary" | "page" }`,卡片里自己再判一次(summary 返回 null)。
|
|
5410
|
+
//
|
|
5411
|
+
// ⚠️ 这个槽 0.1.7+ 才有。旧宿主上 inject 会**抛错**,所以必须整段 try/catch ——
|
|
5412
|
+
// 否则一个可选的插件页卡片会把设置页与侧栏入口一起拖垮(整个 client 插件不 apply)。
|
|
5413
|
+
// 放在 apply 最后:即使这里因宿主差异出问题,前面的界面已经挂上去了。
|
|
5414
|
+
try {
|
|
5415
|
+
if (ctx.slots && typeof ctx.slots.inject === "function") {
|
|
5416
|
+
ctx.slots.inject("plugins.bundle.config", function () {
|
|
5417
|
+
return ctx.slots.register({
|
|
5418
|
+
name: "plugins.bundle.config",
|
|
5419
|
+
key: "sage-mem",
|
|
5420
|
+
}, function (props) {
|
|
5421
|
+
return react.createElement(SidebarEntryConfigCard, { ...(props || {}), ctx });
|
|
5422
|
+
});
|
|
5423
|
+
});
|
|
5424
|
+
}
|
|
5425
|
+
} catch (e) {
|
|
5426
|
+
// 旧宿主没有这个槽 / 不接受注册:静默跳过,绝不影响其余界面。
|
|
5427
|
+
}
|
|
5291
5428
|
}
|
|
5292
5429
|
|
|
5293
5430
|
exports.apply = apply;
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
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.8.
|
|
4
|
+
"version": "1.8.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
7
|
+
"icon": "./icon.png",
|
|
7
8
|
"exports": {
|
|
8
9
|
".": "./lib/index.js",
|
|
9
10
|
"./client": "./lib/client.js",
|
|
@@ -13,7 +14,8 @@
|
|
|
13
14
|
"files": [
|
|
14
15
|
"lib/",
|
|
15
16
|
"cordis.patch.yml",
|
|
16
|
-
"NOTICE"
|
|
17
|
+
"NOTICE",
|
|
18
|
+
"icon.png"
|
|
17
19
|
],
|
|
18
20
|
"scripts": {
|
|
19
21
|
"test": "node test/run.mjs",
|