@dsh-cc/memory 0.5.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.
Files changed (53) hide show
  1. package/LICENSE +201 -0
  2. package/README.i18n.yaml +6 -0
  3. package/README.md +158 -0
  4. package/README.zh.md +125 -0
  5. package/lib/index.d.ts +75 -0
  6. package/lib/index.d.ts.map +1 -0
  7. package/lib/index.js +91 -0
  8. package/lib/index.js.map +1 -0
  9. package/lib/invariant.d.ts +16 -0
  10. package/lib/invariant.d.ts.map +1 -0
  11. package/lib/invariant.js +22 -0
  12. package/lib/invariant.js.map +1 -0
  13. package/lib/parser.d.ts +30 -0
  14. package/lib/parser.d.ts.map +1 -0
  15. package/lib/parser.js +96 -0
  16. package/lib/parser.js.map +1 -0
  17. package/lib/paths.d.ts +98 -0
  18. package/lib/paths.d.ts.map +1 -0
  19. package/lib/paths.js +236 -0
  20. package/lib/paths.js.map +1 -0
  21. package/lib/recall.d.ts +91 -0
  22. package/lib/recall.d.ts.map +1 -0
  23. package/lib/recall.js +253 -0
  24. package/lib/recall.js.map +1 -0
  25. package/lib/save.d.ts +53 -0
  26. package/lib/save.d.ts.map +1 -0
  27. package/lib/save.js +180 -0
  28. package/lib/save.js.map +1 -0
  29. package/lib/scan.d.ts +29 -0
  30. package/lib/scan.d.ts.map +1 -0
  31. package/lib/scan.js +70 -0
  32. package/lib/scan.js.map +1 -0
  33. package/lib/section.d.ts +129 -0
  34. package/lib/section.d.ts.map +1 -0
  35. package/lib/section.js +353 -0
  36. package/lib/section.js.map +1 -0
  37. package/lib/team.d.ts +90 -0
  38. package/lib/team.d.ts.map +1 -0
  39. package/lib/team.js +167 -0
  40. package/lib/team.js.map +1 -0
  41. package/lib/truncate.d.ts +35 -0
  42. package/lib/truncate.d.ts.map +1 -0
  43. package/lib/truncate.js +52 -0
  44. package/lib/truncate.js.map +1 -0
  45. package/lib/types.d.ts +34 -0
  46. package/lib/types.d.ts.map +1 -0
  47. package/lib/types.js +18 -0
  48. package/lib/types.js.map +1 -0
  49. package/lib/writeback.d.ts +85 -0
  50. package/lib/writeback.d.ts.map +1 -0
  51. package/lib/writeback.js +121 -0
  52. package/lib/writeback.js.map +1 -0
  53. package/package.json +65 -0
