dsh-engramory 1.0.1 → 1.0.2

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.
Files changed (4) hide show
  1. package/README.en.md +218 -213
  2. package/README.md +135 -135
  3. package/index.js +2692 -2563
  4. package/package.json +79 -79
package/README.en.md CHANGED
@@ -1,213 +1,218 @@
1
- [简体中文](README.md) | **English**
2
-
3
- # dsh-engramory
4
-
5
- [![dsh-xray](https://img.shields.io/endpoint?url=https%3A%2F%2Funstone.github.io%2Fdsh-xray%2Fbadge%2Ftinqiao-oss__engramory.json)](https://unstone.github.io/dsh-xray/registry.html#tinqiao-oss__engramory)
6
-
7
- Long-term memory for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
8
- (dsh). What the AI remembers is kept as plain markdown files you can open and edit
9
- at any time. Part of [Engramory](https://github.com/tinqiao-oss/engramory).
10
-
11
- ![The memory page](https://raw.githubusercontent.com/tinqiao-oss/engramory/master/adapters/dsh/plugin/screenshots/memory-page.png)
12
-
13
- ## What it does
14
-
15
- - **The memory index rides along on every step.** The index, `MEMORY.md`, lists the
16
- notes by name. The plugin puts it into dsh's context on every step, so the AI knows
17
- what it has saved without reading a file first, and opens a note when it needs the
18
- detail. A write shows up on the next request; whatever is past the cap is left
19
- out, and the AI is told to compact.
20
- - **The index has a hard cap.** 200 lines / 25 KB by default. When the AI writes the
21
- index with dsh's file tools and the plugin can work out the result, a write that
22
- would grow it past the cap is refused before it happens, and the AI is told to
23
- compact; compaction still passes. Details in [How the cap decides](#how-the-cap-decides).
24
- - **The rules come with it.** What to save, how, and when to clean up: the rules
25
- (a skill) are registered by the plugin, with no files to install.
26
- - **A Memory page in the sidebar.** Read-only; it lists the notes by name and opens
27
- one in your computer's default app.
28
-
29
- ## Install
30
-
31
- Needs Node.js 18+ and `pnpm` on PATH (dsh's plugin command uses it). In dsh's desktop
32
- app you can also search for `dsh-engramory` on the Plugins page.
33
-
34
- ```sh
35
- dsh plugin --profile <name> add dsh-engramory
36
- ```
37
-
38
- To uninstall, use `remove` instead of `add`. Compatible dsh releases are listed in
39
- `dsh.compatibility.dshReleases` in [`package.json`](package.json); an unlisted release
40
- is unverified.
41
-
42
- **Where the memory lives:**
43
-
44
- - **The global store**, `~/.dsh/.engramory-memory/` (under `$DSH_HOME` when it is set).
45
- With the plugin alone this is the only store; the AI creates it on its first save.
46
- - **A project store** (optional), `.engramory-memory/` in a project's root, is created
47
- by you only: run `python tools/engramory_init.py dsh --project-root <project>` from
48
- the Engramory repo, which also git-ignores it. Where one exists, what holds only in
49
- that project goes there; everything else stays in the global store.
50
-
51
- Only one AI may write a store; two writers overwrite each other. To share it, let the
52
- others read it only: see [one writer, many readers](../../reader/README.md). Memories are plain text and carry machine-local detail. Inside a project's git repo
53
- the store must be git-ignored; a dedicated private repo for the store is fine.
54
-
55
- **Without the plugin:** `python tools/engramory_init.py dsh --install-skill` writes the
56
- rules into `$DSH_HOME/AGENTS.md`, installs the skill in `$DSH_HOME/skills/engramory/`
57
- (`$DSH_HOME` defaults to `~/.dsh`) and creates the global store. The AI still keeps memory, but reads the index itself, and
58
- the cap holds only if it runs `engramory_check.py` after each write.
59
-
60
- ## Companion mode (大肥鱼模式)
61
-
62
- For a character you chat with: the community's 大肥鱼, or any persona. Turn on the
63
- companion switch on the settings page, or set `companion: true`.
64
-
65
- ![The memory page in companion mode](https://raw.githubusercontent.com/tinqiao-oss/engramory/master/adapters/dsh/plugin/screenshots/companion-mode.png)
66
-
67
- - **Three more types for the character's own life** ([SKILL.md §2.2](../../../SKILL.md)):
68
- `moment`, something you shared, with its date, folded into a monthly archive when
69
- the index fills; `upcoming`, something coming up in your life, asked about once its
70
- date passes and rewritten as a moment once you say how it went; `self`, a change in
71
- the character itself, such as a nickname. A moment or self note is deleted only when
72
- you ask it to forget.
73
- - **Recall in character.** Even a greeting may call for recall, and what it remembered,
74
- or failed to save, is told in the character's voice, without naming files or tools.
75
- - **The persona preset needs the file tools.** The memory arrives even under a preset
76
- that turns optional context off (dsh's `minimal`, for one). To save notes, though, the
77
- preset needs dsh's file tools and the skill tool; a preset with only a shell reads the
78
- memory, and its writes go past the cap.
79
-
80
- ## Social mode
81
-
82
- A choice inside companion mode, for a character other people can talk to as well, such
83
- as one wired to QQ, WeChat, Douyin or Bilibili through a bridge plugin. Choose *Social*
84
- under *Memory access* on the settings page, or set `audience: social`. If only you talk
85
- to this dsh, keep the default, *Private*.
86
-
87
- With it on, **only your own conversations get the memory**: one is yours when you have
88
- typed in it in dsh's own window (the desktop app or `dsh web`) and nobody else has.
89
- A subagent goes with its parent. Messages from a bridge plugin (unless it marks them as
90
- the owner's, below), `dsh headless`, other programs, and terminal or editor clients
91
- count as someone else's. Once anyone else has written in a conversation it stays
92
- theirs, and the memory already put into it stays in its history.
93
-
94
- Someone else's conversation gets no index and is not told where the store is; dsh's
95
- file tools there may name files only (every directory is refused), and any path into
96
- a store is refused however it is spelled. This is **not** a security boundary: a shell
97
- tool reads any file, and another profile on the same account can read the store too.
98
- Real isolation needs filesystem permissions or a sandbox.
99
-
100
- ### For bridge authors
101
-
102
- With social mode on, every message a bridge submits counts as someone else's. For a
103
- message from the owner's own account (their private QQ chat with the character, say),
104
- put a `speaker` on its source:
105
-
106
- ```js
107
- agent.followup(createUserMessage({
108
- content: [{ type: 'text', text }],
109
- source: { kind: 'plugin:your-bridge', speaker: { role: 'owner' } },
110
- }))
111
- ```
112
-
113
- | `speaker.role` | Meaning |
114
- |---|---|
115
- | `owner` | The owner wrote it: it comes from the account the user set as theirs in your bridge. |
116
- | `guest` | Someone else wrote it, the same as no `speaker` at all. |
117
- | `none` | Nobody wrote it: your plugin's own report, reminder or notice. It does not make the owner's conversation someone else's. |
118
-
119
- A group conversation stays someone else's once anyone else in it speaks. A program
120
- that talks to dsh only through dsh web's API cannot set a source, so its messages count
121
- as someone else's; if it sends `clientTimeZone` with a prompt it is taken for the
122
- owner, so a program forwarding other people's messages must leave it out.
123
-
124
- ## Settings page and memory page
125
-
126
- ![The settings page](https://raw.githubusercontent.com/tinqiao-oss/engramory/master/adapters/dsh/plugin/screenshots/settings-page.png)
127
-
128
- - **Settings page:** on dsh's *Plugins* page, open *Engramory memory* to set companion
129
- mode, memory access and the index cap. Saving goes through dsh's own profile editor,
130
- which writes your profile's `cordis.patch.yml` and reloads the plugin. Only you, signed
131
- in, can call it, so an agent cannot use it to switch social mode off.
132
- - **Memory page:** *Memory* in the sidebar. It lists the global store and each
133
- workspace's project store (with an absolute `indexPath`, that store alone; with a
134
- relative one, none), with each index's size against the cap, its notes' names
135
- by type, and its archive. Read-only, never a note's text; a name, *Open the index* and
136
- *Open the folder* open on the machine dsh runs on. The two switches at the top save
137
- as you flip them.
138
-
139
- ## Configuration
140
-
141
- Settings the page does not cover go in your profile's `cordis.patch.yml`. Patch the
142
- EXISTING row by id; a second `- insert:` adds another engramory row and leaves the old
143
- caps in force. A patch replaces the row's whole `config`, so list every key you keep:
144
-
145
- ```yaml
146
- - id: engramory
147
- config:
148
- indexName: MEMORY.md
149
- maxLines: 200
150
- maxBytes: 25600
151
- ```
152
-
153
- | Field | Default | Meaning |
154
- |---|---|---|
155
- | `indexName` | `MEMORY.md` | The file name treated as the index, case-insensitive. |
156
- | `maxLines` | `200` | Line cap for the index. |
157
- | `maxBytes` | `25600` | UTF-8 byte cap for the index (25 KB). |
158
- | `indexPath` | unset | The one index to use: absolute, or relative to the session's directory. Unset, a `MEMORY.md` in any project is capped as an index; a store outside the default places also needs it to be found. |
159
- | `registerSkill` | `true` | `false` keeps the cap but skips the built-in rules. |
160
- | `injectIndex` | `true` | `false` stops putting the index into context; the AI reads it instead. |
161
- | `language` | dsh's | `zh` (or `zh-CN` and other regional forms) writes the store in Chinese: note names, headings such as `## 用户`, the Why/How labels. Any other value is English. Unset, it follows dsh's language; a change applies once dsh restarts. Frontmatter keys and `type:` values stay English, so every host reads the store. |
162
- | `companion` | `false` | `true` turns companion mode on. |
163
- | `audience` | `private` | With companion mode on, `social` turns social mode on. |
164
- | `skill` | built-in | Your own markdown in place of the built-in rules; it must then say where the store is. |
165
-
166
- ## How the cap decides
167
-
168
- | Case | Decision |
169
- |---|---|
170
- | The index ends within the caps | allow |
171
- | The write grows the index past a cap | **deny**, with the numbers and what to compact |
172
- | The index is over a cap and the write shrinks it or keeps its size | allow, so compaction can go step by step (210 → 205 → 198) |
173
- | An edit with `old_str`/`new_str` | its result is worked out first, then judged as above |
174
- | A partial edit whose result cannot be worked out (e.g. `insert`) | deny on an over-cap index, else allow |
175
- | The index is missing, unreadable, or not a regular file | a whole-file write is judged as if it were empty; a partial edit passes |
176
- | The index is past the read limit (`min(16 MiB, max(1 MiB, 4 × maxBytes))`) | not read whole: a whole-file write is judged on bytes alone; a partial edit passes |
177
- | Reads, writes to other files, unknown tools or arguments | allow; reads are never blocked, even over the cap |
178
-
179
- Lines and bytes are judged apart: only a dimension that is over its cap AND grew is
180
- refused, so a write with fewer bytes but lines grown past the cap is still refused.
181
- The line count ignores a trailing newline, so an index exactly at the cap stays
182
- writable. This table is pinned by `node --test` in CI.
183
-
184
- ## Known limits
185
-
186
- - **Only dsh's own file tools are capped** (`write`, `edit`, `str_replace_editor` and
187
- the like). Shell tools, MCP file tools and external editors go past it: run
188
- `engramory_check.py` after editing the index and `engramory_doctor.py` now and then.
189
- - **Stores are found only where the installer puts them.** Elsewhere, or for a project
190
- store that links out of the project, set `indexPath`. A project's root is found by
191
- its `.git` only, and a project store created mid-session can take ten seconds to appear.
192
- - **Social mode knows you by what dsh's window sends.** Apart from a bridge's owner
193
- messages, only what you type in dsh's own window is yours, and not when the browser's
194
- time zone cannot be read. A kind of
195
- message a later dsh adds counts as someone else's until this plugin knows it: your
196
- conversation may lose the memory, never the other way round.
197
- - **What the memory page opens is checked by path.** dsh's opener resolves the path once
198
- more before the desktop opens it, so a process changing the store's files at that
199
- moment could redirect what opens (as with dsh's own opener for any workspace file).
200
- - **Experimental:** companion mode, social mode, and the settings and memory pages may
201
- change in a minor version.
202
-
203
- ## Versions
204
-
205
- This package carries Engramory's version: `dsh-engramory@X.Y.Z` is the repo's
206
- `vX.Y.Z`. The cap's decisions (the table above) and the settings outside the
207
- experimental features change incompatibly only in a major version. See Engramory's
208
- [Versioning](https://github.com/tinqiao-oss/engramory/blob/master/README.en.md#versioning); history is in the
209
- [CHANGELOG](../../../CHANGELOG.md).
210
-
211
- ## License
212
-
213
- MIT — see the [Engramory repository](https://github.com/tinqiao-oss/engramory).
1
+ [简体中文](README.md) | **English**
2
+
3
+ # dsh-engramory
4
+
5
+ [![dsh-xray](https://img.shields.io/endpoint?url=https%3A%2F%2Funstone.github.io%2Fdsh-xray%2Fbadge%2Ftinqiao-oss__engramory.json)](https://unstone.github.io/dsh-xray/registry.html#tinqiao-oss__engramory)
6
+
7
+ Long-term memory for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
8
+ (dsh). What the AI remembers is kept as plain markdown files you can open and edit
9
+ at any time. It is the dsh build of [Engramory](https://github.com/tinqiao-oss/engramory),
10
+ which is general-purpose agent memory: Claude Code, Codex and others can use the same store.
11
+
12
+ ![The memory page](https://raw.githubusercontent.com/tinqiao-oss/engramory/master/adapters/dsh/plugin/screenshots/memory-page.png)
13
+
14
+ ## What it does
15
+
16
+ - **The memory index rides along on every step.** The index, `MEMORY.md`, lists the
17
+ notes by name. The plugin puts it into dsh's context on every step, so the AI knows
18
+ what it has saved without reading a file first, and opens a note when it needs the
19
+ detail. A write shows up on the next request; whatever is past the cap is left
20
+ out, and the AI is told to compact.
21
+ - **The index has a hard cap.** 200 lines / 25 KB by default. When the AI writes the
22
+ index with dsh's file tools and the plugin can work out the result, a write that
23
+ would grow it past the cap is refused before it happens, and the AI is told to
24
+ compact; compaction still passes. Details in [How the cap decides](#how-the-cap-decides).
25
+ - **The rules come with it.** What to save, how, and when to clean up: the rules
26
+ (a skill) are registered by the plugin, with no files to install.
27
+ - **A Memory page in the sidebar.** Read-only; it lists the notes by name and opens
28
+ one in your computer's default app.
29
+
30
+ ## Install
31
+
32
+ Needs Node.js 18+ and `pnpm` on PATH (dsh's plugin command uses it). In dsh's desktop
33
+ app you can also search for `dsh-engramory` on the Plugins page.
34
+
35
+ ```sh
36
+ dsh plugin --profile <name> add dsh-engramory
37
+ ```
38
+
39
+ To uninstall, use `remove` instead of `add`. Compatible dsh releases are listed in
40
+ `dsh.compatibility.dshReleases` in [`package.json`](https://github.com/tinqiao-oss/engramory/blob/master/adapters/dsh/plugin/package.json); an unlisted release
41
+ is unverified.
42
+
43
+ **Where the memory lives:**
44
+
45
+ - **The global store**, `~/.dsh/.engramory-memory/` (under `$DSH_HOME` when it is set).
46
+ With the plugin alone this is the only store; the AI creates it on its first save.
47
+ - **A project store** (optional), `.engramory-memory/` in a project's root, is created
48
+ by you only: run `python tools/engramory_init.py dsh --project-root <project>` from
49
+ the Engramory repo, which also git-ignores it. Where one exists, what holds only in
50
+ that project goes there; everything else stays in the global store.
51
+
52
+ Only one AI may write a store; two writers overwrite each other. To share it, let the
53
+ others read it only: see [one writer, many readers](https://github.com/tinqiao-oss/engramory/blob/master/adapters/reader/README.md). Memories are plain text and carry machine-local detail. Inside a project's git repo
54
+ the store must be git-ignored; a dedicated private repo for the store is fine.
55
+
56
+ **Without the plugin:** `python tools/engramory_init.py dsh --install-skill` writes the
57
+ rules into `$DSH_HOME/AGENTS.md`, installs the skill in `$DSH_HOME/skills/engramory/`
58
+ (`$DSH_HOME` defaults to `~/.dsh`) and creates the global store. The AI still keeps memory, but reads the index itself, and
59
+ the cap holds only if it runs `engramory_check.py` after each write.
60
+
61
+ ## Companion mode (大肥鱼模式)
62
+
63
+ For a character you chat with: the community's 大肥鱼, or any persona. Turn on the
64
+ companion switch on the settings page, or set `companion: true`.
65
+
66
+ ![The memory page in companion mode](https://raw.githubusercontent.com/tinqiao-oss/engramory/master/adapters/dsh/plugin/screenshots/companion-mode.png)
67
+
68
+ - **Three more types for the character's own life** ([SKILL.md §2.2](https://github.com/tinqiao-oss/engramory/blob/master/SKILL.md)):
69
+ `moment`, something you shared, with its date, folded into a monthly archive when
70
+ the index fills; `upcoming`, something coming up in your life, asked about once its
71
+ date passes and rewritten as a moment once you say how it went; `self`, a change in
72
+ the character itself, such as a nickname. A moment or self note is deleted only when
73
+ you ask it to forget.
74
+ - **Recall in character.** Even a greeting may call for recall, and what it remembered,
75
+ or failed to save, is told in the character's voice, without naming files or tools.
76
+ - **The persona preset needs the file tools.** The memory arrives even under a preset
77
+ that turns optional context off (dsh's `minimal`, for one). To save notes, though, the
78
+ preset needs dsh's file tools and the skill tool; a preset with only a shell reads the
79
+ memory, and its writes go past the cap.
80
+
81
+ ## Social mode
82
+
83
+ A choice inside companion mode, for a character other people can talk to as well, such
84
+ as one wired to QQ, WeChat, Douyin or Bilibili through a bridge plugin. Choose *Social*
85
+ under *Memory access* on the settings page, or set `audience: social`. If only you talk
86
+ to this dsh, keep the default, *Private*.
87
+
88
+ With it on, **only your own conversations get the memory**: one is yours when you have
89
+ typed in it in dsh's own window (the desktop app or `dsh web`) and nobody else has.
90
+ A subagent goes with its parent. Messages from a bridge plugin (unless it marks them as
91
+ the owner's, below), `dsh headless`, other programs, and terminal or editor clients
92
+ count as someone else's. Once anyone else has written in a conversation it stays
93
+ theirs, and the memory already put into it stays in its history.
94
+
95
+ Someone else's conversation gets no index and is not told where the store is; dsh's
96
+ file tools there may name files only (every directory is refused), and any path into
97
+ a store is refused however it is spelled. This is **not** a security boundary: a shell
98
+ tool reads any file, and another profile on the same account can read the store too.
99
+ Real isolation needs filesystem permissions or a sandbox. A custom `skill` (see
100
+ [Configuration](#configuration)) reaches every conversation as written, someone else's
101
+ included, so if it says where the store is, they are told too.
102
+
103
+ ### For bridge authors
104
+
105
+ With social mode on, every message a bridge submits counts as someone else's. For a
106
+ message from the owner's own account (their private QQ chat with the character, say),
107
+ put a `speaker` on its source:
108
+
109
+ ```js
110
+ agent.followup(createUserMessage({
111
+ content: [{ type: 'text', text }],
112
+ source: { kind: 'plugin:your-bridge', speaker: { role: 'owner' } },
113
+ }))
114
+ ```
115
+
116
+ | `speaker.role` | Meaning |
117
+ |---|---|
118
+ | `owner` | The owner wrote it: it comes from the account the user set as theirs in your bridge. |
119
+ | `guest` | Someone else wrote it, the same as no `speaker` at all. |
120
+ | `none` | Nobody wrote it: your plugin's own report, reminder or notice. It does not make the owner's conversation someone else's. |
121
+
122
+ A group conversation stays someone else's once anyone else in it speaks. A program
123
+ that talks to dsh only through dsh web's API cannot set a source, so its messages count
124
+ as someone else's; if it sends `clientTimeZone` with a prompt it is taken for the
125
+ owner, so a program forwarding other people's messages must leave it out.
126
+
127
+ ## Settings page and memory page
128
+
129
+ ![The settings page](https://raw.githubusercontent.com/tinqiao-oss/engramory/master/adapters/dsh/plugin/screenshots/settings-page.png)
130
+
131
+ - **Settings page:** on dsh's *Plugins* page, open *Engramory memory* to set companion
132
+ mode, memory access and the index cap. Saving goes through dsh's own profile editor,
133
+ which writes your profile's `cordis.patch.yml` and reloads the plugin. Only you, signed
134
+ in, can call it, so an agent cannot use it to switch social mode off.
135
+ - **Memory page:** *Memory* in the sidebar. It lists the global store and each
136
+ workspace's project store (with an absolute `indexPath`, that store alone; with a
137
+ relative one, none), with each index's size against the cap, its notes' names
138
+ by type, and its archive. Read-only, never a note's text; a name, *Open the index* and
139
+ *Open the folder* open on the machine dsh runs on; a machine with no desktop (a
140
+ headless server, a session over SSH) shows none of them. The switches at the top
141
+ (companion mode, and memory access once it is on) save as you flip them.
142
+
143
+ ## Configuration
144
+
145
+ Settings the page does not cover go in your profile's `cordis.patch.yml`. Patch the
146
+ EXISTING row by id; a second `- insert:` adds another engramory row and leaves the old
147
+ caps in force. A patch replaces the row's whole `config`, so list every key you keep:
148
+
149
+ ```yaml
150
+ - id: engramory
151
+ config:
152
+ indexName: MEMORY.md
153
+ maxLines: 200
154
+ maxBytes: 25600
155
+ ```
156
+
157
+ | Field | Default | Meaning |
158
+ |---|---|---|
159
+ | `indexName` | `MEMORY.md` | The file name treated as the index. The cap matches it case-insensitively; putting the index into context needs the exact name on a case-sensitive filesystem (Linux, say). |
160
+ | `maxLines` | `200` | Line cap for the index. |
161
+ | `maxBytes` | `25600` | UTF-8 byte cap for the index (25 KB). |
162
+ | `indexPath` | unset | The one index to use: absolute, or relative to the session's directory. Unset, a `MEMORY.md` in any project is capped as an index; a store outside the default places also needs it to be found. |
163
+ | `registerSkill` | `true` | `false` skips the built-in rules; the cap and the index in context stay. |
164
+ | `injectIndex` | `true` | `false` stops putting the index into context; the AI reads it instead. |
165
+ | `language` | dsh's | `zh` (or `zh-CN` and other regional forms) writes the store in Chinese: note names, headings such as `## 用户`, the Why/How labels. Any other string is English; a value that is not a string, or is empty, counts as unset. Unset, it follows dsh's language; a change applies once dsh restarts. Frontmatter keys and `type:` values stay English, so every host reads the store. |
166
+ | `companion` | `false` | `true` turns companion mode on. |
167
+ | `audience` | `private` | With companion mode on, `social` turns social mode on. |
168
+ | `skill` | built-in | Your own markdown in place of the built-in rules; it must then say where the store is. In social mode, others' conversations see it too. |
169
+
170
+ ## How the cap decides
171
+
172
+ | Case | Decision |
173
+ |---|---|
174
+ | The index ends within the caps | allow |
175
+ | The write grows the index past a cap | **deny**, with the numbers and what to compact |
176
+ | The index is over a cap and the write shrinks it or keeps its size | allow, so compaction can go step by step (210 → 205 → 198) |
177
+ | A partial edit whose result can be worked out: `edit` (`old_string`/`new_string`), `str_replace_editor`'s replace (`old_str`/`new_str`) and `insert` | its result is worked out first, then judged as above; each tool from the arguments it uses and the way it matches |
178
+ | A partial edit whose result cannot be worked out (arguments missing or not recognised) | deny on an over-cap index, else allow |
179
+ | The index is missing, unreadable, or not a regular file | a whole-file write is judged as if it were empty; a partial edit passes |
180
+ | The index is past the read limit (`min(16 MiB, max(1 MiB, 4 × maxBytes))`) | not read whole: a whole-file write is judged on bytes alone; a partial edit passes |
181
+ | Reads, writes to other files, unknown tools or arguments | allow; reads are never blocked, even over the cap |
182
+
183
+ Lines and bytes are judged apart: only a dimension that is over its cap AND grew is
184
+ refused, so a write with fewer bytes but lines grown past the cap is still refused.
185
+ The line count ignores a trailing newline, so an index exactly at the cap stays
186
+ writable. This table is pinned by `node --test` in CI.
187
+
188
+ ## Known limits
189
+
190
+ - **Only dsh's file tools are capped:** `write`, `edit`, and the `str_replace_editor`
191
+ some versions and presets carry; a tool of the same name from another plugin is
192
+ judged the same way. Shell tools, MCP file tools and external editors go past it: run
193
+ `engramory_check.py` after editing the index and `engramory_doctor.py` now and then.
194
+ - **Stores are found only where the installer puts them.** Elsewhere, or for a project
195
+ store that links out of the project, set `indexPath`. A project's root is found by
196
+ its `.git` only, and a project store created mid-session can take ten seconds to appear.
197
+ - **Social mode knows you by what dsh's window sends.** Apart from a bridge's owner
198
+ messages, only what you type in dsh's own window is yours, and not when the browser's
199
+ time zone cannot be read. A kind of
200
+ message a later dsh adds counts as someone else's until this plugin knows it: your
201
+ conversation may lose the memory, never the other way round.
202
+ - **What the memory page opens is checked by path.** dsh's opener resolves the path once
203
+ more before the desktop opens it, so a process changing the store's files at that
204
+ moment could redirect what opens (as with dsh's own opener for any workspace file).
205
+ - **Experimental:** companion mode, social mode, and the settings and memory pages may
206
+ change in a minor version.
207
+
208
+ ## Versions
209
+
210
+ This package carries Engramory's version: `dsh-engramory@X.Y.Z` is the repo's
211
+ `vX.Y.Z`. The cap's decisions (the table above) and the settings outside the
212
+ experimental features change incompatibly only in a major version. See Engramory's
213
+ [Versioning](https://github.com/tinqiao-oss/engramory/blob/master/README.en.md#versioning); history is in the
214
+ [CHANGELOG](https://github.com/tinqiao-oss/engramory/blob/master/CHANGELOG.md).
215
+
216
+ ## License
217
+
218
+ MIT — see the [Engramory repository](https://github.com/tinqiao-oss/engramory).