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/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Beijing Tinqiao Technology Co., Ltd.
|
|
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.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Beijing Tinqiao Technology Co., Ltd.
|
|
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,134 +1,154 @@
|
|
|
1
|
-
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
2
|
-
|
|
3
|
-
# dsh-engramory
|
|
4
|
-
|
|
5
|
-
[](https://unstone.github.io/dsh-xray/registry.html#tinqiao-oss__engramory)
|
|
6
|
-
|
|
7
|
-
<sub>`C2` is the level `manifest.bundle.patch` puts every mountable dsh plugin at (74.6% of the scanned ecosystem); it is the only flag on this card — no `exec`, `eval`, install script, outbound domain, or environment read.</sub>
|
|
8
|
-
|
|
9
|
-
Curated, file-based long-term memory for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) —
|
|
10
|
-
plain markdown notes you can open with anything, one store shared across every host you
|
|
11
|
-
use, and an index cap that is a **real refusal** rather than a request. (If the store
|
|
12
|
-
lives inside a project's git repo it must be git-ignored — memories carry machine-local
|
|
13
|
-
detail; see SKILL.md §1. A *dedicated private* repo for the store itself is fine.)
|
|
14
|
-
|
|
15
|
-
Part of [Engramory](https://github.com/tinqiao-oss/engramory).
|
|
16
|
-
|
|
17
|
-
## Why a plugin, when an `AGENTS.md` block already works
|
|
18
|
-
|
|
19
|
-
The block does carry the discipline, and for recall that is enough. Two things it cannot
|
|
20
|
-
do:
|
|
21
|
-
|
|
22
|
-
**The cap becomes deterministic.** Engramory keeps its index under 200 lines / 25 KB
|
|
23
|
-
because the index loads every session and everything past the cap silently stops being
|
|
24
|
-
recalled. On most hosts that limit is rules plus a checker the agent has to remember to
|
|
25
|
-
run. dsh exposes `ctx.tools.guard()` — a synchronous, **monotonic** refusal: once a guard
|
|
26
|
-
returns a reason, no later listener can turn it back into an allow. So here the limit is
|
|
27
|
-
enforced, not requested. Use **0.2.
|
|
28
|
-
worked upstream and carried an `inject` declaration this Cordis does not accept — it
|
|
29
|
-
installed but never activated (issue #8)
|
|
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
|
-
|
|
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
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
1
|
+
[English](README.md) | [简体中文](README.zh-CN.md)
|
|
2
|
+
|
|
3
|
+
# dsh-engramory
|
|
4
|
+
|
|
5
|
+
[](https://unstone.github.io/dsh-xray/registry.html#tinqiao-oss__engramory)
|
|
6
|
+
|
|
7
|
+
<sub>`C2` is the level `manifest.bundle.patch` puts every mountable dsh plugin at (74.6% of the scanned ecosystem); it is the only flag on this card — no `exec`, `eval`, install script, outbound domain, or environment read.</sub>
|
|
8
|
+
|
|
9
|
+
Curated, file-based long-term memory for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) —
|
|
10
|
+
plain markdown notes you can open with anything, one store shared across every host you
|
|
11
|
+
use, and an index cap that is a **real refusal** rather than a request. (If the store
|
|
12
|
+
lives inside a project's git repo it must be git-ignored — memories carry machine-local
|
|
13
|
+
detail; see SKILL.md §1. A *dedicated private* repo for the store itself is fine.)
|
|
14
|
+
|
|
15
|
+
Part of [Engramory](https://github.com/tinqiao-oss/engramory).
|
|
16
|
+
|
|
17
|
+
## Why a plugin, when an `AGENTS.md` block already works
|
|
18
|
+
|
|
19
|
+
The block does carry the discipline, and for recall that is enough. Two things it cannot
|
|
20
|
+
do:
|
|
21
|
+
|
|
22
|
+
**The cap becomes deterministic.** Engramory keeps its index under 200 lines / 25 KB
|
|
23
|
+
because the index loads every session and everything past the cap silently stops being
|
|
24
|
+
recalled. On most hosts that limit is rules plus a checker the agent has to remember to
|
|
25
|
+
run. dsh exposes `ctx.tools.guard()` — a synchronous, **monotonic** refusal: once a guard
|
|
26
|
+
returns a reason, no later listener can turn it back into an allow. So here the limit is
|
|
27
|
+
enforced, not requested. Use **0.2.7 or later**: 0.2.0 shipped before profile installs
|
|
28
|
+
worked upstream and carried an `inject` declaration this Cordis does not accept — it
|
|
29
|
+
installed but never activated (issue #8) — and up to 0.2.5 an edit could slip past the
|
|
30
|
+
cap on a CRLF index, or on dsh 0.2 through a relative path after a cd, and up to 0.2.6
|
|
31
|
+
through a symlink under another name — or a FIFO named like the index could hang the host
|
|
32
|
+
(#20, #21; see Known limits). Outside Claude Code, dsh is the first host with
|
|
33
|
+
the shim actually written (a few others expose equivalent pre-write seams — see
|
|
34
|
+
PORTING.md — but have no shim yet).
|
|
35
|
+
|
|
36
|
+
**The protocol arrives with the plugin.** The skill is registered at runtime through
|
|
37
|
+
dsh's skill registry — via a reactive `ctx.inject(['skills'], …)` child, so a profile
|
|
38
|
+
without a registry still gets the cap and one whose registry activates late still gets
|
|
39
|
+
the skill — which means it does not depend on landing files in one of the five skill
|
|
40
|
+
roots dsh scans, a path that is easy to get subtly wrong and fails silently when you do.
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
dsh plugin --profile <name> add dsh-engramory
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That is the whole install. The package ships a `dsh.bundle` manifest pointing at its own
|
|
49
|
+
`cordis.patch.yml`, so the row is inserted into the profile's plugin tree for you — no
|
|
50
|
+
hand-editing. To change a default, patch the EXISTING row by id in your profile's own
|
|
51
|
+
patch layer — do **not** use a second `- insert:` (insert always appends, so you would
|
|
52
|
+
end up with two engramory rows and the original caps still enforced):
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
- id: engramory
|
|
56
|
+
config:
|
|
57
|
+
indexName: MEMORY.md
|
|
58
|
+
maxLines: 200
|
|
59
|
+
maxBytes: 25600
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
A patch replaces the targeted row's whole `config`, so list every key you mean to keep.
|
|
63
|
+
|
|
64
|
+
For the store itself and the always-on block, use the installer in the Engramory repo
|
|
65
|
+
(`python tools/engramory_init.py dsh --install-skill`). This plugin enforces the cap and
|
|
66
|
+
supplies the protocol; it does not create the store.
|
|
67
|
+
|
|
68
|
+
## Configuration
|
|
69
|
+
|
|
70
|
+
| Field | Default | Meaning |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `indexName` | `MEMORY.md` | Basename treated as the memory index (case-insensitive; both `file_path` and `str_replace_editor`'s `path` are checked) — the name a write is called by, or the name of the file it lands on, so a symlink under another name is caught too. An empty or non-string value falls back to the default. |
|
|
73
|
+
| `maxLines` | `200` | Hard line cap. Non-positive or non-finite values fall back to the default. |
|
|
74
|
+
| `maxBytes` | `25600` | Hard UTF-8 byte cap (25 KB). |
|
|
75
|
+
| `indexPath` | unset | Absolute path of the ONE index to guard. Without it the guard matches on basename alone, so an unrelated `MEMORY.md` in another project is gated too; set this and only that exact file is. Compared by identity — the file the write lands on, whatever it is called (symlinks and `..` resolved; case-folded on Windows only) — and a path that does not exist yet still has its first write guarded. |
|
|
76
|
+
| `registerSkill` | `true` | Set `false` to keep the cap but skip the runtime skill. |
|
|
77
|
+
| `skill` | built-in | Replace the skill body with your own markdown. |
|
|
78
|
+
|
|
79
|
+
## What the guard actually does
|
|
80
|
+
|
|
81
|
+
| Call | Decision |
|
|
82
|
+
|---|---|
|
|
83
|
+
| `write` (or `str_replace_editor` `create`) ending within caps | allow |
|
|
84
|
+
| `write`/`create` that GROWS the index past a cap | **deny**, naming the numbers and what to compact |
|
|
85
|
+
| `write`/`create` that SHRINKS or keeps an over-cap index | allow — incremental compaction (210 → 205 → 198) must stay possible |
|
|
86
|
+
| Edit carrying `old_str`/`new_str`: result simulated | judged by the RESULT, same grow/shrink rule as a write — sized before it is built, so a `replace_all` whose result would be too long to build is refused by its size alone |
|
|
87
|
+
| Unsimulable partial (e.g. `insert`) on an over-cap index | **deny**, telling the agent a shrinking whole-file write passes |
|
|
88
|
+
| Unsimulable partial on a healthy index | allow |
|
|
89
|
+
| `read`, `view`, or any unknown tool | allow — recall must never be blocked, even over cap |
|
|
90
|
+
| Any write to any other file | allow — not the guard's business |
|
|
91
|
+
| Missing/unreadable current index on a whole-file write | treated as EMPTY (mirrors the Python guard) — a within-caps write passes, an over-cap first write is refused |
|
|
92
|
+
| Missing/unreadable index on a partial edit | allow — a guard must never block work over a path it cannot read |
|
|
93
|
+
| A FIFO, device, or directory under the index's name | never read — counts as unreadable (a read there would hang or flood the host, which runs guards on its main thread) |
|
|
94
|
+
| An index past the read limit (1 MiB, or 4× `maxBytes`, at most 16 MiB) | not read whole: a whole-file write is judged on bytes against the filesystem's size (every shrink passes; its line count is unknown, so lines are not capped there), and an edit passes unmeasured, as on an unreadable index |
|
|
95
|
+
| Malformed execution (no arguments, non-string path/content) | allow — not recognisably a write |
|
|
96
|
+
|
|
97
|
+
The deny rule mirrors `hooks/engramory_index_guard.py` exactly: refuse only a result
|
|
98
|
+
that is over a cap AND grew past the current file. The line count ignores a trailing
|
|
99
|
+
newline, so an index sitting exactly at the cap stays writable.
|
|
100
|
+
|
|
101
|
+
## Known limits
|
|
102
|
+
|
|
103
|
+
- **An unsimulable partial that crosses the cap from under it is not caught.** `insert`
|
|
104
|
+
and friends do not carry the resulting text. The breach is caught by the next
|
|
105
|
+
whole-file write, or by `engramory_check.py`. What is guaranteed: an already-over
|
|
106
|
+
index cannot be grown further, and a shrinking write always passes.
|
|
107
|
+
- **The tool roster follows dsh's documented tool-fs contract** (`write`/`edit` with
|
|
108
|
+
`file_path`, `str_replace_editor` with `path`) and is deliberately conservative:
|
|
109
|
+
unknown tools pass.
|
|
110
|
+
- **0.2.0 never activated; 0.2.1 is the first version that runs.** While the rc.6
|
|
111
|
+
preview bug blocked all third-party profile installs upstream, the only coverage a
|
|
112
|
+
plugin could have was mocks — and 0.2.0's mock hid two activation bugs: an
|
|
113
|
+
older-Cordis `{ required, optional }` inject shape (read as waiting for services
|
|
114
|
+
literally named `required`/`optional`, pending forever) and a bare `ctx.skills` read
|
|
115
|
+
of an undeclared service (throws under Cordis' reflective context). The first field
|
|
116
|
+
install caught both the moment installs became possible (issue #8). 0.2.1 fixes them,
|
|
117
|
+
verified end to end on a live dsh 0.1.0-rc.7 web profile (0.2.0 reproduces the boot
|
|
118
|
+
failure byte for byte; 0.2.1 boots and serves) and against the vendored
|
|
119
|
+
`@deepseek-ai/cordis` resolver (activation with, without, and with a late-mounted
|
|
120
|
+
skill registry). The test mock now mirrors the reflective-context access rules, and
|
|
121
|
+
the guard's decision table stays covered by `node --test` (47 cases, run in
|
|
122
|
+
Engramory's CI).
|
|
123
|
+
- **A relative path is measured where dsh writes it.** dsh 0.2 gives every session its
|
|
124
|
+
own working directory (moved by its `working_directory` tool) and resolves a
|
|
125
|
+
relative `file_path` against that. Up to 0.2.5 the guard resolved against the
|
|
126
|
+
process's cwd instead, so after a cd an edit of a bare `MEMORY.md` read no current
|
|
127
|
+
file and went through unmeasured — on dsh 0.2.1-rc.2 it grew the index to 253
|
|
128
|
+
lines. 0.2.6 reads the session's directory from dsh's `workingDirectory` service
|
|
129
|
+
when the profile has one, and falls back to the process's cwd when it does not
|
|
130
|
+
(every 0.1.x release).
|
|
131
|
+
- **A CRLF index is edited the way dsh edits it.** dsh matches an edit on
|
|
132
|
+
LF-normalised text and writes the result back in the file's own line endings. Up
|
|
133
|
+
to 0.2.5 the guard matched the raw bytes, so on a CRLF `MEMORY.md` — the usual
|
|
134
|
+
shape on Windows — an LF `old_string` looked absent, the edit was predicted to
|
|
135
|
+
change nothing, and dsh applied it. 0.2.6 normalises the same way and sizes a CRLF
|
|
136
|
+
index in CRLF bytes.
|
|
137
|
+
- **Checked against the dsh you are likely running, not only the one it was written
|
|
138
|
+
on.** `tests/dsh_e2e/run.py` in the Engramory repo installs a dsh release into a
|
|
139
|
+
throwaway prefix, adds this plugin with `dsh plugin add`, and drives one headless
|
|
140
|
+
task against a scripted stand-in for the DeepSeek API: a 260-line write and an edit
|
|
141
|
+
past the cap must come back refused by this guard with nothing reaching the disk, a
|
|
142
|
+
small write must land byte for byte, the skill must be advertised, every tool dsh
|
|
143
|
+
offers must already be classified (a new one could write past the cap), and removal
|
|
144
|
+
must leave a profile that still boots. The releases it passed on are listed in
|
|
145
|
+
`package.json` under `dsh.compatibility.dshReleases`; a unit test rejects a
|
|
146
|
+
`compatible` there without a recorded run of the same plugin version and code.
|
|
147
|
+
- dsh is a developer preview and its plugin API can change. This plugin deliberately
|
|
148
|
+
touches only `ctx.tools.guard()` and two reactive `ctx.inject()` children — one
|
|
149
|
+
registers the skill, one reads each session's working directory — so it stays cheap
|
|
150
|
+
to fix.
|
|
151
|
+
|
|
152
|
+
## License
|
|
153
|
+
|
|
154
|
+
MIT — see the [Engramory repository](https://github.com/tinqiao-oss/engramory).
|