stratagate-dsh 0.3.17 → 0.3.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/docs/DSH.md CHANGED
@@ -117,8 +117,8 @@ two differences:
117
117
  that thread — so no fabricated user message is ever created. Only when a session has no
118
118
  ingested messages at all does a synthetic `agent-memory:` provenance Block get created.
119
119
  - **Pre-write resolution.** Before writing, StrataGate searches existing memory (both pools)
120
- for duplicates and conflicts. Exact and near duplicates reinforce the existing card instead
121
- of writing a new one. Ambiguous lexical overlap triggers one synchronous model adjudication
120
+ for duplicates and conflicts. Only normalized text-identical active facts reinforce the existing card
121
+ without a model call. Other lexical overlap, including containment or reordered words, triggers synchronous model adjudication
122
122
  reusing the external-memory decision contract: `ADD`, `MERGE`, `SUPERSEDE` (high confidence
123
123
  only — low-confidence merge/supersede is downgraded to a non-destructive conflict mark),
124
124
  `CONFLICT` (symmetric back-links), or `IGNORE`. The tool result reports `action`, `gate`,
@@ -148,17 +148,35 @@ memory_record_use memory_remember
148
148
  memory_profile_update
149
149
  ```
150
150
 
151
- `memory_get_blocks` accepts `scope=session` (the default, preserving the historical
152
- session-local behavior) or `scope=namespace` (all threads in the active project,
153
- session, or global namespace). Every response includes the selected `scope`,
151
+ `memory_get_blocks` returns a paginated directory: each row contains only `id`,
152
+ `title`, `turnRange`, and the current `level`. Read source content with `memory_expand_block`.
153
+ It accepts `offset` and `limit` (default 12, bounded to 1–20), and returns `total`,
154
+ `offset`, `limit`, and `nextOffset` (`null` at the end). `blockCount` counts all matching
155
+ ready blocks before pagination. An offset past the end returns an empty page without
156
+ reporting that the scope has no blocks.
157
+
158
+ `scope=session` (the default) strictly matches the active `threadId`.
159
+ `scope=namespace` includes all threads and legacy unthreaded blocks in the active project,
160
+ session, or global namespace. Every response includes the selected `scope`,
154
161
  `namespace`, `threadId`, block counts, and `emptyReason`. A `null` reason means
155
- blocks were returned; `no_blocks_in_namespace` means the namespace has no sealed
162
+ matching ready blocks exist; `no_blocks_in_namespace` means the namespace has no sealed
156
163
  blocks, `blocks_exist_in_other_threads` means only another thread has sealed
157
164
  blocks, and `open_tail_pending` means matching turns exist but have not sealed yet.
158
- `memory_search_raw` defaults to namespace scope and accepts the same `scope` filter,
165
+ `memory_search_raw` defaults to namespace scope and uses the same strict thread filter for `scope=session`,
159
166
  so a raw hit's `blockId` can be followed by `memory_get_blocks(scope=namespace)`
160
167
  or `memory_expand_block` without an unexplained visibility mismatch.
161
168
 
169
+ The deprecated `memory_search_elements` tool is no longer registered; use
170
+ `memory_search_graph`. Legacy agent-memory provenance transcripts display `Recorded:`
171
+ while preserving the stored raw messages.
172
+
173
+ Automatic Event extraction now checks existing cards before admission. After upgrade,
174
+ a resumable background task scans each existing namespace once when a model is available.
175
+ Exact duplicates retain one card; similar candidates receive a dedicated conservative model
176
+ review. Distinct timing, scope, versions, progress, and uncertain facts remain separate.
177
+ Confirmed redundant cards are archived with their original provenance, never deleted.
178
+ System Status shows a summary without requiring pair-by-pair user action.
179
+
162
180
  Search responses use compact cards by default. Event cards keep `id`, `title`, `summary`, source time,
163
181
  status/scope, `sourceBlockId`, `batchId`, and `evidenceRefs`; graph cards keep `id`, `name`, type,
164
182
  aliases, current state, status, and explainable `matchedFields`/`matchReason`; raw cards keep the
package/docs/DSH.zh-CN.md CHANGED
@@ -108,7 +108,7 @@ DSH_HOME/stratagate/memory.db
108
108
  `memory_remember` 让 agent 主动记录值得记忆的事实:适用于特定情境的用户偏好、事实纠正、决定和持久的项目事实。记录会进入与对话派生记忆相同的长期 Event 管线,但有两点不同:
109
109
 
110
110
  - **隔离存储。** agent 记录的 Event 存放在专属的 `agent_events` 隔离表中,数据模型与普通 Event 完全一致,但与被动对话表物理分离。其溯源直接引用**记录会话中的真实对话消息** —— 优先取 open tail 中的 user/assistant 消息,其次取该会话最新封存 Block 的消息 —— 绝不生成虚构的 user 消息。仅当会话中没有任何已摄取消息时,才会创建合成的 `agent-memory:` 溯源 Block。
111
- - **写入前消解。** 写入前,StrataGate 会先在两个池中检索重复与冲突:精确或高度近似的重复会强化既有卡片而不新写;词面重叠模糊的记录会触发一次同步模型仲裁,复用外部记忆导入的决策契约 —— `ADD`、`MERGE`、`SUPERSEDE`(仅高置信度;低置信度的合并/取代会降级为非破坏性的冲突标记)、`CONFLICT`(双向回链)或 `IGNORE`。工具结果会回报 `action`、`gate` 与 `reason`。与既有记忆没有实质重叠的事实直接写入,不调用模型。
111
+ - **写入前消解。** 写入前,StrataGate 会先在两个池中检索重复与冲突:只有规范化全文一致的当前有效事实直接强化既有卡片;其他词面重合(包括单向包含和词序变化)走同步模型仲裁,复用外部记忆导入的决策契约 —— `ADD`、`MERGE`、`SUPERSEDE`(仅高置信度;低置信度的合并/取代会降级为非破坏性的冲突标记)、`CONFLICT`(双向回链)或 `IGNORE`。没有仲裁器或调用失败时保留新增内容并标记冲突,不因相似度丢弃。仲裁后的并发重复检查同样只直接强化全文一致的当前有效事实。工具结果会回报 `action`、`gate` 与 `reason`。与既有记忆没有实质重叠的事实直接写入,不调用模型。
112
112
 
113
113
  其余行为与普通 Event 一致:agent 记录会投影进 Knowledge Graph,跨会话持久保存,走同样的 turn 衰减与引用强化生命周期(`memory_assess` → `memory_record_use`),也可以通过该生命周期遗忘。检索为两个池各开一条独立的 top-k 通道 —— 被动池与 agent 池分别独立排序,再按加权 RRF 融合,任何一方都无法把另一方挤出结果窗口。agent 通道的占比由 `agentMemoryRetrievalWeight` 配置(默认 `1` = 平权;`0` = 记录保留但不再浮现;更大值提升 agent 记录的排序权重),卡片带 `source: 'agent-recorded'` 标记。记忆面板通过 `/api/stratagate/agent-memories`(查询参数 `session` 与 `includeArchived=true`)展示这些记录。可用 `agentMemoryEnabled: false` 整体关闭该功能,同时注销对应工具。
114
114
 
@@ -128,15 +128,25 @@ memory_record_use memory_remember
128
128
  memory_profile_update
129
129
  ```
