sage-mem 2.0.0 → 2.1.0
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 +5 -5
- package/README.zh.md +5 -5
- package/lib/client.js +656 -194
- package/lib/recall/injector.js +52 -1
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -2923,14 +2923,18 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
2923
2923
|
: null);
|
|
2924
2924
|
|
|
2925
2925
|
/**
|
|
2926
|
-
* 「注入调试」面板(1.7.0
|
|
2926
|
+
* 「注入调试」面板(1.7.0;2.0.1 起显示有效性抑制明细)。
|
|
2927
2927
|
*
|
|
2928
2928
|
* 只读:没有「清空 / 重跑」按钮 —— 账是注入链在 `SAGE_MEM_DEBUG=1` 时自动记的,
|
|
2929
2929
|
* 人能做的是看,以及点「刷新」再取一次。入口只在开关打开时出现(宿主告诉我们)。
|
|
2930
2930
|
*
|
|
2931
|
-
* 每一条记录的口径写在 `lib/recall/
|
|
2931
|
+
* 每一条记录的口径写在 `lib/recall/injector.js` 的 makeDebug 注释里;这里只负责把
|
|
2932
2932
|
* 「为什么这一步没注入」翻译成人话 —— 那是这个面板存在的唯一理由。**尤其是
|
|
2933
2933
|
* `empty + deduped`**:每轮多步里它占大多数,不解释就会被当成「插件坏了」。
|
|
2934
|
+
*
|
|
2935
|
+
* 2.0.0 让失效 / 未裁决矛盾的记忆不再进正文。2.0.1 把这两类的**文件名、理由与
|
|
2936
|
+
* 冲突 id** 单列一行:否则「这条记忆不见了」在面板上只能看到它不在 injected 里,
|
|
2937
|
+
* 分不清是没检索到还是被新规则挡住 —— 而那正是 2.0.0 之后最需要解释的一种消失。
|
|
2934
2938
|
*/
|
|
2935
2939
|
function RecallDebugPanel(props) {
|
|
2936
2940
|
const data = props.data || {};
|
|
@@ -2965,6 +2969,9 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
2965
2969
|
}));
|
|
2966
2970
|
}
|
|
2967
2971
|
const injected = latest && Array.isArray(latest.injected) ? latest.injected : [];
|
|
2972
|
+
// 2.0.1:被有效性挡掉的条目。旧记录没有这个字段 → 空数组(面板显示「无」),
|
|
2973
|
+
// 不因为多一个可选字段就把老账读崩。
|
|
2974
|
+
const validitySuppressed = latest && Array.isArray(latest.validitySuppressed) ? latest.validitySuppressed : [];
|
|
2968
2975
|
const histogram = recs.slice(1).map(function (r, i) {
|
|
2969
2976
|
return h("div", { className: "smem-dbg-hist-row", key: "h" + i }, [
|
|
2970
2977
|
h("span", { className: "smem-dbg-time", key: "t" }, clock(r.at)),
|
|
@@ -3008,6 +3015,23 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
3008
3015
|
"正文字节 " + String(latest.chars || 0) + "(本代累计 " + String(latest.sessionChars || 0) + ")" +
|
|
3009
3016
|
" · 状态卡 " + (latest.cardChars ? String(latest.cardChars) + " 字节" : "未发") +
|
|
3010
3017
|
" · 预算挡掉 " + String(latest.skippedByBudget || 0) + " 条"),
|
|
3018
|
+
h("div", { className: "smem-dbg-row", key: "vs" }, [
|
|
3019
|
+
h("span", { className: "smem-label", key: "l" }, "有效性抑制"),
|
|
3020
|
+
validitySuppressed.length
|
|
3021
|
+
? h("span", { className: "smem-dbg-scores", key: "v" }, validitySuppressed.map(function (x, i) {
|
|
3022
|
+
const reason = String((x && x.label) || (x && x.reason) || "未知理由");
|
|
3023
|
+
const cid = x && x.conflictId ? " · " + String(x.conflictId) : "";
|
|
3024
|
+
return h("span", { className: "smem-dbg-score", key: String((x && x.file) || i) + "-" + i }, [
|
|
3025
|
+
h("span", { className: "smem-dbg-file", key: "f" }, String((x && x.file) || "?")),
|
|
3026
|
+
h("span", { className: "smem-dbg-num", key: "r" }, reason + cid),
|
|
3027
|
+
]);
|
|
3028
|
+
}))
|
|
3029
|
+
: h("span", { className: "smem-muted", key: "v" }, "无"),
|
|
3030
|
+
]),
|
|
3031
|
+
validitySuppressed.length
|
|
3032
|
+
? h("div", { className: "smem-ar-hint", key: "vshint" },
|
|
3033
|
+
"被标失效或待裁决矛盾挡掉的记忆不会进正文 —— 这不是检索没命中,是 2.0.0 的有效性规则生效;去「冲突」页签可裁决。")
|
|
3034
|
+
: null,
|
|
3011
3035
|
h("div", { className: "smem-ar-hint", key: "hint" },
|
|
3012
3036
|
"注入每走一步都会尝试一次;同题同命中集不会重复注入 —— 所以「空」多半是去重生效,不是坏了。"),
|
|
3013
3037
|
histogram.length
|
|
@@ -3855,6 +3879,220 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
3855
3879
|
"notice.conflictDismissed": "已标记为已裁决 —— 没有改任何记忆文件",
|
|
3856
3880
|
"notice.conflictUndone": "已撤销 {file} 的失效标记 —— 它重新参与注入",
|
|
3857
3881
|
"notice.conflictRemoved": "已删除冲突记录(记忆文件没动)",
|
|
3882
|
+
|
|
3883
|
+
// ── 2.1.0: autodream tab (60-autodream-panel.js) ──
|
|
3884
|
+
"ad.tab.run": "运行",
|
|
3885
|
+
"ad.tab.settings": "设置",
|
|
3886
|
+
"ad.tab.records": "记录",
|
|
3887
|
+
"ad.title": "自动做梦",
|
|
3888
|
+
"ad.switch.onTitle": "点击停用",
|
|
3889
|
+
"ad.switch.offTitle": "点击启用",
|
|
3890
|
+
"ad.switch.aria": "启用或停用自动做梦",
|
|
3891
|
+
"ad.status.running": "运行中 · {phase}",
|
|
3892
|
+
"ad.status.preparing": "准备中",
|
|
3893
|
+
"ad.status.idle": "已启用 · 空闲",
|
|
3894
|
+
"ad.status.off": "未启用",
|
|
3895
|
+
"ad.note.what": "把记忆目录通读一遍:合并重复的、删掉被推翻的、修索引与断链、把相对日期换成绝对日期。",
|
|
3896
|
+
"ad.row.trigger": "触发",
|
|
3897
|
+
"ad.trigger.manual": "只手动",
|
|
3898
|
+
"ad.trigger.auto": "满足条件自动跑",
|
|
3899
|
+
"ad.row.apply": "改动方式",
|
|
3900
|
+
"ad.apply.report": "只出报告",
|
|
3901
|
+
"ad.apply.apply": "直接改写(先快照)",
|
|
3902
|
+
"ad.row.source": "输入源",
|
|
3903
|
+
"ad.source.memory": "仅记忆目录",
|
|
3904
|
+
"ad.source.sessions": "记忆 + 会话记录",
|
|
3905
|
+
"ad.note.sourceSessions": "会解压会话记录做定向搜索 —— 私密对话会进入模型上下文,token 成本也更高。",
|
|
3906
|
+
"ad.note.sourceMemory": "只看记忆文件本身,不碰会话记录。",
|
|
3907
|
+
"ad.note.applySnapshot": "改写前会自动把整个记忆目录快照一份(memory 上一层 .sage-mem/autodream/snapshots/),改坏可回退。",
|
|
3908
|
+
"ad.note.reportOnly": "只读模式:这一趟连写工具都不会挂给模型,所以它不可能改到任何文件。",
|
|
3909
|
+
"ad.row.model": "模型",
|
|
3910
|
+
"ad.model.followDefault": "跟随当前会话默认模型",
|
|
3911
|
+
"ad.model.defaultRoute": "({provider} / {model})",
|
|
3912
|
+
"ad.model.currentOutOfCatalog": "(配置里指定,不在目录中)",
|
|
3913
|
+
"ad.model.placeholder.provider": "provider(留空=跟随默认)",
|
|
3914
|
+
"ad.model.placeholder.model": "model(留空=跟随默认)",
|
|
3915
|
+
"ad.model.saved": "模型已保存",
|
|
3916
|
+
"ad.action.save": "保存",
|
|
3917
|
+
"ad.action.manualEntry": "手填",
|
|
3918
|
+
"ad.loadingModels": "模型目录载入中…",
|
|
3919
|
+
"ad.route.bad": "实际生效路线:拿不到 —— {error}",
|
|
3920
|
+
"ad.route.badFallback": "宿主没能解析出 provider / model(配置留空且取不到会话默认模型)",
|
|
3921
|
+
"ad.route.ok": "实际生效路线:{provider} / {model}",
|
|
3922
|
+
"ad.route.fromDefault": "(跟随默认)",
|
|
3923
|
+
"ad.route.fromConfig": "(配置指定)",
|
|
3924
|
+
"ad.action.runningNow": "正在整理…",
|
|
3925
|
+
"ad.action.runNow": "立即整理",
|
|
3926
|
+
"ad.run.steps": "已跑 {count} 轮 · {phase}",
|
|
3927
|
+
"ad.run.hoursSince": "距上次整理 {hours} 小时",
|
|
3928
|
+
"ad.run.never": "从未整理过",
|
|
3929
|
+
"ad.run.progress": "第 {step} 轮:{tools}",
|
|
3930
|
+
"ad.run.stepDone": "结束",
|
|
3931
|
+
"ad.toolSep": "、",
|
|
3932
|
+
"ad.errSep": ";",
|
|
3933
|
+
"ad.progressSep": " · ",
|
|
3934
|
+
"ad.last.prefix": "上次 {at} · {result}",
|
|
3935
|
+
"ad.last.ok": "成功",
|
|
3936
|
+
"ad.last.applied": ",改写了 {count} 个文件",
|
|
3937
|
+
"ad.last.reportOnly": "(只出报告)",
|
|
3938
|
+
"ad.last.problems": " · 结构问题 {before} → {after}",
|
|
3939
|
+
"ad.last.tokens": " · token {in} / {out}",
|
|
3940
|
+
"ad.last.failed": "失败:{error}",
|
|
3941
|
+
"ad.unknown": "未知",
|
|
3942
|
+
"ad.last.truncated": " · ⚠ 报告被截断(残篇)",
|
|
3943
|
+
"ad.last.retried": " · 零正文重试 {count} 次后补齐",
|
|
3944
|
+
"ad.last.continuations": " · 续写 {count} 轮",
|
|
3945
|
+
"ad.last.report": " · 报告 {name}",
|
|
3946
|
+
"ad.sidebar.title": "侧栏入口",
|
|
3947
|
+
"ad.sidebar.on": "已开启",
|
|
3948
|
+
"ad.sidebar.off": "已关闭",
|
|
3949
|
+
"ad.sidebar.note": "在 DSH 侧栏显示「记忆管理」图标,点它把中央主区切到本页。默认开启;关闭后「设置 → 记忆管理」这个入口仍然保留。改动立即生效,不需要重启。",
|
|
3950
|
+
"ad.sidebar.label": "在侧栏显示「记忆管理」入口",
|
|
3951
|
+
"ad.limits.title": "记忆注入参数",
|
|
3952
|
+
"ad.limits.pending": "有改动待生效",
|
|
3953
|
+
"ad.limits.restartWarn": "⚠ 这些值在 DSH 启动时就固化了 —— 要重启 DSH 才生效。下面每行都写清「当前 → 重启后」。",
|
|
3954
|
+
"ad.limits.restartNote": "这些值在 DSH 启动时固化:保存后要重启 DSH 才生效 —— 所以每行都会标出「当前生效」与「重启后」。",
|
|
3955
|
+
"ad.limit.maxResults": "一次注入条数",
|
|
3956
|
+
"ad.limit.maxChars": "单条字符上限",
|
|
3957
|
+
"ad.limit.maxBaseline": "baseline 名额",
|
|
3958
|
+
"ad.limit.maxSessionBytes": "单会话字节预算",
|
|
3959
|
+
"ad.limit.staleDays": "新鲜度门槛",
|
|
3960
|
+
"ad.unit.items": "条",
|
|
3961
|
+
"ad.unit.chars": "字符",
|
|
3962
|
+
"ad.unit.bytes": "字节",
|
|
3963
|
+
"ad.unit.days": "天",
|
|
3964
|
+
"ad.unit.copies": "份",
|
|
3965
|
+
"ad.limit.labelTitle": "{unit} · 范围 {lo}–{hi}",
|
|
3966
|
+
"ad.limit.inputTitle": "范围 {lo}–{hi} {unit}",
|
|
3967
|
+
"ad.limit.diff": "当前 {current} → 重启后 {pending}",
|
|
3968
|
+
"ad.limit.range": "范围 {lo}–{hi}",
|
|
3969
|
+
"ad.limit.current": " · 当前生效 {current}",
|
|
3970
|
+
"ad.action.saving": "保存中…",
|
|
3971
|
+
"ad.action.saveLimits": "保存注入参数",
|
|
3972
|
+
"ad.limits.saveHint": "保存后需重启 DSH 才生效",
|
|
3973
|
+
"ad.reserved.title": "保留名保护名单",
|
|
3974
|
+
"ad.reserved.subtitle": "名单里的文件不参与注入",
|
|
3975
|
+
"ad.reserved.note": "内置的两个固定不可删;下面可以再加自己的(只写文件名,不要带路径)。改完需重启 DSH 才生效。",
|
|
3976
|
+
"ad.reserved.fixedSuffix": " · 固定",
|
|
3977
|
+
"ad.reserved.fixedTitle": "内置固定,不可删除",
|
|
3978
|
+
"ad.reserved.removeTitle": "从名单里移除 {name}",
|
|
3979
|
+
"ad.reserved.empty": "(还没有自己加的保留名)",
|
|
3980
|
+
"ad.reserved.placeholder": "例如 project_notes.md(要 .md 文件名)",
|
|
3981
|
+
"ad.action.addToList": "加入名单",
|
|
3982
|
+
"ad.advanced": "高级设置",
|
|
3983
|
+
"ad.adv.autoTrigger": "自动触发",
|
|
3984
|
+
"ad.adv.since": "距上次 ≥",
|
|
3985
|
+
"ad.adv.hoursAndSessions": "小时,且期间 ≥",
|
|
3986
|
+
"ad.adv.sessionsUpdated": "个会话有更新",
|
|
3987
|
+
"ad.adv.maxSteps": "最多轮数",
|
|
3988
|
+
"ad.adv.stepNote": "一轮 = 一次模型调用 + 它要的工具调用",
|
|
3989
|
+
"ad.adv.maxOutput": "单次输出上限",
|
|
3990
|
+
"ad.adv.maxOutputNote": "token(思考与正文共用;报告残篇时先调它)",
|
|
3991
|
+
"ad.adv.keepSnapshots": "保留快照",
|
|
3992
|
+
"ad.adv.paths": "配置与状态:{path}",
|
|
3993
|
+
"ad.records.snapshots": "回滚点",
|
|
3994
|
+
"ad.records.countSnaps": "{count} 个",
|
|
3995
|
+
"ad.action.refresh": "刷新",
|
|
3996
|
+
"ad.row.rollbackScope": "回滚范围",
|
|
3997
|
+
"ad.scope.files": "只恢复本次动过的文件",
|
|
3998
|
+
"ad.scope.all": "整目录回到该时点",
|
|
3999
|
+
"ad.note.rollback": "回滚前会自动把整个记忆目录再存一份保护快照 —— 所以回滚本身也能再退回来。多出来的文件只移进 archive/,不删除。",
|
|
4000
|
+
"ad.snap.empty": "还没有回滚点 —— 用「直接改写」跑过一次就有了。",
|
|
4001
|
+
"ad.snap.legacyLabel": "旧版快照",
|
|
4002
|
+
"ad.snap.runLabel": "运行 {id}",
|
|
4003
|
+
"ad.snap.protectionLabel": "回滚保护",
|
|
4004
|
+
"ad.snap.meta": "{at} · {files} 个文件",
|
|
4005
|
+
"ad.snap.legacyBadge": "旧版",
|
|
4006
|
+
"ad.action.rollback": "回滚",
|
|
4007
|
+
"ad.snap.confirmTitle": "确认回滚到 {at}?",
|
|
4008
|
+
"ad.snap.confirmScope": "范围:{scope}",
|
|
4009
|
+
"ad.snap.scopeAll": "整目录回到该时点 —— 快照里没有、当前目录里有的文件会被移进 archive/(不删除)",
|
|
4010
|
+
"ad.snap.scopeFiles": "只恢复这次运行动过的文件 —— 该次新建的移进 archive/,该次归档的移回顶层",
|
|
4011
|
+
"ad.snap.confirmSuffix": "。回滚前会先存一份保护快照,所以这一步本身也能退。",
|
|
4012
|
+
"ad.action.rollingBack": "回滚中…",
|
|
4013
|
+
"ad.action.confirmRollback": "确认回滚",
|
|
4014
|
+
"ad.action.cancel": "取消",
|
|
4015
|
+
"ad.rb.rolledBack": "已回滚到 {at}:恢复 {restored} 个文件,移走 {parked} 个",
|
|
4016
|
+
"ad.rb.skipped": ",跳过 {count} 个(详见回滚报告)",
|
|
4017
|
+
"ad.rb.protection": " · 保护快照 {id}",
|
|
4018
|
+
"ad.rb.report": " · 报告 {name}",
|
|
4019
|
+
"ad.records.runs": "整理记录",
|
|
4020
|
+
"ad.records.countRuns": "{count} 条",
|
|
4021
|
+
"ad.records.runsNote": "每一趟都留一份整理声明:改了什么、为什么改、哪些想法被拒了。点一条看全文。",
|
|
4022
|
+
"ad.records.runsEmpty": "还没有整理记录 —— 跑一次就有了。",
|
|
4023
|
+
"ad.badge.apply": "直接改写",
|
|
4024
|
+
"ad.badge.report": "只出报告",
|
|
4025
|
+
"ad.run.changes": "改 {count} 条",
|
|
4026
|
+
"ad.run.reportName": "报告 {name}",
|
|
4027
|
+
"ad.run.rolledBack": "已回滚",
|
|
4028
|
+
"ad.run.rolledBackAt": "已回滚 · {at}",
|
|
4029
|
+
"ad.action.collapse": "收起",
|
|
4030
|
+
"ad.action.showDeclaration": "看声明",
|
|
4031
|
+
"ad.records.reports": "历史报告",
|
|
4032
|
+
"ad.records.countReports": "{count} 份",
|
|
4033
|
+
"ad.records.reportsEmpty": "还没有报告 —— 跑一次就有了。报告写在 memory/autodream/ 下,不会混进记忆列表。",
|
|
4034
|
+
"ad.action.view": "查看",
|
|
4035
|
+
"ad.loading": "载入中…",
|
|
4036
|
+
"ad.empty": "(空)",
|
|
4037
|
+
"ad.emptyDeclaration": "(这一趟没有留下声明正文)",
|
|
4038
|
+
"ad.err.readFailed": "读取失败:{error}",
|
|
4039
|
+
"ad.err.rollbackFailed": "回滚失败:{error}",
|
|
4040
|
+
"ad.unknownReason": "未知原因",
|
|
4041
|
+
"ad.err.startFailed": "启动失败",
|
|
4042
|
+
"ad.notice.started": "已开始整理 —— 进度每 2 秒刷新一次",
|
|
4043
|
+
"ad.err.remoteUnavailable": "remote.autodream 不可用 —— 插件的 宿主侧可能没加载成功,看 DSH 启动日志",
|
|
4044
|
+
"ad.err.noInterface": "宿主侧没有 {name} 接口(版本较旧?)",
|
|
4045
|
+
"ad.err.noSetSettings": "宿主侧还没有 setSettings 接口(插件版本较旧)",
|
|
4046
|
+
"ad.err.limitInteger": "{label}要整数",
|
|
4047
|
+
"ad.err.limitRange": "{label} 要在 {lo}–{hi} 之间",
|
|
4048
|
+
"ad.err.notSaved": "没保存 —— {detail}",
|
|
4049
|
+
"ad.err.saveRejected": "保存被拒绝",
|
|
4050
|
+
"ad.notice.limitsSaved": "已保存 —— 需重启 DSH 才生效",
|
|
4051
|
+
"ad.notice.sidebarOn": "侧栏入口已开启(立即生效)",
|
|
4052
|
+
"ad.notice.sidebarOff": "侧栏入口已关闭(设置页入口仍在)",
|
|
4053
|
+
"ad.err.reservedRejected": "名单被拒绝",
|
|
4054
|
+
"ad.notice.reservedSaved": "保留名名单已保存 —— 需重启 DSH 才生效",
|
|
4055
|
+
"ad.err.reservedNeedName": "先写一个文件名,例如 project_notes.md",
|
|
4056
|
+
"ad.err.reservedBadName": "保留名要是一个 .md 文件名(不能带路径):{name}",
|
|
4057
|
+
"ad.err.reservedDup": "名单里已经有 {name} 了",
|
|
4058
|
+
"ad.note.modelCatalogUnavailable": "模型目录不可用({error})—— 请在下面手填 provider 与 model。",
|
|
4059
|
+
|
|
4060
|
+
// ── 2.1.0: auto-archive settings (62-archive-settings.js) ──
|
|
4061
|
+
"archive.title": "自动归档做什么",
|
|
4062
|
+
"archive.intro1": "闲置太久没被注入过的记忆,会被移进 archive/ —— 不删除,随时能恢复。这一页决定「多久算闲置」,以及要不要真的动手。",
|
|
4063
|
+
"archive.intro2": "它不调模型:候选完全按访问台账的天数算,同一份输入每次算出来都一样。",
|
|
4064
|
+
"archive.intro3": "想现在看看会被收走哪些:文件列表 →「工具 ▾」→ 归档候选。",
|
|
4065
|
+
"archive.modeTitle": "自动归档",
|
|
4066
|
+
"archive.mode.off": "关闭",
|
|
4067
|
+
"archive.mode.report": "只出报告",
|
|
4068
|
+
"archive.mode.auto": "满足条件自动跑",
|
|
4069
|
+
"archive.cost.off": "关闭:一个文件都不动 —— 候选只列在清单里,等你手点。",
|
|
4070
|
+
"archive.cost.report": "只出报告:候选写进整理报告,一个文件都不动。默认档,最保守。",
|
|
4071
|
+
"archive.cost.auto": "满足条件自动跑:会真的把候选移进 archive/(只移不删,随时可恢复);若「改动方式」是只出报告,这一档会自动降级成只出报告。",
|
|
4072
|
+
"archive.degrade": "注意:「自动做梦 → 改动方式」现在是只出报告 —— 这一档会自动降级成只出报告,不会动文件。",
|
|
4073
|
+
"archive.th.title": "闲置多少天算可以归档",
|
|
4074
|
+
"archive.th.project": "项目",
|
|
4075
|
+
"archive.th.reference": "参考",
|
|
4076
|
+
"archive.th.user": "用户",
|
|
4077
|
+
"archive.th.hintProject": "project_* 连续这么多天没被注入 → 进候选",
|
|
4078
|
+
"archive.th.hintReference": "reference_* 同上(资料一般比项目留得久)",
|
|
4079
|
+
"archive.th.hintUser": "user_* 同上(人设这类动它要格外谨慎)",
|
|
4080
|
+
"archive.loadingConfig": "读配置中…",
|
|
4081
|
+
"archive.action.save": "保存归档条件",
|
|
4082
|
+
"archive.action.saving": "保存中…",
|
|
4083
|
+
"archive.note.immediate": "保存即生效,不用重启 DSH",
|
|
4084
|
+
"archive.note.thresholds": "天数按访问台账算(这条记忆上次被注入给模型是什么时候)。feedback_* 永不入选;锁定的记忆也不会被选中。",
|
|
4085
|
+
"archive.err.noConfig": "宿主侧没有 autodream 配置接口(插件版本较旧)",
|
|
4086
|
+
"archive.err.readConfig": "读不出自动做梦配置",
|
|
4087
|
+
"archive.err.readFailed": "读配置失败:{error}",
|
|
4088
|
+
"archive.err.saveFailed": "保存失败:{error}",
|
|
4089
|
+
"archive.err.notApplied": "保存没生效:{error}。setConfig 对非法值会忽略并保留原值 —— 请重试,或看 DSH 启动日志。",
|
|
4090
|
+
"archive.notice.modeSet": "自动归档已设为「{mode}」",
|
|
4091
|
+
"archive.err.modeStill": "配置里仍是「{mode}」",
|
|
4092
|
+
"archive.unknown": "读不出来",
|
|
4093
|
+
"archive.err.thresholdBad": "{label}的阈值要是 1~3650 之间的天数(现在是「{value}」)",
|
|
4094
|
+
"archive.notice.thresholdsSaved": "归档条件已保存",
|
|
4095
|
+
"archive.err.thresholdStill": "{label} 仍是 {value}",
|
|
3858
4096
|
};
|
|
3859
4097
|
|
|
3860
4098
|
const EN = {
|
|
@@ -4119,6 +4357,220 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4119
4357
|
"notice.conflictDismissed": "Marked as decided — no memory file was changed",
|
|
4120
4358
|
"notice.conflictUndone": "Undid the invalidation of {file} — it takes part in injection again",
|
|
4121
4359
|
"notice.conflictRemoved": "Deleted the conflict record (no memory file was touched)",
|
|
4360
|
+
|
|
4361
|
+
// ── 2.1.0: autodream tab (60-autodream-panel.js) ──
|
|
4362
|
+
"ad.tab.run": "Run",
|
|
4363
|
+
"ad.tab.settings": "Settings",
|
|
4364
|
+
"ad.tab.records": "Records",
|
|
4365
|
+
"ad.title": "Autodream",
|
|
4366
|
+
"ad.switch.onTitle": "Click to disable",
|
|
4367
|
+
"ad.switch.offTitle": "Click to enable",
|
|
4368
|
+
"ad.switch.aria": "Enable or disable Autodream",
|
|
4369
|
+
"ad.status.running": "Running · {phase}",
|
|
4370
|
+
"ad.status.preparing": "preparing",
|
|
4371
|
+
"ad.status.idle": "Enabled · idle",
|
|
4372
|
+
"ad.status.off": "Disabled",
|
|
4373
|
+
"ad.note.what": "Read the whole memory directory: merge duplicates, drop what has been overruled, fix the index and broken links, and turn relative dates into absolute ones.",
|
|
4374
|
+
"ad.row.trigger": "Trigger",
|
|
4375
|
+
"ad.trigger.manual": "Manual only",
|
|
4376
|
+
"ad.trigger.auto": "Run when conditions are met",
|
|
4377
|
+
"ad.row.apply": "Changes",
|
|
4378
|
+
"ad.apply.report": "Report only",
|
|
4379
|
+
"ad.apply.apply": "Rewrite directly (snapshot first)",
|
|
4380
|
+
"ad.row.source": "Input",
|
|
4381
|
+
"ad.source.memory": "Memory directory only",
|
|
4382
|
+
"ad.source.sessions": "Memory + session logs",
|
|
4383
|
+
"ad.note.sourceSessions": "Session logs are unpacked for a targeted search — private conversations enter the model context, and token cost is higher.",
|
|
4384
|
+
"ad.note.sourceMemory": "Only the memory files themselves; session logs are never touched.",
|
|
4385
|
+
"ad.note.applySnapshot": "Before rewriting, the whole memory directory is snapshotted (under .sage-mem/autodream/snapshots/, one level above memory), so a bad run can be rolled back.",
|
|
4386
|
+
"ad.note.reportOnly": "Read-only mode: this run does not even give the model write tools, so it cannot change any file.",
|
|
4387
|
+
"ad.row.model": "Model",
|
|
4388
|
+
"ad.model.followDefault": "Follow the current session's default model",
|
|
4389
|
+
"ad.model.defaultRoute": " ({provider} / {model})",
|
|
4390
|
+
"ad.model.currentOutOfCatalog": " (set in config, not in the catalog)",
|
|
4391
|
+
"ad.model.placeholder.provider": "provider (blank = follow default)",
|
|
4392
|
+
"ad.model.placeholder.model": "model (blank = follow default)",
|
|
4393
|
+
"ad.model.saved": "Model saved",
|
|
4394
|
+
"ad.action.save": "Save",
|
|
4395
|
+
"ad.action.manualEntry": "Enter manually",
|
|
4396
|
+
"ad.loadingModels": "Loading the model catalog…",
|
|
4397
|
+
"ad.route.bad": "Effective route: unavailable — {error}",
|
|
4398
|
+
"ad.route.badFallback": "the host could not resolve provider / model (config is blank and no session default is available)",
|
|
4399
|
+
"ad.route.ok": "Effective route: {provider} / {model}",
|
|
4400
|
+
"ad.route.fromDefault": " (following default)",
|
|
4401
|
+
"ad.route.fromConfig": " (from config)",
|
|
4402
|
+
"ad.action.runningNow": "Organizing…",
|
|
4403
|
+
"ad.action.runNow": "Organize now",
|
|
4404
|
+
"ad.run.steps": "Ran {count} rounds · {phase}",
|
|
4405
|
+
"ad.run.hoursSince": "Last organized {hours} hours ago",
|
|
4406
|
+
"ad.run.never": "Never organized",
|
|
4407
|
+
"ad.run.progress": "Round {step}: {tools}",
|
|
4408
|
+
"ad.run.stepDone": "done",
|
|
4409
|
+
"ad.toolSep": ", ",
|
|
4410
|
+
"ad.errSep": "; ",
|
|
4411
|
+
"ad.progressSep": " · ",
|
|
4412
|
+
"ad.last.prefix": "Last {at} · {result}",
|
|
4413
|
+
"ad.last.ok": "succeeded",
|
|
4414
|
+
"ad.last.applied": ", rewrote {count} files",
|
|
4415
|
+
"ad.last.reportOnly": " (report only)",
|
|
4416
|
+
"ad.last.problems": " · structural issues {before} → {after}",
|
|
4417
|
+
"ad.last.tokens": " · tokens {in} / {out}",
|
|
4418
|
+
"ad.last.failed": " failed: {error}",
|
|
4419
|
+
"ad.unknown": "unknown",
|
|
4420
|
+
"ad.last.truncated": " · ⚠ report was truncated",
|
|
4421
|
+
"ad.last.retried": " · completed after {count} zero-body retries",
|
|
4422
|
+
"ad.last.continuations": " · {count} continuation rounds",
|
|
4423
|
+
"ad.last.report": " · report {name}",
|
|
4424
|
+
"ad.sidebar.title": "Sidebar entry",
|
|
4425
|
+
"ad.sidebar.on": "On",
|
|
4426
|
+
"ad.sidebar.off": "Off",
|
|
4427
|
+
"ad.sidebar.note": "Show the “Memory management” icon in the DSH sidebar; clicking it switches the main area to this page. On by default; turning it off keeps the Settings → Memory management entry. Changes take effect immediately, no restart needed.",
|
|
4428
|
+
"ad.sidebar.label": "Show the “Memory management” entry in the sidebar",
|
|
4429
|
+
"ad.limits.title": "Injection limits",
|
|
4430
|
+
"ad.limits.pending": "changes pending",
|
|
4431
|
+
"ad.limits.restartWarn": "⚠ These values are fixed at DSH startup — restart DSH for them to apply. Each row below shows “current → after restart”.",
|
|
4432
|
+
"ad.limits.restartNote": "These values are fixed at DSH startup: saving requires a DSH restart to take effect — so each row shows both “currently effective” and “after restart”.",
|
|
4433
|
+
"ad.limit.maxResults": "Memories per injection",
|
|
4434
|
+
"ad.limit.maxChars": "Characters per memory",
|
|
4435
|
+
"ad.limit.maxBaseline": "baseline slots",
|
|
4436
|
+
"ad.limit.maxSessionBytes": "Bytes per session",
|
|
4437
|
+
"ad.limit.staleDays": "Freshness threshold",
|
|
4438
|
+
"ad.unit.items": "items",
|
|
4439
|
+
"ad.unit.chars": "chars",
|
|
4440
|
+
"ad.unit.bytes": "bytes",
|
|
4441
|
+
"ad.unit.days": "days",
|
|
4442
|
+
"ad.unit.copies": "copies",
|
|
4443
|
+
"ad.limit.labelTitle": "{unit} · range {lo}–{hi}",
|
|
4444
|
+
"ad.limit.inputTitle": "range {lo}–{hi} {unit}",
|
|
4445
|
+
"ad.limit.diff": "current {current} → after restart {pending}",
|
|
4446
|
+
"ad.limit.range": "range {lo}–{hi}",
|
|
4447
|
+
"ad.limit.current": " · currently {current}",
|
|
4448
|
+
"ad.action.saving": "Saving…",
|
|
4449
|
+
"ad.action.saveLimits": "Save injection limits",
|
|
4450
|
+
"ad.limits.saveHint": "Requires a DSH restart to take effect",
|
|
4451
|
+
"ad.reserved.title": "Reserved-name protection",
|
|
4452
|
+
"ad.reserved.subtitle": "Files in this list never take part in injection",
|
|
4453
|
+
"ad.reserved.note": "The two built-in names are fixed and cannot be removed; you can add your own below (file name only, no path). Requires a DSH restart to take effect.",
|
|
4454
|
+
"ad.reserved.fixedSuffix": " · fixed",
|
|
4455
|
+
"ad.reserved.fixedTitle": "Built-in and fixed; cannot be removed",
|
|
4456
|
+
"ad.reserved.removeTitle": "Remove {name} from the list",
|
|
4457
|
+
"ad.reserved.empty": "(no reserved names of your own yet)",
|
|
4458
|
+
"ad.reserved.placeholder": "e.g. project_notes.md (must be a .md file name)",
|
|
4459
|
+
"ad.action.addToList": "Add to list",
|
|
4460
|
+
"ad.advanced": "Advanced",
|
|
4461
|
+
"ad.adv.autoTrigger": "Auto trigger",
|
|
4462
|
+
"ad.adv.since": "At least",
|
|
4463
|
+
"ad.adv.hoursAndSessions": "hours since last run, and at least",
|
|
4464
|
+
"ad.adv.sessionsUpdated": "sessions with updates meanwhile",
|
|
4465
|
+
"ad.adv.maxSteps": "Max rounds",
|
|
4466
|
+
"ad.adv.stepNote": "one round = one model call plus the tool calls it makes",
|
|
4467
|
+
"ad.adv.maxOutput": "Max output per call",
|
|
4468
|
+
"ad.adv.maxOutputNote": "tokens (thinking and body share this budget; raise it first when reports get truncated)",
|
|
4469
|
+
"ad.adv.keepSnapshots": "Keep snapshots",
|
|
4470
|
+
"ad.adv.paths": "Config and state: {path}",
|
|
4471
|
+
"ad.records.snapshots": "Rollback points",
|
|
4472
|
+
"ad.records.countSnaps": "{count}",
|
|
4473
|
+
"ad.action.refresh": "Refresh",
|
|
4474
|
+
"ad.row.rollbackScope": "Rollback scope",
|
|
4475
|
+
"ad.scope.files": "Restore only the files this run touched",
|
|
4476
|
+
"ad.scope.all": "Return the whole directory to that moment",
|
|
4477
|
+
"ad.note.rollback": "Before rolling back, the whole memory directory is snapshotted again as a protection copy — so the rollback itself can be undone. Extra files are only moved into archive/, never deleted.",
|
|
4478
|
+
"ad.snap.empty": "No rollback points yet — run once with “Rewrite directly” and one appears.",
|
|
4479
|
+
"ad.snap.legacyLabel": "Legacy snapshot",
|
|
4480
|
+
"ad.snap.runLabel": "Run {id}",
|
|
4481
|
+
"ad.snap.protectionLabel": "Rollback protection",
|
|
4482
|
+
"ad.snap.meta": "{at} · {files} files",
|
|
4483
|
+
"ad.snap.legacyBadge": "Legacy",
|
|
4484
|
+
"ad.action.rollback": "Roll back",
|
|
4485
|
+
"ad.snap.confirmTitle": "Roll back to {at}?",
|
|
4486
|
+
"ad.snap.confirmScope": "Scope: {scope}",
|
|
4487
|
+
"ad.snap.scopeAll": "Return the whole directory to that moment — files that exist now but not in the snapshot are moved into archive/ (not deleted)",
|
|
4488
|
+
"ad.snap.scopeFiles": "Restore only the files that run touched — files created by it move into archive/, files it archived move back to the top level",
|
|
4489
|
+
"ad.snap.confirmSuffix": ". A protection snapshot is taken first, so this step can itself be undone.",
|
|
4490
|
+
"ad.action.rollingBack": "Rolling back…",
|
|
4491
|
+
"ad.action.confirmRollback": "Confirm rollback",
|
|
4492
|
+
"ad.action.cancel": "Cancel",
|
|
4493
|
+
"ad.rb.rolledBack": "Rolled back to {at}: restored {restored} files, moved away {parked}",
|
|
4494
|
+
"ad.rb.skipped": ", skipped {count} (see the rollback report)",
|
|
4495
|
+
"ad.rb.protection": " · protection snapshot {id}",
|
|
4496
|
+
"ad.rb.report": " · report {name}",
|
|
4497
|
+
"ad.records.runs": "Organization records",
|
|
4498
|
+
"ad.records.countRuns": "{count}",
|
|
4499
|
+
"ad.records.runsNote": "Every run leaves a declaration: what changed, why, and which ideas were rejected. Click one to read the full text.",
|
|
4500
|
+
"ad.records.runsEmpty": "No organization records yet — run once and one appears.",
|
|
4501
|
+
"ad.badge.apply": "Rewritten",
|
|
4502
|
+
"ad.badge.report": "Report only",
|
|
4503
|
+
"ad.run.changes": "{count} changes",
|
|
4504
|
+
"ad.run.reportName": "report {name}",
|
|
4505
|
+
"ad.run.rolledBack": "Rolled back",
|
|
4506
|
+
"ad.run.rolledBackAt": "Rolled back · {at}",
|
|
4507
|
+
"ad.action.collapse": "Collapse",
|
|
4508
|
+
"ad.action.showDeclaration": "Show declaration",
|
|
4509
|
+
"ad.records.reports": "Past reports",
|
|
4510
|
+
"ad.records.countReports": "{count}",
|
|
4511
|
+
"ad.records.reportsEmpty": "No reports yet — run once and one appears. Reports live under memory/autodream/ and never mix into the memory list.",
|
|
4512
|
+
"ad.action.view": "View",
|
|
4513
|
+
"ad.loading": "Loading…",
|
|
4514
|
+
"ad.empty": "(empty)",
|
|
4515
|
+
"ad.emptyDeclaration": "(that run left no declaration body)",
|
|
4516
|
+
"ad.err.readFailed": "Read failed: {error}",
|
|
4517
|
+
"ad.err.rollbackFailed": "Rollback failed: {error}",
|
|
4518
|
+
"ad.unknownReason": "unknown reason",
|
|
4519
|
+
"ad.err.startFailed": "Failed to start",
|
|
4520
|
+
"ad.notice.started": "Organization started — progress refreshes every 2 seconds",
|
|
4521
|
+
"ad.err.remoteUnavailable": "remote.autodream is unavailable — the host side of the plugin may have failed to load; check the DSH startup log",
|
|
4522
|
+
"ad.err.noInterface": "The host has no {name} API (older version?)",
|
|
4523
|
+
"ad.err.noSetSettings": "The host has no setSettings API yet (older plugin version)",
|
|
4524
|
+
"ad.err.limitInteger": "{label} must be an integer",
|
|
4525
|
+
"ad.err.limitRange": "{label} must be between {lo}–{hi}",
|
|
4526
|
+
"ad.err.notSaved": "Not saved — {detail}",
|
|
4527
|
+
"ad.err.saveRejected": "Save rejected",
|
|
4528
|
+
"ad.notice.limitsSaved": "Saved — restart DSH for it to take effect",
|
|
4529
|
+
"ad.notice.sidebarOn": "Sidebar entry enabled (takes effect immediately)",
|
|
4530
|
+
"ad.notice.sidebarOff": "Sidebar entry disabled (the Settings entry remains)",
|
|
4531
|
+
"ad.err.reservedRejected": "Reserved-name list rejected",
|
|
4532
|
+
"ad.notice.reservedSaved": "Reserved-name list saved — restart DSH for it to take effect",
|
|
4533
|
+
"ad.err.reservedNeedName": "Enter a file name first, e.g. project_notes.md",
|
|
4534
|
+
"ad.err.reservedBadName": "A reserved name must be a .md file name (no path): {name}",
|
|
4535
|
+
"ad.err.reservedDup": "{name} is already in the list",
|
|
4536
|
+
"ad.note.modelCatalogUnavailable": "The model catalog is unavailable ({error}) — enter provider and model below by hand.",
|
|
4537
|
+
|
|
4538
|
+
// ── 2.1.0: auto-archive settings (62-archive-settings.js) ──
|
|
4539
|
+
"archive.title": "What auto-archive does",
|
|
4540
|
+
"archive.intro1": "Memories that have not been injected for too long are moved into archive/ — never deleted, always recoverable. This page decides how long counts as idle, and whether it may actually act.",
|
|
4541
|
+
"archive.intro2": "It does not call a model: candidates are computed purely from the access ledger's day counts, so the same input always yields the same result.",
|
|
4542
|
+
"archive.intro3": "To see what would be collected right now: Files → “Tools ▾” → Archive candidates.",
|
|
4543
|
+
"archive.modeTitle": "Auto-archive",
|
|
4544
|
+
"archive.mode.off": "Off",
|
|
4545
|
+
"archive.mode.report": "Report only",
|
|
4546
|
+
"archive.mode.auto": "Run when conditions are met",
|
|
4547
|
+
"archive.cost.off": "Off: no file is touched — candidates are only listed, waiting for you to act.",
|
|
4548
|
+
"archive.cost.report": "Report only: candidates go into the organization report, no file is touched. The default and most conservative option.",
|
|
4549
|
+
"archive.cost.auto": "Run when conditions are met: candidates are really moved into archive/ (moved, not deleted; recoverable at any time); if “Changes” is set to report only, this option degrades to report only.",
|
|
4550
|
+
"archive.degrade": "Note: “Autodream → Changes” is currently report only — this option degrades to report only and will not touch any file.",
|
|
4551
|
+
"archive.th.title": "How many idle days before a memory may be archived",
|
|
4552
|
+
"archive.th.project": "Project",
|
|
4553
|
+
"archive.th.reference": "Reference",
|
|
4554
|
+
"archive.th.user": "User",
|
|
4555
|
+
"archive.th.hintProject": "project_* not injected for this many days in a row → candidate",
|
|
4556
|
+
"archive.th.hintReference": "reference_* same (documents usually outlive projects)",
|
|
4557
|
+
"archive.th.hintUser": "user_* same (be extra careful with persona-type memories)",
|
|
4558
|
+
"archive.loadingConfig": "Reading config…",
|
|
4559
|
+
"archive.action.save": "Save archive conditions",
|
|
4560
|
+
"archive.action.saving": "Saving…",
|
|
4561
|
+
"archive.note.immediate": "Takes effect immediately, no DSH restart needed",
|
|
4562
|
+
"archive.note.thresholds": "Days are counted from the access ledger (when this memory was last injected to the model). feedback_* is never selected; locked memories are never selected either.",
|
|
4563
|
+
"archive.err.noConfig": "The host has no autodream config API yet (older plugin version)",
|
|
4564
|
+
"archive.err.readConfig": "Could not read the Autodream config",
|
|
4565
|
+
"archive.err.readFailed": "Failed to read config: {error}",
|
|
4566
|
+
"archive.err.saveFailed": "Save failed: {error}",
|
|
4567
|
+
"archive.err.notApplied": "Save did not take effect: {error}. setConfig ignores invalid values and keeps the old ones — try again, or check the DSH startup log.",
|
|
4568
|
+
"archive.notice.modeSet": "Auto-archive set to “{mode}”",
|
|
4569
|
+
"archive.err.modeStill": "Config still says “{mode}”",
|
|
4570
|
+
"archive.unknown": "unreadable",
|
|
4571
|
+
"archive.err.thresholdBad": "{label} threshold must be between 1 and 3650 days (currently “{value}”)",
|
|
4572
|
+
"archive.notice.thresholdsSaved": "Archive conditions saved",
|
|
4573
|
+
"archive.err.thresholdStill": "{label} is still {value}",
|
|
4122
4574
|
};
|
|
4123
4575
|
|
|
4124
4576
|
/**
|
|
@@ -4248,7 +4700,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4248
4700
|
const sidebarEntryOn = !(memSet && memSet.ui && memSet.ui.sidebarEntry === false);
|
|
4249
4701
|
|
|
4250
4702
|
react.useEffect(function () {
|
|
4251
|
-
if (!remote) { setErr("
|
|
4703
|
+
if (!remote) { setErr(t("ad.err.remoteUnavailable")); return undefined; }
|
|
4252
4704
|
let alive = true;
|
|
4253
4705
|
function pull() {
|
|
4254
4706
|
callRemote("status", [], function (d) {
|
|
@@ -4288,7 +4740,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4288
4740
|
*/
|
|
4289
4741
|
function callRemote(name, args, onOk, onErr) {
|
|
4290
4742
|
const fn = remote ? remote[name] : null;
|
|
4291
|
-
if (typeof fn !== "function") { if (onErr) onErr("
|
|
4743
|
+
if (typeof fn !== "function") { if (onErr) onErr(t("ad.err.noInterface", { name: name })); return; }
|
|
4292
4744
|
let p;
|
|
4293
4745
|
try { p = fn.apply(remote, args || []); }
|
|
4294
4746
|
catch (e) { if (onErr) onErr(detail(e)); return; }
|
|
@@ -4306,12 +4758,14 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4306
4758
|
// ── 注入设置:5 项上限 + 保留名名单 ─────────────────────────────
|
|
4307
4759
|
// 边界与 host 的 LIMIT_SPECS 一一对应(那边是权威,这里只是提前拦明显笔误)。
|
|
4308
4760
|
const LIMIT_FIELDS = [
|
|
4309
|
-
{ key: "maxResults",
|
|
4310
|
-
{ key: "maxChars",
|
|
4311
|
-
{ key: "maxBaseline",
|
|
4312
|
-
{ key: "maxSessionBytes",
|
|
4313
|
-
{ key: "staleDays",
|
|
4761
|
+
{ key: "maxResults", labelKey: "ad.limit.maxResults", lo: 1, hi: 30, unitKey: "ad.unit.items" },
|
|
4762
|
+
{ key: "maxChars", labelKey: "ad.limit.maxChars", lo: 200, hi: 20000, unitKey: "ad.unit.chars" },
|
|
4763
|
+
{ key: "maxBaseline", labelKey: "ad.limit.maxBaseline", lo: 1, hi: 50, unitKey: "ad.unit.items" },
|
|
4764
|
+
{ key: "maxSessionBytes", labelKey: "ad.limit.maxSessionBytes", lo: 4096, hi: 524288, unitKey: "ad.unit.bytes" },
|
|
4765
|
+
{ key: "staleDays", labelKey: "ad.limit.staleDays", lo: 0, hi: 365, unitKey: "ad.unit.days" },
|
|
4314
4766
|
];
|
|
4767
|
+
const limitLabel = function (f) { return t(f.labelKey); };
|
|
4768
|
+
const limitUnit = function (f) { return t(f.unitKey); };
|
|
4315
4769
|
|
|
4316
4770
|
/** 把一份 limits 对象摊成输入框用的字符串草稿(输入期间允许非法,保存时再校验)。 */
|
|
4317
4771
|
function limitsDraftOf(src) {
|
|
@@ -4332,27 +4786,27 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4332
4786
|
|
|
4333
4787
|
/** 保存 5 项上限。越界/非整数**本地先拦**(host 也是直接拒绝),别让它白跑一趟。 */
|
|
4334
4788
|
function saveLimits() {
|
|
4335
|
-
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr("
|
|
4789
|
+
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr(t("ad.err.noSetSettings")); return; }
|
|
4336
4790
|
const draft = limitsDraft || {};
|
|
4337
4791
|
const limits = {};
|
|
4338
4792
|
const bad = [];
|
|
4339
4793
|
for (const f of LIMIT_FIELDS) {
|
|
4340
4794
|
const raw = String(draft[f.key] == null ? "" : draft[f.key]).trim();
|
|
4341
4795
|
const n = Number(raw);
|
|
4342
|
-
if (raw === "" || !Number.isFinite(n) || !Number.isInteger(n)) { bad.push(
|
|
4343
|
-
if (n < f.lo || n > f.hi) { bad.push(
|
|
4796
|
+
if (raw === "" || !Number.isFinite(n) || !Number.isInteger(n)) { bad.push(t("ad.err.limitInteger", { label: limitLabel(f) })); continue; }
|
|
4797
|
+
if (n < f.lo || n > f.hi) { bad.push(t("ad.err.limitRange", { label: limitLabel(f), lo: f.lo, hi: f.hi })); continue; }
|
|
4344
4798
|
limits[f.key] = n;
|
|
4345
4799
|
}
|
|
4346
|
-
if (bad.length) { setErr("
|
|
4800
|
+
if (bad.length) { setErr(t("ad.err.notSaved", { detail: bad.join(t("ad.errSep")) })); return; }
|
|
4347
4801
|
setSaveBusy(true); setErr(null); setNotice("");
|
|
4348
4802
|
Promise.resolve(memRemote.setSettings({ limits: limits })).then(function (out) {
|
|
4349
4803
|
setSaveBusy(false);
|
|
4350
4804
|
// setSettings 是 {ok:true, settings, restartRequired} 形状:不要过 unwrap
|
|
4351
4805
|
// (unwrap 只认 {ok, value},这里会把 settings 吃掉)。
|
|
4352
|
-
if (out && out.ok === false) { setErr(out.error || "
|
|
4806
|
+
if (out && out.ok === false) { setErr(out.error || t("ad.err.saveRejected")); return; }
|
|
4353
4807
|
if (out && out.settings) { setMemSet(out.settings); setLimitsDraft(limitsDraftOf(out.settings.pendingLimits)); }
|
|
4354
4808
|
else loadMemSettings();
|
|
4355
|
-
setNotice("
|
|
4809
|
+
setNotice(t("ad.notice.limitsSaved"));
|
|
4356
4810
|
}, function (e) { setSaveBusy(false); setErr(detail(e)); });
|
|
4357
4811
|
}
|
|
4358
4812
|
|
|
@@ -4365,7 +4819,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4365
4819
|
*/
|
|
4366
4820
|
function saveSidebarEntry(next) {
|
|
4367
4821
|
if (!memRemote || typeof memRemote.setSettings !== "function") {
|
|
4368
|
-
setSidebarErr("
|
|
4822
|
+
setSidebarErr(t("ad.err.noSetSettings"));
|
|
4369
4823
|
return;
|
|
4370
4824
|
}
|
|
4371
4825
|
if (sidebarBusy) return;
|
|
@@ -4373,11 +4827,11 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4373
4827
|
Promise.resolve(memRemote.setSettings({ ui: { sidebarEntry: !!next } })).then(function (out) {
|
|
4374
4828
|
setSidebarBusy(false);
|
|
4375
4829
|
// 与 saveLimits 同款:setSettings 是 {ok, settings, restartRequired} 形状,不要过 unwrap。
|
|
4376
|
-
if (out && out.ok === false) { setSidebarErr(out.error || "
|
|
4830
|
+
if (out && out.ok === false) { setSidebarErr(out.error || t("ad.err.saveRejected")); return; }
|
|
4377
4831
|
if (out && out.settings) setMemSet(out.settings);
|
|
4378
4832
|
else loadMemSettings();
|
|
4379
4833
|
setSidebarEntry(!!next);
|
|
4380
|
-
setNotice(!!next ? "
|
|
4834
|
+
setNotice(!!next ? t("ad.notice.sidebarOn") : t("ad.notice.sidebarOff"));
|
|
4381
4835
|
}, function (e) { setSidebarBusy(false); setSidebarErr(detail(e)); });
|
|
4382
4836
|
}
|
|
4383
4837
|
|
|
@@ -4388,28 +4842,28 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4388
4842
|
* 那是生效清单里固定的两个,删不掉。这里管的只是用户自己加的那些。
|
|
4389
4843
|
*/
|
|
4390
4844
|
function saveReserved(list) {
|
|
4391
|
-
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr("
|
|
4845
|
+
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr(t("ad.err.noSetSettings")); return; }
|
|
4392
4846
|
setSaveBusy(true); setErr(null); setNotice("");
|
|
4393
4847
|
Promise.resolve(memRemote.setSettings({ reserved: list })).then(function (out) {
|
|
4394
4848
|
setSaveBusy(false);
|
|
4395
|
-
if (out && out.ok === false) { setErr(out.error || "
|
|
4849
|
+
if (out && out.ok === false) { setErr(out.error || t("ad.err.reservedRejected")); return; }
|
|
4396
4850
|
if (out && out.settings) setMemSet(out.settings);
|
|
4397
4851
|
else loadMemSettings();
|
|
4398
|
-
setNotice("
|
|
4852
|
+
setNotice(t("ad.notice.reservedSaved"));
|
|
4399
4853
|
}, function (e) { setSaveBusy(false); setErr(detail(e)); });
|
|
4400
4854
|
}
|
|
4401
4855
|
|
|
4402
4856
|
function addReserved() {
|
|
4403
4857
|
const name = String(reservedNew || "").trim();
|
|
4404
4858
|
const extra = Array.isArray(memSet && memSet.reservedExtra) ? memSet.reservedExtra : [];
|
|
4405
|
-
if (!name) { setErr("
|
|
4859
|
+
if (!name) { setErr(t("ad.err.reservedNeedName")); return; }
|
|
4406
4860
|
// 与 host 的校验同一套:单个 .md 文件名、不许带路径
|
|
4407
4861
|
if (!name.endsWith(".md") || name.includes("/") || name.includes("\\") || name.startsWith("..")) {
|
|
4408
|
-
setErr("
|
|
4862
|
+
setErr(t("ad.err.reservedBadName", { name: name }));
|
|
4409
4863
|
return;
|
|
4410
4864
|
}
|
|
4411
4865
|
if (extra.some(function (x) { return String(x).toLowerCase() === name.toLowerCase(); })) {
|
|
4412
|
-
setErr("
|
|
4866
|
+
setErr(t("ad.err.reservedDup", { name: name }));
|
|
4413
4867
|
return;
|
|
4414
4868
|
}
|
|
4415
4869
|
setReservedNew("");
|
|
@@ -4438,7 +4892,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4438
4892
|
callRemote("listModels", [], function (d) {
|
|
4439
4893
|
setModels(d && typeof d === "object" && !Array.isArray(d) ? d : { catalogAvailable: false, routes: [], note: "" });
|
|
4440
4894
|
}, function (m) {
|
|
4441
|
-
setModels({ catalogAvailable: false, routes: [], note: "
|
|
4895
|
+
setModels({ catalogAvailable: false, routes: [], note: t("ad.note.modelCatalogUnavailable", { error: m }) });
|
|
4442
4896
|
});
|
|
4443
4897
|
}
|
|
4444
4898
|
|
|
@@ -4447,27 +4901,27 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4447
4901
|
// 契约:runNow({ reason })。reason 只落进 manifest 给人看;门控的豁免
|
|
4448
4902
|
// 由 宿主侧负责(手动点了按钮就不该被「还没到 N 小时」挡回来)。
|
|
4449
4903
|
callRemote("runNow", [{ reason: "设置页手动触发" }], function (d) {
|
|
4450
|
-
if (d && d.ok === false) setErr(d.error || "
|
|
4451
|
-
else { setNotice("
|
|
4904
|
+
if (d && d.ok === false) setErr(d.error || t("ad.err.startFailed"));
|
|
4905
|
+
else { setNotice(t("ad.notice.started")); refreshLists(); }
|
|
4452
4906
|
setBusy(false);
|
|
4453
4907
|
}, function (m) { setErr(m); setBusy(false); });
|
|
4454
4908
|
}
|
|
4455
4909
|
|
|
4456
4910
|
function openReport(name) {
|
|
4457
4911
|
if (name === null) { setOpen(null); setBody(""); return; }
|
|
4458
|
-
setOpen(name); setBody("
|
|
4912
|
+
setOpen(name); setBody(t("ad.loading"));
|
|
4459
4913
|
callRemote("readReport", [name], function (d) {
|
|
4460
|
-
setBody((d && d.content) ||
|
|
4461
|
-
}, function (m) { setBody("
|
|
4914
|
+
setBody((d && d.content) || t("ad.empty"));
|
|
4915
|
+
}, function (m) { setBody(t("ad.err.readFailed", { error: m })); });
|
|
4462
4916
|
}
|
|
4463
4917
|
|
|
4464
4918
|
/** 整理声明:点开才拉,拉回来的是 Markdown 原文,直接用 pre 呈现。 */
|
|
4465
4919
|
function openDecl(runId) {
|
|
4466
4920
|
if (declOpen === runId) { setDeclOpen(null); setDeclBody(""); return; }
|
|
4467
|
-
setDeclOpen(runId); setDeclBody("
|
|
4921
|
+
setDeclOpen(runId); setDeclBody(t("ad.loading"));
|
|
4468
4922
|
callRemote("readDeclaration", [{ runId: runId }], function (d) {
|
|
4469
|
-
setDeclBody((d && d.markdown) ||
|
|
4470
|
-
}, function (m) { setDeclBody("
|
|
4923
|
+
setDeclBody((d && d.markdown) || t("ad.emptyDeclaration"));
|
|
4924
|
+
}, function (m) { setDeclBody(t("ad.err.readFailed", { error: m })); });
|
|
4471
4925
|
}
|
|
4472
4926
|
|
|
4473
4927
|
/**
|
|
@@ -4480,26 +4934,31 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4480
4934
|
callRemote("rollback", [{ snapshotId: sp.name, scope: scope }], function (d) {
|
|
4481
4935
|
const x = d || {};
|
|
4482
4936
|
if (x.ok === false) {
|
|
4483
|
-
setRbMsg({ bad: true, text: "
|
|
4937
|
+
setRbMsg({ bad: true, text: t("ad.err.rollbackFailed", { error: x.error || t("ad.unknownReason") }) });
|
|
4484
4938
|
} else {
|
|
4485
|
-
setRbMsg({ bad: false, text: "
|
|
4486
|
-
|
|
4939
|
+
setRbMsg({ bad: false, text: t("ad.rb.rolledBack", {
|
|
4940
|
+
at: String(sp.atHuman || ""),
|
|
4941
|
+
restored: (x.restored == null ? "?" : x.restored),
|
|
4942
|
+
parked: (x.parked == null ? "?" : x.parked),
|
|
4943
|
+
}) +
|
|
4487
4944
|
// skipped = 想动但没动的(archive/ 已有同名、或回滚点里没那一版)。
|
|
4488
4945
|
// 不显示它等于「部分成功」被说成「成功」——回滚这种事不能含糊。
|
|
4489
|
-
(x.skipped ? "
|
|
4490
|
-
(x.protection ? "
|
|
4946
|
+
(x.skipped ? t("ad.rb.skipped", { count: x.skipped }) : "") +
|
|
4947
|
+
(x.protection ? t("ad.rb.protection", { id: x.protection }) : "") +
|
|
4948
|
+
(x.report ? t("ad.rb.report", { name: x.report }) : "") });
|
|
4491
4949
|
setConfirmSnap(null);
|
|
4492
4950
|
}
|
|
4493
4951
|
setRbBusy(null);
|
|
4494
4952
|
refreshLists();
|
|
4495
|
-
}, function (m) { setRbMsg({ bad: true, text: "
|
|
4953
|
+
}, function (m) { setRbMsg({ bad: true, text: t("ad.err.rollbackFailed", { error: m }) }); setRbBusy(null); });
|
|
4496
4954
|
}
|
|
4497
4955
|
|
|
4498
4956
|
// 实际生效路线(契约:route 可能为 null;source==='none' 时 error 里是原因)。
|
|
4499
4957
|
const routeBad = !route || route.source === "none" || !route.provider || !route.model;
|
|
4500
4958
|
const routeInfo = routeBad
|
|
4501
|
-
? ("
|
|
4502
|
-
: ("
|
|
4959
|
+
? t("ad.route.bad", { error: (route && route.error) || t("ad.route.badFallback") })
|
|
4960
|
+
: t("ad.route.ok", { provider: route.provider, model: route.model }) +
|
|
4961
|
+
(route.fromDefault ? t("ad.route.fromDefault") : t("ad.route.fromConfig"));
|
|
4503
4962
|
|
|
4504
4963
|
// 模型 <select> 的取值:''=跟随默认;否则是 "provider::model" 的 JSON 编码。
|
|
4505
4964
|
// 配置里指定的路线万一下架了(不在目录里),也要让它显示出来 —— 否则
|
|
@@ -4523,9 +4982,9 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4523
4982
|
|
|
4524
4983
|
return h("div", { className: "smem-autodream" }, [
|
|
4525
4984
|
h("div", { className: "smem-subtabs", key: "segs" }, [
|
|
4526
|
-
subtab("run", "
|
|
4527
|
-
subtab("settings", "
|
|
4528
|
-
subtab("records", "
|
|
4985
|
+
subtab("run", t("ad.tab.run")),
|
|
4986
|
+
subtab("settings", t("ad.tab.settings")),
|
|
4987
|
+
subtab("records", t("ad.tab.records"), String(snaps.length + runs.length + reports.length)),
|
|
4529
4988
|
]),
|
|
4530
4989
|
segOf === "run" ? h("div", { key: "seg-run" }, [
|
|
4531
4990
|
h("div", { className: "smem-autodream-sec", key: "top" }, [
|
|
@@ -4533,54 +4992,53 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4533
4992
|
h("button", {
|
|
4534
4993
|
type: "button", key: "sw", className: "smem-switch",
|
|
4535
4994
|
"data-on": c.enabled ? "1" : "0",
|
|
4536
|
-
title: c.enabled ? "
|
|
4537
|
-
"aria-label": "
|
|
4995
|
+
title: c.enabled ? t("ad.switch.onTitle") : t("ad.switch.offTitle"),
|
|
4996
|
+
"aria-label": t("ad.switch.aria"),
|
|
4538
4997
|
onClick: function () { patch({ enabled: !c.enabled }); },
|
|
4539
4998
|
}),
|
|
4540
|
-
h("span", { key: "t", style: { fontWeight: "600" } }, "
|
|
4999
|
+
h("span", { key: "t", style: { fontWeight: "600" } }, t("ad.title")),
|
|
4541
5000
|
h("span", { className: "smem-status", key: "s" },
|
|
4542
|
-
running ? ("
|
|
5001
|
+
running ? t("ad.status.running", { phase: String((st && st.phase) || t("ad.status.preparing")) }) : (c.enabled ? t("ad.status.idle") : t("ad.status.off"))),
|
|
4543
5002
|
]),
|
|
4544
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4545
|
-
"把记忆目录通读一遍:合并重复的、删掉被推翻的、修索引与断链、把相对日期换成绝对日期。"),
|
|
5003
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.note.what")),
|
|
4546
5004
|
]),
|
|
4547
5005
|
h("div", { className: "smem-autodream-sec", key: "mode" }, [
|
|
4548
5006
|
h("div", { className: "smem-autodream-row", key: "a" }, [
|
|
4549
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5007
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.trigger")),
|
|
4550
5008
|
h(Seg, {
|
|
4551
5009
|
key: "v", value: c.trigger,
|
|
4552
|
-
options: [{ value: "manual", label: "
|
|
5010
|
+
options: [{ value: "manual", label: t("ad.trigger.manual") }, { value: "auto", label: t("ad.trigger.auto") }],
|
|
4553
5011
|
onChange: function (v) { patch({ trigger: v }); },
|
|
4554
5012
|
}),
|
|
4555
5013
|
]),
|
|
4556
5014
|
h("div", { className: "smem-autodream-row", key: "b" }, [
|
|
4557
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5015
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.apply")),
|
|
4558
5016
|
h(Seg, {
|
|
4559
5017
|
key: "v", value: c.apply ? "apply" : "report",
|
|
4560
|
-
options: [{ value: "report", label: "
|
|
5018
|
+
options: [{ value: "report", label: t("ad.apply.report") }, { value: "apply", label: t("ad.apply.apply") }],
|
|
4561
5019
|
onChange: function (v) { patch({ apply: v === "apply" }); },
|
|
4562
5020
|
}),
|
|
4563
5021
|
]),
|
|
4564
5022
|
h("div", { className: "smem-autodream-row", key: "c" }, [
|
|
4565
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5023
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.source")),
|
|
4566
5024
|
h(Seg, {
|
|
4567
5025
|
key: "v", value: c.source,
|
|
4568
|
-
options: [{ value: "memory", label: "
|
|
5026
|
+
options: [{ value: "memory", label: t("ad.source.memory") }, { value: "memory+sessions", label: t("ad.source.sessions") }],
|
|
4569
5027
|
onChange: function (v) { patch({ source: v }); },
|
|
4570
5028
|
}),
|
|
4571
5029
|
]),
|
|
4572
5030
|
h("div", { className: "smem-autodream-note", key: "w" },
|
|
4573
5031
|
c.source === "memory+sessions"
|
|
4574
|
-
? "
|
|
4575
|
-
: "
|
|
5032
|
+
? t("ad.note.sourceSessions")
|
|
5033
|
+
: t("ad.note.sourceMemory")),
|
|
4576
5034
|
h("div", { className: "smem-autodream-note", key: "w2" },
|
|
4577
5035
|
c.apply
|
|
4578
|
-
? "
|
|
4579
|
-
: "
|
|
5036
|
+
? t("ad.note.applySnapshot")
|
|
5037
|
+
: t("ad.note.reportOnly")),
|
|
4580
5038
|
]),
|
|
4581
5039
|
h("div", { className: "smem-autodream-sec", key: "model" }, [
|
|
4582
5040
|
h("div", { className: "smem-autodream-row", key: "model" }, [
|
|
4583
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5041
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.model")),
|
|
4584
5042
|
// 有可枚举的模型目录就给 <select>(首项 = 跟随会话默认,值 "":"");
|
|
4585
5043
|
// 目录不可用就退化成两个文本框(契约第六节第 2 条)。
|
|
4586
5044
|
(models && models.catalogAvailable)
|
|
@@ -4594,11 +5052,11 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4594
5052
|
patch({ provider: r.provider, model: r.model });
|
|
4595
5053
|
},
|
|
4596
5054
|
}, [h("option", { key: "__def", value: "" },
|
|
4597
|
-
"
|
|
4598
|
-
? ("
|
|
5055
|
+
t("ad.model.followDefault") + ((models.defaultRoute && models.defaultRoute.provider && models.defaultRoute.model)
|
|
5056
|
+
? t("ad.model.defaultRoute", { provider: String(models.defaultRoute.provider), model: String(models.defaultRoute.model) })
|
|
4599
5057
|
: ""),
|
|
4600
5058
|
)].concat(
|
|
4601
|
-
modelValueInCatalog ? [] : [h("option", { key: "__cur", value: modelValue }, String(c.provider) + " / " + String(c.model) + "
|
|
5059
|
+
modelValueInCatalog ? [] : [h("option", { key: "__cur", value: modelValue }, String(c.provider) + " / " + String(c.model) + t("ad.model.currentOutOfCatalog"))],
|
|
4602
5060
|
modelRoutes.map(function (r) {
|
|
4603
5061
|
return h("option", { key: String(r.provider) + "/" + String(r.model), value: encodeRoute(r.provider, r.model) },
|
|
4604
5062
|
r.label || (String(r.provider) + " / " + String(r.model)));
|
|
@@ -4606,13 +5064,13 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4606
5064
|
))
|
|
4607
5065
|
: models
|
|
4608
5066
|
? [
|
|
4609
|
-
h("input", { className: "smem-input", key: "p", style: { flex: "1", minWidth: "110px" }, value: prov, placeholder: "provider
|
|
4610
|
-
h("input", { className: "smem-input", key: "m", style: { flex: "1", minWidth: "110px" }, value: model, placeholder: "model
|
|
4611
|
-
h("button", { type: "button", className: "smem-btn smem-btn-primary", key: "ok", onClick: function () { patch({ provider: prov, model: model }); setNotice("
|
|
5067
|
+
h("input", { className: "smem-input", key: "p", style: { flex: "1", minWidth: "110px" }, value: prov, placeholder: t("ad.model.placeholder.provider"), onChange: function (e) { setProv(e.target.value); } }),
|
|
5068
|
+
h("input", { className: "smem-input", key: "m", style: { flex: "1", minWidth: "110px" }, value: model, placeholder: t("ad.model.placeholder.model"), onChange: function (e) { setModel(e.target.value); } }),
|
|
5069
|
+
h("button", { type: "button", className: "smem-btn smem-btn-primary", key: "ok", onClick: function () { patch({ provider: prov, model: model }); setNotice(t("ad.model.saved")); } }, t("ad.action.save")),
|
|
4612
5070
|
]
|
|
4613
5071
|
: [
|
|
4614
|
-
h("span", { className: "smem-status", key: "load" }, "
|
|
4615
|
-
h("button", { type: "button", className: "smem-btn", key: "man", onClick: function () { setModels({ catalogAvailable: false, routes: [], note: "" }); } }, "
|
|
5072
|
+
h("span", { className: "smem-status", key: "load" }, t("ad.loadingModels")),
|
|
5073
|
+
h("button", { type: "button", className: "smem-btn", key: "man", onClick: function () { setModels({ catalogAvailable: false, routes: [], note: "" }); } }, t("ad.action.manualEntry")),
|
|
4616
5074
|
],
|
|
4617
5075
|
]),
|
|
4618
5076
|
h("div", {
|
|
@@ -4629,29 +5087,32 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4629
5087
|
type: "button", className: "smem-btn smem-btn-primary", key: "go",
|
|
4630
5088
|
disabled: busy || running || !remote,
|
|
4631
5089
|
onClick: run,
|
|
4632
|
-
}, running ? "
|
|
5090
|
+
}, running ? t("ad.action.runningNow") : t("ad.action.runNow")),
|
|
4633
5091
|
h("span", { className: "smem-status", key: "s" },
|
|
4634
5092
|
running
|
|
4635
|
-
? ("
|
|
4636
|
-
: (st && st.hoursSince != null ? ("
|
|
5093
|
+
? t("ad.run.steps", { count: ((st && st.steps && st.steps.length) || 0), phase: String((st && st.phase) || "") })
|
|
5094
|
+
: (st && st.hoursSince != null ? t("ad.run.hoursSince", { hours: st.hoursSince.toFixed(1) }) : t("ad.run.never"))),
|
|
4637
5095
|
]),
|
|
4638
5096
|
running && st && st.steps && st.steps.length
|
|
4639
5097
|
? h("div", { className: "smem-autodream-note", key: "prog" }, st.steps.map(function (x) {
|
|
4640
|
-
return "
|
|
4641
|
-
}).join("
|
|
5098
|
+
return t("ad.run.progress", { step: x.step, tools: (x.tools.length ? x.tools.join(t("ad.toolSep")) : t("ad.run.stepDone")) });
|
|
5099
|
+
}).join(t("ad.progressSep")))
|
|
4642
5100
|
: null,
|
|
4643
5101
|
last
|
|
4644
5102
|
? h("div", { className: "smem-status", key: "last" },
|
|
4645
|
-
"
|
|
4646
|
-
|
|
4647
|
-
|
|
4648
|
-
"
|
|
4649
|
-
|
|
5103
|
+
t("ad.last.prefix", {
|
|
5104
|
+
at: String(last.atHuman || ""),
|
|
5105
|
+
result: (last.ok
|
|
5106
|
+
? (t("ad.last.ok") + (last.apply ? t("ad.last.applied", { count: ((last.touched || []).length) }) : t("ad.last.reportOnly")) +
|
|
5107
|
+
t("ad.last.problems", { before: last.problemsBefore, after: last.problemsAfter }) +
|
|
5108
|
+
t("ad.last.tokens", { in: last.tokensIn, out: last.tokensOut }))
|
|
5109
|
+
: t("ad.last.failed", { error: (last.error || t("ad.unknown")) })),
|
|
5110
|
+
}) +
|
|
4650
5111
|
// 1.9.2:残篇必须在状态行上就能看见 —— 只写「成功」正是 10-25 那次误判的来源。
|
|
4651
|
-
(last.ok && last.truncated ? "
|
|
4652
|
-
(last.ok && !last.truncated && last.retries ? ("
|
|
4653
|
-
(last.continuations ? ("
|
|
4654
|
-
(last.report ? "
|
|
5112
|
+
(last.ok && last.truncated ? t("ad.last.truncated") : "") +
|
|
5113
|
+
(last.ok && !last.truncated && last.retries ? t("ad.last.retried", { count: last.retries }) : "") +
|
|
5114
|
+
(last.continuations ? t("ad.last.continuations", { count: last.continuations }) : "") +
|
|
5115
|
+
(last.report ? t("ad.last.report", { name: last.report }) : ""))
|
|
4655
5116
|
: null,
|
|
4656
5117
|
]),
|
|
4657
5118
|
]) : null,
|
|
@@ -4660,11 +5121,10 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4660
5121
|
// 「设置 → 记忆管理」这个入口永远在(它注册在 80-apply,和这个开关无关)。
|
|
4661
5122
|
h("div", { className: "smem-autodream-sec", key: "sidebar-entry" }, [
|
|
4662
5123
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4663
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4664
|
-
h("span", { className: "smem-status", key: "s" }, sidebarEntryOn ? "
|
|
5124
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("ad.sidebar.title")),
|
|
5125
|
+
h("span", { className: "smem-status", key: "s" }, sidebarEntryOn ? t("ad.sidebar.on") : t("ad.sidebar.off")),
|
|
4665
5126
|
]),
|
|
4666
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4667
|
-
"在 DSH 侧栏显示「记忆管理」图标,点它把中央主区切到本页。默认开启;关闭后「设置 → 记忆管理」这个入口仍然保留。改动立即生效,不需要重启。"),
|
|
5127
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.sidebar.note")),
|
|
4668
5128
|
h("label", { className: "smem-check", key: "row" }, [
|
|
4669
5129
|
h("input", {
|
|
4670
5130
|
type: "checkbox", key: "c",
|
|
@@ -4672,7 +5132,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4672
5132
|
disabled: sidebarBusy,
|
|
4673
5133
|
onChange: function (e) { saveSidebarEntry(!!(e.target && e.target.checked)); },
|
|
4674
5134
|
}),
|
|
4675
|
-
h("span", { key: "l" }, "
|
|
5135
|
+
h("span", { key: "l" }, t("ad.sidebar.label")),
|
|
4676
5136
|
]),
|
|
4677
5137
|
sidebarErr ? h("div", { className: "smem-autodream-note smem-ad-warn", key: "err" }, sidebarErr) : null,
|
|
4678
5138
|
]),
|
|
@@ -4681,27 +5141,25 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4681
5141
|
// 同一件事只在一处设定,这里不再重复摆一份。
|
|
4682
5142
|
h("div", { className: "smem-autodream-sec", key: "limits" }, [
|
|
4683
5143
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4684
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
5144
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("ad.limits.title")),
|
|
4685
5145
|
memSet && memSet.restartRequired
|
|
4686
|
-
? h("span", { className: "smem-ad-badge smem-ad-badge--warn", key: "w" }, "
|
|
5146
|
+
? h("span", { className: "smem-ad-badge smem-ad-badge--warn", key: "w" }, t("ad.limits.pending"))
|
|
4687
5147
|
: null,
|
|
4688
5148
|
]),
|
|
4689
5149
|
memSet && memSet.restartRequired
|
|
4690
|
-
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "rw" },
|
|
4691
|
-
|
|
4692
|
-
: h("div", { className: "smem-autodream-note", key: "rn" },
|
|
4693
|
-
"这些值在 DSH 启动时固化:保存后要重启 DSH 才生效 —— 所以每行都会标出「当前生效」与「重启后」。"),
|
|
5150
|
+
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "rw" }, t("ad.limits.restartWarn"))
|
|
5151
|
+
: h("div", { className: "smem-autodream-note", key: "rn" }, t("ad.limits.restartNote")),
|
|
4694
5152
|
// 五个参数排两列(一行两个):同样的信息,行数少一半,扫读反而更清楚。
|
|
4695
5153
|
h("div", { className: "smem-ad-grid2", key: "grid" }, LIMIT_FIELDS.map(function (f) {
|
|
4696
5154
|
const cur = memSet && memSet.limits ? memSet.limits[f.key] : null;
|
|
4697
5155
|
const pend = memSet && memSet.pendingLimits ? memSet.pendingLimits[f.key] : null;
|
|
4698
5156
|
const diff = cur != null && pend != null && cur !== pend;
|
|
4699
5157
|
return h("div", { className: "smem-ad-field", key: f.key }, [
|
|
4700
|
-
h("span", { className: "smem-autodream-key", key: "k", title:
|
|
5158
|
+
h("span", { className: "smem-autodream-key", key: "k", title: t("ad.limit.labelTitle", { unit: limitUnit(f), lo: f.lo, hi: f.hi }) }, limitLabel(f)),
|
|
4701
5159
|
h("input", {
|
|
4702
5160
|
className: "smem-num", key: "v", type: "number",
|
|
4703
5161
|
min: String(f.lo), max: String(f.hi),
|
|
4704
|
-
title: "
|
|
5162
|
+
title: t("ad.limit.inputTitle", { lo: f.lo, hi: f.hi, unit: limitUnit(f) }),
|
|
4705
5163
|
value: limitsDraft && limitsDraft[f.key] != null ? limitsDraft[f.key] : "",
|
|
4706
5164
|
onChange: function (e) {
|
|
4707
5165
|
const nx = Object.assign({}, limitsDraft);
|
|
@@ -4710,73 +5168,72 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4710
5168
|
},
|
|
4711
5169
|
}),
|
|
4712
5170
|
diff
|
|
4713
|
-
? h("span", { className: "smem-ad-badge smem-ad-badge--warn", key: "d" }, "
|
|
4714
|
-
: h("span", { className: "smem-status", key: "d" }, "
|
|
5171
|
+
? h("span", { className: "smem-ad-badge smem-ad-badge--warn", key: "d" }, t("ad.limit.diff", { current: String(cur), pending: String(pend) }))
|
|
5172
|
+
: h("span", { className: "smem-status", key: "d" }, t("ad.limit.range", { lo: f.lo, hi: f.hi }) + (cur == null ? "" : t("ad.limit.current", { current: String(cur) }))),
|
|
4715
5173
|
]);
|
|
4716
5174
|
})),
|
|
4717
5175
|
h("div", { className: "smem-autodream-row", key: "save" }, [
|
|
4718
|
-
h("button", { type: "button", className: "smem-btn smem-btn-primary", key: "s", disabled: saveBusy, onClick: saveLimits }, saveBusy ? "
|
|
4719
|
-
h("span", { className: "smem-status", key: "n" }, "
|
|
5176
|
+
h("button", { type: "button", className: "smem-btn smem-btn-primary", key: "s", disabled: saveBusy, onClick: saveLimits }, saveBusy ? t("ad.action.saving") : t("ad.action.saveLimits")),
|
|
5177
|
+
h("span", { className: "smem-status", key: "n" }, t("ad.limits.saveHint")),
|
|
4720
5178
|
]),
|
|
4721
5179
|
]),
|
|
4722
5180
|
h("div", { className: "smem-autodream-sec", key: "reserved" }, [
|
|
4723
5181
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4724
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4725
|
-
h("span", { className: "smem-status", key: "c" }, "
|
|
5182
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("ad.reserved.title")),
|
|
5183
|
+
h("span", { className: "smem-status", key: "c" }, t("ad.reserved.subtitle")),
|
|
4726
5184
|
]),
|
|
4727
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4728
|
-
"内置的两个固定不可删;下面可以再加自己的(只写文件名,不要带路径)。改完需重启 DSH 才生效。"),
|
|
5185
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.reserved.note")),
|
|
4729
5186
|
h("div", { className: "smem-raw-names", key: "fixed" }, fixedReserved.map(function (n) {
|
|
4730
|
-
return h("span", { className: "smem-chip smem-chip--fixed", key: n, title: "
|
|
5187
|
+
return h("span", { className: "smem-chip smem-chip--fixed", key: n, title: t("ad.reserved.fixedTitle") }, n + t("ad.reserved.fixedSuffix"));
|
|
4731
5188
|
})),
|
|
4732
5189
|
h("div", { className: "smem-raw-names", key: "extra" },
|
|
4733
5190
|
extraReserved.length
|
|
4734
5191
|
? extraReserved.map(function (n) {
|
|
4735
5192
|
return h("span", { className: "smem-chip", key: n, "data-on": "1" }, [
|
|
4736
5193
|
n,
|
|
4737
|
-
h("button", { type: "button", className: "smem-chip-x", key: "x", title: "
|
|
5194
|
+
h("button", { type: "button", className: "smem-chip-x", key: "x", title: t("ad.reserved.removeTitle", { name: n }), onClick: function () { removeReserved(n); } }, "✕"),
|
|
4738
5195
|
]);
|
|
4739
5196
|
})
|
|
4740
|
-
: h("span", { className: "smem-status", key: "none" }, "
|
|
5197
|
+
: h("span", { className: "smem-status", key: "none" }, t("ad.reserved.empty"))),
|
|
4741
5198
|
h("div", { className: "smem-autodream-row", key: "add" }, [
|
|
4742
5199
|
h("input", {
|
|
4743
5200
|
className: "smem-input", key: "i",
|
|
4744
5201
|
style: { flex: "1", minWidth: "150px" },
|
|
4745
5202
|
value: reservedNew,
|
|
4746
|
-
placeholder: "
|
|
5203
|
+
placeholder: t("ad.reserved.placeholder"),
|
|
4747
5204
|
onChange: function (e) { setReservedNew(e.target.value); },
|
|
4748
5205
|
}),
|
|
4749
|
-
h("button", { type: "button", className: "smem-btn", key: "a", disabled: saveBusy, onClick: addReserved }, "
|
|
5206
|
+
h("button", { type: "button", className: "smem-btn", key: "a", disabled: saveBusy, onClick: addReserved }, t("ad.action.addToList")),
|
|
4750
5207
|
]),
|
|
4751
5208
|
]),
|
|
4752
5209
|
h("details", { className: "smem-autodream-adv", key: "adv", open: true }, [
|
|
4753
|
-
h("summary", { key: "s" }, "
|
|
5210
|
+
h("summary", { key: "s" }, t("ad.advanced")),
|
|
4754
5211
|
h("div", { key: "d" }, [
|
|
4755
5212
|
c.trigger === "auto" ? h("div", { className: "smem-autodream-row", key: "gate" }, [
|
|
4756
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
4757
|
-
h("span", { className: "smem-status", key: "a" }, "
|
|
5213
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.adv.autoTrigger")),
|
|
5214
|
+
h("span", { className: "smem-status", key: "a" }, t("ad.adv.since")),
|
|
4758
5215
|
h("input", { className: "smem-num", key: "h", type: "number", min: "1", max: "720", value: c.minHours, onChange: function (e) { patch({ minHours: Number(e.target.value) }); } }),
|
|
4759
|
-
h("span", { className: "smem-status", key: "b" }, "
|
|
5216
|
+
h("span", { className: "smem-status", key: "b" }, t("ad.adv.hoursAndSessions")),
|
|
4760
5217
|
h("input", { className: "smem-num", key: "m", type: "number", min: "1", max: "200", value: c.minSessions, onChange: function (e) { patch({ minSessions: Number(e.target.value) }); } }),
|
|
4761
|
-
h("span", { className: "smem-status", key: "c" }, "
|
|
5218
|
+
h("span", { className: "smem-status", key: "c" }, t("ad.adv.sessionsUpdated")),
|
|
4762
5219
|
]) : null,
|
|
4763
5220
|
h("div", { className: "smem-autodream-row", key: "steps" }, [
|
|
4764
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5221
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.adv.maxSteps")),
|
|
4765
5222
|
h("input", { className: "smem-num", key: "v", type: "number", min: "1", max: "80", value: c.maxSteps, onChange: function (e) { patch({ maxSteps: Number(e.target.value) }); } }),
|
|
4766
|
-
h("span", { className: "smem-status", key: "s" }, "
|
|
5223
|
+
h("span", { className: "smem-status", key: "s" }, t("ad.adv.stepNote")),
|
|
4767
5224
|
]),
|
|
4768
5225
|
// 1.9.2:单次输出上限(原写死 4096)。思考型模型下 4096 会让收尾轮只剩思考、零正文。
|
|
4769
5226
|
h("div", { className: "smem-autodream-row", key: "maxout" }, [
|
|
4770
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5227
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.adv.maxOutput")),
|
|
4771
5228
|
h("input", { className: "smem-num", key: "v", type: "number", min: "1024", max: "131072", value: c.maxOutputTokens, onChange: function (e) { patch({ maxOutputTokens: Number(e.target.value) }); } }),
|
|
4772
|
-
h("span", { className: "smem-status", key: "s" }, "
|
|
5229
|
+
h("span", { className: "smem-status", key: "s" }, t("ad.adv.maxOutputNote")),
|
|
4773
5230
|
]),
|
|
4774
5231
|
h("div", { className: "smem-autodream-row", key: "keep" }, [
|
|
4775
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5232
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.adv.keepSnapshots")),
|
|
4776
5233
|
h("input", { className: "smem-num", key: "v", type: "number", min: "1", max: "50", value: c.maxSnapshotKeep, onChange: function (e) { patch({ maxSnapshotKeep: Number(e.target.value) }); } }),
|
|
4777
|
-
h("span", { className: "smem-status", key: "s" }, "
|
|
5234
|
+
h("span", { className: "smem-status", key: "s" }, t("ad.unit.copies")),
|
|
4778
5235
|
]),
|
|
4779
|
-
h("div", { className: "smem-status", key: "paths" }, "
|
|
5236
|
+
h("div", { className: "smem-status", key: "paths" }, t("ad.adv.paths", { path: ((st && st.paths && st.paths.configPath) || "—") })),
|
|
4780
5237
|
]),
|
|
4781
5238
|
]),
|
|
4782
5239
|
]) : null,
|
|
@@ -4785,53 +5242,51 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4785
5242
|
segOf === "records" ? h("div", { key: "seg-rec" }, [
|
|
4786
5243
|
h("div", { className: "smem-autodream-sec", key: "snap" }, [
|
|
4787
5244
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4788
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4789
|
-
h("span", { className: "smem-status", key: "c" }, String(snaps.length)
|
|
4790
|
-
h("button", { type: "button", className: "smem-btn", key: "rf", onClick: refreshLists }, "
|
|
5245
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("ad.records.snapshots")),
|
|
5246
|
+
h("span", { className: "smem-status", key: "c" }, t("ad.records.countSnaps", { count: String(snaps.length) })),
|
|
5247
|
+
h("button", { type: "button", className: "smem-btn", key: "rf", onClick: refreshLists }, t("ad.action.refresh")),
|
|
4791
5248
|
]),
|
|
4792
5249
|
h("div", { className: "smem-autodream-row", key: "scope" }, [
|
|
4793
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5250
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.rollbackScope")),
|
|
4794
5251
|
h(Seg, {
|
|
4795
5252
|
key: "v", value: rollbackScope,
|
|
4796
|
-
options: [{ value: "files", label: "
|
|
5253
|
+
options: [{ value: "files", label: t("ad.scope.files") }, { value: "all", label: t("ad.scope.all") }],
|
|
4797
5254
|
onChange: function (v) { patch({ rollbackScope: v }); },
|
|
4798
5255
|
}),
|
|
4799
5256
|
]),
|
|
4800
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4801
|
-
"回滚前会自动把整个记忆目录再存一份保护快照 —— 所以回滚本身也能再退回来。多出来的文件只移进 archive/,不删除。"),
|
|
5257
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.note.rollback")),
|
|
4802
5258
|
snaps.length === 0
|
|
4803
|
-
? h("div", { className: "smem-status", key: "e" }, "
|
|
5259
|
+
? h("div", { className: "smem-status", key: "e" }, t("ad.snap.empty"))
|
|
4804
5260
|
: h("div", { className: "smem-autodream-list", key: "l" }, snaps.slice(0, 15).map(function (sp) {
|
|
4805
|
-
const label = sp.legacy ? "
|
|
5261
|
+
const label = sp.legacy ? t("ad.snap.legacyLabel") : (sp.runId ? t("ad.snap.runLabel", { id: String(sp.runId) }) : t("ad.snap.protectionLabel"));
|
|
4806
5262
|
return h("div", { className: "smem-ad-wrap", key: String(sp.name) }, [
|
|
4807
5263
|
h("div", { className: "smem-ad-snap", key: "row" }, [
|
|
4808
5264
|
h("div", { className: "smem-ad-main", key: "m" }, [
|
|
4809
|
-
h("div", { key: "l1" }, String(sp.atHuman || "")
|
|
5265
|
+
h("div", { key: "l1" }, t("ad.snap.meta", { at: String(sp.atHuman || ""), files: (sp.files == null ? "?" : sp.files) })),
|
|
4810
5266
|
h("div", { className: "smem-ad-sub", key: "l2" }, label + " · " + String(sp.name || "")),
|
|
4811
5267
|
]),
|
|
4812
|
-
sp.legacy ? h("span", { className: "smem-ad-badge smem-ad-badge--legacy", key: "lg" }, "
|
|
5268
|
+
sp.legacy ? h("span", { className: "smem-ad-badge smem-ad-badge--legacy", key: "lg" }, t("ad.snap.legacyBadge")) : null,
|
|
4813
5269
|
confirmSnap === sp.name
|
|
4814
5270
|
? null
|
|
4815
5271
|
: h("button", {
|
|
4816
5272
|
type: "button", className: "smem-btn", key: "go",
|
|
4817
5273
|
disabled: rbBusy !== null,
|
|
4818
5274
|
onClick: function () { setRbMsg(null); setConfirmSnap(sp.name); },
|
|
4819
|
-
}, "
|
|
5275
|
+
}, t("ad.action.rollback")),
|
|
4820
5276
|
]),
|
|
4821
5277
|
confirmSnap === sp.name
|
|
4822
5278
|
? h("div", { className: "smem-ad-confirm", key: "cf" }, [
|
|
4823
|
-
h("div", { key: "t", style: { fontWeight: "600" } }, "
|
|
4824
|
-
h("div", { key: "d" }, "
|
|
4825
|
-
? "
|
|
4826
|
-
|
|
4827
|
-
"。回滚前会先存一份保护快照,所以这一步本身也能退。"),
|
|
5279
|
+
h("div", { key: "t", style: { fontWeight: "600" } }, t("ad.snap.confirmTitle", { at: String(sp.atHuman || "") })),
|
|
5280
|
+
h("div", { key: "d" }, t("ad.snap.confirmScope", {
|
|
5281
|
+
scope: rollbackScope === "all" ? t("ad.snap.scopeAll") : t("ad.snap.scopeFiles"),
|
|
5282
|
+
}) + t("ad.snap.confirmSuffix")),
|
|
4828
5283
|
h("div", { className: "smem-autodream-row", key: "acts" }, [
|
|
4829
5284
|
h("button", {
|
|
4830
5285
|
type: "button", className: "smem-btn smem-btn-danger", key: "yes",
|
|
4831
5286
|
disabled: rbBusy === sp.name,
|
|
4832
5287
|
onClick: function () { doRollback(sp); },
|
|
4833
|
-
}, rbBusy === sp.name ? "
|
|
4834
|
-
h("button", { type: "button", className: "smem-btn", key: "no", onClick: function () { setConfirmSnap(null); } }, "
|
|
5288
|
+
}, rbBusy === sp.name ? t("ad.action.rollingBack") : t("ad.action.confirmRollback")),
|
|
5289
|
+
h("button", { type: "button", className: "smem-btn", key: "no", onClick: function () { setConfirmSnap(null); } }, t("ad.action.cancel")),
|
|
4835
5290
|
]),
|
|
4836
5291
|
])
|
|
4837
5292
|
: null,
|
|
@@ -4842,14 +5297,13 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4842
5297
|
// ── 整理声明(新)──────────────────────────────────────────
|
|
4843
5298
|
h("div", { className: "smem-autodream-sec", key: "runs" }, [
|
|
4844
5299
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4845
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4846
|
-
h("span", { className: "smem-status", key: "c" }, String(runs.length)
|
|
4847
|
-
h("button", { type: "button", className: "smem-btn", key: "rf", onClick: refreshLists }, "
|
|
5300
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("ad.records.runs")),
|
|
5301
|
+
h("span", { className: "smem-status", key: "c" }, t("ad.records.countRuns", { count: String(runs.length) })),
|
|
5302
|
+
h("button", { type: "button", className: "smem-btn", key: "rf", onClick: refreshLists }, t("ad.action.refresh")),
|
|
4848
5303
|
]),
|
|
4849
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4850
|
-
"每一趟都留一份整理声明:改了什么、为什么改、哪些想法被拒了。点一条看全文。"),
|
|
5304
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.records.runsNote")),
|
|
4851
5305
|
runs.length === 0
|
|
4852
|
-
? h("div", { className: "smem-status", key: "e" }, "
|
|
5306
|
+
? h("div", { className: "smem-status", key: "e" }, t("ad.records.runsEmpty"))
|
|
4853
5307
|
: h("div", { className: "smem-autodream-list", key: "l" }, runs.slice(0, 15).map(function (rn) {
|
|
4854
5308
|
return h("div", { className: "smem-ad-wrap", key: String(rn.runId) }, [
|
|
4855
5309
|
h("button", {
|
|
@@ -4858,14 +5312,14 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4858
5312
|
onClick: function () { openDecl(rn.runId); },
|
|
4859
5313
|
}, [
|
|
4860
5314
|
h("span", { key: "t" }, String(rn.atHuman || "")),
|
|
4861
|
-
h("span", { className: "smem-ad-badge", key: "m" }, rn.mode === "apply" ? "
|
|
4862
|
-
h("span", { key: "c" }, "
|
|
4863
|
-
rn.report ? h("span", { className: "smem-ad-sub", key: "r" }, "
|
|
5315
|
+
h("span", { className: "smem-ad-badge", key: "m" }, rn.mode === "apply" ? t("ad.badge.apply") : t("ad.badge.report")),
|
|
5316
|
+
h("span", { key: "c" }, t("ad.run.changes", { count: (rn.changeCount == null ? "?" : rn.changeCount) })),
|
|
5317
|
+
rn.report ? h("span", { className: "smem-ad-sub", key: "r" }, t("ad.run.reportName", { name: String(rn.report) })) : null,
|
|
4864
5318
|
rn.rolledBackAt
|
|
4865
5319
|
? h("span", { className: "smem-ad-badge smem-ad-badge--warn", key: "rb" },
|
|
4866
|
-
|
|
5320
|
+
rn.rolledBackAtHuman ? t("ad.run.rolledBackAt", { at: String(rn.rolledBackAtHuman) }) : t("ad.run.rolledBack"))
|
|
4867
5321
|
: null,
|
|
4868
|
-
h("span", { className: "smem-ad-sub", key: "x" }, declOpen === rn.runId ? "
|
|
5322
|
+
h("span", { className: "smem-ad-sub", key: "x" }, declOpen === rn.runId ? t("ad.action.collapse") : t("ad.action.showDeclaration")),
|
|
4869
5323
|
]),
|
|
4870
5324
|
declOpen === rn.runId
|
|
4871
5325
|
? h("pre", { className: "smem-ad-md", key: "body" }, declBody)
|
|
@@ -4875,11 +5329,11 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4875
5329
|
]),
|
|
4876
5330
|
h("div", { className: "smem-autodream-sec", key: "rep" }, [
|
|
4877
5331
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4878
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4879
|
-
h("span", { className: "smem-status", key: "c" }, String(reports.length)
|
|
5332
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("ad.records.reports")),
|
|
5333
|
+
h("span", { className: "smem-status", key: "c" }, t("ad.records.countReports", { count: String(reports.length) })),
|
|
4880
5334
|
]),
|
|
4881
5335
|
reports.length === 0
|
|
4882
|
-
? h("div", { className: "smem-status", key: "e" }, "
|
|
5336
|
+
? h("div", { className: "smem-status", key: "e" }, t("ad.records.reportsEmpty"))
|
|
4883
5337
|
: h("div", { className: "smem-autodream-list", key: "l" }, reports.slice(0, 12).map(function (r) {
|
|
4884
5338
|
return h("button", {
|
|
4885
5339
|
type: "button", key: r.name, className: "smem-report",
|
|
@@ -4887,8 +5341,8 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4887
5341
|
onClick: function () { openReport(open === r.name ? null : r.name); },
|
|
4888
5342
|
}, [
|
|
4889
5343
|
h("span", { key: "t" }, String(r.atHuman || "") + " · " + String(r.name) + " · " + fmtSize(r.bytes)),
|
|
4890
|
-
r.legacy ? h("span", { className: "smem-ad-badge smem-ad-badge--legacy", key: "lg", style: { marginLeft: "6px" } }, "
|
|
4891
|
-
h("span", { className: "smem-ad-sub", key: "x", style: { marginLeft: "6px" } }, open === r.name ? "
|
|
5344
|
+
r.legacy ? h("span", { className: "smem-ad-badge smem-ad-badge--legacy", key: "lg", style: { marginLeft: "6px" } }, t("ad.snap.legacyBadge")) : null,
|
|
5345
|
+
h("span", { className: "smem-ad-sub", key: "x", style: { marginLeft: "6px" } }, open === r.name ? t("ad.action.collapse") : t("ad.action.view")),
|
|
4892
5346
|
]);
|
|
4893
5347
|
})),
|
|
4894
5348
|
open && body ? h("pre", { className: "smem-report-pre", key: "body" }, body) : null,
|
|
@@ -4929,12 +5383,25 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4929
5383
|
const sBusy = react.useState(false); const busy = sBusy[0], setBusy = sBusy[1];
|
|
4930
5384
|
|
|
4931
5385
|
const TH_KEYS = ["archiveAfterDaysProject", "archiveAfterDaysReference", "archiveAfterDaysUser"];
|
|
4932
|
-
|
|
4933
|
-
const
|
|
4934
|
-
archiveAfterDaysProject: "
|
|
4935
|
-
archiveAfterDaysReference: "
|
|
4936
|
-
archiveAfterDaysUser: "
|
|
5386
|
+
// 2.1.0:标签与提示走字典(键在这里,文案在 55-i18n-memory.js)。
|
|
5387
|
+
const TH_LABEL_KEY = {
|
|
5388
|
+
archiveAfterDaysProject: "archive.th.project",
|
|
5389
|
+
archiveAfterDaysReference: "archive.th.reference",
|
|
5390
|
+
archiveAfterDaysUser: "archive.th.user",
|
|
5391
|
+
};
|
|
5392
|
+
const TH_HINT_KEY = {
|
|
5393
|
+
archiveAfterDaysProject: "archive.th.hintProject",
|
|
5394
|
+
archiveAfterDaysReference: "archive.th.hintReference",
|
|
5395
|
+
archiveAfterDaysUser: "archive.th.hintUser",
|
|
4937
5396
|
};
|
|
5397
|
+
const thLabel = function (k) { return t(TH_LABEL_KEY[k] || ""); };
|
|
5398
|
+
const thHint = function (k) { return t(TH_HINT_KEY[k] || ""); };
|
|
5399
|
+
// 2.1.0:三档策略的标题与代价走字典。键必须**写成字面量**(死键守卫会逐键回分片里找);
|
|
5400
|
+
// 用 "archive.mode." + v 拼接既拿不到字面量,也会让守卫把前缀当成一个缺失的键。
|
|
5401
|
+
const MODE_KEY = { off: "archive.mode.off", report: "archive.mode.report", auto: "archive.mode.auto" };
|
|
5402
|
+
const COST_KEY = { off: "archive.cost.off", report: "archive.cost.report", auto: "archive.cost.auto" };
|
|
5403
|
+
const modeLabel = function (v) { return t(MODE_KEY[v] || ""); };
|
|
5404
|
+
const costText = function (v) { return t(COST_KEY[v] || ""); };
|
|
4938
5405
|
|
|
4939
5406
|
/** 阈值 → 输入框草稿。存成字符串:number input 清空时立刻变 0 会很难用。 */
|
|
4940
5407
|
const draftOf = function (c) {
|
|
@@ -4946,18 +5413,18 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4946
5413
|
|
|
4947
5414
|
react.useEffect(function () {
|
|
4948
5415
|
if (!remote || typeof remote.getConfig !== "function") {
|
|
4949
|
-
setErr("
|
|
5416
|
+
setErr(t("archive.err.noConfig"));
|
|
4950
5417
|
return;
|
|
4951
5418
|
}
|
|
4952
5419
|
remote.getConfig()
|
|
4953
5420
|
.then(function (r) {
|
|
4954
5421
|
const d = unwrap(r);
|
|
4955
5422
|
const c = d && d.config ? d.config : null;
|
|
4956
|
-
if (!c) { setErr("
|
|
5423
|
+
if (!c) { setErr(t("archive.err.readConfig")); return; }
|
|
4957
5424
|
setCfg(c);
|
|
4958
5425
|
setDraft(draftOf(c));
|
|
4959
5426
|
})
|
|
4960
|
-
.catch(function (e) { setErr("
|
|
5427
|
+
.catch(function (e) { setErr(t("archive.err.readFailed", { error: (e && e.message) || e })); });
|
|
4961
5428
|
}, []);
|
|
4962
5429
|
|
|
4963
5430
|
const c = cfg || {};
|
|
@@ -4979,16 +5446,16 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4979
5446
|
if (got) { setCfg(got); setDraft(draftOf(got)); }
|
|
4980
5447
|
const bad = failOf(got);
|
|
4981
5448
|
if (!bad) { setNotice(okMsg); return; }
|
|
4982
|
-
setErr("
|
|
5449
|
+
setErr(t("archive.err.notApplied", { error: bad }));
|
|
4983
5450
|
})
|
|
4984
|
-
.catch(function (e) { setErr("
|
|
5451
|
+
.catch(function (e) { setErr(t("archive.err.saveFailed", { error: (e && e.message) || e })); })
|
|
4985
5452
|
.then(function () { setBusy(false); });
|
|
4986
5453
|
}
|
|
4987
5454
|
|
|
4988
5455
|
function saveMode(v) {
|
|
4989
|
-
save({ autoArchive: v }, "
|
|
5456
|
+
save({ autoArchive: v }, t("archive.notice.modeSet", { mode: modeLabel(v) }), function (got) {
|
|
4990
5457
|
const g = got ? autoArchiveOf(got.autoArchive) : null;
|
|
4991
|
-
return g === v ? null : ("
|
|
5458
|
+
return g === v ? null : t("archive.err.modeStill", { mode: g ? modeLabel(g) : t("archive.unknown") });
|
|
4992
5459
|
});
|
|
4993
5460
|
}
|
|
4994
5461
|
|
|
@@ -4999,15 +5466,15 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4999
5466
|
for (const k of TH_KEYS) {
|
|
5000
5467
|
const n = Number(d[k]);
|
|
5001
5468
|
if (!Number.isFinite(n) || n < 1 || n > 3650) {
|
|
5002
|
-
setErr(
|
|
5469
|
+
setErr(t("archive.err.thresholdBad", { label: thLabel(k), value: d[k] }));
|
|
5003
5470
|
return;
|
|
5004
5471
|
}
|
|
5005
5472
|
patch[k] = Math.round(n);
|
|
5006
5473
|
}
|
|
5007
|
-
save(patch, "
|
|
5474
|
+
save(patch, t("archive.notice.thresholdsSaved"), function (got) {
|
|
5008
5475
|
const bad = TH_KEYS.filter(function (k) { return !got || Number(got[k]) !== patch[k]; });
|
|
5009
5476
|
if (!bad.length) return null;
|
|
5010
|
-
return bad.map(function (k) { return
|
|
5477
|
+
return bad.map(function (k) { return t("archive.err.thresholdStill", { label: thLabel(k), value: got ? got[k] : t("archive.unknown") }); }).join(t("ad.toolSep"));
|
|
5011
5478
|
});
|
|
5012
5479
|
}
|
|
5013
5480
|
|
|
@@ -5015,45 +5482,41 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5015
5482
|
// ── ① 它做什么(这一屏存在的理由写在最前面)──
|
|
5016
5483
|
h("div", { className: "smem-autodream-sec", key: "what" }, [
|
|
5017
5484
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
5018
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
5485
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("archive.title")),
|
|
5019
5486
|
]),
|
|
5020
|
-
h("div", { className: "smem-autodream-note", key: "p1" },
|
|
5021
|
-
|
|
5022
|
-
h("div", { className: "smem-autodream-note", key: "
|
|
5023
|
-
"它不调模型:候选完全按访问台账的天数算,同一份输入每次算出来都一样。"),
|
|
5024
|
-
h("div", { className: "smem-autodream-note", key: "p3" },
|
|
5025
|
-
"想现在看看会被收走哪些:文件列表 →「工具 ▾」→ 归档候选。"),
|
|
5487
|
+
h("div", { className: "smem-autodream-note", key: "p1" }, t("archive.intro1")),
|
|
5488
|
+
h("div", { className: "smem-autodream-note", key: "p2" }, t("archive.intro2")),
|
|
5489
|
+
h("div", { className: "smem-autodream-note", key: "p3" }, t("archive.intro3")),
|
|
5026
5490
|
]),
|
|
5027
5491
|
// ── ② 三档策略 ──
|
|
5028
5492
|
h("div", { className: "smem-autodream-sec", key: "mode" }, [
|
|
5029
5493
|
h("div", { className: "smem-autodream-row", key: "r" }, [
|
|
5030
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5494
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("archive.modeTitle")),
|
|
5031
5495
|
h(Seg, {
|
|
5032
5496
|
key: "v", value: mode,
|
|
5033
5497
|
options: [
|
|
5034
|
-
{ value: "off", label:
|
|
5035
|
-
{ value: "report", label:
|
|
5036
|
-
{ value: "auto", label:
|
|
5498
|
+
{ value: "off", label: modeLabel("off") },
|
|
5499
|
+
{ value: "report", label: modeLabel("report") },
|
|
5500
|
+
{ value: "auto", label: modeLabel("auto") },
|
|
5037
5501
|
],
|
|
5038
5502
|
onChange: function (v) { if (v !== mode) saveMode(v); },
|
|
5039
5503
|
}),
|
|
5040
|
-
busy ? h("span", { className: "smem-status", key: "s" }, "
|
|
5504
|
+
busy ? h("span", { className: "smem-status", key: "s" }, t("archive.action.saving")) : null,
|
|
5041
5505
|
]),
|
|
5042
|
-
h("div", { className: "smem-autodream-note", key: "cost" },
|
|
5506
|
+
h("div", { className: "smem-autodream-note", key: "cost" }, costText(mode)),
|
|
5043
5507
|
mode === "auto" && !c.apply
|
|
5044
|
-
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "deg" },
|
|
5045
|
-
"注意:「自动做梦 → 改动方式」现在是只出报告 —— 这一档会自动降级成只出报告,不会动文件。")
|
|
5508
|
+
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "deg" }, t("archive.degrade"))
|
|
5046
5509
|
: null,
|
|
5047
5510
|
]),
|
|
5048
5511
|
// ── ③ 三个阈值 ──
|
|
5049
5512
|
h("div", { className: "smem-autodream-sec", key: "th" }, [
|
|
5050
5513
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
5051
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
5514
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("archive.th.title")),
|
|
5052
5515
|
]),
|
|
5053
5516
|
draft
|
|
5054
5517
|
? h("div", { key: "rows" }, TH_KEYS.map(function (k) {
|
|
5055
5518
|
return h("div", { className: "smem-autodream-row", key: k }, [
|
|
5056
|
-
h("span", { className: "smem-autodream-key", key: "k" },
|
|
5519
|
+
h("span", { className: "smem-autodream-key", key: "k" }, thLabel(k)),
|
|
5057
5520
|
h("input", {
|
|
5058
5521
|
className: "smem-input smem-num", key: "i", type: "number", min: 1, max: 3650,
|
|
5059
5522
|
value: draft[k],
|
|
@@ -5063,19 +5526,18 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5063
5526
|
setDraft(nx);
|
|
5064
5527
|
},
|
|
5065
5528
|
}),
|
|
5066
|
-
h("span", { className: "smem-muted", key: "u" },
|
|
5529
|
+
h("span", { className: "smem-muted", key: "u" }, thHint(k)),
|
|
5067
5530
|
]);
|
|
5068
5531
|
}))
|
|
5069
|
-
: h("div", { className: "smem-muted", key: "loading" }, "
|
|
5532
|
+
: h("div", { className: "smem-muted", key: "loading" }, t("archive.loadingConfig")),
|
|
5070
5533
|
h("div", { className: "smem-autodream-row", key: "btns" }, [
|
|
5071
5534
|
h("button", {
|
|
5072
5535
|
type: "button", className: "smem-btn smem-btn-primary", key: "save",
|
|
5073
5536
|
disabled: busy || !draft, onClick: saveThresholds,
|
|
5074
|
-
}, busy ? "
|
|
5075
|
-
h("span", { className: "smem-muted", key: "n" }, "
|
|
5537
|
+
}, busy ? t("archive.action.saving") : t("archive.action.save")),
|
|
5538
|
+
h("span", { className: "smem-muted", key: "n" }, t("archive.note.immediate")),
|
|
5076
5539
|
]),
|
|
5077
|
-
h("div", { className: "smem-autodream-note", key: "note" },
|
|
5078
|
-
"天数按访问台账算(这条记忆上次被注入给模型是什么时候)。feedback_* 永不入选;锁定的记忆也不会被选中。"),
|
|
5540
|
+
h("div", { className: "smem-autodream-note", key: "note" }, t("archive.note.thresholds")),
|
|
5079
5541
|
]),
|
|
5080
5542
|
err ? h("div", { className: "smem-err", key: "err" }, "⚠ " + err) : null,
|
|
5081
5543
|
notice ? h("div", { className: "smem-ok", key: "ok" }, "✓ " + notice) : null,
|