package/LICENSE ADDED
@@ -0,0 +1,201 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
@@ -0,0 +1,6 @@
1
+ # Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
2
+ # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
+ # after editing either side, bring the other along and re-record with:
4
+ # pnpm run verify-translation-pairing --write packages/memory/memory/README.md
5
+ README.md: e3d00ee79cf9394465b6022485a6a32442a90322
6
+ README.zh.md: 2a052e4fe0493f782b5acf29b44786d321b04952
package/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # dsh-memory
2
+
3
+ English | [中文](README.zh.md)
4
+
5
+ Claude Code-style file-based memory for the DeepSeek Harness: a durable memdir
6
+ format, a `memory` system-prompt section, the `memory_save` write channel, and
7
+ dynamic recall by a forked side-query. All file access goes through the
8
+ optional `ctx.fs` seam, so a remote or sandboxed backend works unchanged (a
9
+ providerless host mounts memory read-only).
10
+
11
+ ## What this package provides
12
+
13
+ - **Memdir format** — a memory home (default the harness home's `memory/`)
14
+ holding an always-loaded `MEMORY.md` entrypoint (capped at 200 lines / 25 KB)
15
+ per layer, each a one-line index of `.md` topic files. Each topic file
16
+ carries `name`, `description`, and `type` (`user` / `feedback` / `project` /
17
+ `reference`) frontmatter plus a Markdown body. The parser is independently
18
+ exported.
19
+ - **Per-workspace isolation with a global layer** — memories are scoped to the
20
+ git repository (Claude Code auto-memory identity): `canonicalMemoryRoot`
21
+ collapses linked worktrees and subdirectories onto the main checkout, then
22
+ the slug is the upstream `projectKey` dash-encoding of that root, so the
23
+ workspace directory is `<memoryHome>/projects/<slug>/`. Worktrees of one
24
+ repo — and the opt-in `<workspaceDir>/team` layer — share one store. The
25
+ home root itself is the global layer shared by every workspace. Sessions
26
+ of different repositories never see each other's private memories; facts
27
+ useful everywhere are saved with `scope: "global"`. Session transcripts
28
+ still group by raw cwd (`sessions/--<cwd-slug>--`); memory no longer
29
+ matches that grouping for worktree sessions.
30
+ - **`memory` system-prompt section** — save-channel guidance, each layer's
31
+ entrypoint content (truncated), a scope-tagged combined index of topic
32
+ files, and grep search guidance. The section ALWAYS renders (a memoryless
33
+ layer shows a placeholder) so the save guidance never disappears. One global
34
+ registration serves every agent: the text callback renders the assembling
35
+ agent's own workspace layer (the agent arrives via the assemble scope).
36
+ Delegated children (`delegationDepth > 0`) render an empty string so the
37
+ section drops out of the child prompt — matching Claude Code, auto-memory is
38
+ not shared with non-fork subagents; the parent prompt must pass any facts the
39
+ child needs. Directory scans run in the background through `ctx.fs`; rendered
40
+ per-layer fragments are cached and `system-prompt/change` fires only when a
41
+ fragment actually changed; a turn-end listener re-scans so host-side writes
42
+ surface without a restart.
43
+ - **`memory_save` tool** — the ONLY working save channel. Memory directories
44
+ live outside every session workspace, so direct `write`/`edit` calls
45
+ against them are fenced by the fs sandbox and always fail; the section says
46
+ so explicitly. The tool takes structured fields (`name`, `type`,
47
+ `description`, `body`, optional `scope`: `workspace` (default) or `global`),
48
+ resolves the target directory from the calling agent's canonical git root,
49
+ generates the frontmatter host-side, upserts the `MEMORY.md` pointer, and
50
+ writes via the `ctx.fs` seam under a per-call policy of
51
+ `{ mode: 'workspace-write', workspaceRoot: <memory dir> }` — confinement is
52
+ kept, the writable root IS the memory directory. Validation (kebab-case
53
+ slugs, the four types, size caps) shares the `writeback` boundary with
54
+ `dsh-memory-consolidation`'s fork write-back. Registration is opportunistic:
55
+ hosts without a tools service skip it and stay read-only.
56
+ - **Dynamic recall** — an `agent/pre-step` listener asks a small-model side
57
+ query (a forked subagent via `ctx.subagents`) which topic files are relevant
58
+ to the turn, then injects their bodies through `agent.inject()`. Recall
59
+ scans both layers (the agent's workspace directory plus the global one) and
60
+ deduplicates: topic files already shown this session are never re-injected.
61
+ Tools used earlier in the session are tracked (`tools/post-execute`) and
62
+ passed to the selector so reference-doc memories for an actively-used tool
63
+ are suppressed (warnings/gotchas about it are still surfaced). Absence of the
64
+ subagent service or provider skips recall without error.
65
+ - **Team memory (opt-in)** — when `teamEnabled` is `true`, a shared
66
+ per-workspace team directory (`<workspaceDir>/team`) is layered on the
67
+ workspace's private memdir and the `memory` section renders a combined
68
+ workspace + team + global prompt. Every team-memory access runs a
69
+ seam-native validation chain (pure-string key sanitization first, then
70
+ `lstat` final-segment symlink rejection, then `resolve` + `contains` prefix
71
+ containment).
72
+
73
+ ## Usage
74
+
75
+ Load the plugin with `@dsh-cc/memory`. Configuration knobs:
76
+
77
+ | Key | Default | Meaning |
78
+ |---|---|---|
79
+ | `memoryHome` | harness home `memory/` | memory home root: the global layer, and the parent of each repository's `projects/<slug>/` directory |
80
+ | `sectionEnabled` | `true` | register the `memory` system-prompt section |
81
+ | `recallEnabled` | `true` | run dynamic recall on pre-step |
82
+ | `recallProviderName` | `fork` | one-shot subagent provider for the recall query |
83
+ | `recallAgentOptions` | unset | raw `agentOptions` stamped onto the recall fork; wins over `recallUseSmallFast` and is NOT alias-resolved (pass a resolved route, not `{ model: 'haiku' }`) |
84
+ | `recallUseSmallFast` | `false` | opt the recall fork into the cheap lane: stamp `resolve('haiku')` from `ccModelRoutes` (inherit when unconfigured). Opt-in so configuring `haiku` for typed agents doesn't silently flip every recall onto a cross-model, prefix-inheriting fork |
85
+ | `teamEnabled` | `false` | enable the per-workspace team memory directory + combined section |
86
+
87
+ > **`teamEnabled` is off by default.** Enabling it changes the persisted
88
+ > memory layout (creates and reads `<workspaceDir>/team/`), changes what the
89
+ > model writes (workspace vs `team` scope), and points team-memory reads at a
90
+ > shared directory. It is intended for single-tenant, trusted-writer projects:
91
+ > the per-access validation closes traversal, but the *intermediate*-component
92
+ > TOCTOU window is not fully closed (only the final segment is `lstat`-checked,
93
+ > and the resolve/containment check and the read are not atomic). Do not enable
94
+ > `teamEnabled` in multi-tenant or untrusted-writer deployments.
95
+
96
+ > **Layout change.** Before per-workspace isolation, all memories lived flat
97
+ > in `memoryHome/`. Those top-level files are not migrated: they now serve as
98
+ > the global layer (visible to every workspace). A pre-isolation team
99
+ > directory at `<memoryHome>/team/` is inert — team memory now lives at
100
+ > `<workspaceDir>/team/`; move its files manually if you had `teamEnabled`
101
+ > on. Pre-collapse worktree-cwd slugs (`projects/<slug-of-the-worktree-path>/`)
102
+ > are likewise not migrated: they stay on disk unused; new writes land in the
103
+ > main-checkout bucket.
104
+
105
+ ```ts
106
+ import memory from '@dsh-cc/memory'
107
+ await ctx.plugin(memory, { memoryHome: '/tmp/mem' })
108
+ ```
109
+
110
+ ## Model Experience
111
+
112
+ Baseline cost: one synchronous render of the cached section text per step (no
113
+ I/O). The cheapest path additionally scans the memory directory — when it
114
+ contains topics, recall may spend one small-model subagent call per turn until
115
+ all topics have been shown, then stops. Token growth is bounded by the
116
+ entrypoint truncation caps and the five-file recall ceiling.
117
+
118
+ ## API
119
+
120
+ - `parseMemoryFile(raw)` — split a topic file into frontmatter + body.
121
+ - `scanMemoryDirectory(fs, dir, signal?)` — read the entrypoint and topic index.
122
+ - `renderMemorySection(globalDir, workspaceDir, ...)` /
123
+ `renderTeamMemorySection(...)` / `renderLayers(layers)` /
124
+ `saveGuidance(workspaceDir, globalDir)` — the section text builders.
125
+ - `MemorySection` — background-refresh cache holder for the section (one
126
+ registration, per-agent workspace layers).
127
+ - `registerMemorySaveTool(ctx, home, section)` / `MEMORY_SAVE_TOOL` /
128
+ `MEMORY_SAVE_SCOPES` — the model-facing save channel.
129
+ - `validateMemoryWrites(input)` / `writeMemoryFiles(fs, dir, writes)` /
130
+ `memoryWritePolicy(dir)` / `MEMORY_WRITES_SCHEMA` — the host-side write-back
131
+ shared with `dsh-memory-consolidation`.
132
+ - `MemoryRecall` — the pre-step recall coordinator.
133
+ - `truncateEntrypointContent(raw)` — apply the line/byte caps.
134
+ - `resolveMemoryHome`, `resolveWorkspaceMemoryDir`, `canonicalMemoryRoot`,
135
+ `projectSlug`, `cwdOf`, `resolveProjectMemoryRoot` — memdir root and
136
+ workspace helpers. `canonicalMemoryRoot` is the git-repo collapse
137
+ (worktrees → main checkout); `cwdOf` stays the live working copy.
138
+ - `sanitizePathKey(key)`, `validateTeamMemKey(fs, teamDir, relativeKey)`,
139
+ `resolveTeamMemoryRoot(workspaceDir)` — the team-memory security chain and
140
+ path helpers.
141
+
142
+ ## Known Limitations and Deferred Work
143
+
144
+ - The `ctx.fs` seam exposes no mtime, so recall deduplication tracks shown
145
+ paths per session rather than mtime+path; content is re-read fresh each
146
+ injection, which still reflects on-disk changes. CC's `memoryAge` freshness
147
+ weighting is therefore deferred until the seam carries mtime (see
148
+ `docs/cc-parity-matrix.md`).
149
+ - The recall side-query relies on a registered one-shot subagent provider; no
150
+ provider ships with this package (compose `fork` or `spawn`).
151
+ - `memory_save` writes the workspace and global layers only; a team-scope save
152
+ channel and a delete channel are deferred (as is CC's direct-Write parity,
153
+ which the fs sandbox makes impossible for session tools).
154
+ - Team memory (`teamEnabled`) reversibility and safety: enabling it is a
155
+ persisted-format change, and the intermediate-component TOCTOU window (only
156
+ the final segment is `lstat`-checked; resolve/containment and the read are
157
+ not atomic) means it must not be enabled in multi-tenant or untrusted-writer
158
+ deployments.
package/README.zh.md ADDED
@@ -0,0 +1,125 @@
1
+ # dsh-memory
2
+
3
+ 中文 | [English](README.md)
4
+
5
+ 面向 DeepSeek Harness 的 Claude Code 风格文件记忆:可持久化的 memdir 格式、
6
+ `memory` 系统提示词 section、`memory_save` 写入通道,以及通过 fork 的
7
+ side-query 进行动态召回。所有文件访问都走可选的 `ctx.fs` 缝,因此远程或沙箱
8
+ 后端可无改动使用(无 provider 的宿主将记忆挂载为只读)。
9
+
10
+ ## 本包提供的能力
11
+
12
+ - **Memdir 格式** —— 一个记忆 home(默认为 harness home 的 `memory/`),每层
13
+ 包含始终加载的 `MEMORY.md` 入口(上限 200 行 / 25 KB,是 `.md` 主题文件的
14
+ 一行式索引)。每个主题文件带 `name`、`description` 与 `type`(`user` /
15
+ `feedback` / `project` / `reference`)frontmatter 以及 Markdown 正文。解析器
16
+ 已独立导出。
17
+ - **按仓库隔离 + 全局层** —— 记忆按 git 仓库划分(对齐 Claude Code auto memory):
18
+ `canonicalMemoryRoot` 把 linked worktree 和子目录折叠到主 checkout,再对这个
19
+ 根做上游 `projectKey` 的 dash 编码,工作区目录即为 `<memoryHome>/projects/<slug>/`。
20
+ 同一仓库的 worktree —— 以及可选的 `<workspaceDir>/team` 层 —— 共用一份存储。
21
+ home 根本身是所有工作区共享的全局层。不同仓库的会话互相看不到对方的私有记忆;
22
+ 到处都用得上的事实以 `scope: "global"` 保存。会话转录仍按原始 cwd 分组
23
+ (`sessions/--<cwd-slug>--`);worktree 会话的记忆目录不再与之对齐。
24
+ - **`memory` 系统提示词 section** —— 保存通道指引、各层入口内容(截断)、按
25
+ scope 标注的合并主题索引、以及 grep 搜索指引。section 始终渲染(无记忆的层显示
26
+ 占位符),保存指引永不缺席。一次全局注册服务所有 agent:text 回调渲染发起组装
27
+ 的 agent 自己的工作区层(agent 经 assemble scope 传入)。委派 child
28
+ (`delegationDepth > 0`)渲染空串,section 从 child 提示词中消失——对齐 Claude
29
+ Code:自动记忆不与非 fork 子代理共享,父 prompt 必须自行传递 child 需要的事实。
30
+ 目录扫描经 `ctx.fs` 在后台进行;各层渲染片段缓存,仅当片段实际变化时才发出
31
+ `system-prompt/change`;轮末监听器会重新扫描,host 侧写入无需重启即可进入提示词。
32
+ - **`memory_save` 工具** —— 唯一可用的保存通道。记忆目录在所有会话 workspace
33
+ 之外,直接的 `write`/`edit` 调用会被 fs sandbox 拦截、必然失败,section 文案
34
+ 对此有明确说明。工具接收结构化字段(`name`、`type`、`description`、`body`、可选
35
+ `scope`:`workspace`(默认)或 `global`),按调用 agent 的规范 git 根解析目标
36
+ 目录,由 host 侧生成 frontmatter、upsert `MEMORY.md` 指针行,并经 `ctx.fs` 缝以
37
+ `{ mode: 'workspace-write', workspaceRoot: <记忆目录> }` 的 per-call 策略
38
+ 落盘——围栏保留,可写根恰好是记忆目录。校验(kebab-case slug、四种类型、大小
39
+ 上限)与 `dsh-memory-consolidation` 的 fork 写回共用同一 `writeback` 边界。
40
+ 注册是机会式的:宿主无 tools 服务时跳过并保持只读。
41
+ - **动态召回** —— `agent/pre-step` 监听器用小型模型 side-query(通过
42
+ `ctx.subagents` fork)判断哪些主题文件与当前轮相关,再通过 `agent.inject()`
43
+ 注入其正文。召回扫描两层(agent 的工作区目录加上全局目录)并去重:本会话已
44
+ 展示过的主题文件不会重复注入。会跟踪本会话早期使用过的工具
45
+ (`tools/post-execute`)并传给 selector,从而抑制正在使用工具的参考文档类记忆
46
+ (其警告/坑点仍会呈现)。subagent 服务或 provider 缺失时跳过召回,不报错。
47
+ - **团队记忆(可选)** —— 当 `teamEnabled` 为 `true` 时,在工作区私有 memdir 之内
48
+ 叠加该工作区共享的团队目录(`<workspaceDir>/team`),`memory` section 渲染合并的
49
+ 工作区 + 团队 + 全局提示词。每次团队记忆访问都走 seam 原生校验链(先纯字符串键
50
+ sanitization,再 `lstat` 末段 symlink 拒绝,最后 `resolve` + `contains` 前缀
51
+ 包含校验)。
52
+
53
+ ## 使用
54
+
55
+ 以 `@dsh-cc/memory` 加载插件。配置项:
56
+
57
+ | Key | 默认值 | 含义 |
58
+ |---|---|---|
59
+ | `memoryHome` | harness home `memory/` | 记忆 home 根:全局层本身,也是各仓库 `projects/<slug>/` 目录的父级 |
60
+ | `sectionEnabled` | `true` | 注册 `memory` 系统提示词 section |
61
+ | `recallEnabled` | `true` | 在 pre-step 上运行动态召回 |
62
+ | `recallProviderName` | `fork` | 召回查询的一次性子 agent provider |
63
+ | `recallAgentOptions` | 未设置 | 直接盖到召回 fork 上的原始 `agentOptions`;优先于 `recallUseSmallFast` 且**不做** alias 解析(传已解析路由,别传 `{ model: 'haiku' }`) |
64
+ | `recallUseSmallFast` | `false` | 让召回 fork 走低价车道:盖 `ccModelRoutes` 的 `resolve('haiku')` 路由(未配置则继承父路由)。默认关闭——为 typed agent 配置 `haiku` 不应悄悄把每次召回变成跨模型、继承前缀的 fork |
65
+ | `teamEnabled` | `false` | 启用 per-workspace 团队记忆目录与合并 section |
66
+
67
+ > **`teamEnabled` 默认关闭。** 开启会改变持久化记忆布局(创建并读取
68
+ > `<workspaceDir>/team/`)、改变模型写入内容(workspace 与 `team` 两种 scope),
69
+ > 并把团队记忆读取指向共享目录。适用于单租户、受信写者项目:逐次访问校验关闭了
70
+ > 穿越,但*中间组件* TOCTOU 窗口并未完全关闭(仅末段做 `lstat` 校验,且
71
+ > resolve/包含校验与读取并非原子)。不得在**多租户或不可信写者**场景启用
72
+ > `teamEnabled`。
73
+
74
+ > **布局变更。** 按工作区隔离之前,所有记忆平铺在 `memoryHome/`。这些顶层文件不做
75
+ > 迁移:它们现在充当全局层(对所有工作区可见)。隔离前的 `<memoryHome>/team/` 团队
76
+ > 目录已失效——团队记忆现在位于 `<workspaceDir>/team/`;若你开启过 `teamEnabled`,
77
+ > 请手动搬移其中的文件。折叠前按 worktree cwd 编码的 `projects/<slug>/` 同样不迁移:
78
+ > 留在磁盘上不再写入;新写入落在主 checkout 的桶里。
79
+
80
+ ```ts
81
+ import memory from '@dsh-cc/memory'
82
+ await ctx.plugin(memory, { memoryHome: '/tmp/mem' })
83
+ ```
84
+
85
+ ## Model Experience
86
+
87
+ 基准开销:每步一次同步渲染缓存的 section 文本(无 I/O)。最便宜路径还会扫描
88
+ 记忆目录——当存在主题时,召回每轮可能花费一次小型模型 subagent 调用,直到所有
89
+ 主题均已展示为止。token 增长受入口截断上限与五文件召回上限约束。
90
+
91
+ ## API
92
+
93
+ - `parseMemoryFile(raw)` —— 将主题文件拆分为 frontmatter 与正文。
94
+ - `scanMemoryDirectory(fs, dir, signal?)` —— 读取入口与主题索引。
95
+ - `renderMemorySection(globalDir, workspaceDir, ...)` /
96
+ `renderTeamMemorySection(...)` / `renderLayers(layers)` /
97
+ `saveGuidance(workspaceDir, globalDir)` —— section 文本构建器。
98
+ - `MemorySection` —— section 的后台刷新缓存持有者(一次注册,按 agent 呈现
99
+ 工作区层)。
100
+ - `registerMemorySaveTool(ctx, home, section)` / `MEMORY_SAVE_TOOL` /
101
+ `MEMORY_SAVE_SCOPES` —— 面向模型的保存通道。
102
+ - `validateMemoryWrites(input)` / `writeMemoryFiles(fs, dir, writes)` /
103
+ `memoryWritePolicy(dir)` / `MEMORY_WRITES_SCHEMA` —— 与
104
+ `dsh-memory-consolidation` 共用的 host 侧写回。
105
+ - `MemoryRecall` —— pre-step 召回协调器。
106
+ - `truncateEntrypointContent(raw)` —— 施加行/字节上限。
107
+ - `resolveMemoryHome`、`resolveWorkspaceMemoryDir`、`canonicalMemoryRoot`、
108
+ `projectSlug`、`cwdOf`、`resolveProjectMemoryRoot` —— memdir 根与工作区解析辅助。
109
+ `canonicalMemoryRoot` 是 git 仓库折叠(worktree → 主 checkout);`cwdOf` 仍是
110
+ 现场工作副本。
111
+ - `sanitizePathKey(key)`、`validateTeamMemKey(fs, teamDir, relativeKey)`、
112
+ `resolveTeamMemoryRoot(workspaceDir)` —— 团队记忆安全链与路径辅助。
113
+
114
+ ## 已知限制与延期工作
115
+
116
+ - `ctx.fs` 缝不暴露 mtime,因此召回去重按会话记录已展示路径,而非 mtime+path;
117
+ 每次注入都会重新读取内容,仍能反映磁盘变更。CC 的 `memoryAge` 新鲜度加权
118
+ 因此推迟,直到 seam 携带 mtime(见 `docs/cc-parity-matrix.md`)。
119
+ - 召回 side-query 依赖已注册的一次性子 agent provider;本包不内置 provider
120
+ (请组合 `fork` 或 `spawn`)。
121
+ - `memory_save` 只写工作区层与全局层;团队 scope 的保存通道与删除通道已延期(CC 的
122
+ 直接 Write 语义同样推迟——fs sandbox 使会话工具无法直接写记忆目录)。
123
+ - 团队记忆(`teamEnabled`)的可逆性与安全性:启用即改变持久化格式,且中间组件
124
+ TOCTOU 窗口(仅末段做 `lstat` 校验;resolve/包含校验与读取并非原子)意味着
125
+ 不得在**多租户或不可信写者**场景启用。
package/lib/index.d.ts ADDED
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Claude Code-style file-based memory: the memdir format and parser, the
3
+ * `memory` system-prompt section, and dynamic recall by a forked side-query.
4
+ *
5
+ * All file access goes through the optional `ctx.fs` seam, so a remote or
6
+ * sandboxed backend works unchanged; a providerless host mounts memory as a
7
+ * no-op. Recall needs `ctx.subagents` and a registered one-shot provider;
8
+ * absence of either skips recall without error.
9
+ *
10
+ * @module @dsh-cc/memory
11
+ */
12
+ import type { Context } from '@deepseek-ai/cordis';
13
+ import z from '@deepseek-ai/schemastery';
14
+ export { parseMemoryFile } from './parser.ts';
15
+ export type { ParsedMemoryFile } from './parser.ts';
16
+ export { truncateEntrypointContent, ENTRYPOINT_NAME, MAX_ENTRYPOINT_LINES, MAX_ENTRYPOINT_BYTES } from './truncate.ts';
17
+ export type { EntrypointTruncation } from './truncate.ts';
18
+ export { MEMORY_TYPES, parseMemoryType } from './types.ts';
19
+ export type { MemoryType, MemoryFrontmatter, MemoryIndexEntry } from './types.ts';
20
+ export { scanMemoryDirectory } from './scan.ts';
21
+ export type { MemoryDirectoryState } from './scan.ts';
22
+ export { resolveMemoryHome, resolveProjectMemoryRoot, resolveWorkspaceMemoryDir, canonicalMemoryRoot, projectSlug, cwdOf, gitExecSync, PROJECT_MEMORY_DIR, PROJECTS_DIR, GIT_PROBE_TIMEOUT_MS, } from './paths.ts';
23
+ export type { MemoryGitExec, MemoryGitExecResult } from './paths.ts';
24
+ export { MemorySection, renderMemorySection, renderTeamMemorySection, renderLayers, saveGuidance, MEMORY_SECTION_NAME, MEMORY_SECTION_ORDER } from './section.ts';
25
+ export type { MemoryLayer } from './section.ts';
26
+ export { MemoryRecall, SubagentMemorySelector, extractSelectedNames, MAX_RECALL_MEMORIES } from './recall.ts';
27
+ export type { MemorySelector, RecallCandidate } from './recall.ts';
28
+ export { TeamMemoryError, sanitizePathKey, resolveTeamMemoryRoot, validateTeamMemKey, readTeamMemFile, TEAM_MEMORY_DIR, TEAM_ENTRYPOINT_NAME } from './team.ts';
29
+ export { MEMORY_SAVE_TOOL, MEMORY_SAVE_SCOPES, MemorySaveError, pointerLine, registerMemorySaveTool, renderTopicFile, upsertPointer, } from './save.ts';
30
+ export type { MemorySaveArgs } from './save.ts';
31
+ export { MEMORY_WRITES_SCHEMA, WRITEBACK_MAX_FILE_BYTES, WRITEBACK_MAX_FILES, WRITEBACK_MAX_TOTAL_BYTES, memoryWritePolicy, validateMemoryWrites, writeMemoryFiles, } from './writeback.ts';
32
+ export type { MemoryWrite, MemoryWritePolicy } from './writeback.ts';
33
+ export declare const name = "memory";
34
+ /** Core services required for section registration and event listeners. */
35
+ export declare const inject: string[];
36
+ /** Memory plugin configuration. */
37
+ export interface Config {
38
+ /**
39
+ * Memory home root. Defaults to the harness home `memory/`. The home IS the
40
+ * global layer; each repository's private layer lives at
41
+ * `<home>/projects/<slug>` (slug encodes the canonical git root).
42
+ */
43
+ memoryHome?: string;
44
+ /** Whether the `memory` system-prompt section is registered (default true). */
45
+ sectionEnabled?: boolean;
46
+ /** Whether dynamic recall runs on pre-step (default true). */
47
+ recallEnabled?: boolean;
48
+ /** One-shot subagent provider used by recall (default `fork`). */
49
+ recallProviderName?: string;
50
+ /** Optional small-model selection passed to the recall subagent. */
51
+ recallAgentOptions?: unknown;
52
+ /**
53
+ * Opt the recall selector into `resolve('haiku')`. Default false so
54
+ * configuring `haiku` for typed agents does not silently cross-model
55
+ * every recall fork. Ignored when `recallAgentOptions` is set.
56
+ */
57
+ recallUseSmallFast?: boolean;
58
+ /**
59
+ * Whether team memory is enabled (default false). Enables a shared
60
+ * per-workspace team directory (`<workspaceDir>/team`), renders the
61
+ * triple-layer section, and validates all team-memory access. Off by
62
+ * default: onboarding a team directory changes the persisted format and is
63
+ * not safe in multi-tenant or untrusted-writer deployments (see README
64
+ * residual).
65
+ */
66
+ teamEnabled?: boolean;
67
+ }
68
+ export declare const Config: z<Config>;
69
+ /**
70
+ * Register the `memory` system-prompt section and recall listener.
71
+ * @param ctx - the host context carrying the system-prompt and agent seams.
72
+ * @param config - memory behavior knobs.
73
+ */
74
+ export declare function apply(ctx: Context, config?: Config): void;
75
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,CAAC,MAAM,0BAA0B,CAAA;AAQxC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AACnD,OAAO,EAAE,yBAAyB,EAAE,eAAe,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACtH,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAC1D,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAC/C,YAAY,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AACrD,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EACzB,mBAAmB,EACnB,WAAW,EACX,KAAK,EACL,WAAW,EACX,kBAAkB,EAClB,YAAY,EACZ,oBAAoB,GACrB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AACpE,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,YAAY,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACjK,YAAY,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAC7G,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAA;AAC/J,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,sBAAsB,EACtB,eAAe,EACf,aAAa,GACd,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,EACnB,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,GACjB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEpE,eAAO,MAAM,IAAI,WAAW,CAAA;AAC5B,2EAA2E;AAC3E,eAAO,MAAM,MAAM,UAAmB,CAAA;AAEtC,mCAAmC;AACnC,MAAM,WAAW,MAAM;IACrB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,+EAA+E;IAC/E,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,8DAA8D;IAC9D,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,kEAAkE;IAClE,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAQ3B,CAAA;AAkBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,MAAW,GAAG,IAAI,CAiC7D"}