130
130
 
131
- `memory_get_blocks` 支持 `scope=session`(默认值,保留历史上的当前会话隔离语义)和
132
- `scope=namespace`(当前 project、session 或 global 命名空间中的全部 thread)。每次响应都会
133
- 包含实际 `scope`、`namespace`、`threadId`、Block 计数,以及机器可读的 `emptyReason`:返回
134
- Block 时为 `null`;`no_blocks_in_namespace` 表示命名空间没有已封存 Block;
131
+ `memory_get_blocks` 返回分页目录,每项仅含 `id`、`title`、`turnRange` 和当前 `level`;
132
+ 正文通过 `memory_expand_block` 获取。支持 `offset/limit`,默认每页 12 项,限制在 1–20 项,
133
+ 返回 `total/offset/limit/nextOffset`,末页的 `nextOffset` 为 `null`。
134
+ `blockCount` 表示分页前匹配的就绪块总数;offset 超出末页时返回空页,不误报范围内没有块。
135
+
136
+ `scope=session`(默认值)严格匹配当前 `threadId`;`scope=namespace` 包含当前 project、session
137
+ 或 global 命名空间中的全部 thread 及没有 threadId 的旧块。每次响应都会
138
+ 包含实际 `scope`、`namespace`、`threadId`、Block 计数,以及机器可读的 `emptyReason`:
139
+ 匹配的就绪 Block 存在时为 `null`;`no_blocks_in_namespace` 表示命名空间没有已封存 Block;
135
140
  `blocks_exist_in_other_threads` 表示只有其他 thread 有已封存 Block;`open_tail_pending`
136
141
  表示匹配的轮次存在但尚未封存。`memory_search_raw` 默认使用 namespace 范围,也接受同样的
137
- `scope` 过滤,因此可以用 `memory_get_blocks(scope=namespace)` 或 `memory_expand_block`
142
+ `scope` 过滤,`scope=session` 同样严格按 threadId 匹配。因此可以用 `memory_get_blocks(scope=namespace)` 或 `memory_expand_block`
138
143
  继续浏览 raw 命中的 `blockId`,不会再出现范围不明的空结果。
139
144
 
