dsh-plugin-om 0.0.19 → 0.0.20

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/README.md CHANGED
@@ -33,7 +33,7 @@ dsh plugin --profile <profile> add dsh-plugin-om
33
33
  ```yaml
34
34
  - id: dsh-plugin-om
35
35
  config:
36
- thresholdRatio: 0.2
36
+ observeThresholdTokens: 100000
37
37
  # 其他配置参考下文
38
38
  ```
39
39
 
@@ -69,7 +69,7 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
69
69
 
70
70
  - 直接改`preset-agent`的定义(不推荐)
71
71
  - 定义不含`compaction-basic`的`preset-agent`
72
- - `compaction-basic`压缩阈值是80%上下文窗口,而只需要确保OM的配置中,`thresholdRatio`+`historyMergeRatio`<0.8,理论上没到强制摘要就会被OM压缩了(默认值满足这一条件)
72
+ - `compaction-basic`压缩阈值是80%上下文窗口,而OM的观察阈值是绝对 token 数(默认 `observeThresholdTokens`=100000):模型窗口小于该值/0.8(即 12.5 万 tokens)时宿主强制摘要会先于 OM 触发,需要把 `observeThresholdTokens` 调低到窗口的 0.8 以下
73
73
 
74
74
  ## 工作原理
75
75
 
@@ -79,27 +79,28 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
79
79
  4. 提供recall/recall-semantic两个tool进行检索
80
80
 
81
81
  观察与反思共用同一套系统提示词(buildHistoryPrompt):输入与输出都是合法的 <history> 块(模型消息 + index 的表达形式),先定义块、要求压缩、再给出数据源(下方 <history> 消息记录)。
82
+ 观察压缩按 `observeChunkTokens` 边界把未压缩消息分块(完整消息不跨块,thinking/text/toolcall&result 同块)并行摘要,各块合并为一个 <history> 块提交;提示词按块位次分档——前块要求简单摘要,最后一块要求「越往后越细」保留细节。
82
83
 
83
84
  ### 压缩日志块结构
84
85
 
85
86
  - 观察压缩只精确替换被压缩的新消息区间:旧 <history> 块保留为独立消息,新观察日志作为新块追加在其后(多块并存按序排列),历史不会随压缩次数膨胀;反思合并时才把多个块合并为一条更紧凑的摘要
86
87
  - 压缩边界:消息列表中最后一个合法的 <history> 块(source 为插件,plugin 为插件标识 `dsh-plugin-om`;兼容旧日志的宿主 checkpoint 标记 `compact`)之后的消息视为未压缩;其前(含自身)视为已压缩,不重复压缩、不计入观察阈值
87
88
  - 观察输入经 `@xmldom/xmldom` 构建为合法 <history> 块(用户消息文本/assistant 文本/reasoning 特殊字符自动转义,图片/文件以注释补充;assistant 文本与 toolcall&result 原样;系统消息——user_message 中 source.kind 非 user 的部分,如宿主注入的上下文——渲染为 <sys type="KIND" index="N"></sys> 空块,内容不进入压缩输入)
88
- - 输出经 `@xmldom/xmldom` 解析校验:结构合法(标签匹配/闭合/单块)、不含 <reasoning>、index/start/end 连续(与预期覆盖区间一致),失败按 `compressRetryCount` 重试
89
+ - 输出经 `@xmldom/xmldom` 解析校验:结构合法(标签匹配/闭合/单块)、不含 <reasoning>、index/start/end 连续(与预期覆盖区间一致),失败按 `compressRetryCount` 重试;观察分块时每块独立校验(预期覆盖各自 index 区间,失败按 `compressRetryCount` 重试),各块校验通过后再合并为一个 <history> 块提交
89
90
  - 块开标签带 `tip` 属性(对 AI 的提醒:"当前块是历史消息的压缩产物,不要复述");块顶为构成逻辑注释(完整消息定义串 + 条目标签语义),消息正文不再附加前缀句
90
- - 压缩生命周期:每次摘要调用前先追加 `compaction/start`(载荷带 `phase`:`observe`/观察 或 `reflect`/反思,客户端据此渲染「正在压缩上下文(观察/反思)…」提示行);摘要成功提交 summary + 替换消息 + end,summary 载荷带 `attemptCount`(第几次尝试成功);摘要失败或重试耗尽时追加 `compaction/end(error)` 关闭生命周期(不产生 summary/替换),客户端随之撤回提示行
91
+ - 压缩生命周期:每次摘要调用前先追加 `compaction/start`(载荷带 `phase`:`observe`/观察 或 `reflect`/反思,客户端据此渲染「正在压缩上下文(观察/反思)…」提示行);摘要成功提交 summary + 替换消息 + end,summary 载荷带 `attemptCount`(重试次数;观察分块为各块重试之和,反思为尝试次数 - 1);摘要失败或重试耗尽时追加 `compaction/end(error)` 关闭生命周期(不产生 summary/替换),客户端随之撤回提示行
91
92
 
92
93
  ### 压缩卡片(消息列表中的「已压缩」展示)
93
94
 
94
95
  插件自产的替换消息使用插件自身标记(source.plugin = `dsh-plugin-om`);宿主 conversation UI 只识别内置的 `compact` 检查点,因此插件额外注册一个浏览器客户端 bundle(`src/client/`,经 `dsh.client` 声明、exports["./client"] → `dist/client.js`,由 dsh 客户端模块系统按 `/plugins/dsh-plugin-om/client.js` 加载),自行认领压缩生命周期事件与替换检查点,在消息列表渲染折叠式「已压缩」卡片:
95
96
 
96
97
  - 卡片默认折叠,采用与宿主「上下文注入」卡片同款样式:同一 DisclosureRow 外壳组件 + IconBrowseOutline16 图标,行内统计与展开体样式按宿主 ContextInjectionRow 的规则注入(插件 bundle 无法引用宿主编译期哈希的 CSS Module 类名,按宿主 client bundle 的同一机制注入 <style>),视觉一致
97
- - 标题直接展示「压缩前 N 条消息 · X 字符(约 A tokens)→ 压缩后 Y 字符(约 B tokens)」:压缩前字符数来自插件扩展 shadowedCharCount,压缩前 token 来自 shadowedTokenCount,压缩后字符数为摘要文本长度,压缩后 token 在客户端按 4 字符 ≈ 1 token 估算(与服务端 estimateTextTokens 同一启发式);旧会话载荷缺少 shadowedCharCount 时回落为「已压缩 N 条历史记录(约 M tokens)」
98
+ - 标题直接展示紧凑统计「N · X 字符(A tokens)→ Y 字符(B tokens)」,数字按 k/w/M 单位化(≥1k 用 k、≥1w 用 w、≥1M 用 M,如 8.6k/3.5w/1.2M):压缩前字符数来自插件扩展 shadowedCharCount,压缩前 token 来自 shadowedTokenCount,压缩后字符数为摘要文本长度,压缩后 token 在客户端按 4 字符 ≈ 1 token 估算(与服务端 estimateTextTokens 同一启发式);旧会话载荷缺少 shadowedCharCount 时回落为「已压缩 N 条历史记录(约 M tokens)」
98
99
  - 点击展开显示压缩摘要文本,按代码块样式呈现(缩小字号、代码背景与等宽字体、保留换行)
99
100
  - summary 事件落在当前加载窗口之外时卡片不可展开(不显示空内容)
100
101
  - 卡片只认领插件自产的检查点(source.plugin = `dsh-plugin-om`);宿主 `compact` 检查点仍由宿主自己渲染,不会出现双卡片
101
102
  - 压缩进行中(`compaction/start` 已到、summary/检查点未到):渲染不可展开的「正在压缩上下文(观察/反思)…」提示行,按 start 载荷的 `phase` 区分文案(旧会话无 phase 时回落通用文案);压缩失败收到 `compaction/end` 时以 hidden 可见性撤回提示行
102
- - summary 载荷带 `attemptCount`(第几次尝试成功)时,重试次数 > 0 在标题统计行追加「重试 N 次」(首次成功不显示;旧载荷无 attemptCount 时不显示)
103
+ - summary 载荷带 `attemptCount`(重试次数)时,重试次数 > 0 在标题统计行追加「重试 N 次」(无重试不显示;旧载荷无 attemptCount 时不显示)
103
104
  - 平台模块(react / cordis / ui-primitives / ui-slots 等)走宿主冻结模块表(保持 external),其余依赖内联进 bundle
104
105
 
105
106
  ### 注意
@@ -119,9 +120,11 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
119
120
 
120
121
  | 键 | 默认 | 含义 |
121
122
  | ----------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
122
- | `thresholdRatio` | `0.1` | 观察阈值:未压缩消息 窗口 × 该比例触发压缩 |
123
- | `historyMergeRatio` | `0.2` | 反思阈值:全部 <history> 块总长 窗口 × 该比例触发精简合并 |
124
- | `compressMaxTokens` | `10000` | 单次摘要(观察/反思调用)生成上限 |
123
+ | `observeThresholdTokens` | `100000` | 观察阈值(tokens):未压缩消息 token 估算 该值时触发观察压缩 |
124
+ | `reflectThresholdTokens` | `30000` | 反思阈值(tokens):全部 <history> token 估算合计 该值时触发精简合并 |
125
+ | `compressMaxTokens` | `10000` | 反思(合并调用)生成上限;观察分块用 `observeChunkMaxTokens` |
126
+ | `observeChunkTokens` | `30000` | 观察分块 token 边界:未压缩消息按该边界拆分为多块并行压缩(完整消息不跨块) |
127
+ | `observeChunkMaxTokens` | `5000` | 观察分块单块摘要的生成上限(每块独立调用) |
125
128
  | `tailMessageCount` | `10` | 尾部保留的不压缩消息条数(不压缩、不被替换、不进摘要日志) |
126
129
  | `compressRetryCount` | `10` | 摘要调用失败后的最大重试次数(不含首次;总尝试次数 = 该值 + 1) |
127
130
  | `modelDir` | 共享目录 | recall-semantic 嵌入模型目录(默认 `$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>`,跨插件版本共享;小文件随包分发并在缺失时自动补齐,onnx 缺失且启用语义召回时运行时自动下载到该目录;可指向自定义目录) |
@@ -130,7 +133,7 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
130
133
  | `recallEnabled` | `true` | 是否注册 `recall` 工具(`false` 时禁用,不注册) |
131
134
  | `semanticRecallEnabled` | `true` | 是否注册 `recall-semantic` 工具(`false` 时禁用,不注册、不触发模型下载) |
132
135
 
133
- > 不建议将thresholdRatio设置的过高,越早OM收益越高,且当前的机制需要模型对消息计数,过多的消息会导致历史混乱
136
+ > 不建议将观察阈值设置得过高:越早 OM 收益越高,且当前的机制需要模型对消息计数,过多的消息会导致历史混乱
134
137
 
135
138
  ### 关于配置项变更的说明
136
139
 
@@ -167,8 +170,8 @@ src/
167
170
  │ │ └─▶ model-download.ts # 模型下载原语(URL/跳过判定/原子落盘;dev CLI 复用)
168
171
  │ └─ ④ 事件接线(仅主会话生效)
169
172
  │ └─ ctx.on('agent/pre-step') → compress.ts # maybeCompress:两级压缩阻塞串行(先反思后观察;turn 中间即可触发)
170
- │ ├─ reflectPass → summarize.ts # 全部 <history> 块总长 窗口 × historyMergeRatio:多块拼接 + 共享提示词合并整个块区段
171
- │ ├─ observePass → summarize.ts # 未压缩消息 窗口 × thresholdRatio:渲染 <history> 输入 → 观察日志 → 追加 + 替换
173
+ │ ├─ reflectPass → summarize.ts # 全部 <history> token 估算 reflectThresholdTokens:多块拼接 + 共享提示词合并整个块区段
174
+ │ ├─ observePass → summarize.ts # 未压缩消息 token 估算 observeThresholdTokens:渲染 <history> 输入 → 观察日志 → 追加 + 替换
172
175
  │ └─ 提交 → compress.ts # compaction/start(摘要调用前,载荷 phase) → summary(attemptCount) → 替换消息(source 插件标识 + compactionId) → end;失败补 end(error);usage 归入主会话
173
176
  ├── constants.ts # 共享常量(PLUGIN_LABEL / HISTORY_TAG / HISTORY_TIP / COMPLETE_MESSAGE_DEFINITION / COMPACT_CHECKPOINT_PLUGIN)
174
177
  ├── types.ts # type-only:宿主类型再导出 + 领域类型(MessageNode / MessageIndex)
@@ -177,10 +180,10 @@ src/
177
180
  ├── log-index.ts # 完整消息索引(index 定位 user/sys/assistant/具有result的toolcall;user_message 按 source.kind 分类:kind:user 为用户消息、其余为系统消息;本插件自产压缩日志消息不占位;未闭合 tool-call 不占位;recall 与摘要共用)
178
181
  ├── embedding.ts # 本地 ONNX 嵌入(@huggingface/transformers + 本地模型;共享目录解析 / 小文件补齐 / 运行时按需下载编排 / 懒加载 / 批量 / cosine)
179
182
  ├── model-download.ts # 模型下载原语(modelSourceUrl / needsDownload / 原子落盘 / 开始结束日志与失败镜像建议;运行时与 dev CLI 共用)
180
- ├── summarize.ts # 共享提示词 buildHistoryPrompt(观察/反思同一套)+ renderMessages(@xmldom/xmldom 构建 <history> 块输入:用户消息原样、系统消息渲染为 <sys> 空块、自动转义)+ 直连 ctx.llm.stream() 摘要(new 方式:指令作 system、输入为渲染消息;extractSummaryLog 提取校验:XML 结构合法 / 无 reasoning / index 连续 / 覆盖区间;重试与流式 usage 归入主会话;成功返回 attemptCount 供卡片展示重试次数)
183
+ ├── summarize.ts # 共享提示词 buildHistoryPrompt(观察/反思同一套;mode 控制摘要粒度:summary 简单摘要 / detailed 越往后越细)+ renderMessages(@xmldom/xmldom 构建 <history> 块输入:用户消息原样、系统消息渲染为 <sys> 空块、自动转义)+ 直连 ctx.llm.stream() 摘要(new 方式:指令作 system、输入为渲染消息;extractSummaryLog 提取校验:XML 结构合法 / 无 reasoning / index 连续 / 覆盖区间;重试与流式 usage 归入主会话;成功返回 attemptCount(尝试次数,载荷层换算为重试次数))
181
184
  ├── recall.ts # recall 工具
182
185
  ├── semantic-recall.ts # recall-semantic 工具(query 语义检索 + 区间限定 + 回退全量 + 匹配说明)
183
- └── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / source 标记判定摘要消息 / compaction/* 生命周期事件:start 在摘要调用前追加并带 phase,失败补 end(error) + 替换消息 source 插件标识)
186
+ └── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / source 标记判定摘要消息 / 观察分块并行 chunkCompleteMessages + 合并 mergeChunkReports / compaction/* 生命周期事件:start 在摘要调用前追加并带 phase,失败补 end(error) + 替换消息 source 插件标识)
184
187
  src/client/
185
188
  ├── index.ts # 浏览器客户端入口(exports["./client"] → dist/client.js):注入 slots/conversationEvents/locale,注册卡片定义与渲染器
186
189
  ├── definition.ts # 压缩卡片业务定义:认领插件生命周期事件与替换检查点(source.plugin = dsh-plugin-om),聚合摘要卡片节点;start→压缩中提示行(running+phase)、end→hidden 撤回、summary→卡片(retryCount)
@@ -1 +1 @@
1
- {"version":3,"file":"OmCompactionCard.d.ts","sourceRoot":"","sources":["../../src/client/OmCompactionCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAE/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAIpE,0EAA0E;AAC1E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACzC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;CAC1C;AAgCD,oDAAoD;AACpD,eAAO,MAAM,gBAAgB,oDAA+C,qBAAqB,iCA+F/F,CAAC"}
1
+ {"version":3,"file":"OmCompactionCard.d.ts","sourceRoot":"","sources":["../../src/client/OmCompactionCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gDAAgD,CAAC;AAE/E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAKpE,0EAA0E;AAC1E,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC,eAAe,CAAC,CAAC;IACzC,QAAQ,CAAC,CAAC,EAAE,WAAW,CAAC,eAAe,CAAC,CAAC;CAC1C;AAgCD,oDAAoD;AACpD,eAAO,MAAM,gBAAgB,oDAA+C,qBAAqB,iCAuF/F,CAAC"}
@@ -38,7 +38,7 @@ export interface OmCompactionChatData {
38
38
  readonly summaryCharCount: number | null;
39
39
  /** 压缩后的估算 token 数(4 字符 ≈ 1 token,与服务端 estimateTextTokens 同一启发式),summary 不可用或非法时为 null。 */
