dsh-mnemon 0.5.24 → 0.5.25

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 CHANGED
@@ -34,7 +34,7 @@ An Agent that starts every session from scratch keeps asking what it was already
34
34
 
35
35
  - **The right memory for each turn.** Preferences and working facts stay in context. Project documents and long-term evidence are searched only when a question needs them.
36
36
  - **You can see what a turn used.** Under each reply, the turn memory bar lists the documents and memories the turn read and wrote, and one click opens each in the Memory System. The Memory System shows everything that is stored and lets you edit it.
37
- - **Save to memory in one click.** When a conversation turns up a fact worth keeping, the brain mark under the reply hands it to a task Agent, which removes duplicates, distils it and writes it to the right Memory Space; the receipt says where it went.
37
+ - **Save to memory in one click.** When a conversation turns up a fact worth keeping, the brain mark under the reply hands it to a task Agent, which removes duplicates, distils it and writes it to the right Memory Space, or saves it where you choose: working memory, your profile or a Memory Space. The receipt says where it went.
38
38
  - **Compose it on the Plugins page.** Choose a main strategy and optional enhancements, and switch them without moving any data. Every component has its own page for its settings.
39
39
  - **Keep data where you want it.** Local by default with Mnemon Native, or one of eight third-party Providers. Storage can be global, per workspace or centralized, with ZIP backup.
40
40
  - **Extend it.** Sources and Strategies are ordinary DSH plugins built on public SDKs. An installed component gets the same pages and switches as the shipped ones.
package/README.zh-CN.md CHANGED
@@ -35,7 +35,7 @@
35
35
 
36
36
  - **每一轮都拿到合适的记忆。** 偏好和工作中的事实常驻上下文;项目档案与长期证据只在问题需要时才检索。
37
37
  - **看得见这一轮用了什么。** 每条回复下方的回合记忆栏列出这一轮读到和写入的档案与记忆,点一下就在记忆系统中打开它;记忆系统展示全部已保存的内容,并可直接编辑。
38
- - **一键存入记忆。** 对话里出现值得保留的事实,点击回复下的脑形图标,由任务 Agent 去重、提炼并写入合适的记忆空间,回执写明存到了哪里。
38
+ - **一键存入记忆。** 对话里出现值得保留的事实,点击回复下的脑形图标,由任务 Agent 去重、提炼并写入合适的记忆空间,也可以直接存到你选择的位置:工作记忆、用户画像或某个记忆空间。回执写明存到了哪里。
39
39
  - **在插件页组合。** 选择一个主策略和若干可选增强,切换时无需迁移任何数据;每个组件都有自己的设置页。
40
40
  - **数据放在你想放的地方。** 默认由 Mnemon Native 在本地保存,也可以接入八种第三方 Provider;存储范围可选全局、按工作区或集中存储,并支持 ZIP 备份。
41
41
  - **可以扩展。** Source 与 Strategy 都是基于公开 SDK 的普通 DSH 插件;安装的组件与随附组件拥有同样的页面和开关。
package/lib/bundle.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as prepareStarterResolution } from "./starter-resolution-DFLhgaoL.js";
1
+ import { t as prepareStarterResolution } from "./starter-resolution-UcTGyjgm.js";
2
2
  //#region src/bundle.ts
3
3
  const groupKey = Symbol.for("cordis.group");
4
4
  const init = Symbol.for("cordis.init");
