dsh-plugin-om 0.0.32 → 0.0.34

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
@@ -10,8 +10,8 @@
10
10
  - **工具驱动压缩**:摘要生成走多轮工具会话,模型经 getHistory 查看区间条目、compressHistory 分批替换 assistant 条目、completeCompression 结束。首条消息仅含压缩指令与 index 区间,不含历史内容。用户消息与系统消息不可压缩且原样保留;未压缩条目原样保留,条目正文一律以 CDATA 包裹;skill 加载条目以 `<skill_content name="…" index="…">` 元素呈现,内含 `<skill_resources>` 与 `<skill_instructions>` 两段,首次压缩时要求模型再次确认相关性,压缩后变为常规 assistant 摘要条目。最终 `<history>` 块由插件构建,天然合法无需校验
11
11
  - **压缩会话记录**:每次压缩的工具循环完整对话与压缩统计(起止时间、总耗时、逐轮请求耗时与 token usage 及合计)落盘为 one-shot 子会话,成功为会话记录、失败为失败日志,便于查看模型实际的查看与压缩行为及其 token 成本。主会话日志只保留汇总:`compaction/summary` 携带 usage 与起止时间、总耗时,失败路径 `compaction/end` 携带总耗时
12
12
  - **降级容错**:systemPrompt 或 tokenMeter 服务异常时按 0 计继续压缩,问题通过 console 输出与 log-only `om/warning` 会话事件上报,同会话同一问题至多一条
13
- - **recall 工具**:按完整消息 index 区间回看原始会话,含被压缩内容,图片附件随结果保留
14
- - **recall-semantic 工具**:本地嵌入模型 paraphrase-multilingual-MiniLM-L12-v2 按语义检索全部完整消息,只匹配文本,纯图片消息不进候选池
13
+ - **recall 工具**:按完整消息 index 区间回看原始会话,含被压缩内容;输出为 XML 条目序列(`<user_message>`、`<sys>`、`<assistant type="text"|"toolcall">`),正文一律以 CDATA 包裹逐字原样,toolcall 条目内嵌 `<tool-args>`(合法 JSON,仅一层转义)与 `<tool-result>` CDATA 子元素,图片附件随结果保留
14
+ - **recall-semantic 工具**:本地嵌入模型 paraphrase-multilingual-MiniLM-L12-v2 按语义检索全部完整消息,输出与 recall 相同的 XML 条目序列并以 XML 注释标注相似度与命中词;只匹配文本,纯图片消息不进候选池,本次调用自身的 toolcall 不参与检索
15
15
  - **压缩卡片**:浏览器客户端渲染折叠式已压缩卡片、压缩中提示行与可展开的失败错误行
16
16
  - **降级警告行**:浏览器客户端把 om 警告会话事件渲染为可展开的警告行
17
17
 
@@ -38,7 +38,7 @@ dsh plugin --profile <profile> add dsh-plugin-om
38
38
  ```yaml
39
39
  - id: dsh-plugin-om
40
40
  config:
41
- observeThresholdTokens: 45000
41
+ observeThresholdTokens: 35000
42
42
  ```
43
43
 
44
44
  ### 开发插件
@@ -59,19 +59,25 @@ preset-agent 自带 compaction-basic 压缩,阈值为上下文窗口的 80%,
59
59
 
60
60
  | 键 | 默认 | 含义 |
61
61
  | --- | --- | --- |
62
- | `observeThresholdTokens` | `45000` | 净压力达到该值时触发观察压缩 |
63
- | `reflectThresholdTokens` | `120000` | 全部 `<history>` 块 token 合计达到该值时触发反思合并 |
62
+ | `observeThresholdTokens` | `35000` | 净压力达到该值时触发观察压缩 |
63
+ | `reflectThresholdTokens` | `40000` | 全部 `<history>` 块 token 合计达到该值时触发反思合并 |
64
64
  | `compressMaxTokens` | 不设置 | 压缩循环单轮生成上限,不设置时由模型适配器默认值决定 |
65
65
  | `rateLimitWaitMs` | `60000` | 遇 429 限流后下一次压缩请求前的等待毫秒数,`0` 不限流 |
66
- | `tailMessageCount` | `5` | 观察触发后等待新增完整消息达到该条数才执行压缩,`0` 表示触发当轮立即执行 |
67
- | `compressSkipReasoning` | `true` | 工具循环 getHistory 输出是否携带 `<reasoning>` 参考条目,`true` 时不携带,压缩指令同步省略对应说明;此项关闭会严重降低压缩质量,仅作为可选开关存在 |
66
+ | `tailMessageCount` | `20` | 观察触发后等待新增完整消息达到该条数才执行压缩,`0` 表示触发当轮立即执行 |
67
+ | `compressSkipReasoning` | `true` | 工具循环 getHistory 输出是否携带 `<reasoning>` 参考条目,`true` 时不携带,压缩指令同步省略对应说明 |
68
68
  | `modelDir` | 共享目录 | recall-semantic 嵌入模型目录,默认 `$DSH_HOME/plugin-data/dsh-plugin-om/models/<id>`,onnx 缺失且启用语义召回时运行时自动下载 |
69
69
  | `omEnabled` | `true` | 是否启用自动压缩,关闭后 recall 工具不受影响 |
70
70
  | `debug` | dev | 步骤级日志开关,缺省按 `NODE_ENV` 非 production 判定,压缩结果与失败日志始终输出 |
71
71
  | `recallEnabled` | `true` | 是否注册 `recall` 工具 |
72
72
  | `semanticRecallEnabled` | `true` | 是否注册 `recall-semantic` 工具,关闭时不触发模型下载 |
73
73
 
74
- > 不建议把观察阈值设置得过高:越早压缩收益越高,且机制依赖模型对消息计数,过多消息会导致历史混乱。
74
+ > 检测到未压缩消息到达观察阈值后,插件会等待至少 tailMessageCount 后再压缩之前的消息,因此这个配置可以影响首次压缩的时机,也可以保留更多的会话信息。
75
+
76
+ 合理设置 `observeThresholdTokens` 和 `compressSkipReasoning`:
77
+ - 若模型 reasoning 输出质量差且会把关键结论以 text 形式输出(例如glm和deepseek),无需携带reasoning
78
+ - 若使用 openai/responses 或 anthropic/messages API,reasoning 内容包含大量决策信息,建议携带reasoning
79
+ - 若观察阈值太小,涵盖信息不足以形成逻辑链路,应当让观察阈值覆盖多个 text/reasoning
80
+ - 若观察阈值太大摘要质量也会降低,大幅增加单次观察的耗时,且越早 OM 收益越高
75
81
 