40
40
  readonly summaryTokenCount: number | null;
41
- /** 摘要调用重试次数(= 载荷 attemptCount - 1;载荷缺失或非法时为 null)。 */
41
+ /** 摘要调用重试次数(载荷 attemptCount 即重试次数;缺失或非法时为 null)。 */
42
42
  readonly retryCount: number | null;
43
43
  }
44
44
  /** 压缩生命周期关联状态:summary / checkpoint / end 事件证据。 */
@@ -1 +1 @@
1
- {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/client/definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,iBAAiB,EAEjB,0BAA0B,EAC3B,MAAM,wCAAwC,CAAC;AAGhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAInE,uCAAuC;AACvC,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,OAAO,QAAQ,gDAAgD,CAAC;IAC9D,UAAU,eAAe;QACvB,sCAAsC;QACtC,eAAe,EAAE,oBAAoB,CAAC;KACvC;CACF;AAED,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAAC;IAC7C,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gDAAgD;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,4CAA4C;IAC5C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2CAA2C;IAC3C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,6CAA6C;IAC7C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,0FAA0F;IAC1F,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,sDAAsD;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,kDAAkD;AAClD,UAAU,iBAAiB;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IACxC,oEAAoE;IACpE,QAAQ,CAAC,GAAG,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAgBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAe9E;AA0GD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,0BAA0B,CAAC,iBAAiB,CAuDhF,CAAC"}
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../../src/client/definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,iBAAiB,EAEjB,0BAA0B,EAC3B,MAAM,wCAAwC,CAAC;AAGhD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AAInE,uCAAuC;AACvC,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,OAAO,QAAQ,gDAAgD,CAAC;IAC9D,UAAU,eAAe;QACvB,sCAAsC;QACtC,eAAe,EAAE,oBAAoB,CAAC;KACvC;CACF;AAED,gDAAgD;AAChD,MAAM,WAAW,oBAAoB;IACnC,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,IAAI,CAAC;IAC7C,4DAA4D;IAC5D,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gDAAgD;IAChD,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IACxC,0CAA0C;IAC1C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,4CAA4C;IAC5C,QAAQ,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3C,2CAA2C;IAC3C,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,6CAA6C;IAC7C,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,0FAA0F;IAC1F,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED,kDAAkD;AAClD,UAAU,iBAAiB;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,CAAC;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,iBAAiB,CAAC;IACxC,oEAAoE;IACpE,QAAQ,CAAC,GAAG,CAAC,EAAE,iBAAiB,CAAC;CAClC;AAgBD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAe9E;AA0GD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,EAAE,0BAA0B,CAAC,iBAAiB,CAuDhF,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * 压缩卡片统计行的紧凑数字格式:<1k 原样;≥1k 用 k;≥1w(万)用 w;≥1M 用 M。
3
+ * 保留 1 位小数并去掉末尾的 .0(如 8642 → 8.6k、34567 → 3.5w、1234567 → 1.2M)。
4
+ */
5
+ export declare function formatCompactCount(value: number): string;
6
+ //# sourceMappingURL=format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/client/format.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUxD"}
@@ -10,7 +10,7 @@ export declare const NS = "om-compaction";
10
10
  export declare const zh: {
11
11
  readonly compaction: "上下文已压缩";
12
12
  readonly 'compaction.completed': "已压缩 {items} 条历史记录(约 {tokens} tokens)";
13
- readonly 'compaction.stats': "压缩前 {items} 条消息 · {beforeChars} 字符(约 {beforeTokens} tokens)→ 压缩后 {afterChars} 字符(约 {afterTokens} tokens)";
13
+ readonly 'compaction.stats': "{items} · {beforeChars} 字符({beforeTokens} tokens)→ {afterChars} 字符({afterTokens} tokens)";
14
14
  readonly 'compaction.expand': "点击查看压缩摘要";
15
15
  readonly 'compaction.unavailable': "压缩摘要不可用";
16
16
  readonly 'compaction.running': "正在压缩上下文…";
package/dist/client.js CHANGED
@@ -47,7 +47,7 @@ window.__ModuleLoader__.load({
47
47
  shadowedItemCount = Array.isArray(data.shadowedSeqs) && data.shadowedSeqs.every((seq) => Number.isSafeInteger(seq) && seq >= 0) ? data.shadowedSeqs.length : null;
48
48
  shadowedTokenCount = Number.isSafeInteger(data.shadowedTokenCount) && data.shadowedTokenCount >= 0 ? data.shadowedTokenCount : null;
49
49
  shadowedCharCount = data.shadowedCharCount !== void 0 && Number.isSafeInteger(data.shadowedCharCount) && data.shadowedCharCount >= 0 ? data.shadowedCharCount : null;
50
- retryCount = data.attemptCount !== void 0 && Number.isSafeInteger(data.attemptCount) && data.attemptCount >= 1 ? data.attemptCount - 1 : null;
50
+ retryCount = data.attemptCount !== void 0 && Number.isSafeInteger(data.attemptCount) && data.attemptCount >= 0 ? data.attemptCount : null;
51
51
  }
52
52
  return {
53
53
  running: false,
@@ -170,7 +170,7 @@ window.__ModuleLoader__.load({
170
170
  const zh = {
171
171
  compaction: "上下文已压缩",
172
172
  "compaction.completed": "已压缩 {items} 条历史记录(约 {tokens} tokens)",
173
- "compaction.stats": "压缩前 {items} 条消息 · {beforeChars} 字符(约 {beforeTokens} tokens)→ 压缩后 {afterChars} 字符(约 {afterTokens} tokens)",
173
+ "compaction.stats": "{items} · {beforeChars} 字符({beforeTokens} tokens)→ {afterChars} 字符({afterTokens} tokens)",
174
174
  "compaction.expand": "点击查看压缩摘要",
175
175
  "compaction.unavailable": "压缩摘要不可用",
176
176
  "compaction.running": "正在压缩上下文…",
@@ -182,7 +182,7 @@ window.__ModuleLoader__.load({
182
182
  const en = {
183
183
  compaction: "Context compacted",
184
184
  "compaction.completed": "Compacted {items} messages (~{tokens} tokens)",
185
- "compaction.stats": "{items} messages · {beforeChars} chars (~{beforeTokens} tokens) before → {afterChars} chars (~{afterTokens} tokens) after",
185
+ "compaction.stats": "{items} msgs · {beforeChars} chars ({beforeTokens} tokens) → {afterChars} chars ({afterTokens} tokens)",
186
186
  "compaction.expand": "Click to view summary",
187
187
  "compaction.unavailable": "Summary unavailable",
188
188
  "compaction.running": "Compressing context…",
@@ -191,6 +191,23 @@ window.__ModuleLoader__.load({
191
191
  "compaction.retries": "Retried {count} times"
192
192
  };
193
193
  //#endregion
194
+ //#region src/client/format.ts
195
+ /**
196
+ * 压缩卡片统计行的紧凑数字格式:<1k 原样;≥1k 用 k;≥1w(万)用 w;≥1M 用 M。
197
+ * 保留 1 位小数并去掉末尾的 .0(如 8642 → 8.6k、34567 → 3.5w、1234567 → 1.2M)。
198
+ */
199
+ function formatCompactCount(value) {
200
+ /** 保留 1 位小数并去掉末尾 .0。 */
201
+ const trim = (n) => {
202
+ const text = String(Math.round(n * 10) / 10);
203
+ return text.endsWith(".0") ? text.slice(0, -2) : text;
204
+ };
205
+ if (value >= 1e6) return `${trim(value / 1e6)}M`;
206
+ if (value >= 1e4) return `${trim(value / 1e4)}w`;
207
+ if (value >= 1e3) return `${trim(value / 1e3)}k`;
208
+ return String(value);
209
+ }
210
+ //#endregion
194
211
  //#region src/client/OmCompactionCard.tsx
195
212
  /** 卡片样式类名(与宿主 ContextInjectionRow.module.css 同款规则,见下方注册)。 */
196
213
  const css = {
@@ -228,33 +245,25 @@ window.__ModuleLoader__.load({
228
245
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.DisclosureRow, {
229
246
  className: css.root,
230
247
  icon: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_deepseek_ai_dsh_client_ui_primitives.IconBrowseOutline16, { size: 14 }),
231
- title: t("compaction"),
248
+ title: runningLabel,
232
249
  open: false,
233
250
  expandable: false,
234
- onToggle: () => {},
235
- collapsedContent: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
236
- className: css.sep,
237
- "aria-hidden": true
238
- }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
239
- className: css.summary,
240
- "data-om-compaction-running": true,
241
- children: runningLabel
242
- })] })
251
+ onToggle: () => {}
243
252
  })
244
253
  });
245
254
  }
246
255
  const expandable = data.summary !== null;
247
256
  const open = expandable && expanded;
248
- /** 统计行文案(重试次数仅在有重试时追加;retryCount 缺失视为未重试)。 */
257
+ /** 统计行文案(数字按 k/w/M 单位化;重试次数仅在有重试时追加;retryCount 缺失视为未重试)。 */
249
258
  const stats = data.shadowedItemCount !== null && data.shadowedCharCount !== null && data.shadowedTokenCount !== null && data.summaryCharCount !== null && data.summaryTokenCount !== null ? t("compaction.stats", {
250
- items: data.shadowedItemCount,
251
- beforeChars: data.shadowedCharCount,
252
- beforeTokens: data.shadowedTokenCount,
253
- afterChars: data.summaryCharCount,
254
- afterTokens: data.summaryTokenCount
259
+ items: formatCompactCount(data.shadowedItemCount),
260
+ beforeChars: formatCompactCount(data.shadowedCharCount),
261
+ beforeTokens: formatCompactCount(data.shadowedTokenCount),
262
+ afterChars: formatCompactCount(data.summaryCharCount),
263
+ afterTokens: formatCompactCount(data.summaryTokenCount)
255
264
  }) : data.shadowedItemCount !== null && data.shadowedTokenCount !== null ? t("compaction.completed", {
256
- items: data.shadowedItemCount,
257
- tokens: data.shadowedTokenCount
265
+ items: formatCompactCount(data.shadowedItemCount),
266
+ tokens: formatCompactCount(data.shadowedTokenCount)
258
267
  }) : expandable ? t("compaction.expand") : t("compaction.unavailable");
259
268
  const retry = data.retryCount !== null && data.retryCount > 0 ? ` \u00b7 ${t("compaction.retries", { count: data.retryCount })}` : "";
260
269
  return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["memo","useState","DisclosureRow","IconBrowseOutline16"],"sources":["../src/constants.ts","../src/client/definition.ts","../src/client/locales.ts","../src/client/OmCompactionCard.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * 共享常量:集中定义插件级魔法字符串与事件名,避免散落各模块。\n */\n\n/** 插件标识:压缩消息 source.plugin 取值(注入压缩日志的来源标记)与日志前缀。 */\nexport const PLUGIN_LABEL = 'dsh-plugin-om';\n\n/** 压缩日志标签名:<history>...</history> 包裹观察/反思日志块(输入与输出均为合法 history 块)。 */\nexport const HISTORY_TAG = 'history';\n\n/** 压缩日志块开标签上的 tip 属性(对 AI 的提醒:本块是历史压缩产物,不要复述)。 */\nexport const HISTORY_TIP = '当前块是历史消息的压缩产物,不要复述';\n\n/**\n * 「完整消息」定义(提示词 / recall 工具描述 / 块顶注释共用同一字符串)。\n * 完整消息是摘要日志与 recall 共用的定位单位;首条 index 为 0,按会话顺序递增、全局稳定。\n * 用户消息指 user_message 中 source.kind 为 user 的部分;其余部分(宿主注入的上下文等)\n * 为系统消息,同样是一条完整消息。\n */\nexport const COMPLETE_MESSAGE_DEFINITION =\n '`完整消息`被定义为`用户消息`,`系统消息`,`模型输出文本`和`具有result的toolcall`的集合。首条`完整消息`的index是0,后续的index递增。';\n\n/** 判定 user/message 的 source 是否为本插件自产(压缩日志机制消息;含兼容旧宿主 checkpoint 标记 'compact')。这类消息不占完整消息 index。 */\nexport function isPluginOwnedSource(\n source: { kind?: string; plugin?: string } | undefined,\n): boolean {\n if (source?.kind !== 'plugin') return false;\n return source.plugin === PLUGIN_LABEL || source.plugin === COMPACT_CHECKPOINT_PLUGIN;\n}\n\n/** 旧日志压缩消息的宿主 checkpoint 标记 plugin 名(dsh-compaction-basic 的 COMPACT_CHECKPOINT_MARKER.plugin;历史兼容识别用)。 */\nexport const COMPACT_CHECKPOINT_PLUGIN = 'compact';\n","/**\n * 压缩卡片业务定义:认领插件自产的压缩生命周期事件与替换检查点,\n * 聚合出可渲染的摘要卡片节点。\n *\n * 宿主 conversation 客户端仅识别 source.plugin === 'compact' 的压缩检查点\n * (ui-conversation 的 compactionDefinition),插件自产的检查点标记为\n * PLUGIN_LABEL('dsh-plugin-om'),由本定义认领。生命周期事件(compaction/start\n * |summary|end)同时会被宿主的 compactionDefinition 匹配,但宿主没有检查点证据\n * 时不产出节点(buildViewNode 返回 null),因此双方共存无视觉冲突。\n */\n\nimport type {\n ConversationMatch,\n ConversationNodeContext,\n ConversationNodeDefinition,\n} from '@deepseek-ai/dsh-client-runtime/client';\nimport type { ChatNode, ChatNodeDataMap } from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport type {} from '@deepseek-ai/dsh-compaction/types';\nimport type { SessionEvent } from '@deepseek-ai/dsh-session/types';\nimport { PLUGIN_LABEL } from '../constants.ts';\nimport type { CompactionSummaryPayload } from '../types.ts';\n\n/** 压缩卡片渲染器分发键(合并进 ChatNodeDataMap)。 */\nexport const COMPACTION_CARD_KIND = 'om-compaction';\n\ndeclare module '@deepseek-ai/dsh-client-ui-conversation/client' {\n interface ChatNodeDataMap {\n /** dsh-plugin-om 压缩替换检查点渲染的摘要卡片数据。 */\n 'om-compaction': OmCompactionChatData;\n }\n}\n\n/** 压缩卡片载荷:摘要文本与遮蔽统计(seq/time 由节点本体提供,不重复存放)。 */\nexport interface OmCompactionChatData {\n /** 压缩进行中(compaction/start 已到、checkpoint/end 未到):渲染为「正在压缩上下文…」提示行。 */\n readonly running: boolean;\n /** 压缩 pass(观察/反思;来自 start 事件载荷的插件扩展,缺失或非运行态为 null)。 */\n readonly phase: 'observe' | 'reflect' | null;\n /** compaction/summary 的文本摘要;窗口裁剪把该事件留在窗外时为 null(卡片不可展开)。 */\n readonly summary: string | null;\n /** 已载入的 compaction/summary 事件 seq,窗外时为 null。 */\n readonly summaryEventSeq: number | null;\n /** 被遮蔽的表层节点数,summary 事件缺失或载荷非法时为 null。 */\n readonly shadowedItemCount: number | null;\n /** 遮蔽的 token 数,summary 事件缺失或载荷非法时为 null。 */\n readonly shadowedTokenCount: number | null;\n /** 压缩前的字符数(被压缩内容文本长度合计),载荷缺失或非法时为 null。 */\n readonly shadowedCharCount: number | null;\n /** 压缩后的字符数(摘要文本长度),summary 不可用或非法时为 null。 */\n readonly summaryCharCount: number | null;\n /** 压缩后的估算 token 数(4 字符 ≈ 1 token,与服务端 estimateTextTokens 同一启发式),summary 不可用或非法时为 null。 */\n readonly summaryTokenCount: number | null;\n /** 摘要调用重试次数(= 载荷 attemptCount - 1;载荷缺失或非法时为 null)。 */\n readonly retryCount: number | null;\n}\n\n/** 压缩生命周期关联状态:summary / checkpoint / end 事件证据。 */\ninterface OmCompactionState {\n readonly summary?: ConversationMatch;\n readonly checkpoint?: ConversationMatch;\n /** compaction/end(成功与失败都会到达;无 checkpoint 的 end 表示压缩失败,撤回进行中提示行)。 */\n readonly end?: ConversationMatch;\n}\n\n/**\n * replace-surface 判定:surfaceOp 为 { op: 'replace' } 对象(与宿主 runtime 的\n * isReplacementSurfaceEvent 同义)。宿主该助手位于浏览器 bundle 内\n * (window.__ModuleLoader__ 闭包),node 端测试无法求值,故本地实现同一形状检查。\n */\nfunction isReplacementSurface(event: SessionEvent): boolean {\n const surfaceOp = (event as { surfaceOp?: unknown }).surfaceOp;\n return (\n typeof surfaceOp === 'object' &&\n surfaceOp !== null &&\n (surfaceOp as { op?: unknown }).op === 'replace'\n );\n}\n\n/**\n * 从压缩替换检查点(user/message + replace surface + source.plugin 为本插件)\n * 读取关联 identity。宿主的 'compact' 检查点由宿主自己渲染,本插件不认领,\n * 避免同一压缩出现两张卡片。\n */\nexport function checkpointCompactionId(event: SessionEvent): string | undefined {\n if (event.type !== 'user/message' || !isReplacementSurface(event)) return undefined;\n const data = event.data as unknown as {\n source?: { kind?: unknown; plugin?: unknown; compactionId?: unknown };\n };\n const source = data.source;\n if (\n source === undefined ||\n source.kind !== 'plugin' ||\n source.plugin !== PLUGIN_LABEL ||\n typeof source.compactionId !== 'string'\n ) {\n return undefined;\n }\n return source.compactionId;\n}\n\n/** 从 compaction/summary 载荷提取摘要文本与遮蔽统计(载荷非法时各字段回落 null)。 */\nfunction compactSummaryData(summaryMatch: ConversationMatch | undefined): OmCompactionChatData {\n let summary: string | null = null;\n let shadowedItemCount: number | null = null;\n let shadowedTokenCount: number | null = null;\n let shadowedCharCount: number | null = null;\n let retryCount: number | null = null;\n if (summaryMatch?.event.type === 'compaction/summary') {\n // 宿主载荷类型是 union 且不含插件扩展字段,读取处收窄为插件扩展类型\n const data = summaryMatch.event.data as CompactionSummaryPayload;\n if (Array.isArray(data.summary)) {\n const text = data.summary.map((block) => (block.type === 'text' ? block.text : '')).join('');\n summary = text.trim() === '' ? null : text;\n }\n shadowedItemCount =\n Array.isArray(data.shadowedSeqs) &&\n data.shadowedSeqs.every((seq) => Number.isSafeInteger(seq) && seq >= 0)\n ? data.shadowedSeqs.length\n : null;\n shadowedTokenCount =\n Number.isSafeInteger(data.shadowedTokenCount) && data.shadowedTokenCount >= 0\n ? data.shadowedTokenCount\n : null;\n shadowedCharCount =\n data.shadowedCharCount !== undefined &&\n Number.isSafeInteger(data.shadowedCharCount) &&\n data.shadowedCharCount >= 0\n ? data.shadowedCharCount\n : null;\n retryCount =\n data.attemptCount !== undefined &&\n Number.isSafeInteger(data.attemptCount) &&\n data.attemptCount >= 1\n ? data.attemptCount - 1\n : null;\n }\n return {\n running: false,\n phase: null,\n summary,\n summaryEventSeq: summaryMatch?.event.seq ?? null,\n shadowedItemCount,\n shadowedTokenCount,\n shadowedCharCount,\n summaryCharCount: summary === null ? null : summary.length,\n summaryTokenCount: summary === null ? null : Math.ceil(summary.length / 4),\n retryCount,\n };\n}\n\n/** 压缩进行中提示行的载荷(统计未就绪,全部为 null;phase 取自 start 事件载荷扩展,缺失回退 null)。 */\nfunction runningData(start: ConversationMatch | undefined): OmCompactionChatData {\n /** start 事件载荷(phase 为插件扩展;非法值回退 null)。 */\n const data = start?.event.data as { phase?: unknown } | undefined;\n /** 合法 phase(观察/反思)。 */\n const phase = data?.phase === 'observe' || data?.phase === 'reflect' ? data.phase : null;\n return {\n running: true,\n phase,\n summary: null,\n summaryEventSeq: null,\n shadowedItemCount: null,\n shadowedTokenCount: null,\n shadowedCharCount: null,\n summaryCharCount: null,\n summaryTokenCount: null,\n retryCount: null,\n };\n}\n\n/** 窗口只载入部分事件时的回落证据扫描(summary / 检查点 / end 各自独立)。 */\nfunction fallbackState(context: ConversationNodeContext<OmCompactionState>): OmCompactionState {\n const summary = context.matches.find((match) => match.event.type === 'compaction/summary');\n const checkpoint = context.matches.find(\n (match) => checkpointCompactionId(match.event) !== undefined,\n );\n const end = context.matches.find((match) => match.event.type === 'compaction/end');\n return {\n ...(summary === undefined ? {} : { summary }),\n ...(checkpoint === undefined ? {} : { checkpoint }),\n ...(end === undefined ? {} : { end }),\n };\n}\n\n/** 构建最终 Chat 目标节点(与 ui-conversation 的 chatNode 助手同构)。 */\nfunction chatNode<Kind extends keyof ChatNodeDataMap & string>(\n context: ConversationNodeContext,\n kind: Kind,\n anchorSeq: number,\n data: ChatNodeDataMap[Kind],\n visibility: 'visible' | 'hidden' = 'visible',\n): ChatNode<Kind> {\n return {\n key: context.key,\n kind,\n id: context.id,\n target: 'chat',\n anchorSeq,\n location: context.start?.location ?? context.matches[0]?.location ?? { kind: 'unresolved' },\n visibility,\n data,\n };\n}\n\n/**\n * 插件压缩生命周期定义:compaction/start 开启上下文,summary / 替换检查点 /\n * end 作为 update 折叠证据;仅在存在替换检查点时产出卡片节点。\n */\nexport const omCompactionDefinition: ConversationNodeDefinition<OmCompactionState> = {\n kind: COMPACTION_CARD_KIND,\n target: 'chat',\n match: (event) => {\n const compactionId = checkpointCompactionId(event);\n if (compactionId !== undefined) return { id: compactionId, role: 'update' };\n if (\n event.type === 'compaction/start' ||\n event.type === 'compaction/summary' ||\n event.type === 'compaction/end'\n ) {\n const id: unknown = event.data.compactionId;\n if (typeof id !== 'string' || id === '') return null;\n return { id, role: event.type === 'compaction/start' ? 'start' : 'update' };\n }\n return null;\n },\n start: () => ({}),\n update: (context, match) => {\n if (match.event.type === 'compaction/summary') return { ...context.state, summary: match };\n if (match.event.type === 'compaction/end') return { ...context.state, end: match };\n if (checkpointCompactionId(match.event) !== undefined)\n return { ...context.state, checkpoint: match };\n return context.state;\n },\n buildViewNode: (context) => {\n const state = context.state ?? fallbackState(context);\n // 替换检查点已到达 → 压缩完成,渲染摘要卡片(摘要统计就绪)\n if (state.checkpoint !== undefined) {\n const data = compactSummaryData(state.summary);\n return chatNode(context, COMPACTION_CARD_KIND, state.checkpoint.event.seq, data);\n }\n // end 已到达但无检查点 → 压缩失败/中止:撤回进行中提示行——\n // 已物化节点不能撤回,以 hidden visibility 保留同一 key(聊天视图按可见节点过滤,行从消息流消失)\n if (state.end !== undefined) {\n const anchorSeq = context.start?.event.seq ?? state.end.event.seq;\n return chatNode(\n context,\n COMPACTION_CARD_KIND,\n anchorSeq,\n runningData(context.start),\n 'hidden',\n );\n }\n // start 已到、checkpoint/end 未到 → 压缩进行中,渲染「正在压缩上下文…」提示行\n if (context.start !== undefined) {\n return chatNode(\n context,\n COMPACTION_CARD_KIND,\n context.start.event.seq,\n runningData(context.start),\n );\n }\n return null;\n },\n};\n","/**\n * `om-compaction` namespace dictionaries: 插件压缩卡片的界面文案。\n *\n * 与宿主 conversation 命名空间分离(独立 NS),插件卡片走自己的 keyed 渲染器,\n * 文案键不与对话插件的键冲突。\n */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'om-compaction';\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n compaction: '上下文已压缩',\n 'compaction.completed': '已压缩 {items} 条历史记录(约 {tokens} tokens)',\n 'compaction.stats':\n '压缩前 {items} 条消息 · {beforeChars} 字符(约 {beforeTokens} tokens)→ 压缩后 {afterChars} 字符(约 {afterTokens} tokens)',\n 'compaction.expand': '点击查看压缩摘要',\n 'compaction.unavailable': '压缩摘要不可用',\n 'compaction.running': '正在压缩上下文…',\n 'compaction.running.observe': '正在压缩上下文(观察)…',\n 'compaction.running.reflect': '正在压缩上下文(反思)…',\n 'compaction.retries': '重试 {count} 次',\n} as const;\n\n/** English dictionary(键集与 zh 一致)。 */\nexport const en: Record<keyof typeof zh, string> = {\n compaction: 'Context compacted',\n 'compaction.completed': 'Compacted {items} messages (~{tokens} tokens)',\n 'compaction.stats':\n '{items} messages · {beforeChars} chars (~{beforeTokens} tokens) before → {afterChars} chars (~{afterTokens} tokens) after',\n 'compaction.expand': 'Click to view summary',\n 'compaction.unavailable': 'Summary unavailable',\n 'compaction.running': 'Compressing context…',\n 'compaction.running.observe': 'Compressing context (observation)…',\n 'compaction.running.reflect': 'Compressing context (reflection)…',\n 'compaction.retries': 'Retried {count} times',\n};\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Compaction card copy owned by dsh-plugin-om. */\n 'om-compaction': keyof typeof zh;\n }\n}\n","/**\n * dsh-plugin-om 压缩卡片渲染器:与宿主「上下文注入」卡片同款的折叠行。\n *\n * 复用平台原语 DisclosureRow(宿主 ContextInjectionRow 用的同一外壳组件);行内\n * 统计与展开体样式按宿主 ContextInjectionRow.module.css 的规则注入——宿主该卡片\n * 的样式是编译期哈希的 CSS Module,插件 bundle 无法引用其类名,故按宿主 client\n * bundle 的同一机制(注入 <style>)注册逐字一致的规则,保证视觉同款。\n *\n * 压缩检查点遮蔽的是模型可见表层,人类转录中的历史仍在其上方;卡片只报告\n * 模型从何处起不再看到那段历史。summary 来自检查点引用的 compaction/summary\n * 事件;窗口裁剪把该事件留在窗外时,行不可展开而不是显示空内容。\n */\n\nimport type { ChatNode } from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport { DisclosureRow, IconBrowseOutline16 } from '@deepseek-ai/dsh-client-ui-primitives';\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';\nimport type { CSSProperties } from 'react';\nimport { memo, useState } from 'react';\n\n/** Keyed renderer props: 装配好的卡片节点 + 绑定 `om-compaction` 命名空间的 locale 座。 */\nexport interface OmCompactionCardProps {\n readonly node: ChatNode<'om-compaction'>;\n readonly t: TranslateNS<'om-compaction'>;\n}\n\n/** 卡片样式类名(与宿主 ContextInjectionRow.module.css 同款规则,见下方注册)。 */\nconst css = {\n root: 'om-compaction-root',\n chevron: 'om-compaction-chevron',\n sep: 'om-compaction-sep',\n summary: 'om-compaction-summary',\n body: 'om-compaction-body',\n} as const;\n\n/** 宿主 ContextInjectionRow.module.css 的规则(逐字一致,仅类名按插件前缀)。 */\nconst cardCss = `\n.${css.root}{min-width:0}\n.${css.root}[data-open]{padding-bottom:4px}\n.${css.chevron}{color:var(--dsw-alias-label-secondary)}\n.${css.sep}{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}\n.${css.summary}{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:14px;line-height:24px;overflow:hidden}\n.${css.body}{box-sizing:border-box;background:var(--dsw-alias-markdown-code-block);width:calc(100% - 22px);max-height:141px;color:var(--dsw-alias-label-tertiary);font:400 11px/16px var(--ds-font-family-code);border:none;border-radius:8px;margin:4px 0 0 22px;padding:10px 16px 12px 12px;overflow:auto}\n`;\n\n/** 注册卡片样式(与宿主 client bundle 同机制;无 document 的环境如 node 测试直接跳过)。 */\nif (\n typeof document !== 'undefined' &&\n document.querySelector('style[data-dsh-plugin-om-card]') === null\n) {\n const tag = document.createElement('style');\n tag.dataset.dshPluginOmCard = 'true';\n tag.textContent = cardCss;\n document.head.appendChild(tag);\n}\n\n/** 折叠式压缩卡片(统计标题行 + 代码块样式 summary 展开,同「上下文注入」卡片)。 */\nexport const OmCompactionCard = memo(function OmCompactionCard({ node, t }: OmCompactionCardProps) {\n const [expanded, setExpanded] = useState(false);\n const { data } = node;\n // 压缩进行中:start 已到、checkpoint/end 未到 → 不可展开的「正在压缩上下文…」提示行\n if (data.running) {\n /** 提示行文案(按 phase 区分观察/反思;缺失回退通用文案)。 */\n const runningLabel =\n data.phase === 'observe'\n ? t('compaction.running.observe')\n : data.phase === 'reflect'\n ? t('compaction.running.reflect')\n : t('compaction.running');\n return (\n <div style={styles.row}>\n <DisclosureRow\n className={css.root}\n icon={<IconBrowseOutline16 size={14} />}\n title={t('compaction')}\n open={false}\n expandable={false}\n onToggle={() => {\n /* 进行中提示行不可展开 */\n }}\n collapsedContent={\n <>\n <span className={css.sep} aria-hidden />\n <span className={css.summary} data-om-compaction-running>\n {runningLabel}\n </span>\n </>\n }\n />\n </div>\n );\n }\n const expandable = data.summary !== null;\n const open = expandable && expanded;\n /** 统计行文案(重试次数仅在有重试时追加;retryCount 缺失视为未重试)。 */\n const stats =\n data.shadowedItemCount !== null &&\n data.shadowedCharCount !== null &&\n data.shadowedTokenCount !== null &&\n data.summaryCharCount !== null &&\n data.summaryTokenCount !== null\n ? t('compaction.stats', {\n items: data.shadowedItemCount,\n beforeChars: data.shadowedCharCount,\n beforeTokens: data.shadowedTokenCount,\n afterChars: data.summaryCharCount,\n afterTokens: data.summaryTokenCount,\n })\n : data.shadowedItemCount !== null && data.shadowedTokenCount !== null\n ? t('compaction.completed', {\n items: data.shadowedItemCount,\n tokens: data.shadowedTokenCount,\n })\n : expandable\n ? t('compaction.expand')\n : t('compaction.unavailable');\n const retry =\n data.retryCount !== null && data.retryCount > 0\n ? ` \\u00b7 ${t('compaction.retries', { count: data.retryCount })}`\n : '';\n return (\n <div style={styles.row}>\n <DisclosureRow\n className={css.root}\n icon={<IconBrowseOutline16 size={14} />}\n chevronClassName={css.chevron}\n title={t('compaction')}\n collapsedContent={\n <>\n <span className={css.sep} aria-hidden />\n <span className={css.summary} data-om-compaction-summary>\n {stats}\n {retry}\n </span>\n </>\n }\n keepContentWhenOpen\n expandable={expandable}\n expandOnRowClick\n open={open}\n onToggle={() => {\n setExpanded((value) => !value);\n }}\n >\n {data.summary !== null && (\n <div className={css.body} style={styles.body}>\n {data.summary}\n </div>\n )}\n </DisclosureRow>\n </div>\n );\n});\n\nconst styles: Record<string, CSSProperties> = {\n row: {},\n /** 摘要为 <history> 文本,保留换行(宿主 body 的白色空间由内容形态各自处理)。 */\n body: { whiteSpace: 'pre-wrap' },\n};\n","/**\n * dsh-plugin-om 浏览器客户端贡献:压缩卡片。\n *\n * 服务端压缩替换消息继续使用插件自身的检查点标记(source.plugin =\n * 'dsh-plugin-om');宿主 conversation UI 只识别内置的 'compact' 标记,因此本\n * bundle 自行注册一条 conversation 业务定义 + keyed 渲染器,认领插件的压缩\n * 生命周期事件与替换检查点,在消息列表渲染「已压缩」卡片。\n */\nimport type { Context } from '@deepseek-ai/cordis';\n// Type-only: 拉取 locale 插件的 Context 合并(ctx.locale)。\nimport type {} from '@deepseek-ai/dsh-client-locale/client';\n// Type-only: 拉取 runtime 的 Context 合并(ctx.conversationEvents)。\nimport type {} from '@deepseek-ai/dsh-client-runtime/client';\n// Type-only: 'conversation.chat.node' 的 SlotMap 行与 ChatNodeDataMap 声明合并\n// (均由 ui-conversation 声明),注册调用才得以类型化。\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport { omCompactionDefinition } from './definition.ts';\nimport { en, NS, zh } from './locales.ts';\nimport { OmCompactionCard } from './OmCompactionCard.tsx';\n\n/** Required services: 聊天节点槽、会话事件注册表与 locale 服务。 */\nexport const inject = ['slots', 'conversationEvents', 'locale'];\n\n/**\n * Client plugin body: 注册压缩卡片的业务定义与 keyed 渲染器。\n * 注册挂在 effect 包装器上,插件卸载时随之移除。\n * @param ctx - client root context.\n */\nexport function apply(ctx: Context): void {\n ctx.effect(\n () => ctx.locale.register(NS, { zh, en }),\n 'dsh-plugin-om: compaction card dictionaries',\n );\n ctx.effect(\n () => ctx.conversationEvents.register(omCompactionDefinition),\n 'dsh-plugin-om: compaction definition',\n );\n ctx.slots.inject('conversation.chat.node', () =>\n ctx.slots.register(\n { name: 'conversation.chat.node', key: 'om-compaction', locale: NS },\n OmCompactionCard,\n ),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["memo","useState","DisclosureRow","IconBrowseOutline16"],"sources":["../src/constants.ts","../src/client/definition.ts","../src/client/locales.ts","../src/client/format.ts","../src/client/OmCompactionCard.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * 共享常量:集中定义插件级魔法字符串与事件名,避免散落各模块。\n */\n\n/** 插件标识:压缩消息 source.plugin 取值(注入压缩日志的来源标记)与日志前缀。 */\nexport const PLUGIN_LABEL = 'dsh-plugin-om';\n\n/** 压缩日志标签名:<history>...</history> 包裹观察/反思日志块(输入与输出均为合法 history 块)。 */\nexport const HISTORY_TAG = 'history';\n\n/** 压缩日志块开标签上的 tip 属性(对 AI 的提醒:本块是历史压缩产物,不要复述)。 */\nexport const HISTORY_TIP = '当前块是历史消息的压缩产物,不要复述';\n\n/**\n * 「完整消息」定义(提示词 / recall 工具描述 / 块顶注释共用同一字符串)。\n * 完整消息是摘要日志与 recall 共用的定位单位;首条 index 为 0,按会话顺序递增、全局稳定。\n * 用户消息指 user_message 中 source.kind 为 user 的部分;其余部分(宿主注入的上下文等)\n * 为系统消息,同样是一条完整消息。\n */\nexport const COMPLETE_MESSAGE_DEFINITION =\n '`完整消息`被定义为`用户消息`,`系统消息`,`模型输出文本`和`具有result的toolcall`的集合。首条`完整消息`的index是0,后续的index递增。';\n\n/** 判定 user/message 的 source 是否为本插件自产(压缩日志机制消息;含兼容旧宿主 checkpoint 标记 'compact')。这类消息不占完整消息 index。 */\nexport function isPluginOwnedSource(\n source: { kind?: string; plugin?: string } | undefined,\n): boolean {\n if (source?.kind !== 'plugin') return false;\n return source.plugin === PLUGIN_LABEL || source.plugin === COMPACT_CHECKPOINT_PLUGIN;\n}\n\n/** 旧日志压缩消息的宿主 checkpoint 标记 plugin 名(dsh-compaction-basic 的 COMPACT_CHECKPOINT_MARKER.plugin;历史兼容识别用)。 */\nexport const COMPACT_CHECKPOINT_PLUGIN = 'compact';\n","/**\n * 压缩卡片业务定义:认领插件自产的压缩生命周期事件与替换检查点,\n * 聚合出可渲染的摘要卡片节点。\n *\n * 宿主 conversation 客户端仅识别 source.plugin === 'compact' 的压缩检查点\n * (ui-conversation 的 compactionDefinition),插件自产的检查点标记为\n * PLUGIN_LABEL('dsh-plugin-om'),由本定义认领。生命周期事件(compaction/start\n * |summary|end)同时会被宿主的 compactionDefinition 匹配,但宿主没有检查点证据\n * 时不产出节点(buildViewNode 返回 null),因此双方共存无视觉冲突。\n */\n\nimport type {\n ConversationMatch,\n ConversationNodeContext,\n ConversationNodeDefinition,\n} from '@deepseek-ai/dsh-client-runtime/client';\nimport type { ChatNode, ChatNodeDataMap } from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport type {} from '@deepseek-ai/dsh-compaction/types';\nimport type { SessionEvent } from '@deepseek-ai/dsh-session/types';\nimport { PLUGIN_LABEL } from '../constants.ts';\nimport type { CompactionSummaryPayload } from '../types.ts';\n\n/** 压缩卡片渲染器分发键(合并进 ChatNodeDataMap)。 */\nexport const COMPACTION_CARD_KIND = 'om-compaction';\n\ndeclare module '@deepseek-ai/dsh-client-ui-conversation/client' {\n interface ChatNodeDataMap {\n /** dsh-plugin-om 压缩替换检查点渲染的摘要卡片数据。 */\n 'om-compaction': OmCompactionChatData;\n }\n}\n\n/** 压缩卡片载荷:摘要文本与遮蔽统计(seq/time 由节点本体提供,不重复存放)。 */\nexport interface OmCompactionChatData {\n /** 压缩进行中(compaction/start 已到、checkpoint/end 未到):渲染为「正在压缩上下文…」提示行。 */\n readonly running: boolean;\n /** 压缩 pass(观察/反思;来自 start 事件载荷的插件扩展,缺失或非运行态为 null)。 */\n readonly phase: 'observe' | 'reflect' | null;\n /** compaction/summary 的文本摘要;窗口裁剪把该事件留在窗外时为 null(卡片不可展开)。 */\n readonly summary: string | null;\n /** 已载入的 compaction/summary 事件 seq,窗外时为 null。 */\n readonly summaryEventSeq: number | null;\n /** 被遮蔽的表层节点数,summary 事件缺失或载荷非法时为 null。 */\n readonly shadowedItemCount: number | null;\n /** 遮蔽的 token 数,summary 事件缺失或载荷非法时为 null。 */\n readonly shadowedTokenCount: number | null;\n /** 压缩前的字符数(被压缩内容文本长度合计),载荷缺失或非法时为 null。 */\n readonly shadowedCharCount: number | null;\n /** 压缩后的字符数(摘要文本长度),summary 不可用或非法时为 null。 */\n readonly summaryCharCount: number | null;\n /** 压缩后的估算 token 数(4 字符 ≈ 1 token,与服务端 estimateTextTokens 同一启发式),summary 不可用或非法时为 null。 */\n readonly summaryTokenCount: number | null;\n /** 摘要调用重试次数(载荷 attemptCount 即重试次数;缺失或非法时为 null)。 */\n readonly retryCount: number | null;\n}\n\n/** 压缩生命周期关联状态:summary / checkpoint / end 事件证据。 */\ninterface OmCompactionState {\n readonly summary?: ConversationMatch;\n readonly checkpoint?: ConversationMatch;\n /** compaction/end(成功与失败都会到达;无 checkpoint 的 end 表示压缩失败,撤回进行中提示行)。 */\n readonly end?: ConversationMatch;\n}\n\n/**\n * replace-surface 判定:surfaceOp 为 { op: 'replace' } 对象(与宿主 runtime 的\n * isReplacementSurfaceEvent 同义)。宿主该助手位于浏览器 bundle 内\n * (window.__ModuleLoader__ 闭包),node 端测试无法求值,故本地实现同一形状检查。\n */\nfunction isReplacementSurface(event: SessionEvent): boolean {\n const surfaceOp = (event as { surfaceOp?: unknown }).surfaceOp;\n return (\n typeof surfaceOp === 'object' &&\n surfaceOp !== null &&\n (surfaceOp as { op?: unknown }).op === 'replace'\n );\n}\n\n/**\n * 从压缩替换检查点(user/message + replace surface + source.plugin 为本插件)\n * 读取关联 identity。宿主的 'compact' 检查点由宿主自己渲染,本插件不认领,\n * 避免同一压缩出现两张卡片。\n */\nexport function checkpointCompactionId(event: SessionEvent): string | undefined {\n if (event.type !== 'user/message' || !isReplacementSurface(event)) return undefined;\n const data = event.data as unknown as {\n source?: { kind?: unknown; plugin?: unknown; compactionId?: unknown };\n };\n const source = data.source;\n if (\n source === undefined ||\n source.kind !== 'plugin' ||\n source.plugin !== PLUGIN_LABEL ||\n typeof source.compactionId !== 'string'\n ) {\n return undefined;\n }\n return source.compactionId;\n}\n\n/** 从 compaction/summary 载荷提取摘要文本与遮蔽统计(载荷非法时各字段回落 null)。 */\nfunction compactSummaryData(summaryMatch: ConversationMatch | undefined): OmCompactionChatData {\n let summary: string | null = null;\n let shadowedItemCount: number | null = null;\n let shadowedTokenCount: number | null = null;\n let shadowedCharCount: number | null = null;\n let retryCount: number | null = null;\n if (summaryMatch?.event.type === 'compaction/summary') {\n // 宿主载荷类型是 union 且不含插件扩展字段,读取处收窄为插件扩展类型\n const data = summaryMatch.event.data as CompactionSummaryPayload;\n if (Array.isArray(data.summary)) {\n const text = data.summary.map((block) => (block.type === 'text' ? block.text : '')).join('');\n summary = text.trim() === '' ? null : text;\n }\n shadowedItemCount =\n Array.isArray(data.shadowedSeqs) &&\n data.shadowedSeqs.every((seq) => Number.isSafeInteger(seq) && seq >= 0)\n ? data.shadowedSeqs.length\n : null;\n shadowedTokenCount =\n Number.isSafeInteger(data.shadowedTokenCount) && data.shadowedTokenCount >= 0\n ? data.shadowedTokenCount\n : null;\n shadowedCharCount =\n data.shadowedCharCount !== undefined &&\n Number.isSafeInteger(data.shadowedCharCount) &&\n data.shadowedCharCount >= 0\n ? data.shadowedCharCount\n : null;\n retryCount =\n data.attemptCount !== undefined &&\n Number.isSafeInteger(data.attemptCount) &&\n data.attemptCount >= 0\n ? data.attemptCount\n : null;\n }\n return {\n running: false,\n phase: null,\n summary,\n summaryEventSeq: summaryMatch?.event.seq ?? null,\n shadowedItemCount,\n shadowedTokenCount,\n shadowedCharCount,\n summaryCharCount: summary === null ? null : summary.length,\n summaryTokenCount: summary === null ? null : Math.ceil(summary.length / 4),\n retryCount,\n };\n}\n\n/** 压缩进行中提示行的载荷(统计未就绪,全部为 null;phase 取自 start 事件载荷扩展,缺失回退 null)。 */\nfunction runningData(start: ConversationMatch | undefined): OmCompactionChatData {\n /** start 事件载荷(phase 为插件扩展;非法值回退 null)。 */\n const data = start?.event.data as { phase?: unknown } | undefined;\n /** 合法 phase(观察/反思)。 */\n const phase = data?.phase === 'observe' || data?.phase === 'reflect' ? data.phase : null;\n return {\n running: true,\n phase,\n summary: null,\n summaryEventSeq: null,\n shadowedItemCount: null,\n shadowedTokenCount: null,\n shadowedCharCount: null,\n summaryCharCount: null,\n summaryTokenCount: null,\n retryCount: null,\n };\n}\n\n/** 窗口只载入部分事件时的回落证据扫描(summary / 检查点 / end 各自独立)。 */\nfunction fallbackState(context: ConversationNodeContext<OmCompactionState>): OmCompactionState {\n const summary = context.matches.find((match) => match.event.type === 'compaction/summary');\n const checkpoint = context.matches.find(\n (match) => checkpointCompactionId(match.event) !== undefined,\n );\n const end = context.matches.find((match) => match.event.type === 'compaction/end');\n return {\n ...(summary === undefined ? {} : { summary }),\n ...(checkpoint === undefined ? {} : { checkpoint }),\n ...(end === undefined ? {} : { end }),\n };\n}\n\n/** 构建最终 Chat 目标节点(与 ui-conversation 的 chatNode 助手同构)。 */\nfunction chatNode<Kind extends keyof ChatNodeDataMap & string>(\n context: ConversationNodeContext,\n kind: Kind,\n anchorSeq: number,\n data: ChatNodeDataMap[Kind],\n visibility: 'visible' | 'hidden' = 'visible',\n): ChatNode<Kind> {\n return {\n key: context.key,\n kind,\n id: context.id,\n target: 'chat',\n anchorSeq,\n location: context.start?.location ?? context.matches[0]?.location ?? { kind: 'unresolved' },\n visibility,\n data,\n };\n}\n\n/**\n * 插件压缩生命周期定义:compaction/start 开启上下文,summary / 替换检查点 /\n * end 作为 update 折叠证据;仅在存在替换检查点时产出卡片节点。\n */\nexport const omCompactionDefinition: ConversationNodeDefinition<OmCompactionState> = {\n kind: COMPACTION_CARD_KIND,\n target: 'chat',\n match: (event) => {\n const compactionId = checkpointCompactionId(event);\n if (compactionId !== undefined) return { id: compactionId, role: 'update' };\n if (\n event.type === 'compaction/start' ||\n event.type === 'compaction/summary' ||\n event.type === 'compaction/end'\n ) {\n const id: unknown = event.data.compactionId;\n if (typeof id !== 'string' || id === '') return null;\n return { id, role: event.type === 'compaction/start' ? 'start' : 'update' };\n }\n return null;\n },\n start: () => ({}),\n update: (context, match) => {\n if (match.event.type === 'compaction/summary') return { ...context.state, summary: match };\n if (match.event.type === 'compaction/end') return { ...context.state, end: match };\n if (checkpointCompactionId(match.event) !== undefined)\n return { ...context.state, checkpoint: match };\n return context.state;\n },\n buildViewNode: (context) => {\n const state = context.state ?? fallbackState(context);\n // 替换检查点已到达 → 压缩完成,渲染摘要卡片(摘要统计就绪)\n if (state.checkpoint !== undefined) {\n const data = compactSummaryData(state.summary);\n return chatNode(context, COMPACTION_CARD_KIND, state.checkpoint.event.seq, data);\n }\n // end 已到达但无检查点 → 压缩失败/中止:撤回进行中提示行——\n // 已物化节点不能撤回,以 hidden visibility 保留同一 key(聊天视图按可见节点过滤,行从消息流消失)\n if (state.end !== undefined) {\n const anchorSeq = context.start?.event.seq ?? state.end.event.seq;\n return chatNode(\n context,\n COMPACTION_CARD_KIND,\n anchorSeq,\n runningData(context.start),\n 'hidden',\n );\n }\n // start 已到、checkpoint/end 未到 → 压缩进行中,渲染「正在压缩上下文…」提示行\n if (context.start !== undefined) {\n return chatNode(\n context,\n COMPACTION_CARD_KIND,\n context.start.event.seq,\n runningData(context.start),\n );\n }\n return null;\n },\n};\n","/**\n * `om-compaction` namespace dictionaries: 插件压缩卡片的界面文案。\n *\n * 与宿主 conversation 命名空间分离(独立 NS),插件卡片走自己的 keyed 渲染器,\n * 文案键不与对话插件的键冲突。\n */\n\n/** Dictionary namespace owned by this plugin. */\nexport const NS = 'om-compaction';\n\n/** Simplified Chinese dictionary (the key-set source of truth). */\nexport const zh = {\n compaction: '上下文已压缩',\n 'compaction.completed': '已压缩 {items} 条历史记录(约 {tokens} tokens)',\n 'compaction.stats':\n '{items} 条 · {beforeChars} 字符({beforeTokens} tokens)→ {afterChars} 字符({afterTokens} tokens)',\n 'compaction.expand': '点击查看压缩摘要',\n 'compaction.unavailable': '压缩摘要不可用',\n 'compaction.running': '正在压缩上下文…',\n 'compaction.running.observe': '正在压缩上下文(观察)…',\n 'compaction.running.reflect': '正在压缩上下文(反思)…',\n 'compaction.retries': '重试 {count} 次',\n} as const;\n\n/** English dictionary(键集与 zh 一致)。 */\nexport const en: Record<keyof typeof zh, string> = {\n compaction: 'Context compacted',\n 'compaction.completed': 'Compacted {items} messages (~{tokens} tokens)',\n 'compaction.stats':\n '{items} msgs · {beforeChars} chars ({beforeTokens} tokens) → {afterChars} chars ({afterTokens} tokens)',\n 'compaction.expand': 'Click to view summary',\n 'compaction.unavailable': 'Summary unavailable',\n 'compaction.running': 'Compressing context…',\n 'compaction.running.observe': 'Compressing context (observation)…',\n 'compaction.running.reflect': 'Compressing context (reflection)…',\n 'compaction.retries': 'Retried {count} times',\n};\n\ndeclare module '@deepseek-ai/dsh-client-ui-slots' {\n interface LocaleNamespaceMap {\n /** Compaction card copy owned by dsh-plugin-om. */\n 'om-compaction': keyof typeof zh;\n }\n}\n","/**\n * 压缩卡片统计行的紧凑数字格式:<1k 原样;≥1k 用 k;≥1w(万)用 w;≥1M 用 M。\n * 保留 1 位小数并去掉末尾的 .0(如 8642 → 8.6k、34567 → 3.5w、1234567 → 1.2M)。\n */\nexport function formatCompactCount(value: number): string {\n /** 保留 1 位小数并去掉末尾 .0。 */\n const trim = (n: number): string => {\n const text = String(Math.round(n * 10) / 10);\n return text.endsWith('.0') ? text.slice(0, -2) : text;\n };\n if (value >= 1_000_000) return `${trim(value / 1_000_000)}M`;\n if (value >= 10_000) return `${trim(value / 10_000)}w`;\n if (value >= 1_000) return `${trim(value / 1_000)}k`;\n return String(value);\n}\n","/**\n * dsh-plugin-om 压缩卡片渲染器:与宿主「上下文注入」卡片同款的折叠行。\n *\n * 复用平台原语 DisclosureRow(宿主 ContextInjectionRow 用的同一外壳组件);行内\n * 统计与展开体样式按宿主 ContextInjectionRow.module.css 的规则注入——宿主该卡片\n * 的样式是编译期哈希的 CSS Module,插件 bundle 无法引用其类名,故按宿主 client\n * bundle 的同一机制(注入 <style>)注册逐字一致的规则,保证视觉同款。\n *\n * 压缩检查点遮蔽的是模型可见表层,人类转录中的历史仍在其上方;卡片只报告\n * 模型从何处起不再看到那段历史。summary 来自检查点引用的 compaction/summary\n * 事件;窗口裁剪把该事件留在窗外时,行不可展开而不是显示空内容。\n */\n\nimport type { ChatNode } from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport { DisclosureRow, IconBrowseOutline16 } from '@deepseek-ai/dsh-client-ui-primitives';\nimport type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots';\nimport type { CSSProperties } from 'react';\nimport { memo, useState } from 'react';\nimport { formatCompactCount } from './format.ts';\n\n/** Keyed renderer props: 装配好的卡片节点 + 绑定 `om-compaction` 命名空间的 locale 座。 */\nexport interface OmCompactionCardProps {\n readonly node: ChatNode<'om-compaction'>;\n readonly t: TranslateNS<'om-compaction'>;\n}\n\n/** 卡片样式类名(与宿主 ContextInjectionRow.module.css 同款规则,见下方注册)。 */\nconst css = {\n root: 'om-compaction-root',\n chevron: 'om-compaction-chevron',\n sep: 'om-compaction-sep',\n summary: 'om-compaction-summary',\n body: 'om-compaction-body',\n} as const;\n\n/** 宿主 ContextInjectionRow.module.css 的规则(逐字一致,仅类名按插件前缀)。 */\nconst cardCss = `\n.${css.root}{min-width:0}\n.${css.root}[data-open]{padding-bottom:4px}\n.${css.chevron}{color:var(--dsw-alias-label-secondary)}\n.${css.sep}{background:var(--dsw-alias-label-caption);border-radius:1px;flex:none;width:2px;height:2px;margin:0 8px}\n.${css.summary}{min-width:0;color:var(--dsw-alias-label-tertiary);text-overflow:ellipsis;white-space:nowrap;flex:auto;font-size:14px;line-height:24px;overflow:hidden}\n.${css.body}{box-sizing:border-box;background:var(--dsw-alias-markdown-code-block);width:calc(100% - 22px);max-height:141px;color:var(--dsw-alias-label-tertiary);font:400 11px/16px var(--ds-font-family-code);border:none;border-radius:8px;margin:4px 0 0 22px;padding:10px 16px 12px 12px;overflow:auto}\n`;\n\n/** 注册卡片样式(与宿主 client bundle 同机制;无 document 的环境如 node 测试直接跳过)。 */\nif (\n typeof document !== 'undefined' &&\n document.querySelector('style[data-dsh-plugin-om-card]') === null\n) {\n const tag = document.createElement('style');\n tag.dataset.dshPluginOmCard = 'true';\n tag.textContent = cardCss;\n document.head.appendChild(tag);\n}\n\n/** 折叠式压缩卡片(统计标题行 + 代码块样式 summary 展开,同「上下文注入」卡片)。 */\nexport const OmCompactionCard = memo(function OmCompactionCard({ node, t }: OmCompactionCardProps) {\n const [expanded, setExpanded] = useState(false);\n const { data } = node;\n // 压缩进行中:start 已到、checkpoint/end 未到 → 不可展开的「正在压缩上下文…」提示行\n if (data.running) {\n /** 提示行文案(按 phase 区分观察/反思;缺失回退通用文案)。 */\n const runningLabel =\n data.phase === 'observe'\n ? t('compaction.running.observe')\n : data.phase === 'reflect'\n ? t('compaction.running.reflect')\n : t('compaction.running');\n return (\n <div style={styles.row}>\n <DisclosureRow\n className={css.root}\n icon={<IconBrowseOutline16 size={14} />}\n title={runningLabel}\n open={false}\n expandable={false}\n onToggle={() => {\n /* 进行中提示行不可展开 */\n }}\n />\n </div>\n );\n }\n const expandable = data.summary !== null;\n const open = expandable && expanded;\n /** 统计行文案(数字按 k/w/M 单位化;重试次数仅在有重试时追加;retryCount 缺失视为未重试)。 */\n const stats =\n data.shadowedItemCount !== null &&\n data.shadowedCharCount !== null &&\n data.shadowedTokenCount !== null &&\n data.summaryCharCount !== null &&\n data.summaryTokenCount !== null\n ? t('compaction.stats', {\n items: formatCompactCount(data.shadowedItemCount),\n beforeChars: formatCompactCount(data.shadowedCharCount),\n beforeTokens: formatCompactCount(data.shadowedTokenCount),\n afterChars: formatCompactCount(data.summaryCharCount),\n afterTokens: formatCompactCount(data.summaryTokenCount),\n })\n : data.shadowedItemCount !== null && data.shadowedTokenCount !== null\n ? t('compaction.completed', {\n items: formatCompactCount(data.shadowedItemCount),\n tokens: formatCompactCount(data.shadowedTokenCount),\n })\n : expandable\n ? t('compaction.expand')\n : t('compaction.unavailable');\n const retry =\n data.retryCount !== null && data.retryCount > 0\n ? ` \\u00b7 ${t('compaction.retries', { count: data.retryCount })}`\n : '';\n return (\n <div style={styles.row}>\n <DisclosureRow\n className={css.root}\n icon={<IconBrowseOutline16 size={14} />}\n chevronClassName={css.chevron}\n title={t('compaction')}\n collapsedContent={\n <>\n <span className={css.sep} aria-hidden />\n <span className={css.summary} data-om-compaction-summary>\n {stats}\n {retry}\n </span>\n </>\n }\n keepContentWhenOpen\n expandable={expandable}\n expandOnRowClick\n open={open}\n onToggle={() => {\n setExpanded((value) => !value);\n }}\n >\n {data.summary !== null && (\n <div className={css.body} style={styles.body}>\n {data.summary}\n </div>\n )}\n </DisclosureRow>\n </div>\n );\n});\n\nconst styles: Record<string, CSSProperties> = {\n row: {},\n /** 摘要为 <history> 文本,保留换行(宿主 body 的白色空间由内容形态各自处理)。 */\n body: { whiteSpace: 'pre-wrap' },\n};\n","/**\n * dsh-plugin-om 浏览器客户端贡献:压缩卡片。\n *\n * 服务端压缩替换消息继续使用插件自身的检查点标记(source.plugin =\n * 'dsh-plugin-om');宿主 conversation UI 只识别内置的 'compact' 标记,因此本\n * bundle 自行注册一条 conversation 业务定义 + keyed 渲染器,认领插件的压缩\n * 生命周期事件与替换检查点,在消息列表渲染「已压缩」卡片。\n */\nimport type { Context } from '@deepseek-ai/cordis';\n// Type-only: 拉取 locale 插件的 Context 合并(ctx.locale)。\nimport type {} from '@deepseek-ai/dsh-client-locale/client';\n// Type-only: 拉取 runtime 的 Context 合并(ctx.conversationEvents)。\nimport type {} from '@deepseek-ai/dsh-client-runtime/client';\n// Type-only: 'conversation.chat.node' 的 SlotMap 行与 ChatNodeDataMap 声明合并\n// (均由 ui-conversation 声明),注册调用才得以类型化。\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport { omCompactionDefinition } from './definition.ts';\nimport { en, NS, zh } from './locales.ts';\nimport { OmCompactionCard } from './OmCompactionCard.tsx';\n\n/** Required services: 聊天节点槽、会话事件注册表与 locale 服务。 */\nexport const inject = ['slots', 'conversationEvents', 'locale'];\n\n/**\n * Client plugin body: 注册压缩卡片的业务定义与 keyed 渲染器。\n * 注册挂在 effect 包装器上,插件卸载时随之移除。\n * @param ctx - client root context.\n */\nexport function apply(ctx: Context): void {\n ctx.effect(\n () => ctx.locale.register(NS, { zh, en }),\n 'dsh-plugin-om: compaction card dictionaries',\n );\n ctx.effect(\n () => ctx.conversationEvents.register(omCompactionDefinition),\n 'dsh-plugin-om: compaction definition',\n );\n ctx.slots.inject('conversation.chat.node', () =>\n ctx.slots.register(\n { name: 'conversation.chat.node', key: 'om-compaction', locale: NS },\n OmCompactionCard,\n ),\n );\n}\n"],"mappings":";;;;;;;;;;;;ECuBA,MAAa,uBAAuB;;;;;;EA8CpC,SAAS,qBAAqB,OAA8B;GAC1D,MAAM,YAAa,MAAkC;GACrD,OACE,OAAO,cAAc,YACrB,cAAc,QACb,UAA+B,OAAO;EAE3C;;;;;;EAOA,SAAgB,uBAAuB,OAAyC;GAC9E,IAAI,MAAM,SAAS,kBAAkB,CAAC,qBAAqB,KAAK,GAAG,OAAO,KAAA;GAI1E,MAAM,SAHO,MAAM,KAGC;GACpB,IACE,WAAW,KAAA,KACX,OAAO,SAAS,YAChB,OAAO,WAAA,mBACP,OAAO,OAAO,iBAAiB,UAE/B;GAEF,OAAO,OAAO;EAChB;;EAGA,SAAS,mBAAmB,cAAmE;GAC7F,IAAI,UAAyB;GAC7B,IAAI,oBAAmC;GACvC,IAAI,qBAAoC;GACxC,IAAI,oBAAmC;GACvC,IAAI,aAA4B;GAChC,IAAI,cAAc,MAAM,SAAS,sBAAsB;IAErD,MAAM,OAAO,aAAa,MAAM;IAChC,IAAI,MAAM,QAAQ,KAAK,OAAO,GAAG;KAC/B,MAAM,OAAO,KAAK,QAAQ,KAAK,UAAW,MAAM,SAAS,SAAS,MAAM,OAAO,EAAG,CAAC,CAAC,KAAK,EAAE;KAC3F,UAAU,KAAK,KAAK,MAAM,KAAK,OAAO;IACxC;IACA,oBACE,MAAM,QAAQ,KAAK,YAAY,KAC/B,KAAK,aAAa,OAAO,QAAQ,OAAO,cAAc,GAAG,KAAK,OAAO,CAAC,IAClE,KAAK,aAAa,SAClB;IACN,qBACE,OAAO,cAAc,KAAK,kBAAkB,KAAK,KAAK,sBAAsB,IACxE,KAAK,qBACL;IACN,oBACE,KAAK,sBAAsB,KAAA,KAC3B,OAAO,cAAc,KAAK,iBAAiB,KAC3C,KAAK,qBAAqB,IACtB,KAAK,oBACL;IACN,aACE,KAAK,iBAAiB,KAAA,KACtB,OAAO,cAAc,KAAK,YAAY,KACtC,KAAK,gBAAgB,IACjB,KAAK,eACL;GACR;GACA,OAAO;IACL,SAAS;IACT,OAAO;IACP;IACA,iBAAiB,cAAc,MAAM,OAAO;IAC5C;IACA;IACA;IACA,kBAAkB,YAAY,OAAO,OAAO,QAAQ;IACpD,mBAAmB,YAAY,OAAO,OAAO,KAAK,KAAK,QAAQ,SAAS,CAAC;IACzE;GACF;EACF;;EAGA,SAAS,YAAY,OAA4D;;GAE/E,MAAM,OAAO,OAAO,MAAM;GAG1B,OAAO;IACL,SAAS;IACT,OAHY,MAAM,UAAU,aAAa,MAAM,UAAU,YAAY,KAAK,QAAQ;IAIlF,SAAS;IACT,iBAAiB;IACjB,mBAAmB;IACnB,oBAAoB;IACpB,mBAAmB;IACnB,kBAAkB;IAClB,mBAAmB;IACnB,YAAY;GACd;EACF;;EAGA,SAAS,cAAc,SAAwE;GAC7F,MAAM,UAAU,QAAQ,QAAQ,MAAM,UAAU,MAAM,MAAM,SAAS,oBAAoB;GACzF,MAAM,aAAa,QAAQ,QAAQ,MAChC,UAAU,uBAAuB,MAAM,KAAK,MAAM,KAAA,CACrD;GACA,MAAM,MAAM,QAAQ,QAAQ,MAAM,UAAU,MAAM,MAAM,SAAS,gBAAgB;GACjF,OAAO;IACL,GAAI,YAAY,KAAA,IAAY,CAAC,IAAI,EAAE,QAAQ;IAC3C,GAAI,eAAe,KAAA,IAAY,CAAC,IAAI,EAAE,WAAW;IACjD,GAAI,QAAQ,KAAA,IAAY,CAAC,IAAI,EAAE,IAAI;GACrC;EACF;;EAGA,SAAS,SACP,SACA,MACA,WACA,MACA,aAAmC,WACnB;GAChB,OAAO;IACL,KAAK,QAAQ;IACb;IACA,IAAI,QAAQ;IACZ,QAAQ;IACR;IACA,UAAU,QAAQ,OAAO,YAAY,QAAQ,QAAQ,EAAE,EAAE,YAAY,EAAE,MAAM,aAAa;IAC1F;IACA;GACF;EACF;;;;;EAMA,MAAa,yBAAwE;GACnF,MAAM;GACN,QAAQ;GACR,QAAQ,UAAU;IAChB,MAAM,eAAe,uBAAuB,KAAK;IACjD,IAAI,iBAAiB,KAAA,GAAW,OAAO;KAAE,IAAI;KAAc,MAAM;IAAS;IAC1E,IACE,MAAM,SAAS,sBACf,MAAM,SAAS,wBACf,MAAM,SAAS,kBACf;KACA,MAAM,KAAc,MAAM,KAAK;KAC/B,IAAI,OAAO,OAAO,YAAY,OAAO,IAAI,OAAO;KAChD,OAAO;MAAE;MAAI,MAAM,MAAM,SAAS,qBAAqB,UAAU;KAAS;IAC5E;IACA,OAAO;GACT;GACA,cAAc,CAAC;GACf,SAAS,SAAS,UAAU;IAC1B,IAAI,MAAM,MAAM,SAAS,sBAAsB,OAAO;KAAE,GAAG,QAAQ;KAAO,SAAS;IAAM;IACzF,IAAI,MAAM,MAAM,SAAS,kBAAkB,OAAO;KAAE,GAAG,QAAQ;KAAO,KAAK;IAAM;IACjF,IAAI,uBAAuB,MAAM,KAAK,MAAM,KAAA,GAC1C,OAAO;KAAE,GAAG,QAAQ;KAAO,YAAY;IAAM;IAC/C,OAAO,QAAQ;GACjB;GACA,gBAAgB,YAAY;IAC1B,MAAM,QAAQ,QAAQ,SAAS,cAAc,OAAO;IAEpD,IAAI,MAAM,eAAe,KAAA,GAAW;KAClC,MAAM,OAAO,mBAAmB,MAAM,OAAO;KAC7C,OAAO,SAAS,SAAS,sBAAsB,MAAM,WAAW,MAAM,KAAK,IAAI;IACjF;IAGA,IAAI,MAAM,QAAQ,KAAA,GAAW;KAC3B,MAAM,YAAY,QAAQ,OAAO,MAAM,OAAO,MAAM,IAAI,MAAM;KAC9D,OAAO,SACL,SACA,sBACA,WACA,YAAY,QAAQ,KAAK,GACzB,QACF;IACF;IAEA,IAAI,QAAQ,UAAU,KAAA,GACpB,OAAO,SACL,SACA,sBACA,QAAQ,MAAM,MAAM,KACpB,YAAY,QAAQ,KAAK,CAC3B;IAEF,OAAO;GACT;EACF;;;;;;;;;;EC/PA,MAAa,KAAK;;EAGlB,MAAa,KAAK;GAChB,YAAY;GACZ,wBAAwB;GACxB,oBACE;GACF,qBAAqB;GACrB,0BAA0B;GAC1B,sBAAsB;GACtB,8BAA8B;GAC9B,8BAA8B;GAC9B,sBAAsB;EACxB;;EAGA,MAAa,KAAsC;GACjD,YAAY;GACZ,wBAAwB;GACxB,oBACE;GACF,qBAAqB;GACrB,0BAA0B;GAC1B,sBAAsB;GACtB,8BAA8B;GAC9B,8BAA8B;GAC9B,sBAAsB;EACxB;;;;;;;EChCA,SAAgB,mBAAmB,OAAuB;;GAExD,MAAM,QAAQ,MAAsB;IAClC,MAAM,OAAO,OAAO,KAAK,MAAM,IAAI,EAAE,IAAI,EAAE;IAC3C,OAAO,KAAK,SAAS,IAAI,IAAI,KAAK,MAAM,GAAG,EAAE,IAAI;GACnD;GACA,IAAI,SAAS,KAAW,OAAO,GAAG,KAAK,QAAQ,GAAS,EAAE;GAC1D,IAAI,SAAS,KAAQ,OAAO,GAAG,KAAK,QAAQ,GAAM,EAAE;GACpD,IAAI,SAAS,KAAO,OAAO,GAAG,KAAK,QAAQ,GAAK,EAAE;GAClD,OAAO,OAAO,KAAK;EACrB;;;;ECaA,MAAM,MAAM;GACV,MAAM;GACN,SAAS;GACT,KAAK;GACL,SAAS;GACT,MAAM;EACR;;EAGA,MAAM,UAAU;GACb,IAAI,KAAK;GACT,IAAI,KAAK;GACT,IAAI,QAAQ;GACZ,IAAI,IAAI;GACR,IAAI,QAAQ;GACZ,IAAI,KAAK;;;EAIZ,IACE,OAAO,aAAa,eACpB,SAAS,cAAc,gCAAgC,MAAM,MAC7D;GACA,MAAM,MAAM,SAAS,cAAc,OAAO;GAC1C,IAAI,QAAQ,kBAAkB;GAC9B,IAAI,cAAc;GAClB,SAAS,KAAK,YAAY,GAAG;EAC/B;;EAGA,MAAa,oBAAA,GAAmBA,MAAAA,KAAAA,CAAK,SAAS,iBAAiB,EAAE,MAAM,KAA4B;GACjG,MAAM,CAAC,UAAU,gBAAA,GAAeC,MAAAA,SAAAA,CAAS,KAAK;GAC9C,MAAM,EAAE,SAAS;GAEjB,IAAI,KAAK,SAAS;;IAEhB,MAAM,eACJ,KAAK,UAAU,YACX,EAAE,4BAA4B,IAC9B,KAAK,UAAU,YACb,EAAE,4BAA4B,IAC9B,EAAE,oBAAoB;IAC9B,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;KAAK,OAAO,OAAO;KACjB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,eAAD;MACE,WAAW,IAAI;MACf,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,qBAAD,EAAqB,MAAM,GAAK,CAAA;MACtC,OAAO;MACP,MAAM;MACN,YAAY;MACZ,gBAAgB,CAEhB;KACD,CAAA;IACE,CAAA;GAET;GACA,MAAM,aAAa,KAAK,YAAY;GACpC,MAAM,OAAO,cAAc;;GAE3B,MAAM,QACJ,KAAK,sBAAsB,QAC3B,KAAK,sBAAsB,QAC3B,KAAK,uBAAuB,QAC5B,KAAK,qBAAqB,QAC1B,KAAK,sBAAsB,OACvB,EAAE,oBAAoB;IACpB,OAAO,mBAAmB,KAAK,iBAAiB;IAChD,aAAa,mBAAmB,KAAK,iBAAiB;IACtD,cAAc,mBAAmB,KAAK,kBAAkB;IACxD,YAAY,mBAAmB,KAAK,gBAAgB;IACpD,aAAa,mBAAmB,KAAK,iBAAiB;GACxD,CAAC,IACD,KAAK,sBAAsB,QAAQ,KAAK,uBAAuB,OAC7D,EAAE,wBAAwB;IACxB,OAAO,mBAAmB,KAAK,iBAAiB;IAChD,QAAQ,mBAAmB,KAAK,kBAAkB;GACpD,CAAC,IACD,aACE,EAAE,mBAAmB,IACrB,EAAE,wBAAwB;GACpC,MAAM,QACJ,KAAK,eAAe,QAAQ,KAAK,aAAa,IAC1C,WAAW,EAAE,sBAAsB,EAAE,OAAO,KAAK,WAAW,CAAC,MAC7D;GACN,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,OAAO,OAAO;IACjB,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACD,sCAAAA,eAAD;KACE,WAAW,IAAI;KACf,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,qBAAD,EAAqB,MAAM,GAAK,CAAA;KACtC,kBAAkB,IAAI;KACtB,OAAO,EAAE,YAAY;KACrB,kBACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,WAAW,IAAI;MAAK,eAAA;KAAa,CAAA,GACvC,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;MAAM,WAAW,IAAI;MAAS,8BAAA;MAA9B,UAAA,CACG,OACA,KACG;KACN,CAAA,CAAA,EAAA,CAAA;KAEJ,qBAAA;KACY;KACZ,kBAAA;KACM;KACN,gBAAgB;MACd,aAAa,UAAU,CAAC,KAAK;KAC/B;KAEC,UAAA,KAAK,YAAY,QAChB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,WAAW,IAAI;MAAM,OAAO,OAAO;MACrC,UAAA,KAAK;KACH,CAAA;IAEM,CAAA;GACZ,CAAA;EAET,CAAC;EAED,MAAM,SAAwC;GAC5C,KAAK,CAAC;;GAEN,MAAM,EAAE,YAAY,WAAW;EACjC;;;;ECjIA,MAAa,SAAS;GAAC;GAAS;GAAsB;EAAQ;;;;;;EAO9D,SAAgB,MAAM,KAAoB;GACxC,IAAI,aACI,IAAI,OAAO,SAAS,IAAI;IAAE;IAAI;GAAG,CAAC,GACxC,6CACF;GACA,IAAI,aACI,IAAI,mBAAmB,SAAS,sBAAsB,GAC5D,sCACF;GACA,IAAI,MAAM,OAAO,gCACf,IAAI,MAAM,SACR;IAAE,MAAM;IAA0B,KAAK;IAAiB,QAAQ;GAAG,GACnE,gBACF,CACF;EACF"}
@@ -1,7 +1,18 @@
1
- import type { Agent, Context, PluginConfig, Session } from './types.ts';
1
+ import type { Agent, CompleteMessage, Context, PluginConfig, Session } from './types.ts';
2
2
  import { type RoutedTarget } from './utils.ts';
3
3
  /** 历史文本 token 估算:4 字符 ≈ 1 token(与宿主 dsh-token-meter 启发式一致)。 */
4
4
  export declare function estimateTextTokens(text: string): number;
5
+ /**
6
+ * 按 token 边界把完整消息序列分块(观察并行压缩用):完整消息不跨块——
7
+ * 一条完整消息(含其 thinking/text/toolcall&result 全部 seqs)必然整体落在同一块,
8
+ * 单条超界的消息独立成块。空输入返回空数组。
9
+ */
10
+ export declare function chunkCompleteMessages<M>(session: Session, cms: readonly CompleteMessage[], tokenBoundary: number, estimateMessage: (message: M) => number): CompleteMessage[][];
11
+ /**
12
+ * 合并分块摘要为单个 <history> 块:剥离各块的 <history> 开/闭标签(保留块内全部内容),
13
+ * 以统一的开标签(带 tip 属性)与闭标签包裹拼接。块内内容原样保留(含格式说明注释)。
14
+ */
15
+ export declare function mergeChunkReports(parts: readonly string[]): string;
5
16
  /** token 估算器的结构类型(仅需 estimateMessage;避免依赖完整 TokenMeter 接口)。 */
6
17
  export type TokenEstimator = {
7
18
  estimateMessage(message: unknown): number;
@@ -43,16 +54,16 @@ export declare function historySection(session: Session): {
43
54
  boundarySeq: number | undefined;
44
55
  };
45
56
  /**
46
- * 反思:全部 <history> 块 tokens 合计 ≥ 窗口 × historyMergeRatio 时,摘要调用
57
+ * 反思:全部 <history> 块 tokens 合计 ≥ reflectThresholdTokens 时,摘要调用
47
58
  * 精简合并(多个块拼接为输入,与观察共用同一套提示词),把整个块区段替换为一条
48
59
  * 更紧凑的摘要。失败不产生部分替换。
49
60
  */
50
- export declare function reflectPass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, window: number, target: RoutedTarget, signal?: AbortSignal): Promise<void>;
61
+ export declare function reflectPass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, target: RoutedTarget, signal?: AbortSignal): Promise<void>;
51
62
  /**
52
- * 观察:未压缩消息 tokens ≥ 窗口 × thresholdRatio 时,摘要调用把未压缩消息压缩为
63
+ * 观察:未压缩消息 tokens ≥ observeThresholdTokens 时,摘要调用把未压缩消息压缩为
53
64
  * 观察日志,追加到旧摘要并替换被压缩消息区间。失败不产生部分替换。
54
65
  */
55
- export declare function observePass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, window: number, tailCount: number, target: RoutedTarget, signal?: AbortSignal): Promise<void>;
66
+ export declare function observePass(ctx: Context, agent: Agent, config: Readonly<PluginConfig>, tailCount: number, target: RoutedTarget, signal?: AbortSignal): Promise<void>;
56
67
  /**
57
68
  * 压力检查 + 两级压缩:先反思(压缩过往摘要,有必要才做),后观察(压缩新消息,
58
69
  * 有必要才做)。在 pre-step 阻塞串行执行(避免压缩失败或重复压缩)。仅主会话生效。
@@ -1 +1 @@
1
- {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,KAAK,EAIL,OAAO,EACP,YAAY,EACZ,OAAO,EAIR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAgB,KAAK,YAAY,EAAqC,MAAM,YAAY,CAAC;AAEhG,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAoDD,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,CAAC;AAE3E;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAyBzF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAc1E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CA8BpE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG;IAChD,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAaA;AA8GD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA8Hf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAwJf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAwCf"}
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAsCA,OAAO,KAAK,EACV,KAAK,EAIL,eAAe,EACf,OAAO,EACP,YAAY,EACZ,OAAO,EAIR,MAAM,YAAY,CAAC;AACpB,OAAO,EAAgB,KAAK,YAAY,EAAqC,MAAM,YAAY,CAAC;AAEhG,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,EACrC,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,SAAS,eAAe,EAAE,EAC/B,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,MAAM,GACtC,eAAe,EAAE,EAAE,CA6BrB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAiBlE;AAyED,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,CAAC;AAE3E;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAyBzF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAc1E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CA8BpE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG;IAChD,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAaA;AA8GD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA8Hf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA0Lf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAyBf"}
package/dist/config.d.ts CHANGED
@@ -1,11 +1,15 @@
1
1
  /** 插件配置项(全部可选覆盖,未给出或留空的键用默认值)。 */
2
2
  export type PluginConfig = {
3
- /** 压力阈值比例:压力窗口 × 该比例时触发自动压缩。 */
4
- thresholdRatio: number;
5
- /** 反思阈值比例:摘要(<history> 内容)≥ 窗口 × 该比例时由反思摘要调用精简合并。 */
6
- historyMergeRatio: number;
3
+ /** 观察阈值(tokens):未压缩消息 tokens 该值时触发观察压缩。 */
4
+ observeThresholdTokens: number;
5
+ /** 反思阈值(tokens):全部 <history> tokens 合计 该值时由反思摘要调用精简合并。 */
6
+ reflectThresholdTokens: number;
7
7
  /** 单次摘要(合并调用)生成上限(LLM maxTokens)。 */
8
8
  compressMaxTokens: number;
9
+ /** 观察分块 token 边界:未压缩消息按该边界拆分为多块并行压缩(完整消息不跨块)。 */
10
+ observeChunkTokens: number;
11
+ /** 观察分块单块摘要的生成上限(LLM maxTokens;每块独立调用)。 */
12
+ observeChunkMaxTokens: number;
9
13
  /** 压缩边界:其后不压缩消息数下限(正整数,尾部保留)。 */
10
14
  tailMessageCount: number;
11
15
  /** 摘要调用失败后的最大重试次数(不含首次尝试;总尝试次数 = 该值 + 1,默认 11)。 */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,qDAAqD;IACrD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,YAAY,CAWhD,CAAC;AAmCH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CA4BnE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAOA,kCAAkC;AAClC,MAAM,MAAM,YAAY,GAAG;IACzB,6CAA6C;IAC7C,sBAAsB,EAAE,MAAM,CAAC;IAC/B,8DAA8D;IAC9D,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qCAAqC;IACrC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iDAAiD;IACjD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,2CAA2C;IAC3C,qBAAqB,EAAE,MAAM,CAAC;IAC9B,iCAAiC;IACjC,gBAAgB,EAAE,MAAM,CAAC;IACzB,mDAAmD;IACnD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,wDAAwD;IACxD,SAAS,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,KAAK,EAAE,OAAO,CAAC;IACf,0CAA0C;IAC1C,aAAa,EAAE,OAAO,CAAC;IACvB,2DAA2D;IAC3D,qBAAqB,EAAE,OAAO,CAAC;IAC/B,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,iEAAiE;AACjE,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,YAAY,CAahD,CAAC;AAuCH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC,YAAY,CAAC,CA4BnE"}
package/dist/index.d.ts CHANGED
@@ -8,8 +8,8 @@
8
8
  * 按语义在全部完整消息(含被压缩/遮蔽)中检索,返回最匹配的完整消息与匹配说明
9
9
  * (本地 ONNX embedding,模型随插件打包,懒加载)
10
10
  * - compress.ts 自动压缩(OM 观察/反思两级阈值):pre-step 阻塞串行执行——
11
- * 反思(摘要 窗口 × historyMergeRatio 时摘要调用精简合并 <history>)、
12
- * 观察(未压缩消息 ≥ 窗口 × thresholdRatio 时摘要调用压缩为观察日志并追加)
11
+ * 反思(<history> tokens 合计 reflectThresholdTokens 时摘要调用精简合并)、
12
+ * 观察(未压缩消息 tokens observeThresholdTokens 时摘要调用压缩为观察日志并追加)
13
13
  *
14
14
  * 约束:不引入自定义会话事件类型——压缩复用宿主已知的 compaction/* 生命周期事件
15
15
  * (start/summary/end)与 checkpoint 标记,结果写入消息记录与轨迹。
package/dist/index.mjs CHANGED
@@ -7412,9 +7412,11 @@ var import_lib = (/* @__PURE__ */ __commonJSMin(((exports) => {
7412
7412
  * 共享压缩提示词(观察/反思同一套):定义 history 块(模型消息 + index 的表达形式)、
7413
7413
  * 完整消息定义、要求压缩(完整保留用户消息 / reasoning 仅参考 / 关联 assistant 合并 /
7414
7414
  * index/start/end 连续)、输出格式(一个合法 <history> 块,无 reasoning)、数据源说明。
7415
+ * mode 控制【摘要粒度】:无参为通用提示词(反思调用);'summary' 要求简单摘要(较早分块);
7416
+ * 'detailed' 要求越往后越细(最后一块保留细节)。观察分块时除最后一块外均用 'summary'。
7415
7417
  */
7416
- function buildHistoryPrompt() {
7417
- return [
7418
+ function buildHistoryPrompt(mode) {
7419
+ const lines = [
7418
7420
  "把下方的 <history> 消息记录压缩为一份更紧凑的 <history> 压缩日志。不用工具、不展示思考、不输出多余文字。",
7419
7421
  "",
7420
7422
  "【history 块定义】",
@@ -7450,7 +7452,14 @@ function buildHistoryPrompt() {
7450
7452
  `</${HISTORY_TAG}>`,
7451
7453
  "",
7452
7454
  "【数据源】下方的 <history> 消息记录是本次要压缩的全部消息;压缩结果作为一个新的 <history> 块输出。"
7453
- ].join("\n");
7455
+ ];
7456
+ if (mode !== void 0) {
7457
+ const granularity = mode === "summary" ? "【摘要粒度】本条为较早的消息,做简单摘要即可:概括要点,不必展开细节;但用户消息仍逐条保留原文,不概括、不省略。" : "【摘要粒度】本条为最近的消息,越往后越细:靠近末尾的完整消息保留更多细节(关键文件、改动与结论),前面的可适当从简;但用户消息始终逐条保留原文,不概括、不省略。";
7458
+ const marker = "【输出格式】只输出一个 <history> 包裹的合法 XML 日志块";
7459
+ const markerIndex = lines.findIndex((line) => line.includes(marker));
7460
+ if (markerIndex !== -1) lines.splice(markerIndex, 0, granularity);
7461
+ }
7462
+ return lines.join("\n");
7454
7463
  }
7455
7464
  /** 渲染用户消息条目(DOM 元素):文本块原样;图片/文件等非文本块以注释补充(说明传入了什么)。 */
7456
7465
  function renderUserEntry(doc, session, cm) {
@@ -7859,11 +7868,11 @@ async function runSummarySubagent(ctx, agent, instruction, contextText, maxToken
7859
7868
  //#region src/compress.ts
7860
7869
  /**
7861
7870
  * 自动压缩(OM 观察/反思两级阈值,思路参考 Mastra Observational Memory):
7862
- * - 观察:未压缩消息 tokens ≥ 窗口 × thresholdRatio → 直连 ctx.llm.stream() 摘要
7863
- * new 方式:共享提示词作为 system、被压缩消息渲染为 <history> 块输入)把未压缩消息
7871
+ * - 观察:未压缩消息 tokens ≥ observeThresholdTokens(默认 100000)→ 直连 ctx.llm.stream()
7872
+ * 摘要(new 方式:共享提示词作为 system、被压缩消息渲染为 <history> 块输入)把未压缩消息
7864
7873
  * 压缩为观察日志,作为独立的新 <history> 块,只精确替换被压缩的新消息区间(旧块原地
7865
7874
  * 保留,多块并存按序排列;不再把旧+新合并进一条消息);
7866
- * - 反思:全部 <history> 块 tokens 合计 ≥ 窗口 × historyMergeRatio(默认 0.2)→
7875
+ * - 反思:全部 <history> 块 tokens 合计 ≥ reflectThresholdTokens(默认 30000)→
7867
7876
  * 同上摘要调用精简合并(输入为多个块拼接,共用同一套提示词),把整个块区段合并为一条。
7868
7877
  * 两级检查在 pre-step 阻塞串行执行(先反思后观察),避免压缩失败或重复压缩。
7869
7878
  * 自动压缩由配置键 omEnabled 开关(false 时关闭;recall 工具不受影响)。
@@ -7890,6 +7899,79 @@ function estimateTextTokens(text) {
7890
7899
  return Math.ceil(text.length / 4);
7891
7900
  }
7892
7901
  /**
7902
+ * 按 token 边界把完整消息序列分块(观察并行压缩用):完整消息不跨块——
7903
+ * 一条完整消息(含其 thinking/text/toolcall&result 全部 seqs)必然整体落在同一块,
7904
+ * 单条超界的消息独立成块。空输入返回空数组。
7905
+ */
7906
+ function chunkCompleteMessages(session, cms, tokenBoundary, estimateMessage) {
7907
+ if (cms.length === 0) return [];
7908
+ /** 分块结果。 */
7909
+ const chunks = [];
7910
+ /** 当前块。 */
7911
+ let current = [];
7912
+ /** 当前块累计 token(按各完整消息 seqs 的派生消息估算合计)。 */
7913
+ let currentTokens = 0;
7914
+ for (const cm of cms) {
7915
+ /** 当前完整消息的 token 估算(其全部表层 seqs 的派生消息估算之和)。 */
7916
+ let cmTokens = 0;
7917
+ for (const seq of cm.seqs) {
7918
+ /** 表层事件(seq 缺失跳过)。 */
7919
+ const event = session.events[seq];
7920
+ /** 派生消息(用于 token 估算)。 */
7921
+ const message = event ? session.deriveEventMessage(event) : null;
7922
+ if (message) cmTokens += estimateMessage(message);
7923
+ }
7924
+ if (current.length > 0 && currentTokens + cmTokens > tokenBoundary) {
7925
+ chunks.push(current);
7926
+ current = [];
7927
+ currentTokens = 0;
7928
+ }
7929
+ current.push(cm);
7930
+ currentTokens += cmTokens;
7931
+ }
7932
+ if (current.length > 0) chunks.push(current);
7933
+ return chunks;
7934
+ }
7935
+ /**
7936
+ * 合并分块摘要为单个 <history> 块:剥离各块的 <history> 开/闭标签(保留块内全部内容),
7937
+ * 以统一的开标签(带 tip 属性)与闭标签包裹拼接。块内内容原样保留(含格式说明注释)。
7938
+ */
7939
+ function mergeChunkReports(parts) {
7940
+ /** 各块内层内容(剥离外壳失败时原样保留该块文本)。 */
7941
+ const inners = [];
7942
+ for (const part of parts) {
7943
+ /** 首个 <history 开标签位置。 */
7944
+ const open = part.indexOf(`<${HISTORY_TAG}`);
7945
+ /** 开标签右括号位置。 */
7946
+ const gt = open === -1 ? -1 : part.indexOf(">", open);
7947
+ /** 最后一个 </history> 闭标签位置。 */
7948
+ const close = part.lastIndexOf(`</${HISTORY_TAG}>`);
7949
+ if (open === -1 || gt === -1 || close === -1 || close <= gt) {
7950
+ inners.push(part);
7951
+ continue;
7952
+ }
7953
+ inners.push(part.slice(gt + 1, close));
7954
+ }
7955
+ return `<${HISTORY_TAG} tip="${HISTORY_TIP}">\n${inners.join("\n").trim()}\n</${HISTORY_TAG}>`;
7956
+ }
7957
+ /** 合并多块摘要的 token usage(同名数字字段求和;全部为空返回 undefined)。 */
7958
+ function mergeUsage(usages) {
7959
+ if (usages.length === 0) return void 0;
7960
+ /** 合并结果(必填字段从 0 起累加)。 */
7961
+ const out = {
7962
+ inputTokens: 0,
7963
+ outputTokens: 0
7964
+ };
7965
+ for (const u of usages) {
7966
+ out.inputTokens += u.inputTokens;
7967
+ out.outputTokens += u.outputTokens;
7968
+ if (u.cacheReadTokens !== void 0) out.cacheReadTokens = (out.cacheReadTokens ?? 0) + u.cacheReadTokens;
7969
+ if (u.cacheWriteTokens !== void 0) out.cacheWriteTokens = (out.cacheWriteTokens ?? 0) + u.cacheWriteTokens;
7970
+ if (u.reasoningTokens !== void 0) out.reasoningTokens = (out.reasoningTokens ?? 0) + u.reasoningTokens;
7971
+ }
7972
+ return out;
7973
+ }
7974
+ /**
7893
7975
  * 反思输入块引用的最大完整消息 index(解析全部条目取最大 end;无条目返回 -1)。
7894
7976
  * 反思输出必须覆盖输入引用的完整 index 区间(0..max),连续性校验据此约束。
7895
7977
  */
@@ -8124,24 +8206,24 @@ function appendHistoryMessage(session, content, sourceEventSeqs, surfaceOp, comp
8124
8206
  });
8125
8207
  }
8126
8208
  /**
8127
- * 反思:全部 <history> 块 tokens 合计 ≥ 窗口 × historyMergeRatio 时,摘要调用
8209
+ * 反思:全部 <history> 块 tokens 合计 ≥ reflectThresholdTokens 时,摘要调用
8128
8210
  * 精简合并(多个块拼接为输入,与观察共用同一套提示词),把整个块区段替换为一条
8129
8211
  * 更紧凑的摘要。失败不产生部分替换。
8130
8212
  */
8131
- async function reflectPass(ctx, agent, config, window, target, signal) {
8213
+ async function reflectPass(ctx, agent, config, target, signal) {
8132
8214
  /** 当前会话。 */
8133
8215
  const session = agent.session;
8134
8216
  /** 插件日志门面。 */
8135
8217
  const logger = makeLogger(ctx, config.debug);
8136
- logger.step(`反思检查(窗口 ${window} × historyMergeRatio ${config.historyMergeRatio})`);
8218
+ logger.step(`反思检查(反思阈值 ${config.reflectThresholdTokens} tokens)`);
8137
8219
  /** 全部 <history> 压缩日志块(按表层顺序;无则跳过)。 */
8138
8220
  const { blocks } = historySection(session);
8139
8221
  if (blocks.length === 0) {
8140
8222
  logger.step("反思:无 <history> 压缩日志,跳过");
8141
8223
  return;
8142
8224
  }
8143
- /** 反思阈值(窗口 × historyMergeRatio 向下取整)。 */
8144
- const threshold = Math.floor(window * config.historyMergeRatio);
8225
+ /** 反思阈值(配置的绝对 token 数)。 */
8226
+ const threshold = config.reflectThresholdTokens;
8145
8227
  /** 全部块 token 估算合计(摘要总长)。 */
8146
8228
  const tokens = blocks.reduce((total, block) => total + estimateTextTokens(block.text), 0);
8147
8229
  /** 被压缩块区段的字符数合计(压缩前内文长度;UI 标题统计用,与观察路径一致只计内容不计标签)。 */
@@ -8213,7 +8295,7 @@ async function reflectPass(ctx, agent, config, window, target, signal) {
8213
8295
  provider: target.provider,
8214
8296
  model: target.model,
8215
8297
  maxTokens: config.compressMaxTokens,
8216
- attemptCount: summaryResult.attemptCount,
8298
+ attemptCount: summaryResult.attemptCount - 1,
8217
8299
  ...summaryResult.usage === void 0 ? {} : { usage: summaryResult.usage }
8218
8300
  });
8219
8301
  logger.step("反思提交:替换整个 <history> 块区段为合并摘要");
@@ -8235,17 +8317,17 @@ async function reflectPass(ctx, agent, config, window, target, signal) {
8235
8317
  }
8236
8318
  }
8237
8319
  /**
8238
- * 观察:未压缩消息 tokens ≥ 窗口 × thresholdRatio 时,摘要调用把未压缩消息压缩为
8320
+ * 观察:未压缩消息 tokens ≥ observeThresholdTokens 时,摘要调用把未压缩消息压缩为
8239
8321
  * 观察日志,追加到旧摘要并替换被压缩消息区间。失败不产生部分替换。
8240
8322
  */
8241
- async function observePass(ctx, agent, config, window, tailCount, target, signal) {
8323
+ async function observePass(ctx, agent, config, tailCount, target, signal) {
8242
8324
  /** 当前会话。 */
8243
8325
  const session = agent.session;
8244
8326
  /** 插件日志门面。 */
8245
8327
  const logger = makeLogger(ctx, config.debug);
8246
- logger.step(`观察检查(窗口 ${window} × thresholdRatio ${config.thresholdRatio},尾部保留 ${tailCount} 条)`);
8247
- /** 观察阈值(窗口 × thresholdRatio 向下取整)。 */
8248
- const threshold = Math.floor(window * config.thresholdRatio);
8328
+ logger.step(`观察检查(观察阈值 ${config.observeThresholdTokens} tokens,尾部保留 ${tailCount} 条)`);
8329
+ /** 观察阈值(配置的绝对 token 数)。 */
8330
+ const threshold = config.observeThresholdTokens;
8249
8331
  /** 未压缩消息 token 估算(最后一个 <history> 块之后;其前视为已压缩)。 */
8250
8332
  const uncompressedTokens = measureUncompressedTokens(session, ctx.tokenMeter);
8251
8333
  if (uncompressedTokens < threshold) {
@@ -8282,10 +8364,9 @@ async function observePass(ctx, agent, config, window, tailCount, target, signal
8282
8364
  const startIndex = inRangeCms[0]?.index ?? 0;
8283
8365
  const endIndex = inRangeCms[inRangeCms.length - 1]?.index ?? startIndex;
8284
8366
  logger.step(`观察:保留旧块 ${blocks.length} 条,替换新消息 [${replaceSeqs[0]}..${range.end}](${replaceSeqs.length} 条),尾部保留 ${tailCount} 条(不压缩、不进日志),新消息 index ${startIndex}..${endIndex}`);
8285
- /** 共享提示词(观察/反思同一套)。 */
8286
- const instruction = buildHistoryPrompt();
8287
- /** 渲染输入:本次要压缩的完整消息(合法 <history> 块,含绝对 index;不含尾部;系统消息渲染为 <sys> 空块,本插件自产消息不占位)。 */
8288
- const contextText = renderMessages(session, replaceSeqs);
8367
+ /** observeChunkTokens 边界把完整消息分块(完整消息不跨块;每块独立并行压缩)。 */
8368
+ const chunks = chunkCompleteMessages(session, inRangeCms, config.observeChunkTokens, (message) => ctx.tokenMeter.estimateMessage(message));
8369
+ logger.step(`观察:${inRangeCms.length} 条完整消息按 ${config.observeChunkTokens} tokens 边界分为 ${chunks.length} 块(每块摘要 maxTokens ${config.observeChunkMaxTokens},除最后一块外要求简单摘要)`);
8289
8370
  /** 本次压缩生命周期(compactionId + 当前轮次;start 在摘要调用前开启,UI 压缩中提示)。 */
8290
8371
  const lifecycle = {
8291
8372
  compactionId: newCompactionId(),
@@ -8300,23 +8381,38 @@ async function observePass(ctx, agent, config, window, tailCount, target, signal
8300
8381
  logger.warn(`观察压缩启动失败: ${message}`);
8301
8382
  return;
8302
8383
  }
8303
- /** 观察摘要结果(null 表示失败/跳过)。 */
8304
- const summaryResult = await runSummarySubagent(ctx, agent, instruction, contextText, config.compressMaxTokens, target, config.debug, signal, {
8305
- maxAttempts: config.compressRetryCount + 1,
8306
- expected: {
8307
- start: startIndex,
8308
- end: endIndex
8309
- }
8310
- });
8311
- if (summaryResult === null || summaryResult.text.trim().length === 0) {
8312
- logger.step("观察:摘要调用失败/无输出,追加 compaction/end(error)");
8384
+ /** 各块摘要结果(Promise.all 并行;null = 该块失败)。 */
8385
+ const chunkResults = await Promise.all(chunks.map(async (chunk, index) => {
8386
+ /** 该块提示词(较早块简单摘要;最后一块越往后越细)。 */
8387
+ const instruction = buildHistoryPrompt(index === chunks.length - 1 ? "detailed" : "summary");
8388
+ /** 该块渲染输入(块内完整消息的 seqs;合法 <history> 块,含绝对 index)。 */
8389
+ const contextText = renderMessages(session, chunk.flatMap((cm) => cm.seqs));
8390
+ /** 该块覆盖的完整消息 index 区间(连续性校验预期)。 */
8391
+ const start = chunk[0]?.index ?? 0;
8392
+ const end = chunk[chunk.length - 1]?.index ?? start;
8393
+ return runSummarySubagent(ctx, agent, instruction, contextText, config.observeChunkMaxTokens, target, config.debug, signal, {
8394
+ maxAttempts: config.compressRetryCount + 1,
8395
+ expected: {
8396
+ start,
8397
+ end
8398
+ }
8399
+ });
8400
+ }));
8401
+ if (chunkResults.find((r) => r === null || r.text.trim().length === 0) !== void 0) {
8402
+ logger.step("观察:分块摘要存在失败/无输出,不产生部分替换,追加 compaction/end(error)");
8313
8403
  try {
8314
8404
  appendCompactionEnd(session, lifecycle, "摘要调用失败/无输出");
8315
8405
  } catch {}
8316
8406
  return;
8317
8407
  }
8318
- /** 观察摘要文本(独立新块;旧块保留,不再合并)。 */
8319
- const report = summaryResult.text;
8408
+ /** 成功结果(上方已排除 null/空输出;每块已独立校验并规范化)。 */
8409
+ const okResults = chunkResults;
8410
+ /** 合并各块摘要为单个带 tip 的 <history> 块(剥离各块外壳拼接内层;不再整体校验)。 */
8411
+ const report = mergeChunkReports(okResults.map((r) => r.text));
8412
+ /** 重试次数合计(各块重试次数之和;每块重试次数 = 该块成功尝试次数 - 1)。 */
8413
+ const attemptCount = okResults.reduce((sum, r) => sum + (r.attemptCount - 1), 0);
8414
+ /** token usage 合计(各块求和;全部缺省为 undefined)。 */
8415
+ const usage = mergeUsage(okResults.map((r) => r.usage).filter((u) => u !== void 0));
8320
8416
  /** 被替换表层节点的 token 估算合计(仅新消息区间)。 */
8321
8417
  const shadowedTokenCount = replaceSeqs.reduce((total, seq) => {
8322
8418
  /** 当前表层事件。 */
@@ -8348,9 +8444,9 @@ async function observePass(ctx, agent, config, window, tailCount, target, signal
8348
8444
  shadowedCharCount,
8349
8445
  provider: target.provider,
8350
8446
  model: target.model,
8351
- maxTokens: config.compressMaxTokens,
8352
- attemptCount: summaryResult.attemptCount,
8353
- ...summaryResult.usage === void 0 ? {} : { usage: summaryResult.usage }
8447
+ maxTokens: config.observeChunkMaxTokens,
8448
+ attemptCount,
8449
+ ...usage === void 0 ? {} : { usage }
8354
8450
  });
8355
8451
  logger.step("观察提交:替换被压缩新消息区间为 <history>(旧块保留)");
8356
8452
  appendHistoryMessage(session, report, [summarySeq, ...replaceSeqs], {
@@ -8391,27 +8487,12 @@ async function maybeCompress(ctx, agent, config, signal) {
8391
8487
  return;
8392
8488
  }
8393
8489
  logger.step(`会话路由:provider ${target.provider},model ${target.model}`);
8394
- /** 模型容量信息(contextWindow 决定两级阈值)。 */
8395
- let info;
8396
- try {
8397
- info = await ctx.llm.resolveModelInfo(target.provider, target.model, signal);
8398
- } catch (error) {
8399
- logger.warn(`解析模型容量失败: ${error instanceof Error ? error.message : String(error)}`);
8400
- return;
8401
- }
8402
- /** 模型上下文窗口大小(非法值视为无法压缩)。 */
8403
- const window = info.context?.contextWindow;
8404
- if (typeof window !== "number" || !Number.isFinite(window) || window <= 0) {
8405
- logger.step(`模型上下文窗口非法(${String(window)}),跳过压缩`);
8406
- return;
8407
- }
8408
- logger.step(`模型上下文窗口 ${window} tokens,开始两级压缩(先反思后观察)`);
8409
8490
  /** 尾部保留条数(config.tailMessageCount,缺省 10)。 */
8410
8491
  const tailCount = config.tailMessageCount;
8411
8492
  logger.step("反思 pass 开始");
8412
- await reflectPass(ctx, agent, config, window, target, signal);
8493
+ await reflectPass(ctx, agent, config, target, signal);
8413
8494
  logger.step("反思 pass 结束,观察 pass 开始");
8414
- await observePass(ctx, agent, config, window, tailCount, target, signal);
8495
+ await observePass(ctx, agent, config, tailCount, target, signal);
8415
8496
  logger.step("观察 pass 结束,压缩流程完成");
8416
8497
  }
8417
8498
  //#endregion
@@ -8654,9 +8735,11 @@ function cosineSimilarity(a, b) {
8654
8735
  */
8655
8736
  /** 默认配置(冻结对象,resolveConfig 合并的基底;debug 缺省值在解析时按 NODE_ENV 判定)。 */
8656
8737
  const DEFAULT_CONFIG = Object.freeze({
8657
- thresholdRatio: .1,
8658
- historyMergeRatio: .2,
8738
+ observeThresholdTokens: 1e5,
8739
+ reflectThresholdTokens: 3e4,
8659
8740
  compressMaxTokens: 1e4,
8741
+ observeChunkTokens: 3e4,
8742
+ observeChunkMaxTokens: 5e3,
8660
8743
  tailMessageCount: 10,
8661
8744
  compressRetryCount: 10,
8662
8745
  omEnabled: true,
@@ -8667,9 +8750,11 @@ const DEFAULT_CONFIG = Object.freeze({
8667
8750
  });
8668
8751
  /** 数值键校验参数表:键名 + [integer]。不限制取值区间——用户提供的值按原样接受(便于调试)。 */
8669
8752
  const NUMBER_KEYS = [
8670
- ["thresholdRatio", false],
8671
- ["historyMergeRatio", false],
8753
+ ["observeThresholdTokens", true],
8754
+ ["reflectThresholdTokens", true],
8672
8755
  ["compressMaxTokens", true],
8756
+ ["observeChunkTokens", true],
8757
+ ["observeChunkMaxTokens", true],
8673
8758
  ["tailMessageCount", true],
8674
8759
  ["compressRetryCount", true]
8675
8760
  ];
@@ -9066,8 +9151,8 @@ function buildSemanticRecallTool(options) {
9066
9151
  * 按语义在全部完整消息(含被压缩/遮蔽)中检索,返回最匹配的完整消息与匹配说明
9067
9152
  * (本地 ONNX embedding,模型随插件打包,懒加载)
9068
9153
  * - compress.ts 自动压缩(OM 观察/反思两级阈值):pre-step 阻塞串行执行——
9069
- * 反思(摘要 窗口 × historyMergeRatio 时摘要调用精简合并 <history>)、
9070
- * 观察(未压缩消息 ≥ 窗口 × thresholdRatio 时摘要调用压缩为观察日志并追加)
9154
+ * 反思(<history> tokens 合计 reflectThresholdTokens 时摘要调用精简合并)、
9155
+ * 观察(未压缩消息 tokens observeThresholdTokens 时摘要调用压缩为观察日志并追加)
9071
9156
  *
9072
9157
  * 约束:不引入自定义会话事件类型——压缩复用宿主已知的 compaction/* 生命周期事件
9073
9158
  * (start/summary/end)与 checkpoint 标记,结果写入消息记录与轨迹。
@@ -9092,7 +9177,7 @@ function apply(ctx, config) {
9092
9177
  const resolved = resolveConfig(config);
9093
9178
  /** 插件日志门面(step=debug 按配置 debug 开关输出;info/warn 始终输出)。 */
9094
9179
  const logger = makeLogger(ctx, resolved.debug);
9095
- logger.step(`apply 启动:thresholdRatio=${String(resolved.thresholdRatio)} historyMergeRatio=${String(resolved.historyMergeRatio)} compressMaxTokens=${String(resolved.compressMaxTokens)} tailMessageCount=${String(resolved.tailMessageCount)} omEnabled=${String(resolved.omEnabled)} debug=${String(resolved.debug)}`);
9180
+ logger.step(`apply 启动:observeThresholdTokens=${String(resolved.observeThresholdTokens)} reflectThresholdTokens=${String(resolved.reflectThresholdTokens)} compressMaxTokens=${String(resolved.compressMaxTokens)} tailMessageCount=${String(resolved.tailMessageCount)} omEnabled=${String(resolved.omEnabled)} debug=${String(resolved.debug)}`);
9096
9181
  if (resolved.recallEnabled) ctx.tools.register(buildRecallTool(() => ctx.get("toolResultPruner")));
9097
9182
  if (resolved.semanticRecallEnabled) {
9098
9183
  const warnModel = (message) => ctx.logger.warn(`dsh-plugin-om: ${message}`);
@@ -19,8 +19,10 @@ import { type RoutedTarget } from './utils.ts';
19
19
  * 共享压缩提示词(观察/反思同一套):定义 history 块(模型消息 + index 的表达形式)、
20
20
  * 完整消息定义、要求压缩(完整保留用户消息 / reasoning 仅参考 / 关联 assistant 合并 /
21
21
  * index/start/end 连续)、输出格式(一个合法 <history> 块,无 reasoning)、数据源说明。
22
+ * mode 控制【摘要粒度】:无参为通用提示词(反思调用);'summary' 要求简单摘要(较早分块);
23
+ * 'detailed' 要求越往后越细(最后一块保留细节)。观察分块时除最后一块外均用 'summary'。
22
24
  */
23
- export declare function buildHistoryPrompt(): string;
25
+ export declare function buildHistoryPrompt(mode?: 'summary' | 'detailed'): string;
24
26
  /**
25
27
  * 渲染完整消息记录(观察输入,new 模式):输出一个合法的 <history> 块——
26
28
  * - user → <user_message index="N">(文本原样;图片/文件注释);
@@ -35,7 +37,7 @@ export declare function renderMessages(session: Session, seqs: readonly number[]
35
37
  export type SummarySubagentResult = {
36
38
  text: string;
37
39
  usage?: TokenUsage;
38
- /** 成功时的尝试次数(1 起;1=首次即成功,>1 表示重试后成功;UI 重试次数 = 该值 - 1)。 */
40
+ /** 成功时的尝试次数(1 起;1=首次即成功,>1 表示重试后成功;载荷层换算为重试次数 = 该值 - 1)。 */
39
41
  attemptCount: number;
40
42
  };
41
43
  /** 日志最小有效长度:<history> 中间内容小于该长度视为不合法(C 段校验)。 */
@@ -1 +1 @@
1
- {"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH,OAAO,KAAK,EAAE,KAAK,EAAmB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AACpG,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,YAAY,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAsC3C;AA0CD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAqEhF;AAED,yEAAyE;AACzE,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,yDAAyD;IACzD,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AA8EF,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,yrBAA6K,CAAC;AAE9M,4CAA4C;AAC5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,KAAK,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAoFF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAYrE;AACD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAAE,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAuBvC;AAED,sCAAsC;AACtC,MAAM,MAAM,sBAAsB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAiC/F;AACD,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAK/C;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,sBAAsB,CAAA;CAAE,GACpE,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CA+EvC"}
1
+ {"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAaH,OAAO,KAAK,EAAE,KAAK,EAAmB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AACpG,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,YAAY,CAAC;AAErD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,MAAM,CAiDxE;AA0CD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAqEhF;AAED,yEAAyE;AACzE,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AA8EF,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,yrBAA6K,CAAC;AAE9M,4CAA4C;AAC5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,KAAK,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAoFF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAYrE;AACD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAAE,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAuBvC;AAED,sCAAsC;AACtC,MAAM,MAAM,sBAAsB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAiC/F;AACD,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAK/C;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,sBAAsB,CAAA;CAAE,GACpE,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CA+EvC"}
package/dist/types.d.ts CHANGED
@@ -73,7 +73,7 @@ export type CompleteMessage = {
73
73
  export type CompactionSummaryPayload = SessionEventMap['compaction/summary'] & {
74
74
  /** 被压缩内容的字符数(压缩前文本长度合计;UI 标题统计用),旧载荷可能缺失。 */
75
75
  shadowedCharCount?: number;
76
- /** 摘要调用成功时的尝试次数(1 起;重试次数 = attemptCount - 1,UI 展示用),旧载荷可能缺失。 */
76
+ /** 摘要重试次数(0 起;观察分块为各块重试之和,反思为尝试次数 - 1),旧载荷可能缺失。 */
77
77
  attemptCount?: number;
78
78
  };
79
79
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gDAAgD,CAAC;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACnG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7E,gDAAgD;AAChD,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;GAGG;AACH,YAAY,EACV,KAAK,EACL,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,MAAM,EACN,OAAO,EACP,OAAO,EACP,YAAY,EACZ,eAAe,EACf,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,WAAW,GACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,mBAAmB;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,cAAc,GAAG,mBAAmB,GAAG,aAAa,CAAC;CAC5D,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG;IACzB,oBAAoB;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,kCAAkC;IAClC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC;IAChD,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG,eAAe,CAAC,oBAAoB,CAAC,GAAG;IAC7E,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,eAAe,CAAC,kBAAkB,CAAC,GAAG;IACzE,oCAAoC;IACpC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;CAC9B,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACzE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gDAAgD,CAAC;AACvF,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACnG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAC;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7E,gDAAgD;AAChD,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;GAGG;AACH,YAAY,EACV,KAAK,EACL,YAAY,EACZ,OAAO,EACP,YAAY,EACZ,MAAM,EACN,OAAO,EACP,OAAO,EACP,YAAY,EACZ,eAAe,EACf,cAAc,EACd,WAAW,EACX,oBAAoB,EACpB,YAAY,EACZ,UAAU,EACV,UAAU,EACV,cAAc,EACd,gBAAgB,EAChB,cAAc,EACd,WAAW,GACZ,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,mBAAmB;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,IAAI,EAAE,cAAc,GAAG,mBAAmB,GAAG,aAAa,CAAC;CAC5D,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,YAAY,GAAG;IACzB,oBAAoB;IACpB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,kCAAkC;IAClC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,WAAW,GAAG,UAAU,CAAC;IAChD,uEAAuE;IACvE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,wBAAwB,GAAG,eAAe,CAAC,oBAAoB,CAAC,GAAG;IAC7E,6CAA6C;IAC7C,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,mDAAmD;IACnD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,sBAAsB,GAAG,eAAe,CAAC,kBAAkB,CAAC,GAAG;IACzE,oCAAoC;IACpC,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;CAC9B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-om",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",