dsh-memento 0.2.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.
@@ -0,0 +1,132 @@
1
+ # ARCHITECTURE
2
+
3
+ `dsh-memento` 的架构文档:三角色 seam、数据流、以及每个关键设计决策的理由。面向对象:插件维护者与想接入 `ctx.memory` 的其它插件作者(如 dsh-claude-move 的 seed 集成)。
4
+
5
+ ## 三角色 seam
6
+
7
+ 本插件是完整的能力接缝(Service Definition / Provider / Consumer 三角色齐全),与"只做记忆仓库"的插件有本质区别:
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────────────┐
11
+ │ Consumer:memory 工具(F5) Consumer:冻结快照注入(F6) │
12
+ │ - add/replace/remove/query - systemPrompt 段,order=-50 │
13
+ │ - 规范 JSON + 纯 render - 会话首 assemble 时同步读库 │
14
+ │ - 尊重 exec.signal - WeakMap 按 Session 冻结 │
15
+ └───────────────┬──────────────────────────────┬──────────────────────┘
16
+ │ 写(带 exec.agent/callId) │ 读(同步,session cwd)
17
+ ▼ ▼
18
+ ┌─────────────────────────────────────────────────────────────────────┐
19
+ │ Service Definition:ctx.memory(F1,index.mjs MemoryService) │
20
+ │ budgets() add() replace() remove() query() seed() │
21
+ │ │
22
+ │ 写路径(不可绕过的审批门,S3): │
23
+ │ 预算预检 ──▶ ctx.approval.request ──▶ 预算复审 ──▶ 落盘 ──▶ 审计 │
24
+ │ (审批对 approval/asked+decided 自动入会话日志) │
25
+ │ 读路径:无审批;query 带 sessionId 时记 recalled 审计行 │
26
+ └───────────────┬─────────────────────────────────────────────────────┘
27
+ │ 同步 SQL(node:sqlite,单连接串行)
28
+ ▼
29
+ ┌─────────────────────────────────────────────────────────────────────┐
30
+ │ Provider:lib/store.mjs(F2,本地 SQLite,WAL,零依赖) │
31
+ │ entries:双轨×双层×文本 + 元数据(来源/时间/会话 id/workspace_key) │
32
+ │ audit: 插件自有审计账本(动作/结果/审批来源/会话 id) │
33
+ │ 唯一子串匹配用 instr();零/多命中报错;事务内 replace/remove 原子 │
34
+ └─────────────────────────────────────────────────────────────────────┘
35
+ ```
36
+
37
+ 设计目标:将来任何插件(包括 dsh-claude-move 的 `seed(source:'claude')`)都能向同一个 store 喂数据、读数据——store 是同一份,信任门在 Service 层统一把守。
38
+
39
+ ## 数据流(一次写 → 下次会话可见)
40
+
41
+ ```
42
+ memory 工具(add)
43
+ → MemoryService.add(预算预检:store.usage + checkBudget)
44
+ → ctx.approval.request(toolName:'memory',reason 携带完整载荷 [dsh-memento] 前缀)
45
+ ├─ 审批服务先裁决会话级 policy(never 不可绕过)
46
+ └─ waterfall:本插件 answerer(prepend)按 writePolicy 裁决
47
+ ask → 委托 UI answerer(人类批准/拒绝)
48
+ auto → allowed-once;off → rejected
49
+ → outcome === allowed-once 才继续(否则 WriteDeniedError,零落盘)
50
+ → 预算复审(审批等待期间用量可能变化,此刻为权威)
51
+ → store.insertEntry + audit 行(outcome 含 policy 来源)
52
+ → 会话日志(已知事件类型)已有 approval/asked+decided 审计对
53
+ → 下一会话首个 assemble:渲染冻结快照(带用量头)注入 systemPrompt 段
54
+ └ 同一文本也写入 audit(snapshot) 行 + request/header.system(S2 可重建)
55
+ ```
56
+
57
+ ## 关键设计决策
58
+
59
+ 1. **快照注入走 systemPrompt 段(而非 pre-step sourced message)**。
60
+ - `snapshotOrder` 直接映射段顺序语义(默认 -50:harness identity(-100) 之后、persona(0) 之前);
61
+ - rc.6 已证明的路径:`assemble.agent.session.header.cwd` 提供工作区作用域(dsh-claude-move 同款);
62
+ - 渲染文本随 `request/header` 事件逐字落会话日志(system 字段),加上 `audit(snapshot)` 行,S2 可重建有两条独立证据链;
63
+ - 冻结语义 = systemPrompt 全文会话内不变 = 前缀缓存稳定(这正是"冻结快照"的设计目的)。
64
+ 代价与约束:提供者必须同步(rc.6 不 await),SQLite 同步读 + WeakMap 冻结满足 N1。
65
+
66
+ 2. **审批门做在 Service 写方法内部,不在工具层**(对应 Hermes issue #48181 教训)。
67
+ - 任何路径(memory 工具、其它插件、未来 /memory 命令)只要调 `ctx.memory.add/replace/remove/seed` 就必然经过 `ctx.approval.request`;
68
+ - `writePolicy` 是 Config(ask/auto/off,默认 ask),模型不可见、不可改;
69
+ - 本插件在 `approval/request` 上注册 prepend answerer:只认领 toolName='memory' 且 reason 带 `[dsh-memento]` 前缀的请求;ask 委托续链(人类 answerer),auto/off 直接裁决;
70
+ - 会话级 `approval/never` 由审批服务在 answerer 之前裁决,任何 answerer(含 prepend)都无法绕过——本插件遵从该硬不变量。
71
+
72
+ 3. **预算在 Service 层双重校验(审批前后各一次),Provider 层绝不截断**。
73
+ - 预检在打扰用户之前拒绝明显超限;复审以审批等待后的真实用量为权威(期间可能有其它写);
74
+ - 写满抛结构化 `BUDGET_EXCEEDED`(含 used/limit/needed),由模型整合/删除后重试;绝不自动压缩、绝不静默截断;
75
+ - 计数单位是 JS 字符(UTF-16 code unit):中文场景一个汉字计 1,预算可预测,按需调大(默认 user 2000 / agent 4000 字符/层)。
76
+
77
+ 4. **memory/* 会话事件:词汇已声明,运行时自适应派发(rc.6 约束)**。
78
+ - `types.d.ts` 声明合并了 `memory/added|updated|removed|recalled|snapshot` 的 SessionEventMap 词汇与载荷形状;
79
+ - rc.6 无插件事件注册面:`KNOWN_SESSION_EVENT_TYPES` 不含 memory/*,且 `Session.append` 无法标记 `ignorable`——append 未注册类型会让该会话下次加载被持久化层整体拒绝(read 路径 enforce,见 session-persistence coordinator);
80
+ - 因此运行时只在 `KNOWN_SESSION_EVENT_TYPES.has(type)` 时才 append(未来 harness 收录后自动开启);当前审计链 = approval/asked+decided(已知类型,reason 携带完整写载荷)+ 插件审计表 audit。这是与官方机制对齐后的必然选择,不是偷工减料。
81
+
82
+ 5. **审计 = 审批对 + 审计表 + 快照三条链**。
83
+ - 每次写:approval/asked(reason 全文载荷)→ approval/decided(结果)→ audit 行(outcome 含 policy 来源、entry id、会话 id);
84
+ - 每次 recall:audit(recalled) 行;每次快照:audit(snapshot) 行(与注入文本逐字一致);
85
+ - 卸载插件后:记忆库与会话日志保留,旧会话可正常加载(因为从不 append 未注册事件类型)。
86
+
87
+ 6. **替换/删除的并发与回滚**。
88
+ - replace/remove 在 Provider 层事务内"定位+变更"原子执行;Service 层审批前先定位(零/多命中不打扰用户);
89
+ - 审批期间条目被并发写移除:审批后重定位失败即结构化报错(响亮,不静默)。
90
+ - seed 整批先全量预算预检,通过后同步插入(无 await 间隔),不存在部分写入。
91
+
92
+ 7. **工作区键**:workspace 条目按会话 cwd 的规范化绝对值隔离;Windows 下大小写不敏感(同一项目以不同大小写路径打开仍命中同一 workspace 层)。两个进程共用一个 `$DSH_HOME` 时,SQLite 以 busy_timeout 串行写,但"谁先写谁赢",跨进程一致性不保证(学 Hermes 的官方警告,见 README 安全边界)。
93
+
94
+ 8. **V2 观察面的命令写路径(turn 外审批门)**:`/memory` 命令在模型回合之外执行,而审批服务 `ctx.approval.request` 要求 open turn(`approval/asked + approval/decided` 审计对必须被 turn 包围,这是 DSH 持久化日志的 commit/replay 硬边界)。命令写因此走**同一** `approval/request` waterfall(同一 answerer 链、同一 `writePolicy` 裁决),差异只在审计落点:turn 内路径落审批审计对,命令路径落插件审计表 + `command/done`。会话级 `never` 策略按公开 API(`approval.overrideOf`)在派发前预检,与审批服务同语义、不可绕过。这是对审批 seam 约束(审计对需 turn 包围)的最小偏离,已文档化并测试(`test/v2.test.mjs`)。
95
+
96
+ 9. **V2 面板只读**:Web 面板(`dsh.client` 零构建抽屉)只做条目浏览/搜索/预算条/审计尾;审批与写操作一律发生在 DSH 内置审批 UI + `memory` 工具(否则会与内置审批呈现重复并产生分歧)。
97
+
98
+ 10. **检索引擎 = 大小写不敏感 instr + 召回计数排序,不用 FTS5**。
99
+ - 实测(Node 22 内置 SQLite,FTS5 可用):trigram 分词器无法索引单字 CJK 字符——`'中文测试'` 中查 `'中文'` 零命中;unicode61 把 CJK 连续段当一个 token,仅前缀可查。本插件语料以中文记忆为主,子串语义必须对 CJK 成立,instr 是唯一正确的内置引擎。
100
+ - query 大小写不敏感(lower() 折叠 ASCII;CJK 无大小写不受影响),与面板过滤、sessionQuery 文本检索语义一致;replace/remove/consolidate 定位同语义(`lib/match.mjs` 的 `findUniqueMatch` 统一折叠,store 层 lower(instr) 与之一致)。
101
+ - 召回排序:query 命中页的条目 `recall_count` +1、`last_recalled` 落地(SCHEMA v3 列);排序 `recall_count DESC, updated_at DESC`(高频即重要)。快照仍走 `listEntries` 创建序(冻结块稳定优先)。
102
+ - 未来真正的升级路径是 harness 出现 embedding seam 后的语义召回(Provider 角色天然兼容),不是 FTS5。
103
+
104
+ 11. **第三维 agentKey(per-agent 作用域,SCHEMA v3)**。
105
+ - 写方 session 的 `header.agentPreset` 经 `agentKeyOf` 规范化(缺失→'' 共享层);条目与提案落 `agent_key`。
106
+ - 可见性:`agent_key === '' || === 会话 agentKey`,且 scope 规则不变;预算仍按 track×scope 计(agentKey 不新增预算维度)。
107
+ - 工具不暴露 agentKey 参数——由写方 session 自动决定,模型不可选,避免污染。
108
+
109
+ 12. **语言面(Config.language,en/zh)与错误文案的分界**。
110
+ - 随语言切换的只有**模型可见/命令/面板**文案:`memory`/`memory_recall` 工具描述与参数说明、冻结快照(`lib/strings.mjs` 词表)、`/memory` 命令输出、Web 面板标签(语言经 `/api/memento/*` 响应的 `language` 字段下发)。en 为源文,zh 为对应译文;未知语言回退 en。
111
+ - **错误信息保持英文**:结构化错误码(`INVALID_INPUT`、`BUDGET_EXCEEDED`…)与 message 是跨语言的审计契约,模型按 code 分支(整合后重试等),不受 language 影响。
112
+ - 非法 `language` 值在加载期响亮失败(schema 层 union + apply 直调路径双保险)。默认 `en` 与 DSH 核心提示一致。
113
+ - `/memory export` 是纯只读路径(条目 + 预算的 JSON 导出,备份/迁移/透明性),不落审计、不走审批门——与 Claude Code/Codex"记忆是用户可读的纯文本"精神对齐。
114
+
115
+ ## V3 协同(F12/F13,接口已就位,文档对齐)
116
+
117
+ ### F12:seed 与 dsh-claude-move 的对接方式
118
+
119
+ `ctx.memory.seed(entries, write)` 已在 V1 实现:一次 `ask` 审批整批、任一条超预算整批拒绝、逐条落审计(source 透传)。dsh-claude-move 接入时的对齐约定:
120
+
121
+ - 把 Claude `memory/*.md` 解析为条目数组,每条 `{ track: 'agent', scope: 'workspace', text, source: 'claude', workspaceKey }`(workspaceKey 用会话 cwd 规范化键,缺省时取写方 agent 的会话 cwd);
122
+ - 以 `ctx.get('memory')` 可选依赖读取服务(dsh-claude-move 已有 `withService` 同款模式),服务缺失时优雅跳过——**不破坏其现有行为**;
123
+ - seed 的 `write.agent` 必须存在(审批路由),dsh-claude-move 的导入命令/工具有 invocation/exec agent 可传入;
124
+ - 预算吃紧时 seed 整批失败并返回结构化 `BUDGET_EXCEEDED`,由导入方拆分批次重试。
125
+
126
+ ### F13:auto-review hook 点(不实现第二模型)
127
+
128
+ 本插件暴露的接缝:`write.gate`(写上下文里的可选函数)。默认走 `ctx.approval.request`(turn 内、落审批审计对);`/memory` 命令在 turn 外以 `makeCommandGate` 注入同一 waterfall 的无审计对变体。未来的 dsh-auto-review 若想接管记忆写审批,可在 `approval/request` 上注册自己的 answerer(先于/取代人类 answerer),无需改本插件一行——审批 answerer 链本身就是 hook 点;`writePolicy` 为 `ask` 时 `applyWritePolicy` 委托 `next()`,任何挂链的第二模型 answerer 都能接管。
129
+
130
+ ## 配置面(无硬编码 tunable)
131
+
132
+ 全部字段可 cordis.yml 覆盖,schema 见 `index.mjs` 的 `Config`;完整字段表(`enabled` / `dbPath` / `budgets` / `writePolicy` / `writePolicies` / `language` / `snapshotOrder` / `maxEntriesPerQuery` / `commandListLimit` / `commandAuditLimit` / `recall.*` / `panelEntriesLimit` / `panelAuditLimit` / `auditRetentionDays` / `proposals.*`)以 README 配置表为准,本文件不再逐项复制以免漂移。非法值加载期响亮失败。
package/CHANGELOG.md ADDED
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.2.0] - 2026-08-14
9
+
10
+ ### Added
11
+
12
+ - `language` config (`'en'` default / `'zh'`): model-visible text, the frozen snapshot, `/memory` command output, and the web panel all switch languages; invalid values fail loudly at load.
13
+ - `/memory export` subcommand: read-only JSON dump of all entries + budgets (backup / migration / transparency).
14
+ - Web panel renders `en`/`zh` labels according to the plugin's `language` (the language travels with the `/api/memento/*` responses).
15
+ - Bilingual `memory_recall` tool description, parameter descriptions, and result renderer.
16
+ - New README section "What we learned from the terminal memories" (Claude Code / Codex / Hermes), mirrored across all five languages.
17
+ - `commandListLimit` (default 50) and `commandAuditLimit` (default 10) config fields for the `/memory` command surface.
18
+ - Coverage gate (`npm run check:coverage`: lib ≥90%, index.mjs ≥85%, all files ≥90%) and a weekly `next`-rc compatibility probe workflow.
19
+ - Peer dependency ranges widened to `>=0.1.0-rc.6` so later harness rc releases resolve without a coordinated release.
20
+ - Package metadata (`repository`/`homepage`/`bugs`), `types` conditions on the `exports` map, and this changelog.
21
+
22
+ ### Fixed
23
+
24
+ - Web panel entries route now honors the `limit` query parameter and renders a truncation notice (previously >20 entries were silently capped).
25
+ - `/memory list` / `query` render at most `commandListLimit` entries and label truncation instead of silently dropping rows.
26
+ - `seed` inserts run in one SQLite transaction: any mid-batch failure rolls back the whole batch (the documented all-or-nothing promise now holds).
27
+ - `replace` re-resolves the target and recomputes the net budget delta after approval, closing the stale-previous race during the approval wait.
28
+ - Audit rows record the real decision source (`via approval, writePolicy …` vs `via write gate`) instead of always labeling the configured policy.
29
+ - `memory_recall` description now states the true case semantics (case-sensitive for memory entries, case-insensitive for session history).
30
+ - `maxEntriesPerQuery` is documented and enforced as the default result cap; explicit `limit` values are hard-capped at 1000 by the provider.
31
+
32
+ ## [0.1.0] - 2026-08-14
33
+
34
+ ### Added
35
+
36
+ - `ctx.memory` service seam (Service Definition): `budgets` / `add` / `replace` / `remove` / `query` / `seed`, with the approval gate forced inside the write methods.
37
+ - Local SQLite provider (`node:sqlite`, WAL, `0600`): entries + audit tables, unique-substring replace/remove, migrations with loud version checks.
38
+ - Approval-gated write policy (`ask` / `auto` / `off`, model-invisible) with a prepend answerer on the `approval/request` waterfall.
39
+ - `memory` tool with structured results, Save/Skip guidance, and pure renderers.
40
+ - Frozen per-session snapshot injection via a `systemPrompt` section (order `-50`), reconstructed verbatim from `request/header.system` plus audit rows.
41
+ - `memory_recall` tool: two-part recall over memory and session history with graceful degradation.
42
+ - `/memory` command (`list` / `query` / `add` / `remove` / `budgets` / `audit`) with an out-of-turn write gate sharing the same waterfall and policy.
43
+ - Read-only web panel (`dsh.client` drawer): browse entries, search, budget bars, audit tail.
44
+ - Session-event vocabulary (`memory/added|updated|removed|recalled|snapshot`) merge-declared in `types.d.ts` with rc.6-adaptive dispatch.
45
+ - Hard per-track/per-layer character budgets with structured `BUDGET_EXCEEDED` errors — never truncate, never auto-compact.
46
+ - CI matrix (three platforms × Node 22.19/24), typecheck gate, and five-language README consistency gate.
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 2026 dsh-memento contributors
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.
package/README.es.md ADDED
@@ -0,0 +1,166 @@
1
+ # dsh-memento
2
+
3
+ **Memoria entre sesiones acotada, por capas, con puerta de aprobación y auditable para DeepSeek Harness.**
4
+
5
+ [![license](https://img.shields.io/badge/license-Apache--2.0-3a7d44)](LICENSE)
6
+ [![dsh](https://img.shields.io/badge/dsh-0.1.0--rc.6-4e51e8)](https://www.npmjs.com/package/@deepseek-ai/dsh)
7
+ [![node](https://img.shields.io/badge/node-%5E22.19%20%7C%7C%20%3E%3D24-339933)](https://nodejs.org/)
8
+ [![platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-lightgrey)]()
9
+ [![no build step](https://img.shields.io/badge/build-none%20%28pure%20ESM%29-8a6d3b)]()
10
+
11
+ [English](README.md) · [中文](README.zh.md) · [Español](README.es.md) · [Português](README.pt.md) · [हिन्दी](README.hi.md)
12
+
13
+ > Otros plugins de memoria venden un **almacén**. dsh-memento vende la **costura**: un servicio tipado `ctx.memory`, una puerta de aprobación de escritura que ninguna ruta del modelo puede eludir y pistas de auditoría que puedes reconstruir desde el registro de sesión. Memoria nativa de primera clase para DeepSeek Harness: protocolo + puerta de confianza + auditoría, con cero red y cero credenciales.
14
+
15
+ ## ✨ ¿Por qué dsh-memento?
16
+
17
+ - **Es una costura de capacidad, no otro almacén.** Definición de Servicio (`ctx.memory`), Proveedor SQLite local (`node:sqlite`, WAL, `0600`) y Consumidores (herramienta `memory` + inyección de instantánea congelada). Cualquier plugin futuro —una integración semilla `dsh-claude-move`, un puente, un panel— alimenta y lee el **mismo almacén a través de la misma puerta**.
18
+ - **La puerta no se puede eludir.** Toda ruta de escritura (`add`/`replace`/`remove`/`seed`) se fuerza a través de la cascada de aprobación **dentro del servicio**, no en la capa de herramientas. `writePolicy: ask | auto | off` es configuración que el modelo no puede ver ni cambiar; una postura `never` a nivel de sesión sigue prevaleciendo sobre todo.
19
+ - **Visible para el modelo ⟺ registrado.** La instantánea inyectada llega textualmente a `request/header.system`; cada escritura es reconstruible a partir de `approval/asked` (carga útil completa) + `approval/decided` (resultado) + la propia tabla de auditoría del plugin.
20
+ - **Acotada y honesta.** Presupuestos estrictos de caracteres por pista y por capa (por defecto usuario 2000 / agente 4000). Un almacén lleno **falla con un error estructurado** (uso + límite): el modelo consolida y reintenta. Nunca se trunca, nunca se compacta automáticamente.
21
+
22
+ ## ⚡ Inicio rápido
23
+
24
+ ```sh
25
+ # requires Node ^22.19 || >=24 and DSH 0.1.0-rc.6
26
+ dsh plugin --profile web add dsh-memento # or ./dsh-memento / a tarball / a GitHub URL
27
+ dsh --profile web --dump-config # expect a "# == dsh-memento" layer, no FAILED at startup
28
+ ```
29
+
30
+ Luego, en la interfaz web: pide al modelo que recuerde algo → aprueba la escritura → inicia una **sesión nueva** y pregúntale qué recuerda. Esa es toda la demostración.
31
+
32
+ ```yaml
33
+ # optional override in the profile's cordis.patch.yml
34
+ - id: memento
35
+ config:
36
+ writePolicy: ask # ask (default) | auto | off — model-invisible
37
+ budgets:
38
+ user: { userGlobal: 4000, workspace: 2000 } # Chinese-heavy memory: raise + note why
39
+ agent: { userGlobal: 4000, workspace: 4000 }
40
+ ```
41
+
42
+ ## 🧠 Qué hace
43
+
44
+ | | Componente | Qué obtienes |
45
+ | --- | --- | --- |
46
+ | 🧩 Definición de Servicio | `ctx.memory` — `add` / `replace` / `remove` / `query` / `seed` / `budgets()` | Servicio tipado y declarado por fusión; los métodos de escritura aplican la puerta internamente |
47
+ | 💾 Proveedor | `lib/store.mjs` — un solo archivo `node:sqlite` (`$DSH_HOME/dsh-memento/memory.db`, WAL) | Cero dependencias, cero red; tablas de entradas + auditoría; coincidencia por subcadena única |
48
+ | 🛠 Consumidores | herramienta `memory` · inyección de instantánea congelada (sección del system prompt, orden `-50`) · herramienta `memory_recall` · comando `/memory` · panel web de solo lectura | Escrituras/lecturas orientadas al modelo, instantánea congelada encabezada por presupuesto, recuperación en dos partes, comando del lado del usuario, panel lateral en el navegador |
49
+
50
+ **Dos pistas × dos capas × clave por agente.** La pista `user` = hechos sobre el usuario (preferencias, estilo de comunicación, temas delicados); la pista `agent` = hechos del entorno, convenciones del proyecto, lecciones aprendidas. Cada pista tiene capas `user-global` (entre espacios de trabajo) y `workspace` (cwd por sesión): capas fusionadas al estilo Codex, no solo global al estilo Hermes. Una tercera dimensión aísla entradas por el `agentPreset` de la sesión (ámbito por agente); las entradas sin preset quedan en la capa compartida visible para todos.
51
+
52
+ **Instantáneas congeladas.** La instantánea se renderiza una vez por sesión en el primer ensamblado del prompt (lectura síncrona de SQLite + caché por sesión) y nunca cambia a mitad de sesión: estable en caché de prefijo por construcción. Los cambios internos de la sesión persisten solo a disco + auditoría.
53
+
54
+ ```
55
+ Consumer: memory tool Consumer: frozen snapshot (systemPrompt section, order -50)
56
+ add/replace/remove/query per-session freeze, budget-headed
57
+ │ writes (agent+callId) │ reads (sync, session cwd)
58
+ ▼ ▼
59
+ Service Definition: ctx.memory — budgets/add/replace/remove/query/seed
60
+ every write: budget precheck → ctx.approval.request (approval waterfall) → budget recheck → persist → audit
61
+ │
62
+ ▼
63
+ Provider: lib/store.mjs — node:sqlite (WAL, 0600), entries + audit tables, unique-substring match
64
+ ```
65
+
66
+ ## 🧰 Instalación y desinstalación
67
+
68
+ ```sh
69
+ dsh plugin --profile <name> add ./dsh-memento # local checkout (no build step)
70
+ dsh plugin --profile <name> add git+https://github.com/PerryLink/dsh-memento.git # GitHub; npm tras el primer release
71
+ dsh plugin --profile <name> remove dsh-memento # uninstall: DB + session logs are kept
72
+ ```
73
+
74
+ Tras la desinstalación, la base de datos de memoria y los registros de sesión que guardaron la actividad de memoria permanecen; las sesiones antiguas siguen siendo cargables.
75
+
76
+ ## ⚙️ Configuración
77
+
78
+ Cada campo es un `Config` de Schemastery validado; los valores inválidos fallan de forma explícita al cargar. Sobrescríbelos en cordis.yml bajo la fila `memento`.
79
+
80
+ | Campo | Valor por defecto | Significado |
81
+ | --- | --- | --- |
82
+ | `enabled` | `true` | `false` elimina por completo el servicio, las herramientas, la instantánea, el comando, el panel y el contestador (sin estados a medias) |
83
+ | `dbPath` | `''` → `$DSH_HOME/dsh-memento/memory.db` | absoluto, o relativo a `$DSH_HOME` |
84
+ | `budgets.user.userGlobal` / `budgets.user.workspace` | `2000` / `2000` | presupuesto estricto de caracteres por capa de la pista de usuario |
85
+ | `budgets.agent.userGlobal` / `budgets.agent.workspace` | `4000` / `4000` | presupuesto estricto de caracteres por capa de la pista de agente |
86
+ | `writePolicy` | `'ask'` | `'ask'` = aprobación del usuario; `'auto'` = dejar pasar (se registra el origen de la aprobación); `'off'` = rechazar. Invisible para el modelo |
87
+ | `writePolicies` | `{}` | anulaciones por pista/capa o por fuente: claves `user/workspace`, `agent/user-global`, `source:claude`, … → `ask`/`auto`/`off`; sin coincidencia cae a `writePolicy` |
88
+ | `language` | `'en'` | idioma del texto visible para el modelo y de la salida del comando: `'en'` (por defecto) o `'zh'` — descripciones de herramientas, instantánea congelada, comando `/memory` y panel web lo siguen |
89
+ | `snapshotOrder` | `-50` | orden de la sección de la instantánea: después de la identidad del harness (`-100`), antes de la persona (`0`) |
90
+ | `maxEntriesPerQuery` | `20` | tope de resultados por consulta por defecto (`limit` explícito permitido, tope duro 1000) |
91
+ | `commandListLimit` | `50` | entradas mostradas por comando `/memory list` / `query` |
92
+ | `commandAuditLimit` | `10` | filas de auditoría mostradas por comando `/memory audit` |
93
+ | `recall.historyLimitDefault` / `recall.snippetCap` / `recall.snippetChars` / `recall.windowDays` | `8` / `5` / `300` / `30` | valores por defecto de historial de `memory_recall`: sesiones escaneadas, fragmentos por sesión, caracteres por fragmento, ventana de días |
94
+ | `panelEntriesLimit` | `200` | tamaño de página de entradas del panel web (y tope) |
95
+ | `panelAuditLimit` | `20` | filas de auditoría del panel web por defecto (tope 200) |
96
+ | `auditRetentionDays` | `0` | retención de auditoría: 0 = para siempre, >0 = poda al abrir la tienda |
97
+ | `proposals.enabled` / `proposals.maxChars` / `proposals.maxPending` | `true` / `2000` / `8` | auto-captura: propuesta de memoria pendiente tras cada compactación exitosa (truncada, una por sesión); desactivar o ajustar topes |
98
+
99
+ ## 🛠 Herramientas y superficies
100
+
101
+ - **`memory`** — add/replace/remove/consolidate/query con guía Guardar/Omitir incrustada en la descripción (guarda preferencias del usuario, correcciones, hechos del entorno, convenciones, lecciones; omite trivialidades, hechos re-derivables, volcados, rutas de un solo uso). Las escrituras pasan por la puerta de aprobación; las lecturas son libres; replace/remove apuntan a una **subcadena única** (las coincidencias ambiguas fallan con la lista de candidatos); consolidate fusiona 1..20 entradas en una con una sola aprobación y una escritura atómica.
102
+ - **`memory_recall`** — recuperación en dos partes: coincidencias acotadas de memoria **más** coincidencias recientes del historial de sesión vía `ctx.sessionQuery` (se degrada con elegancia a solo memoria donde el servicio está ausente).
103
+ - **`/memory`** — comando activado por el usuario (no es un turno del modelo): `list` · `query <word>` · `add [--track=user|agent] [--scope=user-global|workspace] <text>` · `remove [flags] <substring>` · `consolidate [flags] <substring...> => <text>` · `proposals [approve|dismiss <id>]` · `budgets` · `audit` · `export`. Las escrituras del comando pasan por la misma cascada + política; la auditoría se registra en la tabla de auditoría del plugin + `command/done`. `export` es de solo lectura y vuelca todas las entradas + presupuestos como un documento JSON (copia de seguridad / migración).
104
+ - **Propuestas auto-capturadas** — tras una compactación de sesión exitosa, el resumen se registra como propuesta de memoria pendiente (`agent/workspace`); aprobarla la escribe a través de la puerta de aprobación, descartarla la elimina. Las propuestas pendientes aparecen en la instantánea congelada y en el panel.
105
+ - **Panel web** — panel lateral `dsh.client` sin compilación: navega por entradas por pista/capa, busca, barras de presupuesto, cola de auditoría. De solo lectura por diseño: las escrituras y la aprobación ocurren a través de la herramienta `memory` y la interfaz de aprobación integrada.
106
+
107
+ ## 🎓 Lo que aprendimos de las memorias de terminal
108
+
109
+ dsh-memento no es un port de Claude Code, Codex ni Hermes — pero su diseño absorbió deliberadamente las partes que cada uno hizo bien y rechazó las que hacen daño:
110
+
111
+ | Memoria de terminal | Qué hizo bien | Qué adoptó dsh-memento |
112
+ | --- | --- | --- |
113
+ | **Claude Code** — `CLAUDE.md` | **archivos de memoria en texto plano** jerárquicos (nivel usuario → nivel proyecto), legibles y editables por humanos, y combinados automáticamente en cada sesión — memoria que puedes leer y corregir tú mismo | entradas en texto plano; capas `user-global` / `workspace` combinadas por sesión; un almacén que puedes navegar, `export` y auditar — la transparencia como característica |
114
+ | **Codex** — `AGENTS.md` | **instrucciones con alcance por directorio** autodescubiertas e inyectadas sin fricción del modelo — la localidad importa más que el volumen; no hace falta llamada de herramienta para "cargar" memoria | capa `workspace` vinculada al cwd de la sesión (insensible a mayúsculas en Windows); la instantánea congelada se inyecta automáticamente al inicio de la sesión |
115
+ | **Hermes** — `memory.md` | **guardados de memoria proactivos** (guardar/actualizar/borrar) y, en el [issue #48181](https://github.com/NousResearch/hermes-agent/issues/48181), la lección de seguridad de que una puerta impuesta solo en la capa de herramientas es eludible mediante inyección tardía de herramientas — hay que imponerla donde convergen todas las rutas de escritura | la herramienta `memory` con guía explícita Guardar/Omitir + propuestas de auto-captura con puerta de aprobación; la puerta de aprobación vive **dentro** de los métodos de escritura de `ctx.memory`, no en la capa de herramientas |
116
+
117
+ Fuentes: [memoria de Claude Code](https://code.claude.com/docs/en/memory) · [AGENTS.md de Codex](https://developers.openai.com/codex/cli/agents-md) · [memoria de Hermes](https://github.com/NousResearch/hermes-agent/blob/main/website/docs/user-guide/features/memory.md) · [Hermes #48181](https://github.com/NousResearch/hermes-agent/issues/48181).
118
+
119
+ Y las partes que rechazamos deliberadamente: la auto-resumición oculta en estado privado del modelo (aquí los resúmenes de compactación se convierten en **propuestas pendientes** que esperan un aprobar/descartar humano), las ambiciones de almacén/vectorial, y cualquier escritura sin aprobación o rastro de auditoría visible para el humano. También adoptamos la advertencia documentada de Hermes: dos procesos que comparten un directorio home escriben el mismo archivo de memoria — véase Límites de seguridad.
120
+
121
+ ## 🆚 En qué se diferencia
122
+
123
+ | Plugin | Qué es | La diferencia de dsh-memento |
124
+ | --- | --- | --- |
125
+ | dsh-memory-evolve | almacén de memoria / bucles de evolución | una costura de servicio tipado, puerta de aprobación y auditoría del registro de sesión; sin ambición de almacén |
126
+ | dsh-mnemon | asistente de almacén de memoria | protocolo + puerta + auditoría, no otro almacén |
127
+ | dsh-kb-sieve | cribado de base de conocimiento | sin ingeniería de recuperación: búsqueda por subcadena sobre un corpus pequeño, recuperación entre sesiones vía `session_search`/`sessionQuery` |
128
+ | dsh-tdai-memory | herramientas de memoria dirigidas por tareas | los presupuestos son por pista×capa y se aplican en el servicio, no a mejor esfuerzo |
129
+ | claude-bridge | puente con Claude Code | nativo de DSH; una futura ruta `seed(source:'claude')` permite que un puente alimente el mismo almacén |
130
+ | dsh-external/Recall | memoria externa de agente | local primero, cero red, se apoya en la propia costura de aprobación de DSH |
131
+ | Ejemplos oficiales de memoria MCP | la postura declarada de DSH de "memoria = MCP externo" | el complemento **nativo de primera parte**: mismo objetivo, sin servidor externo; ambos coexisten |
132
+
133
+ El nombre es **`dsh-memento`** (libre en npm y GitHub). No `dsh-recall` (confundible con dsh-external/Recall), ni el nombre heredado eliminado `dsh-memory`.
134
+
135
+ ## 🔒 Límites de seguridad
136
+
137
+ - **Solo servicios públicos** (`tools`, `systemPrompt`, la costura de aprobación). Sin cambios en el motor / agent-loop / apiproxy / UI oficial.
138
+ - **Cero red, cero credenciales.** Base de datos local; modo de archivo POSIX `0600`.
139
+ - **Falla de forma explícita.** Una base de datos corrupta o un esquema más nuevo falla al cargar; los presupuestos llenos y las coincidencias ambiguas de subcadena fallan con errores estructurados. Nada se traga ni se trunca en silencio.
140
+ - **Un proceso, un almacén.** Varias sesiones en un proceso comparten el almacén SQLite (escrituras serializadas, auditoría por sesión). Dos **procesos** que comparten un `$DSH_HOME` escriben el mismo archivo: gana el último escritor bajo el bloqueo de SQLite — no ejecutes dos instancias del harness sobre un mismo `$DSH_HOME` si necesitas coherencia entre procesos (la misma advertencia que documenta el proyecto Hermes).
141
+
142
+ ## ⚠️ Limitaciones conocidas
143
+
144
+ - **El vocabulario de eventos de sesión está declarado, pero aún no se emite (rc.6).** `memory/added|updated|removed|recalled|snapshot` están declarados por fusión en `types.d.ts`, pero rc.6 no tiene superficie de registro para tipos de eventos fuera del repositorio (los appends no registrados harían que las sesiones persistidas no se pudieran cargar). La completitud de la auditoría proviene del par de aprobación + la tabla de auditoría; la emisión se activa automáticamente en cuanto una compilación del harness registre los tipos. Véase [ARCHITECTURE.md](ARCHITECTURE.md), decisión 4.
145
+ - **La política `ask` necesita un contestador.** Sin un contestador de UI/ACP compuesto, las escrituras fallan en modo cerrado (`unavailable`): por diseño, la postura de fallo cerrado de la costura de aprobación.
146
+ - **Sin índice FTS5.** La búsqueda por subcadena usa `instr` insensible a mayúsculas (correcto para CJK); el ranking de recuperación usa contadores de aciertos por entrada. El tokenizador trigram de FTS5 no puede indexar caracteres CJK de un solo carácter, así que no se usa — véase [ARCHITECTURE.md](ARCHITECTURE.md), decisión 10.
147
+
148
+ ## 🧪 Desarrollo
149
+
150
+ ```sh
151
+ npm install
152
+ npm test # node --test: 103 tests — budget, unique-substring, gate policy, store, snapshot, mock-ctx integration (S2/S3 invariants), V2 command/recall/panel
153
+ npm run typecheck # puerta tsc --checkJs sobre index.mjs / lib / scripts
154
+ npm run check:coverage # puerta de cobertura de líneas: lib ≥90 %, index.mjs ≥85 %, todos ≥90 %
155
+ npm run check:readmes # puerta de coherencia de los cinco README
156
+ ```
157
+
158
+ `lib/` no tiene dependencias de DSH (solo builtins de node:); las importaciones de DSH solo existen en `index.mjs`. Disciplina completa en [AGENTS.md](AGENTS.md); decisiones de diseño en [ARCHITECTURE.md](ARCHITECTURE.md).
159
+
160
+ ## 🏷 Temas
161
+
162
+ Temas sugeridos para GitHub: `dsh` · `dsh-plugin` · `deepseek-harness` · `memory` · `agent-memory` · `approval` · `audit` · `sqlite` · `cordis` · `llm`
163
+
164
+ ## 📄 Licencia
165
+
166
+ Licencia Apache 2.0 — véase [LICENSE](LICENSE). No se redistribuye código de terceros; véase [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).