dsh-memory-with-soul 0.0.0-stage → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 pzh-xsj
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,121 @@
1
- # Temporary Holding Version
1
+ # dsh-memory-with-soul
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 长期记忆库插件,给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(DSH)用。
4
+
5
+ 一句话:**让 AI 记住你**——会话开始时把记忆库注入上下文,读文件时记下它读过什么,回合结束时提醒它把结论写回文件。
6
+
7
+ - 纯 Markdown + git,不依赖任何数据库或云服务
8
+ - 记忆库放在 `~/.memory/`,可以单独 `git` 版本化、手动编辑、换机器带走
9
+ - 人格(角色卡)是一份 `SOUL.md`,写文件而不写配置
10
+ - 设置页挂在 DSH 侧边栏「记忆库」,七个页签
11
+
12
+ ## 装之前
13
+
14
+ | 依赖 | 版本 | 说明 |
15
+ | --- | --- | --- |
16
+ | DSH | `>=0.1.0-rc.1 <0.3.0` | 宿主,插件以 bundle 形式挂进 profile |
17
+ | Node.js | `>=22` | 适配器与内置设置页的路由 |
18
+ | **Python** | **`>=3.10`** | **硬依赖**:核心逻辑(`core/memory.py`)是 Python,不是可选项 |
19
+
20
+ Python 不在 `PATH` 里也能用:在 `cordis.patch.yml` 的 `python` 候选里写绝对路径(见下面的配置表)。
21
+
22
+ ## 安装
23
+
24
+ ```bash
25
+ # 1. 装插件(profile 名按你的实际写;默认叫 desktop)
26
+ dsh plugin --profile desktop add dsh-memory-with-soul
27
+
28
+ # 2. 第一次用:建记忆库(不建就只有空人格,插件本身照常工作)
29
+ python ~/.dsh/profiles/desktop/node_modules/dsh-memory-with-soul/core/memory.py init
30
+ ```
31
+
32
+ 装好、重启 DSH 后,侧边栏会多出「记忆库」入口;新会话开头会出现一段 `<memory-context>`(里面是记忆库内容)。
33
+
34
+ Windows 用户注意:上面第 2 步的路径用 `%USERPROFILE%\.dsh\profiles\desktop\node_modules\dsh-memory-with-soul\core\memory.py`,或直接用设置页里的路径提示。
35
+
36
+ ## 它做了什么
37
+
38
+ 插件挂在 DSH 的三个生命周期点上:
39
+
40
+ | 时机 | 动作 | 落到哪 |
41
+ | --- | --- | --- |
42
+ | 会话开始 | 读 `~/.memory/config.json` 的注入表,把记忆拼成 `<memory-context>` 注入 | `core/render.py` 输出 → `agent.inject()` |
43
+ | 读文件(`read` / `ops_read_files`) | 记下被读的记忆库文件 | `core/memory.py track` |
44
+ | 回合结束 | 判断这一回合有没有「该提炼但没写日志」 | `core/memory.py digest` |
45
+
46
+ 另外,插件加载时会把你装的 `skills/` 复制进 `~/.dsh/skills/`(复制,不是软链——版本升级后不会指向旧路径;内容变了下次加载自动刷新)。
47
+
48
+ ## 记忆库长什么样
49
+
50
+ ```
51
+ ~/.memory/
52
+ ├── config.json # 注入表:哪些文件、多大预算、什么顺序
53
+ ├── index.md # 总览:东西在哪
54
+ ├── README.md # 给 AI 看的协议(保存/提炼/遗忘的规矩)
55
+ ├── roles/<role>/SOUL.md # 角色卡:人格
56
+ ├── playbooks/ # 跨项目方法论
57
+ ├── topics/ # 单文件主题
58
+ ├── user/ # 用户档案与偏好
59
+ ├── agents/ # agent 注册表(每个 agent 一个目录)
60
+ ├── archive/ # 归档(设置页只读)
61
+ └── .state/ # 运行时状态(gitignore)
62
+ ```
63
+
64
+ 项目级记忆在**各工作区**的 `.memory/`(`log/<agent>/YYYY-MM-DD.md` 就是每日日志),插件按 cwd 向上找 `_meta.md` 定位。
65
+
66
+ ## 配置
67
+
68
+ 装完后在 profile 的 `cordis.patch.yml` 里覆盖 `memory-inject` 那一行的 `config`(整行覆盖,不深合并):
69
+
70
+ | 键 | 默认 | 作用 |
71
+ | --- | --- | --- |
72
+ | `enabled` | `true` | `false` 关掉插件 |
73
+ | `python` | `["python","python3","py"]` | 候选解释器,按顺序试;含路径分隔符的按绝对路径检查 |
74
+ | `renderer` | 空 | 渲染器路径,留空自动找 `core/render.py` |
75
+ | `harness` | `dsh` | 环境变量 `MEMORY_HARNESS` 的值,只影响日志与注入头 |
76
+ | `agentName` | 空 | 直接指定 agent id,留空按 `config.defaultAgent` |
77
+ | `timeoutMs` | `10000` | 渲染器超时(毫秒) |
78
+ | `injectSubagents` | `false` | 子 agent 也注入(每份记忆都是实付 token,默认省) |
79
+ | `trackAccess` | `true` | 记录读过哪些记忆文件 |
80
+ | `digest` | `true` | 回合结束判断要不要提醒提炼 |
81
+ | `installSkills` | `true` | 加载时把 `skills/` 复制进 `~/.dsh/skills/` |
82
+ | `webUi` | `true` | 挂内置设置页(`/api/memory/ui`) |
83
+ | `cwd` | 包根 | 渲染器工作目录,只影响相对路径解析 |
84
+
85
+ ## 自检
86
+
87
+ ```bash
88
+ node adapters/dsh/smoke.mjs # 清单与渲染器(19 项)
89
+ node adapters/dsh/test.mjs # 三条接线(11 项)
90
+ node adapters/dsh/web.test.mjs # 内置设置页路由(47 项)
91
+ python ui/serve.py --self-test # 独立设置页与核心逻辑(68 项)
92
+ python core/memory.py checkup # 真库体检
93
+ ```
94
+
95
+ ## 已知边界
96
+
97
+ - 设置页只读写**总册**;项目册有自己的页签,日志落在项目册里。
98
+ - `archive/` 只读:归档要回答「当时为什么这么定」,改了就查不回去了。
99
+ - 角色卡与 agent 是两件事:`config.roles` 决定「谁在跑」用哪张卡,`defaultRole` 只做兜底(兜底能读、不能写)。
100
+ - DSH 是唯一支持的宿主。旧版 WorkBuddy 适配器已在 0.6.5 删除。
101
+
102
+ ## English
103
+
104
+ A long-term memory plugin for DeepSeek Harness (DSH).
105
+
106
+ **What it does:** injects a Markdown memory library at session start, records which memory files the agent reads, and reminds it at turn end to write conclusions back. Plain Markdown + git — no database, no cloud.
107
+
108
+ **Requires:** DSH `>=0.1.0-rc.1 <0.3.0`, Node.js `>=22`, and **Python 3.10+** (the core is Python, not optional).
109
+
110
+ ```bash
111
+ dsh plugin --profile desktop add dsh-memory-with-soul
112
+ python ~/.dsh/profiles/desktop/node_modules/dsh-memory-with-soul/core/memory.py init
113
+ ```
114
+
115
+ Restart DSH afterwards: a "Memory" entry appears in the sidebar, and new sessions start with a `<memory-context>` block. The plugin also copies its skills into `~/.dsh/skills/` on load.
116
+
117
+ Configuration lives in the `memory-inject` row of your profile's `cordis.patch.yml`. See the Chinese section above for the full key table.
118
+
119
+ ## License
120
+
121
+ MIT
@@ -0,0 +1,106 @@
1
+ # adapters —— 一个中轴,每个 harness 一个薄壳
2
+
3
+ 记忆系统的**中轴**是 `core/render.py`(渲染器)+ `~/.memory/config.json`(注入表)。
4
+ 本目录只放**把中轴接到某个 harness 上**的薄壳,每个 100–200 行,不重复渲染逻辑。
5
+
6
+ ```
7
+ ~/.memory/config.json 注入表(priority / maxChars / tailLines)
8
+ │
9
+ core/render.py 渲染器:读 config → 拼 <memory-context> → stdout
10
+ │
11
+ │
12
+ adapters/dsh/ SessionStart hook 的 stdout 直接进上下文
13
+ ```
14
+
15
+ ## 契约:适配器只干四件事
16
+
17
+ | 动词 | 时机 | 干什么 |
18
+ | --- | --- | --- |
19
+ | **inject** | 会话开始 | 跑渲染器,把输出塞进模型能看见的第一处 |
20
+ | track | 读记忆库文件后 | 写访问戳记(`touch_access`),供 checkup 判「读过没」 |
21
+ | flush | 会话结束 | 落一条日志尾巴(`extraction_state` 判提炼) |
22
+ | probe | 随时 | 回答「我接上了吗、注入几字符、谁在读」 |
23
+
24
+ **第一版只实现 inject。** track / flush / probe 在 DSH 侧是 P1:
25
+ - track 需要 `tools/result` 监听 + 路径匹配
26
+ - flush 需要 `turn/end` 或会话结束事件
27
+ - probe 需要 `command.register`(做一个 `/memory-status`)
28
+
29
+ 写之前先看 `dsh 的 practices.md` 两条硬规矩:注入必须走 `agent.inject()`(写进 session log,可重建);
30
+ per-agent 的东西注册在 `agent/created` 拿到的 `agent.ctx` 上。
31
+
32
+ ## 各 harness 怎么接
33
+
34
+ | harness | 注入点 | 身份来源 |
35
+ | --- | --- | --- |
36
+ | dsh (DeepSeek Harness) | bundle 监听 `agent/created` → `agent.inject(createUserMessage)` | `MEMORY_AGENT` > `defaultAgent`(0.6.6 起 agent_id 不再拼 `-dsh`) |
37
+
38
+ **0.6.5 起只剩 DSH 一个宿主。** WorkBuddy 的钩子(`hooks/hooks.json`、`post-read.py`、`stop.py`)与它的两层清单已删——WB 从 10-27 起冻结,目标是卸掉,不再维护第二个适配器。注入表仍然只有 `config.json` 一处定义,接第三个 harness 只需写 `adapters/<name>/`。
39
+
40
+ ## adapters/dsh
41
+
42
+ ```
43
+ adapters/dsh/
44
+ index.js apply(ctx, config):agent/created → 渲染 → inject
45
+ cordis.patch.yml - insert: [{ id: memory-inject, name: 'dsh-memory-with-soul' }]
46
+ locale/{zh,en}.json meta.title / meta.description
47
+ icon.svg 952 B
48
+ smoke.mjs 19 项自检,不需要装进 DSH 就能跑
49
+ ```
50
+
51
+ ### 可调项(写在用户的 `cordis.patch.yml` 里,同 id 整行覆盖)
52
+
53
+ | 键 | 默认 | 说明 |
54
+ | --- | --- | --- |
55
+ | `enabled` | `true` | 关掉注入,不动其他 |
56
+ | `python` | `['python','python3','py']` | 按顺序试;含路径分隔符的当绝对路径 |
57
+ | `renderer` | 空 | 留空自动找 `core/render.py`;`sourceRoot` 只是兜底 |
58
+ | `sourceRoot` | 空 | 插件被复制进 node_modules(而非软链)时用它兜底找渲染器 |
59
+ | `harness` | `dsh` | 只进日志与注入头(0.6.6 起不再拼进 agent id) |
60
+ | `agentName` | 空 | 直接指定 agent id,留空则用 `config.defaultAgent` |
61
+ | `timeoutMs` | `10000` | 超时先 `terminate()` 再强杀 |
62
+ | `injectSubagents` | `false` | 子 agent 不该吃掉一份上下文(靠 `session.header.origin === 'subagent'` 判,**不能用 parentSession,fork 也带**) |
63
+ | `cwd` | 插件根 | 只影响相对路径解析 |
64
+
65
+ ### 装
66
+
67
+ 包根是一整个 npm 包,装它就等于装 Python 侧 + 设置页 + skills:
68
+
69
+ ```bash
70
+ dsh plugin --profile desktop add dsh-memory-with-soul
71
+ ```
72
+
73
+ 开发时也可以让 profile 直接指向本仓库(`"dsh-memory-with-soul": "link:E:/path/to/memory-plugin"`,
74
+ 见根目录 `README.md`)。装完重启一次 DSH 才会挂上。
75
+
76
+ ### 验
77
+
78
+ ```bash
79
+ cd <插件根>/adapters/dsh # 包根是 <插件根>,adapters/dsh 只是它的子目录
80
+ node smoke.mjs # 19/19 通过 = 清单、模块、渲染器、路由、左侧栏五关都过
81
+ ```
82
+
83
+ 真装进 DSH 之后再看两处:
84
+
85
+ 1. `~/.memory/.state/session-start-<agent>.json` 里 `harness: dsh`、`agent` 与你设的 id 一致
86
+ 2. 新会话里**没有**重复的手抄人格块——注入接管以后,`AGENTS.md` 只该留「人格摘要 + 路由 + 故障提示」,
87
+ 否则同一个人格吃两遍 token
88
+
89
+ ### 故障特征
90
+
91
+ | 现象 | 去哪看 |
92
+ | --- | --- |
93
+ | 新会话没有记忆块 | 日志里找 `记忆注入` 的 warn:渲染器缺失 / 找不到 python / 非零退出 / 空输出 |
94
+ | 有块但 agent 名不对 | `MEMORY_AGENT` / `agentName` 与实际不符 |
95
+ | 会话建不起来 | 监听器抛了异常——`agent/created` 是 serial 派发,**抛错会回滚整个 agent 创建** |
96
+ | 块重复两遍 | 旧的手抄 `AGENTS.md` 没收缩(Step 0b) |
97
+
98
+ ## 加一个新 harness
99
+
100
+ 1. 在 `adapters/<name>/` 建壳,只调用 `core/render.py`,**不复制渲染逻辑**;
101
+ 2. 身份:先设 `<NAME>_HARNESS` 等价物(只影响日志与注入头;agent id 由 `config.defaultAgent` 决定);
102
+ 3. 注入点必须是「模型能看见、且能重建」的位置——抄进 system prompt 不算;
103
+ 4. 写一个不需要装进该 harness 的 smoke,覆盖「有项目册 / 无项目册 / 路径不存在」三种 cwd;
104
+ 5. 在 README 的「各 harness 怎么接」表里加一行。
105
+
106
+ _(wb 那份已随 0.6.5 删除;要接新 harness 见上一节五步。)_
@@ -0,0 +1,80 @@
1
+ # dsh-memory-with-soul · DSH 适配层
2
+
3
+ DSH 侧的记忆系统适配层。三件事:
4
+
5
+ 1. **注入**——会话创建时把 `~/.memory` 渲染出的人格、偏好、项目册入口和最近日志,经 `agent.inject()` 放进新会话;
6
+ 2. **戳记**——会话里 `read` / `ops_read_files` 读到的记忆库文件,把访问时间记到今天(体检据此判断哪些文件没人读);
7
+ 3. **提炼提醒**——回合结束时问一次 core,命中就往会话里塞一句提醒,同一段时间内不重复提醒。
8
+
9
+ **完整契约、可调项、故障排查在上一级:** [../README.md](../README.md)
10
+
11
+ ## 装
12
+
13
+ 它不是一个独立的包——整份仓库就是一个 npm 包,装顶层即可:
14
+
15
+ ```bash
16
+ dsh plugin --profile desktop add dsh-memory-with-soul
17
+ ```
18
+
19
+ 开发时也可以链接本仓库(`"dsh-memory-with-soul": "link:<插件根>"`,见根目录 `README.md`)。
20
+ 装完重启 DSH,侧边栏才会出现「记忆库」。
21
+
22
+ ## 验
23
+
24
+ ```bash
25
+ node smoke.mjs # 19/19:清单、模块加载、v4 来源准入、渲染器四关、路由表、左侧栏、包名一致性
26
+ node test.mjs # 11/11:用假宿主验三条接线(注入 / track / digest)+ skills 自动装
27
+ node web.test.mjs # 47/47:内置设置页的十一条路由(入口层 + 真起 python 打每条路由)
28
+ ```
29
+
30
+ `test.mjs` 不启动 DSH,也不需要 python 真的跑完——`ctx.subprocess` 被换成记录器,只断言
31
+ `argv`、`env`、`cwd` 对不对。改了 `index.js` 的事件处理,先跑它。
32
+
33
+ 装好后开一个新会话,注入块的首行应为 `# 长期记忆(agent: <你的 agent id>,harness: dsh,库:…)`,
34
+ 并在会话里读一个记忆库文件后,`~/.memory/.state/access-<agent>.json` 的当天计数会增加。
35
+
36
+ ## 结构
37
+
38
+ | 文件 | 作用 |
39
+ | --- | --- |
40
+ | `index.js` | 三条接线:`agent/created` → 渲染器 → `agent.inject()`;`session/event` 的 `tool/call` → `core/memory.py track`;`turn/end` → `core/memory.py digest` |
41
+ | `cordis.patch.yml` | 挂载行 `- insert: [{ id: memory-inject, … }]`,可调项都写在它的注释里 |
42
+ | `web.js` | 把 `ui/settings.html` 和十一条 `/api/memory/*` 路由挂进宿主端口,页面不用另起服务 |
43
+ | `client.js` | 浏览器半边:`main` 面板 + 左侧栏 `sidebar.panellist` 行 + 设置分区,内容都是 iframe 装 `/api/memory/ui` |
44
+ | `locale/{zh,en}.json` | 界面上的插件标题与描述 |
45
+ | `icon.svg` | 插件图标(952 B) |
46
+ | `smoke.mjs` | 不装进 DSH 也能跑的自检(清单 + 渲染器 + 路由表 + 左侧栏声明) |
47
+ | `test.mjs` | 假宿主下的接线测试(事件 → 子进程参数) |
48
+ | `web.test.mjs` | 设置页回归测试(入口层假 ctx + 接线层真起 `python ui/api.py`) |
49
+
50
+ ## 设置页
51
+
52
+ 三处入口,同一份 `ui/settings.html`:主界面面板(`main`)、左侧栏行、设置分区。面板里带 `?embed=1`,
53
+ 页面据此加 `body.embed`——只收起品牌和标题(外壳已经有了),**不收起「连接本地目录…」**。
54
+ 这条 CSS 曾经是 `display:none`,结果嵌进宿主以后连接入口整个消失,用户只能猜。
55
+
56
+ 写入有两条通道,目录授权降级成兜底:
57
+
58
+ | 通道 | 端点 | 用途 |
59
+ | --- | --- | --- |
60
+ | 服务端 | `POST /api/persona`(只认 `SOUL.md`;0.6.3 起按角色写 `roles/<role>/SOUL.md`,agent 没配角色 → 404) | 人格页整份回写(`name` / `identity` + 分节正文) |
61
+ | 服务端 | `POST /api/file`(只认 `.md`,不收 `.state` / `.git`) | 记忆页自由文本编辑 |
62
+ | 浏览器目录授权 | File System Access API | 没连服务端时的兜底 |
63
+
64
+ 两条服务端通道都带 mtime 冲突检测,写前备份到 `.state/ui-backups/`。
65
+ 人格页按 `## ` 分节编辑:`SOUL.md` 是 原则 / 沟通(含 `### 提问`、`### 语气`)/ 禁区 / 记忆,
66
+ `IDENTITY.md` 的 `## 称呼` 单拎成一张卡。节名是页面分卡片的依据,改名前先看 `parseSoul()`。
67
+
68
+ ## 子 agent
69
+
70
+ 注入对子 agent 也生效:`render()` 发现 `session.header.origin` 是 `subagent` / `subagent-deep` 时给渲染器加 `--subagent`,渲染器据此查 `config.roles["subagent"]`(没配就告警并沿用 `defaultRole`);日志与戳记仍记在父 agent 名下。`injectSubagents` 默认 `false`——想给子 agent 注人格要显式打开。
71
+
72
+ ## 身份
73
+
74
+ `agent` id 不在插件里写死:插件只设环境变量 `MEMORY_HARNESS`(默认 `dsh`),core 取
75
+ `config.defaultAgent`(0.6.6 起不再拼 `-harness`)。要换 id,在 `cordis.patch.yml` 里设 `agentName`。
76
+
77
+ ## 依赖
78
+
79
+ Python 3.10+。插件启动时按 `python` → `python3` → `py` 顺序探测(也可在 `cordis.patch.yml` 的
80
+ `python` 里写绝对路径);全找不到时只警告一次,本会话不注入,其余功能不受影响。
@@ -0,0 +1,113 @@
1
+ /**
2
+ * 记忆库 —— DSH 主界面左侧栏一级页面(浏览器半边)。
3
+ *
4
+ * 三处注册合成一个入口:
5
+ * 1. `main` 面板(key `memory`):内容区的整页;
6
+ * 2. `sidebar.panellist` 行(id `memory`):左侧栏那一行,点它宿主自己
7
+ * 调 `layout.selectPanel('memory')`——id 与面板 key 相同才会对上;
8
+ * 3. `settings.section`:设置里也留一份,习惯从设置点的人不至于找不到。
9
+ *
10
+ * 内容仍是 iframe 装 `/api/memory/ui`:那一页 1400 行、判定全在
11
+ * `core/memory.py`,用 React 重写就是第二个真相源——上一版设置页正是
12
+ * 这么烂掉的。iframe 只负责显示,逻辑一行都不搬。
13
+ *
14
+ * 加载约定(宿主 `@deepseek-ai/dsh-client-modules` 定的):
15
+ * - 入口走 package.json 的 `exports['./client']`;
16
+ * - 文件必须是 `window.__ModuleLoader__.load({ id, factory })` 形态;
17
+ * - factory 的 `require` 只能拿宿主共享模块表(react / react-dom /
18
+ * @deepseek-ai/dsh-client-ui-primitives),拿不到 node 内置模块;
19
+ * - 导出 `apply(ctx)` 与 `inject`(服务名数组),按 Cordis 生命周期挂载。
20
+ */
21
+
22
+ window.__ModuleLoader__.load({
23
+ // 这个 id 必须等于 package.json 的 name:宿主按包名找客户端模块,
24
+ // 名字对不上就是「entry did not activate / import failed」。
25
+ id: 'dsh-memory-with-soul',
26
+ factory: (require) => {
27
+ const { createElement: h } = require('react')
28
+
29
+ /** 面板 key 与侧栏行 id 必须一致,宿主靠它把两半接起来。 */
30
+ const PANEL_ID = 'memory'
31
+ /** 设置分区的 id,与面板 id 不冲突(宿主按 slot 分开记)。 */
32
+ const SECTION_ID = 'memory'
33
+
34
+ const LABEL = () => '记忆库'
35
+ /** 页面地址:相对路径在 `dsh-app://app/` 下解析,天然同源、带登录态。 */
36
+ const PAGE_URL = '/api/memory/ui?embed=1'
37
+
38
+ const FRAME_STYLE = {
39
+ display: 'block',
40
+ width: '100%',
41
+ height: '100%',
42
+ border: '0',
43
+ background: '#12141a',
44
+ colorScheme: 'dark',
45
+ }
46
+
47
+ /** 铺满宿主给的内容区;外壳自己带圆角与边距,这里不再叠一层。 */
48
+ function Frame({ radius = '0' }) {
49
+ return h(
50
+ 'div',
51
+ { style: { width: '100%', height: '100%', minHeight: '360px', overflow: 'hidden', borderRadius: radius } },
52
+ h('iframe', { title: '记忆库', src: PAGE_URL, style: FRAME_STYLE }),
53
+ )
54
+ }
55
+
56
+ function MainPanel() {
57
+ return h(Frame, { radius: '0' })
58
+ }
59
+
60
+ function SettingsSection() {
61
+ return h(
62
+ 'div',
63
+ { style: { width: '100%', height: 'calc(100vh - 200px)', minHeight: '420px' } },
64
+ h('iframe', { title: '记忆库', src: PAGE_URL, style: FRAME_STYLE }),
65
+ )
66
+ }
67
+
68
+ /** 侧栏图标:16px 线稿,跟着宿主的 `owner.size` 走。 */
69
+ function SidebarIcon(owner = {}) {
70
+ const size = owner.size ?? 16
71
+ return h(
72
+ 'svg',
73
+ {
74
+ width: size,
75
+ height: size,
76
+ viewBox: '0 0 16 16',
77
+ fill: 'none',
78
+ stroke: 'currentColor',
79
+ strokeWidth: 1.4,
80
+ strokeLinecap: 'round',
81
+ strokeLinejoin: 'round',
82
+ 'aria-hidden': 'true',
83
+ },
84
+ h('path', { d: 'M2.5 3.2h5.2a1 1 0 0 1 1 1v8.6H3.5a1 1 0 0 1-1-1V3.2Z' }),
85
+ h('path', { d: 'M8.7 4.2h3.8a1 1 0 0 1 1 1v7.6H9.7' }),
86
+ h('path', { d: 'M2.5 11.8h5.2' }),
87
+ h('path', { d: 'M10.4 6.6h1.6M10.4 8.8h1.6' }),
88
+ )
89
+ }
90
+
91
+ /** `slots` 是宿主 slots 服务;不声明会在 ctx.slots 处抛错。 */
92
+ const inject = ['slots']
93
+
94
+ function apply(ctx) {
95
+ // 三个 slot 都要等外壳把 declaration 登记到 ledger 之后才注册。
96
+ // 侧栏行与 main 面板共用 id,宿主据此把点击接到面板上。
97
+ const seat = (name, options, component) => {
98
+ if (typeof ctx.slots.inject === 'function') {
99
+ ctx.slots.inject(name, () => ctx.slots.register(options, component))
100
+ return
101
+ }
102
+ // 老宿主没有 inject:直接注册,失败由宿主自己报。
103
+ ctx.slots.register(options, component)
104
+ }
105
+
106
+ seat('main', { name: 'main', key: PANEL_ID }, MainPanel)
107
+ seat('sidebar.panellist', { name: 'sidebar.panellist', id: PANEL_ID, order: 120, label: LABEL }, SidebarIcon)
108
+ seat('settings.section', { name: 'settings.section', id: SECTION_ID, order: 120, label: LABEL }, SettingsSection)
109
+ }
110
+
111
+ return { name: 'memory', inject, apply }
112
+ },
113
+ })
@@ -0,0 +1,19 @@
1
+ # dsh-memory-with-soul 的 bundle patch:装到 profile 后由 Loader 合并这一行挂载插件。
2
+ # 行说明符必须等于 package.json 的 name(插件清单按最近 manifest 的名字匹配)。
3
+ #
4
+ # 可调项(用户的 cordis.patch.yml 里同 id 的行会整体覆盖 config,不是深合并):
5
+ # enabled false 关掉注入
6
+ # python 候选列表,按顺序试;含路径分隔符的当绝对路径
7
+ # renderer 渲染器脚本路径;留空 = 自动找 core/render.py
8
+ # sourceRoot 额外的源码目录兜底(留空即可;包根不对时才有用)
9
+ # harness 环境变量 MEMORY_HARNESS 的值,只影响日志与注入头(0.6.6 起不进 agent id)
10
+ # agentName 直接指定 agent id,留空则用 config.defaultAgent
11
+ # timeoutMs 渲染器超时(毫秒),超时先 terminate 再强杀
12
+ # injectSubagents true 时连子 agent 也注入(默认 false,省 token)
13
+ # installSkills true 时插件加载后自动把 skills/ 复制进 ~/.dsh/skills/(默认 true)
14
+ # cwd 渲染器的工作目录,只影响相对路径解析
15
+ - insert:
16
+ - id: memory-inject
17
+ name: 'dsh-memory-with-soul'
18
+ config:
19
+ harness: dsh
@@ -0,0 +1,23 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="Memory Inject">
2
+ <defs>
3
+ <linearGradient id="cover" x1="0" y1="0" x2="0" y2="1">
4
+ <stop offset="0" stop-color="#3b4a6b"/>
5
+ <stop offset="1" stop-color="#1f2739"/>
6
+ </linearGradient>
7
+ </defs>
8
+ <!-- book cover -->
9
+ <rect x="10" y="8" width="44" height="48" rx="6" fill="url(#cover)"/>
10
+ <!-- spine -->
11
+ <rect x="10" y="8" width="7" height="48" rx="3" fill="#8fa4cc" opacity="0.55"/>
12
+ <!-- text lines on the page -->
13
+ <g fill="#e8eefb" opacity="0.9">
14
+ <rect x="23" y="19" width="24" height="3.5" rx="1.75"/>
15
+ <rect x="23" y="27" width="19" height="3.5" rx="1.75"/>
16
+ <rect x="23" y="35" width="22" height="3.5" rx="1.75"/>
17
+ </g>
18
+ <!-- downward arrow: injected into the session -->
19
+ <g fill="#5ad1a8">
20
+ <rect x="30.5" y="42" width="3" height="9" rx="1.5"/>
21
+ <path d="M32 58 L25.5 49.5 H38.5 Z"/>
22
+ </g>
23
+ </svg>