dsh-engramory 0.2.5 → 0.2.7
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 +21 -21
- package/README.md +154 -134
- package/README.zh-CN.md +132 -118
- package/cordis.patch.yml +16 -16
- package/index.js +655 -454
- package/package.json +64 -57
package/README.zh-CN.md
CHANGED
|
@@ -1,118 +1,132 @@
|
|
|
1
|
-
[English](README.md) | **简体中文**
|
|
2
|
-
|
|
3
|
-
# dsh-engramory
|
|
4
|
-
|
|
5
|
-
[](https://unstone.github.io/dsh-xray/registry.html#tinqiao-oss__engramory)
|
|
6
|
-
|
|
7
|
-
<sub>`C2` 是 `manifest.bundle.patch` 给每个可挂载 dsh 插件定的档位(被扫描生态里 74.6% 都在这一档);它是这张卡片上**唯一**的标记 —— 没有 `exec`、没有 `eval`、没有安装脚本、没有外连域名、不读环境变量。</sub>
|
|
8
|
-
|
|
9
|
-
给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 用的**策展式、文件式**长期记忆 ——
|
|
10
|
-
纯 markdown 笔记,随便什么工具都能打开;一个记忆库被你用的所有宿主共用;索引上限是一次
|
|
11
|
-
**真正的拒绝**,而不是一句请求。(记忆库若位于某个项目的 git 仓库内,必须 git-ignore ——
|
|
12
|
-
记忆里带着本机独有的细节,见 SKILL.md §1。给记忆库单独开一个*私有仓*则完全可以。)
|
|
13
|
-
|
|
14
|
-
隶属于 [Engramory](https://github.com/tinqiao-oss/engramory)。
|
|
15
|
-
|
|
16
|
-
## 已经有 `AGENTS.md` 常驻块了,为什么还要插件
|
|
17
|
-
|
|
18
|
-
那个常驻块确实承载了纪律,单论召回也够用了。但有两件事它做不到:
|
|
19
|
-
|
|
20
|
-
**上限从此是确定性的。** Engramory 把索引控制在 200 行 / 25 KB 以内,是因为索引每次会话
|
|
21
|
-
都要加载,超出的部分会**悄无声息地不再被召回**。在多数宿主上,这个限制只是"规则 + 一个
|
|
22
|
-
要靠 agent 自己记得去跑的校验器"。而 dsh 暴露了 `ctx.tools.guard()` —— 一个同步的、
|
|
23
|
-
**单调**的否决:守卫一旦返回理由,后面任何监听器都无法把它翻回放行。所以在这里,上限是被
|
|
24
|
-
**强制执行**的,不是被请求的。请用 **0.2.
|
|
25
|
-
没打通,而且它的 `inject` 声明是这版 Cordis 不接受的写法 —— 装得上,但**永远激活不了**
|
|
26
|
-
(issue #8)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
|
64
|
-
|
|
65
|
-
| `
|
|
66
|
-
| `
|
|
67
|
-
| `
|
|
68
|
-
| `
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
|
75
|
-
|
|
76
|
-
| `write
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
| `
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1
|
+
[English](README.md) | **简体中文**
|
|
2
|
+
|
|
3
|
+
# dsh-engramory
|
|
4
|
+
|
|
5
|
+
[](https://unstone.github.io/dsh-xray/registry.html#tinqiao-oss__engramory)
|
|
6
|
+
|
|
7
|
+
<sub>`C2` 是 `manifest.bundle.patch` 给每个可挂载 dsh 插件定的档位(被扫描生态里 74.6% 都在这一档);它是这张卡片上**唯一**的标记 —— 没有 `exec`、没有 `eval`、没有安装脚本、没有外连域名、不读环境变量。</sub>
|
|
8
|
+
|
|
9
|
+
给 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 用的**策展式、文件式**长期记忆 ——
|
|
10
|
+
纯 markdown 笔记,随便什么工具都能打开;一个记忆库被你用的所有宿主共用;索引上限是一次
|
|
11
|
+
**真正的拒绝**,而不是一句请求。(记忆库若位于某个项目的 git 仓库内,必须 git-ignore ——
|
|
12
|
+
记忆里带着本机独有的细节,见 SKILL.md §1。给记忆库单独开一个*私有仓*则完全可以。)
|
|
13
|
+
|
|
14
|
+
隶属于 [Engramory](https://github.com/tinqiao-oss/engramory)。
|
|
15
|
+
|
|
16
|
+
## 已经有 `AGENTS.md` 常驻块了,为什么还要插件
|
|
17
|
+
|
|
18
|
+
那个常驻块确实承载了纪律,单论召回也够用了。但有两件事它做不到:
|
|
19
|
+
|
|
20
|
+
**上限从此是确定性的。** Engramory 把索引控制在 200 行 / 25 KB 以内,是因为索引每次会话
|
|
21
|
+
都要加载,超出的部分会**悄无声息地不再被召回**。在多数宿主上,这个限制只是"规则 + 一个
|
|
22
|
+
要靠 agent 自己记得去跑的校验器"。而 dsh 暴露了 `ctx.tools.guard()` —— 一个同步的、
|
|
23
|
+
**单调**的否决:守卫一旦返回理由,后面任何监听器都无法把它翻回放行。所以在这里,上限是被
|
|
24
|
+
**强制执行**的,不是被请求的。请用 **0.2.7 或更高版本**:0.2.0 发布时上游的 profile 安装还
|
|
25
|
+
没打通,而且它的 `inject` 声明是这版 Cordis 不接受的写法 —— 装得上,但**永远激活不了**
|
|
26
|
+
(issue #8);0.2.5 及以前,在 CRLF 换行的索引上、或在 dsh 0.2 上 cd 之后用相对路径,edit
|
|
27
|
+
能绕过上限;0.2.6 及以前,换了名字的 symlink 也能绕过、用索引名字的 FIFO 能把宿主卡死
|
|
28
|
+
(#20、#21;见下文「已知限制」)。在 Claude Code 之外,dsh 是第一个真正把这层垫片写出来的宿主(另有几个宿主
|
|
29
|
+
同样暴露了等价的写前接缝,见 PORTING.md,但还没有垫片实现)。
|
|
30
|
+
|
|
31
|
+
**协议随插件一起到达。** skill 是在运行时通过 dsh 的 skill 注册表注册的 —— 走一个响应式的
|
|
32
|
+
`ctx.inject(['skills'], …)` 子上下文,所以没有注册表的 profile 照样拿到上限,而注册表晚一步
|
|
33
|
+
挂载的 profile 也照样拿得到 skill。这意味着它**不依赖**把文件放进 dsh 扫描的那五个 skill 根
|
|
34
|
+
目录之一 —— 那条路很容易在细微处放错,而一旦放错就是**静默失败**。
|
|
35
|
+
|
|
36
|
+
## 安装
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
dsh plugin --profile <名字> add dsh-engramory
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
装到这里就完了。这个包自带 `dsh.bundle` manifest 指向它自己的 `cordis.patch.yml`,所以那一行
|
|
43
|
+
会被自动插进 profile 的插件树里 —— 不需要手改配置。要改默认值,请在你自己 profile 的 patch
|
|
44
|
+
层里**按 id 修改那一行已存在的配置**,**不要**再写一个 `- insert:`(insert 永远是追加,你会得到
|
|
45
|
+
两行 engramory,而原来那行的上限依旧在生效):
|
|
46
|
+
|
|
47
|
+
```yaml
|
|
48
|
+
- id: engramory
|
|
49
|
+
config:
|
|
50
|
+
indexName: MEMORY.md
|
|
51
|
+
maxLines: 200
|
|
52
|
+
maxBytes: 25600
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
一次 patch 会**整体替换**目标行的 `config`,所以你想保留的键要逐个列全。
|
|
56
|
+
|
|
57
|
+
记忆库本身和常驻块,请用 Engramory 仓库里的安装器
|
|
58
|
+
(`python tools/engramory_init.py dsh --install-skill`)。本插件负责强制上限、提供协议;
|
|
59
|
+
**它不创建记忆库**。
|
|
60
|
+
|
|
61
|
+
## 配置
|
|
62
|
+
|
|
63
|
+
| 字段 | 默认值 | 含义 |
|
|
64
|
+
|---|---|---|
|
|
65
|
+
| `indexName` | `MEMORY.md` | 被当作记忆索引的文件名(不区分大小写;`file_path` 与 `str_replace_editor` 的 `path` 都会检查)—— 看写入时用的文件名,或写入实际落到的那个文件的文件名,所以换了名字的 symlink 也会被拦。空值或非字符串会回退到默认值。 |
|
|
66
|
+
| `maxLines` | `200` | 硬性行数上限。非正数或非有限值回退到默认值。 |
|
|
67
|
+
| `maxBytes` | `25600` | 硬性 UTF-8 字节上限(25 KB)。 |
|
|
68
|
+
| `indexPath` | 未设置 | 要守护的**那一个**索引的绝对路径。不设它时,守卫只按文件名匹配,于是**别的项目里一个无关的 `MEMORY.md` 也会被拦**;设了它就只有这个确切的文件会被守。比较走的是身份而非拼写 —— 看写入实际落到的那个文件,不管它叫什么名字(symlink 与 `..` 都会解析;仅在 Windows 上折叠大小写),而且即使文件尚不存在,它的第一次写入照样受守护。 |
|
|
69
|
+
| `registerSkill` | `true` | 设为 `false` 则保留上限、但跳过运行时 skill。 |
|
|
70
|
+
| `skill` | 内置 | 用你自己的 markdown 替换 skill 正文。 |
|
|
71
|
+
|
|
72
|
+
## 守卫到底做了什么
|
|
73
|
+
|
|
74
|
+
| 调用 | 判决 |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `write`(或 `str_replace_editor` 的 `create`)且结果在上限内 | 放行 |
|
|
77
|
+
| `write`/`create` 会让索引**增长**并越过上限 | **拒绝**,并报出具体数字和该压缩什么 |
|
|
78
|
+
| `write`/`create` 让一个已超限的索引**缩小或持平** | 放行 —— 增量压缩(210 → 205 → 198)必须走得通 |
|
|
79
|
+
| 带 `old_str`/`new_str` 的 edit:结果可模拟 | 按**结果**判定,增长/缩小规则与整文件写入相同 —— 先算大小再构建,结果长到没法构建的 `replace_all` 直接按大小拒绝 |
|
|
80
|
+
| 无法模拟的部分写入(如 `insert`)落在已超限的索引上 | **拒绝**,并告诉 agent:缩小的整文件写入是放行的 |
|
|
81
|
+
| 无法模拟的部分写入落在健康的索引上 | 放行 |
|
|
82
|
+
| `read`、`view`,以及任何未知工具 | 放行 —— 哪怕已超限,召回也绝不能被挡住 |
|
|
83
|
+
| 对任何其他文件的写入 | 放行 —— 不关守卫的事 |
|
|
84
|
+
| 整文件写入时当前索引缺失/读不到 | 视为**空**(与 Python 版守卫一致)—— 上限内的写入放行,而首次就超限的写入被拒 |
|
|
85
|
+
| 部分编辑时索引缺失/读不到 | 放行 —— 守卫绝不能因为一个自己读不了的路径而挡住工作 |
|
|
86
|
+
| 用索引名字的 FIFO、设备或目录 | 一律不读,按读不到处理(在那里读会把宿主卡死或撑爆内存,而宿主在主线程上跑守卫) |
|
|
87
|
+
| 超过读取上限的索引(1 MiB,或 `maxBytes` 的 4 倍,最多 16 MiB) | 不整份读入:整文件写入按文件系统报告的大小比较字节(缩小一律放行;行数未知,这时不卡行数),edit 无法测量,按读不到的索引处理,放行 |
|
|
88
|
+
| 畸形的执行(没有 arguments、路径/内容不是字符串) | 放行 —— 它压根不像一次写入 |
|
|
89
|
+
|
|
90
|
+
拒绝规则与 `hooks/engramory_index_guard.py` **完全一致**:只拒绝那种结果既超过上限、
|
|
91
|
+
又比当前文件更大的写入。行数统计忽略结尾换行,所以一个正好卡在上限的索引仍然可写。
|
|
92
|
+
|
|
93
|
+
## 已知限制
|
|
94
|
+
|
|
95
|
+
- **从上限之下一跃越过上限的、无法模拟的部分写入,抓不住。** `insert` 这类调用并不携带
|
|
96
|
+
写入后的文本。这种越界会由下一次整文件写入、或 `engramory_check.py` 抓到。能保证的是:
|
|
97
|
+
一个**已经超限**的索引不会被继续撑大,而缩小的写入永远放行。
|
|
98
|
+
- **工具名单遵循 dsh 文档化的 tool-fs 契约**(`write`/`edit` 用 `file_path`,
|
|
99
|
+
`str_replace_editor` 用 `path`),并且刻意保守:未知工具一律放行。
|
|
100
|
+
- **0.2.0 从未激活成功;0.2.1 才是第一个真正跑得起来的版本。** 在 rc.6 预览版那个 bug
|
|
101
|
+
堵住上游所有第三方 profile 安装期间,插件能有的覆盖只有 mock —— 而 0.2.0 的 mock 恰好
|
|
102
|
+
掩盖了两个激活期 bug:一个是旧版 Cordis 的 `{ required, optional }` inject 写法(被解读成
|
|
103
|
+
在等待名字就叫 `required`/`optional` 的服务,于是永远 pending),另一个是裸读未声明的
|
|
104
|
+
`ctx.skills`(在 Cordis 的反射式上下文下会直接抛错)。安装一旦成为可能,**首个真实安装
|
|
105
|
+
当场把两个都暴露了**(issue #8)。0.2.1 修掉了它们,并在一个真跑着的 dsh 0.1.0-rc.7 web
|
|
106
|
+
profile 上端到端验证过(0.2.0 逐字节复现启动失败;0.2.1 能启动并提供服务),也对着 dsh
|
|
107
|
+
内置的 `@deepseek-ai/cordis` 解析器验过(注册表存在、不存在、以及晚挂载三种情况下的激活)。
|
|
108
|
+
测试用的 mock 现在会照着反射式上下文的访问规则来,守卫的判决表则由 `node --test`
|
|
109
|
+
持续覆盖(47 个用例,在 Engramory 的 CI 里跑)。
|
|
110
|
+
- **相对路径按 dsh 实际写入的位置来量。** dsh 0.2 给每个会话一个自己的工作目录(由它的
|
|
111
|
+
`working_directory` 工具切换),并按这个目录解析相对的 `file_path`。0.2.5 及以前的守卫却按
|
|
112
|
+
进程的 cwd 解析,于是 cd 之后对裸 `MEMORY.md` 的 edit 读不到当前文件、不经测量就放行 ——
|
|
113
|
+
在 dsh 0.2.1-rc.2 上它把索引撑到了 253 行。0.2.6 在 profile 提供 `workingDirectory` 服务时
|
|
114
|
+
读取会话目录,没有时(所有 0.1.x 版本)退回进程 cwd。
|
|
115
|
+
- **CRLF 索引按 dsh 的方式来模拟 edit。** dsh 先把文本统一成 LF 再匹配 edit,写回时恢复文件
|
|
116
|
+
原来的换行。0.2.5 及以前的守卫按原始字节匹配,于是在 CRLF 的 `MEMORY.md` 上(Windows 上很
|
|
117
|
+
常见),LF 的 `old_string` 看起来不存在,edit 被预测成"什么都不改"而放行,dsh 却照常改了。
|
|
118
|
+
0.2.6 用同样的方式归一化,并按 CRLF 字节数计算 CRLF 索引的大小。
|
|
119
|
+
- **验的是你大概率正在用的 dsh,不只是写它时的那一版。** Engramory 仓库里的
|
|
120
|
+
`tests/dsh_e2e/run.py` 会把指定的 dsh 版本装进临时目录,用 `dsh plugin add` 装上本插件,
|
|
121
|
+
再对着一个脚本化的 DeepSeek API 替身跑一次 headless 任务:260 行的整文件写入、把索引撑过
|
|
122
|
+
上限的 edit,都必须被本守卫拒绝,而且磁盘上什么都没变;小写入必须逐字节落盘;skill 必须出现
|
|
123
|
+
在发给模型的技能目录里;dsh 提供的每个工具都必须已经归过类(没归类的新工具可能绕过上限写文件);卸载后
|
|
124
|
+
profile 必须还能启动。通过的版本逐个写在 `package.json` 的 `dsh.compatibility.dshReleases`
|
|
125
|
+
里;没有同一插件版本、同一份代码的通过记录,单元测试就不接受把它标成 `compatible`。
|
|
126
|
+
- dsh 目前是开发者预览版,插件 API 可能变化。本插件刻意只碰 `ctx.tools.guard()` 和两个
|
|
127
|
+
响应式 `ctx.inject()` 子上下文 —— 一个注册 skill,一个读取各会话的工作目录 —— 所以要跟着改
|
|
128
|
+
也很便宜。
|
|
129
|
+
|
|
130
|
+
## 许可
|
|
131
|
+
|
|
132
|
+
MIT —— 见 [Engramory 仓库](https://github.com/tinqiao-oss/engramory)。
|
package/cordis.patch.yml
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
# The dsh-engramory bundle patch: one row, inserted over whatever layers came before
|
|
2
|
-
# it. A patch replaces the targeted row's whole `config`, so a profile overriding this
|
|
3
|
-
# row must restate every key it means to keep.
|
|
4
|
-
#
|
|
5
|
-
# The numbers below are the protocol's own caps, not this port's opinion: 200 lines /
|
|
6
|
-
# 25 KB is what keeps MEMORY.md cheap enough to load at the start of every session, and
|
|
7
|
-
# it is the same limit hooks/engramory_index_guard.py enforces on Claude Code. They live
|
|
8
|
-
# here, beside the row, so a deployment can raise or lower them without touching code.
|
|
9
|
-
|
|
10
|
-
- insert:
|
|
11
|
-
- id: engramory
|
|
12
|
-
name: dsh-engramory
|
|
13
|
-
config:
|
|
14
|
-
indexName: MEMORY.md
|
|
15
|
-
maxLines: 200
|
|
16
|
-
maxBytes: 25600
|
|
1
|
+
# The dsh-engramory bundle patch: one row, inserted over whatever layers came before
|
|
2
|
+
# it. A patch replaces the targeted row's whole `config`, so a profile overriding this
|
|
3
|
+
# row must restate every key it means to keep.
|
|
4
|
+
#
|
|
5
|
+
# The numbers below are the protocol's own caps, not this port's opinion: 200 lines /
|
|
6
|
+
# 25 KB is what keeps MEMORY.md cheap enough to load at the start of every session, and
|
|
7
|
+
# it is the same limit hooks/engramory_index_guard.py enforces on Claude Code. They live
|
|
8
|
+
# here, beside the row, so a deployment can raise or lower them without touching code.
|
|
9
|
+
|
|
10
|
+
- insert:
|
|
11
|
+
- id: engramory
|
|
12
|
+
name: dsh-engramory
|
|
13
|
+
config:
|
|
14
|
+
indexName: MEMORY.md
|
|
15
|
+
maxLines: 200
|
|
16
|
+
maxBytes: 25600
|