dsh-plugin-om 0.0.40 → 0.0.41

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
@@ -7,7 +7,7 @@
7
7
  ## 功能
8
8
 
9
9
  - **自动压缩**:仅主会话,在 agent/pre-step 阻塞串行执行。净压力达到 `observeThresholdTokens` 后再累计 `tailMessageCount` 条完整消息时,把触发点前的全部消息摘要为新 `<history>` 块并精确替换对应消息区间;全部 `<history>` 块 token 合计达到 `reflectThresholdTokens` 时把全部块内条目送入重新压缩,合并为一条更紧凑的摘要。待定标记以 log-only 会话事件持久化,重启后从日志恢复
10
- - **工具驱动压缩**:摘要生成走多轮工具会话,模型经 getHistory 查看区间条目、compressHistory 分批替换 assistant 条目、completeCompression 结束。首条消息仅含压缩指令与 index 区间,不含历史内容。用户消息与系统消息不可压缩且原样保留;未压缩条目原样保留,条目正文一律以 CDATA 包裹;toolcall 条目以 `<assistant type="toolcall" tool-name="…" callId="…" index="…">` 元素呈现,内含 `<tool-args>`(调用参数 JSON,仅一层转义)与 `<tool-result>` 两段 CDATA 子元素,与 recall 工具输出同构;skill 加载条目以 `<skill_content name="…" index="…">` 元素呈现,内含 `<skill_resources>` 与 `<skill_instructions>` 两段;ask_user_question 提问条目以 `<ask-user-question index="…">` 元素呈现,CDATA 内为 q:(问题文本,一个问题一行)与 a:(用户回答原文)行,旧版 `<askuserquestion>` 条目原样保留;两类条目首次压缩时都要求模型再次确认相关性,压缩后变为常规 assistant 摘要条目。最终 `<history>` 块由插件构建,天然合法无需校验,块首格式说明注释中的 `<user_message>`、`<sys>`、`<skill_content>`、`<ask-user-question>` 条目说明仅在块内存在对应条目时包含
10
+ - **工具驱动压缩**:摘要生成走多轮工具会话,模型经 getHistory 查看区间条目、compressHistory 分批替换 assistant 条目、completeCompression 结束。首条消息仅含压缩指令与 index 区间,不含历史内容。用户消息与系统消息不可压缩且原样保留;未压缩条目原样保留,条目正文一律以 CDATA 包裹;toolcall 条目以 `<assistant type="toolcall" tool-name="…" callId="…" index="…">` 元素呈现,内含 `<tool-args>`(调用参数 JSON,仅一层转义)与 `<tool-result>` 两段 CDATA 子元素,与 recall 工具输出同构;skill 加载条目以 `<skill_content name="…" index="…">` 元素呈现,内含 `<skill_resources>` 与 `<skill_instructions>` 两段;ask_user_question 提问条目以 `<ask-user-question index="…">` 元素呈现,CDATA 内为逐题成对的 q:(问题文本)与 a:(用户回答)行,未作答的题记为 a:(未回答),旧版 `<askuserquestion>` 条目原样保留;两类条目首次压缩时都要求模型再次确认相关性,压缩后变为常规 assistant 摘要条目。最终 `<history>` 块由插件构建,天然合法无需校验,块首格式说明注释中的 `<user_message>`、`<sys>`、`<skill_content>`、`<ask-user-question>` 条目说明仅在块内存在对应条目时包含
11
11
  - **压缩会话记录**:每次压缩的工具循环完整对话与压缩统计(起止时间、总耗时、逐轮请求耗时与 token usage 及合计)落盘为 one-shot 子会话,成功为会话记录、失败为失败日志,便于查看模型实际的查看与压缩行为及其 token 成本。主会话日志只保留汇总:`compaction/summary` 携带 usage 与起止时间、总耗时,失败路径 `compaction/end` 携带总耗时
