dsh-plugin-om 0.0.14 → 0.0.15
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 +5 -4
- package/dist/compress.d.ts +1 -0
- package/dist/compress.d.ts.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.mjs +99 -12
- package/dist/summarize.d.ts +8 -2
- package/dist/summarize.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,13 +83,14 @@ dsh的"预设"分为两层,`dsh web`等同于`dsh --profile web`,调用的
|
|
|
83
83
|
### 压缩日志块结构
|
|
84
84
|
|
|
85
85
|
- 观察压缩只精确替换被压缩的新消息区间:旧 <history> 块保留为独立消息,新观察日志作为新块追加在其后(多块并存按序排列),历史不会随压缩次数膨胀;反思合并时才把多个块合并为一条更紧凑的摘要
|
|
86
|
-
- 压缩边界:消息列表中最后一个合法的 <history> 块(source
|
|
87
|
-
- 观察输入经 `@xmldom/xmldom` 构建为合法 <history> 块(用户消息文本/assistant 文本/reasoning 特殊字符自动转义,图片/文件以注释补充;assistant 文本与 toolcall&result
|
|
86
|
+
- 压缩边界:消息列表中最后一个合法的 <history> 块(source 为插件,plugin 为插件标识 `dsh-plugin-om`;兼容旧日志的宿主 checkpoint 标记 `compact`)之后的消息视为未压缩;其前(含自身)视为已压缩,不重复压缩、不计入观察阈值
|
|
87
|
+
- 观察输入经 `@xmldom/xmldom` 构建为合法 <history> 块(用户消息文本/assistant 文本/reasoning 特殊字符自动转义,图片/文件以注释补充;assistant 文本与 toolcall&result 原样;用户消息中合法的 <system-reminder> 块——完整开闭标签对且内容可被 XML 解析——整块原样保留、不转义,非法片段照常转义)
|
|
88
88
|
- 输出经 `@xmldom/xmldom` 解析校验:结构合法(标签匹配/闭合/单块)、不含 <reasoning>、index/start/end 连续(与预期覆盖区间一致),失败按 `compressRetryCount` 重试
|
|
89
89
|
- 块开标签带 `tip` 属性(对 AI 的提醒:"当前块是历史消息的压缩产物,不要复述");块顶为构成逻辑注释(完整消息定义串 + 条目标签语义),消息正文不再附加前缀句
|
|
90
90
|
|
|
91
91
|
### 注意
|
|
92
92
|
|
|
93
|
+
- 未压缩消息的 token 统计排除用户消息中合法 <system-reminder> 块(宿主注入的工作区/会话提醒不计入观察阈值的触发量)
|
|
93
94
|
- recall 不截断,建议保留 `tool-result-pruner`
|
|
94
95
|
- recall-semantic 使用本地多语言嵌入模型(paraphrase-multilingual-MiniLM-L12-v2),
|
|
95
96
|
|
|
@@ -153,7 +154,7 @@ src/
|
|
|
153
154
|
│ └─ ctx.on('agent/pre-step') → compress.ts # maybeCompress:两级压缩阻塞串行(先反思后观察;turn 中间即可触发)
|
|
154
155
|
│ ├─ reflectPass → summarize.ts # 全部 <history> 块总长 ≥ 窗口 × historyMergeRatio:多块拼接 + 共享提示词合并整个块区段
|
|
155
156
|
│ ├─ observePass → summarize.ts # 未压缩消息 ≥ 窗口 × thresholdRatio:渲染 <history> 输入 → 观察日志 → 追加 + 替换
|
|
156
|
-
│ └─ 提交 → compress.ts # compaction/start → summary → 替换消息(
|
|
157
|
+
│ └─ 提交 → compress.ts # compaction/start → summary → 替换消息(source 插件标识 + compactionId) → end;usage 归入主会话
|
|
157
158
|
├── constants.ts # 共享常量(PLUGIN_LABEL / HISTORY_TAG / HISTORY_TIP / COMPLETE_MESSAGE_DEFINITION / COMPACT_CHECKPOINT_PLUGIN)
|
|
158
159
|
├── types.ts # type-only:宿主类型再导出 + 领域类型(MessageNode / MessageIndex)
|
|
159
160
|
├── config.ts # 配置默认值 / 宽松合并(缺省、null、空串回退默认值;未知键忽略、非法值回退默认;数值键/布尔键/omEnabled/modelDir)
|
|
@@ -164,7 +165,7 @@ src/
|
|
|
164
165
|
├── summarize.ts # 共享提示词 buildHistoryPrompt(观察/反思同一套)+ renderMessages(@xmldom/xmldom 构建 <history> 块输入,自动转义)+ 直连 ctx.llm.stream() 摘要(new 方式:指令作 system、输入为渲染消息;extractSummaryLog 提取校验:XML 结构合法 / 无 reasoning / index 连续 / 覆盖区间;重试与流式 usage 归入主会话)
|
|
165
166
|
├── recall.ts # recall 工具
|
|
166
167
|
├── semantic-recall.ts # recall-semantic 工具(query 语义检索 + 区间限定 + 回退全量 + 匹配说明)
|
|
167
|
-
└── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / source 标记判定摘要消息 / compaction/* 生命周期事件 +
|
|
168
|
+
└── compress.ts # 两级自动压缩(测量 / mid-turn 区间计算 / 配对平衡回退 / source 标记判定摘要消息 / compaction/* 生命周期事件 + 替换消息 source 插件标识)
|
|
168
169
|
models/
|
|
169
170
|
└── paraphrase-multilingual-MiniLM-L12-v2/ # 嵌入模型目录(小文件随包分发;onnx 二进制不随包分发、由运行时按需下载到跨版本共享目录,不进 git)
|
|
170
171
|
scripts/ # release-archive.mjs(CHANGELOG 归档)/ download-model.mjs(开发手动预下载 CLI)
|
package/dist/compress.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type TokenEstimator = {
|
|
|
9
9
|
/**
|
|
10
10
|
* 未压缩消息 token 估算:最后一个 <history> 块之后的表层节点合计
|
|
11
11
|
* (其前含自身视为已压缩,不计入观察阈值衡量对象;无压缩日志时计全部)。
|
|
12
|
+
* 用户消息中合法 <system-reminder> 块(宿主注入的提醒)的 token 扣除,不计入阈值。
|
|
12
13
|
*/
|
|
13
14
|
export declare function measureUncompressedTokens(session: Session, meter: TokenEstimator): number;
|
|
14
15
|
/**
|
package/dist/compress.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../src/compress.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EACV,KAAK,EAEL,OAAO,EACP,YAAY,EACZ,OAAO,EAIR,MAAM,YAAY,CAAC;AACpB,OAAO,EAA0B,KAAK,YAAY,EAAsB,MAAM,YAAY,CAAC;AAE3F,+DAA+D;AAC/D,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AA0CD,+DAA+D;AAC/D,MAAM,MAAM,cAAc,GAAG;IAAE,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAA;CAAE,CAAC;AAmB3E;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,GAAG,MAAM,CAwBzF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAc1E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,GAChB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG,SAAS,CA8BpE;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG;IAChD,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC7C,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;CACjC,CAaA;AA4FD;;;;GAIG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA2Gf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAkIf;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,QAAQ,CAAC,YAAY,CAAC,EAC9B,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CAwCf"}
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 共享常量:集中定义插件级魔法字符串与事件名,避免散落各模块。
|
|
3
3
|
*/
|
|
4
|
-
/** 插件标识:压缩消息 source.plugin
|
|
4
|
+
/** 插件标识:压缩消息 source.plugin 取值(注入压缩日志的来源标记)与日志前缀。 */
|
|
5
5
|
export declare const PLUGIN_LABEL = "dsh-plugin-om";
|
|
6
6
|
/** 压缩日志标签名:<history>...</history> 包裹观察/反思日志块(输入与输出均为合法 history 块)。 */
|
|
7
7
|
export declare const HISTORY_TAG = "history";
|
|
@@ -12,6 +12,6 @@ export declare const HISTORY_TIP = "\u5F53\u524D\u5757\u662F\u5386\u53F2\u6D88\u
|
|
|
12
12
|
* 完整消息是摘要日志与 recall 共用的定位单位;首条 index 为 0,按会话顺序递增、全局稳定。
|
|
13
13
|
*/
|
|
14
14
|
export declare const COMPLETE_MESSAGE_DEFINITION = "`\u7528\u6237\u6D88\u606F`\uFF0C`\u6A21\u578B\u8F93\u51FA\u6587\u672C`\u548C`\u5177\u6709result\u7684toolcall`\u88AB\u89C6\u4F5C`\u5B8C\u6574\u6D88\u606F`\u3002\u9996\u6761`\u5B8C\u6574\u6D88\u606F`\u7684index\u662F0\uFF0C\u540E\u7EED\u7684index\u9012\u589E\u3002";
|
|
15
|
-
/**
|
|
15
|
+
/** 旧日志压缩消息的宿主 checkpoint 标记 plugin 名(dsh-compaction-basic 的 COMPACT_CHECKPOINT_MARKER.plugin;历史兼容识别用)。 */
|
|
16
16
|
export declare const COMPACT_CHECKPOINT_PLUGIN = "compact";
|
|
17
17
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oDAAoD;AACpD,eAAO,MAAM,YAAY,kBAAkB,CAAC;AAE5C,sEAAsE;AACtE,eAAO,MAAM,WAAW,YAAY,CAAC;AAErC,kDAAkD;AAClD,eAAO,MAAM,WAAW,iHAAuB,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,2BAA2B,4QACqC,CAAC;AAE9E,0GAA0G;AAC1G,eAAO,MAAM,yBAAyB,YAAY,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -10,7 +10,7 @@ var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).expor
|
|
|
10
10
|
/**
|
|
11
11
|
* 共享常量:集中定义插件级魔法字符串与事件名,避免散落各模块。
|
|
12
12
|
*/
|
|
13
|
-
/** 插件标识:压缩消息 source.plugin
|
|
13
|
+
/** 插件标识:压缩消息 source.plugin 取值(注入压缩日志的来源标记)与日志前缀。 */
|
|
14
14
|
const PLUGIN_LABEL = "dsh-plugin-om";
|
|
15
15
|
/** 压缩日志标签名:<history>...</history> 包裹观察/反思日志块(输入与输出均为合法 history 块)。 */
|
|
16
16
|
const HISTORY_TAG = "history";
|
|
@@ -21,8 +21,6 @@ const HISTORY_TIP = "当前块是历史消息的压缩产物,不要复述";
|
|
|
21
21
|
* 完整消息是摘要日志与 recall 共用的定位单位;首条 index 为 0,按会话顺序递增、全局稳定。
|
|
22
22
|
*/
|
|
23
23
|
const COMPLETE_MESSAGE_DEFINITION = "`用户消息`,`模型输出文本`和`具有result的toolcall`被视作`完整消息`。首条`完整消息`的index是0,后续的index递增。";
|
|
24
|
-
/** 宿主压缩 checkpoint 标记的 plugin 名(dsh-compaction-basic 的 COMPACT_CHECKPOINT_MARKER.plugin)。 */
|
|
25
|
-
const COMPACT_CHECKPOINT_PLUGIN = "compact";
|
|
26
24
|
//#endregion
|
|
27
25
|
//#region src/utils.ts
|
|
28
26
|
/** 判断值是否为普通对象:typeof object 且非 null 且非数组(类型收窄用)。 */
|
|
@@ -7384,6 +7382,7 @@ function buildHistoryPrompt() {
|
|
|
7384
7382
|
"- <history> 是历史消息的记录块:输入与输出都是合法的 <history> 块,块内条目是「模型消息 + index」的表达形式。",
|
|
7385
7383
|
`- 完整消息:${COMPLETE_MESSAGE_DEFINITION}`,
|
|
7386
7384
|
"- <user_message index=\"N\">:用户消息条目(N 为该条完整消息的 index;文本原样,图片/文件等以注释补充)。",
|
|
7385
|
+
"- <system-reminder>:可能出现在 <user_message> 内的提醒块(宿主注入),视为真实元素;压缩产物中保留其块结构与内容原样(标签不转义)。",
|
|
7387
7386
|
"- <reasoning>:模型的思考过程,仅作压缩参考,产物中不要出现。",
|
|
7388
7387
|
"- <assistant index=\"N\">:单条完整消息(模型输出文本,或 toolcall 及其 result)。",
|
|
7389
7388
|
"- <assistant start=\"A\" end=\"B\">:多条连续完整消息聚合的模块(A/B 为模块首尾完整消息的 index)。",
|
|
@@ -7391,7 +7390,7 @@ function buildHistoryPrompt() {
|
|
|
7391
7390
|
"【压缩要求】",
|
|
7392
7391
|
"- 完整保留用户消息:<user_message> 条目逐条保留原文,不概括、不省略。",
|
|
7393
7392
|
"- <reasoning> 只作参考,输出产物中不包含 <reasoning> 块。",
|
|
7394
|
-
"- 将具有关联性的 <assistant> 消息按内在逻辑连贯性划分为连续模块,聚合为 <assistant start=\"\" end=\"\">
|
|
7393
|
+
"- 将具有关联性的 <assistant> 消息按内在逻辑连贯性划分为连续模块,聚合为 <assistant start=\"\" end=\"\"> 块:块内描述模块的目的、行为与结果;涉及的具体文件保留在模块内容中,多个前缀相同的路径合并简写。",
|
|
7395
7394
|
"- 单条重要的完整消息以 <assistant index=\"\"> 单独呈现,内容不受限制。",
|
|
7396
7395
|
"- 条目按 index 顺序覆盖本次压缩的全部完整消息:index/start/end 必须连续(区间内 index 连续、相邻条目相接),不跳号、不重叠、不遗漏。",
|
|
7397
7396
|
"- 条目内容为合法 XML 文本:特殊字符保持转义形式(如 & → &、< → <),用户消息原文的转义形式原样保留,不要反转义。",
|
|
@@ -7412,6 +7411,73 @@ function buildHistoryPrompt() {
|
|
|
7412
7411
|
"【数据源】下方的 <history> 消息记录是本次要压缩的全部消息;压缩结果作为一个新的 <history> 块输出。"
|
|
7413
7412
|
].join("\n");
|
|
7414
7413
|
}
|
|
7414
|
+
/** 合法 <system-reminder> 块匹配:完整开闭标签对(非贪婪最短闭合;不匹配缺闭标签的残缺文本)。 */
|
|
7415
|
+
const SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
7416
|
+
/**
|
|
7417
|
+
* 解析 system-reminder 块(合法性判定):块可被 XML 解析且根元素为 <system-reminder> 时
|
|
7418
|
+
* 视为合法块并返回其根元素;否则(缺闭标签 / 内容含非法字符等)返回 null,按普通文本处理。
|
|
7419
|
+
*/
|
|
7420
|
+
function parseSystemReminder(block) {
|
|
7421
|
+
try {
|
|
7422
|
+
/** 根元素(必须为 system-reminder)。 */
|
|
7423
|
+
const root = new import_lib.DOMParser().parseFromString(block, "text/xml").documentElement;
|
|
7424
|
+
return root && root.nodeName === "system-reminder" ? root : null;
|
|
7425
|
+
} catch {
|
|
7426
|
+
return null;
|
|
7427
|
+
}
|
|
7428
|
+
}
|
|
7429
|
+
/**
|
|
7430
|
+
* 把解析出的 system-reminder 元素按原样复制进目标文档(标签与内容均不转义):
|
|
7431
|
+
* xmldom 无 importNode,手动递归复制元素 / 文本 / CDATA / 注释节点。
|
|
7432
|
+
*/
|
|
7433
|
+
function copySystemReminder(doc, source) {
|
|
7434
|
+
/** 目标元素(同标签名)。 */
|
|
7435
|
+
const el = doc.createElement(source.nodeName);
|
|
7436
|
+
for (let i = 0; i < source.attributes.length; i += 1) {
|
|
7437
|
+
/** 源属性(名称 + 值原样复制)。 */
|
|
7438
|
+
const attr = source.attributes[i];
|
|
7439
|
+
if (attr) el.setAttribute(attr.name, attr.value);
|
|
7440
|
+
}
|
|
7441
|
+
for (const child of Array.from(source.childNodes)) if (child.nodeType === 1) el.appendChild(copySystemReminder(doc, child));
|
|
7442
|
+
else if (child.nodeType === 3) el.appendChild(doc.createTextNode(child.nodeValue ?? ""));
|
|
7443
|
+
else if (child.nodeType === 4) el.appendChild(doc.createCDATASection(child.nodeValue ?? ""));
|
|
7444
|
+
else if (child.nodeType === 8) el.appendChild(doc.createComment(child.nodeValue ?? ""));
|
|
7445
|
+
return el;
|
|
7446
|
+
}
|
|
7447
|
+
/**
|
|
7448
|
+
* 追加用户文本到 user_message 元素:合法 <system-reminder> 块整块原样插入(不转义),
|
|
7449
|
+
* 其余文本按普通文本 XML 转义(createTextNode)。
|
|
7450
|
+
*/
|
|
7451
|
+
function appendUserText(doc, el, text) {
|
|
7452
|
+
/** 已消费的文本位置。 */
|
|
7453
|
+
let last = 0;
|
|
7454
|
+
for (const m of text.matchAll(SYSTEM_REMINDER_RE)) {
|
|
7455
|
+
/** 匹配起点(缺省 0)。 */
|
|
7456
|
+
const start = m.index ?? 0;
|
|
7457
|
+
/** 块前的普通文本(转义)。 */
|
|
7458
|
+
if (start > last) el.appendChild(doc.createTextNode(text.slice(last, start)));
|
|
7459
|
+
/** 块原文。 */
|
|
7460
|
+
const block = m[0];
|
|
7461
|
+
/** 合法判定(可被 XML 解析)。 */
|
|
7462
|
+
const parsed = parseSystemReminder(block);
|
|
7463
|
+
if (parsed !== null) el.appendChild(copySystemReminder(doc, parsed));
|
|
7464
|
+
else el.appendChild(doc.createTextNode(block));
|
|
7465
|
+
/** 消费位置推进到块尾。 */
|
|
7466
|
+
last = start + block.length;
|
|
7467
|
+
}
|
|
7468
|
+
/** 末尾剩余普通文本(转义)。 */
|
|
7469
|
+
if (last < text.length) el.appendChild(doc.createTextNode(text.slice(last)));
|
|
7470
|
+
}
|
|
7471
|
+
/**
|
|
7472
|
+
* 提取文本中全部合法的 <system-reminder> 块(完整开闭标签对且内容可被 XML 解析);
|
|
7473
|
+
* 供渲染(原样插入)与未压缩消息测量(不计入)共用;不合法部分按普通文本处理。
|
|
7474
|
+
*/
|
|
7475
|
+
function legitimateSystemReminderBlocks(text) {
|
|
7476
|
+
/** 合法块缓冲区。 */
|
|
7477
|
+
const out = [];
|
|
7478
|
+
for (const m of text.matchAll(SYSTEM_REMINDER_RE)) if (parseSystemReminder(m[0]) !== null) out.push(m[0]);
|
|
7479
|
+
return out;
|
|
7480
|
+
}
|
|
7415
7481
|
/** 渲染用户消息条目(DOM 元素):文本块原样;图片/文件等非文本块以注释补充(说明传入了什么)。 */
|
|
7416
7482
|
function renderUserEntry(doc, session, cm) {
|
|
7417
7483
|
/** 用户消息事件(seq 缺失则无法渲染)。 */
|
|
@@ -7426,7 +7492,7 @@ function renderUserEntry(doc, session, cm) {
|
|
|
7426
7492
|
/** 是否存在可输出内容(文本或注释)。 */
|
|
7427
7493
|
let hasContent = false;
|
|
7428
7494
|
for (const block of message.content) if (block.type === "text") {
|
|
7429
|
-
|
|
7495
|
+
appendUserText(doc, el, String(block.text));
|
|
7430
7496
|
hasContent = true;
|
|
7431
7497
|
} else if (block.type === "image") {
|
|
7432
7498
|
/** 图片附件元数据(名称 / 媒体类型 / 尺寸 / 字节数)。 */
|
|
@@ -7446,7 +7512,7 @@ function renderUserEntry(doc, session, cm) {
|
|
|
7446
7512
|
}
|
|
7447
7513
|
/**
|
|
7448
7514
|
* 渲染完整消息记录(观察输入,new 模式):输出一个合法的 <history> 块——
|
|
7449
|
-
* - user → <user_message index="N"
|
|
7515
|
+
* - user → <user_message index="N">(文本原样,其中合法 <system-reminder> 块整块原样保留、不转义;图片/文件注释);
|
|
7450
7516
|
* - 每条 assistant 消息的 reasoning → <reasoning>(参考条目,产物中没有);
|
|
7451
7517
|
* - assistant / toolcall → <assistant index="N">(模型输出文本 / toolcall&result 原样)。
|
|
7452
7518
|
* 文本经 XML 序列化自动转义(用户输入 / 文本 / reasoning 中的特殊字符不破坏 XML 合法性)。
|
|
@@ -7815,7 +7881,7 @@ async function runSummarySubagent(ctx, agent, instruction, contextText, maxToken
|
|
|
7815
7881
|
* 压缩结果写入宿主 compaction/* 生命周期事件(compaction/start → compaction/summary →
|
|
7816
7882
|
* 替换 <history> 消息 → compaction/end),使消息记录(聊天视图压缩卡片)与轨迹视图
|
|
7817
7883
|
* 可见;compaction/summary 同时承担影子价格认领(shadowedTokenCount),不再单独发
|
|
7818
|
-
* compaction/prune。替换消息的 source
|
|
7884
|
+
* compaction/prune。替换消息的 source 使用插件标识(plugin: 'dsh-plugin-om' +
|
|
7819
7885
|
* compactionId),UI 据此关联 summary 与替换消息。生命周期事件在摘要成功后才写入
|
|
7820
7886
|
* (失败不产生任何日志变更)。
|
|
7821
7887
|
*
|
|
@@ -7845,7 +7911,8 @@ function reflectExpectedEnd(contextText) {
|
|
|
7845
7911
|
}
|
|
7846
7912
|
/**
|
|
7847
7913
|
* 判定消息是否为本插件的压缩日志消息并提取日志文本(不通过文本含标签判断,
|
|
7848
|
-
* 改用 source 标记——plugin
|
|
7914
|
+
* 改用 source 标记——plugin 为插件标识 'dsh-plugin-om'(兼容旧日志的宿主 checkpoint
|
|
7915
|
+
* 标记 'compact')。
|
|
7849
7916
|
* 仅当消息文本含 <history> 开标签(含带 tip 属性版本)时视为压缩日志,返回整段文本;
|
|
7850
7917
|
* 其余消息返回 undefined(旧格式 <om-history> 块按「不兼容,干净切换」视为普通消息)。
|
|
7851
7918
|
*/
|
|
@@ -7866,8 +7933,25 @@ function historyTextOf(event) {
|
|
|
7866
7933
|
return text;
|
|
7867
7934
|
}
|
|
7868
7935
|
/**
|
|
7936
|
+
* 消息文本中合法 <system-reminder> 块的 token 估算(4 字符 ≈ 1 token,与 estimateTextTokens
|
|
7937
|
+
* 一致):宿主注入的提醒不计入未压缩消息,避免推高观察阈值触发压缩。
|
|
7938
|
+
*/
|
|
7939
|
+
function systemReminderTokens(message) {
|
|
7940
|
+
/** 消息内容块(非记录或非数组则无文本可计)。 */
|
|
7941
|
+
const content = isRecord(message) && Array.isArray(message.content) ? message.content : null;
|
|
7942
|
+
if (!content) return 0;
|
|
7943
|
+
/** token 合计。 */
|
|
7944
|
+
let total = 0;
|
|
7945
|
+
for (const block of content) {
|
|
7946
|
+
if (!isRecord(block) || block.type !== "text" || typeof block.text !== "string") continue;
|
|
7947
|
+
for (const sr of legitimateSystemReminderBlocks(block.text)) total += estimateTextTokens(sr);
|
|
7948
|
+
}
|
|
7949
|
+
return total;
|
|
7950
|
+
}
|
|
7951
|
+
/**
|
|
7869
7952
|
* 未压缩消息 token 估算:最后一个 <history> 块之后的表层节点合计
|
|
7870
7953
|
* (其前含自身视为已压缩,不计入观察阈值衡量对象;无压缩日志时计全部)。
|
|
7954
|
+
* 用户消息中合法 <system-reminder> 块(宿主注入的提醒)的 token 扣除,不计入阈值。
|
|
7871
7955
|
*/
|
|
7872
7956
|
function measureUncompressedTokens(session, meter) {
|
|
7873
7957
|
/** 压缩边界(最后一个 history 块的 seq;无则 undefined)。 */
|
|
@@ -7884,7 +7968,10 @@ function measureUncompressedTokens(session, meter) {
|
|
|
7884
7968
|
/** 事件对应的消息(用于 token 估算)。 */
|
|
7885
7969
|
const event = session.events[seq];
|
|
7886
7970
|
const message = event ? session.deriveEventMessage(event) : null;
|
|
7887
|
-
|
|
7971
|
+
if (!message) continue;
|
|
7972
|
+
/** 消息 token 估算(用户消息扣除其中合法 system-reminder 块,宿主提醒不计入未压缩消息)。 */
|
|
7973
|
+
const tokens = event?.type === "user/message" ? Math.max(0, meter.estimateMessage(message) - systemReminderTokens(message)) : meter.estimateMessage(message);
|
|
7974
|
+
total += tokens;
|
|
7888
7975
|
}
|
|
7889
7976
|
return total;
|
|
7890
7977
|
}
|
|
@@ -8014,10 +8101,10 @@ function appendCompactionEnd(session, lifecycle, error) {
|
|
|
8014
8101
|
...error === void 0 ? {} : { error }
|
|
8015
8102
|
}).seq;
|
|
8016
8103
|
}
|
|
8017
|
-
/** 追加 <history> 压缩日志消息(surfaceOp 替换遮蔽区间,source
|
|
8104
|
+
/** 追加 <history> 压缩日志消息(surfaceOp 替换遮蔽区间,source 标记插件自产 + compactionId 关联压缩生命周期)。 */
|
|
8018
8105
|
function appendHistoryMessage(session, content, sourceEventSeqs, surfaceOp, compactionId) {
|
|
8019
8106
|
/** 压缩替换消息(内容即 <history> 标签块,不再附加前缀句;对 AI 的提醒在块开标签 tip 属性上;
|
|
8020
|
-
* source
|
|
8107
|
+
* source 标记插件标识(plugin: 'dsh-plugin-om')+ compactionId 供 UI 关联)。 */
|
|
8021
8108
|
const message = {
|
|
8022
8109
|
id: uuid(),
|
|
8023
8110
|
role: "user",
|
|
@@ -8027,7 +8114,7 @@ function appendHistoryMessage(session, content, sourceEventSeqs, surfaceOp, comp
|
|
|
8027
8114
|
}],
|
|
8028
8115
|
source: {
|
|
8029
8116
|
kind: "plugin",
|
|
8030
|
-
plugin:
|
|
8117
|
+
plugin: PLUGIN_LABEL,
|
|
8031
8118
|
compactionId
|
|
8032
8119
|
}
|
|
8033
8120
|
};
|
package/dist/summarize.d.ts
CHANGED
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* 输入与输出都是合法的 <history> 块(模型消息 + index 的表达形式),先定义块、要求压缩、
|
|
6
6
|
* 再给出数据源(下方 <history> 消息记录)。
|
|
7
7
|
*
|
|
8
|
-
* 观察输入由 renderMessages 渲染为 <history>
|
|
8
|
+
* 观察输入由 renderMessages 渲染为 <history> 块:用户消息文本原样(其中合法
|
|
9
|
+
* <system-reminder> 块整块原样保留、不转义;图片/文件以注释补充)、
|
|
9
10
|
* <reasoning> 参考条目(产物中没有)、assistant 文本与 toolcall&result 原样。
|
|
10
11
|
* 输出经 extractSummaryLog 校验:合法 <history> 块、不含 <reasoning>、index/start/end 连续
|
|
11
12
|
* (与预期覆盖区间一致),失败按 config.compressRetryCount 重试。
|
|
@@ -19,9 +20,14 @@ import { type RoutedTarget } from './utils.ts';
|
|
|
19
20
|
* index/start/end 连续)、输出格式(一个合法 <history> 块,无 reasoning)、数据源说明。
|
|
20
21
|
*/
|
|
21
22
|
export declare function buildHistoryPrompt(): string;
|
|
23
|
+
/**
|
|
24
|
+
* 提取文本中全部合法的 <system-reminder> 块(完整开闭标签对且内容可被 XML 解析);
|
|
25
|
+
* 供渲染(原样插入)与未压缩消息测量(不计入)共用;不合法部分按普通文本处理。
|
|
26
|
+
*/
|
|
27
|
+
export declare function legitimateSystemReminderBlocks(text: string): string[];
|
|
22
28
|
/**
|
|
23
29
|
* 渲染完整消息记录(观察输入,new 模式):输出一个合法的 <history> 块——
|
|
24
|
-
* - user → <user_message index="N"
|
|
30
|
+
* - user → <user_message index="N">(文本原样,其中合法 <system-reminder> 块整块原样保留、不转义;图片/文件注释);
|
|
25
31
|
* - 每条 assistant 消息的 reasoning → <reasoning>(参考条目,产物中没有);
|
|
26
32
|
* - assistant / toolcall → <assistant index="N">(模型输出文本 / toolcall&result 原样)。
|
|
27
33
|
* 文本经 XML 序列化自动转义(用户输入 / 文本 / reasoning 中的特殊字符不破坏 XML 合法性)。
|
package/dist/summarize.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"summarize.d.ts","sourceRoot":"","sources":["../src/summarize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAaH,OAAO,KAAK,EAAE,KAAK,EAAmB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AACpG,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,YAAY,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAoC3C;AA4ED;;;GAGG;AACH,wBAAgB,8BAA8B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAOrE;AAyCD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CA2DhF;AAED,yEAAyE;AACzE,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB,CAAC;AA8EF,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,gjBAAyK,CAAC;AAE1M,4CAA4C;AAC5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AA8EF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAYrE;AACD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAAE,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAuBvC;AAED,sCAAsC;AACtC,MAAM,MAAM,sBAAsB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAiC/F;AACD,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAK/C;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,sBAAsB,CAAA;CAAE,GACpE,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CA+EvC"}
|