145
+ deprecated 的 `memory_search_elements` 已停止注册,请使用 `memory_search_graph`。
146
+ 旧 agent-memory 来源块的转录展示改为 `Recorded:`,原始消息记录保持不变。
147
+
148
+ 重复记忆由程序发现候选、AI 优先判断并整理完整合并稿。明确应保留两张的结果直接完成;只有不确定、证据不足或未完成的候选进入人工队列。用户可编辑完整稿、仅保留 A/B 或同时保留,并查看来源、处理历史及撤销。双方证据和历史记录保留,后续新增记忆也会发现新候选。任务和调用预算可跨重启恢复。详见 [Event 提取与重复卡整理](event-extractor.md)。
149
+
140
150
  搜索默认返回紧凑卡片:Event 保留 `id`、标题、摘要、时间、状态/范围和 `sourceBlockId`;Graph
141
151
  保留 `id`、名称、类型、别名、当前状态及可解释的 `matchedFields`/`matchReason`;Raw 保留消息
142
152
  ID、`blockId`、角色、轮次和有界摘录。`narrative`、`quotes`、来源消息列表、完整 facts/edges
@@ -11,11 +11,25 @@
11
11
  | `memory_list_topics` / `memory_expand_topic` | 运行空间内目录导航与分页总览,独立 scoped 实现 |
12
12
  | `dreaming_search` | 冻结有效 Event、其引用的原始消息及有效图谱检索 |
13
13
  | `dreaming_read` | 完整 Event、来源 Block、图谱节点的只读分页 |
14
- | `dreaming_directory` | 当前章节改名、小节改名/合并、当前批 Event 移动、带来源的总览/经验替换或追加 |
14
+ | `dreaming_directory` | 当前章节改名、小节改名/合并、当前批 Event 移动、带来源的总览/经验替换、追加或跨小节原子整理 |
15
15
  | `dreaming_finish` | 确认当前批已检查;推进下一批,最终结束 |
16
16
 
17
17
  Agent 只注册上述工具,拒绝继承的 shell、网络、文件和记忆写入工具。普通聊天工具的证据评估批次与使用反馈不复用。`dreaming_search` 使用底层检索;`dreaming_read` 使用 `renderBlock`,不调用会提升层级/改变衰减锚点的 `expandBlock`。Dreaming 系统提示词注入持久用户画像,仅作为相关偏好和长期要求的参考,不能替代目录来源证据;会话不重新摄入记忆,也不继承普通聊天的自动检索上下文。
18
18
 
19
+ ### 概要整理操作
20
+
21
+ `set-overview {title,entries}` 只替换指定小节的全部概要,不替换整章。`append-overview` 仅用于真正新增的事实。`move-events` 只改变当前批 Event 的小节归属,不自动移动概要正文。
22
+
23
+ `rewrite-overview {titles,entries}` 原子替换所列多个已有小节的全部概要;每个 entry 必须显式指定这些小节之一作为 `title`。它支持把事实从错放的小节移到正确小节,也支持合并重复段落、更正旧表述。必须包含所列小节所有需要保留的独有内容,包括目标小节原有概要;未列出的小节保持原样。目标小节可由同一次提交前面的 `move-events` 创建。只有重排概要时也可单独调用,不要求改变 Event 归属。
24
+
25
+ 例如把“旧分类”中的一段移到“新分类”,提交 `titles:["旧分类","新分类"]`,在 entries 中列出两节应保留的全部内容,把该段的 title 改为“新分类”,保留其正文和来源。移动后无需在“旧分类”留下重复正文或仅为通过校验而添加交叉索引。
26
+
27
+ 替换前必须完整读取原有引用来源。Core 按所选小节新概要的来源并集检查,禁止丢失任何原有来源引用;不再要求同一个引用必须留在原小节。任一操作失败则整次提交回滚,其他步骤的改名、移动和重写也不保存。章节版本、认领和冻结来源校验保持有效。
28
+
29
+ 新证据修正旧说法时,优先改写或合并旧段落,同时引用新旧来源并保留时间顺序、适用范围、仍有效的细节和不确定性;不让旧结论继续充当当前事实,再在末尾不断追加补丁。仅因材料较新不能自行裁定矛盾。单小节用 `set-overview`,跨小节用 `rewrite-overview`。这些规则在原生 Agent 与兼容的 detached 路径共用。
30
+
31
+ 概要错误会附上具体 Event ID 和原因:`overview-source-not-retrieved` 表示需要完整读取,按 `nextOffset` 连续翻页;`overview-source-not-in-chapter` 表示来源属于别章,反复读取不会获得本章引用权限;`overview-source-unavailable` 表示未知、已变化或不在本轮冻结范围;`overview-source-reference-missing` 表示替换遗漏旧引用。未知小节、缺少条目 title、空来源和无效正文也分别报错。
32
+
19
33
  ## 持久化与并发
20
34
 
21
35
  任务保存在既有 Topic 状态中,无新增数据库/schema 迁移。任务冻结来源版本,按章节拆成有限上下文页面。提交在 Core 现有事务内检查 run ID、owner、epoch、租约、章节版本、来源有效性和已读证据。大 Event 只有连续分页读完才进入证据集合;检查覆盖断点不依赖最近工具日志的长度。
