@modusensus/dsh-mneme 0.7.31 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -27
- package/lib/api.js +139 -6
- package/lib/client.js +369 -32
- package/lib/commands.js +36 -5
- package/lib/config.js +101 -14
- package/lib/dream/decisions.js +147 -14
- package/lib/dream/sleep.js +84 -34
- package/lib/dream.js +250 -60
- package/lib/embedding.js +22 -2
- package/lib/index.js +68 -37
- package/lib/inject.js +54 -13
- package/lib/lang.js +168 -0
- package/lib/local-embedder.js +23 -9
- package/lib/mirror.js +17 -14
- package/lib/reranker.js +23 -8
- package/lib/runtime/adopt.js +176 -0
- package/lib/runtime/closure.js +172 -0
- package/lib/runtime/download.js +247 -0
- package/lib/runtime/layout.js +235 -0
- package/lib/runtime/loader.js +249 -0
- package/lib/runtime/provision.js +237 -0
- package/lib/runtime/tarball.js +130 -0
- package/lib/runtime/verify.js +191 -0
- package/lib/scope.js +83 -0
- package/lib/service.js +374 -55
- package/lib/settings.js +43 -2
- package/lib/store.js +126 -13
- package/lib/summarize.js +108 -35
- package/lib/tools.js +205 -12
- package/package.json +7 -2
- package/runtime-manifest.json +596 -0
- package/scripts/build-runtime-manifest.mjs +217 -0
- package/scripts/mneme-runtime.mjs +277 -0
- package/src/api.js +139 -6
- package/src/commands.js +36 -5
- package/src/config.js +101 -14
- package/src/dream/decisions.js +147 -14
- package/src/dream/sleep.js +84 -34
- package/src/dream.js +250 -60
- package/src/embedding.js +22 -2
- package/src/index.js +68 -37
- package/src/inject.js +54 -13
- package/src/lang.js +168 -0
- package/src/local-embedder.js +23 -9
- package/src/mirror.js +17 -14
- package/src/reranker.js +23 -8
- package/src/runtime/adopt.js +176 -0
- package/src/runtime/closure.js +172 -0
- package/src/runtime/download.js +247 -0
- package/src/runtime/layout.js +235 -0
- package/src/runtime/loader.js +249 -0
- package/src/runtime/provision.js +237 -0
- package/src/runtime/tarball.js +130 -0
- package/src/runtime/verify.js +191 -0
- package/src/scope.js +83 -0
- package/src/service.js +374 -55
- package/src/settings.js +43 -2
- package/src/store.js +126 -13
- package/src/summarize.js +108 -35
- package/src/tools.js +205 -12
- package/test/api.test.js +175 -9
- package/test/client.test.js +152 -10
- package/test/commands.test.js +40 -3
- package/test/config.test.js +17 -1
- package/test/dream-candidate-hybrid.test.js +281 -0
- package/test/dream.test.js +172 -0
- package/test/embedding-ready.test.js +196 -0
- package/test/helpers/runtime-source.js +60 -0
- package/test/helpers/tar-builder.js +80 -0
- package/test/inject.test.js +130 -1
- package/test/lang.test.js +87 -0
- package/test/quality-filter.test.js +68 -0
- package/test/reasoning-effort.test.js +106 -15
- package/test/reindex-backfill.test.js +62 -0
- package/test/runtime-adopt.test.js +206 -0
- package/test/runtime-cli.test.js +89 -0
- package/test/runtime-closure.test.js +193 -0
- package/test/runtime-download.test.js +302 -0
- package/test/runtime-layout.test.js +163 -0
- package/test/runtime-loader.test.js +237 -0
- package/test/runtime-manifest.test.js +158 -0
- package/test/runtime-provision.test.js +101 -0
- package/test/runtime-tarball.test.js +82 -0
- package/test/runtime-verify.test.js +214 -0
- package/test/scope-retrieval.test.js +197 -0
- package/test/scope-storage.test.js +247 -0
- package/test/scope-strict.test.js +165 -0
- package/test/service.test.js +76 -0
- package/test/sleep-action-set.test.js +422 -0
- package/test/store.test.js +14 -0
- package/test/summarize.test.js +224 -0
- package/test/tools.test.js +30 -5
- package/test/webServer-optional.test.js +34 -1
package/lib/client.js
CHANGED
|
@@ -105,6 +105,31 @@ window.__ModuleLoader__.load({
|
|
|
105
105
|
h("span", { className: "mneme-heatpct" }, `${pct}%`));
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* v0.8.0 A4(issue #17):scope 标注徽章——条目的归属来源一行摘要。
|
|
110
|
+
*
|
|
111
|
+
* 仅在条目带任一 scope 标注(agent_scope / workspace_scope / sensitivity)
|
|
112
|
+
* 时渲染;字段缺省返回 null(未标注条目视觉零变化,前端不感知开关状态,
|
|
113
|
+
* 与 HeatBadge 的「数据缺省即不渲染」同一模式)。tooltip 展示完整归属,
|
|
114
|
+
* 标签复用详情抽屉的 i18n 键(云端复验观察点:tooltip 曾硬编码英文)。
|
|
115
|
+
*
|
|
116
|
+
* @param {{m: object, t: (key: string) => string}} props
|
|
117
|
+
* @param {object} props.m 记忆条目(/list、/search 透出的 toApiList 形状,
|
|
118
|
+
* scope 四字段条件存在)
|
|
119
|
+
* @param {(key: string) => string} props.t i18n 取词函数(调用点闭包提供)
|
|
120
|
+
* @returns {object|null} 徽章 vnode;无任何标注时为 null
|
|
121
|
+
*/
|
|
122
|
+
const ScopeBadge = ({ m, t }) => {
|
|
123
|
+
if (!m || (!m.agent_scope && !m.workspace_scope && !m.sensitivity)) return null;
|
|
124
|
+
const tip = [
|
|
125
|
+
m.agent_scope ? `${t("memory.explorer.detail.agentScope")}: ${m.agent_scope}` : null,
|
|
126
|
+
m.workspace_scope ? `${t("memory.explorer.detail.workspaceScope")}: ${m.workspace_scope}` : null,
|
|
127
|
+
m.sensitivity ? `${t("memory.explorer.detail.sensitivity")}: ${m.sensitivity}` : null
|
|
128
|
+
].filter(Boolean).join(" · ");
|
|
129
|
+
return h("span", { className: "mneme-badge mneme-badge--scope", title: tip },
|
|
130
|
+
t("memory.explorer.scopeBadge"));
|
|
131
|
+
};
|
|
132
|
+
|
|
108
133
|
// Unified API fetcher: attaches the optional apiToken (set in the settings
|
|
109
134
|
// view, persisted in localStorage) as a Bearer header. When no token has
|
|
110
135
|
// been configured the header is omitted and the API stays open (default).
|
|
@@ -270,6 +295,7 @@ window.__ModuleLoader__.load({
|
|
|
270
295
|
"memory.features.group.core": "核心",
|
|
271
296
|
"memory.features.group.enhance": "记忆增强",
|
|
272
297
|
"memory.features.group.dream": "巩固与睡眠",
|
|
298
|
+
"memory.features.group.scope": "作用域隔离",
|
|
273
299
|
"memory.features.group.advanced": "高级",
|
|
274
300
|
"memory.features.advancedToggle": "高级(注入策略 · 反思 · 实验项)",
|
|
275
301
|
"memory.features.restartHint": "重启 DSH 后生效",
|
|
@@ -322,6 +348,10 @@ window.__ModuleLoader__.load({
|
|
|
322
348
|
"memory.features.bm25SearchEnabled.hint": "传统关键词打分检索,与向量召回互补",
|
|
323
349
|
"memory.features.conflictFreezeEnabled": "冲突冻结",
|
|
324
350
|
"memory.features.conflictFreezeEnabled.hint": "巩固发现互相矛盾的记忆时先冻结待确认",
|
|
351
|
+
"memory.features.scopeEnabled": "作用域标注",
|
|
352
|
+
"memory.features.scopeEnabled.hint": "按会话身份(agent/工作区)给新记忆打归属标注;重启 DSH 后生效",
|
|
353
|
+
"memory.features.strictScope": "严格隔离",
|
|
354
|
+
"memory.features.strictScope.hint": "检索与注入硬过滤:带他者作用域的记忆完全不可见(关闭时仅降权保留可见);重启 DSH 后生效",
|
|
325
355
|
"memory.features.trustEpistemicWeighting": "可信度加权",
|
|
326
356
|
"memory.features.trustEpistemicWeighting.hint": "按来源可信度调整召回排序(实验性)",
|
|
327
357
|
"memory.features.reflectionFailureTracking": "反思失败追踪",
|
|
@@ -374,6 +404,11 @@ window.__ModuleLoader__.load({
|
|
|
374
404
|
"memory.explorer.exportFailed": "导出失败",
|
|
375
405
|
"memory.explorer.conflictBadge": "冲突",
|
|
376
406
|
"memory.explorer.archivedBadge": "已归档",
|
|
407
|
+
"memory.explorer.scopeBadge": "作用域",
|
|
408
|
+
"memory.explorer.detail.agentScope": "Agent 作用域",
|
|
409
|
+
"memory.explorer.detail.workspaceScope": "工作区作用域",
|
|
410
|
+
"memory.explorer.detail.sensitivity": "敏感度",
|
|
411
|
+
"memory.explorer.detail.occurred": "发生时间",
|
|
377
412
|
"memory.explorer.detail.edit": "编辑",
|
|
378
413
|
"memory.explorer.detail.save": "保存",
|
|
379
414
|
"memory.explorer.detail.cancel": "取消",
|
|
@@ -394,6 +429,15 @@ window.__ModuleLoader__.load({
|
|
|
394
429
|
"memory.status.dreamNever": "尚未运行",
|
|
395
430
|
"memory.status.conflicts": "待确认冲突",
|
|
396
431
|
"memory.status.conflictsHint": "冻结的矛盾记忆,等待人工确认",
|
|
432
|
+
"memory.status.conflictQueue.reason": "原因",
|
|
433
|
+
"memory.status.conflictQueue.sideA": "A 方",
|
|
434
|
+
"memory.status.conflictQueue.sideB": "B 方",
|
|
435
|
+
"memory.status.conflictQueue.keepA": "保留 A",
|
|
436
|
+
"memory.status.conflictQueue.keepB": "保留 B",
|
|
437
|
+
"memory.status.conflictQueue.markReviewed": "仅标记已处理",
|
|
438
|
+
"memory.status.conflictQueue.applyHint": "确认后:保留方正文追加已否决注记,另一方归档。",
|
|
439
|
+
"memory.status.conflictQueue.missing": "(该记忆已不存在)",
|
|
440
|
+
"memory.status.conflictQueue.refresh": "刷新队列",
|
|
397
441
|
"memory.status.workbench": "工作动态",
|
|
398
442
|
"memory.status.dreamConsolidate": "记忆巩固",
|
|
399
443
|
"memory.status.summarize": "总结提炼",
|
|
@@ -427,7 +471,24 @@ window.__ModuleLoader__.load({
|
|
|
427
471
|
"memory.status.vectorOff": "未启用",
|
|
428
472
|
"memory.status.vectorInit": "初始化中",
|
|
429
473
|
"memory.status.vectorInitHint": "embedder 不可达,正在重试",
|
|
474
|
+
"memory.status.vectorRuntimeMissing": "缺少本地推理运行时",
|
|
475
|
+
"memory.status.vectorRuntimeHint": "本地推理运行时未就绪({status})",
|
|
476
|
+
"memory.status.vectorRuntimeCost": "本地向量化要额外一份本地推理运行时(transformers + onnxruntime 闭包,解包后数百 MB)。下面的按钮会先尝试收编本机已有的那份(同盘则硬链接,不占额外空间),没有再按固定清单从 npm 取回(逐个校验 sha512)。也可以直接让 agent 处理,或在终端里自己跑:node scripts/mneme-runtime.mjs status / adopt [--from <node_modules>] / verify(详见 docs/LOCAL_MODEL.md §2.5)。",
|
|
477
|
+
"memory.runtime.title": "本地推理运行时",
|
|
478
|
+
"memory.runtime.available": "已就绪",
|
|
479
|
+
"memory.runtime.missing": "未就绪 —— 只有用本地嵌入(embedProvider: local)时才需要它",
|
|
480
|
+
"memory.runtime.restart": "本地嵌入要重启 DSH 才会生效",
|
|
481
|
+
"memory.status.vectorRuntimeReady": "运行时已就绪",
|
|
482
|
+
"memory.status.vectorRuntimeReadyHint": "本地嵌入尚未初始化(进程里的 embedder 在运行时缺失时已经失败过一次):若一直停在这里,重启 DSH 即可。",
|
|
483
|
+
"memory.status.vectorRuntimeFetch": "取回本地运行时",
|
|
484
|
+
"memory.status.vectorRuntimeFetchBusy": "正在取回…(先试收编,必要时下载,数千个文件,请稍候)",
|
|
485
|
+
"memory.status.vectorRuntimeFetchAdopted": "已收编本机已有的运行时:{n} 个包 / {m} 个文件({mode})。**请重启 DSH 使本地嵌入生效。**",
|
|
486
|
+
"memory.status.vectorRuntimeFetchDownloaded": "下载完成:{n} 个包 / {m} 个文件。**请重启 DSH 使本地嵌入生效**(取回不会让当前进程里的 embedder 复活)。",
|
|
487
|
+
"memory.status.vectorRuntimeFetchFailed": "取回失败:{reason}",
|
|
430
488
|
"memory.status.vectorIndexed": "已索引 {n} / {m} 条",
|
|
489
|
+
"memory.status.vectorUnconfigured": "未配置",
|
|
490
|
+
"memory.status.vectorUnconfiguredHint": "未填 embedding 端点/模型或未启用,语义召回不可用",
|
|
491
|
+
"memory.status.vectorDegradedHint": "已索引 0 / {m} 条,语义召回实际不可用",
|
|
431
492
|
"memory.status.llm": "LLM 消耗",
|
|
432
493
|
"memory.status.llmCalls": "近 7 天 · {n} 次调用",
|
|
433
494
|
"memory.status.error": "加载失败"
|
|
@@ -575,6 +636,7 @@ window.__ModuleLoader__.load({
|
|
|
575
636
|
"memory.features.group.core": "Core",
|
|
576
637
|
"memory.features.group.enhance": "Enhancement",
|
|
577
638
|
"memory.features.group.dream": "Consolidation",
|
|
639
|
+
"memory.features.group.scope": "Scope isolation",
|
|
578
640
|
"memory.features.group.advanced": "Advanced",
|
|
579
641
|
"memory.features.advancedToggle": "Advanced (injection · reflection · experimental)",
|
|
580
642
|
"memory.features.restartHint": "Takes effect after restarting DSH",
|
|
@@ -627,6 +689,10 @@ window.__ModuleLoader__.load({
|
|
|
627
689
|
"memory.features.bm25SearchEnabled.hint": "Classic keyword scoring, complementary to vector recall",
|
|
628
690
|
"memory.features.conflictFreezeEnabled": "Conflict freezing",
|
|
629
691
|
"memory.features.conflictFreezeEnabled.hint": "Freeze contradictory memories for confirmation during consolidation",
|
|
692
|
+
"memory.features.scopeEnabled": "Scope tagging",
|
|
693
|
+
"memory.features.scopeEnabled.hint": "Stamp new memories with the writing session's identity (agent/workspace); takes effect after restarting DSH",
|
|
694
|
+
"memory.features.strictScope": "Strict isolation",
|
|
695
|
+
"memory.features.strictScope.hint": "Hard-filter search and injection: memories scoped to other agents/workspaces become invisible (off = demoted but visible); takes effect after restarting DSH",
|
|
630
696
|
"memory.features.trustEpistemicWeighting": "Credibility weighting",
|
|
631
697
|
"memory.features.trustEpistemicWeighting.hint": "Adjust recall ranking by source credibility (experimental)",
|
|
632
698
|
"memory.features.reflectionFailureTracking": "Reflection failure tracking",
|
|
@@ -679,6 +745,11 @@ window.__ModuleLoader__.load({
|
|
|
679
745
|
"memory.explorer.exportFailed": "Export failed",
|
|
680
746
|
"memory.explorer.conflictBadge": "Conflict",
|
|
681
747
|
"memory.explorer.archivedBadge": "Archived",
|
|
748
|
+
"memory.explorer.scopeBadge": "Scoped",
|
|
749
|
+
"memory.explorer.detail.agentScope": "Agent scope",
|
|
750
|
+
"memory.explorer.detail.workspaceScope": "Workspace scope",
|
|
751
|
+
"memory.explorer.detail.sensitivity": "Sensitivity",
|
|
752
|
+
"memory.explorer.detail.occurred": "Occurred",
|
|
682
753
|
"memory.explorer.detail.edit": "Edit",
|
|
683
754
|
"memory.explorer.detail.save": "Save",
|
|
684
755
|
"memory.explorer.detail.cancel": "Cancel",
|
|
@@ -699,6 +770,15 @@ window.__ModuleLoader__.load({
|
|
|
699
770
|
"memory.status.dreamNever": "Not yet run",
|
|
700
771
|
"memory.status.conflicts": "Pending conflicts",
|
|
701
772
|
"memory.status.conflictsHint": "Frozen contradictory memories awaiting confirmation",
|
|
773
|
+
"memory.status.conflictQueue.reason": "Reason",
|
|
774
|
+
"memory.status.conflictQueue.sideA": "Side A",
|
|
775
|
+
"memory.status.conflictQueue.sideB": "Side B",
|
|
776
|
+
"memory.status.conflictQueue.keepA": "Keep A",
|
|
777
|
+
"memory.status.conflictQueue.keepB": "Keep B",
|
|
778
|
+
"memory.status.conflictQueue.markReviewed": "Mark reviewed only",
|
|
779
|
+
"memory.status.conflictQueue.applyHint": "On confirm: a veto note is appended to the kept side and the other side is archived.",
|
|
780
|
+
"memory.status.conflictQueue.missing": "(memory no longer exists)",
|
|
781
|
+
"memory.status.conflictQueue.refresh": "Refresh queue",
|
|
702
782
|
"memory.status.workbench": "Activity",
|
|
703
783
|
"memory.status.dreamConsolidate": "Consolidation",
|
|
704
784
|
"memory.status.summarize": "Summarization",
|
|
@@ -732,7 +812,24 @@ window.__ModuleLoader__.load({
|
|
|
732
812
|
"memory.status.vectorOff": "Disabled",
|
|
733
813
|
"memory.status.vectorInit": "Initializing",
|
|
734
814
|
"memory.status.vectorInitHint": "embedder unreachable, retrying",
|
|
815
|
+
"memory.status.vectorRuntimeMissing": "Local inference runtime missing",
|
|
816
|
+
"memory.status.vectorRuntimeHint": "Local inference runtime not ready ({status})",
|
|
817
|
+
"memory.status.vectorRuntimeCost": "Local vectorization needs an extra local inference runtime (the transformers + onnxruntime closure, hundreds of MB unpacked). The button below first tries to adopt an existing copy on this machine (hardlinked when on the same volume, so no extra disk), and otherwise fetches it from npm against a pinned manifest (every tarball checked against its sha512). You can also just ask the agent, or run it yourself: node scripts/mneme-runtime.mjs status / adopt [--from <node_modules>] / verify (see docs/LOCAL_MODEL.md §2.5).",
|
|
818
|
+
"memory.runtime.title": "Local inference runtime",
|
|
819
|
+
"memory.runtime.available": "ready",
|
|
820
|
+
"memory.runtime.missing": "not ready — only needed when you use local embedding (embedProvider: local)",
|
|
821
|
+
"memory.runtime.restart": "Local embedding takes effect after restarting DSH",
|
|
822
|
+
"memory.status.vectorRuntimeReady": "Runtime ready",
|
|
823
|
+
"memory.status.vectorRuntimeReadyHint": "Local embedder is not initialized yet (the in-process embedder already failed while the runtime was missing): restart DSH if this persists.",
|
|
824
|
+
"memory.status.vectorRuntimeFetch": "Fetch local runtime",
|
|
825
|
+
"memory.status.vectorRuntimeFetchBusy": "Fetching… (tries adopt first, may download — thousands of files)",
|
|
826
|
+
"memory.status.vectorRuntimeFetchAdopted": "Adopted the runtime already on this machine: {n} packages / {m} files ({mode}). **Restart DSH to activate local embedding.**",
|
|
827
|
+
"memory.status.vectorRuntimeFetchDownloaded": "Download complete: {n} packages / {m} files. **Restart DSH to activate local embedding** (fetching does not revive the embedder already running in this process).",
|
|
828
|
+
"memory.status.vectorRuntimeFetchFailed": "Fetch failed: {reason}",
|
|
735
829
|
"memory.status.vectorIndexed": "Indexed {n} / {m} items",
|
|
830
|
+
"memory.status.vectorUnconfigured": "Not configured",
|
|
831
|
+
"memory.status.vectorUnconfiguredHint": "No embedding endpoint/model configured — semantic recall is off",
|
|
832
|
+
"memory.status.vectorDegradedHint": "Indexed 0 / {m} items — semantic recall is effectively unavailable",
|
|
736
833
|
"memory.status.llm": "LLM Usage",
|
|
737
834
|
"memory.status.llmCalls": "Last 7 days · {n} calls",
|
|
738
835
|
"memory.status.error": "Failed to load"
|
|
@@ -957,10 +1054,9 @@ window.__ModuleLoader__.load({
|
|
|
957
1054
|
".mneme-heat--warm{color:var(--dsw-alias-label-secondary)}",
|
|
958
1055
|
".mneme-heat--cold{color:var(--dsw-alias-label-tertiary)}",
|
|
959
1056
|
".mneme-heat--cold .mneme-heatpct{display:none}",
|
|
960
|
-
//
|
|
961
|
-
|
|
962
|
-
".mneme-topentry{
|
|
963
|
-
".mneme-topentry-native{width:100%;background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}",
|
|
1057
|
+
// 纵向 flex 容器按原生按钮的宽度与外边距分配空间,保留展开和收起态的对齐。
|
|
1058
|
+
".mneme-topentry{display:flex;flex-direction:column}",
|
|
1059
|
+
".mneme-topentry-native{background:var(--dsw-alias-bg-layer-1);border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary)}",
|
|
964
1060
|
".mneme-topentry-native:hover{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-primary)}",
|
|
965
1061
|
".mneme-topentry-native .mneme-topentry-label{white-space:nowrap}",
|
|
966
1062
|
// --- 功能开关:一行一开关,Claude 式安静排版 ---
|
|
@@ -987,6 +1083,8 @@ window.__ModuleLoader__.load({
|
|
|
987
1083
|
".mneme-featsub{margin:2px 0 8px;padding:10px 12px;border:1px solid var(--dsw-alias-border-l1);border-radius:10px;display:flex;flex-direction:column;gap:6px}",
|
|
988
1084
|
".mneme-featsub .mneme-featnum{padding:6px 0;border-bottom:none}",
|
|
989
1085
|
".mneme-featsubhint{font-size:12px;line-height:17px;color:var(--dsw-alias-label-tertiary)}",
|
|
1086
|
+
// 运行时那一块的说明文字:比常规 hint 再小一档,它是背景信息而不是要读的正文。
|
|
1087
|
+
".mneme-runtimehint{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary)}",
|
|
990
1088
|
// --- 交互式记忆库:工具栏 / 卡片网格 / 详情抽屉 / 更多菜单 ---
|
|
991
1089
|
// sheet 边距放宽:与窗口边缘保持呼吸距离,居中不顶满。
|
|
992
1090
|
// 视图切换(卡片/时间线)分段控件挂在子页栏右侧。
|
|
@@ -1008,6 +1106,20 @@ window.__ModuleLoader__.load({
|
|
|
1008
1106
|
".mneme-badge{flex:none;display:inline-flex;align-items:center;gap:4px;height:18px;padding:0 7px;border-radius:6px;font-size:11px;line-height:14px}",
|
|
1009
1107
|
".mneme-badge--conflict{color:var(--dsw-alias-state-error,#c33);background:color-mix(in srgb,var(--dsw-alias-state-error,#c33) 10%,transparent);border:1px solid color-mix(in srgb,var(--dsw-alias-state-error,#c33) 30%,transparent)}",
|
|
1010
1108
|
".mneme-badge--archived{color:var(--dsw-alias-label-tertiary);background:var(--dsw-alias-interactive-bg-hover)}",
|
|
1109
|
+
".mneme-badge--scope{color:var(--dsw-alias-state-business-primary);background:color-mix(in srgb,var(--dsw-alias-state-business-primary) 10%,transparent);border:1px solid color-mix(in srgb,var(--dsw-alias-state-business-primary) 30%,transparent)}",
|
|
1110
|
+
// 冲突集中处理队列(v0.8.0):状态页的待确认冲突列表
|
|
1111
|
+
".mneme-conflictq{display:flex;flex-direction:column;gap:10px}",
|
|
1112
|
+
".mneme-conflictq-head{display:flex;align-items:center;gap:8px}",
|
|
1113
|
+
".mneme-conflict-item{border:1px solid var(--dsw-alias-border,var(--dsw-alias-interactive-bg-hover));border-radius:10px;padding:10px;display:flex;flex-direction:column;gap:8px}",
|
|
1114
|
+
".mneme-conflict-reason{font-size:12px;line-height:17px;color:var(--dsw-alias-label-secondary)}",
|
|
1115
|
+
".mneme-conflict-pair{display:grid;grid-template-columns:1fr 1fr;gap:8px}",
|
|
1116
|
+
".mneme-conflict-side{border:1px solid var(--dsw-alias-border,var(--dsw-alias-interactive-bg-hover));border-radius:8px;padding:8px;display:flex;flex-direction:column;gap:4px;min-width:0}",
|
|
1117
|
+
".mneme-conflict-sidelabel{font-size:11px;color:var(--dsw-alias-label-tertiary)}",
|
|
1118
|
+
".mneme-conflict-sidetitle{font-size:13px;font-weight:600;color:var(--dsw-alias-label-primary);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}",
|
|
1119
|
+
".mneme-conflict-snippet{font-size:12px;line-height:17px;color:var(--dsw-alias-label-secondary);display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}",
|
|
1120
|
+
".mneme-conflict-missing{font-size:12px;color:var(--dsw-alias-label-tertiary)}",
|
|
1121
|
+
".mneme-conflict-actions{display:flex;align-items:center;gap:6px;flex-wrap:wrap}",
|
|
1122
|
+
".mneme-conflict-hint{font-size:11px;color:var(--dsw-alias-label-tertiary);margin-right:auto}",
|
|
1011
1123
|
// 详情抽屉:sheet 内右侧滑出,覆盖在浏览区之上
|
|
1012
1124
|
".mneme-xmain{position:relative}",
|
|
1013
1125
|
"@keyframes mneme-slidein{from{opacity:0;transform:translateX(16px)}to{opacity:1;transform:translateX(0)}}",
|
|
@@ -1620,7 +1732,9 @@ window.__ModuleLoader__.load({
|
|
|
1620
1732
|
const FEATURE_GROUPS = [
|
|
1621
1733
|
{ key: "group.core", items: ["autoInject", "autoSummarize", "hotMemoryEnabled", "memoryQualityFilter.enabled", "llmAudit.enabled"] },
|
|
1622
1734
|
{ key: "group.enhance", items: ["entityExtractionEnabled", "codingRetrospect", "rerankEnabled", "searchSemanticDedup", "bm25SearchEnabled"] },
|
|
1623
|
-
{ key: "group.dream", items: ["autoDream", "sleepModeEnabled"] }
|
|
1735
|
+
{ key: "group.dream", items: ["autoDream", "sleepModeEnabled"] },
|
|
1736
|
+
// v0.8.0 A4(issue #17):作用域隔离组——标注总开关 + 严格硬过滤。
|
|
1737
|
+
{ key: "group.scope", items: ["scopeEnabled", "strictScope"] }
|
|
1624
1738
|
];
|
|
1625
1739
|
const FEATURE_ADVANCED_BOOLS = ["hybridInject", "selectiveInjectEnabled", "adaptiveThresholdEnabled", "reflectionUpdateEnabled", "reflectionFailureTracking", "conflictFreezeEnabled", "trustEpistemicWeighting"];
|
|
1626
1740
|
// 字符串键(blur/Enter 提交,空串合法 = 跟随默认):巩固模型与语义
|
|
@@ -1891,6 +2005,7 @@ window.__ModuleLoader__.load({
|
|
|
1891
2005
|
savedTick && h("span", { className: "mneme-saved", style: { marginLeft: 8, fontWeight: 400 } }, t("memory.features.restartHint"))
|
|
1892
2006
|
),
|
|
1893
2007
|
h("div", { className: "mneme-set-desc" }, t("memory.features.desc")),
|
|
2008
|
+
h(RuntimeProvisionBlock, { t }),
|
|
1894
2009
|
state === null && !error
|
|
1895
2010
|
? h("div", { className: "mneme-set-hint" }, "…")
|
|
1896
2011
|
: h(react.Fragment, null,
|
|
@@ -2586,8 +2701,89 @@ window.__ModuleLoader__.load({
|
|
|
2586
2701
|
}
|
|
2587
2702
|
|
|
2588
2703
|
// 向量索引 — whether semantic recall is switched on.
|
|
2704
|
+
// 取回本地推理运行时的共享块:**面板状态卡片与设置页共用同一份实现**。
|
|
2705
|
+
//
|
|
2706
|
+
// 为什么设置页也要有:状态卡片是「看到了顺手点」,而设置页是用户主动去找的地方。
|
|
2707
|
+
// 两处都给出代价说明与可复制的命令行,并且在**成功后明确要求重启** —— 取回运行时不会让
|
|
2708
|
+
// 当前进程里那个已经失败的 embedder 复活,重启才生效;不说清这一点,用户会以为功能坏了。
|
|
2709
|
+
function RuntimeProvisionBlock({ t, onChanged }) {
|
|
2710
|
+
const [runtime, setRuntime] = useState(null);
|
|
2711
|
+
const [ready, setReady] = useState(null);
|
|
2712
|
+
const [busy, setBusy] = useState(false);
|
|
2713
|
+
const [msg, setMsg] = useState("");
|
|
2714
|
+
const [tick, setTick] = useState(0);
|
|
2715
|
+
|
|
2716
|
+
useEffect(() => {
|
|
2717
|
+
let cancelled = false;
|
|
2718
|
+
apiFetch("/api/dsh-mneme/semantic")
|
|
2719
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
2720
|
+
.then((j) => {
|
|
2721
|
+
if (cancelled || !j) return;
|
|
2722
|
+
setRuntime(j.localRuntime ?? null);
|
|
2723
|
+
setReady(j.ready ?? null);
|
|
2724
|
+
})
|
|
2725
|
+
.catch(() => {});
|
|
2726
|
+
return () => { cancelled = true; };
|
|
2727
|
+
}, [tick]);
|
|
2728
|
+
|
|
2729
|
+
const available = runtime?.status === "available";
|
|
2730
|
+
// 运行时就绪但 embedder 没起来 = 刚取回到、还没重启。这时唯一有意义的动作就是重启。
|
|
2731
|
+
const needsRestart = available && ready !== true;
|
|
2732
|
+
|
|
2733
|
+
const provision = async () => {
|
|
2734
|
+
setBusy(true);
|
|
2735
|
+
setMsg("");
|
|
2736
|
+
try {
|
|
2737
|
+
const res = await apiFetch("/api/dsh-mneme/runtime/provision", {
|
|
2738
|
+
method: "POST",
|
|
2739
|
+
headers: { "Content-Type": "application/json" },
|
|
2740
|
+
body: JSON.stringify({ overwrite: runtime?.status === "broken" })
|
|
2741
|
+
});
|
|
2742
|
+
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
2743
|
+
const j = await res.json();
|
|
2744
|
+
if (j?.ok) {
|
|
2745
|
+
const key = j.strategy === "download"
|
|
2746
|
+
? "memory.status.vectorRuntimeFetchDownloaded"
|
|
2747
|
+
: "memory.status.vectorRuntimeFetchAdopted";
|
|
2748
|
+
setMsg(t(key).replace("{n}", j.packages ?? 0).replace("{m}", j.files ?? 0).replace("{mode}", j.materialize ?? "?"));
|
|
2749
|
+
} else {
|
|
2750
|
+
setMsg(t("memory.status.vectorRuntimeFetchFailed").replace("{reason}", j?.reason ?? j?.status ?? "?"));
|
|
2751
|
+
}
|
|
2752
|
+
setTick((n) => n + 1);
|
|
2753
|
+
if (onChanged) onChanged();
|
|
2754
|
+
} catch (error) {
|
|
2755
|
+
setMsg(t("memory.status.vectorRuntimeFetchFailed").replace("{reason}", String((error && error.message) || error)));
|
|
2756
|
+
} finally {
|
|
2757
|
+
setBusy(false);
|
|
2758
|
+
}
|
|
2759
|
+
};
|
|
2760
|
+
|
|
2761
|
+
// 配好之后就把整块收起来:设置页只在「需要用户知道或行动」时占地方,配好了就是纯噪音。
|
|
2762
|
+
// (msg 例外:刚点完按钮要看结果,那一刻 ready 还没变,本来也不会命中这条。)
|
|
2763
|
+
if (available && ready === true && msg === "") return null;
|
|
2764
|
+
|
|
2765
|
+
return h(react.Fragment, null,
|
|
2766
|
+
h("div", { className: "mneme-runtimehint" },
|
|
2767
|
+
t("memory.runtime.title") + ":" + (available ? t("memory.runtime.available") : t("memory.runtime.missing"))),
|
|
2768
|
+
// 未就绪时才给代价说明与按钮;已就绪只留上面那行状态(以及在需要重启时下面那行提醒)。
|
|
2769
|
+
available ? null : h(react.Fragment, null,
|
|
2770
|
+
h("div", { className: "mneme-runtimehint" }, t("memory.status.vectorRuntimeCost")),
|
|
2771
|
+
h("button", {
|
|
2772
|
+
type: "button",
|
|
2773
|
+
className: "mneme-footbtn",
|
|
2774
|
+
disabled: busy,
|
|
2775
|
+
onClick: provision
|
|
2776
|
+
}, busy ? t("memory.status.vectorRuntimeFetchBusy") : t("memory.status.vectorRuntimeFetch"))
|
|
2777
|
+
),
|
|
2778
|
+
msg ? h("div", { className: "mneme-runtimehint" }, msg) : null,
|
|
2779
|
+
needsRestart ? h("div", { className: "mneme-saved" }, t("memory.runtime.restart")) : null
|
|
2780
|
+
);
|
|
2781
|
+
}
|
|
2589
2782
|
function VectorStatusCard({ t }) {
|
|
2590
|
-
const [state, setState] = useState({ loading: true, error: false, provider: null, ready: null, dimension: 0, embedded: 0, total: 0 });
|
|
2783
|
+
const [state, setState] = useState({ loading: true, error: false, provider: null, ready: null, configured: null, degraded: false, dimension: 0, embedded: 0, total: 0, localRuntime: null });
|
|
2784
|
+
// 收编是写操作、耗时数秒,所以要有忙碌态与就地结果;reload 只是用来让上面的
|
|
2785
|
+
// 加载 useEffect 重跑一次,从而把最新状态拉回来。
|
|
2786
|
+
const [reload, setReload] = useState(0);
|
|
2591
2787
|
useEffect(() => {
|
|
2592
2788
|
let cancelled = false;
|
|
2593
2789
|
// #118: /vector-config is secret-bearing (401 without a stored token →
|
|
@@ -2603,34 +2799,77 @@ window.__ModuleLoader__.load({
|
|
|
2603
2799
|
error: false,
|
|
2604
2800
|
provider: j?.embedProvider || null,
|
|
2605
2801
|
ready: j?.ready ?? null,
|
|
2802
|
+
// issue #135: missing on older servers → null/false,卡片退化成
|
|
2803
|
+
// 旧行为(只按 ready 判断),不会误报「未配置」。
|
|
2804
|
+
configured: typeof j?.configured === "boolean" ? j.configured : null,
|
|
2805
|
+
degraded: j?.degraded === true,
|
|
2606
2806
|
// Legacy OpenAI embedder exposes dimension only after its first
|
|
2607
2807
|
// successful embed — fall back to the index stats' dimension.
|
|
2608
2808
|
dimension: Number(j?.dimension ?? j?.index?.dimension ?? 0),
|
|
2609
2809
|
embedded: Number(j?.index?.embeddedCount ?? 0),
|
|
2610
|
-
total: Number(j?.index?.totalCount ?? 0)
|
|
2810
|
+
total: Number(j?.index?.totalCount ?? 0),
|
|
2811
|
+
// issue #131: 自管运行时状态。与 provider 无关地拿来,由下面决定是否要展示。
|
|
2812
|
+
localRuntime: j?.localRuntime ?? null
|
|
2611
2813
|
});
|
|
2612
2814
|
})
|
|
2613
|
-
.catch(() => { if (!cancelled) setState({ loading: false, error: true, provider: null, ready: null, dimension: 0, embedded: 0 }); });
|
|
2815
|
+
.catch(() => { if (!cancelled) setState({ loading: false, error: true, provider: null, ready: null, dimension: 0, embedded: 0, localRuntime: null }); });
|
|
2614
2816
|
return () => { cancelled = true; };
|
|
2615
|
-
}, []);
|
|
2817
|
+
}, [reload]);
|
|
2616
2818
|
const off = !state.provider;
|
|
2617
|
-
|
|
2819
|
+
// issue #135: 先把「没配」和「配了但在初始化」分开。此前两者都落到
|
|
2820
|
+
// ready !== true,于是未配置的 legacy embedder(ready 恒 true)反而
|
|
2821
|
+
// 一路显示成正常状态。
|
|
2822
|
+
const unconfigured = !off && state.configured === false;
|
|
2823
|
+
const pending = !off && !unconfigured && state.ready !== true;
|
|
2824
|
+
// 配好了、库里有东西、却一条都没嵌上 —— 这才是真正的降级。
|
|
2825
|
+
const degraded = !off && !unconfigured && !pending && state.degraded === true;
|
|
2826
|
+
// 本地 provider 缺运行时:绝不能显示「初始化中」——它永远不会初始化,只会一直重试,
|
|
2827
|
+
// 而那正是那份运行时(数百 MB)缺位的真实原因。这里必须说真话并给出下一步。
|
|
2828
|
+
const localBlocked = /^Local/.test(state.provider || "") && state.localRuntime?.status !== "available" && state.localRuntime != null;
|
|
2829
|
+
// 运行时已就绪但 embedder 还没起来 —— 这就是「刚取回运行时、但进程里的 embedder 早已失败」的过渡态。
|
|
2830
|
+
// 这时候继续说「embedder 不可达,正在重试」会让人以为永远好不了;要说的是可操作的那句:重启 DSH。
|
|
2831
|
+
const runtimeReadyPending = pending && state.localRuntime?.status === "available";
|
|
2618
2832
|
const num = off
|
|
2619
2833
|
? t("memory.status.vectorOff")
|
|
2620
|
-
:
|
|
2621
|
-
? t("memory.status.
|
|
2622
|
-
:
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2834
|
+
: localBlocked
|
|
2835
|
+
? t("memory.status.vectorRuntimeMissing")
|
|
2836
|
+
: unconfigured
|
|
2837
|
+
? t("memory.status.vectorUnconfigured")
|
|
2838
|
+
: runtimeReadyPending
|
|
2839
|
+
? t("memory.status.vectorRuntimeReady")
|
|
2840
|
+
: pending
|
|
2841
|
+
? t("memory.status.vectorInit")
|
|
2842
|
+
: `${state.provider.replace(/Embedder$/, "")} · ${state.dimension}D`;
|
|
2843
|
+
const cap = localBlocked
|
|
2844
|
+
? t("memory.status.vectorRuntimeHint").replace("{status}", state.localRuntime.status)
|
|
2845
|
+
: unconfigured
|
|
2846
|
+
? t("memory.status.vectorUnconfiguredHint")
|
|
2847
|
+
: runtimeReadyPending
|
|
2848
|
+
? t("memory.status.vectorRuntimeReadyHint")
|
|
2849
|
+
: pending
|
|
2850
|
+
? t("memory.status.vectorInitHint")
|
|
2851
|
+
: off ? ""
|
|
2852
|
+
: degraded
|
|
2853
|
+
? t("memory.status.vectorDegradedHint").replace("{m}", state.total)
|
|
2854
|
+
: t("memory.status.vectorIndexed").replace("{n}", state.embedded).replace("{m}", state.total);
|
|
2855
|
+
|
|
2856
|
+
|
|
2857
|
+
// 取回运行时由共享组件负责(设置页用的是同一份实现,见 RuntimeProvisionBlock):
|
|
2858
|
+
// 代价说明、三档来源、结果文案与「要重启」都在那里 —— 同一件事不该有两份代码。
|
|
2859
|
+
return h(react.Fragment, null,
|
|
2860
|
+
h(StatusCard, {
|
|
2861
|
+
t,
|
|
2862
|
+
title: t("memory.status.vector"),
|
|
2863
|
+
loading: state.loading,
|
|
2864
|
+
error: state.error,
|
|
2865
|
+
num,
|
|
2866
|
+
cap
|
|
2867
|
+
}),
|
|
2868
|
+
!state.loading && !state.error && localBlocked
|
|
2869
|
+
? h("div", { className: "mneme-statuscap", style: { marginTop: "6px" } },
|
|
2870
|
+
h(RuntimeProvisionBlock, { t, onChanged: () => setReload((n) => n + 1) }))
|
|
2871
|
+
: null
|
|
2872
|
+
);
|
|
2634
2873
|
}
|
|
2635
2874
|
|
|
2636
2875
|
// LLM 消耗 — calls + tokens over the trailing 7 days.
|
|
@@ -2696,6 +2935,62 @@ window.__ModuleLoader__.load({
|
|
|
2696
2935
|
);
|
|
2697
2936
|
}
|
|
2698
2937
|
|
|
2938
|
+
// --- 冲突集中处理队列(v0.8.0,状态页)---------------------------------
|
|
2939
|
+
// 此前冻结冲突只有计数与散落徽章,resolveConflictPending 无任何调用方——
|
|
2940
|
+
// 这里是第一处理入口:并排展示双方内容 + reason,人工选保留方后走
|
|
2941
|
+
// POST /conflicts/resolve(service 端按 dream 非冻结 conflict 同款处置)。
|
|
2942
|
+
// 队列为空时整块不渲染(不打扰无冲突实例)。
|
|
2943
|
+
function ConflictsQueue({ t }) {
|
|
2944
|
+
const [items, setItems] = useState(null);
|
|
2945
|
+
const [busy, setBusy] = useState(false);
|
|
2946
|
+
const load = () => {
|
|
2947
|
+
apiFetch("/api/dsh-mneme/conflicts")
|
|
2948
|
+
.then((res) => { if (!res.ok) throw new Error("http"); return res.json(); })
|
|
2949
|
+
.then((d) => setItems(d.items || []))
|
|
2950
|
+
.catch(() => setItems([]));
|
|
2951
|
+
};
|
|
2952
|
+
useEffect(() => { load(); }, []);
|
|
2953
|
+
if (!items || items.length === 0) return null;
|
|
2954
|
+
const resolve = (id, winner) => {
|
|
2955
|
+
setBusy(true);
|
|
2956
|
+
apiFetch("/api/dsh-mneme/conflicts/resolve", {
|
|
2957
|
+
method: "POST",
|
|
2958
|
+
headers: { "Content-Type": "application/json" },
|
|
2959
|
+
body: JSON.stringify({ id, winner, apply: winner !== null })
|
|
2960
|
+
})
|
|
2961
|
+
.then((res) => { if (!res.ok) throw new Error("http"); return res.json(); })
|
|
2962
|
+
.then(() => load())
|
|
2963
|
+
.catch(() => {})
|
|
2964
|
+
.finally(() => setBusy(false));
|
|
2965
|
+
};
|
|
2966
|
+
const side = (s, label) => h("div", { className: "mneme-conflict-side" },
|
|
2967
|
+
h("div", { className: "mneme-conflict-sidelabel" }, label),
|
|
2968
|
+
s.missing
|
|
2969
|
+
? h("div", { className: "mneme-conflict-missing" }, t("memory.status.conflictQueue.missing"))
|
|
2970
|
+
: h(react.Fragment, null,
|
|
2971
|
+
h("div", { className: "mneme-conflict-sidetitle", title: s.title }, s.title || "…"),
|
|
2972
|
+
h("div", { className: "mneme-conflict-snippet" }, (s.content || "").slice(0, 140)),
|
|
2973
|
+
s.archived && h("span", { className: "mneme-badge mneme-badge--archived" }, t("memory.explorer.archivedBadge")))
|
|
2974
|
+
);
|
|
2975
|
+
return h("div", { className: "mneme-conflictq" },
|
|
2976
|
+
h("div", { className: "mneme-conflictq-head" },
|
|
2977
|
+
h("span", { className: "mneme-xcount" }, t("memory.status.conflicts")),
|
|
2978
|
+
h("button", { className: "mneme-footbtn", onClick: () => load() },
|
|
2979
|
+
h(Icon, { name: "refresh", size: 12 }), t("memory.status.conflictQueue.refresh"))),
|
|
2980
|
+
items.map((it) => h("div", { key: it.id, className: "mneme-conflict-item" },
|
|
2981
|
+
it.reason && h("div", { className: "mneme-conflict-reason" },
|
|
2982
|
+
`${t("memory.status.conflictQueue.reason")}: ${it.reason}`),
|
|
2983
|
+
h("div", { className: "mneme-conflict-pair" },
|
|
2984
|
+
side(it.memory_a, t("memory.status.conflictQueue.sideA")),
|
|
2985
|
+
side(it.memory_b, t("memory.status.conflictQueue.sideB"))),
|
|
2986
|
+
h("div", { className: "mneme-conflict-actions" },
|
|
2987
|
+
h("span", { className: "mneme-conflict-hint" }, t("memory.status.conflictQueue.applyHint")),
|
|
2988
|
+
h("button", { className: "mneme-footbtn", disabled: busy, onClick: () => resolve(it.id, "a") }, t("memory.status.conflictQueue.keepA")),
|
|
2989
|
+
h("button", { className: "mneme-footbtn", disabled: busy, onClick: () => resolve(it.id, "b") }, t("memory.status.conflictQueue.keepB")),
|
|
2990
|
+
h("button", { className: "mneme-footbtn", disabled: busy, onClick: () => resolve(it.id, null) }, t("memory.status.conflictQueue.markReviewed"))))
|
|
2991
|
+
));
|
|
2992
|
+
}
|
|
2993
|
+
|
|
2699
2994
|
// --- 状态页工作台:让用户切实看见插件在干活 ---
|
|
2700
2995
|
// 活动流合并 llm_audit 里 autoDream/autoSummarize 的后台调用(状态、
|
|
2701
2996
|
// token、沉淀条数);沉淀记忆列表用 audit 行携带的 related_memory_ids
|
|
@@ -2886,6 +3181,7 @@ window.__ModuleLoader__.load({
|
|
|
2886
3181
|
h(DreamStatusCards, { t }),
|
|
2887
3182
|
h(HeatStatusCard, { t })
|
|
2888
3183
|
),
|
|
3184
|
+
h(ConflictsQueue, { t }),
|
|
2889
3185
|
h(WorkbenchSection, { t, onBrowse })
|
|
2890
3186
|
);
|
|
2891
3187
|
}
|
|
@@ -2979,6 +3275,18 @@ window.__ModuleLoader__.load({
|
|
|
2979
3275
|
memory.source && h(react.Fragment, null,
|
|
2980
3276
|
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.source")),
|
|
2981
3277
|
h("span", { className: "mneme-dmetaval", title: memory.source }, memory.source)),
|
|
3278
|
+
memory.occurred_at && h(react.Fragment, null,
|
|
3279
|
+
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.detail.occurred")),
|
|
3280
|
+
h("span", { className: "mneme-dmetaval", title: memory.occurred_at }, formatDateShort(memory.occurred_at))),
|
|
3281
|
+
memory.agent_scope && h(react.Fragment, null,
|
|
3282
|
+
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.detail.agentScope")),
|
|
3283
|
+
h("span", { className: "mneme-dmetaval", title: memory.agent_scope }, memory.agent_scope)),
|
|
3284
|
+
memory.workspace_scope && h(react.Fragment, null,
|
|
3285
|
+
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.detail.workspaceScope")),
|
|
3286
|
+
h("span", { className: "mneme-dmetaval", title: memory.workspace_scope }, memory.workspace_scope)),
|
|
3287
|
+
memory.sensitivity && h(react.Fragment, null,
|
|
3288
|
+
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.detail.sensitivity")),
|
|
3289
|
+
h("span", { className: "mneme-dmetaval", title: memory.sensitivity }, memory.sensitivity)),
|
|
2982
3290
|
memory.quality_score != null && h(react.Fragment, null,
|
|
2983
3291
|
h("span", { className: "mneme-dmetakey" }, t("memory.explorer.detail.quality")),
|
|
2984
3292
|
h("span", { className: "mneme-dmetaval" }, String(memory.quality_score))),
|
|
@@ -3640,6 +3948,7 @@ window.__ModuleLoader__.load({
|
|
|
3640
3948
|
h("div", { className: "mneme-cardfoot" },
|
|
3641
3949
|
h(ImportanceStars, { value: m.importance || 0, size: 12 }),
|
|
3642
3950
|
h(HeatBadge, { value: m.heat }),
|
|
3951
|
+
h(ScopeBadge, { m, t }),
|
|
3643
3952
|
m.source && h("span", { className: "mneme-cardsrc", title: m.source }, m.source)
|
|
3644
3953
|
)
|
|
3645
3954
|
)),
|
|
@@ -3701,7 +4010,8 @@ window.__ModuleLoader__.load({
|
|
|
3701
4010
|
},
|
|
3702
4011
|
h("span", { className: "mneme-xdot", style: { color: memoryTypeColor(m.type) }, title: typeLabel(t, m.type), "aria-hidden": "true" }),
|
|
3703
4012
|
h("span", { className: "mneme-xtime" }, time),
|
|
3704
|
-
h("span", { className: "mneme-xname" }, m.title || m.content?.slice(0, 40))
|
|
4013
|
+
h("span", { className: "mneme-xname" }, m.title || m.content?.slice(0, 40)),
|
|
4014
|
+
h(ScopeBadge, { m, t })
|
|
3705
4015
|
);
|
|
3706
4016
|
})
|
|
3707
4017
|
))
|
|
@@ -3742,8 +4052,10 @@ window.__ModuleLoader__.load({
|
|
|
3742
4052
|
|
|
3743
4053
|
// --- Sidebar entry: 工作区上方的记忆按钮 ---
|
|
3744
4054
|
// 注册仍在 sidebar.footer.action(list 插槽)——它既是 React 树的挂载
|
|
3745
|
-
// 锚点,也是 portal 失效时的原位回退。真实按钮 portal
|
|
3746
|
-
//
|
|
4055
|
+
// 锚点,也是 portal 失效时的原位回退。真实按钮 portal 插到「新会话」行
|
|
4056
|
+
// 之后、插件入口组的最前:任务看板/技能中心等生态插件也把入口插在新会话
|
|
4057
|
+
// 行后(它们的后插入者在上),若我们固定在 regionArea 之前就会被打成
|
|
4058
|
+
// 入口组末尾的孤立位(issue #130),所以观察器发现错位就搬回锚点位。
|
|
3747
4059
|
// 几何对齐方案:读取宿主「新会话」按钮的实时 className 原样套用,再用
|
|
3748
4060
|
// 我们的修饰类覆盖配色(次级观感)。这样展开态与收起态(rail)都直接
|
|
3749
4061
|
// 继承宿主自己的盒模型与间距——宿主改版/缩进动画零位移,无需硬编码。
|
|
@@ -3770,6 +4082,32 @@ window.__ModuleLoader__.load({
|
|
|
3770
4082
|
const findNative = (region) =>
|
|
3771
4083
|
region.parentElement.querySelector('[class*="newSession"]')
|
|
3772
4084
|
|| region.previousElementSibling;
|
|
4085
|
+
// 占位锚点:新会话行(现役外壳里 newSession 按钮嵌在 logoRow 内,
|
|
4086
|
+
// 旧外壳是根的直接子按钮)。判定与生态插件的 sidebar-entry-core
|
|
4087
|
+
// 一致;锚点不可靠时回退到 regionArea 之前的旧位置。
|
|
4088
|
+
const findAnchor = (region) => {
|
|
4089
|
+
const parent = region.parentElement;
|
|
4090
|
+
const btn = parent.querySelector('[class*="newSession"]');
|
|
4091
|
+
if (!btn) return null;
|
|
4092
|
+
const row = btn.closest('[class*="logoRow"]');
|
|
4093
|
+
if (row && row.parentElement === parent) return row;
|
|
4094
|
+
return btn.parentElement === parent ? btn : null;
|
|
4095
|
+
};
|
|
4096
|
+
// 占位规则:紧跟新会话行(插件入口组的顶部),幂等——已在锚点位就
|
|
4097
|
+
// 不动 DOM。生态插件后插入时会把我们压下去一位,观察器里重排搬回;
|
|
4098
|
+
// 它们只在自身节点被移除时才重插,不会与我们来回争抢。
|
|
4099
|
+
// 注意 created 已紧跟锚点时 target 即 created 自身,必须直接返回:
|
|
4100
|
+
// insertBefore(x, x) 在 Chromium 里不是 no-op,会触发 mutation 造成
|
|
4101
|
+
// 观察器自激风暴、冻死整个 SPA。
|
|
4102
|
+
const place = (region) => {
|
|
4103
|
+
const parent = region.parentElement;
|
|
4104
|
+
if (!parent) return;
|
|
4105
|
+
const anchor = findAnchor(region);
|
|
4106
|
+
const target = anchor ? anchor.nextSibling : region;
|
|
4107
|
+
if (target === created) return;
|
|
4108
|
+
if (created.parentElement === parent && created.nextSibling === target) return;
|
|
4109
|
+
parent.insertBefore(created, target);
|
|
4110
|
+
};
|
|
3773
4111
|
const attempt = () => {
|
|
3774
4112
|
const region = document.querySelector('[class*="regionArea"]');
|
|
3775
4113
|
if (region && region.parentElement) {
|
|
@@ -3779,12 +4117,13 @@ window.__ModuleLoader__.load({
|
|
|
3779
4117
|
// 否则 portal 按钮会停留在捕获时刻的旧类上(宽度/对齐失配)。
|
|
3780
4118
|
created = document.createElement("div");
|
|
3781
4119
|
created.dataset.pluginEntry = "@modusensus/dsh-mneme";
|
|
3782
|
-
|
|
4120
|
+
place(region);
|
|
3783
4121
|
setHost(created);
|
|
3784
4122
|
setNativeCls(findNative(region)?.className || "");
|
|
3785
4123
|
mo = new MutationObserver(() => {
|
|
3786
4124
|
const cur = document.querySelector('[class*="regionArea"]');
|
|
3787
4125
|
if (!cur || !cur.parentElement) return;
|
|
4126
|
+
place(cur);
|
|
3788
4127
|
const cls = findNative(cur)?.className || "";
|
|
3789
4128
|
setNativeCls((prev) => (prev === cls ? prev : cls));
|
|
3790
4129
|
});
|
|
@@ -3807,10 +4146,8 @@ window.__ModuleLoader__.load({
|
|
|
3807
4146
|
};
|
|
3808
4147
|
}, []);
|
|
3809
4148
|
if (!host) return fallback;
|
|
3810
|
-
// wrapper 用 display:contents 隐身:我们的按钮成为侧边栏弹性布局的
|
|
3811
|
-
// 直接子元素,与「新会话」按钮同级同距;展开/收起只切换图标与文案。
|
|
3812
4149
|
return reactDom.createPortal(
|
|
3813
|
-
h("div", { className: "mneme-topentry"
|
|
4150
|
+
h("div", { className: "mneme-topentry" },
|
|
3814
4151
|
h("button", {
|
|
3815
4152
|
type: "button",
|
|
3816
4153
|
className: `${nativeCls} mneme-topentry-native`.trim(),
|
|
@@ -3901,4 +4238,4 @@ window.__ModuleLoader__.load({
|
|
|
3901
4238
|
exports.inject = inject;
|
|
3902
4239
|
return module.exports;
|
|
3903
4240
|
}
|
|
3904
|
-
});
|
|
4241
|
+
});
|