diff --git a/CHANGELOG.md b/CHANGELOG.md index 0597b527180..d17fd744456 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -30,7 +30,7 @@ Docs: https://docs.openclaw.ai ### Fixes -- Browser/tool: tell agents not to pass per-call `timeoutMs` on existing-session evaluate and other Chrome MCP actions that reject timeout overrides. +- Browser/tool: tell agents not to pass per-call `timeoutMs` on existing-session type, evaluate, and other Chrome MCP actions that reject timeout overrides. - Voice-call/Telnyx: preserve inbound/outbound callback metadata and read transcription text from Telnyx's current `transcription_data` payload. - Codex harness: send verbose tool progress to chat channels for native app-server runs, matching the Pi harness `/verbose on` and `/verbose full` behavior. (#70966) Thanks @jalehman. - Codex models: fetch paginated Codex app-server model catalogs, mark truncated `/codex models` output, and keep ChatGPT OAuth defaults on the `openai-codex/gpt-5.5` route instead of the OpenAI API-key route. diff --git a/docs/gateway/troubleshooting.md b/docs/gateway/troubleshooting.md index c946715be0a..bc4132a3f5b 100644 --- a/docs/gateway/troubleshooting.md +++ b/docs/gateway/troubleshooting.md @@ -482,6 +482,7 @@ Common signatures: - `existing-session file uploads do not support element selectors; use ref/inputRef.` → Chrome MCP upload hooks need snapshot refs, not CSS selectors. - `existing-session file uploads currently support one file at a time.` → send one upload per call on Chrome MCP profiles. - `existing-session dialog handling does not support timeoutMs.` → dialog hooks on Chrome MCP profiles do not support timeout overrides. +- `existing-session type does not support timeoutMs overrides.` → omit `timeoutMs` for `act:type` on `profile="user"` / Chrome MCP existing-session profiles, or use a managed/CDP browser profile when a custom timeout is required. - `existing-session evaluate does not support timeoutMs overrides.` → omit `timeoutMs` for `act:evaluate` on `profile="user"` / Chrome MCP existing-session profiles, or use a managed/CDP browser profile when a custom timeout is required. - `response body is not supported for existing-session profiles yet.` → `responsebody` still requires a managed browser or raw CDP profile. - stale viewport / dark-mode / locale / offline overrides on attach-only or remote CDP profiles → run `openclaw browser stop --browser-profile ` to close the active control session and release Playwright/CDP emulation state without restarting the whole gateway. diff --git a/docs/tools/browser.md b/docs/tools/browser.md index 7cbe7dc5a7c..29b737d5160 100644 --- a/docs/tools/browser.md +++ b/docs/tools/browser.md @@ -484,7 +484,7 @@ Notes: Compared to the managed `openclaw` profile, existing-session drivers are more constrained: - **Screenshots** — page captures and `--ref` element captures work; CSS `--element` selectors do not. `--full-page` cannot combine with `--ref` or `--element`. Playwright is not required for page or ref-based element screenshots. -- **Actions** — `click`, `type`, `hover`, `scrollIntoView`, `drag`, and `select` require snapshot refs (no CSS selectors). `click` is left-button only. `type` does not support `slowly=true`; use `fill` or `press`. `press` does not support `delayMs`. `hover`, `scrollIntoView`, `drag`, `select`, `fill`, and `evaluate` do not support per-call timeouts. `select` accepts a single value. +- **Actions** — `click`, `type`, `hover`, `scrollIntoView`, `drag`, and `select` require snapshot refs (no CSS selectors). `click` is left-button only. `type` does not support `slowly=true`; use `fill` or `press`. `press` does not support `delayMs`. `type`, `hover`, `scrollIntoView`, `drag`, `select`, `fill`, and `evaluate` do not support per-call timeouts. `select` accepts a single value. - **Wait / upload / dialog** — `wait --url` supports exact, substring, and glob patterns; `wait --load networkidle` is not supported. Upload hooks require `ref` or `inputRef`, one file at a time, no CSS `element`. Dialog hooks do not support timeout overrides. - **Managed-only features** — batch actions, PDF export, download interception, and `responsebody` still require the managed browser path. diff --git a/extensions/browser/src/browser-tool.test.ts b/extensions/browser/src/browser-tool.test.ts index b33eae01974..a1d123eb068 100644 --- a/extensions/browser/src/browser-tool.test.ts +++ b/extensions/browser/src/browser-tool.test.ts @@ -304,7 +304,7 @@ describe("browser tool description", () => { const tool = createBrowserTool(); expect(tool.description).toContain('profile="user"'); - expect(tool.description).toContain("omit timeoutMs on act:evaluate"); + expect(tool.description).toContain("omit timeoutMs on act:type"); expect(tool.description).toContain("existing-session profiles"); }); }); diff --git a/extensions/browser/src/browser-tool.ts b/extensions/browser/src/browser-tool.ts index b0792674b9c..340817f1d89 100644 --- a/extensions/browser/src/browser-tool.ts +++ b/extensions/browser/src/browser-tool.ts @@ -382,7 +382,7 @@ export function createBrowserTool(opts?: { "Control the browser via OpenClaw's browser control server (status/start/stop/profiles/tabs/open/snapshot/screenshot/actions).", "Browser choice: omit profile by default for the isolated OpenClaw-managed browser (`openclaw`).", 'For the logged-in user browser, use profile="user". A supported Chromium-based browser (v144+) must be running on the selected host or browser node. Use only when existing logins/cookies matter and the user is present.', - 'For profile="user" or other existing-session profiles, omit timeoutMs on act:evaluate, hover, scrollIntoView, drag, select, and fill; that driver rejects per-call timeout overrides for those actions.', + 'For profile="user" or other existing-session profiles, omit timeoutMs on act:type, evaluate, hover, scrollIntoView, drag, select, and fill; that driver rejects per-call timeout overrides for those actions.', 'When a node-hosted browser proxy is available, the tool may auto-route to it. Pin a node with node= or target="node".', "When using refs from snapshot (e.g. e12), keep the same tab: prefer passing targetId from the snapshot response into subsequent actions (act/click/type/etc).", 'For stable, self-resolving refs across calls, use snapshot with refs="aria" (Playwright aria-ref ids). Default refs="role" are role+name-based.', diff --git a/extensions/browser/src/browser/routes/agent.act.ts b/extensions/browser/src/browser/routes/agent.act.ts index d07f1657563..63356a1afa3 100644 --- a/extensions/browser/src/browser/routes/agent.act.ts +++ b/extensions/browser/src/browser/routes/agent.act.ts @@ -286,7 +286,7 @@ function getExistingSessionUnsupportedMessage(action: BrowserActRequest): string if (action.slowly) { return EXISTING_SESSION_LIMITS.act.typeSlowly; } - return null; + return action.timeoutMs ? EXISTING_SESSION_LIMITS.act.typeTimeout : null; case "press": return action.delayMs ? EXISTING_SESSION_LIMITS.act.pressDelay : null; case "hover": diff --git a/extensions/browser/src/browser/routes/agent.existing-session.test.ts b/extensions/browser/src/browser/routes/agent.existing-session.test.ts index ce263d229a1..3aec2196244 100644 --- a/extensions/browser/src/browser/routes/agent.existing-session.test.ts +++ b/extensions/browser/src/browser/routes/agent.existing-session.test.ts @@ -12,6 +12,7 @@ const chromeMcpMocks = vi.hoisted(() => ({ evaluateChromeMcpScript: vi.fn( async (_params: { profileName: string; targetId: string; fn: string }) => true, ), + fillChromeMcpElement: vi.fn(async () => {}), navigateChromeMcpPage: vi.fn(async ({ url }: { url: string }) => ({ url })), takeChromeMcpScreenshot: vi.fn(async () => Buffer.from("png")), takeChromeMcpSnapshot: vi.fn(async () => ({ @@ -33,7 +34,7 @@ vi.mock("../chrome-mcp.js", () => ({ closeChromeMcpTab: vi.fn(async () => {}), dragChromeMcpElement: vi.fn(async () => {}), evaluateChromeMcpScript: chromeMcpMocks.evaluateChromeMcpScript, - fillChromeMcpElement: vi.fn(async () => {}), + fillChromeMcpElement: chromeMcpMocks.fillChromeMcpElement, fillChromeMcpForm: vi.fn(async () => {}), hoverChromeMcpElement: vi.fn(async () => {}), navigateChromeMcpPage: chromeMcpMocks.navigateChromeMcpPage, @@ -109,6 +110,7 @@ describe("existing-session browser routes", () => { routeState.profileCtx.listTabs.mockClear(); chromeMcpMocks.clickChromeMcpElement.mockClear(); chromeMcpMocks.evaluateChromeMcpScript.mockReset(); + chromeMcpMocks.fillChromeMcpElement.mockClear(); chromeMcpMocks.navigateChromeMcpPage.mockClear(); chromeMcpMocks.takeChromeMcpScreenshot.mockClear(); chromeMcpMocks.takeChromeMcpSnapshot.mockClear(); @@ -238,6 +240,25 @@ describe("existing-session browser routes", () => { expect(chromeMcpMocks.evaluateChromeMcpScript).not.toHaveBeenCalled(); }); + it("fails closed for existing-session type timeout overrides", async () => { + const handler = getActPostHandler(); + const response = createBrowserRouteResponse(); + await handler?.( + { + params: {}, + query: {}, + body: { kind: "type", ref: "input-1", text: "hello", timeoutMs: 1234 }, + }, + response.res, + ); + + expect(response.statusCode).toBe(501); + expect(response.body).toMatchObject({ + error: expect.stringContaining("type does not support timeoutMs"), + }); + expect(chromeMcpMocks.fillChromeMcpElement).not.toHaveBeenCalled(); + }); + it("supports glob URL waits for existing-session profiles", async () => { chromeMcpMocks.evaluateChromeMcpScript.mockReset(); chromeMcpMocks.evaluateChromeMcpScript.mockImplementation( diff --git a/extensions/browser/src/browser/routes/existing-session-limits.ts b/extensions/browser/src/browser/routes/existing-session-limits.ts index 47f4e0b8a81..45c4f1e4b8c 100644 --- a/extensions/browser/src/browser/routes/existing-session-limits.ts +++ b/extensions/browser/src/browser/routes/existing-session-limits.ts @@ -5,6 +5,7 @@ export const EXISTING_SESSION_LIMITS = { "existing-session click currently supports left-click only (no button overrides/modifiers).", typeSelector: "existing-session type does not support selector targeting yet; use ref.", typeSlowly: "existing-session type does not support slowly=true; use fill/press instead.", + typeTimeout: "existing-session type does not support timeoutMs overrides.", pressDelay: "existing-session press does not support delayMs.", hoverSelector: "existing-session hover does not support selector targeting yet; use ref.", hoverTimeout: "existing-session hover does not support timeoutMs overrides.",