@@ -24,19 +38,29 @@ Event 分页按递归稳定字段顺序序列化,页结果和持久断点绑
24
38
 
25
39
  暂停等当前步骤到达安全边界,继续时换 epoch;终止立即取消当前请求并提升 epoch,使迟到结果无法写入。已完成写入保留。运行定期续租;崩溃后过期认领可人工继续。并发投影改变来源或章节会拒绝写入,由用户终止后重新规划。
26
40
 
41
+ 最后一批完成后,Agent 可继续生成最终总结。此时同一 run、owner、epoch 的在途心跳只返回已完成状态,不更新时戳、不重建租约,也不取消总结;Runtime 收到完成状态后停止心跳。其他 owner、旧 epoch 和旧 run 仍被拒绝,已完成轮次仍不能写目录。
42
+
43
+ 用户手动触发的 Dreaming 优先于同一记忆空间的自动目录投影。开始时立即持久化目录占用,阻止任何连接认领新的目录投影;已经认领且租约有效的投影先收尾,界面显示“等待当前后台目录整理完成”,等待期间不请求 Dreaming 模型。收尾完成、失败或租约过期后,再按最新目录冻结批次与来源,过期投影的迟到结果不能覆盖 Dreaming。其他记忆空间的目录任务照常调度。
44
+
45
+ 首次目录尚未生成时,只要有有效投影正在执行,同样可以启动等待;投影收尾后重新规划。没有章节且没有有效在途投影时拒绝启动。在途投影失败或过期后若仍无章节,本轮以“暂无可整理的目录章节”完成并释放占用。等待轮询只读取最新状态;现有每 20 秒心跳续租,不按每次轮询写库。暂停、失效认领和投影结束时仍经过原事务处理状态变化。
46
+
47
+ 暂停和宿主重启后保留目录占用,避免继续时目录已经被后台改写;整轮完成或用户终止后释放占用并唤醒后台任务。暂停轮次的状态会提示用户:后台目录整理等待本轮继续或终止。若其他写入仍改变冻结章节或来源,则立即暂停并标记需要重新开始;旧版本遗留的冲突轮次在继续前同样校验,不再让模型反复重读。界面隐藏该轮次的“继续”,提示终止后重新开始,已提交的修改保留。
48
+
27
49
  小节身份不依赖当前标题;合并保留旧身份的别名,后续普通投影保留身份、别名和有效经验。空改动和回到近期旧状态的编辑被拒绝,不能用反复改名制造进展。
28
50
 
29
51
  ## 增长、结束与消耗
30
52
 
31
- 批次最多 24 个 Event,并按预计上下文容量进一步拆小;这是页面大小,不是整轮调用限额。目录页的进度按这些批次计算(例如 `1/14` 批约为 7%),不是简单按章节数计算;一个较大的章节可能拆成多个批次。每个工作步骤和人工继续时使用 DSH 的 surface replacement 重建模型工作上下文,只携带一个当前批次、必要的现有总览、用户方向、最近工具结果和错误反馈;原始会话日志完整保留。目录 sections/overview 页绑定内容摘要并持久记录覆盖,任务明确给出已读范围与下一未读位置,压缩后不重新开始浏览。失败写入提案单独保留,不受最近 12 条工具日志的窗口影响;过大提案只提供错误与分批修正提示,完整内容仍在会话记录。用户方向本身过长时暂停并提示精简,避免静默丢失指令。
53
+ 批次最多 24 个 Event,并按预计上下文容量进一步拆小;这是页面大小,不是整轮调用限额。目录页的进度按这些批次计算(例如 `1/14` 批约为 7%),不是简单按章节数计算;一个较大的章节可能拆成多个批次。Dreaming 使用原生 DSH Agent 会话,启动、恢复和切批时追加当前任务;同批后续步骤沿用对话和工具结果,不逐步替换历史,也不重复注入完整任务。相关旧总览完整提供,不设置插件自己的 4000/6000 Token 截断;历史保留和摘要压缩由宿主根据实际模型窗口管理。
54
+
55
+ 流程为检查本批 Event、对照旧总览、只提交有价值的目录调整、完成本批并继续。已有覆盖时允许不修改就完成,不把同一事实的改写当成新内容追加。目录 sections/overview 页绑定内容摘要并持久记录覆盖;任务提供恢复时的已读范围,随后分页按工具返回的 nextOffset 前进。对话中的核对结论、用户方向、错误和成功结果交由宿主保留或摘要。失败写入提案另有持久化记录,不受最近 12 条诊断日志的窗口影响;恢复时提供完整提案,读取错误不再包装为失败写入。
32
56
 
