pi-agent-browser-native 0.2.47 → 0.2.49
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/CHANGELOG.md +63 -19
- package/README.md +52 -19
- package/dist/extensions/agent-browser/index.js +785 -0
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
- package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
- package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
- package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
- package/dist/extensions/agent-browser/lib/config.js +122 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
- package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
- package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
- package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
- package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
- package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
- package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
- package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
- package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
- package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
- package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
- package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
- package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
- package/dist/extensions/agent-browser/lib/playbook.js +121 -0
- package/dist/extensions/agent-browser/lib/process.js +448 -0
- package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
- package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
- package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
- package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
- package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
- package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
- package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
- package/dist/extensions/agent-browser/lib/results/network.js +73 -0
- package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
- package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
- package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
- package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
- package/dist/extensions/agent-browser/lib/results/text.js +40 -0
- package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
- package/dist/extensions/agent-browser/lib/runtime.js +816 -0
- package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
- package/dist/extensions/agent-browser/lib/temp.js +498 -0
- package/dist/extensions/agent-browser/lib/web-search.js +562 -0
- package/docs/ARCHITECTURE.md +10 -10
- package/docs/COMMAND_REFERENCE.md +35 -21
- package/docs/ELECTRON.md +3 -3
- package/docs/RELEASE.md +46 -26
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +35 -106
- package/docs/TOOL_CONTRACT.md +23 -21
- package/package.json +12 -8
- package/scripts/agent-browser-capability-baseline.mjs +6 -3
- package/scripts/config.mjs +8 -2
- package/scripts/doctor.mjs +19 -17
- package/scripts/platform-smoke.mjs +1 -1
- package/extensions/agent-browser/index.ts +0 -952
- package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
- package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
- package/extensions/agent-browser/lib/bash-guard.ts +0 -205
- package/extensions/agent-browser/lib/command-policy.ts +0 -71
- package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
- package/extensions/agent-browser/lib/config-policy.js +0 -690
- package/extensions/agent-browser/lib/config.ts +0 -209
- package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
- package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
- package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
- package/extensions/agent-browser/lib/electron/launch.ts +0 -499
- package/extensions/agent-browser/lib/executable-path.ts +0 -19
- package/extensions/agent-browser/lib/fs-utils.ts +0 -18
- package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
- package/extensions/agent-browser/lib/input-modes/job.ts +0 -451
- package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
- package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
- package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
- package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
- package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
- package/extensions/agent-browser/lib/input-modes.ts +0 -45
- package/extensions/agent-browser/lib/json-schema.ts +0 -73
- package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
- package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
- package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
- package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -257
- package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -912
- package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -512
- package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -1481
- package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
- package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
- package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
- package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
- package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
- package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -252
- package/extensions/agent-browser/lib/playbook.ts +0 -142
- package/extensions/agent-browser/lib/process.ts +0 -516
- package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
- package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
- package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
- package/extensions/agent-browser/lib/results/categories.ts +0 -106
- package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
- package/extensions/agent-browser/lib/results/contracts.ts +0 -241
- package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
- package/extensions/agent-browser/lib/results/envelope.ts +0 -195
- package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
- package/extensions/agent-browser/lib/results/network.ts +0 -78
- package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
- package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
- package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
- package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
- package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
- package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
- package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
- package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
- package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
- package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
- package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
- package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
- package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
- package/extensions/agent-browser/lib/results/presentation.ts +0 -257
- package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
- package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
- package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
- package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
- package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
- package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
- package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
- package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
- package/extensions/agent-browser/lib/results/text.ts +0 -40
- package/extensions/agent-browser/lib/runtime.ts +0 -988
- package/extensions/agent-browser/lib/session-page-state.ts +0 -512
- package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
- package/extensions/agent-browser/lib/temp.ts +0 -577
- package/extensions/agent-browser/lib/web-search.ts +0 -721
- /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import { isRecord } from "../../parsing.js";
|
|
2
|
+
import { redactSensitiveText } from "../../runtime.js";
|
|
3
|
+
import { withOptionalSessionArgs } from "../../results/next-actions.js";
|
|
4
|
+
import { runSessionCommandData } from "./session-state.js";
|
|
5
|
+
const CLICK_DISPATCH_MARKER_PREFIX = "__piAgentBrowserClickDispatchProbe_";
|
|
6
|
+
const CLICK_DISPATCH_CLEANUP_TIMEOUT_MS = 2_000;
|
|
7
|
+
const ACCESSIBLE_REF_CLICK_DISPATCH_ROLES = new Set(["button", "checkbox", "menuitem", "radio", "switch", "tab"]);
|
|
8
|
+
function parseClickRefId(selector) {
|
|
9
|
+
const trimmed = selector.trim();
|
|
10
|
+
const candidate = trimmed.startsWith("@") ? trimmed.slice(1) : trimmed.startsWith("ref=") ? trimmed.slice(4) : trimmed;
|
|
11
|
+
return /^e\d+$/.test(candidate) ? candidate : undefined;
|
|
12
|
+
}
|
|
13
|
+
function normalizeAccessibleName(name) {
|
|
14
|
+
return name.replace(/\s+/g, " ").trim().toLowerCase();
|
|
15
|
+
}
|
|
16
|
+
function getAccessibleRefDuplicateIndex(refSnapshot, refId, role, name) {
|
|
17
|
+
if (!refSnapshot?.refs)
|
|
18
|
+
return undefined;
|
|
19
|
+
const normalizedRole = role.toLowerCase();
|
|
20
|
+
const normalizedName = normalizeAccessibleName(name);
|
|
21
|
+
const matchingRefIds = refSnapshot.refIds.filter((candidateRefId) => {
|
|
22
|
+
const candidate = refSnapshot.refs?.[candidateRefId];
|
|
23
|
+
return candidate?.role.toLowerCase() === normalizedRole && normalizeAccessibleName(candidate.name) === normalizedName;
|
|
24
|
+
});
|
|
25
|
+
if (matchingRefIds.length <= 1)
|
|
26
|
+
return undefined;
|
|
27
|
+
const duplicateIndex = matchingRefIds.indexOf(refId);
|
|
28
|
+
return duplicateIndex >= 0 ? duplicateIndex : undefined;
|
|
29
|
+
}
|
|
30
|
+
function getClickDispatchProbeTarget(commandTokens, refSnapshot) {
|
|
31
|
+
if (commandTokens[0] !== "click" || commandTokens.includes("--new-tab"))
|
|
32
|
+
return undefined;
|
|
33
|
+
const selector = commandTokens[1];
|
|
34
|
+
if (!selector || selector.startsWith("-"))
|
|
35
|
+
return undefined;
|
|
36
|
+
const refId = parseClickRefId(selector);
|
|
37
|
+
if (refId) {
|
|
38
|
+
const ref = refSnapshot?.refs?.[refId];
|
|
39
|
+
if (!ref || !ACCESSIBLE_REF_CLICK_DISPATCH_ROLES.has(ref.role))
|
|
40
|
+
return undefined;
|
|
41
|
+
const duplicateIndex = getAccessibleRefDuplicateIndex(refSnapshot, refId, ref.role, ref.name);
|
|
42
|
+
return { ...(duplicateIndex === undefined ? {} : { duplicateIndex }), kind: "accessible", name: ref.name, refId, role: ref.role };
|
|
43
|
+
}
|
|
44
|
+
if (selector.startsWith("xpath="))
|
|
45
|
+
return { kind: "xpath", selector: selector.slice("xpath=".length) };
|
|
46
|
+
return { kind: "selector", selector };
|
|
47
|
+
}
|
|
48
|
+
function getEvalResultRecord(data) {
|
|
49
|
+
return isRecord(data) && isRecord(data.result) ? data.result : undefined;
|
|
50
|
+
}
|
|
51
|
+
function buildClickDispatchProbeInstallScript(probe) {
|
|
52
|
+
const target = probe.target;
|
|
53
|
+
const resolveTarget = target.kind === "selector"
|
|
54
|
+
? `(() => { try { return document.querySelector(${JSON.stringify(target.selector)}); } catch { return null; } })()`
|
|
55
|
+
: target.kind === "xpath"
|
|
56
|
+
? `(() => { try { return document.evaluate(${JSON.stringify(target.selector)}, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; } catch { return null; } })()`
|
|
57
|
+
: `(() => {
|
|
58
|
+
const normalize = (value) => String(value ?? "").replace(/\\s+/g, " ").trim();
|
|
59
|
+
const expectedRole = ${JSON.stringify(target.role)};
|
|
60
|
+
const expectedName = normalize(${JSON.stringify(target.name)});
|
|
61
|
+
const duplicateIndex = ${JSON.stringify(target.duplicateIndex)};
|
|
62
|
+
const inferRole = (element) => {
|
|
63
|
+
const explicit = element.getAttribute("role");
|
|
64
|
+
if (explicit) return explicit;
|
|
65
|
+
const tagName = element.tagName.toLowerCase();
|
|
66
|
+
if (tagName === "button" || tagName === "select" || tagName === "textarea") return tagName;
|
|
67
|
+
if (tagName === "a" && element.hasAttribute("href")) return "link";
|
|
68
|
+
if (tagName === "input") {
|
|
69
|
+
const type = (element.getAttribute("type") || "text").toLowerCase();
|
|
70
|
+
if (["button", "submit", "reset", "image"].includes(type)) return "button";
|
|
71
|
+
if (type === "checkbox") return "checkbox";
|
|
72
|
+
if (type === "radio") return "radio";
|
|
73
|
+
return "textbox";
|
|
74
|
+
}
|
|
75
|
+
return "";
|
|
76
|
+
};
|
|
77
|
+
const inferName = (element) => normalize(element.getAttribute("aria-label") || element.getAttribute("title") || element.value || element.textContent || "");
|
|
78
|
+
const isVisible = (element) => {
|
|
79
|
+
const style = window.getComputedStyle(element);
|
|
80
|
+
if (style.display === "none" || style.visibility === "hidden" || Number(style.opacity) === 0) return false;
|
|
81
|
+
return element.getClientRects().length > 0;
|
|
82
|
+
};
|
|
83
|
+
const candidates = Array.from(document.querySelectorAll("button,a[href],input,select,textarea,summary,[role],[onclick],[tabindex]")).filter((element) => inferRole(element) === expectedRole && inferName(element) === expectedName && isVisible(element));
|
|
84
|
+
if (typeof duplicateIndex === "number") return candidates[duplicateIndex] || null;
|
|
85
|
+
return candidates.length === 1 ? candidates[0] : null;
|
|
86
|
+
})()`;
|
|
87
|
+
return `(() => {
|
|
88
|
+
const marker = ${JSON.stringify(probe.marker)};
|
|
89
|
+
const element = ${resolveTarget};
|
|
90
|
+
if (!element) return { status: "target-not-found", marker };
|
|
91
|
+
const cssEscape = (value) => {
|
|
92
|
+
if (window.CSS && typeof window.CSS.escape === "function") return window.CSS.escape(value);
|
|
93
|
+
return String(value).replace(/[^a-zA-Z0-9_-]/g, "\\$&");
|
|
94
|
+
};
|
|
95
|
+
const getSelector = (node) => {
|
|
96
|
+
if (!(node instanceof Element)) return undefined;
|
|
97
|
+
if (node.id) return "#" + cssEscape(node.id);
|
|
98
|
+
const testId = node.getAttribute("data-testid") || node.getAttribute("data-test-id");
|
|
99
|
+
if (testId) return '[data-testid="' + cssEscape(testId) + '"]';
|
|
100
|
+
const parts = [];
|
|
101
|
+
let current = node;
|
|
102
|
+
while (current && current !== document.body && parts.length < 4) {
|
|
103
|
+
const tag = current.tagName.toLowerCase();
|
|
104
|
+
const parent = current.parentElement;
|
|
105
|
+
if (!parent) break;
|
|
106
|
+
const siblings = Array.from(parent.children).filter((child) => child.tagName === current.tagName);
|
|
107
|
+
const index = siblings.indexOf(current) + 1;
|
|
108
|
+
parts.unshift(siblings.length > 1 ? tag + ':nth-of-type(' + index + ')' : tag);
|
|
109
|
+
current = parent;
|
|
110
|
+
}
|
|
111
|
+
return parts.length > 0 ? parts.join(" > ") : undefined;
|
|
112
|
+
};
|
|
113
|
+
const rectInfo = (rect) => ({ bottom: rect.bottom, left: rect.left, right: rect.right, top: rect.top });
|
|
114
|
+
const targetRect = element ? element.getBoundingClientRect() : undefined;
|
|
115
|
+
const targetOutsideViewport = targetRect ? targetRect.bottom < 0 || targetRect.right < 0 || targetRect.top > window.innerHeight || targetRect.left > window.innerWidth : undefined;
|
|
116
|
+
let nearestScrollContainer;
|
|
117
|
+
if (element && targetRect) {
|
|
118
|
+
for (let current = element.parentElement; current && current !== document.body; current = current.parentElement) {
|
|
119
|
+
if (current.scrollHeight > current.clientHeight + 1 || current.scrollWidth > current.clientWidth + 1) {
|
|
120
|
+
const containerRect = current.getBoundingClientRect();
|
|
121
|
+
nearestScrollContainer = {
|
|
122
|
+
selector: getSelector(current),
|
|
123
|
+
tagName: current.tagName.toLowerCase(),
|
|
124
|
+
targetOutsideContainer: targetRect.bottom < containerRect.top || targetRect.top > containerRect.bottom || targetRect.right < containerRect.left || targetRect.left > containerRect.right,
|
|
125
|
+
targetOutsideViewport,
|
|
126
|
+
rect: rectInfo(containerRect),
|
|
127
|
+
scrollLeft: current.scrollLeft,
|
|
128
|
+
scrollTop: current.scrollTop,
|
|
129
|
+
};
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const state = { events: [], target: { tagName: element.tagName.toLowerCase(), nearestScrollContainer, rect: rectInfo(targetRect), targetOutsideViewport } };
|
|
135
|
+
const eventTypes = ["pointerdown", "mousedown", "pointerup", "mouseup", "click"];
|
|
136
|
+
const listeners = eventTypes.map((type) => {
|
|
137
|
+
const listener = (event) => {
|
|
138
|
+
const path = typeof event.composedPath === "function" ? event.composedPath() : [];
|
|
139
|
+
const eventTarget = event.target;
|
|
140
|
+
const targetMatched = path.includes(element) || eventTarget === element || (eventTarget instanceof Node && element.contains(eventTarget));
|
|
141
|
+
state.events.push({ type: event.type, isTrusted: event.isTrusted === true, targetMatched });
|
|
142
|
+
};
|
|
143
|
+
document.addEventListener(type, listener, true);
|
|
144
|
+
return [type, listener];
|
|
145
|
+
});
|
|
146
|
+
state.cleanup = () => listeners.forEach(([type, listener]) => document.removeEventListener(type, listener, true));
|
|
147
|
+
window[marker] = state;
|
|
148
|
+
return { status: "installed", marker, target: state.target };
|
|
149
|
+
})()`;
|
|
150
|
+
}
|
|
151
|
+
function buildClickDispatchProbeCheckScript(probe) {
|
|
152
|
+
return `(() => {
|
|
153
|
+
const marker = ${JSON.stringify(probe.marker)};
|
|
154
|
+
const state = window[marker];
|
|
155
|
+
const finish = (payload) => {
|
|
156
|
+
if (state && typeof state.cleanup === "function") state.cleanup();
|
|
157
|
+
try { delete window[marker]; } catch {}
|
|
158
|
+
return payload;
|
|
159
|
+
};
|
|
160
|
+
if (!state || !Array.isArray(state.events)) return finish({ status: "probe-missing", nativeEventCount: 0 });
|
|
161
|
+
const nativeEventCount = state.events.filter((event) => event && event.isTrusted === true && event.targetMatched === true).length;
|
|
162
|
+
if (nativeEventCount > 0) return finish({ status: "native-event-observed", nativeEventCount, target: state.target });
|
|
163
|
+
return finish({ status: "no-native-event-observed", nativeEventCount, target: state.target });
|
|
164
|
+
})()`;
|
|
165
|
+
}
|
|
166
|
+
function buildClickDispatchProbeCleanupScript(probe) {
|
|
167
|
+
return `(() => {
|
|
168
|
+
const marker = ${JSON.stringify(probe.marker)};
|
|
169
|
+
const state = window[marker];
|
|
170
|
+
if (state && typeof state.cleanup === "function") state.cleanup();
|
|
171
|
+
try { delete window[marker]; } catch {}
|
|
172
|
+
return { status: "cleaned-up" };
|
|
173
|
+
})()`;
|
|
174
|
+
}
|
|
175
|
+
function redactClickDispatchTarget(target) {
|
|
176
|
+
if (target.kind === "selector" || target.kind === "xpath") {
|
|
177
|
+
return { ...target, selector: redactSensitiveText(target.selector) };
|
|
178
|
+
}
|
|
179
|
+
return { ...target, name: redactSensitiveText(target.name) };
|
|
180
|
+
}
|
|
181
|
+
export function formatClickDispatchDiagnosticText(diagnostic) {
|
|
182
|
+
return `Click dispatch diagnostic: ${diagnostic.summary}`;
|
|
183
|
+
}
|
|
184
|
+
export function buildClickDispatchNextActions(options) {
|
|
185
|
+
const retryArgs = options.commandTokens[0] === "click" || options.commandTokens[0] === "find" ? options.commandTokens : ["click", ...options.commandTokens];
|
|
186
|
+
const actions = [
|
|
187
|
+
{
|
|
188
|
+
id: "inspect-click-dispatch-miss",
|
|
189
|
+
params: { args: withOptionalSessionArgs(options.sessionName, ["snapshot", "-i"]) },
|
|
190
|
+
reason: "Refresh interactive refs and verify the intended click target before retrying upstream click.",
|
|
191
|
+
safety: "Read-only snapshot; the wrapper does not replay clicks in-page when upstream reports success without DOM events.",
|
|
192
|
+
tool: "agent_browser",
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
if (options.diagnostic?.scrollContainer) {
|
|
196
|
+
actions.push({
|
|
197
|
+
id: "scroll-target-into-view-after-dispatch-miss",
|
|
198
|
+
params: { args: withOptionalSessionArgs(options.sessionName, ["scrollintoview", retryArgs[1]].filter((item) => typeof item === "string")) },
|
|
199
|
+
reason: options.diagnostic.scrollContainer.selector
|
|
200
|
+
? `The target may be outside nested scroll container ${options.diagnostic.scrollContainer.selector}; scroll the target into view before retrying the click.`
|
|
201
|
+
: "The target may be inside an offscreen nested scroll container; scroll the target into view before retrying the click.",
|
|
202
|
+
safety: "Use only for the same current page and target; run snapshot -i again if the page rerendered.",
|
|
203
|
+
tool: "agent_browser",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
actions.push({
|
|
207
|
+
id: "retry-click-after-dispatch-miss",
|
|
208
|
+
params: { args: withOptionalSessionArgs(options.sessionName, retryArgs) },
|
|
209
|
+
reason: "Retry the same upstream click after confirming the target is visible; do not assume the prior success mutated the page.",
|
|
210
|
+
safety: "Only retry when the target is still intended; use page-change evidence or a fresh snapshot before continuing the workflow.",
|
|
211
|
+
tool: "agent_browser",
|
|
212
|
+
});
|
|
213
|
+
return actions;
|
|
214
|
+
}
|
|
215
|
+
export async function prepareClickDispatchProbe(options) {
|
|
216
|
+
if (!options.sessionName || options.commandTokens[0] !== "click" || options.commandTokens.includes("--new-tab"))
|
|
217
|
+
return undefined;
|
|
218
|
+
const target = getClickDispatchProbeTarget(options.commandTokens, options.refSnapshot);
|
|
219
|
+
if (!target)
|
|
220
|
+
return undefined;
|
|
221
|
+
const probe = { marker: `${CLICK_DISPATCH_MARKER_PREFIX}${Date.now().toString(36)}_${Math.random().toString(36).slice(2)}`, target };
|
|
222
|
+
const installData = await runSessionCommandData({ args: ["eval", "--stdin"], cwd: options.cwd, sessionName: options.sessionName, signal: options.signal, stdin: buildClickDispatchProbeInstallScript(probe) });
|
|
223
|
+
const installResult = getEvalResultRecord(installData);
|
|
224
|
+
return installResult?.status === "installed" ? probe : undefined;
|
|
225
|
+
}
|
|
226
|
+
function getClickDispatchScrollContainerDiagnostic(result) {
|
|
227
|
+
const target = isRecord(result.target) ? result.target : undefined;
|
|
228
|
+
const scrollContainer = isRecord(target?.nearestScrollContainer) ? target.nearestScrollContainer : undefined;
|
|
229
|
+
const targetOutsideViewport = typeof target?.targetOutsideViewport === "boolean" ? target.targetOutsideViewport : undefined;
|
|
230
|
+
const targetOutsideContainer = typeof scrollContainer?.targetOutsideContainer === "boolean" ? scrollContainer.targetOutsideContainer : undefined;
|
|
231
|
+
if (!scrollContainer && !targetOutsideViewport)
|
|
232
|
+
return undefined;
|
|
233
|
+
if (targetOutsideContainer !== true && targetOutsideViewport !== true)
|
|
234
|
+
return undefined;
|
|
235
|
+
const selector = typeof scrollContainer?.selector === "string" ? redactSensitiveText(scrollContainer.selector) : undefined;
|
|
236
|
+
const summary = selector
|
|
237
|
+
? `Target appears outside nested scroll container ${selector}; use scrollintoview on the target or scroll that container before retrying.`
|
|
238
|
+
: "Target appears outside the viewport or a nested scroll container; use scrollintoview on the target before retrying.";
|
|
239
|
+
return { selector, summary, targetOutsideContainer, targetOutsideViewport };
|
|
240
|
+
}
|
|
241
|
+
export async function collectClickDispatchDiagnostic(options) {
|
|
242
|
+
if (!options.probe || !options.sessionName)
|
|
243
|
+
return undefined;
|
|
244
|
+
const data = await runSessionCommandData({ args: ["eval", "--stdin"], cwd: options.cwd, sessionName: options.sessionName, signal: options.signal, stdin: buildClickDispatchProbeCheckScript(options.probe) });
|
|
245
|
+
const result = getEvalResultRecord(data);
|
|
246
|
+
if (!result)
|
|
247
|
+
return undefined;
|
|
248
|
+
const status = typeof result.status === "string" ? result.status : undefined;
|
|
249
|
+
if (status !== "no-native-event-observed")
|
|
250
|
+
return undefined;
|
|
251
|
+
const nativeEventCount = typeof result.nativeEventCount === "number" ? result.nativeEventCount : 0;
|
|
252
|
+
const scrollContainer = getClickDispatchScrollContainerDiagnostic(result);
|
|
253
|
+
const targetLabel = "no trusted DOM event reached the selected element";
|
|
254
|
+
const summary = scrollContainer
|
|
255
|
+
? `Upstream click reported success but ${targetLabel}. ${scrollContainer.summary}`
|
|
256
|
+
: `Upstream click reported success but ${targetLabel}. Gather evidence with snapshot or page-change checks, then retry upstream click or report the workflow issue; the wrapper does not replay clicks in-page.`;
|
|
257
|
+
return {
|
|
258
|
+
nativeEventCount,
|
|
259
|
+
reason: "native-click-produced-no-target-dom-event",
|
|
260
|
+
...(scrollContainer ? { scrollContainer } : {}),
|
|
261
|
+
status,
|
|
262
|
+
summary,
|
|
263
|
+
target: redactClickDispatchTarget(options.probe.target),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
export async function cleanupClickDispatchProbe(options) {
|
|
267
|
+
if (!options.probe || !options.sessionName)
|
|
268
|
+
return;
|
|
269
|
+
await runSessionCommandData({
|
|
270
|
+
args: ["eval", "--stdin"],
|
|
271
|
+
cwd: options.cwd,
|
|
272
|
+
sessionName: options.sessionName,
|
|
273
|
+
stdin: buildClickDispatchProbeCleanupScript(options.probe),
|
|
274
|
+
timeoutMs: CLICK_DISPATCH_CLEANUP_TIMEOUT_MS,
|
|
275
|
+
}).catch(() => undefined);
|
|
276
|
+
}
|