@modusensus/dsh-mneme 0.9.2 → 0.9.4
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 +11 -7
- package/docs/CONFIGURATION.md +9 -2
- package/docs/WORKFLOWS.md +70 -3
- package/lib/api.js +178 -3
- package/lib/capabilities.js +1 -1
- package/lib/client.js +315 -27
- package/lib/config.js +70 -3
- package/lib/dream.js +1 -1
- package/lib/index.js +21 -1
- package/lib/inject.js +10 -25
- package/lib/lang.js +25 -0
- package/lib/organize.js +4 -2
- package/lib/pipeline-nudge-auto.js +126 -0
- package/lib/pipeline-nudge.js +337 -28
- package/lib/pipeline-runs.js +1 -1
- package/lib/pipeline.js +4 -4
- package/lib/profile-extract.js +161 -0
- package/lib/scope.js +30 -1
- package/lib/service.js +3 -2
- package/lib/settings.js +22 -14
- package/lib/summarize.js +64 -3
- package/lib/tools.js +240 -1
- package/lib/user-settings.js +78 -0
- package/package.json +1 -1
- package/src/api.js +178 -3
- package/src/capabilities.js +1 -1
- package/src/config.js +70 -3
- package/src/dream.js +1 -1
- package/src/index.js +21 -1
- package/src/inject.js +10 -25
- package/src/lang.js +25 -0
- package/src/organize.js +4 -2
- package/src/pipeline-nudge-auto.js +126 -0
- package/src/pipeline-nudge.js +337 -28
- package/src/pipeline-runs.js +1 -1
- package/src/pipeline.js +4 -4
- package/src/profile-extract.js +161 -0
- package/src/scope.js +30 -1
- package/src/service.js +3 -2
- package/src/settings.js +22 -14
- package/src/summarize.js +64 -3
- package/src/tools.js +240 -1
- package/src/user-settings.js +78 -0
- package/test/api-nudge-route.test.js +259 -0
- package/test/api.test.js +19 -1
- package/test/client.test.js +115 -7
- package/test/lang.test.js +11 -0
- package/test/organize-tool.test.js +232 -0
- package/test/persona-addressing.test.js +83 -0
- package/test/persona-settings.test.js +30 -8
- package/test/pipeline-nudge-auto.test.js +224 -0
- package/test/pipeline-nudge.test.js +161 -6
- package/test/profile-extract.test.js +269 -0
- package/test/scope-optional-service.test.js +110 -0
- package/test/session-usage.test.js +11 -2
- package/test/summarize.test.js +225 -0
- package/test/tools-dto-schema-matrix.test.js +48 -5
- package/test/tools.test.js +6 -1
- package/test/user-settings-preview.test.js +127 -0
package/lib/client.js
CHANGED
|
@@ -281,6 +281,19 @@ window.__ModuleLoader__.load({
|
|
|
281
281
|
"memory.settings.profileHint": "写一段自我介绍(角色、背景、偏好),Agent 每轮对话都会自动带上。",
|
|
282
282
|
"memory.settings.profileSave": "保存画像",
|
|
283
283
|
"memory.settings.profileSaved": "画像已保存",
|
|
284
|
+
"memory.settings.profileExtract": "从记忆提取草稿",
|
|
285
|
+
"memory.settings.profileExtracting": "正在提取…",
|
|
286
|
+
"memory.settings.profileExtractHint": "会花一次模型调用,只在你点这个按钮时发生;提取出来的是草稿,不采用就不进库,你手写的画像也不会被覆盖。",
|
|
287
|
+
"memory.settings.profileExtractDisabled": "没有提取:功能开关里的「画像提取草稿」还没打开。",
|
|
288
|
+
"memory.settings.profileExtractEmpty": "库里没有可用的记忆条目,这次没有能提炼的内容(空输入下硬写一段画像只会是编的)。",
|
|
289
|
+
"memory.settings.profileExtractNoRoute": "没有可用的模型:先在宿主里选一个默认模型(或在配置里填 dreamProvider / dreamModel),再点一次。",
|
|
290
|
+
"memory.settings.profileSaveFailed": "画像没存进去",
|
|
291
|
+
"memory.settings.profileExtractFailed": "提取没成:模型没按格式返回,或这次调用失败了。你现有的画像一个字都没被改动。",
|
|
292
|
+
"memory.settings.profileDraftTitle": "草稿(还没保存)",
|
|
293
|
+
"memory.settings.profileDraftRows": "依据记忆条数",
|
|
294
|
+
"memory.settings.profileDraftTruncated": "条目较多,这次只读了前面一部分",
|
|
295
|
+
"memory.settings.profileDraftApply": "采用为画像",
|
|
296
|
+
"memory.settings.profileDraftDiscard": "丢弃草稿",
|
|
284
297
|
"memory.settings.persona": "人格定义(beta)",
|
|
285
298
|
"memory.settings.personaHint": "给这位 Agent 一张角色卡:名字、性格、回复要求。开关打开且下方「人格定义」总闸开启时,每轮对话都会注入。",
|
|
286
299
|
"memory.settings.personaName": "名字",
|
|
@@ -294,7 +307,7 @@ window.__ModuleLoader__.load({
|
|
|
294
307
|
"memory.settings.personaSaveFailed": "保存失败",
|
|
295
308
|
"memory.settings.personaEnabledToggle": "启用这张卡",
|
|
296
309
|
"memory.settings.personaEnabledOn": "已启用——身份命中的会话会注入这张卡",
|
|
297
|
-
"memory.settings.personaEnabledOff": "
|
|
310
|
+
"memory.settings.personaEnabledOff": "未启用——默认口径下这个身份的会话会改用默认卡;开着「严格人格寻址」时则完全不注入人格",
|
|
298
311
|
"memory.settings.personaDefaultCard": "默认卡(兜底)",
|
|
299
312
|
"memory.settings.personaAdd": "添加卡",
|
|
300
313
|
"memory.settings.personaAddPlaceholder": "输入 agent 身份名(如 nyx),按它寻址注入",
|
|
@@ -302,10 +315,21 @@ window.__ModuleLoader__.load({
|
|
|
302
315
|
"memory.settings.personaEditingFor": "正在编辑身份卡:",
|
|
303
316
|
"memory.settings.personaDeleteCard": "删除这张卡",
|
|
304
317
|
"memory.settings.personaView": "Agent 眼中的你(注入生效文本)",
|
|
305
|
-
"memory.settings.personaViewHint": "
|
|
306
|
-
"memory.settings.personaViewEmpty": "
|
|
318
|
+
"memory.settings.personaViewHint": "下面是这一轮真正会送进模型的整段文本:人格 → 画像 → 规则(后写的覆盖前写的,规则最硬)。文本由插件渲染,前端只显示。",
|
|
319
|
+
"memory.settings.personaViewEmpty": "三项都是空的——Agent 每轮只会看到记忆库。",
|
|
320
|
+
"memory.settings.personaViewUnknown": "读不到注入文本(预览请求失败或后端版本过旧)。这里不显示猜测的内容。",
|
|
321
|
+
"memory.settings.personaViewSourceDefault": "当前按「解析不到会话身份」的会话预览,取默认卡。",
|
|
322
|
+
"memory.settings.personaViewSourceFallback": "这个身份解析到了,但它没有可用的卡(没建或已关掉),所以落回默认卡——默认口径下这正是「关掉这张卡」不生效的原因;打开「严格人格寻址」后这种情况改为不注入人格。",
|
|
323
|
+
"memory.settings.personaViewSourceCard": "当前按你正在编辑的这张身份卡预览。",
|
|
324
|
+
"memory.settings.personaViewSourceNone": "按当前寻址口径,这个身份不会注入任何人格——默认卡只兜「解析不到会话身份」的会话。",
|
|
325
|
+
"memory.settings.personaViewGateOff": "总闸「人格定义(beta)」现在是关的:下面这段里不会出现人格,只有画像与规则。",
|
|
326
|
+
"memory.settings.personaViewRefresh": "刷新预览",
|
|
307
327
|
"memory.features.personaEnabled": "人格定义(beta)",
|
|
308
328
|
"memory.features.personaEnabled.hint": "把「设置 → 个性化」里的人格卡注入到每轮对话。默认关(beta);总闸与人格卡内的开关同时打开才生效。",
|
|
329
|
+
"memory.features.personaStrictAddressing": "严格人格寻址",
|
|
330
|
+
"memory.features.personaStrictAddressing.hint": "开:会话身份没有对应人格卡、或那张卡被关掉时,就不注入人格——默认卡只兜「解析不到会话身份」的会话。宿主里跑着自带身份的 bot(DS Bot 那类)时关掉这种兜底,免得两句「你是谁」撞在同一轮。默认关 = 未命中就落默认卡(历史行为)。",
|
|
331
|
+
"memory.features.profileExtractEnabled": "画像提取草稿",
|
|
332
|
+
"memory.features.profileExtractEnabled.hint": "打开「设置 → 个性化 → 用户画像」里的「从记忆提取草稿」按钮:读一批活跃记忆、花一次模型调用,产一段「助手眼中的用户」草稿。草稿要你点「采用」才会写进画像,绝不自动覆盖你手写的原文。轻量档预设关(用户显式勾选仍然赢)。",
|
|
309
333
|
"memory.settings.rules": "规则",
|
|
310
334
|
"memory.settings.rulesHint": "给 Agent 立下必须遵守的规矩,随时增删,下一轮即生效。",
|
|
311
335
|
"memory.settings.ruleAdd": "添加规则",
|
|
@@ -531,12 +555,12 @@ window.__ModuleLoader__.load({
|
|
|
531
555
|
"memory.features.heatEnabled.hint": "开:像遗忘曲线一样给记忆降温,长期没用过的往后排、不占注入名额;关:一直按相关度排(热度衰减,默认关)",
|
|
532
556
|
"memory.features.documentMemoryEnabled": "长文档只存一张摘要卡",
|
|
533
557
|
"memory.features.documentMemoryEnabled.hint": "开:Agent 写的长文档在记忆里只放「摘要卡」,正文仍由 Agent 保管、按需读取,注入只带摘要;关:整篇都往上下文里带(document 型记忆,默认关)",
|
|
534
|
-
// --- v0.9.0 M.O.(Modus Operandi
|
|
558
|
+
// --- v0.9.0 M.O.(Modus Operandi,beta)---
|
|
535
559
|
// M.O. = Modus Operandi,拉丁语「一个人一贯的做事方式」;也是 org 词根
|
|
536
560
|
// modus(modusensus)。界面主显示 M.O.,设置项 hint 第一句给全名。
|
|
537
561
|
"memory.actions.beta": "beta",
|
|
538
562
|
"memory.actions.notice": "提炼结果是候选,可能有错、过时、会被推翻。",
|
|
539
|
-
"memory.actions.title": "M.O. ·
|
|
563
|
+
"memory.actions.title": "M.O. · 工作流",
|
|
540
564
|
"memory.actions.open": "打开 M.O.",
|
|
541
565
|
"memory.actions.viewFull": "完整视图",
|
|
542
566
|
"memory.actions.openPane": "在右栏打开完整视图",
|
|
@@ -588,13 +612,24 @@ window.__ModuleLoader__.load({
|
|
|
588
612
|
"memory.actions.broken": "有文件读不出来:",
|
|
589
613
|
"memory.actions.empty.title": "这个项目里还没有工作流",
|
|
590
614
|
"memory.actions.empty.body": "工作流的本体住在项目里的 .mneme/pipelines/ 下——跟着仓库走,能被提交、能被 review、能被 CI 读。做法被复用第二次才算节点类型,第一次只是实例;同一路径被走通两次之后,它才会从会话里被提炼出来、落到那个目录里。",
|
|
615
|
+
"memory.actions.distill.action": "现在沉淀一次",
|
|
616
|
+
"memory.actions.distill.busy": "正在读会话…",
|
|
617
|
+
"memory.actions.distill.hint": "看一遍这个项目自己的会话日志,把反复走通的动作挖成提议。只写提议,不落草稿、不改仓库。",
|
|
618
|
+
"memory.actions.distill.noProject": "读不到会话的工作目录,所以不知道该看哪个项目的日志",
|
|
619
|
+
"memory.actions.distill.saved": "已提出 {n} 条提议(本次挖到 {m} 条候选)",
|
|
620
|
+
"memory.actions.distill.none": "这一遍没挖到新的重复动作",
|
|
621
|
+
"memory.actions.distill.noSessions": "这个项目还没有自己的会话日志,先攒几轮再来",
|
|
622
|
+
"memory.actions.distill.unreadable": "会话日志读不出来(这是环境问题,不是「没有重复动作」)",
|
|
623
|
+
"memory.actions.distill.failed": "这一遍没跑完,详见插件日志",
|
|
591
624
|
"memory.actions.noRuns": "这一条还没有运行记录,所以每个节点都还是候选。",
|
|
592
625
|
"memory.actions.settle.title": "本次运行",
|
|
593
626
|
"memory.actions.settle.line": "「{title}」",
|
|
594
627
|
"memory.actions.settle.used": "本次复用 {n} 条做法(勾选为被用上)",
|
|
595
628
|
"memory.actions.settle.refutedWarn": "本次路径里有已被推翻的节点,结果仅供参考",
|
|
596
629
|
"memory.features.actionPipelineEnabled": "M.O.(beta)",
|
|
597
|
-
"memory.features.actionPipelineEnabled.hint": "
|
|
630
|
+
"memory.features.actionPipelineEnabled.hint": "只读视图:把真实会话里反复走通的动作连成一张图谱——会话头图标、右栏完整视图、每次运行的结算卡。看 ≠ 写:沉淀由「M.O. 自动沉淀」单独开,执行记账由 /mo-run 单独开。Modus Operandi(工作流)。重启 DSH 后生效",
|
|
631
|
+
"memory.features.actionNudgeEnabled": "自动沉淀工作流(beta)",
|
|
632
|
+
"memory.features.actionNudgeEnabled.hint": "开:每轮结束后看一眼这个项目自己的会话日志,把反复走通的动作挖成记忆库里的一条「提议」,等你确认才可能变成工作流。只产草稿、不自动启用、不往仓库里塞文件。挂热路径且要解压日志,轻量档会强制关掉。重启 DSH 后生效",
|
|
598
633
|
"memory.features.resilientModelDownload": "模型下载断了能续上",
|
|
599
634
|
"memory.features.resilientModelDownload.hint": "开:下载中断后从断点续传并自动重试;关:中断就整份重来(断点续传,默认开)",
|
|
600
635
|
"memory.features.searchSemanticDedup": "搜到意思相同的只留一条",
|
|
@@ -964,6 +999,19 @@ window.__ModuleLoader__.load({
|
|
|
964
999
|
"memory.settings.profileHint": "Describe yourself once — the agent reads it every turn.",
|
|
965
1000
|
"memory.settings.profileSave": "Save Profile",
|
|
966
1001
|
"memory.settings.profileSaved": "Profile saved",
|
|
1002
|
+
"memory.settings.profileExtract": "Draft from memory",
|
|
1003
|
+
"memory.settings.profileExtracting": "Extracting…",
|
|
1004
|
+
"memory.settings.profileExtractHint": "Costs one model call, and only when you press this button. The result is a draft: nothing is stored until you apply it, and your hand-written profile is never overwritten.",
|
|
1005
|
+
"memory.settings.profileExtractDisabled": "Nothing extracted: the “Profile draft from memory” feature switch is still off.",
|
|
1006
|
+
"memory.settings.profileExtractEmpty": "No usable memory entries in the store, so there was nothing to draft from (writing a profile out of an empty input would only be invention).",
|
|
1007
|
+
"memory.settings.profileExtractNoRoute": "No model available: pick a default model in the host (or set dreamProvider / dreamModel), then try again.",
|
|
1008
|
+
"memory.settings.profileSaveFailed": "Profile was not saved",
|
|
1009
|
+
"memory.settings.profileExtractFailed": "Extraction failed: the model did not return the required shape, or the call errored. Your existing profile is untouched.",
|
|
1010
|
+
"memory.settings.profileDraftTitle": "Draft (not saved)",
|
|
1011
|
+
"memory.settings.profileDraftRows": "Based on entries",
|
|
1012
|
+
"memory.settings.profileDraftTruncated": "many entries — only the first batch was read",
|
|
1013
|
+
"memory.settings.profileDraftApply": "Apply as profile",
|
|
1014
|
+
"memory.settings.profileDraftDiscard": "Discard draft",
|
|
967
1015
|
"memory.settings.persona": "Persona (beta)",
|
|
968
1016
|
"memory.settings.personaHint": "Give this agent a character card: name, personality, reply style. Injected every turn when the toggle below and the \"Persona\" master switch are both on.",
|
|
969
1017
|
"memory.settings.personaName": "Name",
|
|
@@ -977,7 +1025,7 @@ window.__ModuleLoader__.load({
|
|
|
977
1025
|
"memory.settings.personaSaveFailed": "Save failed",
|
|
978
1026
|
"memory.settings.personaEnabledToggle": "Enable this card",
|
|
979
1027
|
"memory.settings.personaEnabledOn": "On — sessions matching this identity get this card",
|
|
980
|
-
"memory.settings.personaEnabledOff": "Off —
|
|
1028
|
+
"memory.settings.personaEnabledOff": "Off — by default such sessions fall back to the default card; with strict persona addressing they get no persona at all",
|
|
981
1029
|
"memory.settings.personaDefaultCard": "Default (fallback)",
|
|
982
1030
|
"memory.settings.personaAdd": "Add card",
|
|
983
1031
|
"memory.settings.personaAddPlaceholder": "Agent identity (e.g. nyx) used to address injection",
|
|
@@ -985,10 +1033,21 @@ window.__ModuleLoader__.load({
|
|
|
985
1033
|
"memory.settings.personaEditingFor": "Editing identity card:",
|
|
986
1034
|
"memory.settings.personaDeleteCard": "Delete this card",
|
|
987
1035
|
"memory.settings.personaView": "You, as the agent sees you (injected text)",
|
|
988
|
-
"memory.settings.personaViewHint": "
|
|
989
|
-
"memory.settings.personaViewEmpty": "
|
|
1036
|
+
"memory.settings.personaViewHint": "This is the exact block handed to the model this turn: persona → profile → rules (later lines override earlier ones; rules are hardest). The plugin renders it; the panel only displays it.",
|
|
1037
|
+
"memory.settings.personaViewEmpty": "All three are empty — the agent only sees the memory store.",
|
|
1038
|
+
"memory.settings.personaViewUnknown": "Could not read the injected text (preview request failed or backend too old). Nothing is guessed here.",
|
|
1039
|
+
"memory.settings.personaViewSourceDefault": "Preview for sessions whose identity cannot be resolved — the default card applies.",
|
|
1040
|
+
"memory.settings.personaViewSourceFallback": "This identity resolved, but it has no usable card (never created, or switched off), so the default card applies — which is exactly why turning this card off changes nothing under the default mode. With strict persona addressing on, such a session gets no persona at all.",
|
|
1041
|
+
"memory.settings.personaViewSourceCard": "Preview for the identity card you are editing.",
|
|
1042
|
+
"memory.settings.personaViewSourceNone": "Under the current addressing mode this identity injects no persona at all — the default card only covers sessions with no resolvable identity.",
|
|
1043
|
+
"memory.settings.personaViewGateOff": "The master switch “Persona (beta)” is off: the block below contains profile and rules only.",
|
|
1044
|
+
"memory.settings.personaViewRefresh": "Refresh preview",
|
|
990
1045
|
"memory.features.personaEnabled": "Persona (beta)",
|
|
991
1046
|
"memory.features.personaEnabled.hint": "Inject the persona card (Settings → Personalization) into every turn. Off by default (beta); both this switch and the card's own toggle must be on.",
|
|
1047
|
+
"memory.features.personaStrictAddressing": "Strict persona addressing",
|
|
1048
|
+
"memory.features.personaStrictAddressing.hint": "On: when a session's identity has no persona card — or that card is switched off — no persona is injected, so the default card only covers sessions with no resolvable identity. Turn this on when the host runs a bot that brings its own identity (DS Bot and friends), otherwise two \"you are ...\" claims land in the same turn. Off (default): unresolved identities fall back to the default card, as before.",
|
|
1049
|
+
"memory.features.profileExtractEnabled": "Profile draft from memory",
|
|
1050
|
+
"memory.features.profileExtractEnabled.hint": "Adds the \"Draft from memory\" button to Settings → Personalization → profile: reads a batch of active memories, spends one model call, and returns an \"as the assistant sees them\" draft. Nothing is written until you press Apply, so your hand-written profile is never overwritten automatically. Off in the lightweight preset (an explicit choice still wins).",
|
|
992
1051
|
"memory.settings.rules": "Rules",
|
|
993
1052
|
"memory.settings.rulesHint": "Rules the agent must follow — add or remove anytime, effective next turn.",
|
|
994
1053
|
"memory.settings.ruleAdd": "Add Rule",
|
|
@@ -1254,13 +1313,24 @@ window.__ModuleLoader__.load({
|
|
|
1254
1313
|
"memory.actions.broken": "Some files could not be read:",
|
|
1255
1314
|
"memory.actions.empty.title": "No workflows in this project yet",
|
|
1256
1315
|
"memory.actions.empty.body": "A workflow lives in the project, under .mneme/pipelines/ — it travels with the repository: committable, reviewable, readable by CI. A practice becomes a node type on its second reuse; the first run is just an instance. Once the same path has been walked twice it gets distilled out of the session and lands in that directory.",
|
|
1316
|
+
"memory.actions.distill.action": "Distill now",
|
|
1317
|
+
"memory.actions.distill.busy": "Reading sessions…",
|
|
1318
|
+
"memory.actions.distill.hint": "Scan this project's own session logs and mine repeated actions into proposals. Writes proposals only — no drafts, no repository changes.",
|
|
1319
|
+
"memory.actions.distill.noProject": "The session's working directory is unknown, so there is no project to scan",
|
|
1320
|
+
"memory.actions.distill.saved": "{n} proposal(s) written ({m} candidate(s) mined this pass)",
|
|
1321
|
+
"memory.actions.distill.none": "No new repeated action this pass",
|
|
1322
|
+
"memory.actions.distill.noSessions": "This project has no session logs of its own yet — come back after a few turns",
|
|
1323
|
+
"memory.actions.distill.unreadable": "The session logs could not be read (an environment problem, not an empty result)",
|
|
1324
|
+
"memory.actions.distill.failed": "This pass did not finish; see the plugin log",
|
|
1257
1325
|
"memory.actions.noRuns": "No runs recorded for this workflow yet, so every step is still a candidate.",
|
|
1258
1326
|
"memory.actions.settle.title": "This run",
|
|
1259
1327
|
"memory.actions.settle.line": "\"{title}\"",
|
|
1260
1328
|
"memory.actions.settle.used": "Reused {n} steps this run (checked = actually used)",
|
|
1261
1329
|
"memory.actions.settle.refutedWarn": "This path contains refuted steps; take the result with caution",
|
|
1262
1330
|
"memory.features.actionPipelineEnabled": "M.O. (beta)",
|
|
1263
|
-
"memory.features.actionPipelineEnabled.hint": "
|
|
1331
|
+
"memory.features.actionPipelineEnabled.hint": "Read-only view: the ways of working that actually got walked through in real sessions, drawn as a graph — session-header icon, full sidebar view, and a per-run settlement card. Viewing is not writing: distillation has its own switch below, and run accounting has its own (/mo-run). Modus Operandi. Takes effect after restarting DSH",
|
|
1332
|
+
"memory.features.actionNudgeEnabled": "Auto-distill workflows (beta)",
|
|
1333
|
+
"memory.features.actionNudgeEnabled.hint": "On: after each turn, look at this project's own session logs and mine the actions it keeps repeating into a proposal in the memory store — it still needs your sign-off before becoming a workflow. Drafts only; never auto-activates; never writes files into the repo. Hooks a hot path and decompresses logs, so light mode forces it off. Takes effect after restarting DSH",
|
|
1264
1334
|
"memory.features.resilientModelDownload": "Model downloads resume after interruption",
|
|
1265
1335
|
"memory.features.resilientModelDownload.hint": "On: downloads continue from where they stopped and retry automatically. Off: an interrupted download starts over (resumable downloads, on by default)",
|
|
1266
1336
|
"memory.features.searchSemanticDedup": "Keep one entry when search hits near-duplicates",
|
|
@@ -2351,6 +2421,17 @@ window.__ModuleLoader__.load({
|
|
|
2351
2421
|
".mneme-pipe-wfMeta{display:block;margin-top:3px;font-size:12px;color:var(--mneme-fg-dim)}",
|
|
2352
2422
|
".mneme-pipe-wfId{display:block;margin-top:2px;font-size:11px;color:var(--mneme-fg-dim)}",
|
|
2353
2423
|
".mneme-pipe-broken{flex:none;font-size:12px;line-height:17px;color:var(--mneme-warn);padding:6px 14px 0}",
|
|
2424
|
+
// 手动沉淀:按钮与回执占一行。回执用 --mneme-fg-dim 而不是 --mneme-warn——
|
|
2425
|
+
// 「这一遍没挖到」是正常结果,不是警告;只有「读不出来」才是故障,那也由文案说清,
|
|
2426
|
+
// 不靠颜色区分(红黄两色在深浅主题里最容易一起掉对比度)。
|
|
2427
|
+
".mneme-pipe-distill{flex:none;display:flex;align-items:center;gap:8px;flex-wrap:wrap;font-size:12px}",
|
|
2428
|
+
".mneme-pipe-distillBtn{border:1px solid var(--dsw-alias-border-l2);background:0 0;border-radius:8px;cursor:pointer;color:var(--mneme-fg-dim);font-family:inherit;font-size:12px;line-height:16px;padding:2px 9px}",
|
|
2429
|
+
".mneme-pipe-distillBtn:hover:enabled{background:var(--dsw-alias-interactive-bg-hover)}",
|
|
2430
|
+
".mneme-pipe-distillBtn:focus-visible{outline:2px solid var(--dsw-alias-state-business-primary);outline-offset:1px}",
|
|
2431
|
+
// 禁用态用虚线边而不是降透明度:整块降 opacity 会把文字一起压到 AA 以下,
|
|
2432
|
+
// 而「为什么禁用」本来就由 title 说明(读不到会话工作目录)。
|
|
2433
|
+
".mneme-pipe-distillBtn:disabled{cursor:not-allowed;border-style:dashed;color:var(--mneme-fg-dim)}",
|
|
2434
|
+
".mneme-pipe-distillNote{color:var(--mneme-fg-dim);line-height:17px;min-width:0}",
|
|
2354
2435
|
].join("\n");
|
|
2355
2436
|
if (typeof document !== "undefined" && document.querySelector(`style[data-plugin-css="${CSS_TAG}"]`) === null) {
|
|
2356
2437
|
const tag = document.createElement("style");
|
|
@@ -2923,11 +3004,15 @@ window.__ModuleLoader__.load({
|
|
|
2923
3004
|
// (关=一个座位都不注册,见 apply 内的 M.O. 块)。独立成组只解决「混在
|
|
2924
3005
|
// enhance 末位」;可见层由 FEATURE_FIRST_SCREEN 决定(见其注释),
|
|
2925
3006
|
// 本轮已把组名提进首屏——组成员资格不决定可见性。
|
|
2926
|
-
|
|
3007
|
+
// 同一组的三个键是**三件独立能力**,不是一条链的三段:看图(座位闸门)、
|
|
3008
|
+
// 自动沉淀(写提议到记忆库)、执行记账(/mo-run 开窗)。各自默认关,
|
|
3009
|
+
// 开了任何一个都不会顺带打开另一个——写能力必须由人一项一项点。
|
|
3010
|
+
{ key: "group.mo", items: ["actionPipelineEnabled", "actionNudgeEnabled"] },
|
|
2927
3011
|
{ key: "group.enhance", items: ["entityExtractionEnabled", "codingRetrospect", "rerankEnabled", "resilientModelDownload", "searchSemanticDedup", "bm25SearchEnabled", "heatEnabled", "documentMemoryEnabled",
|
|
2928
3012
|
// 人格定义页(beta):注入总闸。分组随 enhance(行为开关,非座位闸门);
|
|
2929
3013
|
// 人格内容在设置页「个性化」区的人格卡。
|
|
2930
|
-
|
|
3014
|
+
// 严格寻址是同一条链上的第二个口径开关(只在总闸打开时起作用)。
|
|
3015
|
+
"personaEnabled", "personaStrictAddressing", "profileExtractEnabled"] },
|
|
2931
3016
|
{ key: "group.dream", items: ["sleepModeEnabled"] },
|
|
2932
3017
|
// v0.8.0 A4(issue #17):作用域隔离组——标注总开关 + 严格硬过滤。
|
|
2933
3018
|
{ key: "group.scope", items: ["scopeEnabled", "strictScope"] }
|
|
@@ -3468,6 +3553,12 @@ window.__ModuleLoader__.load({
|
|
|
3468
3553
|
const [newRule, setNewRule] = react.useState("");
|
|
3469
3554
|
const [newCmd, setNewCmd] = react.useState({ name: "", description: "", instruction: "" });
|
|
3470
3555
|
const [saved, setSaved] = react.useState(false);
|
|
3556
|
+
// 画像提取草稿(#497):null = 没有结果;否则带 kind 的一次性结果,四种结局各一句
|
|
3557
|
+
// 话——把「没启用」「库里没东西」「模型没配合」压成同一句「没有草稿」,用户就只能瞎猜。
|
|
3558
|
+
const [profileDraft, setProfileDraft] = react.useState(null);
|
|
3559
|
+
const [profileExtracting, setProfileExtracting] = react.useState(false);
|
|
3560
|
+
// 保存失败要可见(persona 那条一直有,画像这条以前是静默 catch)。
|
|
3561
|
+
const [profileError, setProfileError] = react.useState("");
|
|
3471
3562
|
const [cmdError, setCmdError] = react.useState("");
|
|
3472
3563
|
// 人格卡(beta,v1.2 多卡):default 卡草稿 + byAgent 卡列表 + 当前编辑位;
|
|
3473
3564
|
// provenance 是 v1.5 协作式人格的预留位,v1 只透传不编辑。
|
|
@@ -3477,6 +3568,11 @@ window.__ModuleLoader__.load({
|
|
|
3477
3568
|
const [personaNewName, setPersonaNewName] = react.useState("");
|
|
3478
3569
|
const [personaSaved, setPersonaSaved] = react.useState(false);
|
|
3479
3570
|
const [personaError, setPersonaError] = react.useState("");
|
|
3571
|
+
// 注入预览:文本由后端渲染(GET /persona 的 preview 字段),前端只显示、不自己拼。
|
|
3572
|
+
// 原先这里是一段前端手拼的 profile + rules 列表,注释却写着「注入生效文本」——
|
|
3573
|
+
// 它既没有表头、没有「- 用户画像:」前缀,也完全没有人格三行,两份渲染代码必然漂。
|
|
3574
|
+
// null = 还没读到或读失败(fail-closed:显示「未知」而不是「空」,也不显示旧值)。
|
|
3575
|
+
const [injectedPreview, setInjectedPreview] = react.useState(null);
|
|
3480
3576
|
const [vector, setVector] = react.useState({ enabled: false, baseUrl: "", apiKey: "", model: "" });
|
|
3481
3577
|
const [vectorSaved, setVectorSaved] = react.useState(false);
|
|
3482
3578
|
const [reindexing, setReindexing] = react.useState(false);
|
|
@@ -3567,11 +3663,34 @@ window.__ModuleLoader__.load({
|
|
|
3567
3663
|
setPersonaDefault(norm(dRaw));
|
|
3568
3664
|
setPersonaByAgent(Object.fromEntries(Object.entries(bRaw).map(([k, c]) => [k, norm(c)])));
|
|
3569
3665
|
setPersonaEditing("default");
|
|
3666
|
+
// 预览不在这里写:它只有一个所有者(loadPreview)。两处写同一个 state 就会
|
|
3667
|
+
// 互相覆盖,而这张卡的标题是「注入生效文本」——显示别张卡的文本比不显示更糟。
|
|
3570
3668
|
} catch { /* ignore */ }
|
|
3571
3669
|
}, []);
|
|
3572
3670
|
|
|
3573
3671
|
react.useEffect(() => { load(); }, [load]);
|
|
3574
3672
|
|
|
3673
|
+
// 预览请求:切卡与点「刷新预览」时各一次。刻意不挂在卡的数据上——卡内改动是
|
|
3674
|
+
// 逐字自动保存的,那样等于每敲一个字发一次 GET。
|
|
3675
|
+
// 序号守卫:先切 A 再切 B 时,A 的 GET 可能后回来;没有这道判断,界面上就是
|
|
3676
|
+
// 「编辑位显示 B、预览显示 A」。
|
|
3677
|
+
const previewSeq = react.useRef(0);
|
|
3678
|
+
const loadPreview = react.useCallback(async (agentId) => {
|
|
3679
|
+
const seq = ++previewSeq.current;
|
|
3680
|
+
const q = agentId && agentId !== "default" ? `?agent=${encodeURIComponent(agentId)}` : "";
|
|
3681
|
+
try {
|
|
3682
|
+
const res = await apiFetch(`/api/dsh-mneme/persona${q}`);
|
|
3683
|
+
const body = await res.json();
|
|
3684
|
+
if (seq !== previewSeq.current) return;
|
|
3685
|
+
setInjectedPreview(body && body.preview && typeof body.preview.text === "string" ? body.preview : null);
|
|
3686
|
+
} catch {
|
|
3687
|
+
if (seq !== previewSeq.current) return;
|
|
3688
|
+
setInjectedPreview(null);
|
|
3689
|
+
}
|
|
3690
|
+
}, []);
|
|
3691
|
+
|
|
3692
|
+
react.useEffect(() => { loadPreview(personaEditing); }, [personaEditing, loadPreview]);
|
|
3693
|
+
|
|
3575
3694
|
// 帮助与反馈卡片的版本号:独立小请求,失败不影响其它卡片。
|
|
3576
3695
|
react.useEffect(() => {
|
|
3577
3696
|
let cancelled = false;
|
|
@@ -3629,17 +3748,76 @@ window.__ModuleLoader__.load({
|
|
|
3629
3748
|
return () => { cancelled = true; };
|
|
3630
3749
|
}, []);
|
|
3631
3750
|
|
|
3632
|
-
|
|
3751
|
+
// 返回「是否真的存下了」。apiFetch 只是 fetch 的薄壳,非 2xx 不会抛——同页的
|
|
3752
|
+
// savePersonaCards 一直判 `res.ok`,画像这条一直没判,于是服务端 403/413 时界面
|
|
3753
|
+
// 照样闪「画像已保存」(评审实测的同类洞,这次一起收)。
|
|
3754
|
+
async function saveProfile(next = profile) {
|
|
3633
3755
|
try {
|
|
3634
|
-
await apiFetch("/api/dsh-mneme/profile", {
|
|
3756
|
+
const res = await apiFetch("/api/dsh-mneme/profile", {
|
|
3635
3757
|
method: "PUT",
|
|
3636
3758
|
headers: { "Content-Type": "application/json" },
|
|
3637
|
-
body: JSON.stringify({ profile })
|
|
3759
|
+
body: JSON.stringify({ profile: next })
|
|
3638
3760
|
});
|
|
3761
|
+
if (!res.ok) throw new Error("HTTP " + res.status);
|
|
3639
3762
|
setSaved(true);
|
|
3763
|
+
setProfileError("");
|
|
3640
3764
|
announce(t("memory.settings.profileSaved"));
|
|
3641
3765
|
setTimeout(() => setSaved(false), 1500);
|
|
3642
|
-
|
|
3766
|
+
return true;
|
|
3767
|
+
} catch (err) {
|
|
3768
|
+
setProfileError((err && err.message) || "failed");
|
|
3769
|
+
setTimeout(() => setProfileError(""), 4000);
|
|
3770
|
+
return false;
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
|
|
3774
|
+
// 画像提取:一次宿主 LLM 调用,只在用户点按钮时发生。开关没开时后端直接回
|
|
3775
|
+
// 409,这里显示「未启用」而不是转圈后什么也没有——那会被读成按钮坏了。
|
|
3776
|
+
async function extractProfileDraft() {
|
|
3777
|
+
setProfileExtracting(true);
|
|
3778
|
+
setProfileDraft(null);
|
|
3779
|
+
try {
|
|
3780
|
+
const res = await apiFetch("/api/dsh-mneme/profile/extract", {
|
|
3781
|
+
method: "POST",
|
|
3782
|
+
headers: { "Content-Type": "application/json" },
|
|
3783
|
+
body: "{}"
|
|
3784
|
+
});
|
|
3785
|
+
const body = await res.json().catch(() => null);
|
|
3786
|
+
if (res.ok && typeof body?.draft === "string" && body.draft.trim()) {
|
|
3787
|
+
setProfileDraft({
|
|
3788
|
+
kind: "draft",
|
|
3789
|
+
text: body.draft,
|
|
3790
|
+
rowsUsed: Number.isInteger(body.rowsUsed) ? body.rowsUsed : 0,
|
|
3791
|
+
truncated: body.truncated === true
|
|
3792
|
+
});
|
|
3793
|
+
return;
|
|
3794
|
+
}
|
|
3795
|
+
const error = body && typeof body.error === "string" ? body.error : "";
|
|
3796
|
+
// 五种结局各一句话:没启用 / 库里没内容 / 没配模型 / 模型不配合或调用失败 /
|
|
3797
|
+
// 成功草稿。压在一起就是把「环境缺东西」报成「这次没提炼出东西」。
|
|
3798
|
+
const kind = error === "disabled" ? "disabled"
|
|
3799
|
+
: error === "no-input" ? "empty"
|
|
3800
|
+
: error === "no-route" ? "noroute"
|
|
3801
|
+
: "failed";
|
|
3802
|
+
setProfileDraft({ kind });
|
|
3803
|
+
} catch {
|
|
3804
|
+
setProfileDraft({ kind: "failed" });
|
|
3805
|
+
} finally {
|
|
3806
|
+
setProfileExtracting(false);
|
|
3807
|
+
}
|
|
3808
|
+
}
|
|
3809
|
+
|
|
3810
|
+
// 采用草稿 = 显式写画像。两件事都要对:把草稿文本直接交给保存(不能 setProfile
|
|
3811
|
+
// 之后再调无参保存——那读的是这次渲染里的旧闭包值,会存回旧文本),以及**只有
|
|
3812
|
+
// 真的存下了才提交界面状态**(否则一次 403 就留下「框里是草稿、库里是旧文、
|
|
3813
|
+
// 草稿已被清掉」的三不像,关掉面板后彻底找不回来)。
|
|
3814
|
+
async function applyProfileDraft() {
|
|
3815
|
+
if (profileDraft?.kind !== "draft") return;
|
|
3816
|
+
const text = profileDraft.text;
|
|
3817
|
+
if (await saveProfile(text)) {
|
|
3818
|
+
setProfile(text);
|
|
3819
|
+
setProfileDraft(null);
|
|
3820
|
+
}
|
|
3643
3821
|
}
|
|
3644
3822
|
|
|
3645
3823
|
// 人格卡保存(v1.2 多卡):全量多卡形 PUT({default, byAgent},后端全量
|
|
@@ -3895,9 +4073,39 @@ window.__ModuleLoader__.load({
|
|
|
3895
4073
|
onChange: (e) => setProfile(e.target.value)
|
|
3896
4074
|
}),
|
|
3897
4075
|
h("div", { style: { display: "flex", alignItems: "center", gap: 8 } },
|
|
3898
|
-
h("button", { className: "mneme-btn", onClick: saveProfile }, t("memory.settings.profileSave")),
|
|
3899
|
-
saved && h("span", { className: "mneme-saved" }, t("memory.settings.profileSaved"))
|
|
3900
|
-
|
|
4076
|
+
h("button", { className: "mneme-btn", onClick: () => saveProfile() }, t("memory.settings.profileSave")),
|
|
4077
|
+
saved && h("span", { className: "mneme-saved" }, t("memory.settings.profileSaved")),
|
|
4078
|
+
profileError ? h("span", { className: "mneme-saved", style: { color: "var(--dsw-danger, #d33)" } },
|
|
4079
|
+
t("memory.settings.profileSaveFailed") + " (" + profileError + ")") : null
|
|
4080
|
+
),
|
|
4081
|
+
// 从记忆提取草稿(#497)。两种结局都要显眼:草稿(要用户点「采用」才落库)
|
|
4082
|
+
// 与失败原因(开关没开 / 没内容可提 / 模型没配合)。手写的原文在这之上一直
|
|
4083
|
+
// 可见——采用前它不会被任何东西覆盖。
|
|
4084
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 8 } },
|
|
4085
|
+
h("button", {
|
|
4086
|
+
type: "button",
|
|
4087
|
+
className: "mneme-btn",
|
|
4088
|
+
disabled: profileExtracting,
|
|
4089
|
+
onClick: extractProfileDraft
|
|
4090
|
+
}, t("memory.settings.profileExtract")),
|
|
4091
|
+
profileExtracting ? h("span", { className: "mneme-saved" }, t("memory.settings.profileExtracting")) : null
|
|
4092
|
+
),
|
|
4093
|
+
h("div", { className: "mneme-set-desc" }, t("memory.settings.profileExtractHint")),
|
|
4094
|
+
profileDraft?.kind === "disabled" ? h("div", { className: "mneme-set-desc" }, t("memory.settings.profileExtractDisabled")) : null,
|
|
4095
|
+
profileDraft?.kind === "empty" ? h("div", { className: "mneme-set-desc" }, t("memory.settings.profileExtractEmpty")) : null,
|
|
4096
|
+
profileDraft?.kind === "noroute" ? h("div", { className: "mneme-set-desc" }, t("memory.settings.profileExtractNoRoute")) : null,
|
|
4097
|
+
profileDraft?.kind === "failed" ? h("div", { className: "mneme-set-desc" }, t("memory.settings.profileExtractFailed")) : null,
|
|
4098
|
+
profileDraft?.kind === "draft" ? h("div", null,
|
|
4099
|
+
h("div", { className: "mneme-featnumlabel" }, t("memory.settings.profileDraftTitle")),
|
|
4100
|
+
h("div", { className: "mneme-set-desc" },
|
|
4101
|
+
t("memory.settings.profileDraftRows") + ": " + profileDraft.rowsUsed
|
|
4102
|
+
+ (profileDraft.truncated ? " · " + t("memory.settings.profileDraftTruncated") : "")),
|
|
4103
|
+
h("pre", { className: "mneme-set-input mneme-set-input--area", style: { whiteSpace: "pre-wrap", minHeight: 0 } }, profileDraft.text),
|
|
4104
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } },
|
|
4105
|
+
h("button", { type: "button", className: "mneme-btn", onClick: applyProfileDraft }, t("memory.settings.profileDraftApply")),
|
|
4106
|
+
h("button", { type: "button", className: "mneme-btn", onClick: () => setProfileDraft(null) }, t("memory.settings.profileDraftDiscard"))
|
|
4107
|
+
)
|
|
4108
|
+
) : null
|
|
3901
4109
|
),
|
|
3902
4110
|
// 人格卡(beta,v1.2 多卡)— who the agent is:多张角色卡按会话身份注入。
|
|
3903
4111
|
// 卡内开关只是该卡 enabled 半边;总闸 personaEnabled 在功能开关组,双闸同真。
|
|
@@ -3988,15 +4196,40 @@ window.__ModuleLoader__.load({
|
|
|
3988
4196
|
t("memory.settings.personaSaveFailed") + " (" + personaError + ")")
|
|
3989
4197
|
)
|
|
3990
4198
|
),
|
|
3991
|
-
// Agent
|
|
3992
|
-
//
|
|
4199
|
+
// Agent 眼中的你(只读)— 注入生效文本由后端渲染(GET /persona 的 preview 字段),
|
|
4200
|
+
// 前端只显示。以前这一段是前端自己拼的 profile + rules,注释却写着「注入生效文本」:
|
|
4201
|
+
// 没有人格三行、没有表头、没有「- 用户画像:」前缀,两份实现必然漂。
|
|
3993
4202
|
h("section", { className: "mneme-set-card" },
|
|
3994
4203
|
h("div", { className: "mneme-set-title" }, t("memory.settings.personaView")),
|
|
3995
4204
|
h("div", { className: "mneme-set-desc" }, t("memory.settings.personaViewHint")),
|
|
3996
|
-
|
|
3997
|
-
? h("
|
|
3998
|
-
|
|
3999
|
-
|
|
4205
|
+
injectedPreview === null
|
|
4206
|
+
? h("div", { className: "mneme-xempty" }, t("memory.settings.personaViewUnknown"))
|
|
4207
|
+
: h("div", null,
|
|
4208
|
+
h("div", { className: "mneme-set-desc" },
|
|
4209
|
+
injectedPreview.source === "byAgent"
|
|
4210
|
+
? t("memory.settings.personaViewSourceCard")
|
|
4211
|
+
: injectedPreview.source === "none"
|
|
4212
|
+
? t("memory.settings.personaViewSourceNone")
|
|
4213
|
+
// source=default 有两种来路,必须分开说:请求带了 ?agent=<身份>
|
|
4214
|
+
// 却仍落默认卡(身份解析到了,只是没建卡或卡被关掉——关卡的人最想
|
|
4215
|
+
// 确认的就是这一格),与真的解析不到会话身份。
|
|
4216
|
+
: injectedPreview.agent
|
|
4217
|
+
? t("memory.settings.personaViewSourceFallback")
|
|
4218
|
+
: t("memory.settings.personaViewSourceDefault")),
|
|
4219
|
+
injectedPreview.configGate === true
|
|
4220
|
+
? null
|
|
4221
|
+
: h("div", { className: "mneme-set-desc" }, t("memory.settings.personaViewGateOff")),
|
|
4222
|
+
injectedPreview.text
|
|
4223
|
+
? h("pre", { className: "mneme-set-input mneme-set-input--area", style: { whiteSpace: "pre-wrap", minHeight: 0 } }, injectedPreview.text)
|
|
4224
|
+
: h("div", { className: "mneme-xempty" }, t("memory.settings.personaViewEmpty"))
|
|
4225
|
+
),
|
|
4226
|
+
h("div", { style: { display: "flex", alignItems: "center", gap: 8, marginTop: 6 } },
|
|
4227
|
+
h("button", {
|
|
4228
|
+
type: "button",
|
|
4229
|
+
className: "mneme-btn",
|
|
4230
|
+
onClick: () => loadPreview(personaEditing)
|
|
4231
|
+
}, t("memory.settings.personaViewRefresh"))
|
|
4232
|
+
)
|
|
4000
4233
|
),
|
|
4001
4234
|
// 规则 — numbered rows, hover reveals the delete affordance
|
|
4002
4235
|
h("section", { className: "mneme-set-card" },
|
|
@@ -7603,8 +7836,62 @@ window.__ModuleLoader__.load({
|
|
|
7603
7836
|
// 条目清单(名字 + 规模 + 作用域):图上方的**选择器**。
|
|
7604
7837
|
// 它解决的是「这是哪条工作流」——一进来就是一张图时这个问题没有落点,多条之间也没得选。
|
|
7605
7838
|
// 但它不占整屏:图和它同屏,见下面的 PipelineBrowser。
|
|
7839
|
+
// 「现在就跑一次」。自动沉淀只在 turn/end 之后跑、还有 10 分钟冷却,所以人想立刻
|
|
7840
|
+
// 看到结果时没有入口。这里的 POST 与自动跑走**同一条** nudgeForProject,区别只有
|
|
7841
|
+
// materialize:按钮永远不落草稿,只把提议写进记忆库——升格仍然要人批。
|
|
7842
|
+
// project 为空(会话里读不到 cwd)时按钮是禁用的:服务端只会回 400,
|
|
7843
|
+
// 而一个点了必然失败的按钮比没有按钮更糟。
|
|
7844
|
+
function PipelineDistillButton(props) {
|
|
7845
|
+
const { t, project } = props;
|
|
7846
|
+
const [busy, setBusy] = useState(false);
|
|
7847
|
+
const [note, setNote] = useState("");
|
|
7848
|
+
const disabled = busy || !project;
|
|
7849
|
+
const run = () => {
|
|
7850
|
+
if (disabled) return;
|
|
7851
|
+
setBusy(true);
|
|
7852
|
+
setNote("");
|
|
7853
|
+
apiFetch("/api/dsh-mneme/nudge?project=" + encodeURIComponent(project), {
|
|
7854
|
+
method: "POST",
|
|
7855
|
+
headers: { "Content-Type": "application/json" },
|
|
7856
|
+
body: JSON.stringify({})
|
|
7857
|
+
})
|
|
7858
|
+
.then((res) => res.json().then((j) => ({ ok: res.ok, status: res.status, body: j || {} })))
|
|
7859
|
+
.then(({ ok, status, body }) => {
|
|
7860
|
+
if (!ok) {
|
|
7861
|
+
// 503 = 会话日志读不到(环境问题),其余是这一跑本身失败。两句不能合并:
|
|
7862
|
+
// 「读不到」和「没有重复动作」在界面上长得一样时,人就只能瞎猜。
|
|
7863
|
+
setNote(t(status === 503
|
|
7864
|
+
? "memory.actions.distill.unreadable"
|
|
7865
|
+
: "memory.actions.distill.failed"));
|
|
7866
|
+
return;
|
|
7867
|
+
}
|
|
7868
|
+
if (body.skipped) {
|
|
7869
|
+
setNote(t("memory.actions.distill.noSessions"));
|
|
7870
|
+
return;
|
|
7871
|
+
}
|
|
7872
|
+
const saved = (body.counts && body.counts.savedProposals) || 0;
|
|
7873
|
+
const found = (body.counts && body.counts.candidates) || 0;
|
|
7874
|
+
setNote(found === 0
|
|
7875
|
+
? t("memory.actions.distill.none")
|
|
7876
|
+
: t("memory.actions.distill.saved").replace("{n}", String(saved)).replace("{m}", String(found)));
|
|
7877
|
+
})
|
|
7878
|
+
.catch(() => setNote(t("memory.actions.distill.failed")))
|
|
7879
|
+
.then(() => setBusy(false));
|
|
7880
|
+
};
|
|
7881
|
+
return h("span", { className: "mneme-pipe-distill" },
|
|
7882
|
+
h("button", {
|
|
7883
|
+
type: "button",
|
|
7884
|
+
className: "mneme-pipe-distillBtn",
|
|
7885
|
+
disabled,
|
|
7886
|
+
title: !project ? t("memory.actions.distill.noProject") : t("memory.actions.distill.hint"),
|
|
7887
|
+
onClick: run
|
|
7888
|
+
}, busy ? t("memory.actions.distill.busy") : t("memory.actions.distill.action")),
|
|
7889
|
+
note ? h("span", { className: "mneme-pipe-distillNote", role: "status" }, note) : null
|
|
7890
|
+
);
|
|
7891
|
+
}
|
|
7892
|
+
|
|
7606
7893
|
function PipelineWorkflowList(props) {
|
|
7607
|
-
const { t, data, activeId, onOpen } = props;
|
|
7894
|
+
const { t, data, activeId, onOpen, project } = props;
|
|
7608
7895
|
const list = (data && data.available) || [];
|
|
7609
7896
|
const scopeOf = (row) => (row.scope === "global"
|
|
7610
7897
|
? t("memory.actions.scope.global")
|
|
@@ -7614,6 +7901,7 @@ window.__ModuleLoader__.load({
|
|
|
7614
7901
|
h("span", null, t("memory.actions.list.heading")),
|
|
7615
7902
|
h("span", null, t("memory.actions.list.count").replace("{n}", String(list.length)))
|
|
7616
7903
|
),
|
|
7904
|
+
h(PipelineDistillButton, { t, project }),
|
|
7617
7905
|
list.length
|
|
7618
7906
|
? list.map((row) => h("button", {
|
|
7619
7907
|
key: row.id,
|
|
@@ -7664,7 +7952,7 @@ window.__ModuleLoader__.load({
|
|
|
7664
7952
|
className: "mneme-pipe",
|
|
7665
7953
|
style: { height: "100%", display: "flex", flexDirection: "column", minHeight: "0" }
|
|
7666
7954
|
},
|
|
7667
|
-
h(PipelineWorkflowList, { t, data: listDoc, activeId, onOpen: setPicked }),
|
|
7955
|
+
h(PipelineWorkflowList, { t, data: listDoc, activeId, onOpen: setPicked, project }),
|
|
7668
7956
|
h(PipelineView, { t, data: detailDoc })
|
|
7669
7957
|
);
|
|
7670
7958
|
}
|