agentpage 0.0.20 → 0.0.21
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 +1 -0
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +80 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
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/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;;;KClDF,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;EFmBF,mBEjBR,MAAA,IAAU,IAAA,mBFmBQ;EEjBlB,UAAA,IAAc,IAAA,UAAc,KAAA,oBFiBkB;EEf9C,YAAA,IAAgB,IAAA,UAAc,MAAA,EAAQ,cAAA,WFe8B;EEbpE,OAAA,IAAW,KAAA;;;;ADrBb;;;;;EC8BE,wBAAA,IAA4B,MAAA,oBDxB5B;EC0BA,SAAA,IAAa,OAAA,EAAS,gBAAA;AAAA;AAAA,KA0BZ,eAAA;ED7BA,iBC+BV,KAAA,UD/B2C;ECiC3C,SAAA,EAAW,KAAA;IAAQ,IAAA;IAAc,KAAA;IAAgB,MAAA,EAAQ,cAAA;EAAA,IDnB/C;ECqBV,QAAA,EAAU,SAAA,IDpBN;ECsBJ,OAAA,EAAS,gBAAA;AAAA;;;;;;AFvDX;;;;;;;;;;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;;;;KCvFM,eAAA;EJMc,mBIJxB,QAAA;EJQgB;;;;;EIFhB,YAAA;EJEgB;AAWlB;;;;EIPE,WAAA;EJesD;;;;;EITtD,QAAA,GAAW,QAAA,EJOX;EILA,QAAA,WJOA;EILA,WAAA,WJKU;EIHV,aAAA;AAAA;;;;;;AH/BF;;;;;;;;;AAYA;;;;;;iBG0CgB,gBAAA,CACd,IAAA,GAAM,OAAA,EACN,OAAA,GAAS,eAAA;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/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
|
@@ -90,7 +90,8 @@ function hasToolError(result) {
|
|
|
90
90
|
* 压缩/剪枝是怎么做的(中)/ How compression & pruning works in practice (EN):
|
|
91
91
|
* - `viewportOnly=true` 时:仅保留与视口相交元素(根层容器保留),完全视口外元素跳过。
|
|
92
92
|
* - `pruneLayout=true` 时:无 id/无语义/无交互/无直接文本的布局容器会被“折叠”,
|
|
93
|
-
*
|
|
93
|
+
* 子节点直接提升输出,减少无意义层级;当同一折叠容器提升出多个相邻节点时,
|
|
94
|
+
* 快照会用括号分组块标记其关联来源(collapsed-group)。
|
|
94
95
|
* - `maxNodes`:全局节点预算,超限后停止继续遍历并追加 truncation 提示。
|
|
95
96
|
* - `maxChildren`:每个父节点只保留前 N 个子元素,其余用 `... (n children omitted)` 汇总。
|
|
96
97
|
* - `maxTextLength`:节点文本按长度截断,避免长段文案占满上下文。
|
|
@@ -1368,8 +1369,11 @@ function buildSystemPrompt(params = {}) {
|
|
|
1368
1369
|
" Output: new remaining task after removing this-round actions.",
|
|
1369
1370
|
"- Use only visible targets from snapshot. Use #hashID as selector. Do not guess CSS selectors.",
|
|
1370
1371
|
"- Batch independent visible actions in one round. Do not split one form into many rounds unnecessarily.",
|
|
1371
|
-
"- Strict input order: before every fill/type/select_option,
|
|
1372
|
-
"-
|
|
1372
|
+
"- Strict input order (MANDATORY): before every fill/type/select_option, click or focus the SAME target immediately in the SAME round.",
|
|
1373
|
+
"- Multi-field rule (MANDATORY): execute alternating pairs in one batch: focus/click field A -> fill/type A -> focus/click field B -> fill/type B.",
|
|
1374
|
+
"- Do NOT run focus-only batches (e.g., focus A -> focus B). Each focused input/select target must be followed by its input/select action right away.",
|
|
1375
|
+
"- Fixed sequence examples: dom.focus(#name) -> dom.fill(#name, \"new-name\") -> dom.focus(#desc) -> dom.fill(#desc, \"new-desc\"); dom.click(#select) -> dom.select_option(#select, ...).",
|
|
1376
|
+
"- For check/uncheck, target the real input control (checkbox/radio), not nearby text/container nodes.",
|
|
1373
1377
|
"- Form batch rule: for one visible form, complete all independent fields in one round; do not fill one field then verify repeatedly.",
|
|
1374
1378
|
"- If an action will change DOM (open modal, navigate), stop after that action batch and continue next round with new snapshot.",
|
|
1375
1379
|
"- Do NOT call page_info (snapshot/query/get_url/get_title). Snapshot is already provided every round.",
|
|
@@ -1603,6 +1607,53 @@ function isEditableElement(el) {
|
|
|
1603
1607
|
if (el instanceof HTMLSelectElement) return true;
|
|
1604
1608
|
return el instanceof HTMLElement && el.isContentEditable;
|
|
1605
1609
|
}
|
|
1610
|
+
function isCheckableInput(el) {
|
|
1611
|
+
return el instanceof HTMLInputElement && (el.type === "checkbox" || el.type === "radio");
|
|
1612
|
+
}
|
|
1613
|
+
function findCheckableIn(el) {
|
|
1614
|
+
if (!el) return null;
|
|
1615
|
+
const found = el.querySelector("input[type=\"checkbox\"], input[type=\"radio\"]");
|
|
1616
|
+
return isCheckableInput(found) ? found : null;
|
|
1617
|
+
}
|
|
1618
|
+
/**
|
|
1619
|
+
* 归一化 check/uncheck 目标:
|
|
1620
|
+
* 允许模型命中文本容器/label/div,再回溯到关联 checkbox/radio,
|
|
1621
|
+
* 以降低快照剪枝导致的“命中语义节点而非真实控件”失败率。
|
|
1622
|
+
*/
|
|
1623
|
+
function resolveCheckableTarget(el) {
|
|
1624
|
+
if (isCheckableInput(el)) return el;
|
|
1625
|
+
if (el instanceof HTMLLabelElement) {
|
|
1626
|
+
const byLabel = findCheckableIn(el);
|
|
1627
|
+
if (byLabel) return byLabel;
|
|
1628
|
+
const htmlFor = el.htmlFor?.trim();
|
|
1629
|
+
if (htmlFor) {
|
|
1630
|
+
const byFor = document.getElementById(htmlFor);
|
|
1631
|
+
if (isCheckableInput(byFor)) return byFor;
|
|
1632
|
+
}
|
|
1633
|
+
}
|
|
1634
|
+
if (el instanceof HTMLElement) {
|
|
1635
|
+
const ownerLabel = el.closest("label");
|
|
1636
|
+
if (ownerLabel) {
|
|
1637
|
+
const byOwnerLabel = findCheckableIn(ownerLabel);
|
|
1638
|
+
if (byOwnerLabel) return byOwnerLabel;
|
|
1639
|
+
const htmlFor = ownerLabel.htmlFor?.trim();
|
|
1640
|
+
if (htmlFor) {
|
|
1641
|
+
const byFor = document.getElementById(htmlFor);
|
|
1642
|
+
if (isCheckableInput(byFor)) return byFor;
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
const inSelf = findCheckableIn(el);
|
|
1646
|
+
if (inSelf) return inSelf;
|
|
1647
|
+
const prev = el.previousElementSibling;
|
|
1648
|
+
if (isCheckableInput(prev)) return prev;
|
|
1649
|
+
const next = el.nextElementSibling;
|
|
1650
|
+
if (isCheckableInput(next)) return next;
|
|
1651
|
+
const parent = el.parentElement;
|
|
1652
|
+
const inParent = findCheckableIn(parent);
|
|
1653
|
+
if (inParent) return inParent;
|
|
1654
|
+
}
|
|
1655
|
+
return null;
|
|
1656
|
+
}
|
|
1606
1657
|
function ensureActionable(el, action, selector) {
|
|
1607
1658
|
if (!el.isConnected) return {
|
|
1608
1659
|
content: `"${selector}" 元素已脱离文档,无法执行 ${action}`,
|
|
@@ -1680,14 +1731,17 @@ function createDomTool() {
|
|
|
1680
1731
|
description: [
|
|
1681
1732
|
"Perform DOM operations on the current page.",
|
|
1682
1733
|
"Actions: click, fill, select_option, clear, check, uncheck, type, focus, hover, press, get_text, get_attr, set_attr, add_class, remove_class.",
|
|
1734
|
+
"Input/Select rule: before each fill/type/select_option, click or focus the same target immediately in the same round.",
|
|
1735
|
+
"For multiple fields, use alternating pairs in one batch: focus/click A -> fill/type A -> focus/click B -> fill/type B.",
|
|
1736
|
+
"Do not send focus-only batches for editable fields.",
|
|
1683
1737
|
"Use the hash ID from DOM snapshot (e.g. #a1b2c) as selector."
|
|
1684
1738
|
].join(" "),
|
|
1685
1739
|
schema: Type.Object({
|
|
1686
|
-
action: Type.String({ description: "DOM action: click | fill | select_option | clear | check | uncheck | type | focus | hover | press | get_text | get_attr | set_attr | add_class | remove_class" }),
|
|
1740
|
+
action: Type.String({ description: "DOM action: click | fill | select_option | clear | check | uncheck | type | focus | hover | press | get_text | get_attr | set_attr | add_class | remove_class. For fill/type/select_option, perform click/focus on same target immediately before it." }),
|
|
1687
1741
|
selector: Type.String({ description: "Element ref ID from snapshot (e.g. #r0, #r5) or CSS selector" }),
|
|
1688
|
-
value: Type.Optional(Type.String({ description: "Value for fill/type/set_attr actions" })),
|
|
1742
|
+
value: Type.Optional(Type.String({ description: "Value for fill/type/set_attr actions. For fill/type, run after click/focus on same target in the same round." })),
|
|
1689
1743
|
key: Type.Optional(Type.String({ description: "Key name for press action (e.g. Enter, Escape, Tab, ArrowDown, ArrowUp, Backspace, Delete, Space)" })),
|
|
1690
|
-
label: Type.Optional(Type.String({ description: "Label text for select_option action (fallback when value is not provided)" })),
|
|
1744
|
+
label: Type.Optional(Type.String({ description: "Label text for select_option action (fallback when value is not provided). Run select_option after click/focus on same target in the same round." })),
|
|
1691
1745
|
index: Type.Optional(Type.Number({ description: "0-based option index for select_option action" })),
|
|
1692
1746
|
attribute: Type.Optional(Type.String({ description: "Attribute name for get_attr/set_attr actions" })),
|
|
1693
1747
|
className: Type.Optional(Type.String({ description: "CSS class name for add_class/remove_class" })),
|
|
@@ -1738,6 +1792,10 @@ function createDomTool() {
|
|
|
1738
1792
|
};
|
|
1739
1793
|
el = elOrError;
|
|
1740
1794
|
}
|
|
1795
|
+
if (action === "check" || action === "uncheck") {
|
|
1796
|
+
const resolvedCheckable = resolveCheckableTarget(el);
|
|
1797
|
+
if (resolvedCheckable) el = resolvedCheckable;
|
|
1798
|
+
}
|
|
1741
1799
|
try {
|
|
1742
1800
|
if (!force) {
|
|
1743
1801
|
const checkResult = ensureActionable(el, action, selector);
|
|
@@ -2255,13 +2313,18 @@ function generateSnapshot(root = document.body, options = {}) {
|
|
|
2255
2313
|
const orderedChildren = [...interactiveChildren, ...nonInteractiveChildren];
|
|
2256
2314
|
const selectedChildren = orderedChildren.slice(0, maxChildren);
|
|
2257
2315
|
const omittedChildren = orderedChildren.length - selectedChildren.length;
|
|
2258
|
-
const
|
|
2316
|
+
const childBlocks = [];
|
|
2259
2317
|
for (let i = 0; i < selectedChildren.length; i++) {
|
|
2260
2318
|
const childResult = walk(selectedChildren[i], depth, currentPath);
|
|
2261
|
-
if (childResult)
|
|
2319
|
+
if (childResult) childBlocks.push(childResult);
|
|
2262
2320
|
}
|
|
2263
|
-
if (
|
|
2264
|
-
return
|
|
2321
|
+
if (childBlocks.length === 0 && omittedChildren <= 0) return "";
|
|
2322
|
+
if (!(childBlocks.length >= 2 || omittedChildren > 0)) return childBlocks.join("\n");
|
|
2323
|
+
const groupLines = [`${" ".repeat(depth)}([${tag}] collapsed-group`];
|
|
2324
|
+
for (const block of childBlocks) groupLines.push(indentMultiline(block, 1));
|
|
2325
|
+
if (omittedChildren > 0) groupLines.push(`${" ".repeat(depth + 1)}... (${omittedChildren} children omitted)`);
|
|
2326
|
+
groupLines.push(`${" ".repeat(depth)})`);
|
|
2327
|
+
return groupLines.join("\n");
|
|
2265
2328
|
}
|
|
2266
2329
|
let line = `${indent}[${tag}]`;
|
|
2267
2330
|
if (directText) line += ` "${directText.slice(0, maxTextLength)}"`;
|
|
@@ -2312,6 +2375,13 @@ function queryAllElements(selector, limit = 20) {
|
|
|
2312
2375
|
return `选择器语法错误: ${selector}`;
|
|
2313
2376
|
}
|
|
2314
2377
|
}
|
|
2378
|
+
/**
|
|
2379
|
+
* 多行文本块缩进(中)/ Indent each line of a multiline block (EN).
|
|
2380
|
+
*/
|
|
2381
|
+
function indentMultiline(block, indentLevel) {
|
|
2382
|
+
const prefix = " ".repeat(indentLevel);
|
|
2383
|
+
return block.split("\n").map((line) => `${prefix}${line}`).join("\n");
|
|
2384
|
+
}
|
|
2315
2385
|
function createPageInfoTool() {
|
|
2316
2386
|
return {
|
|
2317
2387
|
name: "page_info",
|