pageqa 0.7.0 → 0.8.0
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 +32 -8
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +27 -9
- package/dist/agent.js.map +1 -1
- package/dist/bsk/tools.d.ts +27 -1
- package/dist/bsk/tools.js +133 -24
- package/dist/bsk/tools.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/locator.d.ts +14 -0
- package/dist/locator.js +15 -7
- package/dist/locator.js.map +1 -1
- package/dist/record.d.ts +2 -11
- package/dist/record.js +4 -5
- package/dist/record.js.map +1 -1
- package/dist/replay.js +9 -7
- package/dist/replay.js.map +1 -1
- package/dist/report.d.ts +10 -3
- package/dist/report.js +13 -4
- package/dist/report.js.map +1 -1
- package/dist/snapshot.d.ts +15 -0
- package/dist/snapshot.js +167 -0
- package/dist/snapshot.js.map +1 -0
- package/examples/smoke-test-page.html +531 -0
- package/examples/smoke-test.md +43 -0
- package/examples/smoke-test.replay.json +389 -0
- package/package.json +5 -3
package/dist/snapshot.js
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bsk 快照瘦身:把长快照裁剪成「可交互/可引用节点 + 关键文本」再喂给模型。
|
|
3
|
+
*
|
|
4
|
+
* 为什么要瘦身:bsk 快照是整棵 aria 树 + 可见文本,长页面一次几千至上万字符。
|
|
5
|
+
* 它同时进入模型上下文与代价最高的位置(每步一次 snapshot,长流程里累积几十次),
|
|
6
|
+
* 直接决定推理时间与「上下文被裁剪/超限」的风险。
|
|
7
|
+
*
|
|
8
|
+
* 瘦身必须**不改变定位**,因此规则是保守的、可解释的:
|
|
9
|
+
* - 带 `@eN` 的行**永不截断、永不丢弃**:模型靠它点击,录制/回放两侧的语义定位符
|
|
10
|
+
* 也靠它解析 role / name。
|
|
11
|
+
* - `@eN` 行的**祖先链**永不丢弃(祖先路径是同名元素消歧的唯一依据,见 locator.ts)。
|
|
12
|
+
* - 元信息行(`@vom` / `@view` / `@layers` / `L1 page`)保留,很短且能说明页面与视口。
|
|
13
|
+
* - 其余文本行:超长才截断(保留开头);仍超预算时从最长的非关键行开始省略,
|
|
14
|
+
* 短文本(标题/标签/状态提示)优先保留。
|
|
15
|
+
* 注意:瘦身文本只喂模型上下文;assert_text 的字面匹配用瘦身前完整原文
|
|
16
|
+
* (tools.ts 的 ensureRawSnapshot),避免长文本被截断造成假未命中。
|
|
17
|
+
* - 原文不超过阈值时**原样返回**:小页面不折腾,避免无谓的行为差异。
|
|
18
|
+
*/
|
|
19
|
+
import { indentWidth, isSnapshotMetaLine, parseRoleName, } from "./locator.js";
|
|
20
|
+
/** 原文不超过该长度时原样返回(小页面不折腾)。 */
|
|
21
|
+
const SLIM_TRIGGER_CHARS = 8_000;
|
|
22
|
+
/** 非关键文本行保留的长度,超出即截断。 */
|
|
23
|
+
const TEXT_LINE_MAX = 160;
|
|
24
|
+
/** 超预算时非关键行进一步收紧到的长度。 */
|
|
25
|
+
const TEXT_LINE_TIGHT = 80;
|
|
26
|
+
/** 瘦身后仍允许的最大字符数。 */
|
|
27
|
+
const SLIM_BUDGET_CHARS = 20_000;
|
|
28
|
+
/** 祖先行保留的长度(承载区域信息,也可能是「整页文本」这种超长可访问名)。 */
|
|
29
|
+
const ANCESTOR_LINE_MAX = 160;
|
|
30
|
+
/**
|
|
31
|
+
* 祖先行里可访问名的截断长度。
|
|
32
|
+
*
|
|
33
|
+
* 必须大于 locator 的路径名截断长度(`PATH_NAME_MAX = 60`):祖先路径两侧都取
|
|
34
|
+
* 「前 60 字符」比较,只要截断点在其之后,路径仍完全一致,消歧能力不受影响。
|
|
35
|
+
*/
|
|
36
|
+
const ANCESTOR_NAME_MAX = 120;
|
|
37
|
+
function classify(raw) {
|
|
38
|
+
const body = raw.trim();
|
|
39
|
+
if (!body)
|
|
40
|
+
return { raw, body, indent: 0, kind: "empty", keep: false };
|
|
41
|
+
const indent = indentWidth(raw);
|
|
42
|
+
if (isSnapshotMetaLine(body)) {
|
|
43
|
+
return { raw, body, indent, kind: "meta", keep: true };
|
|
44
|
+
}
|
|
45
|
+
if (/^@e\d+\b/.test(body)) {
|
|
46
|
+
return { raw, body, indent, kind: "ref", keep: true };
|
|
47
|
+
}
|
|
48
|
+
return { raw, body, indent, kind: "text", keep: false };
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 祖先行截断:保留缩进与 role,name 截断后**重建引号结构**。
|
|
52
|
+
*
|
|
53
|
+
* 直接按字符 slice 会把 name 的闭合引号切掉,`parseRoleName` 便取不到名字
|
|
54
|
+
* (它的正则需要成对引号),这一层的祖先路径会凭空消失——同名元素消歧能力随之下降。
|
|
55
|
+
*/
|
|
56
|
+
function clipAncestor(line) {
|
|
57
|
+
const indent = line.raw.slice(0, line.raw.length - line.raw.trimStart().length);
|
|
58
|
+
const { role, name } = parseRoleName(line.body);
|
|
59
|
+
if (!name)
|
|
60
|
+
return `${line.raw.slice(0, ANCESTOR_LINE_MAX)}…`;
|
|
61
|
+
const clipped = name.length > ANCESTOR_NAME_MAX
|
|
62
|
+
? name.slice(0, ANCESTOR_NAME_MAX) + "…"
|
|
63
|
+
: name;
|
|
64
|
+
return `${indent}${role ? role + " " : ""}${JSON.stringify(clipped)}`;
|
|
65
|
+
}
|
|
66
|
+
/** 统计渲染后文本的总字符数(含行间换行,用于预算判断)。 */
|
|
67
|
+
function totalChars(items) {
|
|
68
|
+
return items.reduce((n, i) => n + i.text.length + 1, 0);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 瘦身快照。纯函数:不依赖 bsk、不触碰网络,便于单测。
|
|
72
|
+
*/
|
|
73
|
+
export function slimSnapshot(text) {
|
|
74
|
+
const before = text.length;
|
|
75
|
+
const untouched = {
|
|
76
|
+
text,
|
|
77
|
+
before,
|
|
78
|
+
after: before,
|
|
79
|
+
applied: false,
|
|
80
|
+
truncatedLines: 0,
|
|
81
|
+
droppedLines: 0,
|
|
82
|
+
};
|
|
83
|
+
if (before <= SLIM_TRIGGER_CHARS)
|
|
84
|
+
return untouched;
|
|
85
|
+
const lines = text.split(/\r?\n/).map(classify);
|
|
86
|
+
// 1) 标记关键行:ref 行本身保留,并把它所在的祖先链一并保留。
|
|
87
|
+
// 祖先链是「这个元素在哪个区域下」的唯一来源,丢了它,同名元素只能靠序号猜。
|
|
88
|
+
const stack = [];
|
|
89
|
+
for (const line of lines) {
|
|
90
|
+
if (line.kind === "empty")
|
|
91
|
+
continue;
|
|
92
|
+
while (stack.length > 0 && stack[stack.length - 1].indent >= line.indent) {
|
|
93
|
+
stack.pop();
|
|
94
|
+
}
|
|
95
|
+
if (line.kind === "ref")
|
|
96
|
+
for (const ancestor of stack)
|
|
97
|
+
ancestor.keep = true;
|
|
98
|
+
stack.push(line);
|
|
99
|
+
}
|
|
100
|
+
// 2) 渲染:关键行保留;其余行超长即截断(保留开头,断言目标通常很短)。
|
|
101
|
+
let truncated = 0;
|
|
102
|
+
let dropped = 0;
|
|
103
|
+
const items = [];
|
|
104
|
+
for (const line of lines) {
|
|
105
|
+
if (line.kind === "empty")
|
|
106
|
+
continue;
|
|
107
|
+
let rendered = line.raw;
|
|
108
|
+
if (line.raw.length > (line.keep ? ANCESTOR_LINE_MAX : TEXT_LINE_MAX)) {
|
|
109
|
+
if (line.kind === "ref") {
|
|
110
|
+
// 可交互节点行原样保留:模型靠它点击,任何截断都可能让 name 与页面失配
|
|
111
|
+
rendered = line.raw;
|
|
112
|
+
}
|
|
113
|
+
else if (line.keep) {
|
|
114
|
+
rendered = clipAncestor(line);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
truncated += 1;
|
|
118
|
+
rendered = `${line.raw.slice(0, TEXT_LINE_MAX)}…(已截断 ${line.raw.length - TEXT_LINE_MAX} 字符)`;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
items.push({ line, text: rendered });
|
|
122
|
+
}
|
|
123
|
+
// 3) 预算兜底:仍超预算时先收紧非关键长行,再从最长的非关键行开始整行省略。
|
|
124
|
+
let total = totalChars(items);
|
|
125
|
+
if (total > SLIM_BUDGET_CHARS) {
|
|
126
|
+
for (const item of items) {
|
|
127
|
+
if (item.line.keep || item.text.length <= TEXT_LINE_TIGHT)
|
|
128
|
+
continue;
|
|
129
|
+
item.text = item.text.slice(0, TEXT_LINE_TIGHT) + "…";
|
|
130
|
+
truncated += 1;
|
|
131
|
+
}
|
|
132
|
+
total = totalChars(items);
|
|
133
|
+
}
|
|
134
|
+
if (total > SLIM_BUDGET_CHARS) {
|
|
135
|
+
const droppable = items
|
|
136
|
+
.filter((i) => !i.line.keep)
|
|
137
|
+
.sort((a, b) => b.text.length - a.text.length);
|
|
138
|
+
for (const item of droppable) {
|
|
139
|
+
if (total <= SLIM_BUDGET_CHARS)
|
|
140
|
+
break;
|
|
141
|
+
total -= item.text.length + 1;
|
|
142
|
+
item.text = "";
|
|
143
|
+
dropped += 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// 没有任何实质裁剪时保持原文(含空行),不做无意义的改写
|
|
147
|
+
if (truncated === 0 && dropped === 0)
|
|
148
|
+
return untouched;
|
|
149
|
+
const body = items
|
|
150
|
+
.filter((i) => i.text)
|
|
151
|
+
.map((i) => i.text)
|
|
152
|
+
.join("\n");
|
|
153
|
+
const after = body.length;
|
|
154
|
+
// 末尾写明做了什么:模型据此知道「哪些内容没看到」,而不是以为页面就这么点内容
|
|
155
|
+
const notice = `[pageqa] 快照已瘦身:${before} → ${after} 字符` +
|
|
156
|
+
`(截断 ${truncated} 处长文本、省略 ${dropped} 行非关键文本;` +
|
|
157
|
+
`可交互元素及其祖先区域、标题/状态文本均完整保留)`;
|
|
158
|
+
return {
|
|
159
|
+
text: `${body}\n${notice}`,
|
|
160
|
+
before,
|
|
161
|
+
after,
|
|
162
|
+
applied: true,
|
|
163
|
+
truncatedLines: truncated,
|
|
164
|
+
droppedLines: dropped,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../src/snapshot.ts"],"names":[],"mappings":"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"}
|