feat(browser): add evaluate timeout CLI option (#83696)

Summary:
- The branch adds `openclaw browser evaluate --timeout-ms`, forwards it to the evaluate body and request timeo ... ents and tests it, adds a changelog entry, and includes a config.patch no-op shortcut from the repair pass.
- Reproducibility: not applicable. this is a feature PR rather than a bug report. Source inspection shows current main lacks the CLI flag while the branch wires it into an already-supported evaluate `timeoutMs` payload.

Automerge notes:
- PR branch already contained follow-up commit before automerge: feat(browser): add evaluate timeout CLI option

Validation:
- ClawSweeper review passed for head 0d81d3d93e.
- Required merge gates passed before the squash merge.

Prepared head SHA: 0d81d3d93e
Review: https://github.com/openclaw/openclaw/pull/83696#issuecomment-4479900502

Co-authored-by: fred <fengruifree@gmail.com>
Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com>
Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com>
Approved-by: takhoffman
Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
This commit is contained in:
clawsweeper[bot]
2026-05-18 17:30:33 +00:00
committed by GitHub
parent a4f80f905d
commit fa814eb9ed
6 changed files with 59 additions and 1 deletions
+1
View File
@@ -18,6 +18,7 @@ Docs: https://docs.openclaw.ai
- Skills: add a meme-maker skill for curated template search, local SVG/PNG rendering, Imgflip hosted rendering, and Know Your Meme provenance links.
- Skills CLI: allow `openclaw skills install` and `openclaw skills update` to target shared managed skills with `--global`. (#74466) Thanks @Marvae.
- Browser: surface pending and recently handled modal dialogs in snapshots, return `blockedByDialog` when an action opens a modal, and allow `browser dialog --dialog-id` to answer pending dialogs.
- Browser CLI: add `openclaw browser evaluate --timeout-ms` so long-running page functions can extend both the evaluate action and request timeout budgets. (#83447) Thanks @eefreenyc.
- Codex app-server: scope OpenClaw prompt guidance by runtime surface so native Codex keeps Codex-owned base/personality instructions while OpenClaw contributes only runtime context, delivery guidance, and explicitly scoped command hints. (#83454) Thanks @100yenadmin.
- Agents/tools: shorten built-in tool descriptions and schema hints across media, messaging, sessions, cron, Gateway, web, image/PDF, TTS, nodes, and plan tools while preserving routing guardrails.
- Skills: add node inspector debugging, fused diagram generation, and throwaway spike workflow skills.
+4
View File
@@ -191,8 +191,12 @@ openclaw browser select <ref> OptionA OptionB
openclaw browser fill --fields '[{"ref":"1","value":"Ada"}]'
openclaw browser wait --text "Done"
openclaw browser evaluate --fn '(el) => el.textContent' --ref <ref>
openclaw browser evaluate --timeout-ms 30000 --fn 'async () => { await window.ready; return true; }'
```
Use `evaluate --timeout-ms <ms>` when the page-side function may need longer
than the default evaluate timeout.
Action responses return the current raw `targetId` after action-triggered page
replacement when OpenClaw can prove the replacement tab. Scripts should still
store and pass `suggestedTargetId`/labels for long-lived workflows.
+3
View File
@@ -197,6 +197,7 @@ openclaw browser dialog --dismiss --dialog-id d1
openclaw browser wait --text "Done"
openclaw browser wait "#main" --url "**/dash" --load networkidle --fn "window.ready===true"
openclaw browser evaluate --fn '(el) => el.textContent' --ref 7
openclaw browser evaluate --timeout-ms 30000 --fn 'async () => { await window.ready; return true; }'
openclaw browser highlight e12
openclaw browser trace start
openclaw browser trace stop
@@ -362,6 +363,8 @@ These are useful for "make the site behave like X" workflows:
- `browser act kind=evaluate` / `openclaw browser evaluate` and `wait --fn`
execute arbitrary JavaScript in the page context. Prompt injection can steer
this. Disable it with `browser.evaluateEnabled=false` if you do not need it.
- Use `openclaw browser evaluate --timeout-ms <ms>` when the page-side function
may need longer than the default evaluate timeout.
- For logins and anti-bot notes (X/Twitter, etc.), see [Browser login + X/Twitter posting](/tools/browser-login).
- Keep the Gateway/node host private (loopback or tailnet-only).
- Remote CDP endpoints are powerful; tunnel and protect them.
@@ -65,3 +65,28 @@ describe("browser action input wait command", () => {
expect(options?.timeoutMs).toBeGreaterThan(21000);
});
});
describe("browser action input evaluate command", () => {
beforeEach(() => {
mocks.callBrowserRequest.mockClear();
getBrowserCliRuntimeCapture().resetRuntimeCapture();
});
it("passes timeout-ms through to the evaluate action and outer request", async () => {
const program = createActionInputProgram();
await program.parseAsync(
["browser", "evaluate", "--fn", "() => true", "--timeout-ms", "30000"],
{ from: "user" },
);
const request = mocks.callBrowserRequest.mock.calls.at(-1)?.[1] as
| { body?: { timeoutMs?: number } }
| undefined;
const options = mocks.callBrowserRequest.mock.calls.at(-1)?.[2] as
| { timeoutMs?: number }
| undefined;
expect(request?.body?.timeoutMs).toBe(30000);
expect(options?.timeoutMs).toBeGreaterThan(30000);
});
});
@@ -107,6 +107,11 @@ export function registerBrowserFormWaitEvalCommands(
.description("Evaluate a function against the page or a ref")
.option("--fn <code>", "Function source, e.g. (el) => el.textContent")
.option("--ref <id>", "Ref from snapshot")
.option(
"--timeout-ms <ms>",
"How long to allow the evaluate function to run (default: 20000)",
(v: string) => Number(v),
)
.option("--target-id <id>", "CDP target id (or unique prefix)")
.action(async (opts, cmd) => {
const { parent, profile } = resolveBrowserActionContext(cmd, parentOpts);
@@ -116,6 +121,7 @@ export function registerBrowserFormWaitEvalCommands(
return;
}
try {
const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : undefined;
const result = await callBrowserAct<{ result?: unknown }>({
parent,
profile,
@@ -124,7 +130,9 @@ export function registerBrowserFormWaitEvalCommands(
fn: opts.fn,
ref: normalizeOptionalString(opts.ref),
targetId: normalizeOptionalString(opts.targetId),
timeoutMs,
},
timeoutMs,
});
if (parent?.json) {
defaultRuntime.writeJson(result);
+18 -1
View File
@@ -433,6 +433,24 @@ export const configHandlers: GatewayRequestHandlers = {
);
return;
}
const restoredChangedPaths = diffConfigPaths(snapshot.config, restoredMerge.result);
const actor = resolveControlPlaneActor(client);
if (restoredChangedPaths.length === 0) {
context?.logGateway?.info(
`config.patch noop ${formatControlPlaneActor(actor)} (no changed paths)`,
);
respond(
true,
{
ok: true,
noop: true,
path: resolveGatewayConfigPath(snapshot),
config: redactConfigObject(snapshot.config, schemaPatch.uiHints),
},
undefined,
);
return;
}
const validated = validateConfigObjectWithPlugins(restoredMerge.result);
if (!validated.ok) {
respond(
@@ -452,7 +470,6 @@ export const configHandlers: GatewayRequestHandlers = {
return;
}
const changedPaths = diffConfigPaths(snapshot.config, validated.config);
const actor = resolveControlPlaneActor(client);
// No-op: if the validated config is identical to the current config,
// skip the file write and SIGUSR1 restart entirely. This avoids a full