package/lib/client.js CHANGED
@@ -739,6 +739,7 @@ window.__ModuleLoader__.load({
739
739
  "runtime.result.remove": "已从{target}移除 · 当前 {count} 条",
740
740
  "runtime.result.maintenance": "容量整理完成:已先归档到记忆空间 {spaces},再更新{target} · 当前 {count} 条",
741
741
  "runtime.result.localCompaction": "本地画像整理完成:未写入记忆空间,已更新{target} · 当前 {count} 条",
742
+ "runtime.result.localArchive": "容量整理完成:没有可写的记忆空间,已先把 {archived} 条移入本地归档,再更新{target} · 当前 {count} 条",
742
743
  "runtime.readOnly": "当前为只读模式;运行时记忆仍会进入上下文,但不能在此修改。",
743
744
  "runtime.footnote": "memories.json 是唯一事实源;两个 Markdown 文件由控制层生成,不应直接编辑。"
744
745
  };
@@ -785,6 +786,7 @@ window.__ModuleLoader__.load({
785
786
  "runtime.result.remove": "Removed from {target} · {count} entries",
786
787
  "runtime.result.maintenance": "Capacity maintenance complete: archived to {spaces}, then updated {target} · {count} entries",
787
788
  "runtime.result.localCompaction": "Local profile compaction complete: no Memory Space write; updated {target} · {count} entries",
789
+ "runtime.result.localArchive": "Capacity maintenance complete: with no Memory Space to write to, moved {archived} entries to the local archive, then updated {target} · {count} entries",
788
790
  "runtime.readOnly": "Read only here: runtime memory still enters context but cannot be changed on this page.",
789
791
  "runtime.footnote": "memories.json is the only source of truth. The control plane generates both Markdown files; do not edit them directly."
790
792
  };
@@ -824,6 +826,7 @@ window.__ModuleLoader__.load({
824
826
  "documents.updated": "已保存新的档案修订。",
825
827
  "documents.updatedAfterArchive": "已先迁移 {count} 份旧档案,再保存修订。",
826
828
  "documents.archived": "已建立 Mnemon 冷索引并归档;关联记忆空间:{spaces}",
829
+ "documents.archivedLocally": "已归档到本地:原文移入归档目录,没有建立 Mnemon 冷索引。",
827
830
  "documents.list": "项目档案列表",
828
831
  "documents.activeList": "活跃目录",
829
832
  "documents.archiveList": "归档目录",
@@ -843,11 +846,12 @@ window.__ModuleLoader__.load({
843
846
  "documents.hash": "内容哈希",
844
847
  "documents.size": "文件大小",
845
848
  "documents.archiveReceipt": "Mnemon 冷索引回执",
849
+ "documents.archiveNote": "归档说明",
846
850
  "documents.archiveTitle": "迁入归档",
847
- "documents.archiveDescription": "独立任务 Agent 先拟定摘要和目标空间;Host 校验后写入冷索引,再移动原文。需要已启用且支持精确写入与删除的记忆空间。",
851
+ "documents.archiveDescription": "独立任务 Agent 先拟定摘要和目标空间;Host 校验后写入冷索引,再移动原文。没有已启用且支持精确写入与删除的记忆空间时,原文直接移入本地归档,不建立索引。",
848
852
  "documents.archive": "归档",
849
- "documents.archiveConfirm": "确认建立 Mnemon 索引并迁移这份档案?",
850
- "documents.archiving": "索引并迁移中…",
853
+ "documents.archiveConfirm": "确认归档这份档案?",
854
+ "documents.archiving": "归档中…",
851
855
  "documents.archiveNow": "确认归档",
852
856
  "documents.footnote": "`.mnemon/documents/index.json` 是控制面事实源;active 总量固定不超过 10 MB,archived 不计入上限,项目源文件不会被修改。"
853
857
  };
@@ -885,6 +889,7 @@ window.__ModuleLoader__.load({
885
889
  "documents.updated": "New document revision saved.",
886
890
  "documents.updatedAfterArchive": "Archived {count} older document(s), then saved the revision.",
887
891
  "documents.archived": "Mnemon cold index created and document archived; Memory Spaces: {spaces}",
892
+ "documents.archivedLocally": "Archived locally: the original moved to the archive, without a Mnemon cold index.",
888
893
  "documents.list": "Project document list",
889
894
  "documents.activeList": "Active directory",
890
895
  "documents.archiveList": "Archive directory",
@@ -904,11 +909,12 @@ window.__ModuleLoader__.load({
904
909
  "documents.hash": "Content hash",
905
910
  "documents.size": "File size",
906
911
  "documents.archiveReceipt": "Mnemon cold-index receipt",
912
+ "documents.archiveNote": "Archive note",
907
913
  "documents.archiveTitle": "Move to archive",
908
- "documents.archiveDescription": "An independent task Agent proposes a summary and destination. The Host validates and writes the cold index before moving the original. An active Memory Space with exact writes and safe deletion is required.",
914
+ "documents.archiveDescription": "An independent task Agent proposes a summary and destination. The Host validates and writes the cold index before moving the original. Without an active Memory Space that has exact writes and safe deletion, the original moves to the local archive without an index.",
909
915
  "documents.archive": "Archive",
910
- "documents.archiveConfirm": "Create the Mnemon index and archive this document?",
911
- "documents.archiving": "Indexing and moving…",
916
+ "documents.archiveConfirm": "Archive this document?",
917
+ "documents.archiving": "Archiving…",
912
918
  "documents.archiveNow": "Confirm archive",
913
919
  "documents.footnote": "`.mnemon/documents/index.json` is the control-plane source of truth. active stays at or below 10 MB, archived is excluded, and project source files are never modified."
914
920
  };
@@ -1739,6 +1745,7 @@ window.__ModuleLoader__.load({
1739
1745
  "receipt.unfinished": "未完成",
1740
1746
  "receipt.failed": "失败",
1741
1747
  "receipt.view": "在记忆空间中查看",
1748
+ "receipt.viewRuntime": "在运行时记忆中查看",
1742
1749
  "taskAgent.ready": "任务 Agent 就绪",
1743
1750
  "taskAgent.unavailable": "任务 Agent 不可用",
1744
1751
  "saveAction.button": "存入记忆",
@@ -1753,6 +1760,31 @@ window.__ModuleLoader__.load({
1753
1760
  "saveAction.submitting": "任务 Agent 处理中…",
1754
1761
  "saveAction.readOnly": "当前部署为只读模式,无法写入记忆。",
1755
1762
  "saveAction.close": "关闭",
1763
+ "saveAction.hintDirect": "文本直接写入所选位置,不经过任务 Agent。",
1764
+ "saveAction.to": "保存到",
1765
+ "saveAction.to.agent": "由 Agent 决定",
1766
+ "saveAction.to.agentDetail": "任务 Agent 判断是否值得保存,并选择记忆空间",
1767
+ "saveAction.to.memory": "工作记忆(MEMORY.md)",
1768
+ "saveAction.to.user": "用户画像(USER.md)",
1769
+ "saveAction.to.space": "记忆空间 · {provider}",
1770
+ "saveAction.save": "保存",
1771
+ "saveAction.saving": "保存中…",
1772
+ "saveAction.saved.runtime": "已存入{target} · 当前 {count} 条",
1773
+ "saveAction.saved.runtimeMaintained": "已整理容量并存入{target} · 当前 {count} 条",
1774
+ "saveAction.saved.space": "已存入记忆空间“{space}”",
1775
+ "saveAction.saved.spacePending": "已提交到记忆空间“{space}”,由 Provider 稍后确认",
1776
+ "saveAction.saved.spaceOther": "记忆空间“{space}”返回:{action}",
1777
+ "saveAction.saved.duplicate": "内容已存在,没有重复写入。",
1778
+ "saveAction.unavailable": "这个位置当前不可写。",
1779
+ "saveAction.place.delimiter": "{target}的条目不能包含 §;删去后即可保存。",
1780
+ "saveAction.place.tooLong": "这段文本约 {size},超过{target}单条上限 {limit};请删减。",
1781
+ "saveAction.place.orAgent": "也可以交给 Agent 决定。",
1782
+ "saveAction.place.full": "{target}已用 {used} / {limit}。保存前需要先整理容量:已有条目只保留约 {keep},其余移出工作记忆。",
1783
+ "saveAction.place.fullAll": "{target}已用 {used} / {limit}。保存前需要先整理容量:已有条目将全部移出工作记忆。",
1784
+ "saveAction.place.userCondense": "用户画像已用 {used} / {limit}。保存前,任务 Agent 会把已有内容压缩到约 {keep} 以内,不留存档。",
1785
+ "saveAction.place.userTooLong": "这段文本约 {size},整理后的用户画像最多只能放下 {max};请删减。",
1786
+ "saveAction.place.userFull": "用户画像已满,整理它需要任务 Agent;请先在运行时记忆中删减条目,或换一个位置。",
1787
+ "saveAction.noPlace": "现在没有可以写入的位置。",
1756
1788
  "readSources.all": "全部 Provider",
1757
1789
  "readSources.mode.search": "原生检索",
1758
1790
  "readSources.mode.graph": "真实关系图",
@@ -2212,7 +2244,7 @@ window.__ModuleLoader__.load({
2212
2244
  "config.interactionTurnBar": "回合记忆栏",
2213
2245
  "config.interactionTurnBarHint": "每轮回复下方显示本轮的召回与写入",
2214
2246
  "config.interactionSaveAction": "存入记忆按钮",
2215
- "config.interactionSaveActionHint": "在回复旁手动存入,确认后写入记忆空间",
2247
+ "config.interactionSaveActionHint": "在回复旁手动存入,确认后写入你选择的位置",
2216
2248
  "config.packTitle": "备份与迁移",
2217
2249
  "config.packUnavailable": "当前 DSH 主机不支持 Mnemon ZIP 备份通道。",
2218
2250
  "config.packExporting": "导出中…",
@@ -2335,6 +2367,7 @@ window.__ModuleLoader__.load({
2335
2367
  "receipt.unfinished": "Not finished",
2336
2368
  "receipt.failed": "Failed",
2337
2369
  "receipt.view": "View in Memory Spaces",
2370
+ "receipt.viewRuntime": "View in Runtime Memory",
2338
2371
  "taskAgent.ready": "Task Agent ready",
2339
2372
  "taskAgent.unavailable": "Task Agent unavailable",
2340
2373
  "saveAction.button": "Save to memory",
@@ -2349,6 +2382,31 @@ window.__ModuleLoader__.load({
2349
2382
  "saveAction.submitting": "Task Agent working…",
2350
2383
  "saveAction.readOnly": "This deployment is read only; memory writes are disabled.",
2351
2384
  "saveAction.close": "Close",
2385
+ "saveAction.hintDirect": "The text goes straight to the chosen place, without a task Agent.",
2386
+ "saveAction.to": "Save to",
2387
+ "saveAction.to.agent": "Let the Agent decide",
2388
+ "saveAction.to.agentDetail": "A task Agent decides whether to keep it and picks a Memory Space",
2389
+ "saveAction.to.memory": "Working memory (MEMORY.md)",
2390
+ "saveAction.to.user": "User profile (USER.md)",
2391
+ "saveAction.to.space": "Memory Space · {provider}",
2392
+ "saveAction.save": "Save",
2393
+ "saveAction.saving": "Saving…",
2394
+ "saveAction.saved.runtime": "Saved to {target} · {count} entries",
2395
+ "saveAction.saved.runtimeMaintained": "Made room and saved to {target} · {count} entries",
2396
+ "saveAction.saved.space": "Saved to the Memory Space {space}",
2397
+ "saveAction.saved.spacePending": "Submitted to the Memory Space {space}; the Provider confirms it later",
2398
+ "saveAction.saved.spaceOther": "The Memory Space {space} answered: {action}",
2399
+ "saveAction.saved.duplicate": "Already saved; nothing was written twice.",
2400
+ "saveAction.unavailable": "This place cannot take writes now.",
2401
+ "saveAction.place.delimiter": "Entries in {target} cannot contain §; remove it to save.",
2402
+ "saveAction.place.tooLong": "This text is about {size}, more than one entry in {target} holds ({limit}); shorten it.",
2403
+ "saveAction.place.orAgent": " Or let the Agent decide.",
2404
+ "saveAction.place.full": "{target} holds {used} of {limit}. Saving first makes room: about {keep} of the existing entries stay, and the rest move out of working memory.",
2405
+ "saveAction.place.fullAll": "{target} holds {used} of {limit}. Saving first makes room: every existing entry moves out of working memory.",
2406
+ "saveAction.place.userCondense": "The user profile holds {used} of {limit}. Before saving, a task Agent condenses it to about {keep}, keeping no archive.",
2407
+ "saveAction.place.userTooLong": "This text is about {size}, more than the user profile can take after making room ({max}); shorten it.",
2408
+ "saveAction.place.userFull": "The user profile is full, and making room in it needs a task Agent; remove entries in Runtime Memory first, or choose another place.",
2409
+ "saveAction.noPlace": "No place can take this text now.",
2352
2410
  "readSources.all": "All providers",
2353
2411
  "readSources.mode.search": "Native search",
2354
2412
  "readSources.mode.graph": "True relation graph",
@@ -2808,7 +2866,7 @@ window.__ModuleLoader__.load({
2808
2866
  "config.interactionTurnBar": "Turn memory bar",
2809
2867
  "config.interactionTurnBarHint": "Shows each turn's recall and writes below the reply",
2810
2868
  "config.interactionSaveAction": "Save to memory action",
2811
- "config.interactionSaveActionHint": "Save a reply by hand; it goes into a Memory Space after you confirm",
2869
+ "config.interactionSaveActionHint": "Save a reply by hand; it goes where you choose after you confirm",
2812
2870
  "config.packTitle": "Backup and migration",
2813
2871
  "config.packUnavailable": "This DSH host does not provide the Mnemon ZIP backup channel.",
2814
2872
  "config.packExporting": "Exporting…",
@@ -6804,7 +6862,7 @@ window.__ModuleLoader__.load({
6804
6862
  size: "sm",
6805
6863
  className: PageControls_module_css_default.receiptAction,
6806
6864
  onClick: props.onView,
6807
- children: t("receipt.view")
6865
+ children: props.viewLabel ?? t("receipt.view")
6808
6866
  })
6809
6867
  ]
6810
6868
  });
@@ -8726,7 +8784,7 @@ window.__ModuleLoader__.load({
8726
8784
  }
8727
8785
  //#endregion
8728
8786
  //#region \0dsh-mnemon-css:/home/runner/work/dsh-mnemon/dsh-mnemon/src/client/MnemonSaveAction.module.css.mjs
8729
- const css$1 = ".ypSjoa_wrap{display:inline-flex;position:relative}.ypSjoa_button{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.ypSjoa_button:hover,.ypSjoa_button[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.ypSjoa_button:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}.ypSjoa_icon{flex:none;width:16px;height:16px;display:inline-flex}.ypSjoa_modal{width:min(640px, calc(100vw - max(12px, env(safe-area-inset-left,0px)) - max(12px, env(safe-area-inset-right,0px))));max-height:calc(100vh - 24px)}.ypSjoa_modalContent{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;min-height:0;overflow-y:auto}.ypSjoa_modalAction{min-width:96px}.ypSjoa_status{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:20px}.ypSjoa_readOnly{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);margin-bottom:12px;padding:8px 12px;font-size:12px;line-height:18px}.ypSjoa_candidate{display:block}.ypSjoa_candidateHeading{justify-content:space-between;align-items:center;gap:8px;min-width:0;margin-bottom:6px;display:flex}.ypSjoa_candidateHeading>label{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.ypSjoa_candidate textarea{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);resize:vertical;background:var(--dsw-alias-bg-layer-3);width:100%;min-height:260px;max-height:50vh;color:var(--dsw-alias-label-primary);font:14px/22px var(--dsw-font-family);padding:10px 12px;display:block}.ypSjoa_candidate textarea:focus-visible{border-color:var(--dsw-alias-state-business-primary);outline:none}.ypSjoa_truncated{color:var(--dsw-alias-label-tertiary);margin-top:6px;font-size:12px;line-height:18px;display:block}@media (width<=640px),(height<=560px){.ypSjoa_modal{width:min(100%, calc(100vw - max(8px, env(safe-area-inset-left,0px)) - max(8px, env(safe-area-inset-right,0px))));max-height:calc(100vh - 16px)}.ypSjoa_modal .ypSjoa_modalAction{min-width:0;min-height:44px}.ypSjoa_candidate textarea{min-height:clamp(140px,32vh,220px);max-height:42vh}}@media (pointer:coarse) and (width<=640px),(pointer:coarse) and (height<=560px){.ypSjoa_candidate textarea{font-size:16px}}@supports (height:100dvh){.ypSjoa_modal{max-height:calc(100dvh - 24px)}@media (width<=640px),(height<=560px){.ypSjoa_modal{max-height:calc(100dvh - 16px)}.ypSjoa_candidate textarea{min-height:clamp(140px,32dvh,220px);max-height:42dvh}}}";
8787
+ const css$1 = ".ypSjoa_wrap{display:inline-flex;position:relative}.ypSjoa_button{cursor:pointer;width:28px;height:28px;color:var(--dsw-alias-label-tertiary);background:0 0;border:none;border-radius:28px;justify-content:center;align-items:center;padding:6px;display:inline-flex}.ypSjoa_button:hover,.ypSjoa_button[aria-expanded=true]{background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.ypSjoa_button:focus-visible{outline:var(--dsw-focus-ring-width,2px) solid var(--dsw-focus-ring-color,var(--dsw-alias-state-business-primary));outline-offset:2px}.ypSjoa_icon{flex:none;width:16px;height:16px;display:inline-flex}.ypSjoa_modal{width:min(640px, calc(100vw - max(12px, env(safe-area-inset-left,0px)) - max(12px, env(safe-area-inset-right,0px))));max-height:calc(100vh - 24px)}.ypSjoa_modalContent{overscroll-behavior:contain;-webkit-overflow-scrolling:touch;min-height:0;overflow-y:auto}.ypSjoa_modalAction{min-width:96px}.ypSjoa_status{color:var(--dsw-alias-label-tertiary);margin:0;font-size:13px;line-height:20px}.ypSjoa_readOnly{border-radius:var(--dsw-radius-md);background:var(--dsw-alias-state-warn-tertiary);color:var(--dsw-alias-state-warn-label);margin-bottom:12px;padding:8px 12px;font-size:12px;line-height:18px}.ypSjoa_candidate{display:block}.ypSjoa_candidateHeading{justify-content:space-between;align-items:center;gap:8px;min-width:0;margin-bottom:6px;display:flex}.ypSjoa_candidateHeading>label{color:var(--dsw-alias-label-primary);font-size:13px;font-weight:500;line-height:20px}.ypSjoa_candidate textarea{box-sizing:border-box;border:.5px solid var(--dsw-alias-border-l4);border-radius:var(--dsw-radius-md);resize:vertical;background:var(--dsw-alias-bg-layer-3);width:100%;min-height:260px;max-height:50vh;color:var(--dsw-alias-label-primary);font:14px/22px var(--dsw-font-family);padding:10px 12px;display:block}.ypSjoa_candidate textarea:focus-visible{border-color:var(--dsw-alias-state-business-primary);outline:none}.ypSjoa_destination{margin-top:12px}.ypSjoa_truncated,.ypSjoa_placeNote{color:var(--dsw-alias-label-tertiary);margin-top:6px;font-size:12px;line-height:18px;display:block}.ypSjoa_placeNote[data-tone=blocked]{color:var(--dsw-alias-state-warn-label)}@media (width<=640px),(height<=560px){.ypSjoa_modal{width:min(100%, calc(100vw - max(8px, env(safe-area-inset-left,0px)) - max(8px, env(safe-area-inset-right,0px))));max-height:calc(100vh - 16px)}.ypSjoa_modal .ypSjoa_modalAction{min-width:0;min-height:44px}.ypSjoa_candidate textarea{min-height:clamp(140px,32vh,220px);max-height:42vh}}@media (pointer:coarse) and (width<=640px),(pointer:coarse) and (height<=560px){.ypSjoa_candidate textarea{font-size:16px}}@supports (height:100dvh){.ypSjoa_modal{max-height:calc(100dvh - 24px)}@media (width<=640px),(height<=560px){.ypSjoa_modal{max-height:calc(100dvh - 16px)}.ypSjoa_candidate textarea{min-height:clamp(140px,32dvh,220px);max-height:42dvh}}}";
8730
8788
  const tagId$1 = "dsh-mnemon/src/client/MnemonSaveAction.module.css";
8731
8789
  if (typeof document !== "undefined") {
8732
8790
  let tag = document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]");
@@ -8741,10 +8799,12 @@ window.__ModuleLoader__.load({
8741
8799
  "button": "ypSjoa_button",
8742
8800
  "candidate": "ypSjoa_candidate",
8743
8801
  "candidateHeading": "ypSjoa_candidateHeading",
8802
+ "destination": "ypSjoa_destination",
8744
8803
  "icon": "ypSjoa_icon",
8745
8804
  "modal": "ypSjoa_modal",
8746
8805
  "modalAction": "ypSjoa_modalAction",
8747
8806
  "modalContent": "ypSjoa_modalContent",
8807
+ "placeNote": "ypSjoa_placeNote",
8748
8808
  "readOnly": "ypSjoa_readOnly",
8749
8809
  "status": "ypSjoa_status",
8750
8810
  "truncated": "ypSjoa_truncated",
@@ -8753,10 +8813,167 @@ window.__ModuleLoader__.load({
8753
8813
  //#endregion
8754
8814
  //#region src/client/MnemonSaveAction.tsx
8755
8815
  const PREVIEW_LIMIT = 8e3;
8816
+ /** One working-memory entry as Runtime Memory stores it: one line of at most 8 KiB, without the § delimiter. */
8817
+ const RUNTIME_ENTRY_BYTES = 8192;
8818
+ const RUNTIME_PLACES = {
8819
+ "runtime:memory": "saveAction.to.memory",
8820
+ "runtime:user": "saveAction.to.user"
8821
+ };
8822
+ function isRuntimePlace(destination) {
8823
+ return destination === "runtime:memory" || destination === "runtime:user";
8824
+ }
8825
+ /**
8826
+ * The places a candidate can go. A layer takes it while it is on, its Source
8827
+ * runs and its writes are not switched off. What the status does not say
8828
+ * counts as available, so a partial status never hides the task Agent.
8829
+ */
8830
+ function saveDestinations(status, taskAgent, t) {
8831
+ const system = status?.memorySystem;
8832
+ const takes = (sourceTypeId) => {
8833
+ const layer = system?.configuration.layers[sourceTypeId];
8834
+ return layer?.enabled !== false && layer?.participation?.write !== "off" && (system?.sources === void 0 || system.sources.some((source) => source.sourceTypeId === sourceTypeId));
8835
+ };
8836
+ const runtimeOn = takes("runtime");
8837
+ const spacesOn = takes("memory-spaces");
8838
+ const spaces = spacesOn ? (status?.memoryBodies ?? []).filter((body) => body.active !== false && body.providerEnabled !== false && body.provider.capabilities.remember && ((body.provider.typeId ?? body.provider.id) !== "mnemon-native" || status?.commandFound !== false)) : [];
8839
+ const providerReady = spaces.length > 0 || status?.commandFound !== false || (status?.providerServices ?? []).some((provider) => provider.enabled && provider.configured);
8840
+ return [
8841
+ {
8842
+ value: "agent",
8843
+ label: t("saveAction.to.agent"),
8844
+ detail: t("saveAction.to.agentDetail"),
8845
+ disabled: taskAgent === false || !spacesOn || !providerReady
8846
+ },
8847
+ ...runtimeOn ? Object.keys(RUNTIME_PLACES).map((value) => ({
8848
+ value,
8849
+ label: t(RUNTIME_PLACES[value])
8850
+ })) : [],
8851
+ ...spaces.map((body) => ({
8852
+ value: `space:${body.id}`,
8853
+ label: body.name,
8854
+ detail: t("saveAction.to.space", { provider: body.provider.label })
8855
+ }))
8856
+ ];
8857
+ }
8858
+ /** The default instance of a Source type, as the workbench picks it, else the first. */
8859
+ function placeSource(catalog, sourceTypeId) {
8860
+ const sources = catalog.sources.filter((source) => source.sourceTypeId === sourceTypeId);
8861
+ return sources.find((source) => isDefaultSourceInstance(source.sourceInstanceKey, sourceTypeId)) ?? sources[0];
8862
+ }
8863
+ /** Write the text to the place the user chose, through that Source's own operation, with a fresh revision. */
8864
+ async function saveDirectly(client, destination, place, content, t) {
8865
+ const source = placeSource(await client.sourceManagementCatalog(), isRuntimePlace(destination) ? "runtime" : "memory-spaces");
8866
+ if (source === void 0) throw new Error(t("saveAction.unavailable"));
8867
+ if (isRuntimePlace(destination)) {
8868
+ const input = {
8869
+ action: "add",
8870
+ target: destination === "runtime:user" ? "user" : "memory",
8871
+ content
8872
+ };
8873
+ const result = (source.assistance?.includes("mutate") === true ? await client.assistSource(source.sourceInstanceKey, "mutate", input, source.revision, true) : await client.mutateSourceManagement(source.sourceInstanceKey, "mutate", input, source.revision, true)).value;
8874
+ if (result.message?.startsWith("Entry already exists") === true) return {
8875
+ action: "skipped",
8876
+ note: { key: "saveAction.saved.duplicate" }
8877
+ };
8878
+ return {
8879
+ action: "added",
8880
+ note: {
8881
+ key: result.maintenance === void 0 ? "saveAction.saved.runtime" : "saveAction.saved.runtimeMaintained",
8882
+ params: { count: result.entryCount },
8883
+ place: RUNTIME_PLACES[destination]
8884
+ }
8885
+ };
8886
+ }
8887
+ const result = (await client.mutateSourceManagement(source.sourceInstanceKey, "remember", {
8888
+ content,
8889
+ memoryBodyId: destination.slice(6),
8890
+ source: "user"
8891
+ }, source.revision, true)).value;
8892
+ const answered = typeof result.action === "string" ? result.action : "stored";
8893
+ const action = answered === "queued" ? "accepted" : answered;
8894
+ const outcome = writeOutcome(action);
8895
+ return {
8896
+ action,
8897
+ note: {
8898
+ key: outcome === "skipped" ? "saveAction.saved.duplicate" : outcome === "pending" ? "saveAction.saved.spacePending" : outcome === "written" || outcome === "updated" ? "saveAction.saved.space" : "saveAction.saved.spaceOther",
8899
+ params: {
8900
+ space: place,
8901
+ action: answered
8902
+ }
8903
+ }
8904
+ };
8905
+ }
8906
+ /**
8907
+ * What a working-memory place says about this text before it is saved, and
8908
+ * whether it can take it at all, by the Runtime Source's own rules: one line of
8909
+ * at most 8 KiB and never more than its file, without the § delimiter. A full
8910
+ * file keeps its entries within 70% of its limit, less the new one.
8911
+ */
8912
+ function runtimePlaceNote(destination, content, usage, taskAgent, agentOpen, t) {
8913
+ if (!isRuntimePlace(destination) || content === "") return void 0;
8914
+ const user = destination === "runtime:user";
8915
+ const target = t(RUNTIME_PLACES[destination]);
8916
+ const entry = content.replace(/\s+/gu, " ");
8917
+ if (entry.includes("§")) return {
8918
+ text: t("saveAction.place.delimiter", { target }),
8919
+ blocked: true
8920
+ };
8921
+ const bytes = new TextEncoder().encode(entry).length;
8922
+ const room = usage?.[user ? "user" : "memory"];
8923
+ const cap = Math.min(RUNTIME_ENTRY_BYTES, room?.limit ?? RUNTIME_ENTRY_BYTES);
8924
+ const orAgent = agentOpen ? t("saveAction.place.orAgent") : "";
8925
+ if (bytes > cap) {
8926
+ const exact = humanBytes(bytes) === humanBytes(cap);
8927
+ return {
8928
+ text: t("saveAction.place.tooLong", {
8929
+ target,
8930
+ size: exact ? `${bytes} B` : humanBytes(bytes),
8931
+ limit: exact ? `${cap} B` : humanBytes(cap)
8932
+ }) + orAgent,
8933
+ blocked: true
8934
+ };
8935
+ }
8936
+ if (room === void 0 || room.used + bytes + (room.used > 0 ? 4 : 0) <= room.limit) return void 0;
8937
+ const kept = Math.floor(room.limit * .7) - 4;
8938
+ const keep = Math.max(0, kept - bytes);
8939
+ const used = humanBytes(room.used);
8940
+ const limit = humanBytes(room.limit);
8941
+ if (user) {
8942
+ if (taskAgent === false) return {
8943
+ text: t("saveAction.place.userFull"),
8944
+ blocked: true
8945
+ };
8946
+ if (keep === 0) return {
8947
+ text: t("saveAction.place.userTooLong", {
8948
+ size: humanBytes(bytes),
8949
+ max: humanBytes(Math.max(0, kept))
8950
+ }) + orAgent,
8951
+ blocked: true
8952
+ };
8953
+ return {
8954
+ text: t("saveAction.place.userCondense", {
8955
+ used,
8956
+ limit,
8957
+ keep: humanBytes(keep)
8958
+ }),
8959
+ blocked: false
8960
+ };
8961
+ }
8962
+ return {
8963
+ text: t(keep === 0 ? "saveAction.place.fullAll" : "saveAction.place.full", {
8964
+ target,
8965
+ used,
8966
+ limit,
8967
+ keep: humanBytes(keep)
8968
+ }),
8969
+ blocked: false
8970
+ };
8971
+ }
8756
8972
  /**
8757
8973
  * Save-to-memory action on finalized assistant messages. A task Agent decides
8758
- * whether the (editable) reply is worth keeping and writes it; the dialog
8759
- * shows the same receipt as Save to memory on the Memory Spaces page.
8974
+ * whether the (editable) reply is worth keeping and where it goes, or the user
8975
+ * names the place and the text goes there through that place's own write. The
8976
+ * dialog shows the same receipt as Save to memory on the Memory Spaces page.
8760
8977
  */
8761
8978
  const MnemonSaveAction = (0, react.memo)(function MnemonSaveAction({ messageId, sessionId, connection, settingsScope, localeRuntime, t }) {
8762
8979
  const subscribeLocale = (0, react.useCallback)((listener) => localeRuntime.subscribe(listener), [localeRuntime]);
@@ -8769,15 +8986,19 @@ window.__ModuleLoader__.load({
8769
8986
  const [open, setOpen] = (0, react.useState)(false);
8770
8987
  const [writeEnabled, setWriteEnabled] = (0, react.useState)(void 0);
8771
8988
  const [taskAgent, setTaskAgent] = (0, react.useState)(void 0);
8989
+ const [status, setStatus] = (0, react.useState)(void 0);
8990
+ const [chosen, setChosen] = (0, react.useState)(void 0);
8772
8991
  const [candidate, setCandidate] = (0, react.useState)(void 0);
8773
8992
  const [truncated, setTruncated] = (0, react.useState)(false);
8774
8993
  const [missing, setMissing] = (0, react.useState)(false);
8775
8994
  const [submitting, setSubmitting] = (0, react.useState)(false);
8776
8995
  const [outcome, setOutcome] = (0, react.useState)(null);
8996
+ const [usage, setUsage] = (0, react.useState)(void 0);
8777
8997
  const candidateId = (0, react.useId)();
8778
8998
  const openRef = (0, react.useRef)(false);
8779
8999
  const requestVersionRef = (0, react.useRef)(0);
8780
- const submitActiveRef = (0, react.useRef)(false);
9000
+ const inFlightRef = (0, react.useRef)(void 0);
9001
+ const unseenRef = (0, react.useRef)(void 0);
8781
9002
  const setPanelOpen = (next) => {
8782
9003
  requestVersionRef.current += 1;
8783
9004
  openRef.current = next;
@@ -8787,33 +9008,45 @@ window.__ModuleLoader__.load({
8787
9008
  if (!open) {
8788
9009
  setWriteEnabled(void 0);
8789
9010
  setTaskAgent(void 0);
9011
+ setStatus(void 0);
9012
+ setChosen(void 0);
8790
9013
  setCandidate(void 0);
8791
9014
  setTruncated(false);
8792
9015
  setMissing(false);
8793
- setSubmitting(submitActiveRef.current);
9016
+ setSubmitting(inFlightRef.current !== void 0);
8794
9017
  setOutcome(null);
9018
+ setUsage(void 0);
8795
9019
  return;
8796
9020
  }
8797
9021
  const requestVersion = ++requestVersionRef.current;
8798
9022
  let alive = true;
8799
- setSubmitting(submitActiveRef.current);
9023
+ setSubmitting(inFlightRef.current !== void 0);
9024
+ const unseen = unseenRef.current;
9025
+ unseenRef.current = void 0;
9026
+ const restored = unseen ?? inFlightRef.current;
9027
+ if (restored !== void 0) setChosen(restored.destination);
9028
+ if (unseen !== void 0) setOutcome(unseen);
8800
9029
  const client = new MnemonClient(connection, sessionId);
8801
9030
  client.status().then((status) => {
8802
9031
  if (!alive || requestVersionRef.current !== requestVersion) return;
8803
9032
  setWriteEnabled(status.writeEnabled && managementWritable);
8804
9033
  setTaskAgent(status.lifecycle?.taskAgentAvailable);
9034
+ setStatus(status);
8805
9035
  }).catch(() => {
8806
9036
  if (alive && requestVersionRef.current === requestVersion) setWriteEnabled(false);
8807
9037
  });
8808
9038
  client.assistantMessageText(messageId).then((result) => {
8809
9039
  if (!alive || requestVersionRef.current !== requestVersion) return;
8810
- if (result === null || result.text === "") setMissing(true);
9040
+ if (restored !== void 0) setCandidate(restored.content);
9041
+ else if (result === null || result.text === "") setMissing(true);
8811
9042
  else {
8812
9043
  setTruncated(result.text.length > PREVIEW_LIMIT);
8813
9044
  setCandidate(result.text.slice(0, PREVIEW_LIMIT));
8814
9045
  }
8815
9046
  }).catch(() => {
8816
- if (alive && requestVersionRef.current === requestVersion) setMissing(true);
9047
+ if (!alive || requestVersionRef.current !== requestVersion) return;
9048
+ if (restored !== void 0) setCandidate(restored.content);
9049
+ else setMissing(true);
8817
9050
  });
8818
9051
  return () => {
8819
9052
  alive = false;
@@ -8826,37 +9059,83 @@ window.__ModuleLoader__.load({
8826
9059
  managementWritable
8827
9060
  ]);
8828
9061
  const content = candidate?.trim() ?? "";
8829
- const answered = outcome !== null && outcome.content === content;
8830
- const canSubmit = content !== "" && !submitting && writeEnabled === true && taskAgent !== false && !answered;
9062
+ const options = saveDestinations(status, taskAgent, t);
9063
+ const usable = options.filter((option) => option.disabled !== true);
9064
+ const destination = chosen ?? usable[0]?.value ?? "agent";
9065
+ const selected = options.find((option) => option.value === destination);
9066
+ const runtimeTarget = isRuntimePlace(destination) ? destination : void 0;
9067
+ (0, react.useEffect)(() => {
9068
+ if (!open || runtimeTarget === void 0 || usage !== void 0 || status === void 0) return;
9069
+ const runtime = status.memorySystem === void 0 ? void 0 : placeSource(status.memorySystem, "runtime");
9070
+ if (runtime === void 0) {
9071
+ setUsage({});
9072
+ return;
9073
+ }
9074
+ let alive = true;
9075
+ new MnemonClient(connection, sessionId).readSourceManagement(runtime.sourceInstanceKey, "snapshot").then((result) => {
9076
+ if (alive) setUsage(result.value?.targets ?? {});
9077
+ }, () => {
9078
+ if (alive) setUsage({});
9079
+ });
9080
+ return () => {
9081
+ alive = false;
9082
+ };
9083
+ }, [
9084
+ open,
9085
+ runtimeTarget,
9086
+ usage,
9087
+ status,
9088
+ connection,
9089
+ sessionId
9090
+ ]);
9091
+ const placeNote = runtimePlaceNote(destination, content, usage, taskAgent, options.some((option) => option.value === "agent" && option.disabled !== true), t);
9092
+ const answered = outcome !== null && outcome.error === void 0 && outcome.content === content && outcome.destination === destination;
9093
+ const placeReady = runtimeTarget === void 0 || usage !== void 0;
9094
+ const canSubmit = content !== "" && !submitting && writeEnabled === true && selected !== void 0 && selected.disabled !== true && placeReady && placeNote?.blocked !== true && !answered;
8831
9095
  const submit = () => {
8832
- if (!canSubmit || submitActiveRef.current) return;
9096
+ if (!canSubmit || inFlightRef.current !== void 0) return;
8833
9097
  const requestVersion = requestVersionRef.current;
8834
- submitActiveRef.current = true;
9098
+ const target = destination;
9099
+ inFlightRef.current = {
9100
+ content,
9101
+ destination: target
9102
+ };
8835
9103
  setSubmitting(true);
8836
9104
  setOutcome(null);
8837
- new MnemonClient(connection, sessionId).supervise(content, messageId).then((result) => {
8838
- if (openRef.current && requestVersionRef.current === requestVersion) setOutcome({
8839
- content,
8840
- action: result.action,
8841
- summary: result.summary
8842
- });
8843
- }).catch((reason) => {
8844
- if (openRef.current && requestVersionRef.current === requestVersion) setOutcome({
8845
- content,
8846
- error: message(reason)
8847
- });
8848
- }).finally(() => {
8849
- submitActiveRef.current = false;
9105
+ const client = new MnemonClient(connection, sessionId);
9106
+ (target === "agent" ? client.supervise(content, messageId).then((result) => ({
9107
+ action: result.action,
9108
+ summary: result.summary
9109
+ })) : saveDirectly(client, target, selected?.label ?? target, content, t)).then((result) => ({
9110
+ content,
9111
+ destination: target,
9112
+ ...result
9113
+ }), (reason) => ({
9114
+ content,
9115
+ destination: target,
9116
+ error: message(reason)
9117
+ })).then((result) => {
9118
+ inFlightRef.current = void 0;
9119
+ if (openRef.current && (target !== "agent" || requestVersionRef.current === requestVersion)) setOutcome(result);
9120
+ else if (!openRef.current && target !== "agent") unseenRef.current = result;
8850
9121
  if (openRef.current) setSubmitting(false);
9122
+ if (isRuntimePlace(target) && result.error === void 0) setUsage(void 0);
8851
9123
  });
8852
9124
  };
8853
9125
  const viewMemory = () => {
9126
+ const runtime = outcome !== null && isRuntimePlace(outcome.destination);
8854
9127
  setPanelOpen(false);
8855
9128
  dispatchMnemonAnchor({
8856
- page: "memory-spaces/content",
9129
+ page: runtime ? "runtime/entries" : "memory-spaces/content",
9130
+ ...runtime ? { seed: outcome.content.replace(/\s+/gu, " ") } : {},
8857
9131
  ...sessionId === void 0 ? {} : { sessionId }
8858
9132
  });
8859
9133
  };
9134
+ const direct = writeEnabled === true && destination !== "agent";
9135
+ const receiptSummary = outcome?.note === void 0 ? outcome?.summary : t(outcome.note.key, {
9136
+ ...outcome.note.params,
9137
+ ...outcome.note.place === void 0 ? {} : { target: t(outcome.note.place) }
9138
+ });
8860
9139
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
8861
9140
  className: MnemonSaveAction_module_css_default.wrap,
8862
9141
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Tooltip, {
@@ -8880,7 +9159,7 @@ window.__ModuleLoader__.load({
8880
9159
  onClose: () => setPanelOpen(false),
8881
9160
  title: t("saveAction.title"),
8882
9161
  closeLabel: t("saveAction.close"),
8883
- description: t("saveAction.hint"),
9162
+ description: t(direct ? "saveAction.hintDirect" : "saveAction.hint"),
8884
9163
  className: MnemonSaveAction_module_css_default.modal,
8885
9164
  contentClassName: MnemonSaveAction_module_css_default.modalContent,
8886
9165
  footer: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.Button, {
@@ -8894,7 +9173,7 @@ window.__ModuleLoader__.load({
8894
9173
  className: MnemonSaveAction_module_css_default.modalAction,
8895
9174
  disabled: !canSubmit,
8896
9175
  onClick: submit,
8897
- children: submitting ? t("saveAction.submitting") : t("saveAction.submit")
9176
+ children: direct ? submitting ? t("saveAction.saving") : t("saveAction.save") : submitting ? t("saveAction.submitting") : t("saveAction.submit")
8898
9177
  })] }),
8899
9178
  children: [
8900
9179
  writeEnabled === false && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -8919,8 +9198,8 @@ window.__ModuleLoader__.load({
8919
9198
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
8920
9199
  htmlFor: candidateId,
8921
9200
  children: t("saveAction.candidate")
8922
- }), writeEnabled === true && taskAgent !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskAgentTag, {
8923
- available: taskAgent,
9201
+ }), writeEnabled === true && !direct && taskAgent !== void 0 && usable.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TaskAgentTag, {
9202
+ available: taskAgent && selected?.disabled !== true,
8924
9203
  t
8925
9204
  })]
8926
9205
  }),
@@ -8934,15 +9213,34 @@ window.__ModuleLoader__.load({
8934
9213
  truncated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
8935
9214
  className: MnemonSaveAction_module_css_default.truncated,
8936
9215
  children: t("saveAction.truncated", { limit: PREVIEW_LIMIT })
9216
+ }),
9217
+ writeEnabled === true && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SelectField, {
9218
+ className: MnemonSaveAction_module_css_default.destination,
9219
+ label: t("saveAction.to"),
9220
+ value: destination,
9221
+ options,
9222
+ disabled: submitting,
9223
+ onChange: setChosen
9224
+ }),
9225
+ writeEnabled === true && placeNote !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
9226
+ className: MnemonSaveAction_module_css_default.placeNote,
9227
+ "data-tone": placeNote.blocked ? "blocked" : "caution",
9228
+ children: placeNote.text
9229
+ }),
9230
+ writeEnabled === true && status !== void 0 && usable.length === 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("small", {
9231
+ className: MnemonSaveAction_module_css_default.placeNote,
9232
+ "data-tone": "blocked",
9233
+ children: t("saveAction.noPlace")
8937
9234
  })
8938
9235
  ]
8939
9236
  }),
8940
9237
  outcome !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(WriteReceipt, {
8941
9238
  t,
8942
9239
  action: outcome.action,
8943
- summary: outcome.summary,
9240
+ summary: receiptSummary,
8944
9241
  error: outcome.error,
8945
- onView: viewMemory
9242
+ onView: viewMemory,
9243
+ ...isRuntimePlace(outcome.destination) ? { viewLabel: t("receipt.viewRuntime") } : {}
8946
9244
  })
8947
9245
  ]
8948
9246
  })]
@@ -12236,6 +12534,7 @@ window.__ModuleLoader__.load({
12236
12534
  componentChanges: seats.components,
12237
12535
  t: translate
12238
12536
  });
12537
+ const renderContributed = (packageName, owner) => renderComponentRegion(ctx, MNEMON_COMPONENT_SETTINGS_SLOT, packageName, owner);
12239
12538
  ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
12240
12539
  name: "plugins.bundle.config",
12241
12540
  key: MNEMON_PACKAGE_NAME,
@@ -12244,9 +12543,11 @@ window.__ModuleLoader__.load({
12244
12543
  kind: "keyed",
12245
12544
  scope: "root"
12246
12545
  } },
12247
- inject: configurationServices
12546
+ inject: () => ({
12547
+ ...configurationServices(),
12548
+ renderContributed
12549
+ })
12248
12550
  }, MnemonSettingsHost));
12249
- const renderContributed = (packageName, owner) => renderComponentRegion(ctx, MNEMON_COMPONENT_SETTINGS_SLOT, packageName, owner);
12250
12551
  for (const { rowId, packageName } of STARTER_COMPONENT_ROWS) ctx.slots.inject("plugins.row.config", () => ctx.slots.register({
12251
12552
  name: "plugins.row.config",
12252
12553
  key: `${MNEMON_PACKAGE_NAME}#${rowId}`,
package/lib/index.js CHANGED
@@ -7,7 +7,7 @@ import z from "@deepseek-ai/schemastery";
7
7
  import { basename, delimiter, dirname, isAbsolute, join, normalize, resolve } from "node:path";
8
8
  import { resolveEmbedding, resolvePersistenceStrategy, resolveRecallQuality } from "dsh-mnemon-source-memory-spaces";
9
9
  import { createHash, randomUUID } from "node:crypto";
10
- import { accessSync, closeSync, constants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
10
+ import { accessSync, chmodSync, closeSync, constants, copyFileSync, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, rmSync, statSync, writeFileSync } from "node:fs";
11
11
  import { homedir } from "node:os";
12
12
  import { strToU8, unzipSync, zipSync } from "fflate";
13
13
  import { DOCUMENTS_ACTIVE_LIMIT_BYTES, DOCUMENTS_VERSION } from "dsh-mnemon-source-documents/contracts";
@@ -862,6 +862,33 @@ function readCurrentRuntime(root, limits = RUNTIME_MEMORY_LIMITS) {
862
862
  const path = join(root, "runtime", "memories.json");
863
863
  return existsSync(path) ? parseRuntime(JSON.parse(readFileSync(path, "utf8")), limits) : emptyRuntime();
864
864
  }
865
+ /**
866
+ * A Pack carries no local archive: the one MEMORY.md wrote while no Memory Space
867
+ * could take its entries holds their only copy, so an import keeps it (#336).
868
+ * Its files and folders are carried; a link is neither carried nor followed.
869
+ */
870
+ function keepRuntimeArchive(root, stagedRuntime) {
871
+ const copy = (from, to) => {
872
+ let stat;
873
+ try {
874
+ stat = lstatSync(from);
875
+ } catch {
876
+ return;
877
+ }
878
+ if (stat.isSymbolicLink()) return;
879
+ if (stat.isFile()) {
880
+ copyFileSync(from, to);
881
+ chmodSync(to, 384);
882
+ } else if (stat.isDirectory()) {
883
+ mkdirSync(to, {
884
+ recursive: true,
885
+ mode: 448
886
+ });
887
+ for (const name of readdirSync(from)) copy(join(from, name), join(to, name));
888
+ }
889
+ };
890
+ copy(join(root, "runtime", "archived"), join(stagedRuntime, "archived"));
891
+ }
865
892
  function writeRuntime(directory, file) {
866
893
  mkdirSync(directory, {
867
894
  recursive: true,
@@ -1120,6 +1147,7 @@ function stageImport(root, pack, components, mode, runtimeLimits) {
1120
1147
  if (components.includes("runtime")) {
1121
1148
  const runtime = mode === "merge" ? mergeRuntime(root, pack, runtimeLimits) : parseRuntime(json$1(pack.files["payload/runtime/memories.json"], "payload/runtime/memories.json"), runtimeLimits);
1122
1149
  writeRuntime(join(staging, "runtime"), runtime);
1150
+ keepRuntimeArchive(root, join(staging, "runtime"));
1123
1151
  }
1124
1152
  if (components.includes("documents")) {
1125
1153
  const documents = mode === "merge" ? mergeDocuments(root, pack) : archiveDocuments(pack);
@@ -3437,6 +3465,14 @@ function pendingMutationContext(plan) {
3437
3465
  ...plan.excluded === void 0 ? [] : [`- Matched committed entry: excluded by the host because it will be ${plan.action === "replace" ? "replaced" : "removed"}`]
3438
3466
  ].join("\n");
3439
3467
  }
3468
+ /**
3469
+ * A space that can take a MEMORY.md archive or a Document's index: active, its
3470
+ * Provider enabled and ready, with exact writes and safe forget. A Mnemon Native
3471
+ * space stays listed, and active, while its CLI is missing; it takes no writes then.
3472
+ */
3473
+ function archivableSpace(directory, body) {
3474
+ return body.active && body.providerEnabled !== false && body.provider.capabilities.remember === true && body.provider.capabilities.writeMode === "exact" && body.provider.capabilities.forget === true && directory.providers?.find((provider) => provider.id === body.provider.id)?.serviceConfigured !== false;
3475
+ }
3440
3476
  function compactedBudget(plan) {
3441
3477
  const pendingBytes = plan.pending === void 0 ? 0 : Buffer.byteLength(plan.pending.content, "utf8");
3442
3478
  const separatorBytes = plan.pending === void 0 || plan.entries.length === 0 ? 0 : Buffer.byteLength(RUNTIME_ENTRY_DELIMITER, "utf8");
@@ -3595,6 +3631,22 @@ function destinationFromCommittedMutation(result, memoryBodyId, content) {
3595
3631
  digest
3596
3632
  };
3597
3633
  }
3634
+ /**
3635
+ * The memory a skipped write names as already holding exactly this text, when
3636
+ * its receipt carries that memory's id and stored text. Anything less proves
3637
+ * nothing, and the caller verifies by search instead.
3638
+ */
3639
+ function skippedExactDestination(result, memoryBodyId, content) {
3640
+ const value = optionalObject(result);
3641
+ const id = typeof value?.id === "string" ? value.id.trim() : "";
3642
+ if (id === "" || value?.content !== content) return void 0;
3643
+ if (value.memoryBodyId !== void 0 && value.memoryBodyId !== memoryBodyId) return void 0;
3644
+ return {
3645
+ layerId: "memory-spaces",
3646
+ reference: `memory-space:${encodeURIComponent(memoryBodyId)}/item:${encodeURIComponent(id)}`,
3647
+ digest: sha256(content)
3648
+ };
3649
+ }
3598
3650
  function mutationStates(result) {
3599
3651
  const value = optionalObject(result);
3600
3652
  return [value?.action, value?.status].filter((entry) => typeof entry === "string").map((entry) => entry.trim().toLocaleLowerCase());
@@ -3892,11 +3944,13 @@ var MnemonSubagentCoordinator = class {
3892
3944
  commit: () => runtime.mutate("mutate", request, signal),
3893
3945
  memorySpaces: async () => {
3894
3946
  if (!graph.config.writeEnabled || !this.runtimeSource.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only");
3895
- assertParticipation(graph.config, "memory-spaces", "write", "automatic");
3947
+ if (!allowsParticipation(graph.config, "memory-spaces", "write", "automatic") || !lease.generation.sourceInstances().some((source) => source.sourceTypeId === "memory-spaces")) return void 0;
3896
3948
  const source = graph.source("memory-spaces", scope).forGeneration(lease.generation);
3949
+ const { sourceInstanceKey } = await source.identity();
3897
3950
  return {
3898
3951
  source,
3899
- cleanup: source
3952
+ cleanup: source,
3953
+ elsewhere: await this.archivableElsewhere(graph, scope, lease.generation, sourceInstanceKey, signal)
3900
3954
  };
3901
3955
  },
3902
3956
  model: (...args) => this.runtimeModel(scope, parent, signal, ...args)
@@ -3946,7 +4000,7 @@ var MnemonSubagentCoordinator = class {
3946
4000
  },
3947
4001
  memorySpaces: async () => {
3948
4002
  if (!authorize()) throw new Error("Runtime capacity maintenance is no longer authorized");
3949
- return this.runtimeArchiveSource(graph, turn.scope, turn.view, lease.generation, turn);
4003
+ return this.runtimeArchiveSource(graph, turn.scope, turn.view, lease.generation, turn, signal);
3950
4004
  },
3951
4005
  model: (...args) => this.runtimeModel(turn.scope, parent, signal, ...args)
3952
4006
  }, input, signal);
@@ -3992,7 +4046,7 @@ var MnemonSubagentCoordinator = class {
3992
4046
  scenario: "management.runtime-capacity",
3993
4047
  budget: DEFAULT_MEMORY_VIEW_BUDGET
3994
4048
  }, signal);
3995
- return this.runtimeArchiveSource(graph, request.scope, view, generation);
4049
+ return this.runtimeArchiveSource(graph, request.scope, view, generation, void 0, signal);
3996
4050
  },
3997
4051
  model: (...args) => this.runtimeModel(request.scope, void 0, signal, ...args)
3998
4052
  }, request.input, signal);
@@ -4006,11 +4060,14 @@ var MnemonSubagentCoordinator = class {
4006
4060
  lease.release();
4007
4061
  }
4008
4062
  }
4009
- async runtimeArchiveSource(graph, scope, view, generation, turn) {
4063
+ async runtimeArchiveSource(graph, scope, view, generation, turn, signal) {
4010
4064
  if (!graph.config.writeEnabled || !this.runtimeSource.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only");
4011
- assertParticipation(graph.config, "memory-spaces", "write", "automatic");
4065
+ if (!allowsParticipation(graph.config, "memory-spaces", "write", "automatic")) return void 0;
4012
4066
  const candidates = generation.sourceInstances().filter((source) => source.sourceTypeId === "memory-spaces" && view.actionOffers.some((offer) => offer.sourceInstanceKey === source.sourceInstanceKey && offer.sourceActionId === "remember" && offer.authority === void 0));
4013
- if (candidates.length === 0) throw new Error("Source Action is not offered by the current View: memory-spaces/remember");
4067
+ if (candidates.length === 0) {
4068
+ if (await this.noArchivableMemorySpace(graph, scope, generation, signal)) return void 0;
4069
+ throw new Error("Source Action is not offered by the current View: memory-spaces/remember");
4070
+ }
4014
4071
  if (candidates.length !== 1) throw new Error("Runtime archival requires one unambiguous writable Memory Spaces Source");
4015
4072
  const cleanup = graph.source("memory-spaces", scope).forInstance(candidates[0].sourceInstanceKey).forGeneration(generation);
4016
4073
  let source = cleanup;
@@ -4024,6 +4081,46 @@ var MnemonSubagentCoordinator = class {
4024
4081
  writeScope: {
4025
4082
  viewId: view.id,
4026
4083
  grant
4084
+ },
4085
+ elsewhere: await this.archivableElsewhere(graph, scope, generation, candidates[0].sourceInstanceKey, signal)
4086
+ };
4087
+ }
4088
+ /**
4089
+ * MEMORY.md keeps working without a Memory Space to archive into: the Runtime
4090
+ * Source moves the entries compaction leaves out to its local archive, as exact
4091
+ * text, in the same commit as the compaction and the pending write (#336).
4092
+ */
4093
+ async archiveRuntimeLocally(context, request, plan, reason, signal) {
4094
+ signal.throwIfAborted();
4095
+ if (plan.localArchive !== true) throw new Error(`runtime memory archival found no Memory Space that can take the archive (${reason}), and this Runtime Source cannot archive locally; pending write was not committed and existing runtime entries are unchanged; update dsh-mnemon-source-runtime, activate a supported Memory Space, or increase runtimeMemory.memoryLimitBytes`);
4096
+ context.assertWritable?.();
4097
+ const runId = `host-${randomUUID()}`;
4098
+ const compacted = plan.entries.map(({ content, importance, branches }) => ({
4099
+ content,
4100
+ importance,
4101
+ ...branches === void 0 ? {} : { branches }
4102
+ }));
4103
+ const mutation = await context.runtime.mutateResult("compact-and-mutate", {
4104
+ revision: plan.revision,
4105
+ mutation: request,
4106
+ compacted,
4107
+ maxBytes: compactedBudget(plan),
4108
+ archive: "local"
4109
+ }, signal);
4110
+ const archived = mutation.value.archived?.entries ?? 0;
4111
+ this.counters.migrations += 1;
4112
+ this.counters.lastRunId = runId;
4113
+ this.counters.lastOperation = "migration";
4114
+ this.counters.lastAt = (/* @__PURE__ */ new Date()).toISOString();
4115
+ return {
4116
+ ...mutation.value,
4117
+ revision: mutation.revision,
4118
+ maintenance: {
4119
+ kind: "local-archive",
4120
+ runId,
4121
+ provider: "host",
4122
+ summary: `Moved ${archived} MEMORY.md ${archived === 1 ? "entry" : "entries"} to the local archive${mutation.value.archived === void 0 ? "" : ` ${mutation.value.archived.path}`}, since ${reason}.`,
4123
+ memoryBodyIds: []
4027
4124
  }
4028
4125
  };
4029
4126
  }
@@ -4160,15 +4257,22 @@ ${naturalRequest(request)}`;
4160
4257
  const document = await controller.read("document", { id }, signal);
4161
4258
  if (document.status !== "active") throw new Error("only active documents can be archived");
4162
4259
  const source = documentMigrationSource(document);
4163
- const memoryService = (await this.assertAutomaticMemoryWrite(parent)).forGeneration(lease.generation);
4260
+ const indexSpaces = await this.documentIndexSpaces(parent, lease.generation, signal);
4261
+ if (indexSpaces === void 0) return this.archiveDocumentLocally(controller, document, "no Memory Spaces layer takes writes here", signal);
4262
+ const memoryService = indexSpaces.forGeneration(lease.generation);
4164
4263
  const identity = await memoryService.identity();
4165
4264
  const cleanup = graph.source("memory-spaces", agentScope(parent, graph.config)).forInstance(identity.sourceInstanceKey).forGeneration(lease.generation);
4166
4265
  const grant = authority?.context.view.readGrants.find((grant) => grant.sourceInstanceKey === identity.sourceInstanceKey && grant.schema === "dsh-mnemon.memory-spaces/v1");
4167
4266
  if (authority !== void 0 && grant === void 0) throw new Error("document archive requires a Memory Space namespace grant");
4168
4267
  const allowed = grant === void 0 ? void 0 : new Set(strings(object$6(grant.value).memoryBodyIds));
4169
- const eligible = (body) => body.active && body.providerEnabled !== false && body.provider.capabilities.remember && body.provider.capabilities.forget && body.provider.capabilities.writeMode === "exact" && (allowed === void 0 || allowed.has(body.id));
4170
- const bodies = (await memoryService.read("body-directory", null, signal)).items.filter(eligible);
4171
- if (bodies.length === 0) throw new Error("document archive requires an existing active Memory Space with exact writes and safe forget");
4268
+ const directory = await memoryService.read("body-directory", null, signal);
4269
+ const indexable = (body) => archivableSpace(directory, body);
4270
+ const eligible = (body) => indexable(body) && (allowed === void 0 || allowed.has(body.id));
4271
+ const bodies = directory.items.filter(eligible);
4272
+ if (bodies.length === 0) {
4273
+ if (!directory.items.some(indexable) && !await this.archivableElsewhere(graph, agentScope(parent, graph.config), lease.generation, identity.sourceInstanceKey, signal)) return this.archiveDocumentLocally(controller, document, "no Memory Space can take its index", signal);
4274
+ throw new Error("document archive requires an existing active Memory Space with exact writes and safe forget");
4275
+ }
4172
4276
  const { provider, runId, result } = await this.delegate(parent, "document-archive", "Plan managed document archive", documentArchivePrompt(document, source, bodies), [], DOCUMENT_ARCHIVE_SCHEMA, signal, "spawn", DOCUMENT_ARCHIVE_PERSONA);
4173
4277
  const value = object$6(result.structured);
4174
4278
  const summary = typeof value.summary === "string" ? value.summary.trim() : "";
@@ -4220,18 +4324,22 @@ ${naturalRequest(request)}`;
4220
4324
  if (written.length !== 1) throw new Error("document archive requires exactly one index receipt");
4221
4325
  if (isNew && receiptId !== void 0) destination = destinationFromCommittedMutation(receipt, memoryBodyId, content);
4222
4326
  else if (states.includes("skipped")) {
4223
- const exact = (await memoryService.read("search", {
4224
- query: document.contentHash,
4225
- mode: "keyword",
4226
- limit: 50,
4227
- memoryBodyIds: [memoryBodyId]
4228
- }, signal)).results.find((item) => item.memoryBodyId === memoryBodyId && item.id === receiptId && item.content === content);
4229
- if (exact === void 0) throw new Error(`document archive skipped index lacks exact durable evidence in Memory Space ${memoryBodyId}`);
4230
- destination = {
4231
- layerId: "memory-spaces",
4232
- reference: `memory-space:${encodeURIComponent(memoryBodyId)}/item:${encodeURIComponent(exact.id)}`,
4233
- digest: sha256(exact.content)
4234
- };
4327
+ const named = skippedExactDestination(receipt, memoryBodyId, content);
4328
+ if (named !== void 0) destination = named;
4329
+ else {
4330
+ const exact = (await memoryService.read("search", {
4331
+ query: document.contentHash,
4332
+ mode: "keyword",
4333
+ limit: 50,
4334
+ memoryBodyIds: [memoryBodyId]
4335
+ }, signal)).results.find((item) => item.memoryBodyId === memoryBodyId && item.id === receiptId && item.content === content);
4336
+ if (exact === void 0) throw new Error(`document archive skipped index lacks exact durable evidence in Memory Space ${memoryBodyId}`);
4337
+ destination = {
4338
+ layerId: "memory-spaces",
4339
+ reference: `memory-space:${encodeURIComponent(memoryBodyId)}/item:${encodeURIComponent(exact.id)}`,
4340
+ digest: sha256(exact.content)
4341
+ };
4342
+ }
4235
4343
  } else throw new Error(`document archive received no reversible committed index receipt in Memory Space ${memoryBodyId}`);
4236
4344
  }
4237
4345
  signal.throwIfAborted();
@@ -4293,6 +4401,7 @@ ${naturalRequest(request)}`;
4293
4401
  if (plan.entries.length === 0) throw new Error("runtime memory capacity was exceeded without entries available for maintenance");
4294
4402
  if (request.target === "user") return this.compactUserAndCommit(context, request, plan, signal);
4295
4403
  const archive = await context.memorySpaces();
4404
+ if (archive === void 0) return this.archiveRuntimeLocally(context, request, plan, "no Memory Space takes automatic writes here", signal);
4296
4405
  const memoryService = archive.source;
4297
4406
  let allowed = archive.memoryBodyIds;
4298
4407
  const readDirectory = async () => {
@@ -4306,13 +4415,16 @@ ${naturalRequest(request)}`;
4306
4415
  return catalog;
4307
4416
  };
4308
4417
  const catalog = await readDirectory();
4309
- const writable = (body) => body.active && body.providerEnabled !== false && body.provider.capabilities.remember === true && (allowed === void 0 || allowed.has(body.id));
4310
- const eligible = (body) => writable(body) && body.provider.capabilities.writeMode === "exact" && body.provider.capabilities.forget === true;
4418
+ const ready = (body) => catalog.providers?.find((provider) => provider.id === body.provider.id)?.serviceConfigured !== false;
4419
+ const writable = (body) => body.active && body.providerEnabled !== false && body.provider.capabilities.remember === true && ready(body) && (allowed === void 0 || allowed.has(body.id));
4420
+ const exact = (body) => body.provider.capabilities.writeMode === "exact" && body.provider.capabilities.forget === true;
4421
+ const eligible = (body) => writable(body) && exact(body);
4311
4422
  const eligibleBodies = catalog.items.filter(eligible);
4312
4423
  if (eligibleBodies.length === 0) {
4313
4424
  const writableBodies = catalog.items.filter(writable);
4314
4425
  const unsupported = writableBodies.map((body) => `${body.id} (provider=${body.provider.id}, writeMode=${body.provider.capabilities.writeMode}, forget=${body.provider.capabilities.forget})`).join(", ");
4315
4426
  const reason = catalog.items.length === 0 ? "Memory Space body-directory is empty" : allowed?.size === 0 ? "current View write scope is empty" : allowed !== void 0 && catalog.items.some((body) => body.active && body.providerEnabled !== false && body.provider.capabilities.remember && !allowed?.has(body.id)) && writableBodies.length === 0 ? "current View write scope excludes the active writable Memory Spaces" : writableBodies.length === 0 ? "no active writable Memory Space in the authorized scope" : "authorized destinations lack exact writes or safe forget";
4427
+ if (!catalog.items.some((body) => archivableSpace(catalog, body)) && archive.elsewhere !== true) return this.archiveRuntimeLocally(context, request, plan, `${reason}${unsupported === "" ? "" : ` (${unsupported})`}`, signal);
4316
4428
  const scope = archive.writeScope === void 0 ? "management" : `${archive.writeScope.grant.sourceInstanceKey}/${catalog.writeScope === void 0 ? "pinned-read" : "source-write"}`;
4317
4429
  throw new Error(`runtime memory archival requires an existing active writable Memory Space with exact writes and safe forget; ${reason}; scope=${scope}, catalog=${catalog.items.length}, authorized=${allowed?.size ?? "management"}, writable=${writableBodies.length}; pending write was not committed and existing runtime entries are unchanged; retry the same request in a new turn after activating a supported authorized Memory Space, or increase runtimeMemory.memoryLimitBytes${unsupported === "" ? "" : `; unsupported destinations: ${unsupported}`}`);
4318
4430
  }
@@ -4563,12 +4675,18 @@ ${runtimeSnapshotContext("user", plan.entries)}`;
4563
4675
  const committed = destinationFromCommittedMutation(result, memoryBodyId, entry.content);
4564
4676
  if (committed !== void 0) return committed;
4565
4677
  if (!mutationStates(result).includes("skipped")) throw new Error(`runtime archive write did not commit synchronously for Memory Space ${memoryBodyId}`);
4566
- const exact = (await service.read("search", {
4678
+ const named = skippedExactDestination(result, memoryBodyId, entry.content);
4679
+ if (named !== void 0) return named;
4680
+ const recalled = await service.read("search", {
4567
4681
  query: entry.content.slice(0, 500),
4568
4682
  limit: 20,
4569
4683
  memoryBodyIds: [memoryBodyId]
4570
- }, signal)).results.find((candidate) => candidate.memoryBodyId === memoryBodyId && candidate.content.trim() === entry.content);
4571
- if (exact === void 0) throw new Error(`runtime archive skipped an entry without exact durable recall evidence in Memory Space ${memoryBodyId}`);
4684
+ }, signal);
4685
+ const exact = recalled.results.find((candidate) => candidate.memoryBodyId === memoryBodyId && candidate.content.trim() === entry.content);
4686
+ if (exact === void 0) {
4687
+ const unavailable = recalled.sources?.find((source) => source.status === "unavailable");
4688
+ throw new Error(`runtime archive skipped an entry without exact durable recall evidence in Memory Space ${memoryBodyId}: ${unavailable === void 0 ? `its search returned ${recalled.results.length} results, none with that exact text` : `its search was unavailable (${safeFailureDetail(unavailable.hint ?? "no detail")})`}`);
4689
+ }
4572
4690
  return {
4573
4691
  layerId: "memory-spaces",
4574
4692
  reference: `memory-space:${encodeURIComponent(memoryBodyId)}/item:${encodeURIComponent(exact.id)}`,
@@ -4832,6 +4950,68 @@ This is the only completion channel for this run. Do not finish with a plain-tex
4832
4950
  await source.assertActionOffered(actionId, (offer) => authority.graph.config.writeEnabled && offer.authority === void 0);
4833
4951
  return source;
4834
4952
  }
4953
+ /**
4954
+ * The Memory Spaces Source a Document archive indexes into, or undefined when
4955
+ * that layer is off, not installed, takes no automatic writes, or has no space
4956
+ * anywhere that could take the index (#336). A space this View does not offer
4957
+ * keeps the refusal, as before.
4958
+ */
4959
+ async documentIndexSpaces(parent, generation, signal) {
4960
+ const graph = this.runtimeSource.forAgent(parent);
4961
+ if (!graph.config.writeEnabled || !this.runtimeSource.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only");
4962
+ if (!allowsParticipation(graph.config, "memory-spaces", "write", "automatic")) return void 0;
4963
+ const installed = generation.sourceInstances().filter((source) => source.sourceTypeId === "memory-spaces");
4964
+ if (installed.length === 0) return void 0;
4965
+ const authority = this.turnAuthority(parent, false);
4966
+ if (authority !== void 0 && !authority.context.view.actionOffers.some((offer) => offer.sourceActionId === "remember" && offer.authority === void 0 && installed.some((source) => source.sourceInstanceKey === offer.sourceInstanceKey)) && await this.noArchivableMemorySpace(graph, agentScope(parent, graph.config), generation, signal)) return void 0;
4967
+ return this.writableSourceFor(parent, "memory-spaces", "remember");
4968
+ }
4969
+ /** Whether a Memory Spaces Source other than the one in use lists a space that could take the archive. */
4970
+ async archivableElsewhere(graph, scope, generation, sourceInstanceKey, signal) {
4971
+ if (!generation.sourceInstances().some((source) => source.sourceTypeId === "memory-spaces" && source.sourceInstanceKey !== sourceInstanceKey)) return false;
4972
+ return !await this.noArchivableMemorySpace(graph, scope, generation, signal, sourceInstanceKey);
4973
+ }
4974
+ /**
4975
+ * True only when no installed Memory Spaces Source lists a space that could take
4976
+ * an archive (#336). A directory that cannot be read counts as one that could:
4977
+ * the caller then refuses as before, and nothing leaves working memory.
4978
+ */
4979
+ async noArchivableMemorySpace(graph, scope, generation, signal, except) {
4980
+ for (const instance of generation.sourceInstances().filter((source) => source.sourceTypeId === "memory-spaces" && source.sourceInstanceKey !== except)) try {
4981
+ const directory = await graph.source("memory-spaces", scope).forInstance(instance.sourceInstanceKey).forGeneration(generation).read("body-directory", null, signal);
4982
+ if (directory.items.some((body) => archivableSpace(directory, body))) return false;
4983
+ } catch {
4984
+ signal?.throwIfAborted();
4985
+ return false;
4986
+ }
4987
+ return true;
4988
+ }
4989
+ /** A Project Document archived without a Mnemon index keeps its original under .mnemon/documents/archived. */
4990
+ async archiveDocumentLocally(controller, document, reason, signal) {
4991
+ signal.throwIfAborted();
4992
+ const summary = `Archived locally without a Mnemon index, since ${reason}.`;
4993
+ const archived = await controller.mutate("archive", {
4994
+ id: document.id,
4995
+ documentRevision: document.revision,
4996
+ summary,
4997
+ memoryBodyIds: []
4998
+ }, signal);
4999
+ const runId = `host-${randomUUID()}`;
5000
+ this.counters.documentArchives += 1;
5001
+ this.counters.lastRunId = runId;
5002
+ this.counters.lastOperation = "document-archive";
5003
+ this.counters.lastAt = (/* @__PURE__ */ new Date()).toISOString();
5004
+ return {
5005
+ ...archived,
5006
+ maintenance: {
5007
+ runId,
5008
+ provider: "host",
5009
+ summary,
5010
+ memoryBodyIds: [],
5011
+ archivedDocumentIds: [document.id]
5012
+ }
5013
+ };
5014
+ }
4835
5015
  async assertAutomaticMemoryWrite(parent) {
4836
5016
  const graph = this.runtimeSource.forAgent(parent);
4837
5017
  if (!graph.config.writeEnabled || !this.runtimeSource.config.writeEnabled) throw new Error("dsh-mnemon is configured read-only");
@@ -5789,7 +5969,7 @@ var MnemonLifecycle = class {
5789
5969
  };
5790
5970
  /** Creation ids reserved before DSH publishes clean task-root Agents. */
5791
5971
  taskAgentIds = /* @__PURE__ */ new Set();
5792
- /** Bounded process-local replay fence for finalized-message write actions. */
5972
+ /** Bounded process-local replay fence for finalized-message write actions: the latest text per message. */
5793
5973
  supervisedWritebacks = /* @__PURE__ */ new Map();
5794
5974
  constructor(ctx, coordinator, config, runtimeSource) {
5795
5975
  this.ctx = ctx;
@@ -6055,15 +6235,13 @@ var MnemonLifecycle = class {
6055
6235
  if (normalizedKey === void 0 || normalizedKey === "") return execute();
6056
6236
  const replayKey = `${replayScope}\u0000${normalizedKey}`;
6057
6237
  const existing = this.supervisedWritebacks.get(replayKey);
6058
- if (existing !== void 0) {
6059
- if (existing.content !== normalizedContent) throw new Error("idempotency key was already used for different content");
6060
- return existing.result;
6061
- }
6062
- if (this.supervisedWritebacks.size >= 256) {
6238
+ if (existing?.content === normalizedContent) return existing.result;
6239
+ if (existing === void 0 && this.supervisedWritebacks.size >= 256) {
6063
6240
  const oldest = this.supervisedWritebacks.keys().next().value;
6064
6241
  if (oldest !== void 0) this.supervisedWritebacks.delete(oldest);
6065
6242
  }
6066
6243
  const result = execute();
6244
+ this.supervisedWritebacks.delete(replayKey);
6067
6245
  this.supervisedWritebacks.set(replayKey, {
6068
6246
  content: normalizedContent,
6069
6247
  result
@@ -7212,7 +7390,6 @@ async function assisted(runtime, lifecycle, typeId, operation, input, signal, ta
7212
7390
  if (typeId === "documents") {
7213
7391
  if (operation === "archive") {
7214
7392
  requireCapability(runtime, typeId, "archive");
7215
- requireCapability(runtime, "memory-spaces", "write");
7216
7393
  return lifecycle.archiveDocument(sessionId, String(input.id ?? ""), workspaceRoot, signal);
7217
7394
  }
7218
7395
  if (operation === "mutate") {
@@ -8147,7 +8324,6 @@ function registerTools(ctx, runtimeSource, coordinator) {
8147
8324
  const agent = requireAgent(exec);
8148
8325
  if (args.action === "archive") {
8149
8326
  requireSource(exec, "documents", "archive");
8150
- requireSource(exec, "memory-spaces", "write");
8151
8327
  if (isSubagent(agent)) throw new Error("idle document workers cannot cold-archive directly");
8152
8328
  if (args.id === void 0) throw new Error("document id is required for archive");
8153
8329
  return coordinator.archiveDocument(agent, args.id, exec.signal);
@@ -10,15 +10,15 @@ var dependencies = {
10
10
  "dsh-mnemon-provider-holographic": "0.5.6",
11
11
  "dsh-mnemon-provider-honcho": "0.5.5",
12
12
  "dsh-mnemon-provider-mem0": "0.5.5",
13
- "dsh-mnemon-provider-mnemon-native": "0.5.8",
13
+ "dsh-mnemon-provider-mnemon-native": "0.5.9",
14
14
  "dsh-mnemon-provider-openviking": "0.5.7",
15
15
  "dsh-mnemon-provider-retaindb": "0.5.5",
16
16
  "dsh-mnemon-provider-supermemory": "0.5.5",
17
- "dsh-mnemon-source-documents": "0.5.9",
18
- "dsh-mnemon-source-memory-spaces": "0.5.16",
19
- "dsh-mnemon-source-runtime": "0.5.12",
17
+ "dsh-mnemon-source-documents": "0.5.10",
18
+ "dsh-mnemon-source-memory-spaces": "0.5.17",
19
+ "dsh-mnemon-source-runtime": "0.5.13",
20
20
  "dsh-mnemon-strategy-auto-capture": "0.5.5",
21
- "dsh-mnemon-strategy-default-three-tier": "0.5.7",
21
+ "dsh-mnemon-strategy-default-three-tier": "0.5.8",
22
22
  "dsh-mnemon-strategy-general": "0.5.1",
23
23
  "dsh-mnemon-strategy-light-context": "0.5.5",
24
24
  "dsh-mnemon-strategy-scoped": "0.5.5",
package/lib/starter.js CHANGED
@@ -1,4 +1,4 @@
1
- import { t as prepareStarterResolution } from "./starter-resolution-DFLhgaoL.js";
1
+ import { t as prepareStarterResolution } from "./starter-resolution-UcTGyjgm.js";
2
2
  //#region src/starter.ts
3
3
  const name = "dsh-mnemon-starter";
4
4
  const provide = ["mnemonStarterReady"];
@@ -102,6 +102,7 @@ export declare const zh: {
102
102
  'receipt.unfinished': string;
103
103
  'receipt.failed': string;
104
104
  'receipt.view': string;
105
+ 'receipt.viewRuntime': string;
105
106
  'taskAgent.ready': string;
106
107
  'taskAgent.unavailable': string;
107
108
  'saveAction.button': string;
@@ -116,6 +117,31 @@ export declare const zh: {
116
117
  'saveAction.submitting': string;
117
118
  'saveAction.readOnly': string;
118
119
  'saveAction.close': string;
120
+ 'saveAction.hintDirect': string;
121
+ 'saveAction.to': string;
122
+ 'saveAction.to.agent': string;
123
+ 'saveAction.to.agentDetail': string;
124
+ 'saveAction.to.memory': string;
125
+ 'saveAction.to.user': string;
126
+ 'saveAction.to.space': string;
127
+ 'saveAction.save': string;
128
+ 'saveAction.saving': string;
129
+ 'saveAction.saved.runtime': string;
130
+ 'saveAction.saved.runtimeMaintained': string;
131
+ 'saveAction.saved.space': string;
132
+ 'saveAction.saved.spacePending': string;
133
+ 'saveAction.saved.spaceOther': string;
134
+ 'saveAction.saved.duplicate': string;
135
+ 'saveAction.unavailable': string;
136
+ 'saveAction.place.delimiter': string;
137
+ 'saveAction.place.tooLong': string;
138
+ 'saveAction.place.orAgent': string;
139
+ 'saveAction.place.full': string;
140
+ 'saveAction.place.fullAll': string;
141
+ 'saveAction.place.userCondense': string;
142
+ 'saveAction.place.userTooLong': string;
143
+ 'saveAction.place.userFull': string;
144
+ 'saveAction.noPlace': string;
119
145
  'readSources.all': string;
120
146
  'readSources.mode.search': string;
121
147
  'readSources.mode.graph': string;
@@ -980,6 +1006,7 @@ export declare const zh: {
980
1006
  "documents.updated": string;
981
1007
  "documents.updatedAfterArchive": string;
982
1008
  "documents.archived": string;
1009
+ "documents.archivedLocally": string;
983
1010
  "documents.list": string;
984
1011
  "documents.activeList": string;
985
1012
  "documents.archiveList": string;
@@ -999,6 +1026,7 @@ export declare const zh: {
999
1026
  "documents.hash": string;
1000
1027
  "documents.size": string;
1001
1028
  "documents.archiveReceipt": string;
1029
+ "documents.archiveNote": string;
1002
1030
  "documents.archiveTitle": string;
1003
1031
  "documents.archiveDescription": string;
1004
1032
  "documents.archive": string;
@@ -1048,6 +1076,7 @@ export declare const zh: {
1048
1076
  "runtime.result.remove": string;
1049
1077
  "runtime.result.maintenance": string;
1050
1078
  "runtime.result.localCompaction": string;
1079
+ "runtime.result.localArchive": string;
1051
1080
  "runtime.readOnly": string;
1052
1081
  "runtime.footnote": string;
1053
1082
  };
@@ -48,6 +48,7 @@ export declare function WriteReceipt(props: {
48
48
  error?: string | undefined;
49
49
  t?: MnemonTranslate | undefined;
50
50
  onView?: (() => void) | undefined;
51
+ viewLabel?: string | undefined;
51
52
  }): JSX.Element;
52
53
  /** Whether a task Agent can take a write now. */
53
54
  export declare function TaskAgentTag(props: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mnemon",
3
- "version": "0.5.24",
3
+ "version": "0.5.25",
4
4
  "description": "Composable, view-based memory for DeepSeek Harness. Pluggable sources and strategies, with layered memory out of the box.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -157,15 +157,15 @@
157
157
  "dsh-mnemon-provider-holographic": "0.5.6",
158
158
  "dsh-mnemon-provider-honcho": "0.5.5",
159
159
  "dsh-mnemon-provider-mem0": "0.5.5",
160
- "dsh-mnemon-provider-mnemon-native": "0.5.8",
160
+ "dsh-mnemon-provider-mnemon-native": "0.5.9",
161
161
  "dsh-mnemon-provider-openviking": "0.5.7",
162
162
  "dsh-mnemon-provider-retaindb": "0.5.5",
163
163
  "dsh-mnemon-provider-supermemory": "0.5.5",
164
- "dsh-mnemon-source-documents": "0.5.9",
165
- "dsh-mnemon-source-memory-spaces": "0.5.16",
166
- "dsh-mnemon-source-runtime": "0.5.12",
164
+ "dsh-mnemon-source-documents": "0.5.10",
165
+ "dsh-mnemon-source-memory-spaces": "0.5.17",
166
+ "dsh-mnemon-source-runtime": "0.5.13",
167
167
  "dsh-mnemon-strategy-auto-capture": "0.5.5",
168
- "dsh-mnemon-strategy-default-three-tier": "0.5.7",
168
+ "dsh-mnemon-strategy-default-three-tier": "0.5.8",
169
169
  "dsh-mnemon-strategy-general": "0.5.1",
170
170
  "dsh-mnemon-strategy-light-context": "0.5.5",
171
171
  "dsh-mnemon-strategy-scoped": "0.5.5",