12
12
  - **降级容错**:systemPrompt 或 tokenMeter 服务异常时按 0 计继续压缩,问题通过 console 输出与 log-only `om/warning` 会话事件上报,同会话同一问题至多一条
13
13
  - **recall 工具**:按完整消息 index 区间回看原始会话,含被压缩内容;输出为 XML 条目序列(`<user_message>`、`<sys>`、`<assistant type="text"|"toolcall">`),正文一律以 CDATA 包裹逐字原样,toolcall 条目内嵌 `<tool-args>`(合法 JSON,仅一层转义)与 `<tool-result>` CDATA 子元素,图片附件随结果保留
@@ -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 * historyFormatNote / SKILL_TOOL_NAME / ASK_USER_QUESTION_TOOL_NAME /\n * COMPACT_CHECKPOINT_PLUGIN / 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/** 块顶格式说明注释中按块内条目动态追加的分段开关。 */\nexport type HistoryNoteSections = {\n /** 块内存在 user 条目时追加 <user_message> 说明。 */\n user?: boolean;\n /** 块内存在 sys 条目时追加 <sys> 说明。 */\n sys?: boolean;\n /** 块内存在未压缩 skill 条目时追加 <skill_content> 说明。 */\n skill?: boolean;\n /** 块内存在 ask_user_question 条目时追加 <ask-user-question> 说明。 */\n askUserQuestion?: boolean;\n};\n\n/**\n * 最终 <history> 块内文块首的格式说明注释(XML 注释)。通用部分(完整消息定义 +\n * 条目标签语义 + CDATA 约定 + toolcall 结构)始终保留;<user_message> / <sys> /\n * <skill_content> / <ask-user-question> 的条目说明仅在块内存在对应条目时追加。\n */\nexport function historyFormatNote(sections: HistoryNoteSections = {}): string {\n const parts = [\n COMPLETE_MESSAGE_DEFINITION,\n '<TAG index=\"N\">表示单条完整消息,<TAG start=\"A\" end=\"B\"> 表示多条连续消息,start/end 是首尾完整消息的 index;消息块的内容是用CDATA包裹的纯文本',\n '<assistant type=\"toolcall\" tool-name=\"T\" callId=\"C\" index=\"N\"> 表示工具调用,内含 <tool-args> 与 <tool-result> 两个CDATA子元素(调用参数与工具返回内容)',\n ];\n if (sections.user) parts.push('<user_message index=\"N\"> 表示用户消息原文');\n if (sections.sys) parts.push('<sys type=\"KIND\" index=\"N\"> 表示被压缩的系统消息,块中为空');\n if (sections.skill) parts.push('<skill_content name=\"S\" index=\"N\"> 表示未压缩的原始 skill');\n if (sections.askUserQuestion)\n parts.push(\n '<ask-user-question index=\"N\"> 表示向用户提问,CDATA 内为 q:(提问)与 a:(用户回答)行',\n );\n return `<!-- ${parts.join(';')} -->`;\n}\n\n/** skill 工具名:toolcall 条目的工具名为该值时视为 skill 加载,<history> 块中以 <skill_content> 元素呈现。 */\nexport const SKILL_TOOL_NAME = 'skill';\n\n/**\n * ask_user_question 工具名:toolcall 条目的工具名为该值时视为向用户的提问,\n * <history> 块中以 <ask-user-question> 元素呈现,压缩时与 skill 一样要求二次确认。\n */\nexport const ASK_USER_QUESTION_TOOL_NAME = 'ask_user_question';\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 // 载荷已由 isValidPayload 运行时校验;seq 为宿主品牌类型,经 unknown 中转收窄为 OmEvent\n return { kind: omKind, data: rest, seq: event.seq } as unknown 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.snapshotEvents()) {\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 { ChatNode, ChatNodeDataMap } from '@deepseek-ai/dsh-client-ui-chat/client';\nimport type {\n ConversationMatch,\n ConversationNodeContext,\n ConversationNodeDefinition,\n} 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-chat/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: ChatNode<Kind>['data'],\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-chat/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-chat/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: 拉取 uiConversation 服务与 ChatNodeDataMap 的类型合并。\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client';\n// Type-only: 拉取 slots 服务的 Context 合并(ctx.slots)。\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/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: 聊天节点槽、uiConversation 会话定义注册表与 locale 服务。 */\nexport const inject = ['slots', 'uiConversation', '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.uiConversation.events.register(omCompactionDefinition),\n 'dsh-plugin-om: compaction definition',\n );\n ctx.effect(\n () => ctx.uiConversation.events.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 * historyFormatNote / SKILL_TOOL_NAME / ASK_USER_QUESTION_TOOL_NAME /\n * COMPACT_CHECKPOINT_PLUGIN / 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/** 块顶格式说明注释中按块内条目动态追加的分段开关。 */\nexport type HistoryNoteSections = {\n /** 块内存在 user 条目时追加 <user_message> 说明。 */\n user?: boolean;\n /** 块内存在 sys 条目时追加 <sys> 说明。 */\n sys?: boolean;\n /** 块内存在未压缩 skill 条目时追加 <skill_content> 说明。 */\n skill?: boolean;\n /** 块内存在 ask_user_question 条目时追加 <ask-user-question> 说明。 */\n askUserQuestion?: boolean;\n};\n\n/**\n * 最终 <history> 块内文块首的格式说明注释(XML 注释)。通用部分(完整消息定义 +\n * 条目标签语义 + CDATA 约定 + toolcall 结构)始终保留;<user_message> / <sys> /\n * <skill_content> / <ask-user-question> 的条目说明仅在块内存在对应条目时追加。\n */\nexport function historyFormatNote(sections: HistoryNoteSections = {}): string {\n const parts = [\n COMPLETE_MESSAGE_DEFINITION,\n '<TAG index=\"N\">表示单条完整消息,<TAG start=\"A\" end=\"B\"> 表示多条连续消息,start/end 是首尾完整消息的 index;消息块的内容是用CDATA包裹的纯文本',\n '<assistant type=\"toolcall\" tool-name=\"T\" callId=\"C\" index=\"N\"> 表示工具调用,内含 <tool-args> 与 <tool-result> 两个CDATA子元素(调用参数与工具返回内容)',\n ];\n if (sections.user) parts.push('<user_message index=\"N\"> 表示用户消息原文');\n if (sections.sys) parts.push('<sys type=\"KIND\" index=\"N\"> 表示被压缩的系统消息,块中为空');\n if (sections.skill) parts.push('<skill_content name=\"S\" index=\"N\"> 表示未压缩的原始 skill');\n if (sections.askUserQuestion)\n parts.push(\n '<ask-user-question index=\"N\"> 表示向用户提问,CDATA 内为逐题成对的 q:(提问)与 a:(用户回答)行,未作答的题记为 a:(未回答)',\n );\n return `<!-- ${parts.join(';')} -->`;\n}\n\n/** skill 工具名:toolcall 条目的工具名为该值时视为 skill 加载,<history> 块中以 <skill_content> 元素呈现。 */\nexport const SKILL_TOOL_NAME = 'skill';\n\n/**\n * ask_user_question 工具名:toolcall 条目的工具名为该值时视为向用户的提问,\n * <history> 块中以 <ask-user-question> 元素呈现,压缩时与 skill 一样要求二次确认。\n */\nexport const ASK_USER_QUESTION_TOOL_NAME = 'ask_user_question';\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 // 载荷已由 isValidPayload 运行时校验;seq 为宿主品牌类型,经 unknown 中转收窄为 OmEvent\n return { kind: omKind, data: rest, seq: event.seq } as unknown 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.snapshotEvents()) {\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 { ChatNode, ChatNodeDataMap } from '@deepseek-ai/dsh-client-ui-chat/client';\nimport type {\n ConversationMatch,\n ConversationNodeContext,\n ConversationNodeDefinition,\n} 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-chat/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: ChatNode<Kind>['data'],\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-chat/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-chat/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: 拉取 uiConversation 服务与 ChatNodeDataMap 的类型合并。\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client';\n// Type-only: 拉取 slots 服务的 Context 合并(ctx.slots)。\nimport type {} from '@deepseek-ai/dsh-client-ui-renderer/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: 聊天节点槽、uiConversation 会话定义注册表与 locale 服务。 */\nexport const inject = ['slots', 'uiConversation', '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.uiConversation.events.register(omCompactionDefinition),\n 'dsh-plugin-om: compaction definition',\n );\n ctx.effect(\n () => ctx.uiConversation.events.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 +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;AAUxD,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,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,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;AA+CD,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,CAyFjB;AAiBD,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASjD;AAoFD;;GAEG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAgJ7C;;;;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,CAwHvE;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;AAUxD,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,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,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;AAyHD,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,CAyFjB;AAiBD,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASjD;AAoFD;;GAEG;AACH,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAgJ7C;;;;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,CAwHvE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,EAAE,GAAG,MAAM,CAO7D"}
package/dist/index.mjs CHANGED
@@ -44,7 +44,7 @@ function historyFormatNote(sections = {}) {
44
44
  if (sections.user) parts.push("<user_message index=\"N\"> 表示用户消息原文");
45
45
  if (sections.sys) parts.push("<sys type=\"KIND\" index=\"N\"> 表示被压缩的系统消息,块中为空");
46
46
  if (sections.skill) parts.push("<skill_content name=\"S\" index=\"N\"> 表示未压缩的原始 skill");
47
- if (sections.askUserQuestion) parts.push("<ask-user-question index=\"N\"> 表示向用户提问,CDATA 内为 q:(提问)与 a:(用户回答)行");
47
+ if (sections.askUserQuestion) parts.push("<ask-user-question index=\"N\"> 表示向用户提问,CDATA 内为逐题成对的 q:(提问)与 a:(用户回答)行,未作答的题记为 a:(未回答)");
48
48
  return `<!-- ${parts.join(";")} -->`;
49
49
  }
