agentpage 0.0.54 → 0.0.55
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 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +244 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,7 +79,8 @@ npm install agentpage
|
|
|
79
79
|
|
|
80
80
|
- **Prompt + Tools + 路由三层解耦**:可以快速把"可执行 AI 能力"植入现有前端系统,按路由渐进式接入,支持"项目级工具 + 路由级工具"组合。
|
|
81
81
|
- **增量任务消费协议(REMAINING)**:任务不是一次性执行,而是逐轮消费收敛。每轮只做当前快照可执行的动作,通过 `REMAINING` 协议跟踪进度,支持协议修复和启发式回退,确保复杂多步任务稳定收敛。
|
|
82
|
-
-
|
|
82
|
+
- **原始目标锚定(Original Goal Anchor)**:Round 1+ 每轮消息注入用户原始输入作为任务对照组,防止模型在多步执行过程中偏航(如把“去 X 仓库创建 issue”误解为“创建 X 仓库”)。
|
|
83
|
+
- **12 层保护机制**:冗余拦截、快照防抖、元素恢复、Not-found 重试对话流、导航刷新、空转检测、重复批次防自转、协议修复、快照指纹变化检测、无效点击拦截与循环检测、附近可点击元素推荐、原始目标锚定 —— 目标是**稳定收敛**,而不是偶然成功。
|
|
83
84
|
- **Playwright 级别交互语义**:完整 pointer/mouse 事件链、4 种 scrollIntoView 策略轮换、actionability 五重检查(可见/稳定/可用/可编辑/遮挡)、智能重定向 retarget、隐藏控件代理点击(ElementPlus/AntD)、`select_option` value/label/index 三策略。
|
|
84
85
|
- **运行时事件信号追踪**:通过 `EventTarget.prototype` 补丁全局追踪事件绑定,快照中输出 `listeners="clk,inp,chg"` 信号,帮助 AI 精准识别真实可交互元素,而非猜测。
|
|
85
86
|
- **效果验证机制(Effect Check)**:每轮行动前自动检查上轮操作是否在当前快照中生效,未生效则尝试邻近元素,避免重复点击无效目标。
|
|
@@ -233,6 +234,9 @@ Round 3: 执行 C → REMAINING: DONE
|
|
|
233
234
|
| 协议修复回合 | remaining 未完成却无工具调用 | 注入强约束提示 |
|
|
234
235
|
| 轮次稳定等待 | 本轮有 DOM 变化动作 | loading hidden + DOM quiet(200ms/4s) |
|
|
235
236
|
| 快照指纹变化检测 | 本轮有 DOM 变更动作且行动后指纹不变 | 注入 `Snapshot unchanged` 提示,强制模型换目标 |
|
|
237
|
+
| 无效点击拦截与循环检测 | 快照未变时记录无效 click;近 4 轮在 ≤2 个目标间循环 | 拦截重复无效点击;循环检测后将所有循环目标加入拦截集 |
|
|
238
|
+
| 附近可点击元素推荐 | 点击被拦截或证实无效 | 从快照中查找目标附近 15 行内有点击信号的元素,按距离推荐 |
|
|
239
|
+
| 原始目标锚定 | Round 1+ 每轮 | 注入用户原始任务作为对照组,防止多步执行中偏航 |
|
|
236
240
|
|
|
237
241
|
### 5. 停机条件
|
|
238
242
|
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/core/tool-registry.ts","../src/core/types.ts","../src/core/agent-loop/types.ts","../src/web/ref-store.ts","../src/web/tools/page-info-tool.ts","../src/web/ui/panel.ts","../src/web/tools/dom-tool.ts","../src/web/tools/navigate-tool.ts","../src/web/tools/wait-tool.ts","../src/web/tools/evaluate-tool.ts","../src/web/messaging.ts","../src/web/index.ts"],"mappings":";;;;;;KA0BY,cAAA;EAuBV,qCArBA,OAAA,WAAkB,MAAA,mBAqBR;EAnBV,OAAA,GAAU,MAAA;AAAA;;;;;;ACfZ;;;KD0BY,cAAA;ECxBV,4CD0BA,IAAA,UCtBA;EDwBA,WAAA,UCxBK;ED0BL,MAAA,EAAQ,OAAA,ECpBW;EDsBnB,OAAA,GAAU,MAAA,EAAQ,MAAA,sBAA4B,OAAA,CAAQ,cAAA;AAAA;;;;KClC5C,UAAA;ED0BA,0BCxBV,EAAA;EAEA,IAAA,UD8BkB;EC5BlB,KAAA;AAAA;;KAMU,SAAA;EACV,IAAA,4CDiBA;ECfA,OAAA,WAAkB,KAAA;IAAQ,UAAA;IAAoB,MAAA;EAAA,IDmBpC;ECjBV,SAAA,GAAY,UAAA;AAAA;;KAMF,cAAA;iBAEV,IAAA;EAEA,SAAA,GAAY,UAAA,IA3BQ;EA6BpB,KAAA;IAAU,WAAA;IAAqB,YAAA;EAAA;AAAA;;;AAjBjC;;;KA2BY,QAAA;EACV,IAAA,CAAK,MAAA;IACH,YAAA;IACA,QAAA,EAAU,SAAA;IACV,KAAA,GAAQ,cAAA;EAAA,IACN,OAAA,CAAQ,cAAA;AAAA;;;;KCjDF,yBAAA;EFoBA,2BElBV,OAAA,YFkBgB;EEhBhB,SAAA,WF2BwB;EEzBxB,OAAA,WF+BQ;EE7BR,gBAAA;AAAA;AAAA,KAGU,gBAAA;EACV,UAAA;EACA,cAAA;EACA,mBAAA;EACA,eAAA;EACA,eAAA;EACA,aAAA;EACA,uBAAA;EACA,iBAAA;EACA,kBAAA;EACA,eAAA;EACA,eAAA;EACA,WAAA;EACA,YAAA;AAAA;;KAMU,kBAAA;EDzBA,mBC2BV,MAAA,IAAU,IAAA;EAEV,UAAA,IAAc,IAAA,UAAc,KAAA,oBD3B5B;EC6BA,YAAA,IAAgB,IAAA,UAAc,MAAA,EAAQ,cAAA,WDzBtC;EC2BA,OAAA,IAAW,KAAA;ED3BN;AAMP;;;;;;;EC8BE,wBAAA,IAA4B,MAAA,oBD3BkB;EC6B9C,SAAA,IAAa,OAAA,EAAS,gBAAA;AAAA;AAAA,KA4BZ,eAAA;EDhCV,iBCkCA,KAAA,UDhCE;ECkCF,SAAA,EAAW,KAAA;IAAQ,IAAA;IAAc,KAAA;IAAgB,MAAA,EAAQ,cAAA;EAAA,IDhC7C;ECkCZ,QAAA,EAAU,SAAA,IDlCgB;ECoC1B,OAAA,EAAS,gBAAA;AAAA;;;;;;AFrEX;;;;;;;;;;AAeA;;;;;;;;;;;;;;cGAa,QAAA;EAAA,QACH,GAAA;EHOsC;EAAA,QGLtC,MAAA;EHK4D;;;;cGCxD,GAAA;EFnCF;;;;;;;EE8CV,GAAA,CAAI,EAAA,EAAI,OAAA,EAAS,IAAA;EFxCZ;AAMP;;;EEkDE,GAAA,CAAI,EAAA,WAAa,OAAA;EFjDjB;EEsDA,GAAA,CAAI,EAAA;EFpDc;EEyDlB,KAAA,CAAA;EFzD8C;;;;;AAQhD;;;EE6DE,KAAA,CAAM,GAAA;EF3DN;EAAA,IEmEI,IAAA,CAAA;AAAA;;;;KCtFM,eAAA;EJKc,oBIHxB,QAAA;EJOgB;;;;;EIDhB,YAAA;EJCgB;AAWlB;;;;EINE,WAAA;EJcsD;;;;;EIRtD,QAAA,GAAW,QAAA,EJMX;EIJA,QAAA,WJMA;EIJA,WAAA,WJIU;EIFV,aAAA;EJEsD;;;;EIGtD,iBAAA;;AHrCF;;;EG0CE,kBAAA,aHxCA;EG0CA,qBAAA;EHtCA;;;AAMF;;EGsCE,cAAA;EHjCsB;;;;;;;EGyCtB,eAAA;AAAA;;AHnCF;;;;;;;;;;;;AAgBA;;;;;;;iBG6HgB,gBAAA,CACd,IAAA,GAAM,OAAA,EACN,OAAA,GAAS,eAAA;AAAA,iBAilBK,kBAAA,CAAA,GAAsB,cAAA;;;;KCnvB1B,YAAA;kCAEV,SAAA,GAAY,WAAA,ELKY;EKHxB,KAAA,YLOgB;EKLhB,UAAA,YLGkB;EKDlB,WAAA,WLGU;EKDV,QAAA,YLCgB;EKChB,KAAA,WLUwB;EKRxB,WAAA,WLcQ;EKZR,QAAA,WLcsD;EKZtD,SAAA;AAAA;;KAIG,WAAA;;KAGA,WAAA;;;;;;;;;;;;AJ7BL;;;cI6CqB,KAAA;EAAA,QACX,SAAA;EAAA,QACA,UAAA;EAAA,QACA,WAAA;EAAA,QACA,KAAA;EAAA,QACA,WAAA;EAAA,QACA,QAAA;EAAA,QACA,SAAA;EAAA,QAGA,IAAA;EAAA,QACA,GAAA;EAAA,QACA,IAAA;EAAA,QACA,OAAA;EAAA,QACA,UAAA;EAAA,QACA,OAAA;EAAA,QACA,OAAA;EAAA,QACA,SAAA;EAAA,QACA,UAAA;EAAA,QACA,gBAAA;EAAA,QACA,OAAA;EAAA,QAGA,WAAA;EAAA,QACA,WAAA;EAAA,QAEA,OAAA;EAAA,QACA,QAAA;EAAA,QACA,MAAA;EJ9CR;EIiDA,MAAA,IAAU,IAAA,aAAiB,OAAA;EJ/C3B;EIiDA,MAAA;cAEY,OAAA,GAAS,YAAA;EJnDsB;EIsE3C,KAAA,CAAA;EJ5DU;EI2EV,OAAA,CAAA;;EAmBA,IAAA,CAAA;EJ1FU;EIqGV,IAAA,CAAA;EJpGI;EI2GJ,MAAA,CAAA;EJ3GW;EIiHX,UAAA,CAAA;EJpHE;EIyHF,SAAA,CAAA;EJxHY;EI6HZ,UAAA,CAAW,IAAA,EAAM,WAAA,EAAa,IAAA;EJ5HpB;EI4IV,aAAA,CAAA;EJ3II;EIkJJ,SAAA,CAAU,MAAA,EAAQ,WAAA,EAAa,IAAA;EJlJL;EI4K1B,QAAA,CAAA;;EAKA,QAAA,CAAA;;EAKA,UAAA,CAAA;EHvOmC;EGsPnC,YAAA,CAAA;EHtPmC;;;;EAAA,QGiQ3B,YAAA;EAAA,QAYA,YAAA;EAAA,QAOA,SAAA;EAAA,QA6EA,UAAA;EHtVkB;;;;EAAA,QGwYlB,mBAAA;EHrYR;;;;EAAA,QGuaQ,WAAA;EHlaR;;;EAAA,QG0fQ,aAAA;EAAA,QA4BA,UAAA;EAAA,QAiBA,gBAAA;EAAA,QAaA,SAAA;EAAA,QAUA,cAAA;EAAA,QAWA,UAAA;AAAA;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/core/tool-registry.ts","../src/core/types.ts","../src/core/agent-loop/types.ts","../src/web/ref-store.ts","../src/web/tools/page-info-tool.ts","../src/web/ui/panel.ts","../src/web/tools/dom-tool.ts","../src/web/tools/navigate-tool.ts","../src/web/tools/wait-tool.ts","../src/web/tools/evaluate-tool.ts","../src/web/messaging.ts","../src/web/index.ts"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -7,13 +7,32 @@ import { computePosition, flip, offset, shift } from "@floating-ui/dom";
|
|
|
7
7
|
*
|
|
8
8
|
* 统一集中在该文件,避免在主循环中散落“魔法数字”。
|
|
9
9
|
*/
|
|
10
|
+
/** 单次 chat 最大循环轮次(超过后强制停机) */
|
|
10
11
|
const DEFAULT_MAX_ROUNDS = 40;
|
|
12
|
+
/** 元素未找到恢复时的等待时长(毫秒),等待后刷新快照重新定位目标 */
|
|
11
13
|
const DEFAULT_RECOVERY_WAIT_MS = 100;
|
|
14
|
+
/** 同一工具调用(相同 name + input)命中元素未找到时的最大自动恢复轮次 */
|
|
12
15
|
const DEFAULT_ACTION_RECOVERY_ROUNDS = 2;
|
|
16
|
+
/** 元素未找到重试对话流的最大尝试次数(聚合失败工具 + 快照 + attempt 标注发给 AI) */
|
|
13
17
|
const DEFAULT_NOT_FOUND_RETRY_ROUNDS = 2;
|
|
18
|
+
/** 元素未找到重试对话流中每次重试前的等待时长(毫秒),等待页面异步渲染完成 */
|
|
14
19
|
const DEFAULT_NOT_FOUND_RETRY_WAIT_MS = 1e3;
|
|
20
|
+
/** 轮次后稳定等待的总超时(毫秒):包含 loading 隐藏等待 + DOM 静默等待的总上限 */
|
|
15
21
|
const DEFAULT_ROUND_STABILITY_WAIT_TIMEOUT_MS = 4e3;
|
|
22
|
+
/** 轮次后 DOM 静默窗口(毫秒):DOM 在此时间内无变化视为稳定 */
|
|
16
23
|
const DEFAULT_ROUND_STABILITY_WAIT_QUIET_MS = 200;
|
|
24
|
+
/**
|
|
25
|
+
* 轮次后稳定等待的默认 loading 指示器选择器列表。
|
|
26
|
+
*
|
|
27
|
+
* 覆盖主流 UI 框架的加载态组件:
|
|
28
|
+
* - AntD:`.ant-spin` / `.ant-spin-spinning` / `.ant-skeleton`
|
|
29
|
+
* - Element Plus:`.el-loading-mask`
|
|
30
|
+
* - BK(蓝鲸):`.bk-loading` / `.bk-spin-loading` / `.bk-skeleton` / `.bk-sideslider-loading`
|
|
31
|
+
* - TDesign(TD):`.t-loading` / `.t-skeleton` / `.t-skeleton__row`
|
|
32
|
+
* - 通用:`[aria-busy="true"]` / `.skeleton` / `.loading`
|
|
33
|
+
*
|
|
34
|
+
* 用户自定义的 `roundStabilityWait.loadingSelectors` 会与此列表合并去重,不会覆盖默认值。
|
|
35
|
+
*/
|
|
17
36
|
const DEFAULT_ROUND_STABILITY_WAIT_LOADING_SELECTORS = [
|
|
18
37
|
".ant-spin",
|
|
19
38
|
".ant-spin-spinning",
|
|
@@ -30,11 +49,11 @@ const DEFAULT_ROUND_STABILITY_WAIT_LOADING_SELECTORS = [
|
|
|
30
49
|
".skeleton",
|
|
31
50
|
".loading"
|
|
32
51
|
];
|
|
33
|
-
/** 快照起始标记 —
|
|
52
|
+
/** 快照起始标记 — 用于在消息中定位快照边界,配合 stripSnapshotFromPrompt() 实现旧快照剥离 */
|
|
34
53
|
const SNAPSHOT_START = "<!-- SNAPSHOT_START -->";
|
|
35
|
-
/** 快照结束标记 */
|
|
54
|
+
/** 快照结束标记 — 与 SNAPSHOT_START 配对使用 */
|
|
36
55
|
const SNAPSHOT_END = "<!-- SNAPSHOT_END -->";
|
|
37
|
-
/** 旧快照被替换后的占位文本 */
|
|
56
|
+
/** 旧快照被替换后的占位文本 — 防止过期快照干扰模型决策 */
|
|
38
57
|
const SNAPSHOT_OUTDATED = "[此快照已过期,请参考对话中最新的快照]";
|
|
39
58
|
|
|
40
59
|
//#endregion
|
|
@@ -43,6 +62,12 @@ const SNAPSHOT_OUTDATED = "[此快照已过期,请参考对话中最新的快
|
|
|
43
62
|
* 异步睡眠。
|
|
44
63
|
*
|
|
45
64
|
* 用于重试等待、节流等待等场景。
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* ```ts
|
|
68
|
+
* await sleep(1000); // 等待 1 秒
|
|
69
|
+
* await sleep(100); // 元素恢复前等待 100ms
|
|
70
|
+
* ```
|
|
46
71
|
*/
|
|
47
72
|
function sleep$1(ms) {
|
|
48
73
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -52,6 +77,12 @@ function sleep$1(ms) {
|
|
|
52
77
|
*
|
|
53
78
|
* 工具返回 content 可能是 string 或 object;这里统一转成 string,
|
|
54
79
|
* 便于日志、错误判定、摘要拼接。
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* toContentString("已点击按钮") // → "已点击按钮"
|
|
84
|
+
* toContentString({ code: "OK", n: 1 }) // → '{\n "code": "OK",\n "n": 1\n}'
|
|
85
|
+
* ```
|
|
55
86
|
*/
|
|
56
87
|
function toContentString(content) {
|
|
57
88
|
return typeof content === "string" ? content : JSON.stringify(content, null, 2);
|
|
@@ -62,6 +93,15 @@ function toContentString(content) {
|
|
|
62
93
|
* 约定格式:`SNAPSHOT_HINT: EXPAND_CHILDREN #ref1 #ref2`
|
|
63
94
|
*
|
|
64
95
|
* 返回:去掉 `#` 前缀后的 ref id 列表。
|
|
96
|
+
*
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* parseSnapshotExpandHints("SNAPSHOT_HINT: EXPAND_CHILDREN #a1b2c #x9k3d")
|
|
100
|
+
* // → ["a1b2c", "x9k3d"]
|
|
101
|
+
*
|
|
102
|
+
* parseSnapshotExpandHints("REMAINING: DONE")
|
|
103
|
+
* // → [](无匹配)
|
|
104
|
+
* ```
|
|
65
105
|
*/
|
|
66
106
|
function parseSnapshotExpandHints(text) {
|
|
67
107
|
if (!text) return [];
|
|
@@ -78,8 +118,14 @@ function parseSnapshotExpandHints(text) {
|
|
|
78
118
|
* 提取 hash selector 的 ref。
|
|
79
119
|
*
|
|
80
120
|
* 仅处理“纯 hash 选择器”,例如 `#1rv01x`。
|
|
81
|
-
* 如果是复杂 CSS(如 `.x #id`)会返回 null,避免误判。
|
|
82
|
-
|
|
121
|
+
* 如果是复杂 CSS(如 `.x #id`)会返回 null,避免误判。 *
|
|
122
|
+
* @example
|
|
123
|
+
* ```ts
|
|
124
|
+
* extractHashSelectorRef({ selector: "#1rv01x" }) // → "1rv01x"
|
|
125
|
+
* extractHashSelectorRef({ selector: ".btn #id" }) // → null(复杂选择器)
|
|
126
|
+
* extractHashSelectorRef({ selector: "div" }) // → null(非 hash)
|
|
127
|
+
* extractHashSelectorRef({}) // → null
|
|
128
|
+
* ``` */
|
|
83
129
|
function extractHashSelectorRef(toolInput) {
|
|
84
130
|
if (!toolInput || typeof toolInput !== "object") return null;
|
|
85
131
|
const selector = toolInput.selector;
|
|
@@ -95,6 +141,16 @@ function extractHashSelectorRef(toolInput) {
|
|
|
95
141
|
* 再计算 djb2 哈希,确保指纹只反映真实页面结构和文本差异。
|
|
96
142
|
*
|
|
97
143
|
* 用途:轮次行动前后各算一次指纹,若一致说明操作未产生任何可见效果。
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* const before = computeSnapshotFingerprint('[button] "提交" #a1b2c');
|
|
148
|
+
* const after = computeSnapshotFingerprint('[button] "提交" #x9y8z');
|
|
149
|
+
* before === after // → true(内容相同,仅 hashID 变化)
|
|
150
|
+
*
|
|
151
|
+
* const changed = computeSnapshotFingerprint('[button] "已提交" #a1b2c');
|
|
152
|
+
* before === changed // → false(文本变化 → 指纹不同)
|
|
153
|
+
* ```
|
|
98
154
|
*/
|
|
99
155
|
function computeSnapshotFingerprint(snapshot) {
|
|
100
156
|
if (!snapshot) return "";
|
|
@@ -129,6 +185,25 @@ function _djb2(str) {
|
|
|
129
185
|
* - `INEFFECTIVE_CLICK_BLOCKED` 拦截消息中附带推荐
|
|
130
186
|
* - "Snapshot unchanged" 提示中附带推荐
|
|
131
187
|
* - 交替循环检测提示中附带推荐
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```ts
|
|
191
|
+
* // 假设快照片段:
|
|
192
|
+
* // [tr] listeners="clk" #14d1zek
|
|
193
|
+
* // [td]
|
|
194
|
+
* // [span] "forkCte" listeners="blr,fcs" #fkbidm
|
|
195
|
+
* // [td]
|
|
196
|
+
* // [a] "admin/forkCte" href="/repo/1" listeners="clk" #c3hyqd
|
|
197
|
+
*
|
|
198
|
+
* findNearbyClickTargets(snapshot, "#fkbidm")
|
|
199
|
+
* // → [
|
|
200
|
+
* // '#c3hyqd ([a] "admin/forkCte" listeners="clk")', // 距离近
|
|
201
|
+
* // '#14d1zek ([tr] "" listeners="clk")', // 距离稍远
|
|
202
|
+
* // ]
|
|
203
|
+
*
|
|
204
|
+
* findNearbyClickTargets(snapshot, "#fkbidm", new Set(["#14d1zek"]))
|
|
205
|
+
* // → ['#c3hyqd ([a] "admin/forkCte" listeners="clk")'] // 排除 #14d1zek
|
|
206
|
+
* ```
|
|
132
207
|
*/
|
|
133
208
|
function findNearbyClickTargets(snapshot, selector, excludeSelectors, windowSize = 15) {
|
|
134
209
|
if (!snapshot || !selector) return [];
|
|
@@ -183,8 +258,18 @@ function findNearbyClickTargets(snapshot, selector, excludeSelectors, windowSize
|
|
|
183
258
|
* 构建任务数组。
|
|
184
259
|
*
|
|
185
260
|
* 作用:把一轮工具调用规整成稳定字符串数组,
|
|
186
|
-
* 用于“上一轮任务回显”和“重复批次检测”。
|
|
187
|
-
|
|
261
|
+
* 用于“上一轮任务回显”和“重复批次检测”。 *
|
|
262
|
+
* @example
|
|
263
|
+
* ```ts
|
|
264
|
+
* buildTaskArray([
|
|
265
|
+
* { name: "dom", input: { action: "click", selector: "#a1b2c" } },
|
|
266
|
+
* { name: "dom", input: { action: "fill", selector: "#x9k3d", value: "hello" } },
|
|
267
|
+
* ])
|
|
268
|
+
* // → [
|
|
269
|
+
* // 'dom:{"action":"click","selector":"#a1b2c"}',
|
|
270
|
+
* // 'dom:{"action":"fill","selector":"#x9k3d","value":"hello"}',
|
|
271
|
+
* // ]
|
|
272
|
+
* ``` */
|
|
188
273
|
function buildTaskArray(toolCalls) {
|
|
189
274
|
return toolCalls.map((tc) => `${tc.name}:${JSON.stringify(tc.input)}`);
|
|
190
275
|
}
|
|
@@ -193,8 +278,17 @@ function buildTaskArray(toolCalls) {
|
|
|
193
278
|
*
|
|
194
279
|
* 优先保留 REMAINING;否则保留首段摘要,避免长文本污染上下文。
|
|
195
280
|
*
|
|
196
|
-
* 返回字符串会被注入下一轮消息,作为“上一轮模型输出摘要”。
|
|
197
|
-
|
|
281
|
+
* 返回字符串会被注入下一轮消息,作为“上一轮模型输出摘要”。 *
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* normalizeModelOutput("操作完成\nREMAINING: 填写表单")
|
|
285
|
+
* // → "REMAINING: 填写表单"
|
|
286
|
+
*
|
|
287
|
+
* normalizeModelOutput("已点击按钮,等待页面跳转...")
|
|
288
|
+
* // → "已点击按钮,等待页面跳转..."(首段摘要,最多 220 字符)
|
|
289
|
+
*
|
|
290
|
+
* normalizeModelOutput(undefined) // → ""
|
|
291
|
+
* ``` */
|
|
198
292
|
function normalizeModelOutput(text) {
|
|
199
293
|
if (!text) return "";
|
|
200
294
|
const trimmed = text.trim();
|
|
@@ -218,6 +312,21 @@ function normalizeModelOutput(text) {
|
|
|
218
312
|
* - `REMAINING: DONE` → 返回 `""`(任务完成)
|
|
219
313
|
* - `REMAINING: <text>` → 返回 `<text>`
|
|
220
314
|
* - DONE 后面尾随的摘要文本会被忽略(模型常在 DONE 后附加总结)
|
|
315
|
+
*
|
|
316
|
+
* @example
|
|
317
|
+
* ```ts
|
|
318
|
+
* parseRemainingInstruction("REMAINING: 填写表单并提交")
|
|
319
|
+
* // → "填写表单并提交"
|
|
320
|
+
*
|
|
321
|
+
* parseRemainingInstruction("REMAINING: DONE")
|
|
322
|
+
* // → ""(任务完成)
|
|
323
|
+
*
|
|
324
|
+
* parseRemainingInstruction("REMAINING: DONE - 已完成所有操作")
|
|
325
|
+
* // → ""(DONE 后的摘要被忽略)
|
|
326
|
+
*
|
|
327
|
+
* parseRemainingInstruction("我已经点击了按钮")
|
|
328
|
+
* // → null(无 REMAINING 协议)
|
|
329
|
+
* ```
|
|
221
330
|
*/
|
|
222
331
|
function parseRemainingInstruction(text) {
|
|
223
332
|
if (!text) return null;
|
|
@@ -238,6 +347,18 @@ function parseRemainingInstruction(text) {
|
|
|
238
347
|
* 策略:
|
|
239
348
|
* - 有 REMAINING 协议 -> 使用模型给出的 nextInstruction
|
|
240
349
|
* - 无协议 -> 保持 currentInstruction 不变(由上层决定是否启发式推进)
|
|
350
|
+
*
|
|
351
|
+
* @example
|
|
352
|
+
* ```ts
|
|
353
|
+
* deriveNextInstruction("REMAINING: 提交表单", "填写表单并提交")
|
|
354
|
+
* // → { nextInstruction: "提交表单", hasRemainingProtocol: true }
|
|
355
|
+
*
|
|
356
|
+
* deriveNextInstruction("REMAINING: DONE", "提交表单")
|
|
357
|
+
* // → { nextInstruction: "", hasRemainingProtocol: true }
|
|
358
|
+
*
|
|
359
|
+
* deriveNextInstruction("已点击按钮", "填写表单并提交")
|
|
360
|
+
* // → { nextInstruction: "填写表单并提交", hasRemainingProtocol: false }
|
|
361
|
+
* ```
|
|
241
362
|
*/
|
|
242
363
|
function deriveNextInstruction(text, currentInstruction) {
|
|
243
364
|
const parsed = parseRemainingInstruction(text);
|
|
@@ -255,8 +376,21 @@ function deriveNextInstruction(text, currentInstruction) {
|
|
|
255
376
|
*
|
|
256
377
|
* 用于协议缺失但本轮有执行动作时,按线性步骤剔除已执行数量。
|
|
257
378
|
*
|
|
258
|
-
* 这是“保守推进”策略,不保证语义完美,但能避免 remaining 长期不变。
|
|
259
|
-
|
|
379
|
+
* 这是“保守推进”策略,不保证语义完美,但能避免 remaining 长期不变。 *
|
|
380
|
+
* @example
|
|
381
|
+
* ```ts
|
|
382
|
+
* reduceRemainingHeuristically("点击按钮 然后 填写表单 然后 提交", 1)
|
|
383
|
+
* // → "填写表单 -> 提交"(剔除第 1 步)
|
|
384
|
+
*
|
|
385
|
+
* reduceRemainingHeuristically("点击按钮 然后 填写表单 然后 提交", 2)
|
|
386
|
+
* // → "提交"(剔除前 2 步)
|
|
387
|
+
*
|
|
388
|
+
* reduceRemainingHeuristically("点击按钮 然后 填写表单 然后 提交", 5)
|
|
389
|
+
* // → ""(所有步骤已完成)
|
|
390
|
+
*
|
|
391
|
+
* reduceRemainingHeuristically("完成任务", 1)
|
|
392
|
+
* // → "完成任务"(无法拆分,原样返回)
|
|
393
|
+
* ``` */
|
|
260
394
|
function reduceRemainingHeuristically(currentInstruction, executedCount) {
|
|
261
395
|
if (!currentInstruction.trim() || executedCount <= 0) return currentInstruction;
|
|
262
396
|
const parts = currentInstruction.replace(/\s+/g, " ").replace(/(->|=>|→)/g, " 然后 ").replace(/[,,。;;]/g, " 然后 ").split(/\s*(?:然后|再|并且|并|接着|随后|之后)\s*/g).map((part) => part.trim()).filter(Boolean);
|
|
@@ -272,9 +406,20 @@ function reduceRemainingHeuristically(currentInstruction, executedCount) {
|
|
|
272
406
|
*
|
|
273
407
|
* 当前规则:
|
|
274
408
|
* - `navigate.*` 一律断轮
|
|
409
|
+
* - `dom.click` 断轮
|
|
275
410
|
* - `dom.press` 仅 Enter 断轮
|
|
276
411
|
* - `evaluate` 断轮
|
|
277
412
|
* - 其他动作默认不断轮
|
|
413
|
+
*
|
|
414
|
+
* @example
|
|
415
|
+
* ```ts
|
|
416
|
+
* shouldForceRoundBreak("dom", { action: "click", selector: "#btn" }) // → true
|
|
417
|
+
* shouldForceRoundBreak("dom", { action: "fill", selector: "#in" }) // → false
|
|
418
|
+
* shouldForceRoundBreak("dom", { action: "press", key: "Enter" }) // → true
|
|
419
|
+
* shouldForceRoundBreak("dom", { action: "press", key: "Tab" }) // → false
|
|
420
|
+
* shouldForceRoundBreak("navigate", { action: "goto", url: "/home" }) // → true
|
|
421
|
+
* shouldForceRoundBreak("evaluate", { expression: "alert(1)" }) // → true
|
|
422
|
+
* ```
|
|
278
423
|
*/
|
|
279
424
|
function shouldForceRoundBreak(toolName, toolInput) {
|
|
280
425
|
const action = getToolAction(toolInput);
|
|
@@ -287,11 +432,20 @@ function shouldForceRoundBreak(toolName, toolInput) {
|
|
|
287
432
|
return toolName === "evaluate";
|
|
288
433
|
}
|
|
289
434
|
/**
|
|
290
|
-
*
|
|
435
|
+
* 判定动作是否可能引发页面结构或状态变化(宽泛判定)。
|
|
291
436
|
*
|
|
292
|
-
*
|
|
437
|
+
* 用于"轮次后稳定等待"触发条件:
|
|
293
438
|
* - 命中 true:本轮结束后执行加载态 + DOM 静默双重等待
|
|
294
439
|
* - 命中 false:跳过等待,直接进入下一轮
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```ts
|
|
443
|
+
* isPotentialDomMutation("dom", { action: "click" }) // → true
|
|
444
|
+
* isPotentialDomMutation("dom", { action: "fill" }) // → true
|
|
445
|
+
* isPotentialDomMutation("dom", { action: "get_text" }) // → false(只读)
|
|
446
|
+
* isPotentialDomMutation("navigate", { action: "goto" }) // → true
|
|
447
|
+
* isPotentialDomMutation("page_info", { action: "snapshot" }) // → false
|
|
448
|
+
* ```
|
|
295
449
|
*/
|
|
296
450
|
function isPotentialDomMutation(toolName, toolInput) {
|
|
297
451
|
const action = getToolAction(toolInput);
|
|
@@ -330,6 +484,18 @@ function isPotentialDomMutation(toolName, toolInput) {
|
|
|
330
484
|
*
|
|
331
485
|
* 用途:协议缺失计数重置与豁免。仅当本轮有"确定性推进"时才重置协议缺失计数器,
|
|
332
486
|
* 避免模型反复点击无效目标导致死循环。
|
|
487
|
+
*
|
|
488
|
+
* @example
|
|
489
|
+
* ```ts
|
|
490
|
+
* isConfirmedProgressAction("dom", { action: "fill" }) // → true
|
|
491
|
+
* isConfirmedProgressAction("dom", { action: "type" }) // → true
|
|
492
|
+
* isConfirmedProgressAction("dom", { action: "select_option" }) // → true
|
|
493
|
+
* isConfirmedProgressAction("dom", { action: "press" }) // → true
|
|
494
|
+
* isConfirmedProgressAction("dom", { action: "click" }) // → false(不确定是否有效)
|
|
495
|
+
* isConfirmedProgressAction("navigate", { action: "goto" }) // → true
|
|
496
|
+
* isConfirmedProgressAction("my_custom_tool", { query: "..." }) // → true(自定义工具)
|
|
497
|
+
* isConfirmedProgressAction("page_info", { action: "snapshot" }) // → false(只读)
|
|
498
|
+
* ```
|
|
333
499
|
*/
|
|
334
500
|
function isConfirmedProgressAction(toolName, toolInput) {
|
|
335
501
|
if (toolName === "navigate") return true;
|
|
@@ -357,8 +523,20 @@ function isConfirmedProgressAction(toolName, toolInput) {
|
|
|
357
523
|
* 采集找不到元素任务。
|
|
358
524
|
*
|
|
359
525
|
* 返回 null 表示当前结果不属于“元素未找到”,
|
|
360
|
-
* 返回对象表示可进入 not-found retry 对话流。
|
|
361
|
-
|
|
526
|
+
* 返回对象表示可进入 not-found retry 对话流。 *
|
|
527
|
+
* @example
|
|
528
|
+
* ```ts
|
|
529
|
+
* collectMissingTask("dom", { action: "click", selector: "#xyz" }, {
|
|
530
|
+
* content: "未找到 #xyz 对应的元素",
|
|
531
|
+
* details: { error: true, code: "ELEMENT_NOT_FOUND" },
|
|
532
|
+
* })
|
|
533
|
+
* // → { name: "dom", input: {...}, reason: "未找到 #xyz 对应的元素" }
|
|
534
|
+
*
|
|
535
|
+
* collectMissingTask("dom", { action: "click", selector: "#btn" }, {
|
|
536
|
+
* content: "已点击按钮",
|
|
537
|
+
* })
|
|
538
|
+
* // → null(操作成功,非元素未找到)
|
|
539
|
+
* ``` */
|
|
362
540
|
function collectMissingTask(name, input, result) {
|
|
363
541
|
if (!isElementNotFoundResult(result)) return null;
|
|
364
542
|
return {
|
|
@@ -373,6 +551,18 @@ function collectMissingTask(name, input, result) {
|
|
|
373
551
|
* 判定顺序:
|
|
374
552
|
* 1) 优先看结构化错误码 `ELEMENT_NOT_FOUND`
|
|
375
553
|
* 2) 回退看中文错误文本关键词(兼容历史结果格式)
|
|
554
|
+
*
|
|
555
|
+
* @example
|
|
556
|
+
* ```ts
|
|
557
|
+
* isElementNotFoundResult({ content: "...", details: { code: "ELEMENT_NOT_FOUND" } })
|
|
558
|
+
* // → true(结构化错误码命中)
|
|
559
|
+
*
|
|
560
|
+
* isElementNotFoundResult({ content: "未找到 #abc 对应的元素" })
|
|
561
|
+
* // → true(中文关键词回退命中)
|
|
562
|
+
*
|
|
563
|
+
* isElementNotFoundResult({ content: "已点击按钮" })
|
|
564
|
+
* // → false
|
|
565
|
+
* ```
|
|
376
566
|
*/
|
|
377
567
|
function isElementNotFoundResult(result) {
|
|
378
568
|
const details = result.details;
|
|
@@ -386,15 +576,29 @@ function isElementNotFoundResult(result) {
|
|
|
386
576
|
* 生成稳定调用键。
|
|
387
577
|
*
|
|
388
578
|
* 用于 recoveryAttempts 的 map key(同名 + 同参数视为同一调用)。
|
|
579
|
+
*
|
|
580
|
+
* @example
|
|
581
|
+
* ```ts
|
|
582
|
+
* buildToolCallKey("dom", { action: "click", selector: "#a1b2c" })
|
|
583
|
+
* // → 'dom:{"action":"click","selector":"#a1b2c"}'
|
|
584
|
+
* ```
|
|
389
585
|
*/
|
|
390
586
|
function buildToolCallKey(name, input) {
|
|
391
587
|
return `${name}:${JSON.stringify(input)}`;
|
|
392
588
|
}
|
|
393
589
|
/**
|
|
394
590
|
* 解析恢复等待时长。
|
|
395
|
-
* 优先级:waitMs > waitSeconds >
|
|
591
|
+
* 优先级:waitMs > waitSeconds > 默认值(100ms)。
|
|
396
592
|
*
|
|
397
593
|
* 统一返回毫秒整数,且最小为 0。
|
|
594
|
+
*
|
|
595
|
+
* @example
|
|
596
|
+
* ```ts
|
|
597
|
+
* resolveRecoveryWaitMs({ waitMs: 500 }) // → 500
|
|
598
|
+
* resolveRecoveryWaitMs({ waitSeconds: 2 }) // → 2000
|
|
599
|
+
* resolveRecoveryWaitMs({}) // → 100(DEFAULT_RECOVERY_WAIT_MS)
|
|
600
|
+
* resolveRecoveryWaitMs(null) // → 100
|
|
601
|
+
* ```
|
|
398
602
|
*/
|
|
399
603
|
function resolveRecoveryWaitMs(input) {
|
|
400
604
|
if (!input || typeof input !== "object") return DEFAULT_RECOVERY_WAIT_MS;
|
|
@@ -409,6 +613,13 @@ function resolveRecoveryWaitMs(input) {
|
|
|
409
613
|
* 读取工具 action。
|
|
410
614
|
*
|
|
411
615
|
* 仅在 input 是对象且 action 为字符串时返回值,否则返回 undefined。
|
|
616
|
+
*
|
|
617
|
+
* @example
|
|
618
|
+
* ```ts
|
|
619
|
+
* getToolAction({ action: "click", selector: "#btn" }) // → "click"
|
|
620
|
+
* getToolAction({ selector: "#btn" }) // → undefined(无 action)
|
|
621
|
+
* getToolAction(null) // → undefined
|
|
622
|
+
* ```
|
|
412
623
|
*/
|
|
413
624
|
function getToolAction(input) {
|
|
414
625
|
if (!input || typeof input !== "object") return void 0;
|
|
@@ -419,6 +630,15 @@ function getToolAction(input) {
|
|
|
419
630
|
* 判定错误标记。
|
|
420
631
|
*
|
|
421
632
|
* 约定:`result.details.error === true` 视为错误结果。
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```ts
|
|
636
|
+
* hasToolError({ content: "...", details: { error: true, code: "ELEMENT_NOT_FOUND" } })
|
|
637
|
+
* // → true
|
|
638
|
+
*
|
|
639
|
+
* hasToolError({ content: "已点击按钮" })
|
|
640
|
+
* // → false(无 details 或 error 不为 true)
|
|
641
|
+
* ```
|
|
422
642
|
*/
|
|
423
643
|
function hasToolError(result) {
|
|
424
644
|
return result.details && typeof result.details === "object" ? Boolean(result.details.error) : false;
|
|
@@ -2435,7 +2655,6 @@ function normalizeExtraInstructions(input) {
|
|
|
2435
2655
|
* - hash ID 定位:仅交互元素携带 #hashID,非交互元素为上下文
|
|
2436
2656
|
* - 事件信号:listeners="..." 标注运行时事件绑定
|
|
2437
2657
|
* - 批量执行:同轮完成所有独立可见操作
|
|
2438
|
-
* - 输入顺序:fill/type 前必须先 focus/click 同一目标
|
|
2439
2658
|
* - DOM 变化断轮:会改变 DOM 的动作执行后等待下一轮新快照
|
|
2440
2659
|
* - 停机规则:任务完成后输出 REMAINING: DONE
|
|
2441
2660
|
*
|
|
@@ -2471,9 +2690,8 @@ function buildSystemPrompt(params = {}) {
|
|
|
2471
2690
|
"- If the text you want to click has no click signal, look at its parent row/container or nearby sibling that does have clk listener.",
|
|
2472
2691
|
"- No-effect fallback: if a click produced no page change (snapshot unchanged), do NOT repeat the same target. Instead: (1) look for <a> links or <button> inside the clicked container; (2) try a parent or sibling with stronger click signal; (3) try a completely different approach (e.g., search, filter, sidebar navigation, or use evaluate to trigger the action programmatically).",
|
|
2473
2692
|
"- Batch fill/type/check/select_option freely within one round. A click always ends the round — send at most ONE click as the LAST action in a batch.",
|
|
2474
|
-
"-
|
|
2693
|
+
"- fill/type/select_option auto-focus: these actions automatically click and focus the target before input — do NOT send a separate focus/click before them.",
|
|
2475
2694
|
"- Search/filter inputs: after fill, press Enter (or click search button) to trigger the search. Do not assume fill alone submits.",
|
|
2476
|
-
"- Do NOT run focus-only batches. Each focus must be immediately followed by its fill/type/select_option.",
|
|
2477
2695
|
"- Steppers: compute delta from visible value, click exactly |delta| times. Check/uncheck: target real input control.",
|
|
2478
2696
|
"- DOM-changing action (click/modal/navigate): ends the round, next snapshot follows. Actions sent after a click in the same batch are discarded.",
|
|
2479
2697
|
"- Intermediate progress is NOT completion: if an action only opens, expands, reveals, filters, paginates, switches context, or loads the next step, keep REMAINING on the final user goal until the requested end state/value/content is visible in the snapshot.",
|
|
@@ -3084,7 +3302,7 @@ function executeFillOnResolvedTarget(target, value, selector, action, sourceHint
|
|
|
3084
3302
|
}
|
|
3085
3303
|
};
|
|
3086
3304
|
scrollIntoViewIfNeeded(target);
|
|
3087
|
-
target
|
|
3305
|
+
dispatchClickEvents(target);
|
|
3088
3306
|
selectText(target);
|
|
3089
3307
|
setNativeValue(target, value);
|
|
3090
3308
|
dispatchInputEvents(target);
|
|
@@ -3102,7 +3320,7 @@ function executeFillOnResolvedTarget(target, value, selector, action, sourceHint
|
|
|
3102
3320
|
}
|
|
3103
3321
|
if (target instanceof HTMLTextAreaElement) {
|
|
3104
3322
|
scrollIntoViewIfNeeded(target);
|
|
3105
|
-
target
|
|
3323
|
+
dispatchClickEvents(target);
|
|
3106
3324
|
selectText(target);
|
|
3107
3325
|
setNativeValue(target, value);
|
|
3108
3326
|
dispatchInputEvents(target);
|
|
@@ -3124,7 +3342,7 @@ function executeFillOnResolvedTarget(target, value, selector, action, sourceHint
|
|
|
3124
3342
|
return { content: `已填写 ${describeElement(target)}: "${value}"${suffix}` };
|
|
3125
3343
|
}
|
|
3126
3344
|
if (target instanceof HTMLElement && target.isContentEditable) {
|
|
3127
|
-
target
|
|
3345
|
+
dispatchClickEvents(target);
|
|
3128
3346
|
selectText(target);
|
|
3129
3347
|
if (value) document.execCommand("insertText", false, value);
|
|
3130
3348
|
else document.execCommand("delete", false, void 0);
|
|
@@ -3616,7 +3834,8 @@ function createDomTool() {
|
|
|
3616
3834
|
dispatchClickEvents(option);
|
|
3617
3835
|
return { content: `已在自定义下拉中选择 "${wanted}"` };
|
|
3618
3836
|
}
|
|
3619
|
-
target
|
|
3837
|
+
scrollIntoViewIfNeeded(target);
|
|
3838
|
+
dispatchClickEvents(target);
|
|
3620
3839
|
const options = Array.from(target.options);
|
|
3621
3840
|
let selected;
|
|
3622
3841
|
if (value !== void 0) selected = options.find((o) => o.value === value);
|
|
@@ -3711,7 +3930,8 @@ function createDomTool() {
|
|
|
3711
3930
|
if (value === void 0) return { content: "缺少 value 参数" };
|
|
3712
3931
|
const target = retarget(el, "follow-label");
|
|
3713
3932
|
scrollIntoViewIfNeeded(target);
|
|
3714
|
-
if (target instanceof HTMLElement) target
|
|
3933
|
+
if (target instanceof HTMLElement) dispatchClickEvents(target);
|
|
3934
|
+
else if (target instanceof SVGElement) target.focus();
|
|
3715
3935
|
for (const char of value) {
|
|
3716
3936
|
const init = {
|
|
3717
3937
|
key: char,
|