33
57
  `dreaming_finish` 表示批次已全部检查,允许无需修改;所有冻结批次完成后进入 completed。重复无效工具调用、连续无工具进展的模型步骤或失败时暂停。没有累计 token 限额,界面和控制接口不提供上限设置。内部计量保留实际模型输入结构的 token 估算和输出估算/供应商输出计数,仅用于诊断;目录页和模型任务上下文不展示消耗数字,不声称价格或账单精度。旧任务继续时在原子事务中清除历史 tokenLimit,保留任务、批次进度、证据断点与会话身份。
34
58
 
35
- 无进展计数在新增完整 Event 证据、推进连续有效来源页、读取经校验的目录新项或成功修改目录时重置。搜索关键词变化、空结果与重读已覆盖页均不重置;每批允许有限探索。空响应(含 `(empty)`)或普通文字回复结束但任务未完成时,在同一 Agent Loop 内提示继续;连续三次没有工具行动才暂停,不按整轮累计。复用持久化的 idleModelSteps,工具检查点或完成批次后重置;不使用后台定时重试。用户暂停、终止和真实请求错误优先于自动继续。切批消息明确上一批已完成、当前批待处理,完成全部批次后只生成总结,不再催促工具调用。
59
+ 无进展计数在新增完整 Event 证据、推进连续有效来源页、读取经校验的目录新项或成功修改目录时重置。搜索关键词变化、空结果与重读已覆盖页均不重置;连续 12 次无进展工具调用后暂停,允许其间进行有限探索;一旦有上述有效进展,连续计数从零开始。空响应(含 `(empty)`)或普通文字回复结束但任务未完成时,在同一 Agent Loop 内提示继续;连续三次没有工具行动才暂停,不按整轮累计。复用持久化的 idleModelSteps,工具检查点或完成批次后重置;不使用后台定时重试。用户暂停、终止和真实请求错误优先于自动继续。切批消息明确上一批已完成、当前批待处理,完成全部批次后只生成总结,不再催促工具调用。
36
60
 
37
- 原生 Agent 在请求最终组装后的 `llm/stream` 边界计量实际消息(含临时任务和系统提示)及 JSON 工具定义,自动继续也走相同入口。单次响应最多 32768 Token;若一轮回复尚未产生任何原生工具调用,普通文本超过 8192 Token 会中断并暂停,避免模型重复描述调用计划而持续消耗。reasoning 不计入这条普通文本边界,但 reasoning、普通说明和真正的工具参数均计入单次响应消耗及 32768 Token 总上限。模型失败时拦截宿主自动重试并暂停。没有整轮时长或累计调用次数硬限额;完成后的总结请求也计量,但不重新激活任务或租约。
61
+ 原生 Agent 在请求最终组装后的 `llm/stream` 边界计量实际消息(含任务和系统提示)及 JSON 工具定义,自动继续也走相同入口,仅用于诊断。单次输出长度沿用宿主/模型配置,插件不强制 32768 Token 上限,也不按 8192 Token 的普通文本长度提前中断。模型请求错误先交给宿主原生策略处理,包括上下文溢出后的摘要压缩与恢复;宿主拒绝恢复或耗尽其恢复预算后才持久化暂停。宿主摘要请求不被计作 Dreaming 工作步骤,不干扰本批的无工具进展计数;摘要自身用量由宿主记录。没有整轮时长或累计调用次数硬限额;完成后的总结请求也计量,但不重新激活任务或租约。
38
62
 
39
- 目录写入使用带必需 kind 的明确参数结构,操作数组必须扁平;错误不视为成功提交,Agent 可以纠正并重新提交。暂停时不为说明原因额外调用模型,而是在原生会话写入插件通知,报告真实原因、检查批次与已保存修改。start 每次创建新 run/session;resume 保留本轮会话身份,重建工作上下文,避免累计发送旧轮次历史。
63
+ 目录写入使用带必需 kind 的明确参数结构,操作数组必须扁平;错误不视为成功提交,Agent 可以纠正并重新提交。暂停时不为说明原因额外调用模型,而是在原生会话写入插件通知,报告真实原因、检查批次与已保存修改。start 每次创建新 run/session;resume 保留本轮会话与宿主保留的历史,并追加最新任务;之前版本已经裁掉的中间结论不能凭空恢复。
40
64
 
41
65
  每批每次继续最多允许 64 次成功目录提交,达到后在当前步骤检查点暂停并保留已写入内容。用户明确继续或进入下一批时重新计数;页数与整轮调用量不受此上限限制。无工具循环在请求发送前被拦截时,不计入模型调用次数或 Token 消耗。
42
66
 
@@ -1,6 +1,6 @@
1
1
  # Event Extractor v2
2
2
 
3
- 新版规则只用于升级后正常提取的新 Event。不会重放旧 L5、改写旧标题、拆分或合并旧 Event,也不会回填分类提示或版本号。已有待处理任务仍按正常重试规则处理。
3
+ 新版提取规则只用于升级后正常提取的新 Event。提取器不会重放旧 L5、改写旧标题或回填分类提示和版本号。已有待处理任务仍按正常重试规则处理。历史重复卡由下述独立的一次性整理任务处理。
4
4
 