50
50
  /** skill 工具名:toolcall 条目的工具名为该值时视为 skill 加载,<history> 块中以 <skill_content> 元素呈现。 */
@@ -7680,12 +7680,14 @@ function skillNameOf(session, cm) {
7680
7680
  }
7681
7681
  return "";
7682
7682
  }
7683
+ /** 未作答的题在条目正文中的回答段。 */
7684
+ const ASK_UNANSWERED_TEXT = "(未回答)";
7683
7685
  /**
7684
- * 从 ask_user_question 调用参数 JSON 提取问题文本行:questions 数组逐项取
7685
- * question 字段(字符串元素原样),一个问题一行。参数缺失、非法或解析不出
7686
+ * 从 ask_user_question 调用参数 JSON 提取提问项:questions 数组逐项取 id 与
7687
+ * question 字段(字符串元素原样、无 id),保持参数序。参数缺失、非法或解析不出
7686
7688
  * 问题文本时返回空数组。
7687
7689
  */
7688
- function askQuestionLines(args) {
7690
+ function askQuestionItems(args) {
7689
7691
  if (args === void 0 || args.trim() === "") return [];
7690
7692
  let parsed;
7691
7693
  try {
@@ -7694,25 +7696,80 @@ function askQuestionLines(args) {
7694
7696
  return [];
7695
7697
  }
7696
7698
  if (!isRecord(parsed) || !Array.isArray(parsed.questions)) return [];
7699
+ const items = [];
7700
+ for (const q of parsed.questions) if (typeof q === "string" && q.trim() !== "") items.push({ text: q });
7701
+ else if (isRecord(q) && typeof q.question === "string" && q.question.trim() !== "") {
7702
+ const id = typeof q.id === "string" && q.id !== "" ? q.id : void 0;
7703
+ items.push(id === void 0 ? { text: q.question } : {
7704
+ id,
7705
+ text: q.question
7706
+ });
7707
+ }
7708
+ return items;
7709
+ }
7710
+ /**
7711
+ * 从 ask_user_question 工具返回文本提取回答项:返回为 JSON 且含 answers 数组时,
7712
+ * 逐项取 id 与所选内容(selected 逐项拼接、custom 追加在后)。
7713
+ * 返回不是可解析的 answers JSON 时返回 null(调用方回退为返回原文呈现)。
7714
+ */
7715
+ function askAnswerItems(result) {
7716
+ let parsed;
7717
+ try {
7718
+ parsed = JSON.parse(result);
7719
+ } catch {
7720
+ return null;
7721
+ }
7722
+ if (!isRecord(parsed) || !Array.isArray(parsed.answers)) return null;
7723
+ const items = [];
7724
+ for (const a of parsed.answers) {
7725
+ if (!isRecord(a)) continue;
7726
+ const selected = Array.isArray(a.selected) ? a.selected.filter((s) => typeof s === "string") : [];
7727
+ const custom = typeof a.custom === "string" && a.custom !== "" ? [a.custom] : [];
7728
+ const id = typeof a.id === "string" && a.id !== "" ? a.id : void 0;
7729
+ const text = [...selected, ...custom].join(", ");
7730
+ items.push(id === void 0 ? { text } : {
7731
+ id,
7732
+ text
7733
+ });
7734
+ }
7735
+ return items;
7736
+ }
7737
+ /**
7738
+ * ask_user_question 条目正文行:回答可解析时按题输出 q:(问题文本)与 a:(用户回答)
7739
+ * 成对的行——按 id 配对,未作答的题记为 a:(未回答),匹配不到问题的回答单独成行;
7740
+ * 回答不可解析时逐行输出问题、末尾单行输出返回原文。
7741
+ */
7742
+ function askUserQuestionLines(questions, answers, result) {
7743
+ if (answers === null) {
7744
+ const lines = questions.map((q) => `q:${q.text}`);
7745
+ if (result.trim() !== "") lines.push(`a:${result}`);
7746
+ return lines;
7747
+ }
7697
7748
  const lines = [];
7698
- for (const q of parsed.questions) if (typeof q === "string" && q.trim() !== "") lines.push(q);
7699
- else if (isRecord(q) && typeof q.question === "string" && q.question.trim() !== "") lines.push(q.question);
7749
+ const paired = /* @__PURE__ */ new Set();
7750
+ for (const q of questions) {
7751
+ const index = q.id === void 0 ? -1 : answers.findIndex((a, i) => !paired.has(i) && a.id === q.id);
7752
+ if (index >= 0) paired.add(index);
7753
+ const answer = index >= 0 ? answers[index]?.text ?? "" : "";
7754
+ lines.push(`q:${q.text} a:${answer === "" ? ASK_UNANSWERED_TEXT : answer}`);
7755
+ }
7756
+ answers.forEach((a, i) => {
7757
+ if (!paired.has(i) && a.text !== "") lines.push(`a:${a.text}`);
7758
+ });
7700
7759
  return lines;
7701
7760
  }
7702
7761
  /**
7703
- * ask_user_question 条目正文:构造为原生 <ask-user-question> 包裹形态,
7704
- * 内文为 q:(问题文本,一个问题一行)与 a:(用户回答原文)行,缺失段省略。
7705
- * 问题与回答均缺失时返回 null(调用方回退为通用完整消息呈现)。
7762
+ * ask_user_question 条目正文:构造为原生 <ask-user-question> 包裹形态,内文见
7763
+ * askUserQuestionLines。问题与回答均取不到内容时返回 null(调用方回退为通用完整消息呈现)。
7706
7764
  */
7707
7765
  function askUserQuestionText(session, cm) {
7708
7766
  const call = toolCallBlockOf(session, cm);
7709
7767
  const args = call === void 0 ? void 0 : toolArgsJson(call.arguments);
7710
7768
  const resultMessage = toolResultMessageOf(session, cm);
7711
7769
  const result = resultMessage && Array.isArray(resultMessage.content) ? renderMessageText(resultMessage) : "";
7712
- const qLines = askQuestionLines(args);
7713
- const answers = result.trim() === "" ? "" : result;
7714
- if (qLines.length === 0 && answers === "") return null;
7715
- return `<ask-user-question>${[...qLines.map((q) => `q:${q}`), ...answers === "" ? [] : [`a:${answers}`]].join("\n")}</ask-user-question>`;
7770
+ const lines = askUserQuestionLines(askQuestionItems(args), result.trim() === "" ? [] : askAnswerItems(result), result);
7771
+ if (lines.length === 0) return null;
7772
+ return `<ask-user-question>${lines.join("\n")}</ask-user-question>`;
7716
7773
  }
7717
7774
  /**
7718
7775
  * 观察视图:被压缩区间(表层 seq 集合)内的完整消息投影为条目——
@@ -7865,7 +7922,7 @@ function skillContentSections(text) {
7865
7922
  }
7866
7923
  /**
7867
7924
  * 提取新格式 ask_user_question 条目正文(原生 <ask-user-question> 包裹)的内文
7868
- * (CDATA 内 q:/a: 行)。非该包裹形态时返回 undefined。
7925
+ * (CDATA 内 q:/a: 成对行)。非该包裹形态时返回 undefined。
7869
7926
  */
7870
7927
  function askUserQuestionInner(text) {
7871
7928
  return /^[\s]*<ask-user-question\b[^>]*>([\s\S]*)<\/ask-user-question>[\s]*$/.exec(text)?.[1];
@@ -8603,7 +8660,7 @@ function buildCompressionPrompt(skipReasoning) {
8603
8660
  "- <history> 条目正文以 CDATA 包裹(空的 <sys> 条目为自闭合空元素,无正文),CDATA 内为逐字原样内容。",
8604
8661
  "- 工具调用条目为 <assistant index=\"N\" type=\"toolcall\" tool-name=\"…\" callId=\"…\">,内含 <tool-args>(调用参数 JSON)与 <tool-result>(工具返回内容)两个 CDATA 子元素。",
8605
8662
  "- 加载的 skill 以 <skill_content name=\"…\" index=\"N\"> 条目呈现,内含 <skill_resources> 与 <skill_instructions> 两段,内文为其工具返回内容,属于关键信息。仅在你明确判断该 skill 与后续任务无关时,压缩它;如果该 skill 与后续任务相关,或者你无法判断,不要压缩,**保持原文**。",
8606
- "- ask_user_question 工具调用(向用户的提问)以 <ask-user-question index=\"N\"> 条目呈现,CDATA 内为 q:(问题文本,一个问题一行)与 a:(用户回答原文)行,属于关键信息。仅在你明确判断该提问与后续任务无关时,压缩它;如果该提问与后续任务相关,或者你无法判断,不要压缩,**保持原文**。",
8663
+ "- ask_user_question 工具调用(向用户的提问)以 <ask-user-question index=\"N\"> 条目呈现,CDATA 内为逐题成对的 q:(问题文本)与 a:(用户回答)行,未作答的题记为 a:(未回答),属于关键信息。仅在你明确判断该提问与后续任务无关时,压缩它;如果该提问与后续任务相关,或者你无法判断,不要压缩,**保持原文**。",
8607
8664
  "- 未压缩的条目将原样保留;宁可保留也不要强行压缩不确定的内容。",
8608
8665
  "- 全部完成后调用 completeCompression 结束;不要输出与工具调用无关的文本。"
8609
8666
  ].join("\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-om",
3
- "version": "0.0.40",
3
+ "version": "0.0.41",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",