dsh-plugin-om 0.0.33 → 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
 
@@ -71,6 +71,8 @@ preset-agent 自带 compaction-basic 压缩,阈值为上下文窗口的 80%,
71
71
  | `recallEnabled` | `true` | 是否注册 `recall` 工具 |
72
72
  | `semanticRecallEnabled` | `true` | 是否注册 `recall-semantic` 工具,关闭时不触发模型下载 |
73
73
 
74
+ > 检测到未压缩消息到达观察阈值后,插件会等待至少 tailMessageCount 后再压缩之前的消息,因此这个配置可以影响首次压缩的时机,也可以保留更多的会话信息。
75
+
74
76
  合理设置 `observeThresholdTokens` 和 `compressSkipReasoning`:
75
77
  - 若模型 reasoning 输出质量差且会把关键结论以 text 形式输出(例如glm和deepseek),无需携带reasoning
76
78
  - 若使用 openai/responses 或 anthropic/messages API,reasoning 内容包含大量决策信息,建议携带reasoning
@@ -108,6 +110,7 @@ src/
108
110
  ├── rate-limit.ts # 全局 429 限流冷却门,进程级共享状态
109
111
  ├── log-index.ts # 完整消息索引与渲染,recall 与压缩共用同一套编号
110
112
  ├── recall.ts # recall 工具:按完整消息 index 区间回看
113
+ ├── recall-xml.ts # 完整消息到 CDATA 包裹 XML 条目的渲染(recall 系工具共用)
111
114
  ├── recall-output.ts # recall 输出契约:{ text, images } 与 render 投影
112
115
  ├── semantic-recall.ts # recall-semantic 工具:本地嵌入语义检索
113
116
  ├── embedding.ts # 本地 ONNX 嵌入:懒加载、批量、运行时按需下载编排
@@ -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;块解析失败降级为不可定位遗留条目;
@@ -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;;;;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"}
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"}
package/dist/index.mjs CHANGED
@@ -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
  }
@@ -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
  }
@@ -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.33",
3
+ "version": "0.0.34",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",