5
5
  ## 提取判断
6
6
 
@@ -16,6 +16,22 @@ Extractor 结构化输出必须明确返回合法 scope。工具调用及文本
16
16
 
17
17
  纯重复且没有新增长期信息可不提取;明确强化或延续用 sameEventId,替代/纠正用 supersedesEventIds,无法确认替代的矛盾用 conflictsWithEventIds。历史卡保留。相同项目、关键词近似不能单独建立关系。现有 timeline 仍最多提供 8 条相关及 4 条最近 Event,增加每条最多 400 字符的摘要、范围和历史状态以辅助识别;字段对自定义 Extractor 保持可选。timeline / neighbor 只供理解和关系匹配,不能为新事实提供来源。
18
18
 
19
+ ## 入库去重与一次性历史整理
20
+
21
+ 程序负责从当前命名空间的 passive/agent 两个 Event 池发现候选。同批同来源的重复输出仍确定性消重,写入回执继续处理重放;跨 Block 的相似记录即使文字相同,也交给 AI 判断,不能仅凭文本相似直接丢弃新来源。
22
+
23
+ 专用裁决器读取双方完整卡与来源。KEEP_LEFT/KEEP_RIGHT 选择沿用的主卡,并返回完整标题和正文草稿;只有证据支持同一事实、草稿有效且 confidence 至少为 0.9 时才自动采用。缺失信息、表达精度和分类差异不单独构成冲突;不同发生、后续进展、纠正或真实矛盾应明确 KEEP_BOTH,不确定才 REVIEW。明确 KEEP_BOTH 视为 AI 已完成,不要求用户逐对确认。缺草稿、低把握、失败或证据超限时保留原卡,进入人工队列,不回退到程序拼接。阈值是执行规则,并非经真实模型评测校准的准确率。
24
+
25
+ 整理版本为 3,升级时重审未由人工确认的旧候选,并保留已有撤销记录。扫描包括 active/superseded 卡;自动裁决只处理双方 active 的候选,历史版本直接进入人工队列,不消耗自动裁决调用。初次扫描完成后,新增 Event 会触发新候选发现,已处理候选不反复调用模型。程序检查命名空间/会话范围、指纹、置顶和关系完整性;日期、数值等语义差异交给 AI 或人工结合上下文判断。合并采用完整草稿,双方引语、参与者、标签、来源及安全关联按规则保留;沿用主卡的结构化日期和状态,界面提示有差异的字段。自动合并及普通有效卡合并遇到进展、替代、冲突关系或时间环时拒绝折叠;显式人工历史合并保留原始时间链,不重定向历史关联。权重继承双方真实采用字段最大值,不制造采用回执。
26
+
27
+ 后台保留跨进程租约、调用前预留尝试、每对最多两次、60 秒退避和 30 秒外层超时。人工预览复用当前来源指纹对应的 AI 稿;无缓存时可请求草稿,失败后仍可直接填写完整稿,最终确认本身不调用模型。界面分为“需要你判断 / 等待 AI / 已处理”,支持完整标题/正文编辑、仅保留 A/B、保留两张、来源查看和手动接管。切换候选/队列会在当前页面保留最多 50 份编辑草稿;刷新不保存未提交的人工编辑。自动与人工合并均保存可恢复的逆操作;撤销会恢复原内容、历史状态及关系,有后续编辑、采用或相关投影变化时拒绝覆盖新数据。
28
+
29
+ 界面逐组展示 A/B 卡片及发生日期、引语、参与者和来源。用户先选主卡,再选择“合并双方内容”或“只保留所选卡”,预览保留/归档对象、日期和证据去向后确认。日期或事实存在差异时可由用户核对完整稿后合并,不确定时保留两张。superseded 历史卡可在人工确认后补入当前有效卡:默认推荐当前卡作为主卡,完整标题和正文必须经 AI 起草或人工编辑后提交;旧卡归档且保留原文、来源及时间链,双方证据汇入当前卡,当前卡结构化日期和状态不变,外部关联不改写。旧卡即使指向第三个版本,也可在人工确认的完整稿中作为来源,原后继指针保留;将被归档的卡已置顶或已作为其他合并的原始证据时,拒绝并显示具体原因。用户仍可显式二选一(恢复历史卡前单独提示影响):所选卡设为 active,另一张归档,内容、采用权重、原始来源和历史引用均不混合、不改写;不额外排入模型投影任务。置顶副卡、已合并来源和过期指纹仍受保护。归档卡已有的 supersededBy 保留,只有原来没有后继时才指向所选卡;显式恢复被选历史卡时将其设为 active 并清除自身后继,这是用户选择恢复的状态转换,不改写第三张卡。中断恢复复用已持久化逆操作核对归档结果,不把后继指针当作人工选择的唯一凭据。人工选择独立记为 selected,不冒充 merged;归档 agent 卡可通过显式来源页面查看证据。撤销后回到“需要你判断”并定位刚恢复的候选,避免跳到无关卡片。
30
+
31
+ 存储 schema 13 通过独立 event_merge_sources 表记录 mergedFromEventIds;普通卡继续采用单来源边界,合并卡的额外消息必须来自可追溯的归档原卡。schema 12 可只读查看并无损升级,旧插件会拒绝打开更高存储版本。撤销外部导入时,若其证据已参与合并则拒绝整批删除,避免破坏其他来源。narrative 是已退役的 Event 字段,现存正文在 summary;合并不生成或改写事实正文。
32
+
33
+ 这套机制减少重复,但不能保证语义改写零漏判。仅共享主题、后续进展或时间不明的重复发生都优先保留;不会为追求去重率抹掉独立事实。存量扫描采用词元候选召回,低词面重合的同义表达仍可能未命中。
34
+
19
35
  ## 可选字段与兼容
