@a9i5k4/dsh-auto-memory 3.1.0 → 3.1.3
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/docs/CONTRIBUTORS.html +2 -2
- package/docs/HANDBOOK.md +13 -9
- package/lib/client.js +786 -134
- package/lib/config-io.js +59 -6
- package/lib/context-host.js +10 -1
- package/lib/evidence-store.js +27 -1
- package/lib/index.js +937 -51
- package/lib/jsonl-tail-cursor.js +75 -0
- package/lib/m7-index-sync-host.js +4 -6
- package/lib/memory-hub.js +25 -2
- package/lib/migrate-pack.js +431 -0
- package/lib/python-setup.js +109 -27
- package/lib/recall-stats.js +242 -0
- package/lib/rules-layer.js +106 -15
- package/lib/semantic-js.js +25 -7
- package/lib/shadow-host.js +41 -2
- package/lib/subagent-gc.js +5 -1
- package/lib/wb-sidecar.js +8 -3
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 3. settings.section — 自动记忆设置页(存储位置、注入、反思风格)
|
|
8
8
|
* Data flows over /api/dsh-auto-memory/* (loopback-only host routes).
|
|
9
9
|
*/
|
|
10
|
-
console.log('[dsh-auto-memory] client v3.1.
|
|
10
|
+
console.log('[dsh-auto-memory] client v3.1.3 fingerprint: session-name-guide-onboarding')
|
|
11
11
|
window.__ModuleLoader__.load({
|
|
12
12
|
id: '@a9i5k4/dsh-auto-memory',
|
|
13
13
|
factory: (require) => {
|
|
@@ -205,10 +205,41 @@ window.__ModuleLoader__.load({
|
|
|
205
205
|
memoryPanel: '记忆面板',
|
|
206
206
|
memory: '记忆',
|
|
207
207
|
autoMemory: '自动记忆',
|
|
208
|
-
overview: '概览',
|
|
208
|
+
overview: '概览',
|
|
209
|
+
statsTab: '统计',
|
|
210
|
+
statsTitle: '召回统计(谁在被想起来)',
|
|
211
|
+
statsSubtitle: '只统计、不改排序——数据攒够后再决定要不要加权。',
|
|
212
|
+
statsQueries: '检索次数',
|
|
213
|
+
statsZeroHit: '零命中',
|
|
214
|
+
statsDistinct: '被召回条目',
|
|
215
|
+
statsTotalHits: '命中总次数',
|
|
216
|
+
statsWarm: '只被召回一次',
|
|
217
|
+
statsByLayer: '按记忆层分布',
|
|
218
|
+
statsByDay: '按天走势',
|
|
219
|
+
statsSources: '按通路分布',
|
|
220
|
+
statsTop: '召回最多的条目',
|
|
221
|
+
statsEmpty: '(还没有统计数据——用 memory_recall 检索几次后再回来看)',
|
|
222
|
+
statsReset: '清零统计',
|
|
223
|
+
statsResetDone: '已清零',
|
|
224
|
+
statsChannels: '三条通路(分开统计,含义不同)',
|
|
225
|
+
statsChOverview: '各通路占多少',
|
|
226
|
+
statsChModel: '① 模型主动检索',
|
|
227
|
+
statsChModelHint: '模型明确想找 → 高信号:哪些内容真的被需要',
|
|
228
|
+
statsChInject: '② 每轮自动注入',
|
|
229
|
+
statsChInjectHint: '系统一直在塞 → 成本视角:注入预算花在哪一段',
|
|
230
|
+
statsChShadow: '③ 主动唤起',
|
|
231
|
+
statsChShadowHint: '系统判断该唤起 → 质量视角:唤起了但没命中的比例',
|
|
232
|
+
statsEvents: '发生次数',
|
|
233
|
+
statsNoInject: '还没有注入记录(宿主重启后开始累积)',
|
|
234
|
+
statsSegChars: '按字符数(谁最占预算)',
|
|
235
|
+
statsSegCount: '按出现次数',
|
|
236
|
+
statsSince: '开始记录于',
|
|
237
|
+
statsNever: '从未', logs: '日志', refineTab: '唤起回顾', notes: '笔记', reflections: '反思', connect: '接续', calendar: '日历', search: '检索', workspaces: '工作区', hubTab: '记忆中枢', storageTab: '存储管理', planTab: '白板', planTitle: '白板 · PLAN.md(项目全貌)', planEmpty: '(白板尚未建立——宿主会在该工作区第一次会话结束时自动落一版骨架,模型理解项目全貌后把它重写成真内容;也可在对话中让它用 memory_note(kind=plan) 写)', planVersions: '白板历史版本', planLedgers: '交接账本时间线', planDisabled: '交接白板未启用(设置 → 长会话接续)。', planWsUnbound: '当前会话未绑定工作区——白板与交接账本暂不可用(会话的工作区身份解析不出来;把会话开进项目文件夹或绑定工作区后自动恢复)。', planFileTitle: '查看',
|
|
209
238
|
hubSkills: '技能 (Procedural)', hubSkillsEmpty: '暂无已固化的技能。反复成功的流程会自动固化为技能并在相似场景召回。', hubFacts: '事实 (Semantic)', hubFactsEmpty: '暂无固化的事实。', hubConflicts: '待决冲突', hubEpisodic: '经历 (Episodic)', hubEpisodicEmpty: '暂无已巩固的经历。',
|
|
210
239
|
storageScanHint: '语料健康 = 逐源比对索引(sidecar)与正文的 digest。手动改动记忆文件后索引会失配,该记忆会退出检索直到重建索引。',
|
|
211
240
|
storageDeleteHint: '删除记忆 = 正文原子删除 + 在途唤起包清理 + 派生事实撤销(三联动)。已产生的 seen 证据不改写。',
|
|
241
|
+
migTitle: '迁移搬包', migHint: '把某个工作区的记忆打包带走,到另一台机器导入即可(路径变了会自动重写内部引用)。', migExport: '导出本工作区', migImport: '导入包', migPickPack: '选择 .dam-pack 文件', migPreview: '预览差异(不改任何文件)', migApply: '确认导入',
|
|
242
|
+
migPathChanged: '路径变化:将重写内部引用', migSamePath: '路径相同:不做任何重写', migConflictKeep: '冲突时保留本机', migConflictOverwrite: '冲突时用包内版本覆盖', migConflictRename: '冲突时改名为 .from-pack', migBackup: '已备份到', migWritten: '已写入文件数',
|
|
212
243
|
secSemantic: '自动记忆引擎', semMode: '检索模式', semAuto: '自动(推荐)', semLexOnly: '仅词法', semJs: '内置语义', semPy: '高级 Python',
|
|
213
244
|
pyWizTitle: 'Python 引擎一键安装', pyWizStep1: '① 检测 Python 环境', pyWizStep2: '② 创建独立虚拟环境(~/.dsh/python-engine/.venv,不污染系统)', pyWizStep3: '③ 安装引擎依赖(transformers + onnxruntime + torch)', pyWizStep4: '④ 下载 BGE-M3 int8 模型(~539MB,断点续传,失败自动换源)',
|
|
214
245
|
pyWizDetect: '开始检测', pyWizRedetect: '重新检测', pyWizCreate: '创建虚拟环境', pyWizInstall: '安装依赖', pyWizDownload: '开始下载', pyWizCancel: '取消下载', pyWizDone: '全部就绪 ✓ — 回上方启用即可', pyWizOk: 'ok', pyWizMissing: '未找到', pyWizTooOld: '版本不兼容(需 3.9-3.12)', pyWizVenvRec: '推荐', pyWizEta: '剩余', pyWizSec: '秒', pyWizModelHint: '模型与虚拟环境安装在 ~/.dsh/python-engine/(用户目录),升级/重装插件不受影响。',
|
|
@@ -250,7 +281,7 @@ window.__ModuleLoader__.load({
|
|
|
250
281
|
pickedDir: '已选择目录:', rememberSave: '(点击「保存设置」后生效)', pickerUnavailable: '系统文件夹选择器不可用,改用内嵌浏览。',
|
|
251
282
|
fDayBoundary: '日界(分钟)', fDayBoundaryHint: '从 0 点起算:凌晨在此之前的活儿归前一天。默认 450=早上 7:30 进入新一天;改 480=8:00;0=按午夜切日。',
|
|
252
283
|
fVersion: '插件版本', checkUpdate: '检查更新', checking: '检查中…', upToDate: '(已是最新)', hasUpdate: '(有新版本)', versionError: '版本检查失败: ', versionCmdHint: '更新命令: cd ~/.dsh/profiles/web && pnpm up @a9i5k4/dsh-auto-memory,然后重启 dsh web 生效。',
|
|
253
|
-
updateNow: '一键更新', updating: '更新中…', updateDone: '更新完成,请重启 dsh web 生效。', updateFailed: '更新失败: ', devLinkHint: '(
|
|
284
|
+
updateNow: '一键更新', updating: '更新中…', updateDone: '更新完成,请重启 dsh web 生效。', updateFailed: '更新失败: ', devLinkHint: '(当前为本地开发链接 link:,一键更新不适用。若上方显示「开发树 vX → 线上 vY」,说明 npm 上已有更新版,请同步开发源码后重新构建发布。)', noProfileHint: '(未找到 dsh profile,无法自动更新)',
|
|
254
285
|
kvVersion: '插件版本', kvUnreadable: '(读不到)', kvPid: 'PID / 启动', kvRestart: 'Host 需重启?', kvRestartYes: '是(代码比进程新)', kvRestartNo: '否',
|
|
255
286
|
kvHeartbeat: '轮询心跳', hbAliveAgo: '存活(上次 ', hbSecAgo: ' 秒前)', hbAlive: '存活', hbNone: '无心跳文件',
|
|
256
287
|
kvQueue: '沉淀重试队列', kvCountSuffix: ' 条', kvToday: '今日沉淀', kvRecentPrefix: ' 条 / 最近 ', kvBusy: '正在沉淀中', kvYes: '是', kvNo: '否',
|
|
@@ -258,7 +289,10 @@ window.__ModuleLoader__.load({
|
|
|
258
289
|
kvMem: '记忆文件', kvMemUser: '用户', kvMemNotes: '笔记', kvMemLog: '日志', kvMemMissing: '缺失',
|
|
259
290
|
kvWs: '当前工作区', kvWsUnknown: '(未取到)', kvApi: 'API 连通', unknown: '(未知)',
|
|
260
291
|
// ★P0-2/P0-3(2026-09-22):把后端早已算好、前端零消费者的两组可观测性接到界面上。
|
|
261
|
-
dbgHubIo: '三层记忆落盘', dbgPrune: '事实淘汰(保留上限)',
|
|
292
|
+
dbgHubIo: '三层记忆落盘', dbgPrune: '事实淘汰(保留上限)',
|
|
293
|
+
dbgLogs: '诊断日志',
|
|
294
|
+
dbgLogsNone: '尚未生成(本次运行还没有诊断输出)',
|
|
295
|
+
dbgLogsHint: '崩溃/异常时请把下面路径的文件附给维护者(只读展示,不含日志正文)',
|
|
262
296
|
kvPyTitle: 'Python 语义侧车', kvPyOff: '未启用(JS 端语义不受影响)', kvPyAlive: '运行中',
|
|
263
297
|
kvPyWatchdog: '看门狗击杀 / 重生', kvPyStderr: '最近 stderr 尾部', kvPyStderrTrunc: '(已截断)',
|
|
264
298
|
updateTitle: 'dsh-auto-memory 已更新', updateSub: '本次更新内容:', gotIt: '知道了', noticeOpen: '了解更多',
|
|
@@ -355,6 +389,7 @@ window.__ModuleLoader__.load({
|
|
|
355
389
|
fSnapGap: '快照最小间隔(轮)', fSnapGapHint: '动态记忆快照内容变化后,至少隔 N 轮才重新注入,避免每轮日志微变都追加快照导致历史膨胀。默认 5;0=每轮都尝试(仍受内容变化约束)。',
|
|
356
390
|
fReinjectOnCompact: '压缩后立即重注入快照', fReinjectOnCompactHint: '上下文被压缩/截断(通常伴随 contextVersion 重置)后,快照会被清掉;开启后强制立即重注入一次,确保记忆背景重建。默认开。',
|
|
357
391
|
fPromptCustom: '自定义记忆注入 prompt', fPromptCustomHint: '可覆盖各层提示文案(小众功能)。支持占位符 {date} {ws} {budget} {n}。改坏了可一键恢复默认。',
|
|
392
|
+
fPromptSections: '高级:逐段注入控制…', fPromptSectionsHint: '控制每一轮注入里包含哪些段落。默认全部开启;不推荐修改 —— 关掉某段会让模型少掉对应的上下文或纪律。',
|
|
358
393
|
fDays: '注入最近日志天数', fDaysHint: '会话开始时注入最近 N 天的工作日志尾部。默认 1。',
|
|
359
394
|
fExtBudget: '外部记忆注入预算(字符)', fExtBudgetHint: '外部记忆来源在上下文中的注入预算。默认 1400(路径模式下影响有限)。',
|
|
360
395
|
fConsolidateMin: '自动沉淀内容门槛(字符)', fConsolidateMinHint: '本轮 user+assistant 总字符低于此值视为寒暄跳过。默认 240。',
|
|
@@ -412,10 +447,41 @@ window.__ModuleLoader__.load({
|
|
|
412
447
|
memoryPanel: 'Memory Panel',
|
|
413
448
|
memory: 'Memory',
|
|
414
449
|
autoMemory: 'Auto Memory',
|
|
415
|
-
overview: 'Overview',
|
|
450
|
+
overview: 'Overview',
|
|
451
|
+
statsTab: 'Stats',
|
|
452
|
+
statsTitle: 'Recall statistics (what gets remembered)',
|
|
453
|
+
statsSubtitle: 'Recording only — ranking untouched. Weighting comes after the data proves the metric.',
|
|
454
|
+
statsQueries: 'Queries',
|
|
455
|
+
statsZeroHit: 'Zero-hit',
|
|
456
|
+
statsDistinct: 'Distinct recalled',
|
|
457
|
+
statsTotalHits: 'Total hits',
|
|
458
|
+
statsWarm: 'Recalled once',
|
|
459
|
+
statsByLayer: 'By layer',
|
|
460
|
+
statsByDay: 'Per day',
|
|
461
|
+
statsSources: 'By channel',
|
|
462
|
+
statsTop: 'Most recalled',
|
|
463
|
+
statsEmpty: '(no data yet — run memory_recall a few times and come back)',
|
|
464
|
+
statsReset: 'Reset stats',
|
|
465
|
+
statsResetDone: 'Cleared',
|
|
466
|
+
statsChannels: 'Three channels (counted separately — they mean different things)',
|
|
467
|
+
statsChOverview: 'Share per channel',
|
|
468
|
+
statsChModel: '1) Model-initiated recall',
|
|
469
|
+
statsChModelHint: 'The model explicitly looked for it -> high signal: what is actually needed',
|
|
470
|
+
statsChInject: '2) Auto-injected every turn',
|
|
471
|
+
statsChInjectHint: 'The system keeps feeding it -> cost view: which section eats the budget',
|
|
472
|
+
statsChShadow: '3) Proactive shadow recall',
|
|
473
|
+
statsChShadowHint: 'System judged it worth surfacing -> quality view: how often it came up empty',
|
|
474
|
+
statsEvents: 'Events',
|
|
475
|
+
statsNoInject: 'No injection recorded yet (accumulates after host restart)',
|
|
476
|
+
statsSegChars: 'By characters (who eats the budget)',
|
|
477
|
+
statsSegCount: 'By occurrences',
|
|
478
|
+
statsSince: 'Recording since',
|
|
479
|
+
statsNever: 'never', logs: 'Logs', refineTab: 'Recall review', notes: 'Notes', reflections: 'Reflections', connect: 'Connect', calendar: 'Calendar', search: 'Search', workspaces: 'Workspaces', hubTab: 'Memory Hub', storageTab: 'Storage', planTab: 'Whiteboard', planTitle: 'Whiteboard · PLAN.md (project plan)', planEmpty: '(not created yet — the host seeds a skeleton board when a turn first ends in this workspace; the model rewrites it with real content once it grasps the picture, or ask it to write memory_note kind=plan)', planVersions: 'Plan versions', planLedgers: 'Handoff ledger timeline', planDisabled: 'Handoff whiteboard is disabled (Settings → Handoff).', planWsUnbound: 'This session has no bound workspace — whiteboard and handoff ledgers are unavailable (the session workspace could not be resolved; open the session inside a project folder or bind a workspace to restore).', planFileTitle: 'View',
|
|
416
480
|
hubSkills: 'Skills (Procedural)', hubSkillsEmpty: 'No solidified skills yet. Repeatedly-successful workflows become skills and are recalled in similar contexts.', hubFacts: 'Facts (Semantic)', hubFactsEmpty: 'No solidified facts yet.', hubConflicts: 'Pending conflicts', hubEpisodic: 'Episodes (Episodic)', hubEpisodicEmpty: 'No consolidated episodes yet.',
|
|
417
481
|
storageScanHint: 'Corpus health = compare each source\'s index (sidecar) against its body digest. After you hand-edit a memory file the index no longer matches, and that memory drops out of retrieval until the index is rebuilt.',
|
|
418
482
|
storageDeleteHint: 'Delete = atomic body removal + in-flight activation purge + derived-fact revocation (cascading). Evidence already recorded (seen) is never rewritten.',
|
|
483
|
+
migTitle: 'Migrate pack', migHint: 'Pack one workspace memory and carry it to another machine; when the path changes, internal references are rewritten automatically.', migExport: 'Export this workspace', migImport: 'Import pack', migPickPack: 'Pick a .dam-pack file', migPreview: 'Preview diff (writes nothing)', migApply: 'Confirm import',
|
|
484
|
+
migPathChanged: 'Path changed: internal references will be rewritten', migSamePath: 'Same path: no rewrite at all', migConflictKeep: 'On conflict: keep this machine', migConflictOverwrite: 'On conflict: overwrite with pack', migConflictRename: 'On conflict: rename to .from-pack', migBackup: 'Backed up to', migWritten: 'Files written',
|
|
419
485
|
secSemantic: 'Semantic engine', semMode: 'Retrieval mode', semAuto: 'Auto (recommended)', semLexOnly: 'Lexical only', semJs: 'Built-in semantic', semPy: 'Advanced Python',
|
|
420
486
|
pyWizTitle: 'One-click Python engine setup', pyWizStep1: '1. Detect Python environment', pyWizStep2: '2. Create isolated venv (~/.dsh/python-engine/.venv, system untouched)', pyWizStep3: '3. Install engine deps (transformers + onnxruntime + torch)', pyWizStep4: '4. Download BGE-M3 int8 model (~539MB, resumable, auto mirror failover)',
|
|
421
487
|
pyWizDetect: 'Detect', pyWizRedetect: 'Re-detect', pyWizCreate: 'Create venv', pyWizInstall: 'Install deps', pyWizDownload: 'Start download', pyWizCancel: 'Cancel download', pyWizDone: 'All ready - enable it above', pyWizOk: 'ok', pyWizMissing: 'not found', pyWizTooOld: 'incompatible (need 3.9-3.12)', pyWizVenvRec: 'recommended', pyWizEta: 'eta', pyWizSec: 's', pyWizModelHint: 'Model and venv live in ~/.dsh/python-engine/ (user dir) - plugin upgrades never touch them.',
|
|
@@ -456,14 +522,17 @@ window.__ModuleLoader__.load({
|
|
|
456
522
|
pickedDir: 'Selected folder:', rememberSave: '(click Save to apply)', pickerUnavailable: 'Native folder picker unavailable, falling back to in-app browser.',
|
|
457
523
|
fDayBoundary: 'Day boundary (minutes)', fDayBoundaryHint: 'Minutes from midnight: work before it counts as the previous day. Default 450 = 7:30 AM starts the new day; 480 = 8:00; 0 = midnight cut.',
|
|
458
524
|
fVersion: 'Plugin version', checkUpdate: 'Check for updates', checking: 'Checking…', upToDate: '(up to date)', hasUpdate: '(update available)', versionError: 'Version check failed: ', versionCmdHint: 'Update: cd ~/.dsh/profiles/web && pnpm up @a9i5k4/dsh-auto-memory, then restart dsh web.',
|
|
459
|
-
updateNow: 'Update now', updating: 'Updating…', updateDone: 'Update finished, restart dsh web to apply.', updateFailed: 'Update failed: ', devLinkHint: '(local dev link, sync source
|
|
525
|
+
updateNow: 'Update now', updating: 'Updating…', updateDone: 'Update finished, restart dsh web to apply.', updateFailed: 'Update failed: ', devLinkHint: '(local dev link — one-click update does not apply. If "dev tree vX to registry vY" shows above, a newer version is on npm; sync the source and rebuild.)', noProfileHint: '(no dsh profile found, cannot auto-update)',
|
|
460
526
|
kvVersion: 'Plugin version', kvUnreadable: '(unreadable)', kvPid: 'PID / Started', kvRestart: 'Host restart needed?', kvRestartYes: 'yes (code newer than process)', kvRestartNo: 'no',
|
|
461
527
|
kvHeartbeat: 'Heartbeat', hbAliveAgo: 'alive (last ', hbSecAgo: ' s ago)', hbAlive: 'alive', hbNone: 'no heartbeat file',
|
|
462
528
|
kvQueue: 'Consolidation retry queue', kvCountSuffix: ' item(s)', kvToday: 'Consolidated today', kvRecentPrefix: ' item(s) / latest ', kvBusy: 'Consolidating now', kvYes: 'yes', kvNo: 'no',
|
|
463
529
|
kvAvail: 'available', kvUnavail: 'unavailable', kvDup: 'Duplicate note headings', kvDupCount: '',
|
|
464
530
|
kvMem: 'Memory files', kvMemUser: 'user', kvMemNotes: 'notes', kvMemLog: 'log', kvMemMissing: 'missing',
|
|
465
531
|
kvWs: 'Current workspace', kvWsUnknown: '(unknown)', kvApi: 'API probes', unknown: '(unknown)',
|
|
466
|
-
dbgHubIo: 'Layer-3 memory persistence', dbgPrune: 'Fact eviction (retention cap)',
|
|
532
|
+
dbgHubIo: 'Layer-3 memory persistence', dbgPrune: 'Fact eviction (retention cap)',
|
|
533
|
+
dbgLogs: 'Diagnostic log',
|
|
534
|
+
dbgLogsNone: 'not created yet (no diagnostic output this run)',
|
|
535
|
+
dbgLogsHint: 'On a crash/exception, attach the file at the path below (read-only; log body not shown)',
|
|
467
536
|
kvPyTitle: 'Python sidecar', kvPyOff: 'disabled (JS-side semantics unaffected)', kvPyAlive: 'running',
|
|
468
537
|
kvPyWatchdog: 'Watchdog kills / respawns', kvPyStderr: 'Last stderr tail', kvPyStderrTrunc: ' (truncated)',
|
|
469
538
|
updateTitle: 'dsh-auto-memory Updated', updateSub: 'What\'s new in this update:', gotIt: 'Got it', noticeOpen: 'Learn more',
|
|
@@ -557,6 +626,7 @@ window.__ModuleLoader__.load({
|
|
|
557
626
|
fSnapGap: 'Snapshot min gap (turns)', fSnapGapHint: 'After the dynamic memory snapshot changes, re-inject at least N turns later, so small per-turn log changes do not append a new snapshot every turn (history bloat). Default 5; 0 = try every turn (still change-gated).',
|
|
558
627
|
fReinjectOnCompact: 'Re-inject snapshot immediately after compaction', fReinjectOnCompactHint: 'When the context is compacted/truncated (usually a contextVersion reset), the snapshot is cleared; on = force re-inject once so the memory background rebuilds. Default on.',
|
|
559
628
|
fPromptCustom: 'Customize memory-injection prompt', fPromptCustomHint: 'Override any prompt layer (power feature). Placeholders: {date} {ws} {budget} {n}. One-click reset restores defaults.',
|
|
629
|
+
fPromptSections: 'Advanced: per-section injection…', fPromptSectionsHint: 'Controls which sections each round injects. All on by default; not recommended to change — turning one off drops the matching context or discipline.',
|
|
560
630
|
fDays: 'Recent days injected', fDaysHint: 'Inject tails of the last N days of work logs at session start. Default 1.',
|
|
561
631
|
fExtBudget: 'External memory injection budget (chars)', fExtBudgetHint: 'Budget for external memory sources in context. Default 1400 (limited effect with path mode).',
|
|
562
632
|
fConsolidateMin: 'Auto-consolidation content threshold (chars)', fConsolidateMinHint: 'Turns with fewer combined user+assistant chars are treated as chit-chat and skipped. Default 240.',
|
|
@@ -703,7 +773,7 @@ window.__ModuleLoader__.load({
|
|
|
703
773
|
+ '【收尾自检】本轮有实质产出才做;纯只读/闲聊轮跳过。三个方向 + 分类别丢:\n'
|
|
704
774
|
+ '① 写记忆文件 memory_log —— kind 按性质选,**不要一律 fact**:rule=用户约束/约定(会被规则层当硬约束注入)、preference=偏好、fact=事实记录、todo=待办;\n'
|
|
705
775
|
+ '② 更新白板与账本 —— ★**硬映射(满足即必须做,别自行判断"算不算"变化)**:\n · 本轮**改过 lib/ 下任何文件**(含测试/工具) ⇒ 必写 **memory_note(kind=handoff)** 四段式账本;\n · 白板"当前进度"与本轮结束时的**事实不符**(回归数字/已完成项/下一步) ⇒ 必做 **memory_note(kind=plan)** 重写白板;\n · 仅补充一条可复用结论 ⇒ 才用 **memory_note(kind=note, action=append)**。\n ⚠️ **kind=note 不等于白板/账本**——只写 note 却宣称"已更新白板与账本"是**失职**;write 直接写 docs/ 的 md 也**不算**插件记忆。\n'
|
|
706
|
-
+ '③ 长期记忆判断(三个去处)—— 跨会话有用的 → memory_note / 跨项目规则 → memory_user / **跑通且可复用的多步流程 → memory_procedure**(技能库唯一模型入口,不写就没有;建议填 successCriteria);\n'
|
|
776
|
+
+ '③ 长期记忆判断(三个去处)—— 跨会话有用的 → memory_note / 跨项目规则 → memory_user / **规则条目增删改(用户级硬约束,含过时条目真删)→ memory_rules** / **跑通且可复用的多步流程 → memory_procedure**(技能库唯一模型入口,不写就没有;建议填 successCriteria);\n'
|
|
707
777
|
+ '④ 结论失效/被取代 —— 传 `supersedes=` 旧条目 mem_id 标 **superseded(被更新结论取代)**;**当时就做错了要撤回**则传 `retract=` 标 **retracted(撤回)**并尽量附 `retractReason=` 说明错在哪(两者不同:前者有后继,后者本身就是教训);标错了用 `restore=` 撤回;\n'
|
|
708
778
|
+ '⑤ 看板落列 —— 白板/账本内容要进面板看板泳道,须在标题或正文写 tag:`type:goal` / `type:state` / `type:dead-end` / `type:progress`(5 条泳道含「版本归档」,靠归档动作而非 tag);不写 tag 系统只能按标题文字猜,常落空;\n'
|
|
709
779
|
+ '⑥ 语体 —— 客观陈述、第三人称,只留可复用的事实/决策/规则/路径(不写"我考虑/我排查/我想")。',
|
|
@@ -736,6 +806,50 @@ window.__ModuleLoader__.load({
|
|
|
736
806
|
|
|
737
807
|
// ───────────────────────── 更新弹窗 / 首次指导 ─────────────────────────
|
|
738
808
|
var CHANGELOG = {
|
|
809
|
+
'3.1.3': { zh: [
|
|
810
|
+
'★ 修复:宿主下次改会话文件名时,五条功能链会集体静默降级 —— 已从根上拆掉;会话日志路径不再用固定候选表,改按结构识别(兼容未来 v4 命名)。',
|
|
811
|
+
'★ 修复:迁移搬包的范围扩到日历(CALENDAR.md);日历跨工作区共享,导入按合并不覆盖,本机已完成的条目不被打回,同包重导不重复。',
|
|
812
|
+
'★ 修复:「周期记忆快照」开关此前是死开关 —— 界面能翻面、能落盘,但记忆照旧每轮注入。现在关掉它真的会停注入(只关这一条通路,记忆写入与检索不受影响)。',
|
|
813
|
+
'修复:引导里「思维链监听」原写「默认关」,真值是开 —— 方向反了,会误导隐私判断;已改正。',
|
|
814
|
+
'修复:引导里「技能固化」原说晋升审批在记忆中枢页(实际在唤起回顾页),且该开关并不控晋升;两处指引已纠正。',
|
|
815
|
+
'修复:白板空态与索引提示指向的分区名已废弃(自动化/自动记忆引擎);改为实际分区名(长会话接续/语义记忆总开关)。',
|
|
816
|
+
'修复:设置页补上「机械流程切片」控件 —— 该键功能本来是通的,但此前只有向导里能改。',
|
|
817
|
+
'新增:引导末页新增「贡献者与赞助」「DSH API 中转站」两个入口。',
|
|
818
|
+
'新增:关闭新手引导后弹出的更新说明,从此跟随当前版本(此前恒为 2.1.0)。',
|
|
819
|
+
'新增:云盘/远端同步目录探测(判断目录是否可用并给出人话诊断;只当普通目录,不依赖任何网盘专有接口)。',
|
|
820
|
+
], en: [
|
|
821
|
+
'★ Fix: five feature chains would silently degrade the next time the host renamed its session-log file — root cause removed. Session paths are no longer a fixed candidate list; they are matched structurally, so future v4 names work.',
|
|
822
|
+
'★ Fix: pack-and-carry now includes your calendar (CALENDAR.md). The calendar is shared across workspaces, so import merges instead of overwriting: locally completed items are never reverted, and re-importing the same pack adds nothing.',
|
|
823
|
+
'★ Fix: the "periodic memory snapshot" switch used to be dead — it flipped and saved, but memories were injected every turn regardless. Turning it off now really stops injection (only that one path; memory writes and search are untouched).',
|
|
824
|
+
'Fix: the tour said the reasoning observer defaults OFF — it defaults ON. That reversal could mislead privacy decisions; corrected.',
|
|
825
|
+
'Fix: the tour said promotion review lives in the Memory Hub tab (it is the Recall review tab), and that switch does not control promotion at all; both pointers corrected.',
|
|
826
|
+
'Fix: the whiteboard empty state and index hints pointed at renamed/removed settings sections; now they name the real ones.',
|
|
827
|
+
'Fix: Settings now exposes the "mechanical flow slicing" control — the key worked, but previously only the tour could change it.',
|
|
828
|
+
'New: two entry points on the tour’s last page — Contributors & Sponsors, and the DSH API relay.',
|
|
829
|
+
'New: the release-notes card after the tour now follows the current version (it used to be pinned to 2.1.0).',
|
|
830
|
+
'New: cloud/remote sync-directory probe (reports whether a directory is usable, with a plain-language hint; treats it as an ordinary folder and depends on no vendor API).',
|
|
831
|
+
] },
|
|
832
|
+
'3.1.1': { zh: [
|
|
833
|
+
'新增:记忆搬包 —— 把某个工作区的记忆整体搬到另一台机器。「存储管理」页签新增入口:导出本工作区得到 .dam-pack,到新机器导入。',
|
|
834
|
+
'导入是三步向导:选包 → 预览差异(只读,不动任何文件) → 确认导入。预览会列出路径是否变化、新增/覆盖几个文件、内部引用要重写多少处。',
|
|
835
|
+
'换机器会自动重写内部路径:包内指向旧路径/旧 slug 的引用(含正文里的路径字样)按 4 种写法逐一替换成新机器真实路径;路径相同则一个字都不改。',
|
|
836
|
+
'冲突默认保留本机(不覆盖);也可选覆盖或改名为 .from-pack 另存。四道底线:导入前整体备份(失败即中止)、sha256 校验不过即拒、拒绝路径穿越、workspaces-summary.json 走合并(不冲掉你其它工作区)。',
|
|
837
|
+
'修复:PowerShell/控制台不再被自动记忆的日志刷屏 —— 根因是诊断输出在落盘后又打了一遍控制台,而它是唯一每轮都会调用的日志出口。现在只落盘、不上控制台。',
|
|
838
|
+
'修复:诊断日志不再无限增长 —— 此前无任何轮转,实测单文件已达 11 MB / 11.2 万行;现在超过 2 MB 自动轮转,只保留当前 + 1 份历史。',
|
|
839
|
+
'新增:诊断日志现在可以在「设置 → 诊断」页签里查 —— 显示完整路径(可复制)、大小、行数,并提示崩溃时把该文件附给维护者。',
|
|
840
|
+
'说明:两台机器同步本轮只做一次性搬包。后续不发明私有协议 —— 把导出目录指到你的云盘(OneDrive/Dropbox/坚果云等),靠包内校验和 + 显式导入来对齐。',
|
|
841
|
+
'修复(上游回流 PR #124):记忆中枢的「批内合并落盘」此前只挂在宿主喂数循环上 ⇒ 其它的批量入口(HTTP action=feed,面板/外部重放器一次喂一整批)仍是每行一次整份写盘(写放大)。现已把批语义下沉到「拥有这批行」的那一层,任何批量入口都自动只落一次。',
|
|
842
|
+
], en: [
|
|
843
|
+
'New: memory pack — move one workspace\'s memory to another machine. A migrate card in the Storage tab exports this workspace to a .dam-pack; import it on the new machine.',
|
|
844
|
+
'Import is a 3-step wizard: pick pack -> preview diff (read-only, writes nothing) -> confirm. The preview lists whether paths changed, how many files are added or overwritten, and how many internal references will be rewritten.',
|
|
845
|
+
'Paths are rewritten automatically on a new machine: references to the old path and slug (including path text inside bodies) are replaced with the real new path in 4 spellings; if the path is identical, nothing is changed at all.',
|
|
846
|
+
'Conflicts default to keeping yours (no overwrite); you may instead overwrite or save alongside as .from-pack. Four safeguards: full backup before import (abort on failure), sha256 check rejects tampered packs, path traversal rejected, and workspaces-summary.json is MERGED so your other workspaces are preserved.',
|
|
847
|
+
'Fixed: PowerShell and the console are no longer flooded by auto-memory logs — the diagnostic writer logged to the console again after writing the file, and it is the only log outlet invoked every turn. It now writes to file only.',
|
|
848
|
+
'Fixed: the diagnostic log no longer grows without bound — there was no rotation at all and the file had reached 11 MB and 112k lines. It now rotates at 2 MB, keeping the current file plus one history file.',
|
|
849
|
+
'New: the diagnostic log is now viewable under Settings -> Diagnostics, showing the full path (copyable), size and line count, plus a note to attach that file when reporting a crash.',
|
|
850
|
+
'Note: two-machine sync in this release is one-shot packing only. No private sync protocol is invented — point the export folder at your cloud drive and reconcile via the pack checksum plus explicit import.',
|
|
851
|
+
'Fixed (upstream backport PR #124): batch-merged persistence in the memory hub used to hang off the host feed loop only, so other batch entries (HTTP action=feed, panel or external replay feeding a whole batch) still wrote the entire snapshot once per row. Batch semantics now live in the layer that owns the rows, so every batch entry persists once.',
|
|
852
|
+
] },
|
|
739
853
|
'3.1.0': { zh: [
|
|
740
854
|
'新增:记忆中枢「**人工越权晋升**」——给「证据还没攒够」被挡住的自沉淀条目一个真正可用的人工通道。两类门现在分得很清:**统计门**(跨会话数/成功次数没攒够)=内容没问题、只是证据不足 ⇒ 给按钮,你可以人工越过;**结构门**(已弃用/只是观察线索/有纠正记录/没写「怎么算成功」/高风险未批准)=内容本身还不构成技能 ⇒ 不给按钮,并直接写明为什么。所以不会再出现「点了没反应」的假按钮。越权过的条目会留下「人工授权」痕迹,可审计。',
|
|
741
855
|
'新增:设置页按用途重排为 9 个分区(语义记忆总开关 / 记忆窗口(注入什么) / 记忆容量与归档 / 自动沉淀成技能 / 长会话接续 / 自动化与免打扰 / 存储与外部记忆 / 外观与交互 / 关于与诊断)。**控件一字未改**,只是搬进对应分区并改了分组标题——此前「记忆中枢的判定参数」和「注入配置」混在一个分区里,找东西全靠翻。',
|
|
@@ -1230,6 +1344,7 @@ window.__ModuleLoader__.load({
|
|
|
1230
1344
|
update: ROUTE_PREFIX + '/update',
|
|
1231
1345
|
config: ROUTE_PREFIX + '/config',
|
|
1232
1346
|
reflect: ROUTE_PREFIX + '/reflect',
|
|
1347
|
+
recallStats: ROUTE_PREFIX + '/recall-stats',
|
|
1233
1348
|
reflectAuto: ROUTE_PREFIX + '/reflect-auto',
|
|
1234
1349
|
note: ROUTE_PREFIX + '/note',
|
|
1235
1350
|
// ★R7:用户级硬性约束的条目级读写
|
|
@@ -1255,6 +1370,8 @@ window.__ModuleLoader__.load({
|
|
|
1255
1370
|
pyStatus: ROUTE_PREFIX + '/python-setup/status',
|
|
1256
1371
|
handoffState: ROUTE_PREFIX + '/handoff-state',
|
|
1257
1372
|
kanbanBoard: ROUTE_PREFIX + '/kanban-board',
|
|
1373
|
+
// ★v3.1.2:卡片全文按需取(看板载荷不再内联 full)
|
|
1374
|
+
kanbanCard: ROUTE_PREFIX + '/kanban-card',
|
|
1258
1375
|
handoffContinue: ROUTE_PREFIX + '/handoff-continue',
|
|
1259
1376
|
handoffPermission: ROUTE_PREFIX + '/handoff-permission',
|
|
1260
1377
|
autoContState: ROUTE_PREFIX + '/auto-continue-state',
|
|
@@ -1264,6 +1381,9 @@ window.__ModuleLoader__.load({
|
|
|
1264
1381
|
reviewFeedback: ROUTE_PREFIX + '/review-feedback',
|
|
1265
1382
|
memoryHub: ROUTE_PREFIX + '/memory-hub',
|
|
1266
1383
|
storageManage: ROUTE_PREFIX + '/storage-manage',
|
|
1384
|
+
migrateExport: ROUTE_PREFIX + '/migrate-export',
|
|
1385
|
+
migrateInspect: ROUTE_PREFIX + '/migrate-inspect',
|
|
1386
|
+
migrateImport: ROUTE_PREFIX + '/migrate-import',
|
|
1267
1387
|
}
|
|
1268
1388
|
function query(params) {
|
|
1269
1389
|
var search = new URLSearchParams()
|
|
@@ -1441,7 +1561,13 @@ window.__ModuleLoader__.load({
|
|
|
1441
1561
|
// ③ 错开值改走 --dam-dur-stagger(数值不变,只刻度化)。
|
|
1442
1562
|
'[data-dam-page] [data-dam-flow] > [data-dam-card] { grid-column: auto; margin-bottom: 0;',
|
|
1443
1563
|
' animation: dam-card-in var(--dam-dur-fast) var(--dam-ease-out) both; }',
|
|
1444
|
-
'@keyframes dam-
|
|
1564
|
+
'@keyframes dam-stat-num { from { opacity: 0; transform: translateY(4px); filter: blur(2px); } to { opacity: 1; transform: none; filter: none; } }',
|
|
1565
|
+
'@keyframes dam-stat-reveal { from { opacity: 0; filter: blur(2px); } to { opacity: 1; filter: none; } }',
|
|
1566
|
+
'@keyframes dam-stat-grow { from { transform: scaleX(.02); transform-origin: left center; } to { transform: none; } }',
|
|
1567
|
+
'@keyframes dam-stat-pop { from { opacity: 0; transform: scale(.92); } to { opacity: 1; transform: none; } }',
|
|
1568
|
+
'@keyframes dam-stat-pulse { 0%, 100% { opacity: .45; } 50% { opacity: .85; } }',
|
|
1569
|
+
'@keyframes dam-stat-arc { from { opacity: 0; } to { opacity: 1; } }',
|
|
1570
|
+
'@keyframes dam-card-in { from { opacity: 0; transform: translateY(8px) scale(.988); } to { opacity: 1; transform: none; } }',
|
|
1445
1571
|
'[data-dam-page] [data-dam-flow] > [data-dam-card]:nth-child(2) { animation-delay: calc(var(--dam-dur-stagger) * 1); }',
|
|
1446
1572
|
'[data-dam-page] [data-dam-flow] > [data-dam-card]:nth-child(3) { animation-delay: calc(var(--dam-dur-stagger) * 2); }',
|
|
1447
1573
|
'[data-dam-page] [data-dam-flow] > [data-dam-card]:nth-child(4) { animation-delay: calc(var(--dam-dur-stagger) * 3); }',
|
|
@@ -1864,7 +1990,9 @@ window.__ModuleLoader__.load({
|
|
|
1864
1990
|
* 正文 = 剩余行拼接(已清洗), 与标题不重复 —— 这正是「标题+正文分离」。
|
|
1865
1991
|
*/
|
|
1866
1992
|
function wbShape(c) {
|
|
1867
|
-
|
|
1993
|
+
// ★v3.1.2 性能:列表渲染(节点画布)用 preview 即可 —— 载荷已不内联 full,
|
|
1994
|
+
// 此处若仍读 c.full 会恒为空 ⇒ 标题/正文全丢。preview 是稳定来源。
|
|
1995
|
+
var rawFull = c.preview || c.full || ''
|
|
1868
1996
|
var ls = wbCleanLines(rawFull)
|
|
1869
1997
|
var head = ls.length ? ls[0] : ''
|
|
1870
1998
|
var title = head ? (head.length > 60 ? head.slice(0, 57) + '…' : head) : (c.title || c.short || '')
|
|
@@ -1904,6 +2032,34 @@ window.__ModuleLoader__.load({
|
|
|
1904
2032
|
return 'rgba(128,128,128,.75)'
|
|
1905
2033
|
}
|
|
1906
2034
|
|
|
2035
|
+
/**
|
|
2036
|
+
* ★v3.1.2 性能:卡片全文**按需取**。
|
|
2037
|
+
* 看板载荷不再内联 full(1178 卡 × ≤6000 字符、且 lanes+matrix 双份 ⇒ 约 1.7 MB 纯冗余),
|
|
2038
|
+
* 取而代之的是 fullLen;点「展开全文」/打开抽屉时才按 id 向 /kanban-card 单取一次并缓存。
|
|
2039
|
+
* 失败/未取到时**回退 preview**(绝不显示空白)。
|
|
2040
|
+
*/
|
|
2041
|
+
var wbFullCache = {} // id -> string(进程内缓存,避免反复取同一张)
|
|
2042
|
+
function useCardFull(card) {
|
|
2043
|
+
var p = useState(function () { return (card && wbFullCache[card.id]) || '' })
|
|
2044
|
+
var text = p[0], setText = p[1]
|
|
2045
|
+
useEffect(function () {
|
|
2046
|
+
var c = card || {}
|
|
2047
|
+
if (!c.id) return
|
|
2048
|
+
if (wbFullCache[c.id] !== undefined) { setText(wbFullCache[c.id]); return }
|
|
2049
|
+
// 载荷里若仍带 full(老版本/其它来源)直接用,不打请求
|
|
2050
|
+
if (typeof c.full === 'string') { wbFullCache[c.id] = c.full; setText(c.full); return }
|
|
2051
|
+
var alive = true
|
|
2052
|
+
apiGet(API.kanbanCard, { id: c.id, sessionId: currentSessionIdClient() }).then(function (r) {
|
|
2053
|
+
if (!alive) return
|
|
2054
|
+
var t = (r && r.ok && typeof r.full === 'string') ? r.full : ''
|
|
2055
|
+
wbFullCache[c.id] = t
|
|
2056
|
+
setText(t)
|
|
2057
|
+
}).catch(function () { if (alive) { wbFullCache[c.id] = ''; setText('') } })
|
|
2058
|
+
return function () { alive = false }
|
|
2059
|
+
}, [card && card.id])
|
|
2060
|
+
return text
|
|
2061
|
+
}
|
|
2062
|
+
|
|
1907
2063
|
/** 单张卡片: 标题 + 判据徽章 + 预览/全文 + 小节徽章 + 标签 + 来源。可点击展开。 */
|
|
1908
2064
|
function KanbanCard(props) {
|
|
1909
2065
|
var c = props.card || {}
|
|
@@ -1911,8 +2067,10 @@ window.__ModuleLoader__.load({
|
|
|
1911
2067
|
var expanded = !!props.expanded
|
|
1912
2068
|
var onToggle = props.onToggle
|
|
1913
2069
|
var tone = KANBAN_TONES[(c.badge && c.badge.tone) || 'unknown'] || KANBAN_TONES.unknown
|
|
1914
|
-
|
|
1915
|
-
var
|
|
2070
|
+
// ★v3.1.2:全文按需取(展开时才请求;未取到回退 preview)
|
|
2071
|
+
var fullText = useCardFull(expanded ? c : null)
|
|
2072
|
+
var body = expanded ? (fullText || c.full || c.preview || '') : (c.preview || '')
|
|
2073
|
+
var canExpand = (typeof c.fullLen === 'number' ? c.fullLen : (c.full || '').length) > (c.preview || '').length
|
|
1916
2074
|
return h('div', {
|
|
1917
2075
|
'data-dam-kcard': '',
|
|
1918
2076
|
title: c.source + (c.section ? ' §' + c.section : '') + (c.anchored ? ' · 有锚点' : ''),
|
|
@@ -1937,7 +2095,7 @@ window.__ModuleLoader__.load({
|
|
|
1937
2095
|
maxHeight: expanded ? 'none' : (7.2 * zoom) + 'em', overflow: 'hidden',
|
|
1938
2096
|
},
|
|
1939
2097
|
}, body) : null,
|
|
1940
|
-
(canExpand || (c.full || '').length > 300) ? h('button', {
|
|
2098
|
+
(canExpand || (typeof c.fullLen === 'number' ? c.fullLen : (c.full || '').length) > 300) ? h('button', {
|
|
1941
2099
|
'data-dam-btn': '', onClick: function (e) { e.stopPropagation(); if (onToggle) onToggle(c.id) },
|
|
1942
2100
|
style: { marginTop: '3px', padding: '1px 6px', fontSize: (10 * zoom) + 'px', opacity: .7 },
|
|
1943
2101
|
}, expanded ? (locale === 'zh' ? '收起' : 'collapse') : (locale === 'zh' ? '展开全文' : 'expand')) : null,
|
|
@@ -2357,7 +2515,7 @@ window.__ModuleLoader__.load({
|
|
|
2357
2515
|
? h('div', { key: 'sg', style: { display: 'flex', gap: '4px', flexWrap: 'wrap', marginBottom: '8px' } },
|
|
2358
2516
|
drawer.sectionTags.map(function (tg, i) { return h('span', { key: i, 'data-dam-chip': '', style: { fontSize: '10px', padding: '1px 5px', borderRadius: '3px', background: 'rgba(76,141,255,.18)' } }, tg) }))
|
|
2359
2517
|
: null,
|
|
2360
|
-
h('pre', { key: 'body', 'data-dam-kx-full': drawer.id, style: { whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'inherit', margin: 0, fontSize: '12px' } }, drawer.full || drawer.preview || ''),
|
|
2518
|
+
h('pre', { key: 'body', 'data-dam-kx-full': drawer.id, style: { whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontFamily: 'inherit', margin: 0, fontSize: '12px' } }, (useCardFull(drawer) || drawer.full || drawer.preview || '')),
|
|
2361
2519
|
].map(function (node, i) {
|
|
2362
2520
|
// 头部固定、正文滚动: 把内容各自包一层; 正文那层给 flex:1 + overflow:auto
|
|
2363
2521
|
if (i === 0) return h('div', { key: 'headwrap', style: { padding: '12px 14px 8px', flexShrink: 0 } }, node)
|
|
@@ -3655,6 +3813,102 @@ window.__ModuleLoader__.load({
|
|
|
3655
3813
|
var filePair = useState('')
|
|
3656
3814
|
var delFile = filePair[0]
|
|
3657
3815
|
var setDelFile = filePair[1]
|
|
3816
|
+
// 迁移搬包(P4 前端接线):导出/三步向导的全部状态。只用 useState,不引新依赖。
|
|
3817
|
+
var migOutPair = useState('')
|
|
3818
|
+
var migOut = migOutPair[0]
|
|
3819
|
+
var setMigOut = migOutPair[1]
|
|
3820
|
+
var migPackPair = useState('')
|
|
3821
|
+
var migPack = migPackPair[0]
|
|
3822
|
+
var setMigPack = migPackPair[1]
|
|
3823
|
+
var migOutPickingPair = useState(false)
|
|
3824
|
+
var migOutPicking = migOutPickingPair[0]
|
|
3825
|
+
var setMigOutPicking = migOutPickingPair[1]
|
|
3826
|
+
var migPackPickingPair = useState(false)
|
|
3827
|
+
var migPackPicking = migPackPickingPair[0]
|
|
3828
|
+
var setMigPackPicking = migPackPickingPair[1]
|
|
3829
|
+
var migBusyPair = useState('')
|
|
3830
|
+
var migBusy = migBusyPair[0]
|
|
3831
|
+
var setMigBusy = migBusyPair[1]
|
|
3832
|
+
var migErrStatePair = useState('')
|
|
3833
|
+
var migErr = migErrStatePair[0]
|
|
3834
|
+
var setMigErr = migErrStatePair[1]
|
|
3835
|
+
var migNotePair = useState('')
|
|
3836
|
+
var migNote = migNotePair[0]
|
|
3837
|
+
var setMigNote = migNotePair[1]
|
|
3838
|
+
var migPlanPair = useState(null)
|
|
3839
|
+
var migPlan = migPlanPair[0]
|
|
3840
|
+
var setMigPlan = migPlanPair[1]
|
|
3841
|
+
var migResultPair = useState(null)
|
|
3842
|
+
var migResult = migResultPair[0]
|
|
3843
|
+
var setMigResult = migResultPair[1]
|
|
3844
|
+
var migConflictPair = useState('keep')
|
|
3845
|
+
var migConflict = migConflictPair[0]
|
|
3846
|
+
var setMigConflict = migConflictPair[1]
|
|
3847
|
+
// ok===false 时把 error / errors / warnings 一并显示,绝不静默。
|
|
3848
|
+
function migFail(tag, j, e) {
|
|
3849
|
+
var parts = []
|
|
3850
|
+
if (e) parts.push(String(e && e.message ? e.message : e))
|
|
3851
|
+
if (j && j.error) parts.push(String(j.error))
|
|
3852
|
+
if (j && j.errors) parts.push(Array.isArray(j.errors) ? j.errors.join(' | ') : String(j.errors))
|
|
3853
|
+
if (j && j.warnings && j.warnings.length) parts.push((locale === 'zh' ? '警告: ' : 'warnings: ') + j.warnings.join(' | '))
|
|
3854
|
+
if (j && j.skipped && j.skipped.length) parts.push((locale === 'zh' ? '跳过: ' : 'skipped: ') + j.skipped.join(' | '))
|
|
3855
|
+
setMigErr(tag + ' ✗ ' + (parts.length ? parts.join(' · ') : (locale === 'zh' ? '未知错误' : 'unknown error')))
|
|
3856
|
+
}
|
|
3857
|
+
// 选择目录:沿用本文件的 pickDir 原生选择器形态;拿不到目录就提示手填输入框(不阻塞)。
|
|
3858
|
+
function migPickInto(setVal, setPicking) {
|
|
3859
|
+
setPicking(true); setMigErr('')
|
|
3860
|
+
apiPost(API.pickDir, {}).then(function (d) {
|
|
3861
|
+
setPicking(false)
|
|
3862
|
+
if (d && d.dir) { setVal(d.dir); setMigNote((locale === 'zh' ? '已选择目录: ' : 'folder picked: ') + d.dir) }
|
|
3863
|
+
else { setMigNote(locale === 'zh' ? '系统文件夹选择器不可用,请手动填写路径。' : 'Native folder picker unavailable — type the path manually.') }
|
|
3864
|
+
}).catch(function () {
|
|
3865
|
+
setPicking(false)
|
|
3866
|
+
setMigNote(locale === 'zh' ? '系统文件夹选择器不可用,请手动填写路径。' : 'Native folder picker unavailable — type the path manually.')
|
|
3867
|
+
})
|
|
3868
|
+
}
|
|
3869
|
+
function migExport(opts) {
|
|
3870
|
+
if (migBusy) return
|
|
3871
|
+
opts = opts || {}
|
|
3872
|
+
setMigBusy('export'); setMigErr(''); setMigNote('')
|
|
3873
|
+
// ★v3.1.2 修:此前只发 outPath,宿主拿不到工作区 ⇒ 恒返回 missing-ws(用户实测)。
|
|
3874
|
+
// 本文件既有 currentWs()(跟随 GUI 切换工作区),导出必须显式带上。
|
|
3875
|
+
apiPost(API.migrateExport, { ws: currentWs() || undefined, outPath: migOut || undefined }).then(function (j) {
|
|
3876
|
+
setMigBusy('')
|
|
3877
|
+
if (!j || j.ok === false) { migFail(t('migExport'), j, null); return }
|
|
3878
|
+
var s = j.stats || {}
|
|
3879
|
+
setMigNote(t('migExport') + ' ✓ ' + String(j.path || '') +
|
|
3880
|
+
' · ' + (locale === 'zh' ? '文件 ' : 'files ') + String(s.fileCount || 0) +
|
|
3881
|
+
' · ' + (locale === 'zh' ? '原始 ' : 'raw ') + String(s.bytes || 0) + ' B' +
|
|
3882
|
+
' · ' + (locale === 'zh' ? '压缩后 ' : 'packed ') + String(s.packedBytes || 0) + ' B' +
|
|
3883
|
+
(j.checksum ? ' · sha256 ' + String(j.checksum).slice(0, 12) : '') +
|
|
3884
|
+
(j.skipped && j.skipped.length ? ' · ' + (locale === 'zh' ? '跳过 ' : 'skipped ') + j.skipped.length : '') +
|
|
3885
|
+
(j.warnings && j.warnings.length ? ' · ' + (locale === 'zh' ? '警告 ' : 'warnings ') + j.warnings.length : ''))
|
|
3886
|
+
}).catch(function (e) { setMigBusy(''); migFail(t('migExport'), null, e) })
|
|
3887
|
+
}
|
|
3888
|
+
function migPreview() {
|
|
3889
|
+
if (migBusy) return
|
|
3890
|
+
if (!migPack) { setMigErr(t('migPickPack')); return }
|
|
3891
|
+
setMigBusy('preview'); setMigErr(''); setMigNote(''); setMigPlan(null); setMigResult(null)
|
|
3892
|
+
apiPost(API.migrateInspect, { packPath: migPack, targetWs: currentWs() || undefined }).then(function (j) {
|
|
3893
|
+
setMigBusy('')
|
|
3894
|
+
if (!j || j.ok === false) { migFail(t('migPreview'), j, null); return }
|
|
3895
|
+
var p = j.plan || {}
|
|
3896
|
+
setMigPlan(p)
|
|
3897
|
+
setMigNote(t('migPreview') + ' ✓ ' + (p.pathChanged ? t('migPathChanged') : t('migSamePath')) +
|
|
3898
|
+
(j.sourceMissing ? (locale === 'zh' ? ' · ⚠ 源工作区已不存在' : ' · warning: source workspace missing') : ''))
|
|
3899
|
+
}).catch(function (e) { setMigBusy(''); migFail(t('migPreview'), null, e) })
|
|
3900
|
+
}
|
|
3901
|
+
function migApply() {
|
|
3902
|
+
if (migBusy) return
|
|
3903
|
+
if (!migPack) { setMigErr(t('migPickPack')); return }
|
|
3904
|
+
setMigBusy('apply'); setMigErr(''); setMigNote('')
|
|
3905
|
+
apiPost(API.migrateImport, { packPath: migPack, targetWs: currentWs() || undefined, onConflict: migConflict }).then(function (j) {
|
|
3906
|
+
setMigBusy('')
|
|
3907
|
+
if (!j || j.ok === false) { migFail(t('migApply'), j, null); return }
|
|
3908
|
+
setMigResult(j)
|
|
3909
|
+
setMigNote(t('migApply') + ' ✓ ' + String(j.targetWs || ''))
|
|
3910
|
+
}).catch(function (e) { setMigBusy(''); migFail(t('migApply'), null, e) })
|
|
3911
|
+
}
|
|
3658
3912
|
useEffect(function () {
|
|
3659
3913
|
var alive = true
|
|
3660
3914
|
fetch(API.storageManage).then(function (r) { return r.json() }).then(function (j) {
|
|
@@ -3671,7 +3925,7 @@ window.__ModuleLoader__.load({
|
|
|
3671
3925
|
.then(function (j) {
|
|
3672
3926
|
var reason = (j && (j.reason || (j.ok === false ? 'rejected' : 'ok'))) || 'done'
|
|
3673
3927
|
if (j && j.ok === false && j.reason === 'no-doc-store') {
|
|
3674
|
-
reason = locale === 'zh' ? '记忆锚定索引未启用:请在 设置 →
|
|
3928
|
+
reason = locale === 'zh' ? '记忆锚定索引未启用:请在 设置 → 语义记忆总开关 开启「记忆锚定索引」后重试;或忽略此提示(不影响记忆读写与检索)' : 'anchor index disabled: enable "Memory anchor index" under Settings → Semantic engine, or ignore (read/write/recall unaffected)'
|
|
3675
3929
|
}
|
|
3676
3930
|
setMsg(action + ': ' + reason)
|
|
3677
3931
|
if (onDone) onDone(j)
|
|
@@ -3687,7 +3941,7 @@ window.__ModuleLoader__.load({
|
|
|
3687
3941
|
var rows = []
|
|
3688
3942
|
if (data.indexEnabled === false) {
|
|
3689
3943
|
rows.push(h('div', { 'data-dam-hint': '', style: { marginTop: '6px', color: 'var(--dsw-alias-state-warn-primary, #e8c584)' } },
|
|
3690
|
-
locale === 'zh' ? '⚠ 记忆锚定索引未启用:语料健康不做 sidecar 比对(无 stale 可修)。如需「修复 stale / 删除记忆」,请到 设置 →
|
|
3944
|
+
locale === 'zh' ? '⚠ 记忆锚定索引未启用:语料健康不做 sidecar 比对(无 stale 可修)。如需「修复 stale / 删除记忆」,请到 设置 → 语义记忆总开关 开启「记忆锚定索引」。此提示不影响记忆读写与检索。' : '⚠ Memory anchor index disabled: corpus health performs no sidecar comparison (nothing stale). To use repair/delete, enable "Memory anchor index" under Settings → Semantic engine. Read/write/recall are unaffected.'))
|
|
3691
3945
|
}
|
|
3692
3946
|
rows.push(h('div', { 'data-dam-hint': '', style: { marginTop: '8px' } }, t('storageScanHint')))
|
|
3693
3947
|
rows.push(h(Card, { title: (locale === 'zh' ? '语料健康' : 'Corpus health') + ' (' + counts.ok + '/' + counts.total + ' ok)' },
|
|
@@ -3721,6 +3975,58 @@ window.__ModuleLoader__.load({
|
|
|
3721
3975
|
rows.push(h('div', { 'data-dam-hint': '', style: { marginTop: '12px', opacity: .7 } },
|
|
3722
3976
|
(locale === 'zh' ? '最近动作: ' : 'recent: ') + audit.map(function (a) { return a.action }).join(', ')))
|
|
3723
3977
|
}
|
|
3978
|
+
// 迁移搬包(P4):StorageTab 的「迁移搬包」卡片 —— 导出 + 导入三步向导(选包 → 预览差异 → 确认导入)。
|
|
3979
|
+
// 版式:只用 flexWrap / 百分比 / calc 字号,长路径 wordBreak:break-all ⇒ 窄浮层与宽页签都不横向溢出。
|
|
3980
|
+
var migOutPlaceholder = locale === 'zh' ? '输出目录(留空 = 默认导出目录)' : 'output dir (blank = default export dir)'
|
|
3981
|
+
var migRows = []
|
|
3982
|
+
if (migErr) migRows.push(h('div', { 'data-dam-error': '', key: 'merr', style: { wordBreak: 'break-all' } }, migErr))
|
|
3983
|
+
if (migNote) migRows.push(h('div', { 'data-dam-hint': '', key: 'mnote', style: { wordBreak: 'break-all' } }, migNote))
|
|
3984
|
+
migRows.push(h(Card, { title: t('migTitle') },
|
|
3985
|
+
h('div', { 'data-dam-hint': '', style: { marginBottom: '6px', wordBreak: 'break-all' } }, t('migHint')),
|
|
3986
|
+
h('div', { 'data-dam-row': '' },
|
|
3987
|
+
h('input', { 'data-dam-input': '', value: migOut, placeholder: migOutPlaceholder,
|
|
3988
|
+
onChange: function (e) { setMigOut(e.target.value) }, style: { flex: '1 1 100%' } }),
|
|
3989
|
+
h('button', { 'data-dam-btn': '', disabled: !!migBusy, onClick: function () { migPickInto(setMigOut, setMigOutPicking) } },
|
|
3990
|
+
migOutPicking ? t('loading') : (locale === 'zh' ? '选择目录' : 'pick folder')),
|
|
3991
|
+
h('button', { 'data-dam-btn': '', disabled: !!migBusy, style: { opacity: migBusy ? .5 : 1 },
|
|
3992
|
+
onClick: function () { migExport() } }, migBusy === 'export' ? t('saving') : t('migExport'))),
|
|
3993
|
+
h('div', { 'data-dam-row': '', style: { marginTop: '6px' } },
|
|
3994
|
+
h('input', { 'data-dam-input': '', value: migPack, placeholder: t('migPickPack'),
|
|
3995
|
+
onChange: function (e) { setMigPack(e.target.value) }, style: { flex: '1 1 100%' } }),
|
|
3996
|
+
h('button', { 'data-dam-btn': '', disabled: !!migBusy, onClick: function () { migPickInto(setMigPack, setMigPackPicking) } },
|
|
3997
|
+
migPackPicking ? t('loading') : t('migPickPack')),
|
|
3998
|
+
h('button', { 'data-dam-btn': '', disabled: !!migBusy, style: { opacity: migBusy ? .5 : 1 },
|
|
3999
|
+
onClick: migPreview }, migBusy === 'preview' ? t('loading') : t('migPreview'))),
|
|
4000
|
+
migPlan ? h('div', { style: { wordBreak: 'break-all', fontSize: 'calc(11.5px * var(--dam-scale))' } },
|
|
4001
|
+
h('div', null, migPlan.pathChanged ? t('migPathChanged') : t('migSamePath')),
|
|
4002
|
+
h('div', { 'data-dam-hint': '' }, (locale === 'zh' ? '路径: ' : 'path: ') + String(migPlan.fromPath || '') + ' → ' + String(migPlan.toPath || '')),
|
|
4003
|
+
h('div', { 'data-dam-hint': '' }, (locale === 'zh' ? '工作区标识: ' : 'slug: ') + String(migPlan.fromSlug || '') + ' → ' + String(migPlan.toSlug || '')),
|
|
4004
|
+
h('div', null, (locale === 'zh' ? '新增 ' : 'additions ') + String((migPlan.additions || []).length) +
|
|
4005
|
+
' · ' + (locale === 'zh' ? '覆盖 ' : 'overwrites ') + String((migPlan.overwrites || []).length) +
|
|
4006
|
+
' · ' + (locale === 'zh' ? '将写入 ' : 'will write ') + String((migPlan.stats && migPlan.stats.willWrite) || 0) +
|
|
4007
|
+
' · ' + (locale === 'zh' ? '重写命中 ' : 'rewrite hits ') + String((migPlan.rewrite && migPlan.rewrite.totalHits) || 0) +
|
|
4008
|
+
(locale === 'zh' ? ' 处 / 涉及文件 ' : ' across files ') + String((migPlan.rewrite && migPlan.rewrite.fileCount) || 0)),
|
|
4009
|
+
(migPlan.additions || []).length ? h('div', { 'data-dam-hint': '' },
|
|
4010
|
+
(locale === 'zh' ? '新增(前 10): ' : 'additions (top 10): ') + (migPlan.additions || []).slice(0, 10).map(function (a) { return String(a && a.path) + '(' + String((a && a.bytes) || 0) + 'B)' }).join(' , ')) : null,
|
|
4011
|
+
(migPlan.overwrites || []).length ? h('div', { 'data-dam-hint': '' },
|
|
4012
|
+
(locale === 'zh' ? '覆盖: ' : 'overwrites: ') + (migPlan.overwrites || []).map(function (o) { return String(o && o.path) + ' ' + String((o && o.oldBytes) || 0) + 'B→' + String((o && o.newBytes) || 0) + 'B' }).join(' , ')) : null,
|
|
4013
|
+
(migPlan.rewrite && (migPlan.rewrite.files || []).length) ? h('div', { 'data-dam-hint': '' },
|
|
4014
|
+
(locale === 'zh' ? '重写文件(前 10): ' : 'rewrite files (top 10): ') + (migPlan.rewrite.files || []).slice(0, 10).map(function (f) { return String(f && f.path) + '(' + String((f && f.hits) || 0) + ')' }).join(' , ')) : null,
|
|
4015
|
+
(migPlan.warnings && migPlan.warnings.length) ? h('div', { 'data-dam-hint': '' }, (locale === 'zh' ? '警告: ' : 'warnings: ') + migPlan.warnings.join(' | ')) : null,
|
|
4016
|
+
h('div', { 'data-dam-row': '', style: { marginTop: '6px' } },
|
|
4017
|
+
h('button', { 'data-dam-btn': '', style: { opacity: migConflict === 'keep' ? 1 : .5 }, onClick: function () { setMigConflict('keep') } }, (migConflict === 'keep' ? '● ' : '○ ') + t('migConflictKeep')),
|
|
4018
|
+
h('button', { 'data-dam-btn': '', style: { opacity: migConflict === 'overwrite' ? 1 : .5 }, onClick: function () { setMigConflict('overwrite') } }, (migConflict === 'overwrite' ? '● ' : '○ ') + t('migConflictOverwrite')),
|
|
4019
|
+
h('button', { 'data-dam-btn': '', style: { opacity: migConflict === 'rename' ? 1 : .5 }, onClick: function () { setMigConflict('rename') } }, (migConflict === 'rename' ? '● ' : '○ ') + t('migConflictRename'))),
|
|
4020
|
+
h('button', { 'data-dam-btn': '', disabled: !!migBusy, style: { marginTop: '6px', opacity: migBusy ? .5 : 1 }, onClick: migApply },
|
|
4021
|
+
migBusy === 'apply' ? t('saving') : t('migApply'))) : null,
|
|
4022
|
+
migResult ? h('div', { style: { wordBreak: 'break-all', fontSize: 'calc(11.5px * var(--dam-scale))' } },
|
|
4023
|
+
h('div', null, t('migWritten') + ': ' + String(migResult.written || 0) +
|
|
4024
|
+
((migResult.writtenFiles || []).length ? ' (' + migResult.writtenFiles.slice(0, 10).join(' , ') + ')' : '')),
|
|
4025
|
+
h('div', { 'data-dam-hint': '' }, t('migBackup') + ': ' + String(migResult.backup || '')),
|
|
4026
|
+
h('div', { 'data-dam-hint': '' }, (locale === 'zh' ? '冲突策略: ' : 'conflict: ') + String(migResult.onConflict || migConflict) +
|
|
4027
|
+
' · ' + (locale === 'zh' ? '目标工作区: ' : 'target: ') + String(migResult.targetWs || '')),
|
|
4028
|
+
h('div', null, String(migResult.summaryAction || '') + (migResult.summaryError ? ' ✗ ' + String(migResult.summaryError) : ''))) : null))
|
|
4029
|
+
rows.push(h('div', { 'data-dam-flow': '' }, migRows))
|
|
3724
4030
|
// 大屏分列:卡片流页签(见 CSS [data-dam-flow]);非卡片子元素仍整行。
|
|
3725
4031
|
return h('div', { 'data-dam-flow': '' }, rows)
|
|
3726
4032
|
}
|
|
@@ -4344,7 +4650,235 @@ window.__ModuleLoader__.load({
|
|
|
4344
4650
|
}))))
|
|
4345
4651
|
)
|
|
4346
4652
|
}
|
|
4347
|
-
|
|
4653
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4654
|
+
// 统计页签(2026-09-22)—— 召回统计的可视化。
|
|
4655
|
+
//
|
|
4656
|
+
// 为什么单独一页:用户要「看哪个召回得更多、哪个更重要」,这是一眼看的活,
|
|
4657
|
+
// 不适合塞进已有 12 个页签的任意一个里(会把那一页的语义冲淡)。
|
|
4658
|
+
//
|
|
4659
|
+
// 立场:纯只读展示 + 一个清零按钮。**不改召回排序**(加权是后续独立批次,
|
|
4660
|
+
// 等这份数据证明口径可信之后再做)——故本页不做任何排序相关的写操作。
|
|
4661
|
+
//
|
|
4662
|
+
// 视觉:复用面板既有 --dam-* 令牌与 [data-dam-card] 入场,不引第三方图表库
|
|
4663
|
+
// (零构建环境下引库=多一个包+多一套主题;四种图手绘 SVG 足够表达)。
|
|
4664
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
4665
|
+
|
|
4666
|
+
/** 环形图:按层占比。用 stroke-dasharray 画弧,避免 path 三角计算。 */
|
|
4667
|
+
function DamDonut(props) {
|
|
4668
|
+
var items = props.items || []
|
|
4669
|
+
var total = items.reduce(function (n, it) { return n + (it.count || 0) }, 0)
|
|
4670
|
+
var R = 34, C = 2 * Math.PI * R
|
|
4671
|
+
var PALETTE = ['#5b8def', '#3fa96a', '#d4a94f', '#c061c0', '#4aa8b8', '#8a8f98']
|
|
4672
|
+
var acc = 0
|
|
4673
|
+
var arcs = items.slice(0, 6).map(function (it, i) {
|
|
4674
|
+
var frac = total ? (it.count || 0) / total : 0
|
|
4675
|
+
var seg = h('circle', {
|
|
4676
|
+
key: 'a' + i, cx: 44, cy: 44, r: R, fill: 'none',
|
|
4677
|
+
stroke: PALETTE[i % PALETTE.length], strokeWidth: 12,
|
|
4678
|
+
strokeDasharray: (frac * C) + ' ' + C,
|
|
4679
|
+
strokeDashoffset: -acc * C,
|
|
4680
|
+
transform: 'rotate(-90 44 44)',
|
|
4681
|
+
style: { animation: 'dam-stat-arc var(--dam-dur-slow) var(--dam-ease-out) both' },
|
|
4682
|
+
})
|
|
4683
|
+
acc += frac
|
|
4684
|
+
return seg
|
|
4685
|
+
})
|
|
4686
|
+
return h('div', { 'data-dam-stat-donut': '', style: { display: 'flex', gap: '14px', alignItems: 'center', flexWrap: 'wrap' } },
|
|
4687
|
+
h('svg', { width: 88, height: 88, viewBox: '0 0 88 88', role: 'img', 'aria-label': props.label }, arcs),
|
|
4688
|
+
h('div', { style: { display: 'flex', flexDirection: 'column', gap: '4px', minWidth: 0 } },
|
|
4689
|
+
items.slice(0, 6).map(function (it, i) {
|
|
4690
|
+
var pct = total ? Math.round(((it.count || 0) / total) * 100) : 0
|
|
4691
|
+
return h('div', { key: 'l' + i, style: { display: 'flex', alignItems: 'center', gap: '6px', fontSize: '12px' } },
|
|
4692
|
+
h('span', { style: { width: 8, height: 8, borderRadius: 8, background: PALETTE[i % PALETTE.length], flex: '0 0 auto' } }),
|
|
4693
|
+
h('span', null, it.key + ' · ' + it.count + ' (' + pct + '%)'))
|
|
4694
|
+
})))
|
|
4695
|
+
}
|
|
4696
|
+
|
|
4697
|
+
/** 横向柱状:召回最多的条目。条宽用百分比,随容器自适应。 */
|
|
4698
|
+
function DamBars(props) {
|
|
4699
|
+
var items = props.items || []
|
|
4700
|
+
var max = items.reduce(function (n, it) { return Math.max(n, it.count || 0) }, 0) || 1
|
|
4701
|
+
return h('div', { 'data-dam-stat-bars': '', style: { display: 'flex', flexDirection: 'column', gap: '6px' } },
|
|
4702
|
+
items.map(function (it, i) {
|
|
4703
|
+
var pct = Math.max(2, Math.round(((it.count || 0) / max) * 100))
|
|
4704
|
+
return h('div', { key: 'b' + i, style: { display: 'flex', alignItems: 'center', gap: '8px', fontSize: '12px' } },
|
|
4705
|
+
h('code', { style: { flex: '0 0 auto', opacity: .75, fontSize: '11px' } }, String(it.id || '').slice(0, 12)),
|
|
4706
|
+
h('div', { style: { flex: '1 1 auto', height: 8, borderRadius: 6, background: 'color-mix(in srgb, currentColor 10%, transparent)', overflow: 'hidden' } },
|
|
4707
|
+
h('div', {
|
|
4708
|
+
style: {
|
|
4709
|
+
height: '100%', width: pct + '%', borderRadius: 6,
|
|
4710
|
+
background: 'linear-gradient(90deg, #5b8def, #4aa8b8)',
|
|
4711
|
+
animation: 'dam-stat-grow var(--dam-dur-slow) var(--dam-ease-out) both',
|
|
4712
|
+
},
|
|
4713
|
+
})),
|
|
4714
|
+
h('span', { style: { flex: '0 0 auto', fontVariantNumeric: 'tabular-nums' } }, '×' + it.count),
|
|
4715
|
+
it.layer ? h('span', { style: { flex: '0 0 auto', opacity: .6, fontSize: '11px' } }, it.layer) : null)
|
|
4716
|
+
}))
|
|
4717
|
+
}
|
|
4718
|
+
|
|
4719
|
+
/** 折线 + 面积:按天走势。 */
|
|
4720
|
+
function DamSpark(props) {
|
|
4721
|
+
var days = props.items || []
|
|
4722
|
+
var W = 320, H = 68, P = 4
|
|
4723
|
+
if (!days.length) return h('div', { style: { opacity: .6, fontSize: '12px' } }, props.empty)
|
|
4724
|
+
var max = days.reduce(function (n, d) { return Math.max(n, d.count || 0) }, 0) || 1
|
|
4725
|
+
var step = days.length > 1 ? (W - P * 2) / (days.length - 1) : 0
|
|
4726
|
+
var pts = days.map(function (d, i) {
|
|
4727
|
+
var x = P + i * step
|
|
4728
|
+
var y = H - P - ((d.count || 0) / max) * (H - P * 2)
|
|
4729
|
+
return [x, y]
|
|
4730
|
+
})
|
|
4731
|
+
var line = pts.map(function (p, i) { return (i ? 'L' : 'M') + p[0].toFixed(1) + ' ' + p[1].toFixed(1) }).join(' ')
|
|
4732
|
+
var area = line + ' L' + (P + (days.length - 1) * step).toFixed(1) + ' ' + (H - P) + ' L' + P + ' ' + (H - P) + ' Z'
|
|
4733
|
+
return h('svg', {
|
|
4734
|
+
'data-dam-stat-spark': '', width: '100%', height: H, viewBox: '0 0 ' + W + ' ' + H,
|
|
4735
|
+
preserveAspectRatio: 'none', role: 'img', 'aria-label': props.label,
|
|
4736
|
+
style: { animation: 'dam-stat-reveal var(--dam-dur-slow) var(--dam-ease-inout) both' },
|
|
4737
|
+
},
|
|
4738
|
+
h('path', { d: area, fill: 'color-mix(in srgb, #5b8def 22%, transparent)', stroke: 'none' }),
|
|
4739
|
+
h('path', { d: line, fill: 'none', stroke: '#5b8def', strokeWidth: 2, strokeLinejoin: 'round', vectorEffect: 'non-scaling-stroke' }))
|
|
4740
|
+
}
|
|
4741
|
+
|
|
4742
|
+
/** 热力格:天 × 次数。一眼看出「哪天被想起来的次数多」。 */
|
|
4743
|
+
function DamHeat(props) {
|
|
4744
|
+
var days = (props.items || []).slice(-42)
|
|
4745
|
+
if (!days.length) return h('div', { style: { opacity: .6, fontSize: '12px' } }, props.empty)
|
|
4746
|
+
var max = days.reduce(function (n, d) { return Math.max(n, d.count || 0) }, 0) || 1
|
|
4747
|
+
return h('div', { 'data-dam-stat-heat': '', style: { display: 'flex', flexWrap: 'wrap', gap: 3 } },
|
|
4748
|
+
days.map(function (d, i) {
|
|
4749
|
+
var t = (d.count || 0) / max
|
|
4750
|
+
return h('div', {
|
|
4751
|
+
key: 'h' + i, title: d.day + ' · ' + d.count,
|
|
4752
|
+
style: {
|
|
4753
|
+
width: 12, height: 12, borderRadius: 3,
|
|
4754
|
+
background: 'color-mix(in srgb, #5b8def ' + Math.round(18 + t * 82) + '%, transparent)',
|
|
4755
|
+
animation: 'dam-stat-pop var(--dam-dur-fast) var(--dam-ease-out) both',
|
|
4756
|
+
animationDelay: Math.min(i * 12, 240) + 'ms',
|
|
4757
|
+
},
|
|
4758
|
+
})
|
|
4759
|
+
}))
|
|
4760
|
+
}
|
|
4761
|
+
|
|
4762
|
+
/** 数字卡:用途=「数字弹入」⇒ --dam-dur-slow + --dam-ease-out + 2px 模糊(skill number-pop-in 判据)。 */
|
|
4763
|
+
function DamStat(props) {
|
|
4764
|
+
return h('div', { 'data-dam-stat': '', style: { display: 'flex', flexDirection: 'column', gap: 2, minWidth: 92 } },
|
|
4765
|
+
h('div', {
|
|
4766
|
+
style: {
|
|
4767
|
+
fontSize: '22px', fontWeight: 600, fontVariantNumeric: 'tabular-nums',
|
|
4768
|
+
animation: 'dam-stat-num var(--dam-dur-slow) var(--dam-ease-out) both',
|
|
4769
|
+
},
|
|
4770
|
+
}, String(props.value)),
|
|
4771
|
+
h('div', { style: { fontSize: '11px', opacity: .7 } }, props.label))
|
|
4772
|
+
}
|
|
4773
|
+
|
|
4774
|
+
function StatsTab() {
|
|
4775
|
+
var s = useState(null)
|
|
4776
|
+
var data = s[0], setData = s[1]
|
|
4777
|
+
var e = useState('')
|
|
4778
|
+
var err = e[0], setErr = e[1]
|
|
4779
|
+
var r = useState(0)
|
|
4780
|
+
var tick = r[0], setTick = r[1]
|
|
4781
|
+
|
|
4782
|
+
useEffect(function () {
|
|
4783
|
+
var alive = true
|
|
4784
|
+
fetch(API.recallStats)
|
|
4785
|
+
.then(function (res) { return res.json() })
|
|
4786
|
+
.then(function (j) { if (alive) setData(j) })
|
|
4787
|
+
.catch(function (ex) { if (alive) setErr(String((ex && ex.message) || ex)) })
|
|
4788
|
+
return function () { alive = false }
|
|
4789
|
+
}, [tick])
|
|
4790
|
+
|
|
4791
|
+
function reset() {
|
|
4792
|
+
fetch(API.recallStats + '?reset=1', { method: 'POST' })
|
|
4793
|
+
.then(function () { setTick(function (n) { return n + 1 }) })
|
|
4794
|
+
.catch(function (ex) { setErr(String((ex && ex.message) || ex)) })
|
|
4795
|
+
}
|
|
4796
|
+
|
|
4797
|
+
if (err) return h(Card, { title: t('statsTitle') }, h('div', { style: { opacity: .8 } }, err))
|
|
4798
|
+
// 骨架态:用途 =「骨架屏内容揭示」⇒ 脉冲用 --dam-dur-slow(与 skill 同用途判据一致)
|
|
4799
|
+
if (!data) return h(Card, { title: t('statsTitle') },
|
|
4800
|
+
h('div', { 'data-dam-stat-skeleton': '', style: { display: 'flex', flexDirection: 'column', gap: 8 } },
|
|
4801
|
+
[0, 1, 2].map(function (i) {
|
|
4802
|
+
return h('div', {
|
|
4803
|
+
key: 'sk' + i,
|
|
4804
|
+
style: {
|
|
4805
|
+
height: 14, borderRadius: 7, width: (70 - i * 12) + '%',
|
|
4806
|
+
background: 'color-mix(in srgb, currentColor 11%, transparent)',
|
|
4807
|
+
animation: 'dam-stat-pulse var(--dam-dur-slow) var(--dam-ease-linear) infinite',
|
|
4808
|
+
},
|
|
4809
|
+
})
|
|
4810
|
+
})))
|
|
4811
|
+
if (!data.enabled) return h(Card, { title: t('statsTitle') }, h('div', { style: { opacity: .75 } }, t('statsEmpty')))
|
|
4812
|
+
|
|
4813
|
+
var chs = data.channels || {}
|
|
4814
|
+
var chIds = data.channelIds || []
|
|
4815
|
+
// 三条通路各自一格;标签用中文/英文 i18n 键,绝不硬编码文案
|
|
4816
|
+
var META = {
|
|
4817
|
+
model: { name: t('statsChModel'), hint: t('statsChModelHint') },
|
|
4818
|
+
inject: { name: t('statsChInject'), hint: t('statsChInjectHint') },
|
|
4819
|
+
shadow: { name: t('statsChShadow'), hint: t('statsChShadowHint') },
|
|
4820
|
+
}
|
|
4821
|
+
var pick = function (id) { return chs[id] || {} }
|
|
4822
|
+
var model = pick('model'), inject = pick('inject'), shadow = pick('shadow')
|
|
4823
|
+
var totalEvents = chIds.reduce(function (n, id) { return n + (pick(id).events || 0) }, 0)
|
|
4824
|
+
var emptyAll = !totalEvents
|
|
4825
|
+
var since = data.since ? new Date(data.since).toLocaleString() : t('statsNever')
|
|
4826
|
+
|
|
4827
|
+
// 单条通路卡片:数字行 + 可选图表。**图表都走既有 Dam* 组件**(零新依赖)。
|
|
4828
|
+
function chCard(ch, id, extra) {
|
|
4829
|
+
var m = META[id] || {}
|
|
4830
|
+
var body = [
|
|
4831
|
+
h('div', { style: { opacity: .72, fontSize: '12px', marginBottom: 2 } }, m.hint),
|
|
4832
|
+
h('div', { style: { display: 'flex', gap: 18, flexWrap: 'wrap', marginTop: 4 } },
|
|
4833
|
+
h(DamStat, { value: ch.events || 0, label: t('statsEvents') }),
|
|
4834
|
+
h(DamStat, { value: ch.hits || 0, label: t('statsTotalHits') }),
|
|
4835
|
+
h(DamStat, { value: ch.distinct || 0, label: t('statsDistinct') }),
|
|
4836
|
+
h(DamStat, { value: ch.zeroHit || 0, label: t('statsZeroHit') })),
|
|
4837
|
+
]
|
|
4838
|
+
return h(Card, { title: m.name }, h('div', { style: { display: 'flex', flexDirection: 'column', gap: 12 } },
|
|
4839
|
+
extra ? extra(ch) : null,
|
|
4840
|
+
body,
|
|
4841
|
+
(ch.top && ch.top.length) ? h('div', { style: { marginTop: 4 } }, h(DamBars, { items: ch.top.slice(0, 12) }))
|
|
4842
|
+
: h('div', { style: { opacity: .5, fontSize: '12px' } }, t('statsNoInject'))))
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4845
|
+
return h('div', null,
|
|
4846
|
+
h(Card, { title: t('statsTitle') },
|
|
4847
|
+
h('div', { style: { display: 'flex', flexDirection: 'column', gap: 12 } },
|
|
4848
|
+
h('div', { style: { opacity: .75, fontSize: '12px' } }, t('statsSubtitle')),
|
|
4849
|
+
emptyAll ? h('div', { style: { opacity: .7 } }, t('statsEmpty')) : null,
|
|
4850
|
+
// 总览:三条通路各占多少(饼图)—— 一眼看出"哪一条在动"
|
|
4851
|
+
emptyAll ? null : h(DamDonut, {
|
|
4852
|
+
items: chIds.map(function (id) { return { id: (META[id] || {}).name || id, count: pick(id).events || 0 } }),
|
|
4853
|
+
label: t('statsChOverview'),
|
|
4854
|
+
}),
|
|
4855
|
+
h('div', { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: 10 } },
|
|
4856
|
+
h('div', { style: { fontSize: '11px', opacity: .6 } }, t('statsSince') + ': ' + since),
|
|
4857
|
+
h('button', {
|
|
4858
|
+
type: 'button', onClick: reset,
|
|
4859
|
+
style: { fontSize: '12px', padding: '4px 10px', borderRadius: 8, cursor: 'pointer' },
|
|
4860
|
+
}, t('statsReset'))))),
|
|
4861
|
+
// ① 模型主动检索:高信号。附按层分布 + 按天走势
|
|
4862
|
+
chCard(model, 'model', function (ch) {
|
|
4863
|
+
return h('div', { style: { display: 'flex', flexDirection: 'column', gap: 10 } },
|
|
4864
|
+
(ch.byLayer && ch.byLayer.length) ? h(DamDonut, { items: ch.byLayer, label: t('statsByLayer') }) : null,
|
|
4865
|
+
(ch.byDay && ch.byDay.length) ? h(DamSpark, { items: ch.byDay, label: t('statsByDay'), empty: t('statsEmpty') }) : null,
|
|
4866
|
+
(ch.byDay && ch.byDay.length) ? h(DamHeat, { items: ch.byDay, empty: t('statsEmpty') }) : null)
|
|
4867
|
+
}),
|
|
4868
|
+
// ② 每轮注入:成本视角。**条长按字符数**(回答"哪一段最占预算")
|
|
4869
|
+
chCard(inject, 'inject', function (ch) {
|
|
4870
|
+
var segs = (ch.top || []).map(function (it) { return { id: it.id, count: it.score || 0, n: it.count || 0 } })
|
|
4871
|
+
segs.sort(function (a, b) { return b.count - a.count })
|
|
4872
|
+
if (!segs.length) return null
|
|
4873
|
+
return h('div', { style: { display: 'flex', flexDirection: 'column', gap: 6 } },
|
|
4874
|
+
h('div', { style: { opacity: .7, fontSize: '12px' } }, t('statsSegChars')),
|
|
4875
|
+
h(DamBars, { items: segs.slice(0, 14) }))
|
|
4876
|
+
}),
|
|
4877
|
+
// ③ 主动唤起:系统判断质量。没开 shadow 时该卡自然为 0,不谎报
|
|
4878
|
+
chCard(shadow, 'shadow', null))
|
|
4879
|
+
}
|
|
4880
|
+
|
|
4881
|
+
function WorkspaceTab() {
|
|
4348
4882
|
var dataPair = useState(null)
|
|
4349
4883
|
var data = dataPair[0]
|
|
4350
4884
|
var setData = dataPair[1]
|
|
@@ -4834,10 +5368,11 @@ window.__ModuleLoader__.load({
|
|
|
4834
5368
|
if (tab === 'connect') return h(ConnectTab)
|
|
4835
5369
|
if (tab === 'calendar') return h(CalendarTab)
|
|
4836
5370
|
if (tab === 'workspaces') return h(WorkspaceTab)
|
|
5371
|
+
if (tab === 'stats') return h(StatsTab)
|
|
4837
5372
|
return h(SearchTab)
|
|
4838
5373
|
}
|
|
4839
5374
|
function MEMORY_TABS() {
|
|
4840
|
-
return [['overview', t('overview')], ['logs', t('logs')], ['refine', t('refineTab')], ['hub', t('hubTab')], ['storage', t('storageTab')], ['notes', t('notes')], ['plan', t('planTab')], ['reflections', t('reflections')], ['connect', t('connect')], ['calendar', t('calendar')], ['search', t('search')], ['workspaces', t('workspaces')]]
|
|
5375
|
+
return [['overview', t('overview')], ['logs', t('logs')], ['refine', t('refineTab')], ['hub', t('hubTab')], ['storage', t('storageTab')], ['notes', t('notes')], ['plan', t('planTab')], ['reflections', t('reflections')], ['connect', t('connect')], ['calendar', t('calendar')], ['search', t('search')], ['workspaces', t('workspaces')], ['stats', t('statsTab')]]
|
|
4841
5376
|
}
|
|
4842
5377
|
|
|
4843
5378
|
// 「记忆」会话页(2026-09-21 二次修正) —— 注册在 conversation.view 槽位,与「对话轨迹」「上下文」「白板看板」并列。
|
|
@@ -5186,6 +5721,23 @@ window.__ModuleLoader__.load({
|
|
|
5186
5721
|
kv(t('kvWs'), currentWs() || t('kvWsUnknown'), false),
|
|
5187
5722
|
kv(t('kvApi'), probes ? Object.keys(probes).map(function (k) { return k + '=' + (probes[k].status !== undefined ? probes[k].status : probes[k].error) }).join(' ') : '…', apiBad),
|
|
5188
5723
|
hubIo ? kv(t('dbgHubIo'), hubIoText, hubIoWarn) : null,
|
|
5724
|
+
// ★2026-09-22(用户拍板):诊断日志只在**这里**看,不再往控制台刷。
|
|
5725
|
+
// 路径可选中复制;含「崩溃时附这个文件」的人话提示。只读投影,不含日志正文。
|
|
5726
|
+
(function () {
|
|
5727
|
+
var lg = data.logs && typeof data.logs === 'object' ? data.logs : null
|
|
5728
|
+
if (!lg) return null
|
|
5729
|
+
if (!lg.exists) return kv(t('dbgLogs'), t('dbgLogsNone'), false)
|
|
5730
|
+
var lgText = []
|
|
5731
|
+
if (lg.sizeText) lgText.push(lg.sizeText)
|
|
5732
|
+
if (lg.lines !== null && lg.lines !== undefined) lgText.push(lg.lines + ' ' + (locale === 'zh' ? '行' : 'lines'))
|
|
5733
|
+
if (lg.historyBytes > 0) lgText.push((locale === 'zh' ? '历史 ' : 'prev ') + fmtSize(lg.historyBytes))
|
|
5734
|
+
return [
|
|
5735
|
+
kv(t('dbgLogs'), lgText.join(' · '), false),
|
|
5736
|
+
h('div', { 'data-dam-hint': '', style: { marginLeft: '140px', wordBreak: 'break-all' } },
|
|
5737
|
+
(locale === 'zh' ? '路径:' : 'Path: ') + (lg.path || '—')),
|
|
5738
|
+
h('div', { 'data-dam-hint': '', style: { marginLeft: '140px', wordBreak: 'break-all' } }, t('dbgLogsHint')),
|
|
5739
|
+
]
|
|
5740
|
+
})(),
|
|
5189
5741
|
hubIoByFile ? h('div', { 'data-dam-hint': '', style: { marginLeft: '140px', wordBreak: 'break-all' } },
|
|
5190
5742
|
(locale === 'zh' ? '按层拆分(失败次数):' : 'per-store failures: ') + hubIoByFile) : null,
|
|
5191
5743
|
fp ? kv(t('dbgPrune'), fpText, fpWarn) : null,
|
|
@@ -5364,7 +5916,7 @@ window.__ModuleLoader__.load({
|
|
|
5364
5916
|
text: locale === 'zh' ? '词法检索(0GB)永远可用作保底;内置语义引擎(约 130MB 量化模型)显著提升召回;进阶 Python 引擎(BGE-M3,约 563MB)面向深度用户。以下为自动检测结果,可在此直接安装。另有一个与隐私相关的检索信号开关:'
|
|
5365
5917
|
: 'Lexical search (0GB) is the always-on floor; the built-in engine (~130MB quantized model) boosts recall; the advanced Python engine (BGE-M3, ~563MB) is optional. Live detection below — install right here. One privacy-related retrieval switch:',
|
|
5366
5918
|
toggles: [
|
|
5367
|
-
{ key: 'reasoningObserverEnabled', name: locale === 'zh' ? '思维链监听' : 'Reasoning observer', sub: locale === 'zh' ? '
|
|
5919
|
+
{ key: 'reasoningObserverEnabled', name: locale === 'zh' ? '思维链监听' : 'Reasoning observer', sub: locale === 'zh' ? '监听模型思维链分段作为检索信号(默认开;内容比可见输出更敏感,不需要可在此关掉)' : 'Watch model CoT segments as a retrieval signal (default ON; more sensitive than visible output — turn off here if unwanted)', def: false, where: locale === 'zh' ? '自动记忆引擎' : 'Semantic engine' },
|
|
5368
5920
|
] },
|
|
5369
5921
|
{ art: 'radar', core: '', kicker: locale === 'zh' ? '唤起与固化' : 'ACTIVATION',
|
|
5370
5922
|
title: locale === 'zh' ? '该出手时才出手' : 'Interrupt only when it matters',
|
|
@@ -5375,7 +5927,7 @@ window.__ModuleLoader__.load({
|
|
|
5375
5927
|
{ key: 'autoConsolidate', name: locale === 'zh' ? '记忆固化(自动沉淀)' : 'Memory consolidation', sub: locale === 'zh' ? '每轮对话结束自动把结论沉淀进每日日志与记忆店(推荐开)' : 'Consolidate conclusions into the log & stores each turn (recommended)', rec: true, where: locale === 'zh' ? '自动化' : 'Automation' },
|
|
5376
5928
|
// ★2026-09-22 改键(设置页缺口审计 P0-2):旧键 procedurePromotionEnabled 因 DEFAULT_CONFIG
|
|
5377
5929
|
// 恒供给布尔 procedureInjectEnabled 而**结构性不可达** ⇒ 该开关此前是摆设(会翻面、写盘成功、宿主读不到)。
|
|
5378
|
-
{ key: 'procedureInjectEnabled', name: locale === 'zh' ? '技能固化与注入' : 'Skill crystallization', sub: locale === 'zh' ? '重复流程固化为 checklist
|
|
5930
|
+
{ key: 'procedureInjectEnabled', name: locale === 'zh' ? '技能固化与注入' : 'Skill crystallization', sub: locale === 'zh' ? '重复流程固化为 checklist 自动附上(本开关只管「附不附」;晋升审批在「唤起回顾」页,与本开关无关)' : 'Turn repeated flows into auto-attached checklists (this switch only controls attaching; promotion review lives in the Recall review tab and is unrelated)', where: locale === 'zh' ? '记忆中枢' : 'Memory Hub' },
|
|
5379
5931
|
// ★T10(2026-09-20):机械 procedure 切片开关。
|
|
5380
5932
|
// 背景:`crossFeed()` 会把 episode 意图**机械截断**成技能名与步骤(无模型介入),
|
|
5381
5933
|
// 产出的观察行「既不像技能名也不像步骤」——用户报「审批里看不懂」的来源。
|
|
@@ -5464,8 +6016,9 @@ window.__ModuleLoader__.load({
|
|
|
5464
6016
|
try {
|
|
5465
6017
|
apiGet(API.updateCheck).then(function (d) {
|
|
5466
6018
|
var cur = (d && d.current) || '2.1.0'
|
|
5467
|
-
//
|
|
5468
|
-
|
|
6019
|
+
// ★v3.1.3:**动态取最新版本**(此前硬编码 '2.1.0' ⇒ 关掉向导永远弹 2.1.0 的老卡)。
|
|
6020
|
+
// 与 :~7280 那处已正确的写法同源;兜底仍是 '2.1.0'(CHANGELOG 为空也不崩)。
|
|
6021
|
+
var latestKey = Object.keys(CHANGELOG).reduce(function (a, b) { return cmpVersion(b, a) > 0 ? b : a }, '2.1.0')
|
|
5469
6022
|
openDialog({ kind: 'update', versions: [{ version: latestKey, items: CHANGELOG[latestKey] || CHANGELOG['2.1.0'] }], currentVersion: cur })
|
|
5470
6023
|
try { localStorage.setItem('dsh-auto-memory.seenVersion', cur) } catch (e5) {}
|
|
5471
6024
|
}).catch(function () {
|
|
@@ -5565,6 +6118,16 @@ window.__ModuleLoader__.load({
|
|
|
5565
6118
|
return h('div', { key: k }, '· ', h('b', null, k), ' —— ' + whereGroups[k].join(locale === 'zh' ? ' / ' : ' / '))
|
|
5566
6119
|
}),
|
|
5567
6120
|
h('div', null, '· ', h('b', null, locale === 'zh' ? '面板页签' : 'Panel tabs'), locale === 'zh' ? ' —— 唤起回顾(决策打分)/ 存储管理(扫描修复)' : ' — Recall review / Storage tools'))) : null),
|
|
6121
|
+
// ★v3.1.3:末页赞助/贡献入口(用户要求「两个小按钮 + 简短注释,不破坏原有结构」)。
|
|
6122
|
+
// 作为 where 的**兄弟节点追加**;文案走 inline locale(与 TOUR_STEPS 全体现有写法一致),
|
|
6123
|
+
// 不新增 i18n 键 ⇒ 零 i18n 表改动。
|
|
6124
|
+
step.final ? h('div', { 'data-dam-tour-links': '' },
|
|
6125
|
+
h('a', { href: 'https://htmlpreview.github.io/?https://github.com/Aik358/dsh-auto-memory/blob/main/docs/CONTRIBUTORS.html', target: '_blank', rel: 'noopener noreferrer', 'data-dam-tour-link': '' },
|
|
6126
|
+
locale === 'zh' ? '贡献者与赞助' : 'Contributors & Sponsors'),
|
|
6127
|
+
h('a', { href: 'https://api.dshapi.icu/register?aff=HJU27P7JL39N', target: '_blank', rel: 'noopener noreferrer', 'data-dam-tour-link': '' },
|
|
6128
|
+
locale === 'zh' ? 'DSH API 中转站' : 'DSH API relay'),
|
|
6129
|
+
h('div', { 'data-dam-tour-links-note': '' },
|
|
6130
|
+
locale === 'zh' ? '感谢为本项目出力的贡献者与基础设施赞助方。' : 'Thanks to our contributors and infrastructure sponsors.')) : null,
|
|
5568
6131
|
h('div', { 'data-dam-tour-dots': '' },
|
|
5569
6132
|
TOUR_STEPS.map(function (s, si) {
|
|
5570
6133
|
return h('button', { key: si, 'data-dam-tour-dot': '', 'data-on': String(si === tourStep),
|
|
@@ -5831,6 +6394,33 @@ window.__ModuleLoader__.load({
|
|
|
5831
6394
|
}
|
|
5832
6395
|
return null
|
|
5833
6396
|
}
|
|
6397
|
+
// ★P10-T1/T2(2026-09-22):13 个注入分区的**显示文案**。成员与顺序以宿主下发的
|
|
6398
|
+
// `promptSections` 为准(宿主常量 PROMPT_SECTION_KEYS_V1 是唯一真源),本表只提供文案;
|
|
6399
|
+
// 键集合必须与宿主常量相等 —— 由守卫 smoke-test-t1-prompt-sections-pre.mjs 断言。
|
|
6400
|
+
// offZh/offEn 只给 must 段(宿主 PROMPT_SECTION_MUST_V1),其余用通用提示。
|
|
6401
|
+
var PROMPT_SECTION_TEXT = {
|
|
6402
|
+
'rules-section': { zh: '规则段(用户级硬约束)', en: 'Rules section (hard constraints)' },
|
|
6403
|
+
'tier0-catalog': { zh: 'Tier-0 常驻目录(指引层)', en: 'Tier-0 catalog (index layer)' },
|
|
6404
|
+
'whiteboard-plan': { zh: '白板 PLAN 快照', en: 'Whiteboard (PLAN) snapshot' },
|
|
6405
|
+
'handoff-ledger': { zh: '交接账本', en: 'Handoff ledger' },
|
|
6406
|
+
'calendar': { zh: '日历与日程', en: 'Calendar & schedule' },
|
|
6407
|
+
'external-memory': { zh: '其他 AI 工具记忆', en: 'Other AI tools’ memory' },
|
|
6408
|
+
'external-sessions': { zh: '历史会话索引', en: 'Past session index' },
|
|
6409
|
+
'workspace-map': { zh: '其他工作区记忆索引', en: 'Other workspace index' },
|
|
6410
|
+
'welcome-title': { zh: '新工作区欢迎语(标题)', en: 'New-workspace greeting (title)' },
|
|
6411
|
+
'welcome-body': { zh: '新工作区欢迎语(正文)', en: 'New-workspace greeting (body)' },
|
|
6412
|
+
'plan-update-request': {
|
|
6413
|
+
zh: '白板更新请求(硬性要求)', en: 'Whiteboard update request (mandatory)',
|
|
6414
|
+
offZh: '关掉后:模型不再被要求更新白板 ⇒ 白板会逐渐与项目现实脱节。',
|
|
6415
|
+
offEn: 'When off: the model is no longer asked to update the whiteboard — it will drift from reality.',
|
|
6416
|
+
},
|
|
6417
|
+
'water-advisory': {
|
|
6418
|
+
zh: '上下文水位提醒(硬性要求)', en: 'Context water-level advisory (mandatory)',
|
|
6419
|
+
offZh: '关掉后:不再提醒收尾写账本/反思 ⇒ 长任务跨窗口续命会变差。',
|
|
6420
|
+
offEn: 'When off: no reminder to write the ledger/reflection at wrap-up — cross-window continuity suffers.',
|
|
6421
|
+
},
|
|
6422
|
+
'handoff-pointer': { zh: '未绑定工作区时的账本指针', en: 'Ledger pointer (unbound workspace)' },
|
|
6423
|
+
}
|
|
5834
6424
|
function SettingsPage() {
|
|
5835
6425
|
var tickPair = useTick()
|
|
5836
6426
|
var cfgPair = useState(null)
|
|
@@ -5988,7 +6578,13 @@ window.__ModuleLoader__.load({
|
|
|
5988
6578
|
useEffect(function () { return controller.subscribe(tickPair[1]) }, [])
|
|
5989
6579
|
useEffect(function () {
|
|
5990
6580
|
var alive = true
|
|
5991
|
-
apiGet(API.config).then(function (d) {
|
|
6581
|
+
apiGet(API.config).then(function (d) {
|
|
6582
|
+
if (!alive) return
|
|
6583
|
+
setCfg(d.config)
|
|
6584
|
+
// ★P10-T1:分区清单随 config 应答体下发(宿主唯一真源;取不到就留空 ⇒ 面板给 fail-soft 提示)
|
|
6585
|
+
setPsecKeys(Array.isArray(d.promptSections) ? d.promptSections : [])
|
|
6586
|
+
setPsecMust(Array.isArray(d.promptSectionMust) ? d.promptSectionMust : [])
|
|
6587
|
+
}).catch(function (e) { setErr(e.message) })
|
|
5992
6588
|
// 打开设置页自动检查更新(host 有 12h 缓存,不重复查网)
|
|
5993
6589
|
apiGet(API.updateCheck).then(function (d) { if (alive) setVerInfo(d) }).catch(function () {})
|
|
5994
6590
|
return function () { alive = false }
|
|
@@ -6018,6 +6614,18 @@ window.__ModuleLoader__.load({
|
|
|
6018
6614
|
var detBusyPair = useState(false)
|
|
6019
6615
|
var detBusy = detBusyPair[0]
|
|
6020
6616
|
var setDetBusy = detBusyPair[1]
|
|
6617
|
+
// ★P10-T1(2026-09-22):注入分区开关的**二级页**状态 + 清单。
|
|
6618
|
+
// 清单来自宿主常量(经 /config 应答体 promptSections 下发),前端不硬编码成员。
|
|
6619
|
+
// 同 det* 的理由:必须挂在下方 if (!cfg) 早退之前,否则 hooks 数量不一致 → error #310。
|
|
6620
|
+
var psecOpenPair = useState(false)
|
|
6621
|
+
var psecOpen = psecOpenPair[0]
|
|
6622
|
+
var setPsecOpen = psecOpenPair[1]
|
|
6623
|
+
var psecKeysPair = useState([])
|
|
6624
|
+
var psecKeys = psecKeysPair[0]
|
|
6625
|
+
var setPsecKeys = psecKeysPair[1]
|
|
6626
|
+
var psecMustPair = useState([])
|
|
6627
|
+
var psecMust = psecMustPair[0]
|
|
6628
|
+
var setPsecMust = psecMustPair[1]
|
|
6021
6629
|
useEffect(function () {
|
|
6022
6630
|
refreshSem(setSem, function () { setSem({ loaded: true, ready: false }) })
|
|
6023
6631
|
return function () {}
|
|
@@ -6179,117 +6787,6 @@ window.__ModuleLoader__.load({
|
|
|
6179
6787
|
h('button', { 'data-dam-btn': '', title: locale === 'zh' ? '打开/收起安装向导(按当前引擎选择:JS 模型 或 Python 引擎)' : 'Open/collapse setup wizard (follows the selected engine)', onClick: function () { setGuide(guide ? '' : ((cfg.semanticEngineMode === 'python') ? 'python' : 'js')) } },
|
|
6180
6788
|
guide ? (locale === 'zh' ? '收起向导' : 'Hide wizard') : (locale === 'zh' ? '🧩 安装向导' : '🧩 Setup')),
|
|
6181
6789
|
]), t('semModeHint')),
|
|
6182
|
-
sem.loaded ? h('div', { 'data-dam-hint': '', style: { marginTop: '-4px', marginBottom: '8px' } },
|
|
6183
|
-
t('semResolved') + ': ' + (sem.resolvedTier === 'c2' ? t('tierC2') + ' ✓' : sem.resolvedTier === 'c3' ? t('tierC3') + ' ✓' : t('tierC1')))
|
|
6184
|
-
: null,
|
|
6185
|
-
// 2026-09-08 检测联动:所选模式与资产就绪状态不符时显式标注(不再静默掉回词法兜底)
|
|
6186
|
-
(sem.loaded && cfg.semanticEngineMode === 'js' && sem.ready === false) ? h('div', { 'data-dam-error': '', style: { marginTop: '-4px', marginBottom: '8px' } },
|
|
6187
|
-
locale === 'zh' ? '⚠ 已选 JS 引擎,但本地模型未就绪——当前实际生效:词法兜底。下方安装引导卡可下载模型(约130MB),完成后自动启用。' : '⚠ JS engine selected but the local model is not ready — lexical fallback is active. Use the setup card below to download the model (~130MB); it engages automatically when done.') : null,
|
|
6188
|
-
(sem.loaded && cfg.semanticEngineMode === 'python' && sem.pythonInt8Present === false) ? h('div', { 'data-dam-error': '', style: { marginTop: '-4px', marginBottom: '8px' } },
|
|
6189
|
-
locale === 'zh' ? '⚠ 已选 Python 引擎,但 BGE-M3 int8 模型未就绪——当前实际生效:词法兜底。下方安装向导可引导安装;若本机无法安装(如架构不支持),请改用 JS 引擎或 auto。' : '⚠ Python engine selected but the BGE-M3 int8 model is not ready — lexical fallback is active. Use the wizard below; if this machine cannot install it, switch to the JS engine or auto.') : null,
|
|
6190
|
-
field(t('fReasoning'), h('input', { type: 'checkbox', checked: !!cfg.reasoningObserverEnabled, onChange: function (e) { set('reasoningObserverEnabled', e.target.checked) } }), t('fReasoningHint')),
|
|
6191
|
-
field(locale === 'zh' ? '唤起阈值(校准策略)' : 'Activation thresholds (calibrated)', h('div', null,
|
|
6192
|
-
h('span', null, 'tauHi 0.45 · tauLo 0.35 · deltaExp 0.03 · deltaPro 0.05' + (sem.loaded ? ((locale === 'zh' ? ' · 发射模式:' : ' · emit: ') + (sem.activationEmitMode || 'shadow')) : ''))),
|
|
6193
|
-
t('fTuningHint')),
|
|
6194
|
-
]),
|
|
6195
|
-
section('window', sectionLabels.window, [
|
|
6196
|
-
field(t('fInject'), h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), t('fInjectHint')),
|
|
6197
|
-
field(t('fBudget'), h('input', { 'data-dam-input': '', type: 'number', min: 400, value: cfg.injectBudgetChars, onChange: function (e) { set('injectBudgetChars', Number(e.target.value) || 2400) } }), t('fBudgetHint')),
|
|
6198
|
-
// 2026-09-22 补接线(设置页缺口审计 P0-3/P0-4/P0-6):目录层与规则分层此前只有后端实现。
|
|
6199
|
-
field(t('fTier0Catalog'), h('input', { type: 'checkbox', 'data-dam-key': 'tier0CatalogEnabled', checked: cfg.tier0CatalogEnabled !== false, onChange: function (e) { set('tier0CatalogEnabled', e.target.checked) } }), t('fTier0CatalogHint')),
|
|
6200
|
-
field(t('fTier0Max'), h('input', { 'data-dam-input': '', type: 'number', min: 100, value: cfg.tier0MaxTokens === undefined ? 400 : cfg.tier0MaxTokens, onChange: function (e) { set('tier0MaxTokens', Math.max(100, Number(e.target.value) || 400)) } }), t('fTier0MaxHint')),
|
|
6201
|
-
field(t('fRulesLayering'), h('input', { type: 'checkbox', 'data-dam-key': 'rulesLayeringMode', checked: String(cfg.rulesLayeringMode || 'self').toLowerCase() !== 'off', onChange: function (e) { set('rulesLayeringMode', e.target.checked ? 'self' : 'off') } }), t('fRulesLayeringHint')),
|
|
6202
|
-
field(t('fDays'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 14, value: cfg.recentDaysInjected, onChange: function (e) { set('recentDaysInjected', Number(e.target.value) || 1) } }), t('fDaysHint')),
|
|
6203
|
-
field(t('fExtBudget'), h('input', { 'data-dam-input': '', type: 'number', min: 200, value: cfg.externalInjectionChars === undefined ? 1400 : cfg.externalInjectionChars, onChange: function (e) { set('externalInjectionChars', Number(e.target.value) || 1400) } }), t('fExtBudgetHint')),
|
|
6204
|
-
field(t('fSnapGap'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 50, value: cfg.snapshotMinGapRounds === undefined ? 5 : cfg.snapshotMinGapRounds, onChange: function (e) { set('snapshotMinGapRounds', Number(e.target.value) || 5) } }), t('fSnapGapHint')),
|
|
6205
|
-
field(t('fReinjectOnCompact'), h('input', { type: 'checkbox', checked: cfg.snapshotReinjectOnCompact !== false, onChange: function (e) { set('snapshotReinjectOnCompact', e.target.checked) } }), t('fReinjectOnCompactHint')),
|
|
6206
|
-
field(t('fPromptCustom'), h('button', { 'data-dam-btn': '', onClick: function () { setPromptEditOpen(!promptEditOpen) } }, (promptEditOpen ? (locale === 'zh' ? '收起' : 'Collapse') : (locale === 'zh' ? '编辑 prompt 层' : 'Edit prompt layers'))), t('fPromptCustomHint')),
|
|
6207
|
-
promptEditOpen ? [
|
|
6208
|
-
h('div', { key: '__layers', style: { padding: '6px 0 2px', width: '100%' } },
|
|
6209
|
-
(Object.keys(DEFAULT_PROMPT_LAYERS_CLIENT)).map(function (k) {
|
|
6210
|
-
return h('div', { key: k, style: { marginBottom: '6px' } },
|
|
6211
|
-
h('div', { 'data-dam-hint': '', style: { fontWeight: 700, marginBottom: '2px' } }, k),
|
|
6212
|
-
h('textarea', { 'data-dam-input': '', rows: 2, style: { width: '100%', fontFamily: 'monospace', fontSize: 'calc(11px * var(--dam-scale))' }, value: (cfg.promptLayerOverrides || {})[k] || '', placeholder: DEFAULT_PROMPT_LAYERS_CLIENT[k] || '(默认文案)', onChange: function (e) { var ov = Object.assign({}, cfg.promptLayerOverrides || {}); if (e.target.value.trim() === '') delete ov[k]; else ov[k] = e.target.value; set('promptLayerOverrides', ov) } }))
|
|
6213
|
-
})),
|
|
6214
|
-
h('div', { key: '__reset', 'data-dam-row': '', style: { marginTop: '6px' } },
|
|
6215
|
-
h('button', { 'data-dam-btn': '', onClick: function () { set('promptLayerOverrides', {}) } }, locale === 'zh' ? '一键恢复默认' : 'Reset to defaults'))
|
|
6216
|
-
] : null,
|
|
6217
|
-
]),
|
|
6218
|
-
section('capacity', sectionLabels.capacity, [
|
|
6219
|
-
field(t('fChildObs'), h('input', { type: 'checkbox', checked: !!cfg.contextBridgeObserveChildSessions, onChange: function (e) { set('contextBridgeObserveChildSessions', e.target.checked) } }), t('fChildObsHint')),
|
|
6220
|
-
field(t('fEpisodicRet'), h('input', { 'data-dam-input': '', type: 'number', min: 16, max: 4096, value: cfg.episodicRetention === undefined ? 256 : cfg.episodicRetention, onChange: function (e) { set('episodicRetention', Math.max(16, Math.min(4096, Number(e.target.value) || 256))) } }), t('fEpisodicRetHint')),
|
|
6221
|
-
// 2026-09-22 补接线(P0-4):facts 淘汰上限(fact-store.js pruneIfNeeded)。
|
|
6222
|
-
field(t('fFactRetention'), h('input', { 'data-dam-input': '', type: 'number', min: 10, value: cfg.factRetentionMax === undefined ? 1000 : cfg.factRetentionMax, onChange: function (e) { set('factRetentionMax', Math.max(10, Number(e.target.value) || 1000)) } }), t('fFactRetentionHint')),
|
|
6223
|
-
field(t('fNoteCap'), h('input', { 'data-dam-input': '', type: 'number', min: 500, value: cfg.noteCapacityChars === undefined ? 24000 : cfg.noteCapacityChars, onChange: function (e) { set('noteCapacityChars', Number(e.target.value) || 24000) } }), t('fNoteCapHint')),
|
|
6224
|
-
field(t('fUserCap'), h('input', { 'data-dam-input': '', type: 'number', min: 500, value: cfg.userCapacityChars === undefined ? 24000 : cfg.userCapacityChars, onChange: function (e) { set('userCapacityChars', Number(e.target.value) || 24000) } }), t('fUserCapHint')),
|
|
6225
|
-
field(t('fConsolidateMin'), h('input', { 'data-dam-input': '', type: 'number', min: 80, value: cfg.autoConsolidateMinChars === undefined ? 240 : cfg.autoConsolidateMinChars, onChange: function (e) { set('autoConsolidateMinChars', Number(e.target.value) || 240) } }), t('fConsolidateMinHint')),
|
|
6226
|
-
field(t('fAutoConsolidate'), h('input', { type: 'checkbox', checked: cfg.autoConsolidate !== false, onChange: function (e) { set('autoConsolidate', e.target.checked) } }), t('fAutoConsolidateHint')),
|
|
6227
|
-
field(t('fConsolidate'), h('input', { 'data-dam-input': '', type: 'number', min: 5, value: cfg.autoConsolidateCooldownMinutes === undefined ? 30 : cfg.autoConsolidateCooldownMinutes, onChange: function (e) { set('autoConsolidateCooldownMinutes', Number(e.target.value) || 30) } }), t('fConsolidateHint')),
|
|
6228
|
-
field(t('fConsolidateMax'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 50, value: cfg.autoConsolidateDailyMax === undefined ? 8 : cfg.autoConsolidateDailyMax, onChange: function (e) { set('autoConsolidateDailyMax', Number(e.target.value) || 8) } }), t('fConsolidateMaxHint')),
|
|
6229
|
-
field(t('fMemFileIndex'), h('input', { type: 'checkbox', 'data-dam-key': 'memoryFileIndexEnabled', checked: cfg.memoryFileIndexEnabled === true, onChange: function (e) { set('memoryFileIndexEnabled', e.target.checked) } }), t('fMemFileIndexHint')),
|
|
6230
|
-
// 2026-09-14 解耦:交接白板与自动接续各自独立成项(旧实现只在白板页有接续按钮,且 enabled 还被
|
|
6231
|
-
// handoffEnabled 二次与运算)。两者出厂默认均为 false(功能仍在测试期),这里与白板页读写完全相同的配置键。
|
|
6232
|
-
]),
|
|
6233
|
-
section('skills', sectionLabels.skills, [
|
|
6234
|
-
h('div', { 'data-dam-hint': '' }, t('secMemoryHubHint')),
|
|
6235
|
-
field(t('fMemoryHub'), h('input', { type: 'checkbox', checked: !!cfg.memoryHubEnabled, onChange: function (e) { set('memoryHubEnabled', e.target.checked) } }), t('fMemoryHubHint')),
|
|
6236
|
-
field(t('fEpisodicMin'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 16, value: cfg.episodicMinSegments === undefined ? 2 : cfg.episodicMinSegments, onChange: function (e) { set('episodicMinSegments', Math.max(1, Math.min(16, Number(e.target.value) || 2))) } }), t('fEpisodicMinHint')),
|
|
6237
|
-
field(t('fProcSessions'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 20, value: cfg.procedureMinSessions === undefined ? 3 : cfg.procedureMinSessions, onChange: function (e) { set('procedureMinSessions', Math.max(1, Math.min(20, Number(e.target.value) || 3))) } }), t('fProcSessionsHint')),
|
|
6238
|
-
field(t('fProcSuccess'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 20, value: cfg.procedureMinSuccess === undefined ? 2 : cfg.procedureMinSuccess, onChange: function (e) { set('procedureMinSuccess', Math.max(1, Math.min(20, Number(e.target.value) || 2))) } }), t('fProcSuccessHint')),
|
|
6239
|
-
field(t('fProcCorr'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 1, step: 0.05, value: cfg.procedureCorrectionCap === undefined ? 0.3 : cfg.procedureCorrectionCap, onChange: function (e) { set('procedureCorrectionCap', Math.max(0, Math.min(1, Number(e.target.value) || 0.3))) } }), t('fProcCorrHint')),
|
|
6240
|
-
field(t('fProcRisk'), h('input', { type: 'checkbox', checked: cfg.procedureHighRiskApproval !== false, onChange: function (e) { set('procedureHighRiskApproval', e.target.checked) } }), t('fProcRiskHint')),
|
|
6241
|
-
// 2026-09-22 补接线(设置页缺口审计 P0-1):procedureInjectEnabled 才是宿主真读的总闸
|
|
6242
|
-
// (procedure-switch.js 先判它);此前设置页只有旧键 ⇒ 用户改了也无效(灰控件)。
|
|
6243
|
-
field(t('fProcInject'), h('input', { type: 'checkbox', 'data-dam-key': 'procedureInjectEnabled', checked: cfg.procedureInjectEnabled !== false, onChange: function (e) { set('procedureInjectEnabled', e.target.checked) } }), t('fProcInjectHint')),
|
|
6244
|
-
field(t('fProcLevel'), h('select', { 'data-dam-select': '', value: cfg.procedureActiveLevel || 'checklist', onChange: function (e) { set('procedureActiveLevel', e.target.value) } },
|
|
6245
|
-
h('option', { value: 'checklist' }, locale === 'zh' ? 'checklist 完整步骤' : 'checklist (full steps)'),
|
|
6246
|
-
h('option', { value: 'excerpt' }, locale === 'zh' ? 'excerpt 摘要' : 'excerpt (summary)'),
|
|
6247
|
-
h('option', { value: 'hint' }, locale === 'zh' ? 'hint 仅提示' : 'hint (hint only)')), t('fProcLevelHint')),
|
|
6248
|
-
h('div', { 'data-dam-hint': '', style: { marginTop: '4px' } }, t('memoryHubViewHint')),
|
|
6249
|
-
]),
|
|
6250
|
-
section('handoff', sectionLabels.handoff, [
|
|
6251
|
-
field(t('fHandoff'), h('input', { type: 'checkbox', 'data-dam-key': 'handoffEnabled', checked: cfg.handoffEnabled !== false, onChange: function (e) { set('handoffEnabled', e.target.checked) } }), t('fHandoffHint')),
|
|
6252
|
-
field(t('fAutoContinue'), h('input', { type: 'checkbox', 'data-dam-key': 'autoContinueEnabled', checked: cfg.autoContinueEnabled !== false, onChange: function (e) { set('autoContinueEnabled', e.target.checked) } }), t('fAutoContinueHint')),
|
|
6253
|
-
field(t('fHandoffPlan'), h('input', { 'data-dam-input': '', type: 'number', min: 200, value: cfg.handoffPlanChars === undefined ? 1200 : cfg.handoffPlanChars, onChange: function (e) { set('handoffPlanChars', Number(e.target.value) || 1200) } }), t('fHandoffPlanHint')),
|
|
6254
|
-
// 2026-09-22 补接线(设置页缺口审计 P0-5):criteriaGate 此前只能改文件才能退。
|
|
6255
|
-
field(t('fCriteriaGate'), h('input', { type: 'checkbox', 'data-dam-key': 'criteriaGate', checked: cfg.criteriaGate !== false, onChange: function (e) { set('criteriaGate', e.target.checked) } }), t('fCriteriaGateHint')),
|
|
6256
|
-
field(t('fHandoffLedger'), h('input', { 'data-dam-input': '', type: 'number', min: 100, value: cfg.handoffLedgerChars === undefined ? 800 : cfg.handoffLedgerChars, onChange: function (e) { set('handoffLedgerChars', Number(e.target.value) || 800) } }), t('fHandoffLedgerHint')),
|
|
6257
|
-
field(t('fWaterWindow'), h('input', { 'data-dam-input': '', type: 'number', min: 0, value: cfg.waterLevelWindowTokens === undefined ? 65536 : cfg.waterLevelWindowTokens, onChange: function (e) { set('waterLevelWindowTokens', Number(e.target.value) || 0) } }), t('fWaterWindowHint')),
|
|
6258
|
-
field(t('fWaterThreshold'), h('input', { 'data-dam-input': '', type: 'number', min: 0.1, max: 1.5, step: 0.05, value: cfg.waterLevelThreshold === undefined ? 0.75 : cfg.waterLevelThreshold, onChange: function (e) { set('waterLevelThreshold', Number(e.target.value) || 0.75) } }), t('fWaterThresholdHint')),
|
|
6259
|
-
field(t('fWaterAdvisory'), h('input', { type: 'checkbox', checked: cfg.waterLevelAdvisory !== false, onChange: function (e) { set('waterLevelAdvisory', e.target.checked) } }), t('fWaterAdvisoryHint')),
|
|
6260
|
-
field(t('fWaterAuto'), h('input', { type: 'checkbox', checked: cfg.waterLevelAutoHandoff !== false, onChange: function (e) { set('waterLevelAutoHandoff', e.target.checked) } }), t('fWaterAutoHint')),
|
|
6261
|
-
field(locale === 'zh' ? '子代理痕迹回收' : 'Subagent trace recycle', h('input', { type: 'checkbox', checked: cfg.subagentGcEnabled !== false, onChange: function (e) { set('subagentGcEnabled', e.target.checked) } }),
|
|
6262
|
-
locale === 'zh' ? '本插件的一次性子代理(自动沉淀 / 时段总结 / 问候 / 蒸馏)每跑一次都会在 ~/.dsh/sessions 留下一个持久化会话;数量上千后会话列表会明显变慢。开启后任务一结束就把这些痕迹移入 ~/.dsh/subagent-gc-backup 备份(可回滚),不影响子代理结果。默认开。' : 'Every one-shot subagent this plugin spawns (auto-consolidation, scheduled summary, greeting, distillation) leaves a persisted session under ~/.dsh/sessions; hundreds of them slow the session list down. When on, each trace is moved into ~/.dsh/subagent-gc-backup right after the task ends (reversible), never affecting the subagent result. On by default.'),
|
|
6263
|
-
field(locale === 'zh' ? '兜底回收保留天数' : 'Fallback recycle keep days', h('input', { 'data-dam-input': '', type: 'number', min: 0, value: cfg.subagentGcKeepDays === undefined ? 3 : cfg.subagentGcKeepDays, onChange: function (e) { set('subagentGcKeepDays', Number(e.target.value) || 0) } }),
|
|
6264
|
-
locale === 'zh' ? '每天巡检一次,回收超过该天数仍残留的痕迹(比如任务异常中断没删掉的);0 = 不按时间,只靠任务结束即删。' : 'A daily sweep recycles traces older than this many days (e.g. leftovers from interrupted tasks); 0 = rely on the end-of-task recycle only.'),
|
|
6265
|
-
]),
|
|
6266
|
-
section('auto', sectionLabels.auto, [
|
|
6267
|
-
// 欢迎向导:开关(首启自动播放)+ 立即重看按钮(闭包内直调 openDialog——同一作用域,点击立即弹;
|
|
6268
|
-
// 不走 window 全局入口,避免多实例时序导致"点了没反应要刷新")+ 查看更新日志(走 update 弹窗,
|
|
6269
|
-
// 带 Logo 开场动画;versions 取 CHANGELOG 最新一条)
|
|
6270
|
-
field(t('fWelcomeTour'), h('div', { 'data-dam-row': '' },
|
|
6271
|
-
h('input', { type: 'checkbox', checked: cfg.welcomeTourEnabled !== false, onChange: function (e) { set('welcomeTourEnabled', e.target.checked) } }),
|
|
6272
|
-
h('button', { 'data-dam-btn': '', onClick: function () { try { openDialog({ kind: 'welcomeTour' }) } catch (eTour) {} } }, t('tourReplay')),
|
|
6273
|
-
h('button', { 'data-dam-btn': '', onClick: function () {
|
|
6274
|
-
try {
|
|
6275
|
-
var keys = Object.keys(CHANGELOG)
|
|
6276
|
-
if (!keys.length) return
|
|
6277
|
-
var latest = keys.sort(cmpVersion)[keys.length - 1]
|
|
6278
|
-
openDialog({ kind: 'update', versions: [{ version: latest, items: CHANGELOG[latest] }], currentVersion: latest })
|
|
6279
|
-
} catch (eLog) {}
|
|
6280
|
-
} }, locale === 'zh' ? '查看更新日志' : 'View changelog')), t('fWelcomeTourHint')),
|
|
6281
|
-
field(t('fAutoPopup'), h('input', { type: 'checkbox', checked: cfg.autoPopupEnabled !== false, onChange: function (e) { set('autoPopupEnabled', e.target.checked) } }), t('fAutoPopupHint')),
|
|
6282
|
-
field(t('fUnattended'), h('input', { type: 'checkbox', checked: !!cfg.unattendedMode, onChange: function (e) { set('unattendedMode', e.target.checked) } }), t('fUnattendedHint')),
|
|
6283
|
-
field(t('fUnattendedAuto'), h('input', { type: 'checkbox', checked: !!cfg.unattendedAuto, onChange: function (e) { set('unattendedAuto', e.target.checked) } }), t('fUnattendedAutoHint')),
|
|
6284
|
-
field(t('fAway'), h('input', { 'data-dam-input': '', type: 'number', min: 0, value: (Number(cfg.awayMinutes) === 0 ? 0 : (cfg.awayMinutes || 60)), onChange: function (e) { var v = Number(e.target.value); set('awayMinutes', Number.isFinite(v) && v >= 0 ? v : 60) } }), t('fAwayHint')),
|
|
6285
|
-
field(t('fAutoSum'), h('input', { 'data-dam-input': '', value: (cfg.autoSummaryTimes || []).join(','), onChange: function (e) { set('autoSummaryTimes', String(e.target.value || '').split(',').map(function (s) { return s.trim() }).filter(Boolean)) } }), t('fAutoSumHint')),
|
|
6286
|
-
field(t('fConsSchedule'), h('input', { type: 'checkbox', checked: cfg.consolidateScheduleEnabled !== false, onChange: function (e) { set('consolidateScheduleEnabled', e.target.checked) } }), t('fConsScheduleHint')),
|
|
6287
|
-
field(t('fConsScheduleTime'), h('input', { 'data-dam-input': '', placeholder: '09:30', value: cfg.consolidateScheduleTime === undefined ? '09:30' : cfg.consolidateScheduleTime, onChange: function (e) { set('consolidateScheduleTime', String(e.target.value || '').trim()) } }), t('fConsScheduleTimeHint')),
|
|
6288
|
-
field(t('fConsScheduleDays'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 60, value: cfg.consolidateScheduleDays === undefined ? 7 : cfg.consolidateScheduleDays, onChange: function (e) { set('consolidateScheduleDays', Number(e.target.value) || 7) } }), t('fConsScheduleDaysHint')),
|
|
6289
|
-
field(t('fMaintSchedule'), h('input', { type: 'checkbox', checked: cfg.maintainScheduleEnabled !== false, onChange: function (e) { set('maintainScheduleEnabled', e.target.checked) } }), t('fMaintScheduleHint')),
|
|
6290
|
-
field(t('fMaintScheduleTime'), h('input', { 'data-dam-input': '', placeholder: '10:00', value: cfg.maintainScheduleTime === undefined ? '10:00' : cfg.maintainScheduleTime, onChange: function (e) { set('maintainScheduleTime', String(e.target.value || '').trim()) } }), t('fMaintScheduleTimeHint')),
|
|
6291
|
-
]),
|
|
6292
|
-
section('store', sectionLabels.store, [
|
|
6293
6790
|
// M-CM6-B v3·环境检测面板:⟳ 按钮触发,自动检索环境(快检+深扫+热接入),联动安装助手;美学对齐安装向导卡
|
|
6294
6791
|
detOpen ? (function () {
|
|
6295
6792
|
var panelStyle = { border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.25)) 60%, transparent)', borderRadius: '10px', padding: '12px', marginBottom: '10px', background: 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 55%, transparent)', backdropFilter: 'blur(14px) saturate(1.3)', WebkitBackdropFilter: 'blur(14px) saturate(1.3)' }
|
|
@@ -6405,6 +6902,156 @@ window.__ModuleLoader__.load({
|
|
|
6405
6902
|
h('button', { 'data-dam-btn': '', onClick: function () { setGuide('') } }, t('gotIt'))))
|
|
6406
6903
|
})()
|
|
6407
6904
|
: null,
|
|
6905
|
+
sem.loaded ? h('div', { 'data-dam-hint': '', style: { marginTop: '-4px', marginBottom: '8px' } },
|
|
6906
|
+
t('semResolved') + ': ' + (sem.resolvedTier === 'c2' ? t('tierC2') + ' ✓' : sem.resolvedTier === 'c3' ? t('tierC3') + ' ✓' : t('tierC1')))
|
|
6907
|
+
: null,
|
|
6908
|
+
// 2026-09-08 检测联动:所选模式与资产就绪状态不符时显式标注(不再静默掉回词法兜底)
|
|
6909
|
+
(sem.loaded && cfg.semanticEngineMode === 'js' && sem.ready === false) ? h('div', { 'data-dam-error': '', style: { marginTop: '-4px', marginBottom: '8px' } },
|
|
6910
|
+
locale === 'zh' ? '⚠ 已选 JS 引擎,但本地模型未就绪——当前实际生效:词法兜底。下方安装引导卡可下载模型(约130MB),完成后自动启用。' : '⚠ JS engine selected but the local model is not ready — lexical fallback is active. Use the setup card below to download the model (~130MB); it engages automatically when done.') : null,
|
|
6911
|
+
(sem.loaded && cfg.semanticEngineMode === 'python' && sem.pythonInt8Present === false) ? h('div', { 'data-dam-error': '', style: { marginTop: '-4px', marginBottom: '8px' } },
|
|
6912
|
+
locale === 'zh' ? '⚠ 已选 Python 引擎,但 BGE-M3 int8 模型未就绪——当前实际生效:词法兜底。下方安装向导可引导安装;若本机无法安装(如架构不支持),请改用 JS 引擎或 auto。' : '⚠ Python engine selected but the BGE-M3 int8 model is not ready — lexical fallback is active. Use the wizard below; if this machine cannot install it, switch to the JS engine or auto.') : null,
|
|
6913
|
+
field(t('fReasoning'), h('input', { type: 'checkbox', checked: !!cfg.reasoningObserverEnabled, onChange: function (e) { set('reasoningObserverEnabled', e.target.checked) } }), t('fReasoningHint')),
|
|
6914
|
+
field(locale === 'zh' ? '唤起阈值(校准策略)' : 'Activation thresholds (calibrated)', h('div', null,
|
|
6915
|
+
h('span', null, 'tauHi 0.45 · tauLo 0.35 · deltaExp 0.03 · deltaPro 0.05' + (sem.loaded ? ((locale === 'zh' ? ' · 发射模式:' : ' · emit: ') + (sem.activationEmitMode || 'shadow')) : ''))),
|
|
6916
|
+
t('fTuningHint')),
|
|
6917
|
+
]),
|
|
6918
|
+
section('window', sectionLabels.window, [
|
|
6919
|
+
// ★P10-T1/T2/T3(2026-09-22)注入分区开关:**一级只放入口**(中性文案,不推荐用户改),
|
|
6920
|
+
// 13 个开关全在二级页;成员/顺序取自宿主下发的 psecKeys(前端不硬编码)。
|
|
6921
|
+
field(t('fPromptSections'), h('button', { 'data-dam-btn': '', onClick: function () { setPsecOpen(!psecOpen) } }, psecOpen ? (locale === 'zh' ? '收起' : 'Collapse') : (locale === 'zh' ? '高级:逐段注入控制…' : 'Advanced: per-section injection…')), t('fPromptSectionsHint')),
|
|
6922
|
+
psecOpen ? (function () {
|
|
6923
|
+
var toggles = (cfg.promptSectionToggles && typeof cfg.promptSectionToggles === 'object') ? cfg.promptSectionToggles : {}
|
|
6924
|
+
var pText = function (k) { var m = PROMPT_SECTION_TEXT[k]; return m ? (locale === 'zh' ? m.zh : m.en) : k }
|
|
6925
|
+
var pOff = function (k) {
|
|
6926
|
+
var m = PROMPT_SECTION_TEXT[k]
|
|
6927
|
+
var z = m ? (locale === 'zh' ? m.offZh : m.offEn) : ''
|
|
6928
|
+
if (z) return z
|
|
6929
|
+
return locale === 'zh' ? '关掉后本轮不再注入该段落。' : 'When off, this section is not injected this round.'
|
|
6930
|
+
}
|
|
6931
|
+
return h('div', { 'data-dam-prompt-sections': '', style: { border: '1px solid color-mix(in srgb, var(--dsw-alias-border-l1, rgba(128,128,128,.25)) 60%, transparent)', borderRadius: '10px', padding: '12px', marginBottom: '10px', background: 'color-mix(in srgb, var(--dsw-alias-bg-layer-1, rgba(128,128,128,.06)) 55%, transparent)' } }, [
|
|
6932
|
+
h('div', { style: { display: 'flex', gap: '8px', alignItems: 'center', marginBottom: '6px' } }, [
|
|
6933
|
+
h('b', { style: { flex: 1, fontSize: 'calc(12px * var(--dam-scale))' } }, locale === 'zh' ? '逐段注入控制(高级)' : 'Per-section injection (advanced)'),
|
|
6934
|
+
h('button', { 'data-dam-btn': '', onClick: function () { set('promptSectionToggles', {}) } }, locale === 'zh' ? '全部恢复全开' : 'Restore all'),
|
|
6935
|
+
h('button', { 'data-dam-btn': '', onClick: function () { setPsecOpen(false) } }, t('close')),
|
|
6936
|
+
]),
|
|
6937
|
+
h('div', { 'data-dam-hint': '', style: { marginBottom: '8px' } }, locale === 'zh'
|
|
6938
|
+
? '控制每一轮注入里包含哪些段落。默认全部开启;不推荐修改 —— 关掉某段会让模型少掉对应的上下文或纪律。'
|
|
6939
|
+
: 'Which sections each round injects. All on by default; not recommended to change — turning one off drops the matching context or discipline.'),
|
|
6940
|
+
psecKeys.length ? psecKeys.map(function (k) {
|
|
6941
|
+
return h('div', { key: k, 'data-dam-row': '', style: { alignItems: 'flex-start' } }, [
|
|
6942
|
+
h('label', { style: { display: 'flex', gap: '6px', alignItems: 'center', flex: 1, cursor: 'pointer' } }, [
|
|
6943
|
+
h('input', { type: 'checkbox', 'data-dam-pswitch': k, checked: toggles[k] !== false, onChange: function (e) {
|
|
6944
|
+
var t2 = Object.assign({}, toggles)
|
|
6945
|
+
if (e.target.checked) delete t2[k]; else t2[k] = false
|
|
6946
|
+
set('promptSectionToggles', t2)
|
|
6947
|
+
} }),
|
|
6948
|
+
h('span', { style: { fontSize: 'calc(12px * var(--dam-scale))' } }, pText(k) + (psecMust.indexOf(k) >= 0 ? (locale === 'zh' ? ' · 硬性要求' : ' · mandatory') : '')),
|
|
6949
|
+
]),
|
|
6950
|
+
h('div', { 'data-dam-hint': '', style: { flex: 1, opacity: .8 } }, pOff(k)),
|
|
6951
|
+
])
|
|
6952
|
+
}) : h('div', { 'data-dam-hint': '' }, locale === 'zh' ? '未能从宿主取到分区清单(请重启宿主后重试)。' : 'Section list unavailable (restart the host and retry).'),
|
|
6953
|
+
])
|
|
6954
|
+
})() : null,
|
|
6955
|
+
field(t('fInject'), h('input', { type: 'checkbox', checked: !!cfg.injectEnabled, onChange: function (e) { set('injectEnabled', e.target.checked) } }), t('fInjectHint')),
|
|
6956
|
+
field(t('fBudget'), h('input', { 'data-dam-input': '', type: 'number', min: 400, value: cfg.injectBudgetChars, onChange: function (e) { set('injectBudgetChars', Number(e.target.value) || 2400) } }), t('fBudgetHint')),
|
|
6957
|
+
// 2026-09-22 补接线(设置页缺口审计 P0-3/P0-4/P0-6):目录层与规则分层此前只有后端实现。
|
|
6958
|
+
field(t('fTier0Catalog'), h('input', { type: 'checkbox', 'data-dam-key': 'tier0CatalogEnabled', checked: cfg.tier0CatalogEnabled !== false, onChange: function (e) { set('tier0CatalogEnabled', e.target.checked) } }), t('fTier0CatalogHint')),
|
|
6959
|
+
field(t('fTier0Max'), h('input', { 'data-dam-input': '', type: 'number', min: 100, value: cfg.tier0MaxTokens === undefined ? 400 : cfg.tier0MaxTokens, onChange: function (e) { set('tier0MaxTokens', Math.max(100, Number(e.target.value) || 400)) } }), t('fTier0MaxHint')),
|
|
6960
|
+
field(t('fRulesLayering'), h('input', { type: 'checkbox', 'data-dam-key': 'rulesLayeringMode', checked: String(cfg.rulesLayeringMode || 'self').toLowerCase() !== 'off', onChange: function (e) { set('rulesLayeringMode', e.target.checked ? 'self' : 'off') } }), t('fRulesLayeringHint')),
|
|
6961
|
+
field(t('fDays'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 14, value: cfg.recentDaysInjected, onChange: function (e) { set('recentDaysInjected', Number(e.target.value) || 1) } }), t('fDaysHint')),
|
|
6962
|
+
field(t('fExtBudget'), h('input', { 'data-dam-input': '', type: 'number', min: 200, value: cfg.externalInjectionChars === undefined ? 1400 : cfg.externalInjectionChars, onChange: function (e) { set('externalInjectionChars', Number(e.target.value) || 1400) } }), t('fExtBudgetHint')),
|
|
6963
|
+
field(t('fSnapGap'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 50, value: cfg.snapshotMinGapRounds === undefined ? 5 : cfg.snapshotMinGapRounds, onChange: function (e) { set('snapshotMinGapRounds', Number(e.target.value) || 5) } }), t('fSnapGapHint')),
|
|
6964
|
+
field(t('fReinjectOnCompact'), h('input', { type: 'checkbox', checked: cfg.snapshotReinjectOnCompact !== false, onChange: function (e) { set('snapshotReinjectOnCompact', e.target.checked) } }), t('fReinjectOnCompactHint')),
|
|
6965
|
+
field(t('fPromptCustom'), h('button', { 'data-dam-btn': '', onClick: function () { setPromptEditOpen(!promptEditOpen) } }, (promptEditOpen ? (locale === 'zh' ? '收起' : 'Collapse') : (locale === 'zh' ? '编辑 prompt 层' : 'Edit prompt layers'))), t('fPromptCustomHint')),
|
|
6966
|
+
promptEditOpen ? [
|
|
6967
|
+
h('div', { key: '__layers', style: { padding: '6px 0 2px', width: '100%' } },
|
|
6968
|
+
(Object.keys(DEFAULT_PROMPT_LAYERS_CLIENT)).map(function (k) {
|
|
6969
|
+
return h('div', { key: k, style: { marginBottom: '6px' } },
|
|
6970
|
+
h('div', { 'data-dam-hint': '', style: { fontWeight: 700, marginBottom: '2px' } }, k),
|
|
6971
|
+
h('textarea', { 'data-dam-input': '', rows: 2, style: { width: '100%', fontFamily: 'monospace', fontSize: 'calc(11px * var(--dam-scale))' }, value: (cfg.promptLayerOverrides || {})[k] || '', placeholder: DEFAULT_PROMPT_LAYERS_CLIENT[k] || '(默认文案)', onChange: function (e) { var ov = Object.assign({}, cfg.promptLayerOverrides || {}); if (e.target.value.trim() === '') delete ov[k]; else ov[k] = e.target.value; set('promptLayerOverrides', ov) } }))
|
|
6972
|
+
})),
|
|
6973
|
+
h('div', { key: '__reset', 'data-dam-row': '', style: { marginTop: '6px' } },
|
|
6974
|
+
h('button', { 'data-dam-btn': '', onClick: function () { set('promptLayerOverrides', {}) } }, locale === 'zh' ? '一键恢复默认' : 'Reset to defaults'))
|
|
6975
|
+
] : null,
|
|
6976
|
+
]),
|
|
6977
|
+
section('capacity', sectionLabels.capacity, [
|
|
6978
|
+
field(t('fChildObs'), h('input', { type: 'checkbox', checked: !!cfg.contextBridgeObserveChildSessions, onChange: function (e) { set('contextBridgeObserveChildSessions', e.target.checked) } }), t('fChildObsHint')),
|
|
6979
|
+
field(t('fEpisodicRet'), h('input', { 'data-dam-input': '', type: 'number', min: 16, max: 4096, value: cfg.episodicRetention === undefined ? 256 : cfg.episodicRetention, onChange: function (e) { set('episodicRetention', Math.max(16, Math.min(4096, Number(e.target.value) || 256))) } }), t('fEpisodicRetHint')),
|
|
6980
|
+
// 2026-09-22 补接线(P0-4):facts 淘汰上限(fact-store.js pruneIfNeeded)。
|
|
6981
|
+
field(t('fFactRetention'), h('input', { 'data-dam-input': '', type: 'number', min: 10, value: cfg.factRetentionMax === undefined ? 1000 : cfg.factRetentionMax, onChange: function (e) { set('factRetentionMax', Math.max(10, Number(e.target.value) || 1000)) } }), t('fFactRetentionHint')),
|
|
6982
|
+
field(t('fNoteCap'), h('input', { 'data-dam-input': '', type: 'number', min: 500, value: cfg.noteCapacityChars === undefined ? 24000 : cfg.noteCapacityChars, onChange: function (e) { set('noteCapacityChars', Number(e.target.value) || 24000) } }), t('fNoteCapHint')),
|
|
6983
|
+
field(t('fUserCap'), h('input', { 'data-dam-input': '', type: 'number', min: 500, value: cfg.userCapacityChars === undefined ? 24000 : cfg.userCapacityChars, onChange: function (e) { set('userCapacityChars', Number(e.target.value) || 24000) } }), t('fUserCapHint')),
|
|
6984
|
+
field(t('fConsolidateMin'), h('input', { 'data-dam-input': '', type: 'number', min: 80, value: cfg.autoConsolidateMinChars === undefined ? 240 : cfg.autoConsolidateMinChars, onChange: function (e) { set('autoConsolidateMinChars', Number(e.target.value) || 240) } }), t('fConsolidateMinHint')),
|
|
6985
|
+
field(t('fAutoConsolidate'), h('input', { type: 'checkbox', checked: cfg.autoConsolidate !== false, onChange: function (e) { set('autoConsolidate', e.target.checked) } }), t('fAutoConsolidateHint')),
|
|
6986
|
+
field(t('fConsolidate'), h('input', { 'data-dam-input': '', type: 'number', min: 5, value: cfg.autoConsolidateCooldownMinutes === undefined ? 30 : cfg.autoConsolidateCooldownMinutes, onChange: function (e) { set('autoConsolidateCooldownMinutes', Number(e.target.value) || 30) } }), t('fConsolidateHint')),
|
|
6987
|
+
field(t('fConsolidateMax'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 50, value: cfg.autoConsolidateDailyMax === undefined ? 8 : cfg.autoConsolidateDailyMax, onChange: function (e) { set('autoConsolidateDailyMax', Number(e.target.value) || 8) } }), t('fConsolidateMaxHint')),
|
|
6988
|
+
field(t('fMemFileIndex'), h('input', { type: 'checkbox', 'data-dam-key': 'memoryFileIndexEnabled', checked: cfg.memoryFileIndexEnabled === true, onChange: function (e) { set('memoryFileIndexEnabled', e.target.checked) } }), t('fMemFileIndexHint')),
|
|
6989
|
+
// 2026-09-14 解耦:交接白板与自动接续各自独立成项(旧实现只在白板页有接续按钮,且 enabled 还被
|
|
6990
|
+
// handoffEnabled 二次与运算)。两者出厂默认均为 false(功能仍在测试期),这里与白板页读写完全相同的配置键。
|
|
6991
|
+
]),
|
|
6992
|
+
section('skills', sectionLabels.skills, [
|
|
6993
|
+
h('div', { 'data-dam-hint': '' }, t('secMemoryHubHint')),
|
|
6994
|
+
field(t('fMemoryHub'), h('input', { type: 'checkbox', checked: !!cfg.memoryHubEnabled, onChange: function (e) { set('memoryHubEnabled', e.target.checked) } }), t('fMemoryHubHint')),
|
|
6995
|
+
field(t('fEpisodicMin'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 16, value: cfg.episodicMinSegments === undefined ? 2 : cfg.episodicMinSegments, onChange: function (e) { set('episodicMinSegments', Math.max(1, Math.min(16, Number(e.target.value) || 2))) } }), t('fEpisodicMinHint')),
|
|
6996
|
+
field(t('fProcSessions'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 20, value: cfg.procedureMinSessions === undefined ? 3 : cfg.procedureMinSessions, onChange: function (e) { set('procedureMinSessions', Math.max(1, Math.min(20, Number(e.target.value) || 3))) } }), t('fProcSessionsHint')),
|
|
6997
|
+
field(t('fProcSuccess'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 20, value: cfg.procedureMinSuccess === undefined ? 2 : cfg.procedureMinSuccess, onChange: function (e) { set('procedureMinSuccess', Math.max(1, Math.min(20, Number(e.target.value) || 2))) } }), t('fProcSuccessHint')),
|
|
6998
|
+
field(t('fProcCorr'), h('input', { 'data-dam-input': '', type: 'number', min: 0, max: 1, step: 0.05, value: cfg.procedureCorrectionCap === undefined ? 0.3 : cfg.procedureCorrectionCap, onChange: function (e) { set('procedureCorrectionCap', Math.max(0, Math.min(1, Number(e.target.value) || 0.3))) } }), t('fProcCorrHint')),
|
|
6999
|
+
field(t('fProcRisk'), h('input', { type: 'checkbox', checked: cfg.procedureHighRiskApproval !== false, onChange: function (e) { set('procedureHighRiskApproval', e.target.checked) } }), t('fProcRiskHint')),
|
|
7000
|
+
// 2026-09-22 补接线(设置页缺口审计 P0-1):procedureInjectEnabled 才是宿主真读的总闸
|
|
7001
|
+
// (procedure-switch.js 先判它);此前设置页只有旧键 ⇒ 用户改了也无效(灰控件)。
|
|
7002
|
+
field(t('fProcInject'), h('input', { type: 'checkbox', 'data-dam-key': 'procedureInjectEnabled', checked: cfg.procedureInjectEnabled !== false, onChange: function (e) { set('procedureInjectEnabled', e.target.checked) } }), t('fProcInjectHint')),
|
|
7003
|
+
// ★v3.1.3 补控件(引导审计 K-3):本键功能是通的(index.js 有 getter → memory-hub 消费),
|
|
7004
|
+
// 但此前**只有向导里有控件** ⇒ 用户在向导关掉后无处回改。与 procedureInjectEnabled 同节。
|
|
7005
|
+
field(locale === 'zh' ? '机械流程切片' : 'Mechanical flow slicing', h('input', { type: 'checkbox', 'data-dam-key': 'hubMechanicalProcedureFeedEnabled', checked: cfg.hubMechanicalProcedureFeedEnabled === true, onChange: function (e) { set('hubMechanicalProcedureFeedEnabled', e.target.checked) } }), locale === 'zh' ? '把情节意图机械截断成"技能"(无模型介入、可读性差)。关=只保留模型自己写的技能(推荐关)。' : 'Truncates episode intent into "skills" with no model involvement (poor readability). Off = only model-authored skills (recommended).'),
|
|
7006
|
+
field(t('fProcLevel'), h('select', { 'data-dam-select': '', value: cfg.procedureActiveLevel || 'checklist', onChange: function (e) { set('procedureActiveLevel', e.target.value) } },
|
|
7007
|
+
h('option', { value: 'checklist' }, locale === 'zh' ? 'checklist 完整步骤' : 'checklist (full steps)'),
|
|
7008
|
+
h('option', { value: 'excerpt' }, locale === 'zh' ? 'excerpt 摘要' : 'excerpt (summary)'),
|
|
7009
|
+
h('option', { value: 'hint' }, locale === 'zh' ? 'hint 仅提示' : 'hint (hint only)')), t('fProcLevelHint')),
|
|
7010
|
+
h('div', { 'data-dam-hint': '', style: { marginTop: '4px' } }, t('memoryHubViewHint')),
|
|
7011
|
+
]),
|
|
7012
|
+
section('handoff', sectionLabels.handoff, [
|
|
7013
|
+
field(t('fHandoff'), h('input', { type: 'checkbox', 'data-dam-key': 'handoffEnabled', checked: cfg.handoffEnabled !== false, onChange: function (e) { set('handoffEnabled', e.target.checked) } }), t('fHandoffHint')),
|
|
7014
|
+
field(t('fAutoContinue'), h('input', { type: 'checkbox', 'data-dam-key': 'autoContinueEnabled', checked: cfg.autoContinueEnabled !== false, onChange: function (e) { set('autoContinueEnabled', e.target.checked) } }), t('fAutoContinueHint')),
|
|
7015
|
+
field(t('fHandoffPlan'), h('input', { 'data-dam-input': '', type: 'number', min: 200, value: cfg.handoffPlanChars === undefined ? 1200 : cfg.handoffPlanChars, onChange: function (e) { set('handoffPlanChars', Number(e.target.value) || 1200) } }), t('fHandoffPlanHint')),
|
|
7016
|
+
// 2026-09-22 补接线(设置页缺口审计 P0-5):criteriaGate 此前只能改文件才能退。
|
|
7017
|
+
field(t('fCriteriaGate'), h('input', { type: 'checkbox', 'data-dam-key': 'criteriaGate', checked: cfg.criteriaGate !== false, onChange: function (e) { set('criteriaGate', e.target.checked) } }), t('fCriteriaGateHint')),
|
|
7018
|
+
field(t('fHandoffLedger'), h('input', { 'data-dam-input': '', type: 'number', min: 100, value: cfg.handoffLedgerChars === undefined ? 800 : cfg.handoffLedgerChars, onChange: function (e) { set('handoffLedgerChars', Number(e.target.value) || 800) } }), t('fHandoffLedgerHint')),
|
|
7019
|
+
field(t('fWaterWindow'), h('input', { 'data-dam-input': '', type: 'number', min: 0, value: cfg.waterLevelWindowTokens === undefined ? 65536 : cfg.waterLevelWindowTokens, onChange: function (e) { set('waterLevelWindowTokens', Number(e.target.value) || 0) } }), t('fWaterWindowHint')),
|
|
7020
|
+
field(t('fWaterThreshold'), h('input', { 'data-dam-input': '', type: 'number', min: 0.1, max: 1.5, step: 0.05, value: cfg.waterLevelThreshold === undefined ? 0.75 : cfg.waterLevelThreshold, onChange: function (e) { set('waterLevelThreshold', Number(e.target.value) || 0.75) } }), t('fWaterThresholdHint')),
|
|
7021
|
+
field(t('fWaterAdvisory'), h('input', { type: 'checkbox', checked: cfg.waterLevelAdvisory !== false, onChange: function (e) { set('waterLevelAdvisory', e.target.checked) } }), t('fWaterAdvisoryHint')),
|
|
7022
|
+
field(t('fWaterAuto'), h('input', { type: 'checkbox', checked: cfg.waterLevelAutoHandoff !== false, onChange: function (e) { set('waterLevelAutoHandoff', e.target.checked) } }), t('fWaterAutoHint')),
|
|
7023
|
+
field(locale === 'zh' ? '子代理痕迹回收' : 'Subagent trace recycle', h('input', { type: 'checkbox', checked: cfg.subagentGcEnabled !== false, onChange: function (e) { set('subagentGcEnabled', e.target.checked) } }),
|
|
7024
|
+
locale === 'zh' ? '本插件的一次性子代理(自动沉淀 / 时段总结 / 问候 / 蒸馏)每跑一次都会在 ~/.dsh/sessions 留下一个持久化会话;数量上千后会话列表会明显变慢。开启后任务一结束就把这些痕迹移入 ~/.dsh/subagent-gc-backup 备份(可回滚),不影响子代理结果。默认开。' : 'Every one-shot subagent this plugin spawns (auto-consolidation, scheduled summary, greeting, distillation) leaves a persisted session under ~/.dsh/sessions; hundreds of them slow the session list down. When on, each trace is moved into ~/.dsh/subagent-gc-backup right after the task ends (reversible), never affecting the subagent result. On by default.'),
|
|
7025
|
+
field(locale === 'zh' ? '兜底回收保留天数' : 'Fallback recycle keep days', h('input', { 'data-dam-input': '', type: 'number', min: 0, value: cfg.subagentGcKeepDays === undefined ? 3 : cfg.subagentGcKeepDays, onChange: function (e) { set('subagentGcKeepDays', Number(e.target.value) || 0) } }),
|
|
7026
|
+
locale === 'zh' ? '每天巡检一次,回收超过该天数仍残留的痕迹(比如任务异常中断没删掉的);0 = 不按时间,只靠任务结束即删。' : 'A daily sweep recycles traces older than this many days (e.g. leftovers from interrupted tasks); 0 = rely on the end-of-task recycle only.'),
|
|
7027
|
+
]),
|
|
7028
|
+
section('auto', sectionLabels.auto, [
|
|
7029
|
+
// 欢迎向导:开关(首启自动播放)+ 立即重看按钮(闭包内直调 openDialog——同一作用域,点击立即弹;
|
|
7030
|
+
// 不走 window 全局入口,避免多实例时序导致"点了没反应要刷新")+ 查看更新日志(走 update 弹窗,
|
|
7031
|
+
// 带 Logo 开场动画;versions 取 CHANGELOG 最新一条)
|
|
7032
|
+
field(t('fWelcomeTour'), h('div', { 'data-dam-row': '' },
|
|
7033
|
+
h('input', { type: 'checkbox', checked: cfg.welcomeTourEnabled !== false, onChange: function (e) { set('welcomeTourEnabled', e.target.checked) } }),
|
|
7034
|
+
h('button', { 'data-dam-btn': '', onClick: function () { try { openDialog({ kind: 'welcomeTour' }) } catch (eTour) {} } }, t('tourReplay')),
|
|
7035
|
+
h('button', { 'data-dam-btn': '', onClick: function () {
|
|
7036
|
+
try {
|
|
7037
|
+
var keys = Object.keys(CHANGELOG)
|
|
7038
|
+
if (!keys.length) return
|
|
7039
|
+
var latest = keys.sort(cmpVersion)[keys.length - 1]
|
|
7040
|
+
openDialog({ kind: 'update', versions: [{ version: latest, items: CHANGELOG[latest] }], currentVersion: latest })
|
|
7041
|
+
} catch (eLog) {}
|
|
7042
|
+
} }, locale === 'zh' ? '查看更新日志' : 'View changelog')), t('fWelcomeTourHint')),
|
|
7043
|
+
field(t('fAutoPopup'), h('input', { type: 'checkbox', checked: cfg.autoPopupEnabled !== false, onChange: function (e) { set('autoPopupEnabled', e.target.checked) } }), t('fAutoPopupHint')),
|
|
7044
|
+
field(t('fUnattended'), h('input', { type: 'checkbox', checked: !!cfg.unattendedMode, onChange: function (e) { set('unattendedMode', e.target.checked) } }), t('fUnattendedHint')),
|
|
7045
|
+
field(t('fUnattendedAuto'), h('input', { type: 'checkbox', checked: !!cfg.unattendedAuto, onChange: function (e) { set('unattendedAuto', e.target.checked) } }), t('fUnattendedAutoHint')),
|
|
7046
|
+
field(t('fAway'), h('input', { 'data-dam-input': '', type: 'number', min: 0, value: (Number(cfg.awayMinutes) === 0 ? 0 : (cfg.awayMinutes || 60)), onChange: function (e) { var v = Number(e.target.value); set('awayMinutes', Number.isFinite(v) && v >= 0 ? v : 60) } }), t('fAwayHint')),
|
|
7047
|
+
field(t('fAutoSum'), h('input', { 'data-dam-input': '', value: (cfg.autoSummaryTimes || []).join(','), onChange: function (e) { set('autoSummaryTimes', String(e.target.value || '').split(',').map(function (s) { return s.trim() }).filter(Boolean)) } }), t('fAutoSumHint')),
|
|
7048
|
+
field(t('fConsSchedule'), h('input', { type: 'checkbox', checked: cfg.consolidateScheduleEnabled !== false, onChange: function (e) { set('consolidateScheduleEnabled', e.target.checked) } }), t('fConsScheduleHint')),
|
|
7049
|
+
field(t('fConsScheduleTime'), h('input', { 'data-dam-input': '', placeholder: '09:30', value: cfg.consolidateScheduleTime === undefined ? '09:30' : cfg.consolidateScheduleTime, onChange: function (e) { set('consolidateScheduleTime', String(e.target.value || '').trim()) } }), t('fConsScheduleTimeHint')),
|
|
7050
|
+
field(t('fConsScheduleDays'), h('input', { 'data-dam-input': '', type: 'number', min: 1, max: 60, value: cfg.consolidateScheduleDays === undefined ? 7 : cfg.consolidateScheduleDays, onChange: function (e) { set('consolidateScheduleDays', Number(e.target.value) || 7) } }), t('fConsScheduleDaysHint')),
|
|
7051
|
+
field(t('fMaintSchedule'), h('input', { type: 'checkbox', checked: cfg.maintainScheduleEnabled !== false, onChange: function (e) { set('maintainScheduleEnabled', e.target.checked) } }), t('fMaintScheduleHint')),
|
|
7052
|
+
field(t('fMaintScheduleTime'), h('input', { 'data-dam-input': '', placeholder: '10:00', value: cfg.maintainScheduleTime === undefined ? '10:00' : cfg.maintainScheduleTime, onChange: function (e) { set('maintainScheduleTime', String(e.target.value || '').trim()) } }), t('fMaintScheduleTimeHint')),
|
|
7053
|
+
]),
|
|
7054
|
+
section('store', sectionLabels.store, [
|
|
6408
7055
|
field(t('fUserDir'), h('input', { 'data-dam-input': '', value: cfg.userMemoryDir, onChange: function (e) { set('userMemoryDir', e.target.value) } }), t('fUserDirHint')),
|
|
6409
7056
|
field(t('fProjectDir'), h('input', { 'data-dam-input': '', value: cfg.projectMemoryDir, onChange: function (e) { set('projectMemoryDir', e.target.value) } }), t('fProjectDirHint')),
|
|
6410
7057
|
field(t('fMemoryRoot'), h('div', { 'data-dam-row': '', style: { flex: 1 } },
|
|
@@ -6486,6 +7133,11 @@ window.__ModuleLoader__.load({
|
|
|
6486
7133
|
h('span', { style: { flex: 1 } }, verInfo ? (verInfo.current || '?') + (verInfo.latest ? ' → ' + verInfo.latest + (verInfo.upToDate ? ' ' + t('upToDate') : ' ' + t('hasUpdate')) : '') : (checkingUpdate ? t('checking') : '—')),
|
|
6487
7134
|
h('button', { 'data-dam-btn': '', onClick: checkUpdate, disabled: checkingUpdate }, checkingUpdate ? t('checking') : t('checkUpdate')),
|
|
6488
7135
|
(verInfo && verInfo.latest && !verInfo.upToDate && verInfo.installKind === 'registry') ? h('button', { 'data-dam-btn': '', onClick: doUpdate, disabled: upBusy, style: { marginLeft: '4px' } }, upBusy ? t('updating') : t('updateNow')) : null,
|
|
7136
|
+
// ★v3.1.2:dev-link(开发树挂载)无法「一键更新」,但也必须让用户看见线上是否有新版 ——
|
|
7137
|
+
// 此前此处恒为 null,用户既看不到按钮也看不到「有新版」,反馈「按钮消失且不知道为什么」。
|
|
7138
|
+
(verInfo && verInfo.installKind === 'dev-link' && verInfo.latest && verInfo.devVersion && verInfo.latest !== verInfo.devVersion)
|
|
7139
|
+
? h('span', { 'data-dam-hint': '', style: { marginLeft: '6px' } }, (locale === 'zh' ? '开发树 v' : 'dev tree v') + verInfo.devVersion + ' → ' + (locale === 'zh' ? '线上 v' : 'registry v') + verInfo.latest + (locale === 'zh' ? ',请同步开发源码后重新构建' : ' — sync source and rebuild'))
|
|
7140
|
+
: null,
|
|
6489
7141
|
upMsg ? h('span', { 'data-dam-hint': '' }, upMsg) : null),
|
|
6490
7142
|
t('versionCmdHint') + (verInfo && verInfo.error ? ' ' + t('versionError') + verInfo.error : '') + (verInfo && verInfo.installKind === 'dev-link' ? ' ' + t('devLinkHint') : '') + (verInfo && !verInfo.installKind ? ' ' + t('noProfileHint') : '')),
|
|
6491
7143
|
field(locale === 'zh' ? '交流群' : 'Community', h('div', { 'data-dam-row': '' },
|