dsh-plugin-om 0.0.15 → 0.0.17
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 +22 -5
- package/dist/client/OmCompactionCard.d.ts +22 -0
- package/dist/client/OmCompactionCard.d.ts.map +1 -0
- package/dist/client/definition.d.ts +55 -0
- package/dist/client/definition.d.ts.map +1 -0
- package/dist/client/index.d.ts +18 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/locales.d.ts +25 -0
- package/dist/client/locales.d.ts.map +1 -0
- package/dist/client.js +259 -0
- package/dist/client.js.map +1 -0
- package/dist/compress.d.ts +1 -1
- package/dist/compress.d.ts.map +1 -1
- package/dist/constants.d.ts +8 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/index.mjs +120 -115
- package/dist/log-index.d.ts +2 -15
- package/dist/log-index.d.ts.map +1 -1
- package/dist/recall.d.ts +2 -1
- package/dist/recall.d.ts.map +1 -1
- package/dist/semantic-recall.d.ts +3 -2
- package/dist/semantic-recall.d.ts.map +1 -1
- package/dist/summarize.d.ts +9 -12
- package/dist/summarize.d.ts.map +1 -1
- package/dist/types.d.ts +25 -8
- package/dist/types.d.ts.map +1 -1
- package/dist/utils.d.ts +2 -0
- package/dist/utils.d.ts.map +1 -1
- package/package.json +37 -15
package/dist/index.mjs
CHANGED
|
@@ -19,8 +19,15 @@ const HISTORY_TIP = "当前块是历史消息的压缩产物,不要复述";
|
|
|
19
19
|
/**
|
|
20
20
|
* 「完整消息」定义(提示词 / recall 工具描述 / 块顶注释共用同一字符串)。
|
|
21
21
|
* 完整消息是摘要日志与 recall 共用的定位单位;首条 index 为 0,按会话顺序递增、全局稳定。
|
|
22
|
+
* 用户消息指 user_message 中 source.kind 为 user 的部分;其余部分(宿主注入的上下文等)
|
|
23
|
+
* 为系统消息,同样是一条完整消息。
|
|
22
24
|
*/
|
|
23
|
-
const COMPLETE_MESSAGE_DEFINITION = "
|
|
25
|
+
const COMPLETE_MESSAGE_DEFINITION = "`完整消息`被定义为`用户消息`,`系统消息`,`模型输出文本`和`具有result的toolcall`的集合。首条`完整消息`的index是0,后续的index递增。";
|
|
26
|
+
/** 判定 user/message 的 source 是否为本插件自产(压缩日志机制消息;含兼容旧宿主 checkpoint 标记 'compact')。这类消息不占完整消息 index。 */
|
|
27
|
+
function isPluginOwnedSource(source) {
|
|
28
|
+
if (source?.kind !== "plugin") return false;
|
|
29
|
+
return source.plugin === "dsh-plugin-om" || source.plugin === "compact";
|
|
30
|
+
}
|
|
24
31
|
//#endregion
|
|
25
32
|
//#region src/utils.ts
|
|
26
33
|
/** 判断值是否为普通对象:typeof object 且非 null 且非数组(类型收窄用)。 */
|
|
@@ -42,6 +49,15 @@ function blocksToText(blocks) {
|
|
|
42
49
|
for (const block of blocks) if (isRecord(block) && block.type === "text" && typeof block.text === "string") out.push(block.text);
|
|
43
50
|
return out.join("");
|
|
44
51
|
}
|
|
52
|
+
/** 消息内容的字符数:递归计入 text 块与 tool-result 内嵌文本(与宿主 token 估算口径一致)。 */
|
|
53
|
+
function textCharCount(message) {
|
|
54
|
+
if (!message || !Array.isArray(message.content)) return 0;
|
|
55
|
+
/** 累计字符数。 */
|
|
56
|
+
let total = 0;
|
|
57
|
+
for (const block of message.content) if (block.type === "text") total += block.text.length;
|
|
58
|
+
else if (block.type === "tool-result") total += blocksToText(block.content).length;
|
|
59
|
+
return total;
|
|
60
|
+
}
|
|
45
61
|
/** 面向 recall 的完整消息呈现:text 原样;tool-call 展开(参数=代码);tool-result 取文本。 */
|
|
46
62
|
function renderMessageText(message) {
|
|
47
63
|
if (!message || !Array.isArray(message.content)) return "";
|
|
@@ -79,7 +95,22 @@ function routedTarget(session) {
|
|
|
79
95
|
//#endregion
|
|
80
96
|
//#region src/log-index.ts
|
|
81
97
|
/**
|
|
82
|
-
*
|
|
98
|
+
* 会话日志索引:完整消息索引(index 定位完整消息,recall 与摘要共用同一套编号),
|
|
99
|
+
* 以及表层节点定位辅助。事件日志仅追加(被遮蔽的事件仍可读,recall 依赖此性质)。
|
|
100
|
+
*
|
|
101
|
+
* 完整消息是摘要日志与 recall 共用的定位单位,分四类:
|
|
102
|
+
* - user:用户消息(user/message 中 source.kind === 'user')占一条;
|
|
103
|
+
* - sys:系统消息(user/message 中其余 source.kind 的部分,如宿主注入的上下文)
|
|
104
|
+
* 占一条;压缩日志中以 <sys type="KIND" index="N"> 空块表示(内容不进入输入);
|
|
105
|
+
* - assistant:模型输出文本(assistant/message 中的文本块)占一条;
|
|
106
|
+
* - toolcall:单个工具调用及其结果占一条(同一条 AI 消息里的文本与工具调用拆开,
|
|
107
|
+
* tool/result 按 source.callId 匹配其 tool-call;未匹配的 result 独立成条,防御)。
|
|
108
|
+
* 本插件自产的压缩日志消息(source.kind === 'plugin' 且 plugin 为本插件标识)不占 index。
|
|
109
|
+
* index 从 0 起、按日志顺序递增、只追加不重排 → 会话内全局稳定,
|
|
110
|
+
* 压缩后旧摘要条目引用的 index 仍然有效。
|
|
111
|
+
*/
|
|
112
|
+
/**
|
|
113
|
+
* 完整消息索引:按日志顺序把消息事件折叠为完整消息序列(四类,见文件头)。
|
|
83
114
|
* 工具调用结果按 source.callId 匹配其 tool-call 并入该条;未匹配的 result 独立成条(防御)。
|
|
84
115
|
* 插件在 agent/pre-step 触发压缩(日志 call-result 完备),因此压缩时不存在未闭合的
|
|
85
116
|
* tool-call;索引对未闭合调用不设特殊处理。
|
|
@@ -96,12 +127,20 @@ function indexCompleteMessages(session) {
|
|
|
96
127
|
const event = events[seq];
|
|
97
128
|
if (!event) continue;
|
|
98
129
|
if (event.type === "user/message") {
|
|
99
|
-
|
|
100
|
-
|
|
130
|
+
/** 事件 source(区分用户消息与宿主注入/插件消息)。 */
|
|
131
|
+
const source = event.data.source;
|
|
132
|
+
if (isPluginOwnedSource(source)) continue;
|
|
133
|
+
if (source?.kind === "user") cms.push({
|
|
101
134
|
index: cms.length,
|
|
102
135
|
type: "user",
|
|
103
136
|
seqs: [seq]
|
|
104
137
|
});
|
|
138
|
+
else cms.push({
|
|
139
|
+
index: cms.length,
|
|
140
|
+
type: "sys",
|
|
141
|
+
seqs: [seq],
|
|
142
|
+
...source?.kind === void 0 ? {} : { kind: source.kind }
|
|
143
|
+
});
|
|
105
144
|
} else if (event.type === "assistant/message") {
|
|
106
145
|
/** 助手消息(含文本与 tool-call 块)。 */
|
|
107
146
|
const message = event.data.message;
|
|
@@ -153,13 +192,13 @@ function indexCompleteMessages(session) {
|
|
|
153
192
|
}
|
|
154
193
|
/**
|
|
155
194
|
* 渲染一条完整消息的文本(recall 输出 / new 模式输入共用):
|
|
156
|
-
* - user:消息原文;
|
|
195
|
+
* - user / sys:消息原文;
|
|
157
196
|
* - assistant:仅文本块;
|
|
158
197
|
* - toolcall:调用块(工具名 + 参数)+ 结果文本(pruner 裁剪超大结果)。
|
|
159
198
|
*/
|
|
160
199
|
function renderCompleteMessage(session, cm, pruner) {
|
|
161
|
-
if (cm.type === "user") {
|
|
162
|
-
/**
|
|
200
|
+
if (cm.type === "user" || cm.type === "sys") {
|
|
201
|
+
/** 用户/系统消息事件(seq 缺失则无法渲染)。 */
|
|
163
202
|
const seq = cm.seqs[0];
|
|
164
203
|
const event = seq === void 0 ? void 0 : session.events[seq];
|
|
165
204
|
/** 派生的消息对象。 */
|
|
@@ -7382,13 +7421,14 @@ function buildHistoryPrompt() {
|
|
|
7382
7421
|
"- <history> 是历史消息的记录块:输入与输出都是合法的 <history> 块,块内条目是「模型消息 + index」的表达形式。",
|
|
7383
7422
|
`- 完整消息:${COMPLETE_MESSAGE_DEFINITION}`,
|
|
7384
7423
|
"- <user_message index=\"N\">:用户消息条目(N 为该条完整消息的 index;文本原样,图片/文件等以注释补充)。",
|
|
7385
|
-
"- <
|
|
7424
|
+
"- <sys type=\"(kind)\" index=\"N\">:系统消息条目(user_message 中非 kind:user 的部分,如宿主注入的上下文),块中为空,内容不进入压缩输入。",
|
|
7386
7425
|
"- <reasoning>:模型的思考过程,仅作压缩参考,产物中不要出现。",
|
|
7387
7426
|
"- <assistant index=\"N\">:单条完整消息(模型输出文本,或 toolcall 及其 result)。",
|
|
7388
7427
|
"- <assistant start=\"A\" end=\"B\">:多条连续完整消息聚合的模块(A/B 为模块首尾完整消息的 index)。",
|
|
7389
7428
|
"",
|
|
7390
7429
|
"【压缩要求】",
|
|
7391
7430
|
"- 完整保留用户消息:<user_message> 条目逐条保留原文,不概括、不省略。",
|
|
7431
|
+
"- 完整保留系统消息:<sys> 条目逐条原样保留(type 与 index 不变),块中为空,不概括、不省略、不填充内容。",
|
|
7392
7432
|
"- <reasoning> 只作参考,输出产物中不包含 <reasoning> 块。",
|
|
7393
7433
|
"- 将具有关联性的 <assistant> 消息按内在逻辑连贯性划分为连续模块,聚合为 <assistant start=\"\" end=\"\"> 块:块内描述模块的目的、行为与结果;涉及的具体文件保留在模块内容中,多个前缀相同的路径合并简写。",
|
|
7394
7434
|
"- 单条重要的完整消息以 <assistant index=\"\"> 单独呈现,内容不受限制。",
|
|
@@ -7400,6 +7440,7 @@ function buildHistoryPrompt() {
|
|
|
7400
7440
|
"<user_message index=\"(index)\">",
|
|
7401
7441
|
"(user 消息原文)",
|
|
7402
7442
|
"</user_message>",
|
|
7443
|
+
"<sys type=\"(kind)\" index=\"(index)\"></sys>",
|
|
7403
7444
|
"<assistant start=\"(起始 index)\" end=\"(结束 index)\">",
|
|
7404
7445
|
"(模块的目的、行为与结果摘要)",
|
|
7405
7446
|
"</assistant>",
|
|
@@ -7411,73 +7452,6 @@ function buildHistoryPrompt() {
|
|
|
7411
7452
|
"【数据源】下方的 <history> 消息记录是本次要压缩的全部消息;压缩结果作为一个新的 <history> 块输出。"
|
|
7412
7453
|
].join("\n");
|
|
7413
7454
|
}
|
|
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
|
-
}
|
|
7481
7455
|
/** 渲染用户消息条目(DOM 元素):文本块原样;图片/文件等非文本块以注释补充(说明传入了什么)。 */
|
|
7482
7456
|
function renderUserEntry(doc, session, cm) {
|
|
7483
7457
|
/** 用户消息事件(seq 缺失则无法渲染)。 */
|
|
@@ -7492,7 +7466,7 @@ function renderUserEntry(doc, session, cm) {
|
|
|
7492
7466
|
/** 是否存在可输出内容(文本或注释)。 */
|
|
7493
7467
|
let hasContent = false;
|
|
7494
7468
|
for (const block of message.content) if (block.type === "text") {
|
|
7495
|
-
|
|
7469
|
+
el.appendChild(doc.createTextNode(String(block.text)));
|
|
7496
7470
|
hasContent = true;
|
|
7497
7471
|
} else if (block.type === "image") {
|
|
7498
7472
|
/** 图片附件元数据(名称 / 媒体类型 / 尺寸 / 字节数)。 */
|
|
@@ -7512,11 +7486,12 @@ function renderUserEntry(doc, session, cm) {
|
|
|
7512
7486
|
}
|
|
7513
7487
|
/**
|
|
7514
7488
|
* 渲染完整消息记录(观察输入,new 模式):输出一个合法的 <history> 块——
|
|
7515
|
-
* - user → <user_message index="N"
|
|
7489
|
+
* - user → <user_message index="N">(文本原样;图片/文件注释);
|
|
7490
|
+
* - sys → <sys type="KIND" index="N"></sys>(系统消息空块,内容不进入压缩输入);
|
|
7516
7491
|
* - 每条 assistant 消息的 reasoning → <reasoning>(参考条目,产物中没有);
|
|
7517
7492
|
* - assistant / toolcall → <assistant index="N">(模型输出文本 / toolcall&result 原样)。
|
|
7518
7493
|
* 文本经 XML 序列化自动转义(用户输入 / 文本 / reasoning 中的特殊字符不破坏 XML 合法性)。
|
|
7519
|
-
* 仅渲染 seqs
|
|
7494
|
+
* 仅渲染 seqs 全部落在给定集合内的完整消息(本插件自产消息天然不占位)。
|
|
7520
7495
|
*/
|
|
7521
7496
|
function renderMessages(session, seqs) {
|
|
7522
7497
|
/** 遮蔽 seq 集合。 */
|
|
@@ -7542,6 +7517,15 @@ function renderMessages(session, seqs) {
|
|
|
7542
7517
|
const entries = [];
|
|
7543
7518
|
for (const cm of indexCompleteMessages(session)) {
|
|
7544
7519
|
if (!cm.seqs.every((seq) => shadowed.has(seq))) continue;
|
|
7520
|
+
if (cm.type === "sys") {
|
|
7521
|
+
/** sys 元素(type=source.kind,index=完整消息序号)。 */
|
|
7522
|
+
const sysEl = doc.createElement("sys");
|
|
7523
|
+
sysEl.setAttribute("type", cm.kind ?? "");
|
|
7524
|
+
sysEl.setAttribute("index", String(cm.index));
|
|
7525
|
+
sysEl.appendChild(doc.createTextNode(""));
|
|
7526
|
+
entries.push(sysEl);
|
|
7527
|
+
continue;
|
|
7528
|
+
}
|
|
7545
7529
|
if (cm.type === "user") {
|
|
7546
7530
|
/** 用户消息条目(无文本且无注释则跳过)。 */
|
|
7547
7531
|
const rendered = renderUserEntry(doc, session, cm);
|
|
@@ -7640,7 +7624,7 @@ function buildSummaryOptions(session, instruction, contextText, maxTokens, targe
|
|
|
7640
7624
|
};
|
|
7641
7625
|
}
|
|
7642
7626
|
/** 产出日志后插入首个 <history> 后的格式说明(XML 注释,完整消息定义 + 条目标签语义)。 */
|
|
7643
|
-
const HISTORY_FORMAT_NOTE = `<!-- 完整消息:${COMPLETE_MESSAGE_DEFINITION} <
|
|
7627
|
+
const HISTORY_FORMAT_NOTE = `<!-- 完整消息:${COMPLETE_MESSAGE_DEFINITION} <TAG index="N">表示单条完整消息,<TAG start="A" end="B"> 表示连续模块,start/end 是首尾完整消息的 index;<sys type="KIND" index="N"> 表示被压缩的系统消息,块中为空 -->`;
|
|
7644
7628
|
/** 读取元素整数属性(非负整数;缺失 / 非数字返回 undefined)。 */
|
|
7645
7629
|
function intAttr(el, name) {
|
|
7646
7630
|
/** 属性原始值(缺失为 null)。 */
|
|
@@ -7653,7 +7637,7 @@ function intAttr(el, name) {
|
|
|
7653
7637
|
* 用 XML 解析器解析一个 <history> 块(结构合法性校验):
|
|
7654
7638
|
* - 非法 XML(标签不匹配 / 未闭合 / 多根等)→ null;
|
|
7655
7639
|
* - 根节点必须是 <history>;
|
|
7656
|
-
* - 顶层只允许 user_message / assistant 条目元素(其余元素类型视为不合法);
|
|
7640
|
+
* - 顶层只允许 user_message / assistant / sys 条目元素(其余元素类型视为不合法);
|
|
7657
7641
|
* - 出现 <reasoning> 元素时标记 hasReasoning(产物不允许)。
|
|
7658
7642
|
* 条目按文档顺序提取(index / start / end 属性缺失或不合法 → 不合法)。
|
|
7659
7643
|
*/
|
|
@@ -7694,6 +7678,14 @@ function parseHistoryBlock(xml) {
|
|
|
7694
7678
|
kind: "user",
|
|
7695
7679
|
index
|
|
7696
7680
|
});
|
|
7681
|
+
} else if (tag === "sys") {
|
|
7682
|
+
/** index 属性(缺失/非法 → 不合法)。 */
|
|
7683
|
+
const index = intAttr(el, "index");
|
|
7684
|
+
if (index === void 0) return null;
|
|
7685
|
+
entries.push({
|
|
7686
|
+
kind: "sys",
|
|
7687
|
+
index
|
|
7688
|
+
});
|
|
7697
7689
|
} else if (tag === "assistant") {
|
|
7698
7690
|
/** 单条 index 或模块 start..end(二选一)。 */
|
|
7699
7691
|
const index = intAttr(el, "index");
|
|
@@ -7918,10 +7910,9 @@ function reflectExpectedEnd(contextText) {
|
|
|
7918
7910
|
*/
|
|
7919
7911
|
function historyTextOf(event) {
|
|
7920
7912
|
if (event?.type !== "user/message") return void 0;
|
|
7921
|
-
/** 消息 source
|
|
7913
|
+
/** 消息 source(本插件自产消息的标记;含兼容旧宿主 checkpoint 标记)。 */
|
|
7922
7914
|
const source = event.data.source;
|
|
7923
|
-
if (source
|
|
7924
|
-
if (source.plugin !== "compact" && source.plugin !== "dsh-plugin-om") return void 0;
|
|
7915
|
+
if (!isPluginOwnedSource(source)) return void 0;
|
|
7925
7916
|
/** 消息纯文本。 */
|
|
7926
7917
|
const text = blocksToText(event.data.content);
|
|
7927
7918
|
/** 首个 <history 出现位置(无则非压缩日志)。 */
|
|
@@ -7932,26 +7923,22 @@ function historyTextOf(event) {
|
|
|
7932
7923
|
if (!rest.startsWith(">") && !rest.startsWith(" tip=")) return void 0;
|
|
7933
7924
|
return text;
|
|
7934
7925
|
}
|
|
7935
|
-
/**
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
|
|
7939
|
-
|
|
7940
|
-
|
|
7941
|
-
const
|
|
7942
|
-
if (
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
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;
|
|
7926
|
+
/** 提取 <history> 块内文(去掉开/闭标签与首尾空白;非块文本原样返回)。 */
|
|
7927
|
+
function historyInnerText(text) {
|
|
7928
|
+
/** 闭标签。 */
|
|
7929
|
+
const closeTag = `</${HISTORY_TAG}>`;
|
|
7930
|
+
const close = text.lastIndexOf(closeTag);
|
|
7931
|
+
if (close === -1) return text;
|
|
7932
|
+
const open = text.indexOf(`<${HISTORY_TAG}`);
|
|
7933
|
+
if (open === -1) return text;
|
|
7934
|
+
const gt = text.indexOf(">", open);
|
|
7935
|
+
if (gt === -1 || gt >= close) return text;
|
|
7936
|
+
return text.slice(gt + 1, close).trim();
|
|
7950
7937
|
}
|
|
7951
7938
|
/**
|
|
7952
7939
|
* 未压缩消息 token 估算:最后一个 <history> 块之后的表层节点合计
|
|
7953
7940
|
* (其前含自身视为已压缩,不计入观察阈值衡量对象;无压缩日志时计全部)。
|
|
7954
|
-
*
|
|
7941
|
+
* 系统消息(user/message 中非 kind:user 的部分,如宿主注入的上下文)不计入阈值。
|
|
7955
7942
|
*/
|
|
7956
7943
|
function measureUncompressedTokens(session, meter) {
|
|
7957
7944
|
/** 压缩边界(最后一个 history 块的 seq;无则 undefined)。 */
|
|
@@ -7969,9 +7956,10 @@ function measureUncompressedTokens(session, meter) {
|
|
|
7969
7956
|
const event = session.events[seq];
|
|
7970
7957
|
const message = event ? session.deriveEventMessage(event) : null;
|
|
7971
7958
|
if (!message) continue;
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7959
|
+
if (event?.type === "user/message") {
|
|
7960
|
+
if (event.data.source?.kind !== "user") continue;
|
|
7961
|
+
}
|
|
7962
|
+
total += meter.estimateMessage(message);
|
|
7975
7963
|
}
|
|
7976
7964
|
return total;
|
|
7977
7965
|
}
|
|
@@ -8078,7 +8066,8 @@ function appendCompactionStart(session, lifecycle) {
|
|
|
8078
8066
|
* summary 为完整合并后的 <history> 内文;usage 由摘要调用提取(无则省略)。
|
|
8079
8067
|
*/
|
|
8080
8068
|
function appendCompactionSummary(session, data) {
|
|
8081
|
-
|
|
8069
|
+
/** summary 事件载荷(宿主类型 + 插件扩展 shadowedCharCount;宿主 append 不做 schema 剥离,扩展字段原样持久化)。 */
|
|
8070
|
+
const payload = {
|
|
8082
8071
|
compactionId: data.lifecycle.compactionId,
|
|
8083
8072
|
summary: [{
|
|
8084
8073
|
type: "text",
|
|
@@ -8087,11 +8076,13 @@ function appendCompactionSummary(session, data) {
|
|
|
8087
8076
|
shadowedRange: data.shadowedRange,
|
|
8088
8077
|
shadowedSeqs: data.shadowedSeqs,
|
|
8089
8078
|
shadowedTokenCount: data.shadowedTokenCount,
|
|
8079
|
+
shadowedCharCount: data.shadowedCharCount,
|
|
8090
8080
|
provider: data.provider,
|
|
8091
8081
|
model: data.model,
|
|
8092
8082
|
maxTokens: data.maxTokens,
|
|
8093
8083
|
...data.usage === void 0 ? {} : { usage: data.usage }
|
|
8094
|
-
}
|
|
8084
|
+
};
|
|
8085
|
+
return session.append("compaction/summary", payload).seq;
|
|
8095
8086
|
}
|
|
8096
8087
|
/** 追加 compaction/end(log-only,结束生命周期;error 记录失败原因)。 */
|
|
8097
8088
|
function appendCompactionEnd(session, lifecycle, error) {
|
|
@@ -8144,6 +8135,8 @@ async function reflectPass(ctx, agent, config, window, target, signal) {
|
|
|
8144
8135
|
const threshold = Math.floor(window * config.historyMergeRatio);
|
|
8145
8136
|
/** 全部块 token 估算合计(摘要总长)。 */
|
|
8146
8137
|
const tokens = blocks.reduce((total, block) => total + estimateTextTokens(block.text), 0);
|
|
8138
|
+
/** 被压缩块区段的字符数合计(压缩前内文长度;UI 标题统计用,与观察路径一致只计内容不计标签)。 */
|
|
8139
|
+
const shadowedCharCount = blocks.reduce((total, block) => total + historyInnerText(block.text).length, 0);
|
|
8147
8140
|
if (tokens < threshold) {
|
|
8148
8141
|
logger.step(`反思:摘要 ${tokens} tokens < 阈值 ${threshold},跳过`);
|
|
8149
8142
|
return;
|
|
@@ -8197,6 +8190,7 @@ async function reflectPass(ctx, agent, config, window, target, signal) {
|
|
|
8197
8190
|
},
|
|
8198
8191
|
shadowedSeqs: blockSeqs,
|
|
8199
8192
|
shadowedTokenCount: tokens,
|
|
8193
|
+
shadowedCharCount,
|
|
8200
8194
|
provider: target.provider,
|
|
8201
8195
|
model: target.model,
|
|
8202
8196
|
maxTokens: config.compressMaxTokens,
|
|
@@ -8290,6 +8284,14 @@ async function observePass(ctx, agent, config, window, tailCount, target, signal
|
|
|
8290
8284
|
const message = event ? session.deriveEventMessage(event) : null;
|
|
8291
8285
|
return total + (message ? ctx.tokenMeter.estimateMessage(message) : 0);
|
|
8292
8286
|
}, 0);
|
|
8287
|
+
/** 被替换消息的字符数合计(压缩前文本长度;UI 标题统计用,递归计入 tool-result 内嵌文本)。 */
|
|
8288
|
+
const shadowedCharCount = replaceSeqs.reduce((total, seq) => {
|
|
8289
|
+
/** 当前表层事件。 */
|
|
8290
|
+
const event = session.events[seq];
|
|
8291
|
+
/** 事件对应的消息(取文本块字符数)。 */
|
|
8292
|
+
const message = event ? session.deriveEventMessage(event) : null;
|
|
8293
|
+
return total + (message ? textCharCount(message) : 0);
|
|
8294
|
+
}, 0);
|
|
8293
8295
|
/** 本次压缩生命周期(compactionId + 当前轮次;摘要成功后才写入日志)。 */
|
|
8294
8296
|
const lifecycle = {
|
|
8295
8297
|
compactionId: newCompactionId(),
|
|
@@ -8309,6 +8311,7 @@ async function observePass(ctx, agent, config, window, tailCount, target, signal
|
|
|
8309
8311
|
},
|
|
8310
8312
|
shadowedSeqs: replaceSeqs,
|
|
8311
8313
|
shadowedTokenCount,
|
|
8314
|
+
shadowedCharCount,
|
|
8312
8315
|
provider: target.provider,
|
|
8313
8316
|
model: target.model,
|
|
8314
8317
|
maxTokens: config.compressMaxTokens,
|
|
@@ -8684,7 +8687,8 @@ function resolveConfig(raw) {
|
|
|
8684
8687
|
//#region src/recall.ts
|
|
8685
8688
|
/**
|
|
8686
8689
|
* recall 工具:按「完整消息」序号(index)回看原始会话(start/end 为完整消息 index,
|
|
8687
|
-
* offset 为相对 start 的完整消息步数)。完整消息即
|
|
8690
|
+
* offset 为相对 start 的完整消息步数)。完整消息即 `用户消息`(user_message 中 kind 为 user
|
|
8691
|
+
* 的部分)、`系统消息`(user_message 中其余 kind 的部分)、`模型输出文本`和`具有result的
|
|
8688
8692
|
* toolcall`;首条完整消息的 index 为 0,后续递增、会话内全局稳定,与摘要日志条目同一套编号。
|
|
8689
8693
|
* recall 自身不设输出上限:超大的工具结果由 tool-result-pruner 裁剪(pruneContent),
|
|
8690
8694
|
* 输出 token 由 pruner 配置控制。
|
|
@@ -8718,20 +8722,20 @@ function parseRecallArgs(raw) {
|
|
|
8718
8722
|
function buildRecallTool(getPruner) {
|
|
8719
8723
|
return {
|
|
8720
8724
|
name: "recall",
|
|
8721
|
-
description:
|
|
8725
|
+
description: `${COMPLETE_MESSAGE_DEFINITION}此工具可以精确查询完整消息。用index指定一个区间,返回区间内所有完整消息的内容。`,
|
|
8722
8726
|
parameters: {
|
|
8723
8727
|
start: {
|
|
8724
8728
|
type: "number",
|
|
8725
|
-
description: "完整消息序号(index
|
|
8729
|
+
description: "完整消息序号(index),作为基准边界,和end或offset配合,指定区间",
|
|
8726
8730
|
required: true
|
|
8727
8731
|
},
|
|
8728
8732
|
end: {
|
|
8729
8733
|
type: "number",
|
|
8730
|
-
description: "与 offset
|
|
8734
|
+
description: "与 offset 互斥,指定区间的另一个边界。"
|
|
8731
8735
|
},
|
|
8732
8736
|
offset: {
|
|
8733
8737
|
type: "number",
|
|
8734
|
-
description: "与 end
|
|
8738
|
+
description: "与 end 互斥。相对 start 的步数:正数向后、负数向前。"
|
|
8735
8739
|
}
|
|
8736
8740
|
},
|
|
8737
8741
|
output: {
|
|
@@ -8795,7 +8799,8 @@ function buildRecallTool(getPruner) {
|
|
|
8795
8799
|
//#region src/semantic-recall.ts
|
|
8796
8800
|
/**
|
|
8797
8801
|
* recall-semantic 工具:按自然语言 query 对会话全部完整消息(含被压缩/遮蔽的)做语义
|
|
8798
|
-
* 检索,返回最匹配的完整消息与匹配说明。完整消息即
|
|
8802
|
+
* 检索,返回最匹配的完整消息与匹配说明。完整消息即 `用户消息`(user_message 中 kind 为 user
|
|
8803
|
+
* 的部分)、`系统消息`(user_message 中其余 kind 的部分)、`模型输出文本`和`具有result的
|
|
8799
8804
|
* toolcall`;首条完整消息的 index 为 0,后续递增、会话内全局稳定。
|
|
8800
8805
|
*
|
|
8801
8806
|
* - 参数:query 必填;top_k(默认 3,1-10);start/end/offset 限定检索区间
|
|
@@ -8898,7 +8903,7 @@ function matchExplanation(query, text, score) {
|
|
|
8898
8903
|
return `相似度 ${score.toFixed(3)}${keywords}`;
|
|
8899
8904
|
}
|
|
8900
8905
|
/** 模型未就绪时返回给模型的文案(告知即可;下载完成后无需另行通知,直接再次调用)。 */
|
|
8901
|
-
const SEMANTIC_MODEL_NOT_READY_MESSAGE = "
|
|
8906
|
+
const SEMANTIC_MODEL_NOT_READY_MESSAGE = "语义检索暂不可用,本地向量模型尚未就绪。";
|
|
8902
8907
|
/** 构建 recall-semantic 工具定义(embedder/modelStatus 可注入,测试传替身;缺省用本地模型)。 */
|
|
8903
8908
|
function buildSemanticRecallTool(options) {
|
|
8904
8909
|
/** pruner 获取器(可选,裁剪超大工具结果)。 */
|
|
@@ -8907,7 +8912,7 @@ function buildSemanticRecallTool(options) {
|
|
|
8907
8912
|
const embed = options?.embedder ?? ((texts) => getEmbedder().then((fn) => fn(texts)));
|
|
8908
8913
|
return {
|
|
8909
8914
|
name: "recall-semantic",
|
|
8910
|
-
description:
|
|
8915
|
+
description: `${COMPLETE_MESSAGE_DEFINITION}此工具可以按自然语言含义,检索最符合的完整消息。默认在全消息范围搜索,可以指定区间。`,
|
|
8911
8916
|
parameters: {
|
|
8912
8917
|
query: {
|
|
8913
8918
|
type: "string",
|
|
@@ -8920,15 +8925,15 @@ function buildSemanticRecallTool(options) {
|
|
|
8920
8925
|
},
|
|
8921
8926
|
start: {
|
|
8922
8927
|
type: "number",
|
|
8923
|
-
description: "完整消息序号(index
|
|
8928
|
+
description: "完整消息序号(index),可选。作为基准边界,和end或offset配合,指定搜索区间。"
|
|
8924
8929
|
},
|
|
8925
8930
|
end: {
|
|
8926
8931
|
type: "number",
|
|
8927
|
-
description: "
|
|
8932
|
+
description: "必须和start配合使用,与 offset 互斥,指定搜索区间的另一个边界。"
|
|
8928
8933
|
},
|
|
8929
8934
|
offset: {
|
|
8930
8935
|
type: "number",
|
|
8931
|
-
description: "
|
|
8936
|
+
description: "必须和start配合使用,与 end 互斥。相对 start 的步数:正数向后、负数向前。"
|
|
8932
8937
|
}
|
|
8933
8938
|
},
|
|
8934
8939
|
output: {
|
package/dist/log-index.d.ts
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 会话日志索引:完整消息索引(index 定位完整消息,recall 与摘要共用同一套编号),
|
|
3
|
-
* 以及表层节点定位辅助。事件日志仅追加(被遮蔽的事件仍可读,recall 依赖此性质)。
|
|
4
|
-
*
|
|
5
|
-
* 完整消息是摘要日志与 recall 共用的定位单位,分三类:
|
|
6
|
-
* - user:用户消息(user/message)占一条;
|
|
7
|
-
* - assistant:模型输出文本(assistant/message 中的文本块)占一条;
|
|
8
|
-
* - toolcall:单个工具调用及其结果占一条(同一条 AI 消息里的文本与工具调用拆开,
|
|
9
|
-
* tool/result 按 source.callId 匹配其 tool-call;未匹配的 result 独立成条,防御)。
|
|
10
|
-
* 插件自产消息(<history>、运行时快照等 source.kind === 'plugin')不占 index。
|
|
11
|
-
* index 从 0 起、按日志顺序递增、只追加不重排 → 会话内全局稳定,
|
|
12
|
-
* 压缩后旧摘要条目引用的 index 仍然有效。
|
|
13
|
-
*/
|
|
14
1
|
import type { CompleteMessage, MessageIndex, Session, SessionEvent } from './types.ts';
|
|
15
2
|
/** 工具结果裁剪器结构(tool-result-pruner;超大结果渲染前裁剪)。 */
|
|
16
3
|
export type PrunerLike = {
|
|
@@ -26,7 +13,7 @@ export declare function messageIdOfEvent(event: SessionEvent | undefined): strin
|
|
|
26
13
|
/** 消息索引:按日志顺序列出全部消息事件,并按 message_id 定位其在消息序列中的下标。 */
|
|
27
14
|
export declare function indexMessages(session: Session): MessageIndex;
|
|
28
15
|
/**
|
|
29
|
-
*
|
|
16
|
+
* 完整消息索引:按日志顺序把消息事件折叠为完整消息序列(四类,见文件头)。
|
|
30
17
|
* 工具调用结果按 source.callId 匹配其 tool-call 并入该条;未匹配的 result 独立成条(防御)。
|
|
31
18
|
* 插件在 agent/pre-step 触发压缩(日志 call-result 完备),因此压缩时不存在未闭合的
|
|
32
19
|
* tool-call;索引对未闭合调用不设特殊处理。
|
|
@@ -34,7 +21,7 @@ export declare function indexMessages(session: Session): MessageIndex;
|
|
|
34
21
|
export declare function indexCompleteMessages(session: Session): CompleteMessage[];
|
|
35
22
|
/**
|
|
36
23
|
* 渲染一条完整消息的文本(recall 输出 / new 模式输入共用):
|
|
37
|
-
* - user:消息原文;
|
|
24
|
+
* - user / sys:消息原文;
|
|
38
25
|
* - assistant:仅文本块;
|
|
39
26
|
* - toolcall:调用块(工具名 + 参数)+ 结果文本(pruner 裁剪超大结果)。
|
|
40
27
|
*/
|
package/dist/log-index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-index.d.ts","sourceRoot":"","sources":["../src/log-index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"log-index.d.ts","sourceRoot":"","sources":["../src/log-index.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EACV,eAAe,EAEf,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;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAG5E;AAED,wEAAwE;AACxE,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAMpF;AAED,qDAAqD;AACrD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CA0B5D;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,EAAE,CA2EzE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,eAAe,EACnB,MAAM,CAAC,EAAE,UAAU,GAClB,MAAM,CAgER"}
|
package/dist/recall.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* recall 工具:按「完整消息」序号(index)回看原始会话(start/end 为完整消息 index,
|
|
3
|
-
* offset 为相对 start 的完整消息步数)。完整消息即
|
|
3
|
+
* offset 为相对 start 的完整消息步数)。完整消息即 `用户消息`(user_message 中 kind 为 user
|
|
4
|
+
* 的部分)、`系统消息`(user_message 中其余 kind 的部分)、`模型输出文本`和`具有result的
|
|
4
5
|
* toolcall`;首条完整消息的 index 为 0,后续递增、会话内全局稳定,与摘要日志条目同一套编号。
|
|
5
6
|
* recall 自身不设输出上限:超大的工具结果由 tool-result-pruner 裁剪(pruneContent),
|
|
6
7
|
* 输出 token 由 pruner 配置控制。
|
package/dist/recall.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../src/recall.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"recall.d.ts","sourceRoot":"","sources":["../src/recall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,YAAY,CAAC;AAGjE;;;GAGG;AACH,eAAO,MAAM,gBAAgB;;;;iBAQzB,CAAC;AAEL,4CAA4C;AAC5C,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;GAGG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,UAAU,CAYxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,SAAS,CAAC,EAAE,MAAM,OAAO,GAAG,cAAc,CAoFzE"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* recall-semantic 工具:按自然语言 query 对会话全部完整消息(含被压缩/遮蔽的)做语义
|
|
3
|
-
* 检索,返回最匹配的完整消息与匹配说明。完整消息即
|
|
3
|
+
* 检索,返回最匹配的完整消息与匹配说明。完整消息即 `用户消息`(user_message 中 kind 为 user
|
|
4
|
+
* 的部分)、`系统消息`(user_message 中其余 kind 的部分)、`模型输出文本`和`具有result的
|
|
4
5
|
* toolcall`;首条完整消息的 index 为 0,后续递增、会话内全局稳定。
|
|
5
6
|
*
|
|
6
7
|
* - 参数:query 必填;top_k(默认 3,1-10);start/end/offset 限定检索区间
|
|
@@ -45,7 +46,7 @@ export declare function tokenize(text: string): Set<string>;
|
|
|
45
46
|
/** 命中消息的匹配说明行:相似度 + 与 query 共有的关键词(最多 8 个)。 */
|
|
46
47
|
export declare function matchExplanation(query: string, text: string, score: number): string;
|
|
47
48
|
/** 模型未就绪时返回给模型的文案(告知即可;下载完成后无需另行通知,直接再次调用)。 */
|
|
48
|
-
export declare const SEMANTIC_MODEL_NOT_READY_MESSAGE = "\u8BED\u4E49\u68C0\u7D22\u6682\u4E0D\u53EF\u7528\
|
|
49
|
+
export declare const SEMANTIC_MODEL_NOT_READY_MESSAGE = "\u8BED\u4E49\u68C0\u7D22\u6682\u4E0D\u53EF\u7528\uFF0C\u672C\u5730\u5411\u91CF\u6A21\u578B\u5C1A\u672A\u5C31\u7EEA\u3002";
|
|
49
50
|
/** 构建 recall-semantic 工具定义(embedder/modelStatus 可注入,测试传替身;缺省用本地模型)。 */
|
|
50
51
|
export declare function buildSemanticRecallTool(options?: {
|
|
51
52
|
getPruner?: () => unknown;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semantic-recall.d.ts","sourceRoot":"","sources":["../src/semantic-recall.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"semantic-recall.d.ts","sourceRoot":"","sources":["../src/semantic-recall.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAoB,KAAK,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE/F,OAAO,KAAK,EAAmB,cAAc,EAAkB,MAAM,YAAY,CAAC;AAGlF,qEAAqE;AACrE,eAAO,MAAM,wBAAwB;;;;;;iBAUjC,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,CAWxE;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;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC,GACzD,WAAW,CA2Bb;AAED,uCAAuC;AACvC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAOlD;AAED,+CAA+C;AAC/C,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAcnF;AAED,+CAA+C;AAC/C,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,CAqHjB"}
|
package/dist/summarize.d.ts
CHANGED
|
@@ -5,9 +5,10 @@
|
|
|
5
5
|
* 输入与输出都是合法的 <history> 块(模型消息 + index 的表达形式),先定义块、要求压缩、
|
|
6
6
|
* 再给出数据源(下方 <history> 消息记录)。
|
|
7
7
|
*
|
|
8
|
-
* 观察输入由 renderMessages 渲染为 <history>
|
|
9
|
-
*
|
|
10
|
-
* <
|
|
8
|
+
* 观察输入由 renderMessages 渲染为 <history> 块:用户消息文本原样(图片/文件以
|
|
9
|
+
* 注释补充)、系统消息(user_message 中非 kind:user 的部分)渲染为
|
|
10
|
+
* <sys type="KIND" index="N"> 空块(内容不进入输入)、<reasoning> 参考条目
|
|
11
|
+
* (产物中没有)、assistant 文本与 toolcall&result 原样。
|
|
11
12
|
* 输出经 extractSummaryLog 校验:合法 <history> 块、不含 <reasoning>、index/start/end 连续
|
|
12
13
|
* (与预期覆盖区间一致),失败按 config.compressRetryCount 重试。
|
|
13
14
|
* token usage 从流式响应的 usage chunk 提取,归入主会话记录。仅主会话生效(index.ts 守卫)。
|
|
@@ -20,18 +21,14 @@ import { type RoutedTarget } from './utils.ts';
|
|
|
20
21
|
* index/start/end 连续)、输出格式(一个合法 <history> 块,无 reasoning)、数据源说明。
|
|
21
22
|
*/
|
|
22
23
|
export declare function buildHistoryPrompt(): string;
|
|
23
|
-
/**
|
|
24
|
-
* 提取文本中全部合法的 <system-reminder> 块(完整开闭标签对且内容可被 XML 解析);
|
|
25
|
-
* 供渲染(原样插入)与未压缩消息测量(不计入)共用;不合法部分按普通文本处理。
|
|
26
|
-
*/
|
|
27
|
-
export declare function legitimateSystemReminderBlocks(text: string): string[];
|
|
28
24
|
/**
|
|
29
25
|
* 渲染完整消息记录(观察输入,new 模式):输出一个合法的 <history> 块——
|
|
30
|
-
* - user → <user_message index="N"
|
|
26
|
+
* - user → <user_message index="N">(文本原样;图片/文件注释);
|
|
27
|
+
* - sys → <sys type="KIND" index="N"></sys>(系统消息空块,内容不进入压缩输入);
|
|
31
28
|
* - 每条 assistant 消息的 reasoning → <reasoning>(参考条目,产物中没有);
|
|
32
29
|
* - assistant / toolcall → <assistant index="N">(模型输出文本 / toolcall&result 原样)。
|
|
33
30
|
* 文本经 XML 序列化自动转义(用户输入 / 文本 / reasoning 中的特殊字符不破坏 XML 合法性)。
|
|
34
|
-
* 仅渲染 seqs
|
|
31
|
+
* 仅渲染 seqs 全部落在给定集合内的完整消息(本插件自产消息天然不占位)。
|
|
35
32
|
*/
|
|
36
33
|
export declare function renderMessages(session: Session, seqs: readonly number[]): string;
|
|
37
34
|
/** 摘要调用结果:文本 + 可选 token usage(摘要请求自身消耗,随 compaction/summary 归入主会话记录)。 */
|
|
@@ -42,10 +39,10 @@ export type SummarySubagentResult = {
|
|
|
42
39
|
/** 日志最小有效长度:<history> 中间内容小于该长度视为不合法(C 段校验)。 */
|
|
43
40
|
export declare const MIN_HISTORY_LENGTH = 10;
|
|
44
41
|
/** 产出日志后插入首个 <history> 后的格式说明(XML 注释,完整消息定义 + 条目标签语义)。 */
|
|
45
|
-
export declare const HISTORY_FORMAT_NOTE = "<!-- \u5B8C\u6574\u6D88\u606F\uFF1A`\u7528\u6237\u6D88\u606F`\uFF0C`\u6A21\u578B\u8F93\u51FA\u6587\u672C`\u548C`\u5177\u6709result\u7684toolcall`\
|
|
42
|
+
export declare const HISTORY_FORMAT_NOTE = "<!-- \u5B8C\u6574\u6D88\u606F\uFF1A`\u5B8C\u6574\u6D88\u606F`\u88AB\u5B9A\u4E49\u4E3A`\u7528\u6237\u6D88\u606F`\uFF0C`\u7CFB\u7EDF\u6D88\u606F`\uFF0C`\u6A21\u578B\u8F93\u51FA\u6587\u672C`\u548C`\u5177\u6709result\u7684toolcall`\u7684\u96C6\u5408\u3002\u9996\u6761`\u5B8C\u6574\u6D88\u606F`\u7684index\u662F0\uFF0C\u540E\u7EED\u7684index\u9012\u589E\u3002 <TAG index=\"N\">\u8868\u793A\u5355\u6761\u5B8C\u6574\u6D88\u606F\uFF0C<TAG start=\"A\" end=\"B\"> \u8868\u793A\u8FDE\u7EED\u6A21\u5757\uFF0Cstart/end \u662F\u9996\u5C3E\u5B8C\u6574\u6D88\u606F\u7684 index\uFF1B<sys type=\"KIND\" index=\"N\"> \u8868\u793A\u88AB\u538B\u7F29\u7684\u7CFB\u7EDF\u6D88\u606F\uFF0C\u5757\u4E2D\u4E3A\u7A7A -->";
|
|
46
43
|
/** history 块内条目(单条 index 或模块 start/end)。 */
|
|
47
44
|
export type HistoryEntryRange = {
|
|
48
|
-
kind: 'user' | 'assistant';
|
|
45
|
+
kind: 'user' | 'assistant' | 'sys';
|
|
49
46
|
index?: number;
|
|
50
47
|
start?: number;
|
|
51
48
|
end?: number;
|
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;;;;;;;;;;;;;;GAcG;AAaH,OAAO,KAAK,EAAE,KAAK,EAAmB,OAAO,EAAE,OAAO,EAAE,UAAU,EAAe,MAAM,YAAY,CAAC;AACpG,OAAO,EAAE,KAAK,YAAY,EAAQ,MAAM,YAAY,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,kBAAkB,IAAI,MAAM,CAsC3C;AA0CD;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAqEhF;AAED,yEAAyE;AACzE,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB,CAAC;AA8EF,gDAAgD;AAChD,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC,0DAA0D;AAC1D,eAAO,MAAM,mBAAmB,yrBAA6K,CAAC;AAE9M,4CAA4C;AAC5C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,KAAK,CAAC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,CAAC;AAoFF;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,EAAE,CAYrE;AACD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,iBAAiB,EAAE,GAC3B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAuBvC;AAED,sCAAsC;AACtC,MAAM,MAAM,sBAAsB,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,sBAAsB,GAAG,MAAM,GAAG,IAAI,CAiC/F;AACD,iFAAiF;AACjF,eAAO,MAAM,4BAA4B,KAAK,CAAC;AAK/C;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,KAAK,EACZ,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,OAAO,EACd,MAAM,CAAC,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,sBAAsB,CAAA;CAAE,GACpE,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CA+EvC"}
|