20
36
 
21
37
  | 字段 | 语义 | 边界 |
@@ -29,4 +45,4 @@ SQLite 用新增的可选 `event_metadata` 附表保存这两个字段,支持
29
45
 
30
46
  ## 验证边界
31
47
 
32
- 确定性测试验证 prompt/schema 合约、模型输出归一化、同 Block 精确去重、目标来源边界、relation 写入、SQLite 往返、旧库只读/升级、无历史重提取/回填,以及旧卡的检索、Topic、Graph、API 和 UI 路径。测试里的模型响应是 mock,并非真实模型语义准确率测量;同义识别、提取敏感度及泛化质量仍取决于实际模型。未新增模型调用、依赖、全局 consolidation 或目录算法。
48
+ 确定性测试验证 prompt/schema 合约、模型输出归一化、同 Block 及跨 Block 去重、目标来源边界、relation 写入、SQLite 往返、旧库只读/升级、无历史重提取/回填,以及旧卡的检索、Topic、Graph、API 和 UI 路径。去重另覆盖模型失败/低把握时保留、并发变化、跨进程租约、检查点恢复和一次性执行。测试里的模型响应是 mock,并非真实模型语义准确率测量;同义识别、提取敏感度及泛化质量仍取决于实际模型。未新增依赖或目录算法。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stratagate-dsh",
3
- "version": "0.3.17",
3
+ "version": "0.3.19",
4
4
  "description": "Recent conversations stay vivid. Older ones fade into summaries, not oblivion. StrataGate gives DeepSeek Harness six-layer, time-decaying memory, while lasting events and relationships settle into a knowledge graph. Bring your memories from other AIs with you—no need to start over.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,41 +0,0 @@
