aboutsummaryrefslogtreecommitdiff
path: root/src/domains
diff options
context:
space:
mode:
authoramankumarsingh77 <amankumarsingh7702@gmail.com>2026-07-03 09:36:47 +0530
committeramankumarsingh77 <amankumarsingh7702@gmail.com>2026-07-03 09:37:10 +0530
commit28e6390a2a7bdb5db2e70c7c40d8eb884ddedf67 (patch)
treea1d939f33ef83039f5d87d182c0401b9566daf7c /src/domains
parent0b32e88f84f36a759d7191e8bdfd09a44789fa95 (diff)
refactor: extract event queue from transport into dedicated module
Diffstat (limited to 'src/domains')
-rw-r--r--src/domains/page.ts45
1 files changed, 45 insertions, 0 deletions
diff --git a/src/domains/page.ts b/src/domains/page.ts
index 25b3c67..8e674cb 100644
--- a/src/domains/page.ts
+++ b/src/domains/page.ts
@@ -86,3 +86,48 @@ export const waitForLoadTool = defineBrowserTool({
return err({ kind: "timeout", message: `Page did not finish loading in ${args.timeout ?? 15}s` });
},
});
+
+const WaitForArgs = Type.Object({
+ selector: Type.Optional(Type.String({ description: "CSS selector to wait for (or wait to disappear with gone:true)" })),
+ text: Type.Optional(Type.String({ description: "Wait until this text appears anywhere in document.body" })),
+ gone: Type.Optional(Type.Boolean({ default: false, description: "If true with a selector, wait until it is absent instead of present" })),
+ timeout: Type.Optional(Type.Number({ default: 5, minimum: 1, maximum: 60, description: "Max seconds to wait. Default: 5." })),
+});
+
+export const waitForTool = defineBrowserTool({
+ name: "browser_wait_for",
+ label: "Browser Wait For",
+ description:
+ "Poll until a condition holds: a selector appears (or disappears with gone:true), or text appears in the page. Returns a typed timeout error if the deadline elapses. Use to wait for SPA content to render before interacting.",
+ promptSnippet: "Wait for an element or text to appear (or an element to disappear)",
+ promptGuidelines: [
+ "Use before browser_fill / browser_click on dynamic pages so you act after the element renders.",
+ "Provide selector (default: wait until present; gone:true: wait until absent) or text.",
+ "Returns a typed timeout error if the condition isn't met within the timeout.",
+ ],
+ parameters: WaitForArgs,
+ async handler(args, { client, signal }): Promise<Result<ToolOk, ToolErr>> {
+ if (args.selector === undefined && args.text === undefined) {
+ return err({ kind: "invalid_state", message: "Provide a selector or text to wait for." });
+ }
+ const gone = args.gone ?? false;
+ const timeoutMs = (args.timeout ?? 5) * 1000;
+ const expr =
+ args.selector !== undefined
+ ? safeJs`(() => { const found = !!document.querySelector(${args.selector}); return ${gone} ? !found : found; })()`
+ : safeJs`(() => { const t = document.body ? document.body.innerText : ""; return t.includes(${args.text}); })()`;
+ const start = Date.now();
+ while (Date.now() - start < timeoutMs) {
+ if (signal?.aborted) return err({ kind: "internal", message: "aborted" });
+ const r = await client.evaluateJs(expr);
+ if (r.success && r.data === true) {
+ const ms = Date.now() - start;
+ const what = args.selector !== undefined ? `${args.selector}${gone ? " gone" : ""}` : `text "${args.text}"`;
+ return ok({ text: `Condition met (${what}) in ${Math.round(ms / 100) / 10}s`, details: { ms } });
+ }
+ await sleep(50, signal);
+ }
+ const what = args.selector !== undefined ? `${args.selector}${gone ? " to disappear" : ""}` : `text "${args.text}"`;
+ return err({ kind: "timeout", message: `Timed out after ${args.timeout ?? 5}s waiting for ${what}` });
+ },
+});