76
82
  > 想直观理解机制并估算不同参数下的 token 成本,可打开交互式[机制说明与成本计算器](https://fanethedivine.github.io/dsh-plugin-om/)。
77
83
 
@@ -104,6 +110,7 @@ src/
104
110
  ├── rate-limit.ts # 全局 429 限流冷却门,进程级共享状态
105
111
  ├── log-index.ts # 完整消息索引与渲染,recall 与压缩共用同一套编号
106
112
  ├── recall.ts # recall 工具:按完整消息 index 区间回看
113
+ ├── recall-xml.ts # 完整消息到 CDATA 包裹 XML 条目的渲染(recall 系工具共用)
107
114
  ├── recall-output.ts # recall 输出契约:{ text, images } 与 render 投影
108
115
  ├── semantic-recall.ts # recall-semantic 工具:本地嵌入语义检索
109
116
  ├── embedding.ts # 本地 ONNX 嵌入:懒加载、批量、运行时按需下载编排
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["memo","useState","DisclosureRow","IconBrowseOutline16","memo","useState","DisclosureRow","IconBrowseOutline16"],"sources":["../src/constants.ts","../src/om-event.ts","../src/client/definition.ts","../src/client/locales.ts","../src/client/format.ts","../src/client/OmCompactionCard.tsx","../src/client/OmWarningCard.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * 共享常量:插件级魔法字符串,集中定义避免散落各模块。\n * 导出 PLUGIN_LABEL / HISTORY_TAG / HISTORY_TIP / COMPLETE_MESSAGE_DEFINITION /\n * HISTORY_FORMAT_NOTE / SKILL_TOOL_NAME / COMPACT_CHECKPOINT_PLUGIN /\n * COMPACTION_ABORTED_ERROR / isPluginOwnedSource。\n */\n\n/** 插件标识:压缩消息 source.plugin 取值与日志前缀。 */\nexport const PLUGIN_LABEL = 'dsh-plugin-om';\n\n/** 压缩日志标签名:<history>...</history> 包裹观察/反思日志块。 */\nexport const HISTORY_TAG = 'history';\n\n/** 压缩日志块开标签的 tip 属性(对 AI 的提醒:本块是历史压缩产物,不要复述)。 */\nexport const HISTORY_TIP = '当前块是历史消息的压缩产物,不要复述';\n\n/**\n * 「完整消息」定义(提示词 / 工具描述 / 块顶注释共用)。\n * 完整消息是摘要日志与 recall 共用的定位单位;首条 index 为 0,按会话顺序递增、全局稳定。\n */\nexport const COMPLETE_MESSAGE_DEFINITION =\n '`完整消息`指一条`用户消息`、`系统消息`、`模型输出文本`或`具有result的toolcall`;首条 index 为 0,按会话顺序递增。';\n\n/**\n * 最终 <history> 块内文块首的格式说明注释(XML 注释,完整消息定义 + 条目标签语义 + CDATA 约定)。\n */\nexport const HISTORY_FORMAT_NOTE = `<!-- 完整消息:${COMPLETE_MESSAGE_DEFINITION} <TAG index=\"N\">表示单条完整消息,<TAG start=\"A\" end=\"B\"> 表示连续模块,start/end 是首尾完整消息的 index;<sys type=\"KIND\" index=\"N\"> 表示被压缩的系统消息,块中为空;条目正文一律以 CDATA 包裹,CDATA 内为逐字原样内容;<skill_content name=\"S\" index=\"N\"> 表示未压缩的 skill 加载条目,内含 <skill_resources> 与 <skill_instructions> 两段,各自以 CDATA 包裹,内文为其工具返回内容 -->`;\n\n/** skill 工具名:toolcall 条目的工具名为该值时视为 skill 加载,<history> 块中以 <skill_content> 元素呈现。 */\nexport const SKILL_TOOL_NAME = 'skill';\n\n/** 旧日志压缩消息的宿主 checkpoint 标记 plugin 名(历史兼容识别用)。 */\nexport const COMPACT_CHECKPOINT_PLUGIN = 'compact';\n\n/**\n * 压缩因 signal 中止而放弃时 compaction/end 的 error 标识(服务端写入、客户端过滤):\n * 中止不是失败,客户端据此隐藏失败行(宿主不变量要求无 summary 的 end 必须带 error)。\n */\nexport const COMPACTION_ABORTED_ERROR = '压缩已中止(signal aborted)';\n\n/** 判定 user/message 的 source 是否为本插件自产(压缩日志消息;含旧宿主 checkpoint 标记兼容)。这类消息不占完整消息 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 * om 事件借用通道:插件私有事件以宿主已知类型 feedback/record 持久化。\n * 导出 OM_EVENT_PREFIX / OmEventKind / OmEventPayloadMap / OmEvent /\n * appendOmEvent / readOmEvent / findOmEvents。\n *\n * 背景:持久化读取路径拒绝 KNOWN_SESSION_EVENT_TYPES 目录之外的未知类型\n * (dsh-session-persistence 的 assertEventsSupported),而 Session.append 不提供\n * ignorable 标记的写入途径,插件自定义事件类型会使含该事件的会话无法加载。\n * feedback/record(dsh-command-feedback 声明)是目录内 log-only 审计事件,所有者\n * invariant 声明其无跨事件关系、无运行时不变量、永不进入模型上下文,故选为承载\n * 类型:插件数据以 `om:1:` 前缀 + JSON 信封写入其 text 字段,解码失败按非 om\n * 记录处理(含用户自写的真实反馈记录)。\n */\nimport type { Session, SessionEvent } from './types.ts';\n\n/** om 信封 text 前缀:标识该 feedback/record 记录由本插件写入。 */\nexport const OM_EVENT_PREFIX = 'om:1:';\n\n/** om 私有事件类别(信封 kind 字段)。 */\nexport type OmEventKind =\n /** 功能降级警告(degrade.ts 报告,客户端渲染「功能降级」警告行)。 */\n | 'om/warning'\n /** 观察压缩待定标记(compress.ts 观察触发时追加)。 */\n | 'om/observe-pending'\n /** 观察压缩待定失效标记(压缩执行完成后追加,声明待定可重新触发)。 */\n | 'om/observe-invalidate';\n\n/** 各类别信封载荷(kind 之外的 JSON 字段)。 */\nexport type OmEventPayloadMap = {\n 'om/warning': {\n /** 降级问题标识(会话内去重键,取值见 degrade.ts)。 */\n problem: string;\n /** 面向用户的简短说明。 */\n message: string;\n };\n 'om/observe-pending': {\n /** 触发点:追加时的最后一条完整消息 index。 */\n triggerMessageIndex: number;\n };\n 'om/observe-invalidate': {\n /** 被失效的 om/observe-pending 事件 seq。 */\n pendingSeq: number;\n };\n};\n\n/** 一条解码后的 om 私有事件:类别、载荷与承载事件在日志中的 seq。 */\nexport type OmEvent = {\n [K in OmEventKind]: { kind: K; data: OmEventPayloadMap[K]; seq: number };\n}[OmEventKind];\n\n/** 校验载荷字段与类别匹配(运行时守卫,保证返回类型的诚实性)。 */\nfunction isValidPayload(kind: OmEventKind, data: Record<string, unknown>): boolean {\n switch (kind) {\n case 'om/warning':\n return typeof data.problem === 'string' && typeof data.message === 'string';\n case 'om/observe-pending':\n return (\n typeof data.triggerMessageIndex === 'number' &&\n Number.isSafeInteger(data.triggerMessageIndex)\n );\n case 'om/observe-invalidate':\n return typeof data.pendingSeq === 'number' && Number.isSafeInteger(data.pendingSeq);\n }\n}\n\n/**\n * 解码一条会话事件为 om 私有事件:仅识别 feedback/record 中带 om 信封前缀的\n * text;前缀缺失、JSON 非法、kind 未知或载荷字段缺失时返回 undefined。\n */\nexport function readOmEvent(event: SessionEvent | undefined | null): OmEvent | undefined {\n if (event === undefined || event === null || event.type !== 'feedback/record') return undefined;\n const text = (event.data as { text?: unknown } | undefined)?.text;\n if (typeof text !== 'string' || !text.startsWith(OM_EVENT_PREFIX)) return undefined;\n let envelope: unknown;\n try {\n envelope = JSON.parse(text.slice(OM_EVENT_PREFIX.length));\n } catch {\n return undefined;\n }\n if (envelope === null || typeof envelope !== 'object' || Array.isArray(envelope))\n return undefined;\n const { kind, ...rest } = envelope as Record<string, unknown>;\n if (typeof kind !== 'string') return undefined;\n const kinds: OmEventKind[] = ['om/warning', 'om/observe-pending', 'om/observe-invalidate'];\n if (!kinds.includes(kind as OmEventKind)) return undefined;\n const omKind = kind as OmEventKind;\n if (!isValidPayload(omKind, rest)) return undefined;\n return { kind: omKind, data: rest, seq: event.seq } as OmEvent;\n}\n\n/** 编码一条 om 私有事件为 feedback/record 的 text 信封。 */\nfunction encodeOmEvent<K extends OmEventKind>(kind: K, data: OmEventPayloadMap[K]): string {\n return `${OM_EVENT_PREFIX}${JSON.stringify({ kind, ...data })}`;\n}\n\n/** 追加一条 om 私有事件(借用 feedback/record,log-only,不进 surface),返回事件 seq。 */\nexport function appendOmEvent<K extends OmEventKind>(\n session: Session,\n kind: K,\n data: OmEventPayloadMap[K],\n): number {\n return session.append('feedback/record', { text: encodeOmEvent(kind, data) }).seq;\n}\n\n/** 读取日志中全部 om 私有事件(按日志顺序)。 */\nexport function findOmEvents(session: Session): OmEvent[];\n/** 读取日志中指定类别的 om 私有事件(按日志顺序,载荷类型按类别收窄)。 */\nexport function findOmEvents<K extends OmEventKind>(\n session: Session,\n kind: K,\n): Array<{ kind: K; data: OmEventPayloadMap[K]; seq: number }>;\nexport function findOmEvents(session: Session, kind?: OmEventKind): OmEvent[] {\n const result: OmEvent[] = [];\n for (const event of session.events) {\n const om = readOmEvent(event);\n if (om !== undefined && (kind === undefined || om.kind === kind)) result.push(om);\n }\n return result;\n}\n","/**\n * 压缩卡片业务定义:认领插件自产的压缩生命周期事件(compaction/start|summary|end)\n * 与替换检查点(source.plugin = 'dsh-plugin-om'),聚合出可渲染的摘要卡片节点;\n * 并认领 om 警告信封事件(借用 feedback/record,kind 为 om/warning,见 om-event.ts)\n * 渲染为警告行。\n * 导出 COMPACTION_CARD_KIND / OmCompactionChatData / checkpointCompactionId /\n * omCompactionDefinition / OM_WARNING_CARD_KIND / OmWarningChatData / omWarningDefinition。\n * 宿主的 'compact' 检查点由宿主自己渲染,本定义不认领\n * (双方共存无视觉冲突:宿主无检查点证据时不产出节点)。\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 { COMPACTION_ABORTED_ERROR, PLUGIN_LABEL } from '../constants.ts';\nimport { readOmEvent } from '../om-event.ts';\nimport type { CompactionSummaryPayload } from '../types.ts';\n\n/** 压缩卡片渲染器分发键(合并进 ChatNodeDataMap)。 */\nexport const COMPACTION_CARD_KIND = 'om-compaction';\n\n/** 功能降级警告行渲染器分发键(合并进 ChatNodeDataMap)。 */\nexport const OM_WARNING_CARD_KIND = 'om-warning';\n\ndeclare module '@deepseek-ai/dsh-client-ui-conversation/client' {\n interface ChatNodeDataMap {\n /** dsh-plugin-om 压缩替换检查点渲染的摘要卡片数据。 */\n 'om-compaction': OmCompactionChatData;\n /** dsh-plugin-om 功能降级警告行数据。 */\n 'om-warning': OmWarningChatData;\n }\n}\n\n/** 压缩卡片载荷:摘要文本与遮蔽统计。 */\nexport interface OmCompactionChatData {\n /** 压缩进行中(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),summary 不可用或非法时为 null。 */\n readonly summaryTokenCount: number | null;\n /** 压缩失败的错误文案(来自 compaction/end 载荷 error);非失败节点为 null。 */\n readonly error: string | 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,node 端测试无法求值,\n * 故本地实现同一形状检查)。\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 * 关联 compactionId;其余事件返回 undefined。\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 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 }\n return {\n running: false,\n phase: null,\n error: 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 };\n}\n\n/** 从 compaction/start 载荷读取压缩 pass(observe/reflect;缺失或非法时回落 null)。 */\nfunction startPhase(start: ConversationMatch | undefined): 'observe' | 'reflect' | null {\n const data = start?.event.data as { phase?: unknown } | undefined;\n return data?.phase === 'observe' || data?.phase === 'reflect' ? data.phase : null;\n}\n\n/** 压缩进行中提示行的载荷(统计未就绪,全部为 null)。 */\nfunction runningData(start: ConversationMatch | undefined): OmCompactionChatData {\n return {\n running: true,\n phase: startPhase(start),\n error: null,\n summary: null,\n summaryEventSeq: null,\n shadowedItemCount: null,\n shadowedTokenCount: null,\n shadowedCharCount: null,\n summaryCharCount: null,\n summaryTokenCount: null,\n };\n}\n\n/**\n * 压缩失败节点的载荷:end 已到且带非中止 error、无检查点时使用。\n * error 取 compaction/end 载荷中的实际报错文案(非字符串或为空时回落 null);\n * 摘要与统计字段全部为 null。\n */\nfunction failureData(\n start: ConversationMatch | undefined,\n end: ConversationMatch,\n): OmCompactionChatData {\n const data = end.event.data as { error?: unknown } | undefined;\n return {\n running: false,\n phase: startPhase(start),\n error: typeof data?.error === 'string' && data.error !== '' ? data.error : null,\n summary: null,\n summaryEventSeq: null,\n shadowedItemCount: null,\n shadowedTokenCount: null,\n shadowedCharCount: null,\n summaryCharCount: null,\n summaryTokenCount: 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 / 替换检查点 / end\n * 作为 update 折叠证据。有检查点时产出摘要卡片;end 已到且无检查点时,error 为\n * 非中止报错则产出可见失败行,error 为中止标识或缺失则以 hidden 撤回进行中提示行\n * (已物化节点以 hidden visibility 保留同一 key);仅 start 时渲染「正在压缩上下文…」\n * 提示行。\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 已到达但无检查点:error 为非中止的实际报错时渲染可见失败行;\n // 无 error 或 error 以 COMPACTION_ABORTED_ERROR 开头(用户中止)时,\n // 以 hidden visibility 撤回进行中提示行\n if (state.end !== undefined) {\n const anchorSeq = context.start?.event.seq ?? state.end.event.seq;\n const endData = state.end.event.data as { error?: unknown } | undefined;\n const isFailure =\n typeof endData?.error === 'string' &&\n endData.error !== '' &&\n !endData.error.startsWith(COMPACTION_ABORTED_ERROR);\n if (isFailure) {\n return chatNode(\n context,\n COMPACTION_CARD_KIND,\n anchorSeq,\n failureData(context.start, state.end),\n );\n }\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/** 功能降级警告行载荷:降级问题标识与面向用户的说明(信封载荷非法时为 null)。 */\nexport interface OmWarningChatData {\n /** 降级问题标识(om/warning 信封载荷 problem)。 */\n readonly problem: string | null;\n /** 面向用户的降级说明(om/warning 信封载荷 message)。 */\n readonly message: string | null;\n}\n\n/**\n * 插件功能降级警告定义:每条 om/warning 信封事件独立成节点(id 含 seq),渲染为可\n * 展开的「功能降级」警告行。服务端按会话同问题去重后追加,每会话同一问题至多一行;\n * 不带 om 信封的 feedback/record 记录(用户自写反馈)不认领。\n */\nexport const omWarningDefinition: ConversationNodeDefinition = {\n kind: OM_WARNING_CARD_KIND,\n target: 'chat',\n match: (event) => {\n if (readOmEvent(event)?.kind !== 'om/warning') return null;\n return { id: `om-warning:${event.seq}`, role: 'start' };\n },\n start: () => ({}),\n update: (context) => context.state,\n buildViewNode: (context) => {\n const start = context.start;\n if (start === undefined) return null;\n const om = readOmEvent(start.event);\n if (om?.kind !== 'om/warning') return null;\n const problem = om.data.problem !== '' ? om.data.problem : null;\n const message = om.data.message !== '' ? om.data.message : null;\n if (problem === null && message === null) return null;\n return chatNode(context, OM_WARNING_CARD_KIND, start.event.seq, { problem, message });\n },\n};\n","/**\n * `om-compaction` namespace 字典:插件压缩卡片的界面文案(zh/en)。\n * 导出 NS / zh / en;zh 为键集事实源,en 键集与 zh 一致。\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.failed': '上下文压缩失败',\n 'compaction.failed.observe': '上下文压缩失败(观察)',\n 'compaction.failed.reflect': '上下文压缩失败(反思)',\n warning: '上下文压缩功能降级',\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.failed': 'Context compaction failed',\n 'compaction.failed.observe': 'Context compaction failed (observation)',\n 'compaction.failed.reflect': 'Context compaction failed (reflection)',\n warning: 'Context compaction degraded',\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 * 压缩卡片统计行的紧凑数字格式化。导出 formatCompactCount。\n * <1k 原样;≥1k 用 k;≥1w(万)用 w;≥1M 用 M;保留 1 位小数并去掉末尾 .0。\n */\nexport function formatCompactCount(value: number): string {\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 * 压缩卡片渲染器:与宿主「上下文注入」卡片同款的折叠行。\n * 复用平台原语 DisclosureRow;行内统计与展开体样式按宿主 ContextInjectionRow 的规则\n * 注入 <style>(宿主该卡片样式是编译期哈希的 CSS Module,插件 bundle 无法引用其类名)。\n * 卡片报告模型从何处起不再看到那段历史;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/** 注册卡片样式(无 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 // 压缩进行中:不可展开的「正在压缩上下文…」提示行(按 phase 区分文案)\n if (data.running) {\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 // 压缩失败:可展开的错误行(按 phase 区分文案),折叠行为错误摘要、展开体为完整报错\n if (data.error !== null) {\n const failedLabel =\n data.phase === 'observe'\n ? t('compaction.failed.observe')\n : data.phase === 'reflect'\n ? t('compaction.failed.reflect')\n : t('compaction.failed');\n const errorSummary = data.error.length > 80 ? `${data.error.slice(0, 80)}…` : data.error;\n return (\n <div style={styles.row}>\n <DisclosureRow\n className={css.root}\n icon={<IconBrowseOutline16 size={14} />}\n chevronClassName={css.chevron}\n title={failedLabel}\n collapsedContent={\n <>\n <span className={css.sep} aria-hidden />\n <span className={css.summary}>{errorSummary}</span>\n </>\n }\n keepContentWhenOpen\n expandable\n expandOnRowClick\n open={expanded}\n onToggle={() => {\n setExpanded((value) => !value);\n }}\n >\n <div className={css.body} style={styles.body}>\n {data.error}\n </div>\n </DisclosureRow>\n </div>\n );\n }\n const expandable = data.summary !== null;\n const open = expandable && expanded;\n // 统计行:优先完整压缩前后统计,载荷字段不全时逐级回落\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 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 </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 /** 摘要与失败报错为多行文本,保留换行。 */\n body: { whiteSpace: 'pre-wrap' },\n};\n","/**\n * 功能降级警告行渲染器:与压缩卡片同款的折叠行。\n * 折叠行显示降级说明摘要,展开体显示完整说明;数据来自 om/warning 信封载荷\n * (借用 feedback/record),服务端按会话同问题去重后追加(每会话同一问题至多一行)。\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 OmWarningCardProps {\n readonly node: ChatNode<'om-warning'>;\n readonly t: TranslateNS<'om-compaction'>;\n}\n\n/** 折叠式功能降级警告行(降级说明摘要行 + 完整说明展开)。 */\nexport const OmWarningCard = memo(function OmWarningCard({ node, t }: OmWarningCardProps) {\n const [expanded, setExpanded] = useState(false);\n const { data } = node;\n const message = data.message ?? (data.problem !== null ? data.problem : '');\n const expandable = message !== '';\n const messageSummary = message.length > 80 ? `${message.slice(0, 80)}…` : message;\n return (\n <div>\n <DisclosureRow\n className=\"om-compaction-root\"\n icon={<IconBrowseOutline16 size={14} />}\n chevronClassName=\"om-compaction-chevron\"\n title={t('warning')}\n collapsedContent={\n expandable ? (\n <>\n <span className=\"om-compaction-sep\" aria-hidden />\n <span className=\"om-compaction-summary\">{messageSummary}</span>\n </>\n ) : undefined\n }\n keepContentWhenOpen\n expandable={expandable}\n expandOnRowClick\n open={expandable && expanded}\n onToggle={() => {\n if (expandable) setExpanded((value) => !value);\n }}\n >\n {expandable && (\n <div className=\"om-compaction-body\" style={bodyStyle}>\n {message}\n </div>\n )}\n </DisclosureRow>\n </div>\n );\n});\n\nconst bodyStyle: CSSProperties = { whiteSpace: 'pre-wrap' };\n","/**\n * 浏览器客户端入口(exports[\"./client\"] → dist/client.js):注册压缩卡片与功能降级\n * 警告行的 locale 字典、conversation 业务定义与 keyed 渲染器。宿主 conversation UI 只\n * 识别内置 'compact' 检查点,插件自产的压缩检查点(source.plugin = 'dsh-plugin-om')\n * 由本 bundle 认领并在消息列表渲染「已压缩」卡片;om 警告信封事件(借用\n * feedback/record)渲染为「功能降级」警告行。\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 声明合并。\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport { omCompactionDefinition, omWarningDefinition } from './definition.ts';\nimport { en, NS, zh } from './locales.ts';\nimport { OmCompactionCard } from './OmCompactionCard.tsx';\nimport { OmWarningCard } from './OmWarningCard.tsx';\n\n/** Required services: 聊天节点槽、会话事件注册表与 locale 服务。 */\nexport const inject = ['slots', 'conversationEvents', 'locale'];\n\n/** Client plugin body: 注册压缩卡片的业务定义与 keyed 渲染器(随插件卸载移除)。 */\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.effect(\n () => ctx.conversationEvents.register(omWarningDefinition),\n 'dsh-plugin-om: om warning 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 ctx.slots.inject('conversation.chat.node', () =>\n ctx.slots.register(\n { name: 'conversation.chat.node', key: 'om-warning', locale: NS },\n OmWarningCard,\n ),\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["memo","useState","DisclosureRow","IconBrowseOutline16","memo","useState","DisclosureRow","IconBrowseOutline16"],"sources":["../src/constants.ts","../src/om-event.ts","../src/client/definition.ts","../src/client/locales.ts","../src/client/format.ts","../src/client/OmCompactionCard.tsx","../src/client/OmWarningCard.tsx","../src/client/index.ts"],"sourcesContent":["/**\n * 共享常量:插件级魔法字符串,集中定义避免散落各模块。\n * 导出 PLUGIN_LABEL / HISTORY_TAG / HISTORY_TIP / COMPLETE_MESSAGE_DEFINITION /\n * HISTORY_FORMAT_NOTE / SKILL_TOOL_NAME / COMPACT_CHECKPOINT_PLUGIN /\n * COMPACTION_ABORTED_ERROR / isPluginOwnedSource。\n */\n\n/** 插件标识:压缩消息 source.plugin 取值与日志前缀。 */\nexport const PLUGIN_LABEL = 'dsh-plugin-om';\n\n/** 压缩日志标签名:<history>...</history> 包裹观察/反思日志块。 */\nexport const HISTORY_TAG = 'history';\n\n/** 压缩日志块开标签的 tip 属性(对 AI 的提醒:本块是历史压缩产物,不要复述)。 */\nexport const HISTORY_TIP = '当前块是历史消息的压缩产物,不要复述';\n\n/**\n * 「完整消息」定义(提示词 / 工具描述 / 块顶注释共用)。\n * 完整消息是摘要日志与 recall 共用的定位单位;首条 index 为 0,按会话顺序递增、全局稳定。\n */\nexport const COMPLETE_MESSAGE_DEFINITION =\n '`完整消息`指一条`用户消息`、`系统消息`、`模型输出文本`或`具有result的toolcall`;首条 index 为 0,按会话顺序递增。';\n\n/**\n * 最终 <history> 块内文块首的格式说明注释(XML 注释,完整消息定义 + 条目标签语义 + CDATA 约定)。\n */\nexport const HISTORY_FORMAT_NOTE = `<!-- ${COMPLETE_MESSAGE_DEFINITION} <TAG index=\"N\">表示单条完整消息,<TAG start=\"A\" end=\"B\"> 表示多条连续消息,start/end 是首尾完整消息的 index;消息块的内容是用CDATA包裹的纯文本;<sys type=\"KIND\" index=\"N\"> 表示被压缩的系统消息,块中为空;<skill_content name=\"S\" index=\"N\"> 表示未压缩的原始 skill -->`;\n\n/** skill 工具名:toolcall 条目的工具名为该值时视为 skill 加载,<history> 块中以 <skill_content> 元素呈现。 */\nexport const SKILL_TOOL_NAME = 'skill';\n\n/** 旧日志压缩消息的宿主 checkpoint 标记 plugin 名(历史兼容识别用)。 */\nexport const COMPACT_CHECKPOINT_PLUGIN = 'compact';\n\n/**\n * 压缩因 signal 中止而放弃时 compaction/end 的 error 标识(服务端写入、客户端过滤):\n * 中止不是失败,客户端据此隐藏失败行(宿主不变量要求无 summary 的 end 必须带 error)。\n */\nexport const COMPACTION_ABORTED_ERROR = '压缩已中止(signal aborted)';\n\n/** 判定 user/message 的 source 是否为本插件自产(压缩日志消息;含旧宿主 checkpoint 标记兼容)。这类消息不占完整消息 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 * om 事件借用通道:插件私有事件以宿主已知类型 feedback/record 持久化。\n * 导出 OM_EVENT_PREFIX / OmEventKind / OmEventPayloadMap / OmEvent /\n * appendOmEvent / readOmEvent / findOmEvents。\n *\n * 背景:持久化读取路径拒绝 KNOWN_SESSION_EVENT_TYPES 目录之外的未知类型\n * (dsh-session-persistence 的 assertEventsSupported),而 Session.append 不提供\n * ignorable 标记的写入途径,插件自定义事件类型会使含该事件的会话无法加载。\n * feedback/record(dsh-command-feedback 声明)是目录内 log-only 审计事件,所有者\n * invariant 声明其无跨事件关系、无运行时不变量、永不进入模型上下文,故选为承载\n * 类型:插件数据以 `om:1:` 前缀 + JSON 信封写入其 text 字段,解码失败按非 om\n * 记录处理(含用户自写的真实反馈记录)。\n */\nimport type { Session, SessionEvent } from './types.ts';\n\n/** om 信封 text 前缀:标识该 feedback/record 记录由本插件写入。 */\nexport const OM_EVENT_PREFIX = 'om:1:';\n\n/** om 私有事件类别(信封 kind 字段)。 */\nexport type OmEventKind =\n /** 功能降级警告(degrade.ts 报告,客户端渲染「功能降级」警告行)。 */\n | 'om/warning'\n /** 观察压缩待定标记(compress.ts 观察触发时追加)。 */\n | 'om/observe-pending'\n /** 观察压缩待定失效标记(压缩执行完成后追加,声明待定可重新触发)。 */\n | 'om/observe-invalidate';\n\n/** 各类别信封载荷(kind 之外的 JSON 字段)。 */\nexport type OmEventPayloadMap = {\n 'om/warning': {\n /** 降级问题标识(会话内去重键,取值见 degrade.ts)。 */\n problem: string;\n /** 面向用户的简短说明。 */\n message: string;\n };\n 'om/observe-pending': {\n /** 触发点:追加时的最后一条完整消息 index。 */\n triggerMessageIndex: number;\n };\n 'om/observe-invalidate': {\n /** 被失效的 om/observe-pending 事件 seq。 */\n pendingSeq: number;\n };\n};\n\n/** 一条解码后的 om 私有事件:类别、载荷与承载事件在日志中的 seq。 */\nexport type OmEvent = {\n [K in OmEventKind]: { kind: K; data: OmEventPayloadMap[K]; seq: number };\n}[OmEventKind];\n\n/** 校验载荷字段与类别匹配(运行时守卫,保证返回类型的诚实性)。 */\nfunction isValidPayload(kind: OmEventKind, data: Record<string, unknown>): boolean {\n switch (kind) {\n case 'om/warning':\n return typeof data.problem === 'string' && typeof data.message === 'string';\n case 'om/observe-pending':\n return (\n typeof data.triggerMessageIndex === 'number' &&\n Number.isSafeInteger(data.triggerMessageIndex)\n );\n case 'om/observe-invalidate':\n return typeof data.pendingSeq === 'number' && Number.isSafeInteger(data.pendingSeq);\n }\n}\n\n/**\n * 解码一条会话事件为 om 私有事件:仅识别 feedback/record 中带 om 信封前缀的\n * text;前缀缺失、JSON 非法、kind 未知或载荷字段缺失时返回 undefined。\n */\nexport function readOmEvent(event: SessionEvent | undefined | null): OmEvent | undefined {\n if (event === undefined || event === null || event.type !== 'feedback/record') return undefined;\n const text = (event.data as { text?: unknown } | undefined)?.text;\n if (typeof text !== 'string' || !text.startsWith(OM_EVENT_PREFIX)) return undefined;\n let envelope: unknown;\n try {\n envelope = JSON.parse(text.slice(OM_EVENT_PREFIX.length));\n } catch {\n return undefined;\n }\n if (envelope === null || typeof envelope !== 'object' || Array.isArray(envelope))\n return undefined;\n const { kind, ...rest } = envelope as Record<string, unknown>;\n if (typeof kind !== 'string') return undefined;\n const kinds: OmEventKind[] = ['om/warning', 'om/observe-pending', 'om/observe-invalidate'];\n if (!kinds.includes(kind as OmEventKind)) return undefined;\n const omKind = kind as OmEventKind;\n if (!isValidPayload(omKind, rest)) return undefined;\n return { kind: omKind, data: rest, seq: event.seq } as OmEvent;\n}\n\n/** 编码一条 om 私有事件为 feedback/record 的 text 信封。 */\nfunction encodeOmEvent<K extends OmEventKind>(kind: K, data: OmEventPayloadMap[K]): string {\n return `${OM_EVENT_PREFIX}${JSON.stringify({ kind, ...data })}`;\n}\n\n/** 追加一条 om 私有事件(借用 feedback/record,log-only,不进 surface),返回事件 seq。 */\nexport function appendOmEvent<K extends OmEventKind>(\n session: Session,\n kind: K,\n data: OmEventPayloadMap[K],\n): number {\n return session.append('feedback/record', { text: encodeOmEvent(kind, data) }).seq;\n}\n\n/** 读取日志中全部 om 私有事件(按日志顺序)。 */\nexport function findOmEvents(session: Session): OmEvent[];\n/** 读取日志中指定类别的 om 私有事件(按日志顺序,载荷类型按类别收窄)。 */\nexport function findOmEvents<K extends OmEventKind>(\n session: Session,\n kind: K,\n): Array<{ kind: K; data: OmEventPayloadMap[K]; seq: number }>;\nexport function findOmEvents(session: Session, kind?: OmEventKind): OmEvent[] {\n const result: OmEvent[] = [];\n for (const event of session.events) {\n const om = readOmEvent(event);\n if (om !== undefined && (kind === undefined || om.kind === kind)) result.push(om);\n }\n return result;\n}\n","/**\n * 压缩卡片业务定义:认领插件自产的压缩生命周期事件(compaction/start|summary|end)\n * 与替换检查点(source.plugin = 'dsh-plugin-om'),聚合出可渲染的摘要卡片节点;\n * 并认领 om 警告信封事件(借用 feedback/record,kind 为 om/warning,见 om-event.ts)\n * 渲染为警告行。\n * 导出 COMPACTION_CARD_KIND / OmCompactionChatData / checkpointCompactionId /\n * omCompactionDefinition / OM_WARNING_CARD_KIND / OmWarningChatData / omWarningDefinition。\n * 宿主的 'compact' 检查点由宿主自己渲染,本定义不认领\n * (双方共存无视觉冲突:宿主无检查点证据时不产出节点)。\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 { COMPACTION_ABORTED_ERROR, PLUGIN_LABEL } from '../constants.ts';\nimport { readOmEvent } from '../om-event.ts';\nimport type { CompactionSummaryPayload } from '../types.ts';\n\n/** 压缩卡片渲染器分发键(合并进 ChatNodeDataMap)。 */\nexport const COMPACTION_CARD_KIND = 'om-compaction';\n\n/** 功能降级警告行渲染器分发键(合并进 ChatNodeDataMap)。 */\nexport const OM_WARNING_CARD_KIND = 'om-warning';\n\ndeclare module '@deepseek-ai/dsh-client-ui-conversation/client' {\n interface ChatNodeDataMap {\n /** dsh-plugin-om 压缩替换检查点渲染的摘要卡片数据。 */\n 'om-compaction': OmCompactionChatData;\n /** dsh-plugin-om 功能降级警告行数据。 */\n 'om-warning': OmWarningChatData;\n }\n}\n\n/** 压缩卡片载荷:摘要文本与遮蔽统计。 */\nexport interface OmCompactionChatData {\n /** 压缩进行中(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),summary 不可用或非法时为 null。 */\n readonly summaryTokenCount: number | null;\n /** 压缩失败的错误文案(来自 compaction/end 载荷 error);非失败节点为 null。 */\n readonly error: string | 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,node 端测试无法求值,\n * 故本地实现同一形状检查)。\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 * 关联 compactionId;其余事件返回 undefined。\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 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 }\n return {\n running: false,\n phase: null,\n error: 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 };\n}\n\n/** 从 compaction/start 载荷读取压缩 pass(observe/reflect;缺失或非法时回落 null)。 */\nfunction startPhase(start: ConversationMatch | undefined): 'observe' | 'reflect' | null {\n const data = start?.event.data as { phase?: unknown } | undefined;\n return data?.phase === 'observe' || data?.phase === 'reflect' ? data.phase : null;\n}\n\n/** 压缩进行中提示行的载荷(统计未就绪,全部为 null)。 */\nfunction runningData(start: ConversationMatch | undefined): OmCompactionChatData {\n return {\n running: true,\n phase: startPhase(start),\n error: null,\n summary: null,\n summaryEventSeq: null,\n shadowedItemCount: null,\n shadowedTokenCount: null,\n shadowedCharCount: null,\n summaryCharCount: null,\n summaryTokenCount: null,\n };\n}\n\n/**\n * 压缩失败节点的载荷:end 已到且带非中止 error、无检查点时使用。\n * error 取 compaction/end 载荷中的实际报错文案(非字符串或为空时回落 null);\n * 摘要与统计字段全部为 null。\n */\nfunction failureData(\n start: ConversationMatch | undefined,\n end: ConversationMatch,\n): OmCompactionChatData {\n const data = end.event.data as { error?: unknown } | undefined;\n return {\n running: false,\n phase: startPhase(start),\n error: typeof data?.error === 'string' && data.error !== '' ? data.error : null,\n summary: null,\n summaryEventSeq: null,\n shadowedItemCount: null,\n shadowedTokenCount: null,\n shadowedCharCount: null,\n summaryCharCount: null,\n summaryTokenCount: 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 / 替换检查点 / end\n * 作为 update 折叠证据。有检查点时产出摘要卡片;end 已到且无检查点时,error 为\n * 非中止报错则产出可见失败行,error 为中止标识或缺失则以 hidden 撤回进行中提示行\n * (已物化节点以 hidden visibility 保留同一 key);仅 start 时渲染「正在压缩上下文…」\n * 提示行。\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 已到达但无检查点:error 为非中止的实际报错时渲染可见失败行;\n // 无 error 或 error 以 COMPACTION_ABORTED_ERROR 开头(用户中止)时,\n // 以 hidden visibility 撤回进行中提示行\n if (state.end !== undefined) {\n const anchorSeq = context.start?.event.seq ?? state.end.event.seq;\n const endData = state.end.event.data as { error?: unknown } | undefined;\n const isFailure =\n typeof endData?.error === 'string' &&\n endData.error !== '' &&\n !endData.error.startsWith(COMPACTION_ABORTED_ERROR);\n if (isFailure) {\n return chatNode(\n context,\n COMPACTION_CARD_KIND,\n anchorSeq,\n failureData(context.start, state.end),\n );\n }\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/** 功能降级警告行载荷:降级问题标识与面向用户的说明(信封载荷非法时为 null)。 */\nexport interface OmWarningChatData {\n /** 降级问题标识(om/warning 信封载荷 problem)。 */\n readonly problem: string | null;\n /** 面向用户的降级说明(om/warning 信封载荷 message)。 */\n readonly message: string | null;\n}\n\n/**\n * 插件功能降级警告定义:每条 om/warning 信封事件独立成节点(id 含 seq),渲染为可\n * 展开的「功能降级」警告行。服务端按会话同问题去重后追加,每会话同一问题至多一行;\n * 不带 om 信封的 feedback/record 记录(用户自写反馈)不认领。\n */\nexport const omWarningDefinition: ConversationNodeDefinition = {\n kind: OM_WARNING_CARD_KIND,\n target: 'chat',\n match: (event) => {\n if (readOmEvent(event)?.kind !== 'om/warning') return null;\n return { id: `om-warning:${event.seq}`, role: 'start' };\n },\n start: () => ({}),\n update: (context) => context.state,\n buildViewNode: (context) => {\n const start = context.start;\n if (start === undefined) return null;\n const om = readOmEvent(start.event);\n if (om?.kind !== 'om/warning') return null;\n const problem = om.data.problem !== '' ? om.data.problem : null;\n const message = om.data.message !== '' ? om.data.message : null;\n if (problem === null && message === null) return null;\n return chatNode(context, OM_WARNING_CARD_KIND, start.event.seq, { problem, message });\n },\n};\n","/**\n * `om-compaction` namespace 字典:插件压缩卡片的界面文案(zh/en)。\n * 导出 NS / zh / en;zh 为键集事实源,en 键集与 zh 一致。\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.failed': '上下文压缩失败',\n 'compaction.failed.observe': '上下文压缩失败(观察)',\n 'compaction.failed.reflect': '上下文压缩失败(反思)',\n warning: '上下文压缩功能降级',\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.failed': 'Context compaction failed',\n 'compaction.failed.observe': 'Context compaction failed (observation)',\n 'compaction.failed.reflect': 'Context compaction failed (reflection)',\n warning: 'Context compaction degraded',\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 * 压缩卡片统计行的紧凑数字格式化。导出 formatCompactCount。\n * <1k 原样;≥1k 用 k;≥1w(万)用 w;≥1M 用 M;保留 1 位小数并去掉末尾 .0。\n */\nexport function formatCompactCount(value: number): string {\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 * 压缩卡片渲染器:与宿主「上下文注入」卡片同款的折叠行。\n * 复用平台原语 DisclosureRow;行内统计与展开体样式按宿主 ContextInjectionRow 的规则\n * 注入 <style>(宿主该卡片样式是编译期哈希的 CSS Module,插件 bundle 无法引用其类名)。\n * 卡片报告模型从何处起不再看到那段历史;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/** 注册卡片样式(无 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 // 压缩进行中:不可展开的「正在压缩上下文…」提示行(按 phase 区分文案)\n if (data.running) {\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 // 压缩失败:可展开的错误行(按 phase 区分文案),折叠行为错误摘要、展开体为完整报错\n if (data.error !== null) {\n const failedLabel =\n data.phase === 'observe'\n ? t('compaction.failed.observe')\n : data.phase === 'reflect'\n ? t('compaction.failed.reflect')\n : t('compaction.failed');\n const errorSummary = data.error.length > 80 ? `${data.error.slice(0, 80)}…` : data.error;\n return (\n <div style={styles.row}>\n <DisclosureRow\n className={css.root}\n icon={<IconBrowseOutline16 size={14} />}\n chevronClassName={css.chevron}\n title={failedLabel}\n collapsedContent={\n <>\n <span className={css.sep} aria-hidden />\n <span className={css.summary}>{errorSummary}</span>\n </>\n }\n keepContentWhenOpen\n expandable\n expandOnRowClick\n open={expanded}\n onToggle={() => {\n setExpanded((value) => !value);\n }}\n >\n <div className={css.body} style={styles.body}>\n {data.error}\n </div>\n </DisclosureRow>\n </div>\n );\n }\n const expandable = data.summary !== null;\n const open = expandable && expanded;\n // 统计行:优先完整压缩前后统计,载荷字段不全时逐级回落\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 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 </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 /** 摘要与失败报错为多行文本,保留换行。 */\n body: { whiteSpace: 'pre-wrap' },\n};\n","/**\n * 功能降级警告行渲染器:与压缩卡片同款的折叠行。\n * 折叠行显示降级说明摘要,展开体显示完整说明;数据来自 om/warning 信封载荷\n * (借用 feedback/record),服务端按会话同问题去重后追加(每会话同一问题至多一行)。\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 OmWarningCardProps {\n readonly node: ChatNode<'om-warning'>;\n readonly t: TranslateNS<'om-compaction'>;\n}\n\n/** 折叠式功能降级警告行(降级说明摘要行 + 完整说明展开)。 */\nexport const OmWarningCard = memo(function OmWarningCard({ node, t }: OmWarningCardProps) {\n const [expanded, setExpanded] = useState(false);\n const { data } = node;\n const message = data.message ?? (data.problem !== null ? data.problem : '');\n const expandable = message !== '';\n const messageSummary = message.length > 80 ? `${message.slice(0, 80)}…` : message;\n return (\n <div>\n <DisclosureRow\n className=\"om-compaction-root\"\n icon={<IconBrowseOutline16 size={14} />}\n chevronClassName=\"om-compaction-chevron\"\n title={t('warning')}\n collapsedContent={\n expandable ? (\n <>\n <span className=\"om-compaction-sep\" aria-hidden />\n <span className=\"om-compaction-summary\">{messageSummary}</span>\n </>\n ) : undefined\n }\n keepContentWhenOpen\n expandable={expandable}\n expandOnRowClick\n open={expandable && expanded}\n onToggle={() => {\n if (expandable) setExpanded((value) => !value);\n }}\n >\n {expandable && (\n <div className=\"om-compaction-body\" style={bodyStyle}>\n {message}\n </div>\n )}\n </DisclosureRow>\n </div>\n );\n});\n\nconst bodyStyle: CSSProperties = { whiteSpace: 'pre-wrap' };\n","/**\n * 浏览器客户端入口(exports[\"./client\"] → dist/client.js):注册压缩卡片与功能降级\n * 警告行的 locale 字典、conversation 业务定义与 keyed 渲染器。宿主 conversation UI 只\n * 识别内置 'compact' 检查点,插件自产的压缩检查点(source.plugin = 'dsh-plugin-om')\n * 由本 bundle 认领并在消息列表渲染「已压缩」卡片;om 警告信封事件(借用\n * feedback/record)渲染为「功能降级」警告行。\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 声明合并。\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client';\nimport { omCompactionDefinition, omWarningDefinition } from './definition.ts';\nimport { en, NS, zh } from './locales.ts';\nimport { OmCompactionCard } from './OmCompactionCard.tsx';\nimport { OmWarningCard } from './OmWarningCard.tsx';\n\n/** Required services: 聊天节点槽、会话事件注册表与 locale 服务。 */\nexport const inject = ['slots', 'conversationEvents', 'locale'];\n\n/** Client plugin body: 注册压缩卡片的业务定义与 keyed 渲染器(随插件卸载移除)。 */\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.effect(\n () => ctx.conversationEvents.register(omWarningDefinition),\n 'dsh-plugin-om: om warning 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 ctx.slots.inject('conversation.chat.node', () =>\n ctx.slots.register(\n { name: 'conversation.chat.node', key: 'om-warning', locale: NS },\n OmWarningCard,\n ),\n );\n}\n"],"mappings":"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"}
@@ -70,10 +70,9 @@ export declare function buildObserveView(session: Session, seqs: readonly number
70
70
  /** 提取 <history> 块的内文(去开/闭标签;非块文本原样返回)。 */
71
71
  export declare function historyInner(text: string): string;
72
72
  /**
73
- * 向元素追加 CDATA 正文:正文统一以 CDATA 包裹(逐字原样,不做实体转义)。
74
- * 正文含 ]]> 时拆为相邻多个 CDATA 段,拼接后逐字还原原文,对读取方透明。
73
+ * 向元素追加 CDATA 正文(实现在 utils.ts,此处再导出保持既有导入路径)。
75
74
  */
76
- export declare function appendCdataText(doc: Document, el: Element, text: string): void;
75
+ export { appendCdataText } from './utils.ts';
77
76
  /**
78
77
  * 反思视图:全部 <history> 块(historySection 收集,按表层顺序)的内条目投影为条目。
79
78
  * 要求区间为全部块内条目引用的最小 / 最大 index;块解析失败降级为不可定位遗留条目;
@@ -85,7 +84,8 @@ export declare function buildReflectView(blocks: Array<{
85
84
  }>, options?: BuildViewOptions): CompressionView;
86
85
  /**
87
86
  * 把一个视图条目构建为 XML 元素(正文统一以 CDATA 包裹,逐字原样;user 条目的注释
88
- * 输出为 XML 注释节点)。getHistory 输出与最终 <history> 块共用。
87
+ * 输出为 XML 注释节点;sys 条目为无正文的空元素,自闭合输出)。getHistory 输出与
88
+ * 最终 <history> 块共用。
89
89
  */
90
90
  export declare function entryToElement(doc: Document, entry: ViewEntry): Element;
91
91
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"compress-view.d.ts","sourceRoot":"","sources":["../src/compress-view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAIxD,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG3D;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,YAAY;IACZ,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC;IACjD,6CAA6C;IAC7C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG;IAC5B,uDAAuD;IACvD,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAyCF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAaxF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAmBrF;AAED,sDAAsD;AACtD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+BAA+B;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAmEjB;AAiBD,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASjD;AAyCD;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAW9E;AAsFD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5C,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAUjB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAmDvE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAO7D"}
1
+ {"version":3,"file":"compress-view.d.ts","sourceRoot":"","sources":["../src/compress-view.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAIxD,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG3D;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG;IACtB,YAAY;IACZ,IAAI,EAAE,MAAM,GAAG,KAAK,GAAG,WAAW,GAAG,WAAW,CAAC;IACjD,6CAA6C;IAC7C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,kDAAkD;IAClD,IAAI,EAAE,MAAM,CAAC;IACb,2CAA2C;IAC3C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GAAG;IAC5B,uDAAuD;IACvD,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAyCF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAaxF;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,eAAe,GAAG,MAAM,GAAG,SAAS,CAmBrF;AAED,sDAAsD;AACtD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,+BAA+B;IAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAmEjB;AAiBD,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASjD;AAyCD;;GAEG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAsF7C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,EAC5C,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAUjB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAkDvE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAO7D"}
@@ -18,7 +18,7 @@ export declare const COMPLETE_MESSAGE_DEFINITION = "`\u5B8C\u6574\u6D88\u606F`\u
18
18
  /**
19
19
  * 最终 <history> 块内文块首的格式说明注释(XML 注释,完整消息定义 + 条目标签语义 + CDATA 约定)。
20
20
  */
21
- export declare const HISTORY_FORMAT_NOTE = "<!-- \u5B8C\u6574\u6D88\u606F\uFF1A`\u5B8C\u6574\u6D88\u606F`\u6307\u4E00\u6761`\u7528\u6237\u6D88\u606F`\u3001`\u7CFB\u7EDF\u6D88\u606F`\u3001`\u6A21\u578B\u8F93\u51FA\u6587\u672C`\u6216`\u5177\u6709result\u7684toolcall`\uFF1B\u9996\u6761 index \u4E3A 0\uFF0C\u6309\u4F1A\u8BDD\u987A\u5E8F\u9012\u589E\u3002 <TAG index=\"N\">\u8868\u793A\u5355\u6761\u5B8C\u6574\u6D88\u606F\uFF0C<TAG start=\"A\" end=\"B\"> \u8868\u793A\u8FDE\u7EED\u6A21\u5757\uFF0Cstart/end \u662F\u9996\u5C3E\u5B8C\u6574\u6D88\u606F\u7684 index\uFF1B<sys type=\"KIND\" index=\"N\"> \u8868\u793A\u88AB\u538B\u7F29\u7684\u7CFB\u7EDF\u6D88\u606F\uFF0C\u5757\u4E2D\u4E3A\u7A7A\uFF1B\u6761\u76EE\u6B63\u6587\u4E00\u5F8B\u4EE5 CDATA \u5305\u88F9\uFF0CCDATA \u5185\u4E3A\u9010\u5B57\u539F\u6837\u5185\u5BB9\uFF1B<skill_content name=\"S\" index=\"N\"> \u8868\u793A\u672A\u538B\u7F29\u7684 skill \u52A0\u8F7D\u6761\u76EE\uFF0C\u5185\u542B <skill_resources> \u4E0E <skill_instructions> \u4E24\u6BB5\uFF0C\u5404\u81EA\u4EE5 CDATA \u5305\u88F9\uFF0C\u5185\u6587\u4E3A\u5176\u5DE5\u5177\u8FD4\u56DE\u5185\u5BB9 -->";
21
+ export declare const HISTORY_FORMAT_NOTE = "<!-- `\u5B8C\u6574\u6D88\u606F`\u6307\u4E00\u6761`\u7528\u6237\u6D88\u606F`\u3001`\u7CFB\u7EDF\u6D88\u606F`\u3001`\u6A21\u578B\u8F93\u51FA\u6587\u672C`\u6216`\u5177\u6709result\u7684toolcall`\uFF1B\u9996\u6761 index \u4E3A 0\uFF0C\u6309\u4F1A\u8BDD\u987A\u5E8F\u9012\u589E\u3002 <TAG index=\"N\">\u8868\u793A\u5355\u6761\u5B8C\u6574\u6D88\u606F\uFF0C<TAG start=\"A\" end=\"B\"> \u8868\u793A\u591A\u6761\u8FDE\u7EED\u6D88\u606F\uFF0Cstart/end \u662F\u9996\u5C3E\u5B8C\u6574\u6D88\u606F\u7684 index\uFF1B\u6D88\u606F\u5757\u7684\u5185\u5BB9\u662F\u7528CDATA\u5305\u88F9\u7684\u7EAF\u6587\u672C\uFF1B<sys type=\"KIND\" index=\"N\"> \u8868\u793A\u88AB\u538B\u7F29\u7684\u7CFB\u7EDF\u6D88\u606F\uFF0C\u5757\u4E2D\u4E3A\u7A7A\uFF1B<skill_content name=\"S\" index=\"N\"> \u8868\u793A\u672A\u538B\u7F29\u7684\u539F\u59CB skill -->";
22
22
  /** skill 工具名:toolcall 条目的工具名为该值时视为 skill 加载,<history> 块中以 <skill_content> 元素呈现。 */
23
23
  export declare const SKILL_TOOL_NAME = "skill";
24
24
  /** 旧日志压缩消息的宿主 checkpoint 标记 plugin 名(历史兼容识别用)。 */
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,uCAAuC;AACvC,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,iDAAiD;AACjD,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,iDAAiD;AACjD,eAAO,MAAM,WAAW,iHAAuB,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,sRACqC,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,mBAAmB,ojCAAyU,CAAC;AAE1W,mFAAmF;AACnF,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC,kDAAkD;AAClD,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,6DAA0B,CAAC;AAEhE,uFAAuF;AACvF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACrD,OAAO,CAGT"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,uCAAuC;AACvC,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,iDAAiD;AACjD,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,iDAAiD;AACjD,eAAO,MAAM,WAAW,iHAAuB,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,sRACqC,CAAC;AAE9E;;GAEG;AACH,eAAO,MAAM,mBAAmB,2zBAAgP,CAAC;AAEjR,mFAAmF;AACnF,eAAO,MAAM,eAAe,UAAU,CAAC;AAEvC,kDAAkD;AAClD,eAAO,MAAM,yBAAyB,YAAY,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,wBAAwB,6DAA0B,CAAC;AAEhE,uFAAuF;AACvF,wBAAgB,mBAAmB,CACjC,MAAM,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,GACrD,OAAO,CAGT"}
package/dist/index.mjs CHANGED
@@ -33,7 +33,7 @@ const COMPLETE_MESSAGE_DEFINITION = "`完整消息`指一条`用户消息`、`
33
33
  /**
34
34
  * 最终 <history> 块内文块首的格式说明注释(XML 注释,完整消息定义 + 条目标签语义 + CDATA 约定)。
35
35
  */
36
- const HISTORY_FORMAT_NOTE = `<!-- 完整消息:${COMPLETE_MESSAGE_DEFINITION} <TAG index="N">表示单条完整消息,<TAG start="A" end="B"> 表示连续模块,start/end 是首尾完整消息的 index;<sys type="KIND" index="N"> 表示被压缩的系统消息,块中为空;条目正文一律以 CDATA 包裹,CDATA 内为逐字原样内容;<skill_content name="S" index="N"> 表示未压缩的 skill 加载条目,内含 <skill_resources> 与 <skill_instructions> 两段,各自以 CDATA 包裹,内文为其工具返回内容 -->`;
36
+ const HISTORY_FORMAT_NOTE = `<!-- ${COMPLETE_MESSAGE_DEFINITION} <TAG index="N">表示单条完整消息,<TAG start="A" end="B"> 表示多条连续消息,start/end 是首尾完整消息的 index;消息块的内容是用CDATA包裹的纯文本;<sys type="KIND" index="N"> 表示被压缩的系统消息,块中为空;<skill_content name="S" index="N"> 表示未压缩的原始 skill -->`;
37
37
  /** skill 工具名:toolcall 条目的工具名为该值时视为 skill 加载,<history> 块中以 <skill_content> 元素呈现。 */
38
38
  const SKILL_TOOL_NAME = "skill";
39
39
  /**
@@ -99,7 +99,7 @@ function renderMessageText(message) {
99
99
  const parts = [];
100
100
  for (const block of message.content) if (block.type === "text") parts.push(block.text);
101
101
  else if (block.type === "tool-call") parts.push(`[tool-call ${block.name} id=${String(block.id)}]
102
- ${safeJson(block.arguments)}`);
102
+ ${toolArgsJson(block.arguments)}`);
103
103
  else if (block.type === "tool-result") parts.push(blocksToText(block.content));
104
104
  return parts.join("\n");
105
105
  }
@@ -111,6 +111,27 @@ function safeJson(value) {
111
111
  return String(value);
112
112
  }
113
113
  }
114
+ /**
115
+ * 工具调用参数的 JSON 文本(CDATA 内保持只有一层 JSON 转义):
116
+ * 字符串参数是模型产出的原始 JSON 串,原样返回(本身即合法 JSON,不再二次序列化);
117
+ * 其余结构 JSON.stringify 序列化一次。
118
+ */
119
+ function toolArgsJson(args) {
120
+ if (typeof args === "string") return args;
121
+ return safeJson(args);
122
+ }
123
+ /**
124
+ * 向元素追加 CDATA 正文:正文统一以 CDATA 包裹(逐字原样,不做实体转义)。
125
+ * 正文含 ]]> 时拆为相邻多个 CDATA 段,拼接后逐字还原原文,对读取方透明。
126
+ */
127
+ function appendCdataText(doc, el, text) {
128
+ const parts = text.split("]]>");
129
+ for (let i = 0; i < parts.length; i += 1) if (i === 0) el.appendChild(doc.createCDATASection(parts[0] ?? ""));
130
+ else {
131
+ el.appendChild(doc.createCDATASection("]]"));
132
+ el.appendChild(doc.createCDATASection(`>${parts[i] ?? ""}`));
133
+ }
134
+ }
114
135
  /** 主会话判定:subagent 会话 header.origin === 'subagent'。 */
115
136
  function isMainSession(session) {
116
137
  return session.header?.origin !== "subagent";
@@ -7388,7 +7409,8 @@ var import_lib = (/* @__PURE__ */ __commonJSMin(((exports) => {
7388
7409
  * 会话日志索引:完整消息索引与渲染。
7389
7410
  * 导出 indexCompleteMessages(完整消息四类折叠索引,recall 与摘要共用同一套编号)、
7390
7411
  * indexMessages / surfaceIndexOf / messageIdOfEvent(消息级定位辅助)、
7391
- * collectImageRefs / toolResultMessageOf / renderToolResultText / renderCompleteMessageParts /
7412
+ * collectImageRefs / toolCallBlockOf / toolResultMessageOf / renderToolResultText /
7413
+ * renderCompleteMessageParts /
7392
7414
  * renderCompleteMessage(完整消息渲染与图片附件收集)。事件日志仅追加(被遮蔽的事件仍可读,
7393
7415
  * recall 依赖此性质)。
7394
7416
  *
@@ -7491,6 +7513,19 @@ function collectImageRefs(content, out) {
7491
7513
  }
7492
7514
  }
7493
7515
  /**
7516
+ * 取 toolcall 完整消息对应的 tool-call 块(调用参数渲染与 XML 条目渲染共用):
7517
+ * 非 toolcall、调用事件缺失或块内 id 不匹配时返回 undefined。
7518
+ */
7519
+ function toolCallBlockOf(session, cm) {
7520
+ if (cm.type !== "toolcall") return void 0;
7521
+ const callSeq = cm.seqs[0];
7522
+ const callEvent = callSeq === void 0 ? void 0 : session.events[callSeq];
7523
+ if (callEvent?.type !== "assistant/message") return void 0;
7524
+ const message = session.deriveEventMessage(callEvent);
7525
+ if (!message || !Array.isArray(message.content)) return void 0;
7526
+ for (const block of message.content) if (block.type === "tool-call" && String(block.id ?? "") === (cm.callId ?? "")) return block;
7527
+ }
7528
+ /**
7494
7529
  * 取 toolcall 完整消息的 tool/result 消息(超大结果经 pruner 裁剪):
7495
7530
  * 结果文本渲染与 <skill> 条目正文共用;非 toolcall 或无配对 result 时返回 undefined。
7496
7531
  */
@@ -7546,19 +7581,8 @@ function renderCompleteMessageParts(session, cm, pruner) {
7546
7581
  };
7547
7582
  }
7548
7583
  const parts = [];
7549
- const callSeq = cm.seqs[0];
7550
- const callEvent = callSeq === void 0 ? void 0 : session.events[callSeq];
7551
- if (callEvent?.type === "assistant/message") {
7552
- const message = session.deriveEventMessage(callEvent);
7553
- if (message && Array.isArray(message.content)) {
7554
- let call;
7555
- for (const block of message.content) if (block.type === "tool-call" && String(block.id ?? "") === (cm.callId ?? "")) {
7556
- call = block;
7557
- break;
7558
- }
7559
- if (call) parts.push(`[tool-call ${String(call.name ?? "")} id=${String(call.id ?? "")}]\n${safeJson(call.arguments)}`);
7560
- }
7561
- }
7584
+ const call = toolCallBlockOf(session, cm);
7585
+ if (call) parts.push(`[tool-call ${String(call.name ?? "")} id=${String(call.id ?? "")}]\n${toolArgsJson(call.arguments)}`);
7562
7586
  const resultMessage = toolResultMessageOf(session, cm, pruner);
7563
7587
  if (resultMessage && Array.isArray(resultMessage.content)) {
7564
7588
  collectImageRefs(resultMessage.content, images);
@@ -7577,7 +7601,7 @@ function renderCompleteMessage(session, cm, pruner) {
7577
7601
  //#endregion
7578
7602
  //#region src/compress-view.ts
7579
7603
  /** 静默 DOMParser:非致命解析问题不刷 console,fatalError 仍抛 ParseError、解析语义不变。 */
7580
- function newQuietParser() {
7604
+ function newQuietParser$1() {
7581
7605
  return new import_lib.DOMParser({ onError: () => {} });
7582
7606
  }
7583
7607
  /**
@@ -7774,18 +7798,6 @@ function skillContentSections(text) {
7774
7798
  };
7775
7799
  }
7776
7800
  /**
7777
- * 向元素追加 CDATA 正文:正文统一以 CDATA 包裹(逐字原样,不做实体转义)。
7778
- * 正文含 ]]> 时拆为相邻多个 CDATA 段,拼接后逐字还原原文,对读取方透明。
7779
- */
7780
- function appendCdataText(doc, el, text) {
7781
- const parts = text.split("]]>");
7782
- for (let i = 0; i < parts.length; i += 1) if (i === 0) el.appendChild(doc.createCDATASection(parts[0] ?? ""));
7783
- else {
7784
- el.appendChild(doc.createCDATASection("]]"));
7785
- el.appendChild(doc.createCDATASection(`>${parts[i] ?? ""}`));
7786
- }
7787
- }
7788
- /**
7789
7801
  * 解析一个已有 <history> 块的内条目(反思视图):user_message / sys / assistant
7790
7802
  * (index 单条或 start/end 区间)/ skill_content(name 属性 + index 定位)/ reasoning。
7791
7803
  * 整块无法解析或根非 <history> 时降级为单条不可定位的历史遗留条目(text 为块内文原文,
@@ -7799,7 +7811,7 @@ function parseBlockEntries(blockText, blockSeq) {
7799
7811
  }];
7800
7812
  let doc;
7801
7813
  try {
7802
- doc = newQuietParser().parseFromString(blockText, "text/xml");
7814
+ doc = newQuietParser$1().parseFromString(blockText, "text/xml");
7803
7815
  } catch {
7804
7816
  return opaque();
7805
7817
  }
@@ -7897,7 +7909,8 @@ function buildReflectView(blocks, options = {}) {
7897
7909
  }
7898
7910
  /**
7899
7911
  * 把一个视图条目构建为 XML 元素(正文统一以 CDATA 包裹,逐字原样;user 条目的注释
7900
- * 输出为 XML 注释节点)。getHistory 输出与最终 <history> 块共用。
7912
+ * 输出为 XML 注释节点;sys 条目为无正文的空元素,自闭合输出)。getHistory 输出与
7913
+ * 最终 <history> 块共用。
7901
7914
  */
7902
7915
  function entryToElement(doc, entry) {
7903
7916
  if (entry.kind === "user") {
@@ -7911,7 +7924,6 @@ function entryToElement(doc, entry) {
7911
7924
  const el = doc.createElement("sys");
7912
7925
  el.setAttribute("type", entry.sysKind ?? "");
7913
7926
  if (entry.lo !== void 0) el.setAttribute("index", String(entry.lo));
7914
- appendCdataText(doc, el, "");
7915
7927
  return el;
7916
7928
  }
7917
7929
  if (entry.kind === "reasoning") {
@@ -7948,7 +7960,7 @@ function entryToElement(doc, entry) {
7948
7960
  */
7949
7961
  function renderEntriesXml(entries) {
7950
7962
  if (entries.length === 0) return "";
7951
- const doc = newQuietParser().parseFromString("<root />", "text/xml");
7963
+ const doc = newQuietParser$1().parseFromString("<root />", "text/xml");
7952
7964
  const serializer = new import_lib.XMLSerializer();
7953
7965
  return entries.map((entry) => serializer.serializeToString(entryToElement(doc, entry))).join("\n");
7954
7966
  }
@@ -8379,7 +8391,7 @@ function buildCompressionPrompt(skipReasoning) {
8379
8391
  "- 单条重要的完整消息以 index 单独压缩",
8380
8392
  "- 压缩后的 assistant 消息内,应当描述**行为逻辑**,强调关键的**结论、产出和任务**;涉及到的具体文件保留完整路径",
8381
8393
  "- 摘要粒度越往后越细:靠近末尾(最近)的消息保留更多细节,开头(较早)的消息简写。",
8382
- "- <history> 条目正文一律以 CDATA 包裹,CDATA 内为逐字原样内容。",
8394
+ "- <history> 条目正文以 CDATA 包裹(空的 <sys> 条目为自闭合空元素,无正文),CDATA 内为逐字原样内容。",
8383
8395
  "- 加载的 skill 以 <skill_content name=\"…\" index=\"N\"> 条目呈现,内含 <skill_resources> 与 <skill_instructions> 两段,内文为其工具返回内容,属于关键信息。仅在你明确判断该 skill 与后续任务无关时,压缩它;如果该 skill 与后续任务相关,或者你无法判断,不要压缩,**保持原文**。",
8384
8396
  "- 未压缩的条目将原样保留;宁可保留也不要强行压缩不确定的内容。",
8385
8397
  "- 全部完成后调用 completeCompression 结束;不要输出与工具调用无关的文本。"
@@ -9553,11 +9565,11 @@ function cosineSimilarity(a, b) {
9553
9565
  */
9554
9566
  /** 默认配置(冻结对象,resolveConfig 合并的基底;debug 缺省值在解析时按 NODE_ENV 判定)。 */
9555
9567
  const DEFAULT_CONFIG = Object.freeze({
9556
- observeThresholdTokens: 45e3,
9557
- reflectThresholdTokens: 12e4,
9568
+ observeThresholdTokens: 35e3,
9569
+ reflectThresholdTokens: 4e4,
9558
9570
  compressMaxTokens: void 0,
9559
9571
  rateLimitWaitMs: 6e4,
9560
- tailMessageCount: 5,
9572
+ tailMessageCount: 20,
9561
9573
  compressSkipReasoning: true,
9562
9574
  omEnabled: true,
9563
9575
  debug: false,
@@ -9640,7 +9652,7 @@ const RECALL_OUTPUT_SCHEMA = {
9640
9652
  properties: {
9641
9653
  text: {
9642
9654
  type: "string",
9643
- description: "完整消息内容的文本部分(带图位置以 [图片附件:…] 标注行提示)。"
9655
+ description: "完整消息的 XML 条目序列(CDATA 包裹正文;图片位置以 XML 注释 [图片附件:…] 标注)。"
9644
9656
  },
9645
9657
  images: {
9646
9658
  type: "array",
@@ -9702,12 +9714,112 @@ function renderRecallOutput(value) {
9702
9714
  return blocks;
9703
9715
  }
9704
9716
  //#endregion
9717
+ //#region src/recall-xml.ts
9718
+ /** 静默 DOMParser:非致命解析问题不刷 console。 */
9719
+ function newQuietParser() {
9720
+ return new import_lib.DOMParser({ onError: () => {} });
9721
+ }
9722
+ /** XML 注释文本:注释内不允许连续连字符,`--` 统一替换为破折号。 */
9723
+ function xmlCommentText(text) {
9724
+ return text.replace(/--/g, "——");
9725
+ }
9726
+ /** 向元素追加图片附件注释(每张图片一条,附在元素正文之后)。 */
9727
+ function appendImageComments(doc, el, images) {
9728
+ for (const ref of images) el.appendChild(doc.createComment(` ${xmlCommentText(imageNote(ref))} `));
9729
+ }
9730
+ /** 正文有内容时以 CDATA 追加,无内容时不追加(元素自然自闭合或仅含注释)。 */
9731
+ function appendBodyText(doc, el, text) {
9732
+ if (text !== "") appendCdataText(doc, el, text);
9733
+ }
9734
+ /** toolcall 完整消息的元素:<assistant type="toolcall"> 内嵌 <tool-args> / <tool-result>。 */
9735
+ function toolcallElement(doc, session, cm, pruner, images) {
9736
+ const el = doc.createElement("assistant");
9737
+ el.setAttribute("index", String(cm.index));
9738
+ el.setAttribute("type", "toolcall");
9739
+ const call = toolCallBlockOf(session, cm);
9740
+ const toolName = call === void 0 ? "" : String(call.name ?? "");
9741
+ if (toolName !== "") el.setAttribute("tool-name", toolName);
9742
+ if (cm.callId) el.setAttribute("callId", cm.callId);
9743
+ if (call) {
9744
+ const args = doc.createElement("tool-args");
9745
+ appendCdataText(doc, args, toolArgsJson(call.arguments));
9746
+ el.appendChild(args);
9747
+ }
9748
+ const resultMessage = toolResultMessageOf(session, cm, pruner);
9749
+ if (resultMessage && Array.isArray(resultMessage.content)) {
9750
+ collectImageRefs(resultMessage.content, images);
9751
+ const resultText = renderMessageText(resultMessage);
9752
+ if (resultText.trim() !== "") {
9753
+ const result = doc.createElement("tool-result");
9754
+ appendCdataText(doc, result, resultText);
9755
+ el.appendChild(result);
9756
+ }
9757
+ }
9758
+ appendImageComments(doc, el, images);
9759
+ return el;
9760
+ }
9761
+ /** 按完整消息类别构建元素(user / sys / assistant 文本条目复用 parts 渲染)。 */
9762
+ function buildElement(doc, session, cm, pruner, images) {
9763
+ if (cm.type === "toolcall") return toolcallElement(doc, session, cm, pruner, images);
9764
+ const parts = renderCompleteMessageParts(session, cm, pruner);
9765
+ images.push(...parts.images);
9766
+ if (cm.type === "sys") {
9767
+ const el = doc.createElement("sys");
9768
+ el.setAttribute("index", String(cm.index));
9769
+ if (cm.kind !== void 0) el.setAttribute("type", cm.kind);
9770
+ appendBodyText(doc, el, parts.text);
9771
+ appendImageComments(doc, el, parts.images);
9772
+ return el;
9773
+ }
9774
+ const el = doc.createElement(cm.type === "user" ? "user_message" : "assistant");
9775
+ el.setAttribute("index", String(cm.index));
9776
+ if (cm.type === "assistant") el.setAttribute("type", "text");
9777
+ appendBodyText(doc, el, parts.text);
9778
+ appendImageComments(doc, el, parts.images);
9779
+ return el;
9780
+ }
9781
+ /** 主渲染失败时的回退元素:按完整消息类别输出最简形态,正文为回退文本。 */
9782
+ function fallbackElement(doc, cm, text) {
9783
+ if (cm.type === "sys") {
9784
+ const el = doc.createElement("sys");
9785
+ el.setAttribute("index", String(cm.index));
9786
+ if (cm.kind !== void 0) el.setAttribute("type", cm.kind);
9787
+ return el;
9788
+ }
9789
+ const el = doc.createElement(cm.type === "user" ? "user_message" : "assistant");
9790
+ el.setAttribute("index", String(cm.index));
9791
+ if (cm.type !== "user") el.setAttribute("type", cm.type === "toolcall" ? "toolcall" : "text");
9792
+ appendBodyText(doc, el, text);
9793
+ return el;
9794
+ }
9795
+ /**
9796
+ * 渲染一条完整消息为 XML 条目(序列化后的字符串)与其携带的图片附件元数据。
9797
+ * 主渲染失败且提供 fallbackText 时降级为最简元素;两者皆不可用时向上抛出,
9798
+ * 由调用方决定跳过或回退。
9799
+ */
9800
+ function renderCompleteMessageXml(session, cm, options = {}) {
9801
+ const images = [];
9802
+ const doc = newQuietParser().parseFromString("<root />", "text/xml");
9803
+ let el;
9804
+ try {
9805
+ el = buildElement(doc, session, cm, options.pruner, images);
9806
+ } catch (error) {
9807
+ if (options.fallbackText === void 0) throw error;
9808
+ images.length = 0;
9809
+ el = fallbackElement(doc, cm, options.fallbackText);
9810
+ }
9811
+ return {
9812
+ xml: new import_lib.XMLSerializer().serializeToString(el),
9813
+ images
9814
+ };
9815
+ }
9816
+ //#endregion
9705
9817
  //#region src/recall.ts
9706
9818
  /**
9707
9819
  * recall 工具:按完整消息 index 区间回看原始会话(含被压缩内容)。
9708
9820
  * 导出 recallArgsSchema / RecallArgs / parseRecallArgs / buildRecallTool。
9709
- * 输出值为 { text, images }(契约见 recall-output.ts);recall 自身不设输出上限,
9710
- * 超大工具结果由 tool-result-pruner 裁剪。
9821
+ * 输出值为 { text, images }(契约见 recall-output.ts),text 为 XML 条目序列
9822
+ * (形态见 recall-xml.ts);recall 自身不设输出上限,超大工具结果由 tool-result-pruner 裁剪。
9711
9823
  */
9712
9824
  /**
9713
9825
  * recall 工具参数 schema:start 必填;end 与 offset 至少提供一个(同时给出时 end 优先)。
@@ -9731,7 +9843,7 @@ function parseRecallArgs(raw) {
9731
9843
  function buildRecallTool(getPruner) {
9732
9844
  return {
9733
9845
  name: "recall",
9734
- description: `${COMPLETE_MESSAGE_DEFINITION}按 index 区间精确返回区间内全部完整消息的内容。`,
9846
+ description: `在你需要查看历史消息时调用。${COMPLETE_MESSAGE_DEFINITION}此工具按 index 区间精确返回区间内全部完整消息的内容。`,
9735
9847
  parameters: parametersFromZod(recallArgsSchema),
9736
9848
  output: {
9737
9849
  schema: RECALL_OUTPUT_SCHEMA,
@@ -9762,24 +9874,15 @@ function buildRecallTool(getPruner) {
9762
9874
  for (let i = lo; i <= hi; i += 1) {
9763
9875
  const cm = cms[i];
9764
9876
  if (!cm) continue;
9765
- let text = "";
9766
- let cmImages = [];
9767
9877
  try {
9768
- const rendered = renderCompleteMessageParts(session, cm, pruner);
9769
- text = rendered.text;
9770
- cmImages = rendered.images;
9878
+ const rendered = renderCompleteMessageXml(session, cm, { pruner });
9879
+ parts.push(rendered.xml);
9880
+ images.push(...rendered.images);
9771
9881
  } catch {}
9772
- const callAttr = cm.type === "toolcall" && cm.callId ? ` callId=${cm.callId}` : "";
9773
- let body = text;
9774
- if (cmImages.length > 0) {
9775
- body += (body === "" ? "" : "\n") + cmImages.map(imageNote).join("\n");
9776
- images.push(...cmImages);
9777
- }
9778
- parts.push(`-- [index ${cm.index}] ${cm.type}${callAttr} --\n${body}`);
9779
9882
  }
9780
9883
  if (parts.length === 0) return textOnly("指定区间没有完整消息");
9781
9884
  return {
9782
- text: parts.join("\n\n"),
9885
+ text: parts.join("\n"),
9783
9886
  images
9784
9887
  };
9785
9888
  }
@@ -9794,7 +9897,9 @@ function buildRecallTool(getPruner) {
9794
9897
  * resolveSemanticRange / tokenize / matchExplanation / buildSemanticRecallTool。
9795
9898
  * 向量为本地 ONNX 嵌入(cosine 相似度);只匹配文本,纯图片消息不进候选池;
9796
9899
  * 区间缺省检索全部、区间不合法回退全量并在输出中告知;输出契约同 recall
9797
- * ({ text, images },超大结果由 tool-result-pruner 裁剪);仅主会话可用。
9900
+ * ({ text, images },条目为 XML 形态见 recall-xml.ts,超大结果由 tool-result-pruner
9901
+ * 裁剪);候选池排除本次调用自身的 toolcall 完整消息(其渲染文本含 query 原文,
9902
+ * 必居榜首);仅主会话可用。
9798
9903
  */
9799
9904
  /** recall-semantic 工具参数 schema:query 必填;top_k 默认 3(1-10);区间参数均可选。 */
9800
9905
  const semanticRecallArgsSchema = z.object({
@@ -9882,7 +9987,7 @@ function buildSemanticRecallTool(options) {
9882
9987
  const embed = options?.embedder ?? ((texts) => getEmbedder().then((fn) => fn(texts)));
9883
9988
  return {
9884
9989
  name: "recall-semantic",
9885
- description: `${COMPLETE_MESSAGE_DEFINITION}按自然语言含义检索最符合的完整消息,默认全量搜索,可指定区间。注意:只匹配文本。`,
9990
+ description: `在你需要按语义检索历史消息时调用。${COMPLETE_MESSAGE_DEFINITION}此工具按自然语言含义检索最符合的完整消息,默认全量搜索,可指定区间。注意:只匹配文本。`,
9886
9991
  parameters: parametersFromZod(semanticRecallArgsSchema),
9887
9992
  output: {
9888
9993
  schema: RECALL_OUTPUT_SCHEMA,
@@ -9901,10 +10006,12 @@ function buildSemanticRecallTool(options) {
9901
10006
  end,
9902
10007
  offset
9903
10008
  });
10009
+ const selfCallId = String(exec.callId ?? "");
9904
10010
  const candidates = [];
9905
10011
  for (let i = range.lo; i <= range.hi; i += 1) {
9906
10012
  const cm = cms[i];
9907
10013
  if (!cm) continue;
10014
+ if (selfCallId !== "" && cm.type === "toolcall" && cm.callId === selfCallId) continue;
9908
10015
  let text = "";
9909
10016
  try {
9910
10017
  text = renderCompleteMessage(session, cm);
@@ -9929,27 +10036,32 @@ function buildSemanticRecallTool(options) {
9929
10036
  const parts = [];
9930
10037
  const images = [];
9931
10038
  const rangeNote = range.fallback ? "指定区间不合法(start/end 越界等),已回退检索全部消息" : start === void 0 ? `检索全部消息(${candidates.length} 条可嵌入)` : `检索区间 [${range.lo}..${range.hi}](${candidates.length} 条可嵌入)`;
9932
- parts.push(`查询: ${query}`);
9933
- parts.push(rangeNote);
9934
- parts.push(`匹配 TOP-${hits.length}(共 ${scored.length} 条候选):`);
10039
+ parts.push(`<!-- ${xmlCommentText(`查询: ${query}`)} -->`);
10040
+ parts.push(`<!-- ${xmlCommentText(rangeNote)} -->`);
10041
+ parts.push(`<!-- ${xmlCommentText(`匹配 TOP-${hits.length}(共 ${scored.length} 条候选)`)} -->`);
9935
10042
  for (let i = 0; i < hits.length; i += 1) {
9936
10043
  const hit = hits[i];
9937
10044
  if (!hit) continue;
9938
- const callAttr = hit.cm.type === "toolcall" && hit.cm.callId ? ` callId=${hit.cm.callId}` : "";
9939
- let text = hit.text;
10045
+ const note = `<!-- ${xmlCommentText(`${i + 1}. ${matchExplanation(query, hit.text, hit.score)}`)} -->`;
10046
+ let xml;
9940
10047
  let hitImages = [];
9941
10048
  try {
9942
10049
  const pruner = getPruner();
9943
- const rendered = renderCompleteMessageParts(session, hit.cm, pruner);
9944
- text = rendered.text;
10050
+ const rendered = renderCompleteMessageXml(session, hit.cm, {
10051
+ pruner,
10052
+ fallbackText: hit.text
10053
+ });
10054
+ xml = rendered.xml;
9945
10055
  hitImages = rendered.images;
9946
- } catch {}
9947
- parts.push(`-- [${i + 1}] index ${hit.cm.index} ${hit.cm.type}${callAttr} — ${matchExplanation(query, hit.text, hit.score)} --`);
9948
- parts.push(hitImages.length > 0 ? [text, ...hitImages.map(imageNote)].join("\n") : text);
10056
+ } catch {
10057
+ xml = hit.text;
10058
+ }
10059
+ parts.push(note);
10060
+ parts.push(xml);
9949
10061
  images.push(...hitImages);
9950
10062
  }
9951
10063
  return {
9952
- text: parts.join("\n\n"),
10064
+ text: parts.join("\n"),
9953
10065
  images
9954
10066
  };
9955
10067
  }
@@ -21,6 +21,15 @@ export declare function indexCompleteMessages(session: Session): CompleteMessage
21
21
  * image 块按附件元数据收集(字段不全则忽略);tool-result 块递归收集其 content。
22
22
  */
23
23
  export declare function collectImageRefs(content: unknown, out: ImageRefValue[]): void;
24
+ /**
25
+ * 取 toolcall 完整消息对应的 tool-call 块(调用参数渲染与 XML 条目渲染共用):
26
+ * 非 toolcall、调用事件缺失或块内 id 不匹配时返回 undefined。
27
+ */
28
+ export declare function toolCallBlockOf(session: Session, cm: CompleteMessage): {
29
+ name?: unknown;
30
+ id?: unknown;
31
+ arguments?: unknown;
32
+ } | undefined;
24
33
  /**
25
34
  * 取 toolcall 完整消息的 tool/result 消息(超大结果经 pruner 裁剪):
26
35
  * 结果文本渲染与 <skill> 条目正文共用;非 toolcall 或无配对 result 时返回 undefined。
@@ -1 +1 @@
1
- {"version":3,"file":"log-index.d.ts","sourceRoot":"","sources":["../src/log-index.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EACP,YAAY,EAEZ,OAAO,EACP,YAAY,EACb,MAAM,YAAY,CAAC;AAGpB,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG;IAAE,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,CAAC;AAE7F,oCAAoC;AACpC,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMpF;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAoB5D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,EAAE,CA+DzE;AACD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,IAAI,CA+B7E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,GAAG,SAAS,CASrB;AAED,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,EAAE,CAAA;CAAE,CAiD3C;AAED,kEAAkE;AAClE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB,MAAM,CAER"}
1
+ {"version":3,"file":"log-index.d.ts","sourceRoot":"","sources":["../src/log-index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EACP,YAAY,EAEZ,OAAO,EACP,YAAY,EACb,MAAM,YAAY,CAAC;AAGpB,+CAA+C;AAC/C,MAAM,MAAM,UAAU,GAAG;IAAE,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,KAAK,OAAO,EAAE,GAAG,IAAI,CAAA;CAAE,CAAC;AAE7F,oCAAoC;AACpC,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,gEAAgE;AAChE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMpF;AAED,6CAA6C;AAC7C,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CAoB5D;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,EAAE,CA+DzE;AACD;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,GAAG,IAAI,CA+B7E;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,GAClB;IAAE,IAAI,CAAC,EAAE,OAAO,CAAC;IAAC,EAAE,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,SAAS,CAWnE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,GAAG,SAAS,CASrB;AAED,4DAA4D;AAC5D,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB,MAAM,CAGR;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,EAAE,CAAA;CAAE,CAoC3C;AAED,kEAAkE;AAClE,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB,MAAM,CAER"}
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * recall / recall-semantic 共享输出契约:输出值为固定形态 { text, images }。
3
- * text 为完整消息的文本内容(带图消息附图片标注行),images 为图片附件元数据
4
- * (按 attachmentId 引用,不含字节)。导出 ImageRefValue / RecallOutputValue /
5
- * RECALL_OUTPUT_SCHEMA / textOnly / imageNote / renderRecallOutput。
3
+ * text 为完整消息的 XML 条目序列(形态见 recall-xml.ts,图片位置以 XML 注释标注),
4
+ * images 为图片附件元数据(按 attachmentId 引用,不含字节)。
5
+ * 导出 ImageRefValue / RecallOutputValue / RECALL_OUTPUT_SCHEMA / textOnly / imageNote /
6
+ * renderRecallOutput。
6
7
  */
7
8
  import type { ContentBlock } from '@deepseek-ai/dsh-llm';
8
9
  import type { JsonSchemaNode } from '@deepseek-ai/dsh-tools';
@@ -1 +1 @@
1
- {"version":3,"file":"recall-output.d.ts","sourceRoot":"","sources":["../src/recall-output.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,wDAAwD;AACxD,MAAM,MAAM,aAAa,GAAG;IAC1B,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,CAAC;AAEF,uBAAuB;AACvB,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAExD;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAGpD;AAED,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,EAAE,cA4BlC,CAAC;AAEF,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,EAAE,CAY3E"}
1
+ {"version":3,"file":"recall-output.d.ts","sourceRoot":"","sources":["../src/recall-output.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,sBAAsB,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,wDAAwD;AACxD,MAAM,MAAM,aAAa,GAAG;IAC1B,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW;IACX,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,kBAAkB;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,6DAA6D;AAC7D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,EAAE,CAAC;CACzB,CAAC;AAEF,uBAAuB;AACvB,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAExD;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAGpD;AAED,0CAA0C;AAC1C,eAAO,MAAM,oBAAoB,EAAE,cA6BlC,CAAC;AAEF,iDAAiD;AACjD,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,EAAE,CAY3E"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * 完整消息的 XML 条目渲染:recall / recall-semantic 的输出条目形态。
3
+ * 每条完整消息渲染为一个 XML 元素,正文统一以 CDATA 包裹(逐字原样,不做实体转义与
4
+ * 再次序列化),从构建源头避免多层转义:
5
+ * - user → <user_message index>,正文 CDATA,图片等非文本块为 XML 注释
6
+ * - sys → <sys index type>,无正文时自闭合(与压缩日志的 sys 条目同形态)
7
+ * - assistant 文本 → <assistant index type="text">,正文 CDATA
8
+ * - toolcall → <assistant index type="toolcall" tool-name callId>,内嵌 <tool-args>
9
+ * (合法 JSON,仅一层 JSON 转义)与 <tool-result>(工具返回正文)两个 CDATA 子元素
10
+ * 导出 RenderCompleteMessageXmlOptions / renderCompleteMessageXml / xmlCommentText。
11
+ */
12
+ import { type PrunerLike } from './log-index.ts';
13
+ import { type ImageRefValue } from './recall-output.ts';
14
+ import type { CompleteMessage, Session } from './types.ts';
15
+ /** 渲染选项:pruner 裁剪超大工具结果;fallbackText 为主渲染失败时的回退正文。 */
16
+ export type RenderCompleteMessageXmlOptions = {
17
+ pruner?: PrunerLike | undefined;
18
+ fallbackText?: string;
19
+ };
20
+ /** XML 注释文本:注释内不允许连续连字符,`--` 统一替换为破折号。 */
21
+ export declare function xmlCommentText(text: string): string;
22
+ /**
23
+ * 渲染一条完整消息为 XML 条目(序列化后的字符串)与其携带的图片附件元数据。
24
+ * 主渲染失败且提供 fallbackText 时降级为最简元素;两者皆不可用时向上抛出,
25
+ * 由调用方决定跳过或回退。
26
+ */
27
+ export declare function renderCompleteMessageXml(session: Session, cm: CompleteMessage, options?: RenderCompleteMessageXmlOptions): {
28
+ xml: string;
29
+ images: ImageRefValue[];
30
+ };
31
+ //# sourceMappingURL=recall-xml.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recall-xml.d.ts","sourceRoot":"","sources":["../src/recall-xml.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAEL,KAAK,UAAU,EAIhB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,KAAK,aAAa,EAAa,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG3D,sDAAsD;AACtD,MAAM,MAAM,+BAA+B,GAAG;IAC5C,MAAM,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAOF,0CAA0C;AAC1C,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AA0FD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,OAAO,GAAE,+BAAoC,GAC5C;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,aAAa,EAAE,CAAA;CAAE,CAY1C"}
package/dist/recall.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * recall 工具:按完整消息 index 区间回看原始会话(含被压缩内容)。
3
3
  * 导出 recallArgsSchema / RecallArgs / parseRecallArgs / buildRecallTool。
4
- * 输出值为 { text, images }(契约见 recall-output.ts);recall 自身不设输出上限,
5
- * 超大工具结果由 tool-result-pruner 裁剪。
4
+ * 输出值为 { text, images }(契约见 recall-output.ts),text 为 XML 条目序列
5
+ * (形态见 recall-xml.ts);recall 自身不设输出上限,超大工具结果由 tool-result-pruner 裁剪。
6
6
  */
7
7
  import { z } from 'zod';
8
8
  import type { ToolDefinition } from './types.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../src/recall.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,YAAY,CAAC;AAGjE;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;iBAYzB,CAAC;AAEL,4CAA4C;AAC5C,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAUxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,OAAO,GAAG,cAAc,CAoEzE"}
1
+ {"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../src/recall.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAYxB,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,YAAY,CAAC;AAGjE;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;iBAYzB,CAAC;AAEL,4CAA4C;AAC5C,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D,8DAA8D;AAC9D,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAUxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,OAAO,GAAG,cAAc,CA2DzE"}
@@ -5,7 +5,9 @@
5
5
  * resolveSemanticRange / tokenize / matchExplanation / buildSemanticRecallTool。
6
6
  * 向量为本地 ONNX 嵌入(cosine 相似度);只匹配文本,纯图片消息不进候选池;
7
7
  * 区间缺省检索全部、区间不合法回退全量并在输出中告知;输出契约同 recall
8
- * ({ text, images },超大结果由 tool-result-pruner 裁剪);仅主会话可用。
8
+ * ({ text, images },条目为 XML 形态见 recall-xml.ts,超大结果由 tool-result-pruner
9
+ * 裁剪);候选池排除本次调用自身的 toolcall 完整消息(其渲染文本含 query 原文,
10
+ * 必居榜首);仅主会话可用。
9
11
  */
10
12
  import { z } from 'zod';
11
13
  import { type EmbedFn, type ModelStatus } from './embedding.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"semantic-recall.d.ts","sourceRoot":"","sources":["../src/semantic-recall.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAoB,KAAK,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAgB/F,OAAO,KAAK,EAAmB,cAAc,EAAkB,MAAM,YAAY,CAAC;AAGlF,qEAAqE;AACrE,eAAO,MAAM,wBAAwB;;;;;;iBAoBjC,CAAC;AAEL,6DAA6D;AAC7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,+CAA+C;AAC/C,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAUxE;AAED,6DAA6D;AAC7D,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAExE;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC,GACzD,WAAW,CAqBb;AAED,uCAAuC;AACvC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAMlD;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnF;AAED,qCAAqC;AACrC,eAAO,MAAM,gCAAgC,6HAAyB,CAAC;AAEvE,uEAAuE;AACvE,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE;IAChD,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACxD,GAAG,cAAc,CA+EjB"}
1
+ {"version":3,"file":"semantic-recall.d.ts","sourceRoot":"","sources":["../src/semantic-recall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAoB,KAAK,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAW/F,OAAO,KAAK,EAAmB,cAAc,EAAkB,MAAM,YAAY,CAAC;AAGlF,qEAAqE;AACrE,eAAO,MAAM,wBAAwB;;;;;;iBAoBjC,CAAC;AAEL,6DAA6D;AAC7D,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,+CAA+C;AAC/C,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,GAAG,kBAAkB,CAUxE;AAED,6DAA6D;AAC7D,MAAM,MAAM,WAAW,GAAG;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC;AAExE;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC,GACzD,WAAW,CAqBb;AAED,uCAAuC;AACvC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAMlD;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnF;AAED,qCAAqC;AACrC,eAAO,MAAM,gCAAgC,6HAAyB,CAAC;AAEvE,uEAAuE;AACvE,wBAAgB,uBAAuB,CAAC,OAAO,CAAC,EAAE;IAChD,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CACxD,GAAG,cAAc,CAsFjB"}
package/dist/utils.d.ts CHANGED
@@ -1,9 +1,10 @@
1
1
  /**
2
2
  * 零依赖工具函数:文本呈现、主会话判定、路由解析等通用辅助(不 import 运行时包)。
3
- * 导出 isRecord / uuid / blocksToText / textCharCount / renderMessageText / safeJson /
4
- * isMainSession / routedTarget。
3
+ * 导出 isRecord / uuid / blocksToText / textCharCount / renderMessageText / toolArgsJson /
4
+ * safeJson / appendCdataText / isMainSession / routedTarget。
5
5
  */
6
6
  import type { Message } from '@deepseek-ai/dsh-llm';
7
+ import type { Document, Element } from '@xmldom/xmldom';
7
8
  import type { Session } from './types.ts';
8
9
  /** 判断值是否为普通对象(非 null、非数组)。 */
9
10
  export declare function isRecord(value: unknown): value is Record<string, unknown>;
@@ -17,6 +18,17 @@ export declare function textCharCount(message: Message | null | undefined): numb
17
18
  export declare function renderMessageText(message: Message | null | undefined): string;
18
19
  /** 安全 JSON 序列化:序列化失败时退回 String 呈现。 */
19
20
  export declare function safeJson(value: unknown): string;
21
+ /**
22
+ * 工具调用参数的 JSON 文本(CDATA 内保持只有一层 JSON 转义):
23
+ * 字符串参数是模型产出的原始 JSON 串,原样返回(本身即合法 JSON,不再二次序列化);
24
+ * 其余结构 JSON.stringify 序列化一次。
25
+ */
26
+ export declare function toolArgsJson(args: unknown): string;
27
+ /**
28
+ * 向元素追加 CDATA 正文:正文统一以 CDATA 包裹(逐字原样,不做实体转义)。
29
+ * 正文含 ]]> 时拆为相邻多个 CDATA 段,拼接后逐字还原原文,对读取方透明。
30
+ */
31
+ export declare function appendCdataText(doc: Document, el: Element, text: string): void;
20
32
  /** 主会话判定:subagent 会话 header.origin === 'subagent'。 */
21
33
  export declare function isMainSession(session: Session): boolean;
22
34
  /** 会话路由目标:provider + model。 */
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,8BAA8B;AAC9B,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,iDAAiD;AACjD,wBAAgB,IAAI,IAAI,MAAM,CAI7B;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAQpD;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAWzE;AAED,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAc7E;AAED,sCAAsC;AACtC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM/C;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEvD;AAED,+BAA+B;AAC/B,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,iDAAiD;AACjD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAQvE"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE1C,8BAA8B;AAC9B,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE;AAED,iDAAiD;AACjD,wBAAgB,IAAI,IAAI,MAAM,CAI7B;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,CAQpD;AAED,8CAA8C;AAC9C,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAWzE;AAED,qEAAqE;AACrE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAc7E;AAED,sCAAsC;AACtC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAM/C;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,CAGlD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAW9E;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAEvD;AAED,+BAA+B;AAC/B,MAAM,MAAM,YAAY,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,iDAAiD;AACjD,wBAAgB,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,GAAG,SAAS,CAQvE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-om",
3
- "version": "0.0.32",
3
+ "version": "0.0.34",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",