1
- # Issue #119 修复与验证
2
-
3
- 基线:`e954eaf2f2cbbef4bb6226a2ebb35191424868c2`(main,stratagate-dsh 0.3.6)。
4
-
5
- ## 根因与修改
6
-
7
- - 日志中的 656、778 字符概要均正常结束于 `tool-calls`,被 600 字符业务上限拒绝,不能归因于 token 截断。现在使用 `TOPIC_OVERVIEW_MAX_CHARACTERS = 1000`,按 Unicode code point 计数,保留原文,不截断。
8
- - DSH `StringValueSchemaSpec` 只支持类型、枚举、常量和注解,不支持 `maxLength`。工具字段说明与提示词引用同一常量,解析器和存储提交门调用同一校验函数。
9
- - 旧 `TopicOverviewRepairGuard` 把未保存的违规草稿计数和来源持久化成补写义务,还从历史字段错误推导“至少一段”的要求。删除这套草稿守卫、错误携带和跨批分配逻辑,保留语义去重。旧 `overviewRepairRequirements` 字段仅供快照兼容与识别恢复对象,不发送给模型、不约束提交,重排、失败或成功时清理。
10
- - 有效旧概要仍由原有存储逻辑继承;章节成员与小节归属、来源权限、批次覆盖、源版本、租约、陈旧响应和原子提交检查继续生效。原有合法重分类、来源失效和遗忘语义不变,旧小节消失时仍按现有规则移除孤立概要。
11
- - 提示词先要求完整事件归类;新增概要通常 0~2 段,无必要时允许空数组,建议 300~600 字符,不为凑长度省略事实,不复述已有内容。
12
- - 沿用 `recoveryVersion`,升级为 2。仅恢复仍有效、有未完成工作且命中概要字段、旧草稿要求、Provider 失败或原始 `invalid-output` 签名的终态任务。无关内容校验、超时和 worker 失败不获得新周期。已完成来源、失效来源、已有排队工作、重启和并发写入沿用原有排除与持久化机制;单周期仍最多三次任务尝试,没有新增恢复状态机。
13
-
14
- ## Provider 预算与剩余证据
15
-
16
- DSH `LlmResolvedModelInfo` 公开 `context.contextWindow` 和 `defaultMaxTokens`;后者仅是未显式指定输出预算时使用的普通默认值,不证明模型能力,也不限制 Topic 的独立请求。Topic 保留 32,768 上限,仅按实际 provider/model 路由已知的上下文剩余空间收缩。当前元数据没有可用的已验证模型输出硬上限,不据默认值猜测 Provider 限制。提示中的预算与每次实际请求保持一致,包括内容重试和 reasoning-off 回退。审查后的实现删除了普通默认值限流和对应缓存。
17
-
18
- 无法容纳输入的模型配置报告 `provider-failed`,不归类成内容校验错误。输出实际截断继续用 `max-tokens` 和现有有界拆批;Provider 异常继续走现有任务退避,无新增专属补丁或重试次数。普通默认值为 512、1024 的路由测试使用 12 张 Event 和两段 1000 字符概要,完整响应的估算 token 超过这些默认值,断言实际请求仍使用独立预算并只调用一次;这是模拟流的请求策略验证,不是低实际预算下的真实生成能力测试。人工重试路径同时保留携带敏感响应的普通 `worker-failed` 脱敏测试。
19
-
20
- Issue 中那次 `finishReason=error` 的根因尚未确认。32,768 可能超过部分模型的输出能力,但也可能是限流、路由或其他请求错误。确认仍需要实际 Provider/model 标识、路由配置的 defaultMaxTokens 与 contextWindow、Provider 错误码/HTTP 状态/请求编号,以及 token usage(含 reasoning)和对应失败请求日志。不得凭当前安全摘要断言是哪一项。
21
-
22
- ## 固定模拟响应对比
23
-
24
- 在固定 main 与首轮修复提交 `6edcb34445e58e9df1ede0189e118672f90fcb42` 上运行同一临时重放测试,使用 `tests/llm.test.ts` 的 `modelBridge`、`context`、`proposal` 辅助函数;只把新概要 text 替换成 `𠮷.repeat(length)`。第一次与第二次流均正常结束于 `tool-calls`;656、778、1000 的第二次响应重复原响应;1001 的第二次响应返回空概要且保留全部章节/小节归类。其余输入与输出一致。保留这次原始测量;审查修复不改变这些未配置路由默认值的概要校验场景,新的定向测试继续覆盖其行为。
25
-
26
- | 首次字符数 | 修复前结果 / 调用数 | 修复后结果 / 调用数 |
27
- | --- | --- | --- |
28
- | 656 | 长度校验失败 / 2 | 首次通过 / 1 |
29
- | 778 | 长度校验失败 / 2 | 首次通过 / 1 |
30
- | 1000 | 长度校验失败 / 2 | 首次通过 / 1 |
31
- | 1001 → 空概要 | 草稿守卫拒绝 / 2 | 合法重试通过 / 2 |
32
-
33
- 这组样例首次通过率 0/4 → 3/4,总调用数 8 → 5,最终完成 0/4 → 4/4。1001 字符始终拒绝,成功来自第二次合法结果。原始输出保存在 [修复前](issue119-before.json) 与 [修复后](issue119-after.json)。这是模拟流经过真实实现的测量,不是线上成功率、真实模型遵从度或提示词效果的测量;未调用真实 Provider,未读取用户数据库。
34
-
35
- ## 回归范围
36
-
37
- 定向测试覆盖 656、778、1000 / 1001 与补充平面 Unicode 字符,字段级诊断、Schema 与提示词、草稿舍弃、完整 Event 分类、旧概要和来源继承、伪造来源和遗漏 Event 拒绝、SQLite 重启、旧恢复要求、部分完成与重叠任务、重复提交、并发初始化、失败后有界拆批,以及 Provider 预算配置与分类。
38
-
39
- 首轮修复提交在 Node 24.19.0 下:`npm run check` 全部通过;`npm test` 的 core 286、DSH 580、WorkBuddy 10 项通过,共 876 项通过、1 项既有跳过;`npm run build` 的 core、DSH 和 WorkBuddy 全部通过。隔离安装最初跳过安装脚本,导致旧数据库迁移测试缺少 better-sqlite3 绑定;用相同 Node 24 执行 `npm rebuild better-sqlite3` 后,完整测试通过。未修改依赖或锁文件。
40
-
41
- 审查修复后的本地验证:`tests/llm.test.ts` 与 `tests/memory-topics.test.ts` 共 194 项通过,`packages/core/tests/topic-recovery.test.ts` 25 项通过,共 219 项定向测试通过;`npm run check` 和 `npm run build` 全部通过。已自查实际预算反馈、内容重试与 reasoning-off 回退、上下文预检分类、持久化来源/归属/旧概要保护和有界恢复路径。新提交仍需独立复审及其对应的 CI 结果;未执行用户真实 Windows + DSH 数据库的升级验收。