sage-mem 2.0.1 → 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 +2 -2
- package/README.zh.md +2 -2
- package/lib/client.js +630 -192
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -3879,6 +3879,220 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
3879
3879
|
"notice.conflictDismissed": "已标记为已裁决 —— 没有改任何记忆文件",
|
|
3880
3880
|
"notice.conflictUndone": "已撤销 {file} 的失效标记 —— 它重新参与注入",
|
|
3881
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}",
|
|
3882
4096
|
};
|
|
3883
4097
|
|
|
3884
4098
|
const EN = {
|
|
@@ -4143,6 +4357,220 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4143
4357
|
"notice.conflictDismissed": "Marked as decided — no memory file was changed",
|
|
4144
4358
|
"notice.conflictUndone": "Undid the invalidation of {file} — it takes part in injection again",
|
|
4145
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}",
|
|
4146
4574
|
};
|
|
4147
4575
|
|
|
4148
4576
|
/**
|
|
@@ -4272,7 +4700,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4272
4700
|
const sidebarEntryOn = !(memSet && memSet.ui && memSet.ui.sidebarEntry === false);
|
|
4273
4701
|
|
|
4274
4702
|
react.useEffect(function () {
|
|
4275
|
-
if (!remote) { setErr("
|
|
4703
|
+
if (!remote) { setErr(t("ad.err.remoteUnavailable")); return undefined; }
|
|
4276
4704
|
let alive = true;
|
|
4277
4705
|
function pull() {
|
|
4278
4706
|
callRemote("status", [], function (d) {
|
|
@@ -4312,7 +4740,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4312
4740
|
*/
|
|
4313
4741
|
function callRemote(name, args, onOk, onErr) {
|
|
4314
4742
|
const fn = remote ? remote[name] : null;
|
|
4315
|
-
if (typeof fn !== "function") { if (onErr) onErr("
|
|
4743
|
+
if (typeof fn !== "function") { if (onErr) onErr(t("ad.err.noInterface", { name: name })); return; }
|
|
4316
4744
|
let p;
|
|
4317
4745
|
try { p = fn.apply(remote, args || []); }
|
|
4318
4746
|
catch (e) { if (onErr) onErr(detail(e)); return; }
|
|
@@ -4330,12 +4758,14 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4330
4758
|
// ── 注入设置:5 项上限 + 保留名名单 ─────────────────────────────
|
|
4331
4759
|
// 边界与 host 的 LIMIT_SPECS 一一对应(那边是权威,这里只是提前拦明显笔误)。
|
|
4332
4760
|
const LIMIT_FIELDS = [
|
|
4333
|
-
{ key: "maxResults",
|
|
4334
|
-
{ key: "maxChars",
|
|
4335
|
-
{ key: "maxBaseline",
|
|
4336
|
-
{ key: "maxSessionBytes",
|
|
4337
|
-
{ 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" },
|
|
4338
4766
|
];
|
|
4767
|
+
const limitLabel = function (f) { return t(f.labelKey); };
|
|
4768
|
+
const limitUnit = function (f) { return t(f.unitKey); };
|
|
4339
4769
|
|
|
4340
4770
|
/** 把一份 limits 对象摊成输入框用的字符串草稿(输入期间允许非法,保存时再校验)。 */
|
|
4341
4771
|
function limitsDraftOf(src) {
|
|
@@ -4356,27 +4786,27 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4356
4786
|
|
|
4357
4787
|
/** 保存 5 项上限。越界/非整数**本地先拦**(host 也是直接拒绝),别让它白跑一趟。 */
|
|
4358
4788
|
function saveLimits() {
|
|
4359
|
-
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr("
|
|
4789
|
+
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr(t("ad.err.noSetSettings")); return; }
|
|
4360
4790
|
const draft = limitsDraft || {};
|
|
4361
4791
|
const limits = {};
|
|
4362
4792
|
const bad = [];
|
|
4363
4793
|
for (const f of LIMIT_FIELDS) {
|
|
4364
4794
|
const raw = String(draft[f.key] == null ? "" : draft[f.key]).trim();
|
|
4365
4795
|
const n = Number(raw);
|
|
4366
|
-
if (raw === "" || !Number.isFinite(n) || !Number.isInteger(n)) { bad.push(
|
|
4367
|
-
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; }
|
|
4368
4798
|
limits[f.key] = n;
|
|
4369
4799
|
}
|
|
4370
|
-
if (bad.length) { setErr("
|
|
4800
|
+
if (bad.length) { setErr(t("ad.err.notSaved", { detail: bad.join(t("ad.errSep")) })); return; }
|
|
4371
4801
|
setSaveBusy(true); setErr(null); setNotice("");
|
|
4372
4802
|
Promise.resolve(memRemote.setSettings({ limits: limits })).then(function (out) {
|
|
4373
4803
|
setSaveBusy(false);
|
|
4374
4804
|
// setSettings 是 {ok:true, settings, restartRequired} 形状:不要过 unwrap
|
|
4375
4805
|
// (unwrap 只认 {ok, value},这里会把 settings 吃掉)。
|
|
4376
|
-
if (out && out.ok === false) { setErr(out.error || "
|
|
4806
|
+
if (out && out.ok === false) { setErr(out.error || t("ad.err.saveRejected")); return; }
|
|
4377
4807
|
if (out && out.settings) { setMemSet(out.settings); setLimitsDraft(limitsDraftOf(out.settings.pendingLimits)); }
|
|
4378
4808
|
else loadMemSettings();
|
|
4379
|
-
setNotice("
|
|
4809
|
+
setNotice(t("ad.notice.limitsSaved"));
|
|
4380
4810
|
}, function (e) { setSaveBusy(false); setErr(detail(e)); });
|
|
4381
4811
|
}
|
|
4382
4812
|
|
|
@@ -4389,7 +4819,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4389
4819
|
*/
|
|
4390
4820
|
function saveSidebarEntry(next) {
|
|
4391
4821
|
if (!memRemote || typeof memRemote.setSettings !== "function") {
|
|
4392
|
-
setSidebarErr("
|
|
4822
|
+
setSidebarErr(t("ad.err.noSetSettings"));
|
|
4393
4823
|
return;
|
|
4394
4824
|
}
|
|
4395
4825
|
if (sidebarBusy) return;
|
|
@@ -4397,11 +4827,11 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4397
4827
|
Promise.resolve(memRemote.setSettings({ ui: { sidebarEntry: !!next } })).then(function (out) {
|
|
4398
4828
|
setSidebarBusy(false);
|
|
4399
4829
|
// 与 saveLimits 同款:setSettings 是 {ok, settings, restartRequired} 形状,不要过 unwrap。
|
|
4400
|
-
if (out && out.ok === false) { setSidebarErr(out.error || "
|
|
4830
|
+
if (out && out.ok === false) { setSidebarErr(out.error || t("ad.err.saveRejected")); return; }
|
|
4401
4831
|
if (out && out.settings) setMemSet(out.settings);
|
|
4402
4832
|
else loadMemSettings();
|
|
4403
4833
|
setSidebarEntry(!!next);
|
|
4404
|
-
setNotice(!!next ? "
|
|
4834
|
+
setNotice(!!next ? t("ad.notice.sidebarOn") : t("ad.notice.sidebarOff"));
|
|
4405
4835
|
}, function (e) { setSidebarBusy(false); setSidebarErr(detail(e)); });
|
|
4406
4836
|
}
|
|
4407
4837
|
|
|
@@ -4412,28 +4842,28 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4412
4842
|
* 那是生效清单里固定的两个,删不掉。这里管的只是用户自己加的那些。
|
|
4413
4843
|
*/
|
|
4414
4844
|
function saveReserved(list) {
|
|
4415
|
-
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr("
|
|
4845
|
+
if (!memRemote || typeof memRemote.setSettings !== "function") { setErr(t("ad.err.noSetSettings")); return; }
|
|
4416
4846
|
setSaveBusy(true); setErr(null); setNotice("");
|
|
4417
4847
|
Promise.resolve(memRemote.setSettings({ reserved: list })).then(function (out) {
|
|
4418
4848
|
setSaveBusy(false);
|
|
4419
|
-
if (out && out.ok === false) { setErr(out.error || "
|
|
4849
|
+
if (out && out.ok === false) { setErr(out.error || t("ad.err.reservedRejected")); return; }
|
|
4420
4850
|
if (out && out.settings) setMemSet(out.settings);
|
|
4421
4851
|
else loadMemSettings();
|
|
4422
|
-
setNotice("
|
|
4852
|
+
setNotice(t("ad.notice.reservedSaved"));
|
|
4423
4853
|
}, function (e) { setSaveBusy(false); setErr(detail(e)); });
|
|
4424
4854
|
}
|
|
4425
4855
|
|
|
4426
4856
|
function addReserved() {
|
|
4427
4857
|
const name = String(reservedNew || "").trim();
|
|
4428
4858
|
const extra = Array.isArray(memSet && memSet.reservedExtra) ? memSet.reservedExtra : [];
|
|
4429
|
-
if (!name) { setErr("
|
|
4859
|
+
if (!name) { setErr(t("ad.err.reservedNeedName")); return; }
|
|
4430
4860
|
// 与 host 的校验同一套:单个 .md 文件名、不许带路径
|
|
4431
4861
|
if (!name.endsWith(".md") || name.includes("/") || name.includes("\\") || name.startsWith("..")) {
|
|
4432
|
-
setErr("
|
|
4862
|
+
setErr(t("ad.err.reservedBadName", { name: name }));
|
|
4433
4863
|
return;
|
|
4434
4864
|
}
|
|
4435
4865
|
if (extra.some(function (x) { return String(x).toLowerCase() === name.toLowerCase(); })) {
|
|
4436
|
-
setErr("
|
|
4866
|
+
setErr(t("ad.err.reservedDup", { name: name }));
|
|
4437
4867
|
return;
|
|
4438
4868
|
}
|
|
4439
4869
|
setReservedNew("");
|
|
@@ -4462,7 +4892,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4462
4892
|
callRemote("listModels", [], function (d) {
|
|
4463
4893
|
setModels(d && typeof d === "object" && !Array.isArray(d) ? d : { catalogAvailable: false, routes: [], note: "" });
|
|
4464
4894
|
}, function (m) {
|
|
4465
|
-
setModels({ catalogAvailable: false, routes: [], note: "
|
|
4895
|
+
setModels({ catalogAvailable: false, routes: [], note: t("ad.note.modelCatalogUnavailable", { error: m }) });
|
|
4466
4896
|
});
|
|
4467
4897
|
}
|
|
4468
4898
|
|
|
@@ -4471,27 +4901,27 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4471
4901
|
// 契约:runNow({ reason })。reason 只落进 manifest 给人看;门控的豁免
|
|
4472
4902
|
// 由 宿主侧负责(手动点了按钮就不该被「还没到 N 小时」挡回来)。
|
|
4473
4903
|
callRemote("runNow", [{ reason: "设置页手动触发" }], function (d) {
|
|
4474
|
-
if (d && d.ok === false) setErr(d.error || "
|
|
4475
|
-
else { setNotice("
|
|
4904
|
+
if (d && d.ok === false) setErr(d.error || t("ad.err.startFailed"));
|
|
4905
|
+
else { setNotice(t("ad.notice.started")); refreshLists(); }
|
|
4476
4906
|
setBusy(false);
|
|
4477
4907
|
}, function (m) { setErr(m); setBusy(false); });
|
|
4478
4908
|
}
|
|
4479
4909
|
|
|
4480
4910
|
function openReport(name) {
|
|
4481
4911
|
if (name === null) { setOpen(null); setBody(""); return; }
|
|
4482
|
-
setOpen(name); setBody("
|
|
4912
|
+
setOpen(name); setBody(t("ad.loading"));
|
|
4483
4913
|
callRemote("readReport", [name], function (d) {
|
|
4484
|
-
setBody((d && d.content) ||
|
|
4485
|
-
}, function (m) { setBody("
|
|
4914
|
+
setBody((d && d.content) || t("ad.empty"));
|
|
4915
|
+
}, function (m) { setBody(t("ad.err.readFailed", { error: m })); });
|
|
4486
4916
|
}
|
|
4487
4917
|
|
|
4488
4918
|
/** 整理声明:点开才拉,拉回来的是 Markdown 原文,直接用 pre 呈现。 */
|
|
4489
4919
|
function openDecl(runId) {
|
|
4490
4920
|
if (declOpen === runId) { setDeclOpen(null); setDeclBody(""); return; }
|
|
4491
|
-
setDeclOpen(runId); setDeclBody("
|
|
4921
|
+
setDeclOpen(runId); setDeclBody(t("ad.loading"));
|
|
4492
4922
|
callRemote("readDeclaration", [{ runId: runId }], function (d) {
|
|
4493
|
-
setDeclBody((d && d.markdown) ||
|
|
4494
|
-
}, function (m) { setDeclBody("
|
|
4923
|
+
setDeclBody((d && d.markdown) || t("ad.emptyDeclaration"));
|
|
4924
|
+
}, function (m) { setDeclBody(t("ad.err.readFailed", { error: m })); });
|
|
4495
4925
|
}
|
|
4496
4926
|
|
|
4497
4927
|
/**
|
|
@@ -4504,26 +4934,31 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4504
4934
|
callRemote("rollback", [{ snapshotId: sp.name, scope: scope }], function (d) {
|
|
4505
4935
|
const x = d || {};
|
|
4506
4936
|
if (x.ok === false) {
|
|
4507
|
-
setRbMsg({ bad: true, text: "
|
|
4937
|
+
setRbMsg({ bad: true, text: t("ad.err.rollbackFailed", { error: x.error || t("ad.unknownReason") }) });
|
|
4508
4938
|
} else {
|
|
4509
|
-
setRbMsg({ bad: false, text: "
|
|
4510
|
-
|
|
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
|
+
}) +
|
|
4511
4944
|
// skipped = 想动但没动的(archive/ 已有同名、或回滚点里没那一版)。
|
|
4512
4945
|
// 不显示它等于「部分成功」被说成「成功」——回滚这种事不能含糊。
|
|
4513
|
-
(x.skipped ? "
|
|
4514
|
-
(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 }) : "") });
|
|
4515
4949
|
setConfirmSnap(null);
|
|
4516
4950
|
}
|
|
4517
4951
|
setRbBusy(null);
|
|
4518
4952
|
refreshLists();
|
|
4519
|
-
}, function (m) { setRbMsg({ bad: true, text: "
|
|
4953
|
+
}, function (m) { setRbMsg({ bad: true, text: t("ad.err.rollbackFailed", { error: m }) }); setRbBusy(null); });
|
|
4520
4954
|
}
|
|
4521
4955
|
|
|
4522
4956
|
// 实际生效路线(契约:route 可能为 null;source==='none' 时 error 里是原因)。
|
|
4523
4957
|
const routeBad = !route || route.source === "none" || !route.provider || !route.model;
|
|
4524
4958
|
const routeInfo = routeBad
|
|
4525
|
-
? ("
|
|
4526
|
-
: ("
|
|
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"));
|
|
4527
4962
|
|
|
4528
4963
|
// 模型 <select> 的取值:''=跟随默认;否则是 "provider::model" 的 JSON 编码。
|
|
4529
4964
|
// 配置里指定的路线万一下架了(不在目录里),也要让它显示出来 —— 否则
|
|
@@ -4547,9 +4982,9 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4547
4982
|
|
|
4548
4983
|
return h("div", { className: "smem-autodream" }, [
|
|
4549
4984
|
h("div", { className: "smem-subtabs", key: "segs" }, [
|
|
4550
|
-
subtab("run", "
|
|
4551
|
-
subtab("settings", "
|
|
4552
|
-
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)),
|
|
4553
4988
|
]),
|
|
4554
4989
|
segOf === "run" ? h("div", { key: "seg-run" }, [
|
|
4555
4990
|
h("div", { className: "smem-autodream-sec", key: "top" }, [
|
|
@@ -4557,54 +4992,53 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4557
4992
|
h("button", {
|
|
4558
4993
|
type: "button", key: "sw", className: "smem-switch",
|
|
4559
4994
|
"data-on": c.enabled ? "1" : "0",
|
|
4560
|
-
title: c.enabled ? "
|
|
4561
|
-
"aria-label": "
|
|
4995
|
+
title: c.enabled ? t("ad.switch.onTitle") : t("ad.switch.offTitle"),
|
|
4996
|
+
"aria-label": t("ad.switch.aria"),
|
|
4562
4997
|
onClick: function () { patch({ enabled: !c.enabled }); },
|
|
4563
4998
|
}),
|
|
4564
|
-
h("span", { key: "t", style: { fontWeight: "600" } }, "
|
|
4999
|
+
h("span", { key: "t", style: { fontWeight: "600" } }, t("ad.title")),
|
|
4565
5000
|
h("span", { className: "smem-status", key: "s" },
|
|
4566
|
-
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"))),
|
|
4567
5002
|
]),
|
|
4568
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4569
|
-
"把记忆目录通读一遍:合并重复的、删掉被推翻的、修索引与断链、把相对日期换成绝对日期。"),
|
|
5003
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.note.what")),
|
|
4570
5004
|
]),
|
|
4571
5005
|
h("div", { className: "smem-autodream-sec", key: "mode" }, [
|
|
4572
5006
|
h("div", { className: "smem-autodream-row", key: "a" }, [
|
|
4573
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5007
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.trigger")),
|
|
4574
5008
|
h(Seg, {
|
|
4575
5009
|
key: "v", value: c.trigger,
|
|
4576
|
-
options: [{ value: "manual", label: "
|
|
5010
|
+
options: [{ value: "manual", label: t("ad.trigger.manual") }, { value: "auto", label: t("ad.trigger.auto") }],
|
|
4577
5011
|
onChange: function (v) { patch({ trigger: v }); },
|
|
4578
5012
|
}),
|
|
4579
5013
|
]),
|
|
4580
5014
|
h("div", { className: "smem-autodream-row", key: "b" }, [
|
|
4581
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5015
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.apply")),
|
|
4582
5016
|
h(Seg, {
|
|
4583
5017
|
key: "v", value: c.apply ? "apply" : "report",
|
|
4584
|
-
options: [{ value: "report", label: "
|
|
5018
|
+
options: [{ value: "report", label: t("ad.apply.report") }, { value: "apply", label: t("ad.apply.apply") }],
|
|
4585
5019
|
onChange: function (v) { patch({ apply: v === "apply" }); },
|
|
4586
5020
|
}),
|
|
4587
5021
|
]),
|
|
4588
5022
|
h("div", { className: "smem-autodream-row", key: "c" }, [
|
|
4589
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5023
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.source")),
|
|
4590
5024
|
h(Seg, {
|
|
4591
5025
|
key: "v", value: c.source,
|
|
4592
|
-
options: [{ value: "memory", label: "
|
|
5026
|
+
options: [{ value: "memory", label: t("ad.source.memory") }, { value: "memory+sessions", label: t("ad.source.sessions") }],
|
|
4593
5027
|
onChange: function (v) { patch({ source: v }); },
|
|
4594
5028
|
}),
|
|
4595
5029
|
]),
|
|
4596
5030
|
h("div", { className: "smem-autodream-note", key: "w" },
|
|
4597
5031
|
c.source === "memory+sessions"
|
|
4598
|
-
? "
|
|
4599
|
-
: "
|
|
5032
|
+
? t("ad.note.sourceSessions")
|
|
5033
|
+
: t("ad.note.sourceMemory")),
|
|
4600
5034
|
h("div", { className: "smem-autodream-note", key: "w2" },
|
|
4601
5035
|
c.apply
|
|
4602
|
-
? "
|
|
4603
|
-
: "
|
|
5036
|
+
? t("ad.note.applySnapshot")
|
|
5037
|
+
: t("ad.note.reportOnly")),
|
|
4604
5038
|
]),
|
|
4605
5039
|
h("div", { className: "smem-autodream-sec", key: "model" }, [
|
|
4606
5040
|
h("div", { className: "smem-autodream-row", key: "model" }, [
|
|
4607
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5041
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.model")),
|
|
4608
5042
|
// 有可枚举的模型目录就给 <select>(首项 = 跟随会话默认,值 "":"");
|
|
4609
5043
|
// 目录不可用就退化成两个文本框(契约第六节第 2 条)。
|
|
4610
5044
|
(models && models.catalogAvailable)
|
|
@@ -4618,11 +5052,11 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4618
5052
|
patch({ provider: r.provider, model: r.model });
|
|
4619
5053
|
},
|
|
4620
5054
|
}, [h("option", { key: "__def", value: "" },
|
|
4621
|
-
"
|
|
4622
|
-
? ("
|
|
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) })
|
|
4623
5057
|
: ""),
|
|
4624
5058
|
)].concat(
|
|
4625
|
-
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"))],
|
|
4626
5060
|
modelRoutes.map(function (r) {
|
|
4627
5061
|
return h("option", { key: String(r.provider) + "/" + String(r.model), value: encodeRoute(r.provider, r.model) },
|
|
4628
5062
|
r.label || (String(r.provider) + " / " + String(r.model)));
|
|
@@ -4630,13 +5064,13 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4630
5064
|
))
|
|
4631
5065
|
: models
|
|
4632
5066
|
? [
|
|
4633
|
-
h("input", { className: "smem-input", key: "p", style: { flex: "1", minWidth: "110px" }, value: prov, placeholder: "provider
|
|
4634
|
-
h("input", { className: "smem-input", key: "m", style: { flex: "1", minWidth: "110px" }, value: model, placeholder: "model
|
|
4635
|
-
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")),
|
|
4636
5070
|
]
|
|
4637
5071
|
: [
|
|
4638
|
-
h("span", { className: "smem-status", key: "load" }, "
|
|
4639
|
-
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")),
|
|
4640
5074
|
],
|
|
4641
5075
|
]),
|
|
4642
5076
|
h("div", {
|
|
@@ -4653,29 +5087,32 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4653
5087
|
type: "button", className: "smem-btn smem-btn-primary", key: "go",
|
|
4654
5088
|
disabled: busy || running || !remote,
|
|
4655
5089
|
onClick: run,
|
|
4656
|
-
}, running ? "
|
|
5090
|
+
}, running ? t("ad.action.runningNow") : t("ad.action.runNow")),
|
|
4657
5091
|
h("span", { className: "smem-status", key: "s" },
|
|
4658
5092
|
running
|
|
4659
|
-
? ("
|
|
4660
|
-
: (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"))),
|
|
4661
5095
|
]),
|
|
4662
5096
|
running && st && st.steps && st.steps.length
|
|
4663
5097
|
? h("div", { className: "smem-autodream-note", key: "prog" }, st.steps.map(function (x) {
|
|
4664
|
-
return "
|
|
4665
|
-
}).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")))
|
|
4666
5100
|
: null,
|
|
4667
5101
|
last
|
|
4668
5102
|
? h("div", { className: "smem-status", key: "last" },
|
|
4669
|
-
"
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
"
|
|
4673
|
-
|
|
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
|
+
}) +
|
|
4674
5111
|
// 1.9.2:残篇必须在状态行上就能看见 —— 只写「成功」正是 10-25 那次误判的来源。
|
|
4675
|
-
(last.ok && last.truncated ? "
|
|
4676
|
-
(last.ok && !last.truncated && last.retries ? ("
|
|
4677
|
-
(last.continuations ? ("
|
|
4678
|
-
(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 }) : ""))
|
|
4679
5116
|
: null,
|
|
4680
5117
|
]),
|
|
4681
5118
|
]) : null,
|
|
@@ -4684,11 +5121,10 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4684
5121
|
// 「设置 → 记忆管理」这个入口永远在(它注册在 80-apply,和这个开关无关)。
|
|
4685
5122
|
h("div", { className: "smem-autodream-sec", key: "sidebar-entry" }, [
|
|
4686
5123
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4687
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4688
|
-
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")),
|
|
4689
5126
|
]),
|
|
4690
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4691
|
-
"在 DSH 侧栏显示「记忆管理」图标,点它把中央主区切到本页。默认开启;关闭后「设置 → 记忆管理」这个入口仍然保留。改动立即生效,不需要重启。"),
|
|
5127
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.sidebar.note")),
|
|
4692
5128
|
h("label", { className: "smem-check", key: "row" }, [
|
|
4693
5129
|
h("input", {
|
|
4694
5130
|
type: "checkbox", key: "c",
|
|
@@ -4696,7 +5132,7 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4696
5132
|
disabled: sidebarBusy,
|
|
4697
5133
|
onChange: function (e) { saveSidebarEntry(!!(e.target && e.target.checked)); },
|
|
4698
5134
|
}),
|
|
4699
|
-
h("span", { key: "l" }, "
|
|
5135
|
+
h("span", { key: "l" }, t("ad.sidebar.label")),
|
|
4700
5136
|
]),
|
|
4701
5137
|
sidebarErr ? h("div", { className: "smem-autodream-note smem-ad-warn", key: "err" }, sidebarErr) : null,
|
|
4702
5138
|
]),
|
|
@@ -4705,27 +5141,25 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4705
5141
|
// 同一件事只在一处设定,这里不再重复摆一份。
|
|
4706
5142
|
h("div", { className: "smem-autodream-sec", key: "limits" }, [
|
|
4707
5143
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4708
|
-
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")),
|
|
4709
5145
|
memSet && memSet.restartRequired
|
|
4710
|
-
? 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"))
|
|
4711
5147
|
: null,
|
|
4712
5148
|
]),
|
|
4713
5149
|
memSet && memSet.restartRequired
|
|
4714
|
-
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "rw" },
|
|
4715
|
-
|
|
4716
|
-
: h("div", { className: "smem-autodream-note", key: "rn" },
|
|
4717
|
-
"这些值在 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")),
|
|
4718
5152
|
// 五个参数排两列(一行两个):同样的信息,行数少一半,扫读反而更清楚。
|
|
4719
5153
|
h("div", { className: "smem-ad-grid2", key: "grid" }, LIMIT_FIELDS.map(function (f) {
|
|
4720
5154
|
const cur = memSet && memSet.limits ? memSet.limits[f.key] : null;
|
|
4721
5155
|
const pend = memSet && memSet.pendingLimits ? memSet.pendingLimits[f.key] : null;
|
|
4722
5156
|
const diff = cur != null && pend != null && cur !== pend;
|
|
4723
5157
|
return h("div", { className: "smem-ad-field", key: f.key }, [
|
|
4724
|
-
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)),
|
|
4725
5159
|
h("input", {
|
|
4726
5160
|
className: "smem-num", key: "v", type: "number",
|
|
4727
5161
|
min: String(f.lo), max: String(f.hi),
|
|
4728
|
-
title: "
|
|
5162
|
+
title: t("ad.limit.inputTitle", { lo: f.lo, hi: f.hi, unit: limitUnit(f) }),
|
|
4729
5163
|
value: limitsDraft && limitsDraft[f.key] != null ? limitsDraft[f.key] : "",
|
|
4730
5164
|
onChange: function (e) {
|
|
4731
5165
|
const nx = Object.assign({}, limitsDraft);
|
|
@@ -4734,73 +5168,72 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4734
5168
|
},
|
|
4735
5169
|
}),
|
|
4736
5170
|
diff
|
|
4737
|
-
? h("span", { className: "smem-ad-badge smem-ad-badge--warn", key: "d" }, "
|
|
4738
|
-
: 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) }))),
|
|
4739
5173
|
]);
|
|
4740
5174
|
})),
|
|
4741
5175
|
h("div", { className: "smem-autodream-row", key: "save" }, [
|
|
4742
|
-
h("button", { type: "button", className: "smem-btn smem-btn-primary", key: "s", disabled: saveBusy, onClick: saveLimits }, saveBusy ? "
|
|
4743
|
-
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")),
|
|
4744
5178
|
]),
|
|
4745
5179
|
]),
|
|
4746
5180
|
h("div", { className: "smem-autodream-sec", key: "reserved" }, [
|
|
4747
5181
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4748
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4749
|
-
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")),
|
|
4750
5184
|
]),
|
|
4751
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4752
|
-
"内置的两个固定不可删;下面可以再加自己的(只写文件名,不要带路径)。改完需重启 DSH 才生效。"),
|
|
5185
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.reserved.note")),
|
|
4753
5186
|
h("div", { className: "smem-raw-names", key: "fixed" }, fixedReserved.map(function (n) {
|
|
4754
|
-
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"));
|
|
4755
5188
|
})),
|
|
4756
5189
|
h("div", { className: "smem-raw-names", key: "extra" },
|
|
4757
5190
|
extraReserved.length
|
|
4758
5191
|
? extraReserved.map(function (n) {
|
|
4759
5192
|
return h("span", { className: "smem-chip", key: n, "data-on": "1" }, [
|
|
4760
5193
|
n,
|
|
4761
|
-
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); } }, "✕"),
|
|
4762
5195
|
]);
|
|
4763
5196
|
})
|
|
4764
|
-
: h("span", { className: "smem-status", key: "none" }, "
|
|
5197
|
+
: h("span", { className: "smem-status", key: "none" }, t("ad.reserved.empty"))),
|
|
4765
5198
|
h("div", { className: "smem-autodream-row", key: "add" }, [
|
|
4766
5199
|
h("input", {
|
|
4767
5200
|
className: "smem-input", key: "i",
|
|
4768
5201
|
style: { flex: "1", minWidth: "150px" },
|
|
4769
5202
|
value: reservedNew,
|
|
4770
|
-
placeholder: "
|
|
5203
|
+
placeholder: t("ad.reserved.placeholder"),
|
|
4771
5204
|
onChange: function (e) { setReservedNew(e.target.value); },
|
|
4772
5205
|
}),
|
|
4773
|
-
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")),
|
|
4774
5207
|
]),
|
|
4775
5208
|
]),
|
|
4776
5209
|
h("details", { className: "smem-autodream-adv", key: "adv", open: true }, [
|
|
4777
|
-
h("summary", { key: "s" }, "
|
|
5210
|
+
h("summary", { key: "s" }, t("ad.advanced")),
|
|
4778
5211
|
h("div", { key: "d" }, [
|
|
4779
5212
|
c.trigger === "auto" ? h("div", { className: "smem-autodream-row", key: "gate" }, [
|
|
4780
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
4781
|
-
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")),
|
|
4782
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) }); } }),
|
|
4783
|
-
h("span", { className: "smem-status", key: "b" }, "
|
|
5216
|
+
h("span", { className: "smem-status", key: "b" }, t("ad.adv.hoursAndSessions")),
|
|
4784
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) }); } }),
|
|
4785
|
-
h("span", { className: "smem-status", key: "c" }, "
|
|
5218
|
+
h("span", { className: "smem-status", key: "c" }, t("ad.adv.sessionsUpdated")),
|
|
4786
5219
|
]) : null,
|
|
4787
5220
|
h("div", { className: "smem-autodream-row", key: "steps" }, [
|
|
4788
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5221
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.adv.maxSteps")),
|
|
4789
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) }); } }),
|
|
4790
|
-
h("span", { className: "smem-status", key: "s" }, "
|
|
5223
|
+
h("span", { className: "smem-status", key: "s" }, t("ad.adv.stepNote")),
|
|
4791
5224
|
]),
|
|
4792
5225
|
// 1.9.2:单次输出上限(原写死 4096)。思考型模型下 4096 会让收尾轮只剩思考、零正文。
|
|
4793
5226
|
h("div", { className: "smem-autodream-row", key: "maxout" }, [
|
|
4794
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5227
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.adv.maxOutput")),
|
|
4795
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) }); } }),
|
|
4796
|
-
h("span", { className: "smem-status", key: "s" }, "
|
|
5229
|
+
h("span", { className: "smem-status", key: "s" }, t("ad.adv.maxOutputNote")),
|
|
4797
5230
|
]),
|
|
4798
5231
|
h("div", { className: "smem-autodream-row", key: "keep" }, [
|
|
4799
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5232
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.adv.keepSnapshots")),
|
|
4800
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) }); } }),
|
|
4801
|
-
h("span", { className: "smem-status", key: "s" }, "
|
|
5234
|
+
h("span", { className: "smem-status", key: "s" }, t("ad.unit.copies")),
|
|
4802
5235
|
]),
|
|
4803
|
-
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) || "—") })),
|
|
4804
5237
|
]),
|
|
4805
5238
|
]),
|
|
4806
5239
|
]) : null,
|
|
@@ -4809,53 +5242,51 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4809
5242
|
segOf === "records" ? h("div", { key: "seg-rec" }, [
|
|
4810
5243
|
h("div", { className: "smem-autodream-sec", key: "snap" }, [
|
|
4811
5244
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4812
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4813
|
-
h("span", { className: "smem-status", key: "c" }, String(snaps.length)
|
|
4814
|
-
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")),
|
|
4815
5248
|
]),
|
|
4816
5249
|
h("div", { className: "smem-autodream-row", key: "scope" }, [
|
|
4817
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5250
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("ad.row.rollbackScope")),
|
|
4818
5251
|
h(Seg, {
|
|
4819
5252
|
key: "v", value: rollbackScope,
|
|
4820
|
-
options: [{ value: "files", label: "
|
|
5253
|
+
options: [{ value: "files", label: t("ad.scope.files") }, { value: "all", label: t("ad.scope.all") }],
|
|
4821
5254
|
onChange: function (v) { patch({ rollbackScope: v }); },
|
|
4822
5255
|
}),
|
|
4823
5256
|
]),
|
|
4824
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4825
|
-
"回滚前会自动把整个记忆目录再存一份保护快照 —— 所以回滚本身也能再退回来。多出来的文件只移进 archive/,不删除。"),
|
|
5257
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.note.rollback")),
|
|
4826
5258
|
snaps.length === 0
|
|
4827
|
-
? h("div", { className: "smem-status", key: "e" }, "
|
|
5259
|
+
? h("div", { className: "smem-status", key: "e" }, t("ad.snap.empty"))
|
|
4828
5260
|
: h("div", { className: "smem-autodream-list", key: "l" }, snaps.slice(0, 15).map(function (sp) {
|
|
4829
|
-
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"));
|
|
4830
5262
|
return h("div", { className: "smem-ad-wrap", key: String(sp.name) }, [
|
|
4831
5263
|
h("div", { className: "smem-ad-snap", key: "row" }, [
|
|
4832
5264
|
h("div", { className: "smem-ad-main", key: "m" }, [
|
|
4833
|
-
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) })),
|
|
4834
5266
|
h("div", { className: "smem-ad-sub", key: "l2" }, label + " · " + String(sp.name || "")),
|
|
4835
5267
|
]),
|
|
4836
|
-
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,
|
|
4837
5269
|
confirmSnap === sp.name
|
|
4838
5270
|
? null
|
|
4839
5271
|
: h("button", {
|
|
4840
5272
|
type: "button", className: "smem-btn", key: "go",
|
|
4841
5273
|
disabled: rbBusy !== null,
|
|
4842
5274
|
onClick: function () { setRbMsg(null); setConfirmSnap(sp.name); },
|
|
4843
|
-
}, "
|
|
5275
|
+
}, t("ad.action.rollback")),
|
|
4844
5276
|
]),
|
|
4845
5277
|
confirmSnap === sp.name
|
|
4846
5278
|
? h("div", { className: "smem-ad-confirm", key: "cf" }, [
|
|
4847
|
-
h("div", { key: "t", style: { fontWeight: "600" } }, "
|
|
4848
|
-
h("div", { key: "d" }, "
|
|
4849
|
-
? "
|
|
4850
|
-
|
|
4851
|
-
"。回滚前会先存一份保护快照,所以这一步本身也能退。"),
|
|
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")),
|
|
4852
5283
|
h("div", { className: "smem-autodream-row", key: "acts" }, [
|
|
4853
5284
|
h("button", {
|
|
4854
5285
|
type: "button", className: "smem-btn smem-btn-danger", key: "yes",
|
|
4855
5286
|
disabled: rbBusy === sp.name,
|
|
4856
5287
|
onClick: function () { doRollback(sp); },
|
|
4857
|
-
}, rbBusy === sp.name ? "
|
|
4858
|
-
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")),
|
|
4859
5290
|
]),
|
|
4860
5291
|
])
|
|
4861
5292
|
: null,
|
|
@@ -4866,14 +5297,13 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4866
5297
|
// ── 整理声明(新)──────────────────────────────────────────
|
|
4867
5298
|
h("div", { className: "smem-autodream-sec", key: "runs" }, [
|
|
4868
5299
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4869
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4870
|
-
h("span", { className: "smem-status", key: "c" }, String(runs.length)
|
|
4871
|
-
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")),
|
|
4872
5303
|
]),
|
|
4873
|
-
h("div", { className: "smem-autodream-note", key: "n" },
|
|
4874
|
-
"每一趟都留一份整理声明:改了什么、为什么改、哪些想法被拒了。点一条看全文。"),
|
|
5304
|
+
h("div", { className: "smem-autodream-note", key: "n" }, t("ad.records.runsNote")),
|
|
4875
5305
|
runs.length === 0
|
|
4876
|
-
? h("div", { className: "smem-status", key: "e" }, "
|
|
5306
|
+
? h("div", { className: "smem-status", key: "e" }, t("ad.records.runsEmpty"))
|
|
4877
5307
|
: h("div", { className: "smem-autodream-list", key: "l" }, runs.slice(0, 15).map(function (rn) {
|
|
4878
5308
|
return h("div", { className: "smem-ad-wrap", key: String(rn.runId) }, [
|
|
4879
5309
|
h("button", {
|
|
@@ -4882,14 +5312,14 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4882
5312
|
onClick: function () { openDecl(rn.runId); },
|
|
4883
5313
|
}, [
|
|
4884
5314
|
h("span", { key: "t" }, String(rn.atHuman || "")),
|
|
4885
|
-
h("span", { className: "smem-ad-badge", key: "m" }, rn.mode === "apply" ? "
|
|
4886
|
-
h("span", { key: "c" }, "
|
|
4887
|
-
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,
|
|
4888
5318
|
rn.rolledBackAt
|
|
4889
5319
|
? h("span", { className: "smem-ad-badge smem-ad-badge--warn", key: "rb" },
|
|
4890
|
-
|
|
5320
|
+
rn.rolledBackAtHuman ? t("ad.run.rolledBackAt", { at: String(rn.rolledBackAtHuman) }) : t("ad.run.rolledBack"))
|
|
4891
5321
|
: null,
|
|
4892
|
-
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")),
|
|
4893
5323
|
]),
|
|
4894
5324
|
declOpen === rn.runId
|
|
4895
5325
|
? h("pre", { className: "smem-ad-md", key: "body" }, declBody)
|
|
@@ -4899,11 +5329,11 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4899
5329
|
]),
|
|
4900
5330
|
h("div", { className: "smem-autodream-sec", key: "rep" }, [
|
|
4901
5331
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
4902
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
4903
|
-
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) })),
|
|
4904
5334
|
]),
|
|
4905
5335
|
reports.length === 0
|
|
4906
|
-
? h("div", { className: "smem-status", key: "e" }, "
|
|
5336
|
+
? h("div", { className: "smem-status", key: "e" }, t("ad.records.reportsEmpty"))
|
|
4907
5337
|
: h("div", { className: "smem-autodream-list", key: "l" }, reports.slice(0, 12).map(function (r) {
|
|
4908
5338
|
return h("button", {
|
|
4909
5339
|
type: "button", key: r.name, className: "smem-report",
|
|
@@ -4911,8 +5341,8 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4911
5341
|
onClick: function () { openReport(open === r.name ? null : r.name); },
|
|
4912
5342
|
}, [
|
|
4913
5343
|
h("span", { key: "t" }, String(r.atHuman || "") + " · " + String(r.name) + " · " + fmtSize(r.bytes)),
|
|
4914
|
-
r.legacy ? h("span", { className: "smem-ad-badge smem-ad-badge--legacy", key: "lg", style: { marginLeft: "6px" } }, "
|
|
4915
|
-
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")),
|
|
4916
5346
|
]);
|
|
4917
5347
|
})),
|
|
4918
5348
|
open && body ? h("pre", { className: "smem-report-pre", key: "body" }, body) : null,
|
|
@@ -4953,12 +5383,25 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4953
5383
|
const sBusy = react.useState(false); const busy = sBusy[0], setBusy = sBusy[1];
|
|
4954
5384
|
|
|
4955
5385
|
const TH_KEYS = ["archiveAfterDaysProject", "archiveAfterDaysReference", "archiveAfterDaysUser"];
|
|
4956
|
-
|
|
4957
|
-
const
|
|
4958
|
-
archiveAfterDaysProject: "
|
|
4959
|
-
archiveAfterDaysReference: "
|
|
4960
|
-
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",
|
|
4961
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] || ""); };
|
|
4962
5405
|
|
|
4963
5406
|
/** 阈值 → 输入框草稿。存成字符串:number input 清空时立刻变 0 会很难用。 */
|
|
4964
5407
|
const draftOf = function (c) {
|
|
@@ -4970,18 +5413,18 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
4970
5413
|
|
|
4971
5414
|
react.useEffect(function () {
|
|
4972
5415
|
if (!remote || typeof remote.getConfig !== "function") {
|
|
4973
|
-
setErr("
|
|
5416
|
+
setErr(t("archive.err.noConfig"));
|
|
4974
5417
|
return;
|
|
4975
5418
|
}
|
|
4976
5419
|
remote.getConfig()
|
|
4977
5420
|
.then(function (r) {
|
|
4978
5421
|
const d = unwrap(r);
|
|
4979
5422
|
const c = d && d.config ? d.config : null;
|
|
4980
|
-
if (!c) { setErr("
|
|
5423
|
+
if (!c) { setErr(t("archive.err.readConfig")); return; }
|
|
4981
5424
|
setCfg(c);
|
|
4982
5425
|
setDraft(draftOf(c));
|
|
4983
5426
|
})
|
|
4984
|
-
.catch(function (e) { setErr("
|
|
5427
|
+
.catch(function (e) { setErr(t("archive.err.readFailed", { error: (e && e.message) || e })); });
|
|
4985
5428
|
}, []);
|
|
4986
5429
|
|
|
4987
5430
|
const c = cfg || {};
|
|
@@ -5003,16 +5446,16 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5003
5446
|
if (got) { setCfg(got); setDraft(draftOf(got)); }
|
|
5004
5447
|
const bad = failOf(got);
|
|
5005
5448
|
if (!bad) { setNotice(okMsg); return; }
|
|
5006
|
-
setErr("
|
|
5449
|
+
setErr(t("archive.err.notApplied", { error: bad }));
|
|
5007
5450
|
})
|
|
5008
|
-
.catch(function (e) { setErr("
|
|
5451
|
+
.catch(function (e) { setErr(t("archive.err.saveFailed", { error: (e && e.message) || e })); })
|
|
5009
5452
|
.then(function () { setBusy(false); });
|
|
5010
5453
|
}
|
|
5011
5454
|
|
|
5012
5455
|
function saveMode(v) {
|
|
5013
|
-
save({ autoArchive: v }, "
|
|
5456
|
+
save({ autoArchive: v }, t("archive.notice.modeSet", { mode: modeLabel(v) }), function (got) {
|
|
5014
5457
|
const g = got ? autoArchiveOf(got.autoArchive) : null;
|
|
5015
|
-
return g === v ? null : ("
|
|
5458
|
+
return g === v ? null : t("archive.err.modeStill", { mode: g ? modeLabel(g) : t("archive.unknown") });
|
|
5016
5459
|
});
|
|
5017
5460
|
}
|
|
5018
5461
|
|
|
@@ -5023,15 +5466,15 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5023
5466
|
for (const k of TH_KEYS) {
|
|
5024
5467
|
const n = Number(d[k]);
|
|
5025
5468
|
if (!Number.isFinite(n) || n < 1 || n > 3650) {
|
|
5026
|
-
setErr(
|
|
5469
|
+
setErr(t("archive.err.thresholdBad", { label: thLabel(k), value: d[k] }));
|
|
5027
5470
|
return;
|
|
5028
5471
|
}
|
|
5029
5472
|
patch[k] = Math.round(n);
|
|
5030
5473
|
}
|
|
5031
|
-
save(patch, "
|
|
5474
|
+
save(patch, t("archive.notice.thresholdsSaved"), function (got) {
|
|
5032
5475
|
const bad = TH_KEYS.filter(function (k) { return !got || Number(got[k]) !== patch[k]; });
|
|
5033
5476
|
if (!bad.length) return null;
|
|
5034
|
-
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"));
|
|
5035
5478
|
});
|
|
5036
5479
|
}
|
|
5037
5480
|
|
|
@@ -5039,45 +5482,41 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5039
5482
|
// ── ① 它做什么(这一屏存在的理由写在最前面)──
|
|
5040
5483
|
h("div", { className: "smem-autodream-sec", key: "what" }, [
|
|
5041
5484
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
5042
|
-
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, "
|
|
5485
|
+
h("span", { key: "t", style: { fontWeight: "600", fontSize: "12.5px" } }, t("archive.title")),
|
|
5043
5486
|
]),
|
|
5044
|
-
h("div", { className: "smem-autodream-note", key: "p1" },
|
|
5045
|
-
|
|
5046
|
-
h("div", { className: "smem-autodream-note", key: "
|
|
5047
|
-
"它不调模型:候选完全按访问台账的天数算,同一份输入每次算出来都一样。"),
|
|
5048
|
-
h("div", { className: "smem-autodream-note", key: "p3" },
|
|
5049
|
-
"想现在看看会被收走哪些:文件列表 →「工具 ▾」→ 归档候选。"),
|
|
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")),
|
|
5050
5490
|
]),
|
|
5051
5491
|
// ── ② 三档策略 ──
|
|
5052
5492
|
h("div", { className: "smem-autodream-sec", key: "mode" }, [
|
|
5053
5493
|
h("div", { className: "smem-autodream-row", key: "r" }, [
|
|
5054
|
-
h("span", { className: "smem-autodream-key", key: "k" }, "
|
|
5494
|
+
h("span", { className: "smem-autodream-key", key: "k" }, t("archive.modeTitle")),
|
|
5055
5495
|
h(Seg, {
|
|
5056
5496
|
key: "v", value: mode,
|
|
5057
5497
|
options: [
|
|
5058
|
-
{ value: "off", label:
|
|
5059
|
-
{ value: "report", label:
|
|
5060
|
-
{ value: "auto", label:
|
|
5498
|
+
{ value: "off", label: modeLabel("off") },
|
|
5499
|
+
{ value: "report", label: modeLabel("report") },
|
|
5500
|
+
{ value: "auto", label: modeLabel("auto") },
|
|
5061
5501
|
],
|
|
5062
5502
|
onChange: function (v) { if (v !== mode) saveMode(v); },
|
|
5063
5503
|
}),
|
|
5064
|
-
busy ? h("span", { className: "smem-status", key: "s" }, "
|
|
5504
|
+
busy ? h("span", { className: "smem-status", key: "s" }, t("archive.action.saving")) : null,
|
|
5065
5505
|
]),
|
|
5066
|
-
h("div", { className: "smem-autodream-note", key: "cost" },
|
|
5506
|
+
h("div", { className: "smem-autodream-note", key: "cost" }, costText(mode)),
|
|
5067
5507
|
mode === "auto" && !c.apply
|
|
5068
|
-
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "deg" },
|
|
5069
|
-
"注意:「自动做梦 → 改动方式」现在是只出报告 —— 这一档会自动降级成只出报告,不会动文件。")
|
|
5508
|
+
? h("div", { className: "smem-autodream-note smem-ad-warn", key: "deg" }, t("archive.degrade"))
|
|
5070
5509
|
: null,
|
|
5071
5510
|
]),
|
|
5072
5511
|
// ── ③ 三个阈值 ──
|
|
5073
5512
|
h("div", { className: "smem-autodream-sec", key: "th" }, [
|
|
5074
5513
|
h("div", { className: "smem-autodream-row", key: "h" }, [
|
|
5075
|
-
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")),
|
|
5076
5515
|
]),
|
|
5077
5516
|
draft
|
|
5078
5517
|
? h("div", { key: "rows" }, TH_KEYS.map(function (k) {
|
|
5079
5518
|
return h("div", { className: "smem-autodream-row", key: k }, [
|
|
5080
|
-
h("span", { className: "smem-autodream-key", key: "k" },
|
|
5519
|
+
h("span", { className: "smem-autodream-key", key: "k" }, thLabel(k)),
|
|
5081
5520
|
h("input", {
|
|
5082
5521
|
className: "smem-input smem-num", key: "i", type: "number", min: 1, max: 3650,
|
|
5083
5522
|
value: draft[k],
|
|
@@ -5087,19 +5526,18 @@ h("span", { key: "sep", className: "smem-actions-sep", "aria-hidden": "true" }),
|
|
|
5087
5526
|
setDraft(nx);
|
|
5088
5527
|
},
|
|
5089
5528
|
}),
|
|
5090
|
-
h("span", { className: "smem-muted", key: "u" },
|
|
5529
|
+
h("span", { className: "smem-muted", key: "u" }, thHint(k)),
|
|
5091
5530
|
]);
|
|
5092
5531
|
}))
|
|
5093
|
-
: h("div", { className: "smem-muted", key: "loading" }, "
|
|
5532
|
+
: h("div", { className: "smem-muted", key: "loading" }, t("archive.loadingConfig")),
|
|
5094
5533
|
h("div", { className: "smem-autodream-row", key: "btns" }, [
|
|
5095
5534
|
h("button", {
|
|
5096
5535
|
type: "button", className: "smem-btn smem-btn-primary", key: "save",
|
|
5097
5536
|
disabled: busy || !draft, onClick: saveThresholds,
|
|
5098
|
-
}, busy ? "
|
|
5099
|
-
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")),
|
|
5100
5539
|
]),
|
|
5101
|
-
h("div", { className: "smem-autodream-note", key: "note" },
|
|
5102
|
-
"天数按访问台账算(这条记忆上次被注入给模型是什么时候)。feedback_* 永不入选;锁定的记忆也不会被选中。"),
|
|
5540
|
+
h("div", { className: "smem-autodream-note", key: "note" }, t("archive.note.thresholds")),
|
|
5103
5541
|
]),
|
|
5104
5542
|
err ? h("div", { className: "smem-err", key: "err" }, "⚠ " + err) : null,
|
|
5105
5543
|
notice ? h("div", { className: "smem-ok", key: "ok" }, "✓ " + notice) : null,
|