diff --git a/extensions/browser/index.test.ts b/extensions/browser/index.test.ts index 4c7be478006..7aa0b415d62 100644 --- a/extensions/browser/index.test.ts +++ b/extensions/browser/index.test.ts @@ -150,6 +150,8 @@ describe("browser plugin", () => { } expect(tool.name).toBe("browser"); + expect(tool.description).toContain("action=profiles"); + expect(tool.description).not.toContain('profile="user"'); expect(runtimeApiMocks.createBrowserTool).not.toHaveBeenCalled(); await tool.execute("call-1", { action: "status" }); expect(runtimeApiMocks.createBrowserTool).toHaveBeenCalledWith({ diff --git a/extensions/browser/plugin-registration.ts b/extensions/browser/plugin-registration.ts index ad25a6c922f..de2504d0d5e 100644 --- a/extensions/browser/plugin-registration.ts +++ b/extensions/browser/plugin-registration.ts @@ -18,6 +18,7 @@ import { BROWSER_REQUEST_GATEWAY_METHOD, BROWSER_REQUEST_GATEWAY_SCOPE, } from "./src/browser-gateway-contract.js"; +import { describeBrowserTool } from "./src/browser-tool-description.js"; import { BrowserToolSchema } from "./src/browser-tool.schema.js"; const EAGER_BROWSER_CONTROL_SERVICE_ENV = "OPENCLAW_EAGER_BROWSER_CONTROL_SERVER"; @@ -74,19 +75,7 @@ function createLazyBrowserTool(opts?: { return { label: "Browser", name: "browser", - description: [ - "Control the browser via OpenClaw's browser control server (status/start/stop/profiles/tabs/open/snapshot/screenshot/download/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: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 tab operations, targetId also accepts tabId handles (t1) and labels from action=tabs.", - "For multi-step browser work, login checks, stale refs, duplicate tabs, or Google Meet flows, use the bundled browser-automation skill when it is available.", - 'For stable, self-resolving refs across calls, use snapshot with refs="aria" (Playwright aria-ref ids). Default refs="role" are role+name-based.', - "Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.", - `target selects browser location (sandbox|host|node). Default: ${targetDefault}.`, - hostHint, - ].join(" "), + description: describeBrowserTool({ targetDefault, hostHint }), parameters: BrowserToolSchema, execute: async (toolCallId, args, signal, onUpdate) => { const { createBrowserTool } = await loadBrowserRegistrationRuntimeModule(); diff --git a/extensions/browser/src/browser-tool-description.ts b/extensions/browser/src/browser-tool-description.ts new file mode 100644 index 00000000000..e38980dc756 --- /dev/null +++ b/extensions/browser/src/browser-tool-description.ts @@ -0,0 +1,20 @@ +/** Build the Browser tool guidance shared by lazy registration and runtime execution. */ +export function describeBrowserTool(opts: { + targetDefault: "sandbox" | "host"; + hostHint: string; +}): string { + return [ + "Control the browser via OpenClaw's browser control server (status/start/stop/profiles/tabs/open/snapshot/screenshot/download/actions).", + "Browser choice: omit profile to use the configured default (normally the isolated OpenClaw-managed `openclaw` browser).", + "When existing logins/cookies matter, use action=profiles to inspect available profiles, then select the appropriate profile by name. Do not assume a profile name. Use only when the task requires an existing session and the user has authorized it.", + "For Chrome MCP 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 tab operations, targetId also accepts tabId handles (t1) and labels from action=tabs.", + "For multi-step browser work, login checks, stale refs, duplicate tabs, or Google Meet flows, use the bundled browser-automation skill when it is available.", + 'For stable, self-resolving refs across calls, use snapshot with refs="aria" (Playwright aria-ref ids). Default refs="role" are role+name-based.', + "Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.", + "For file chooser uploads, pass the trigger ref with paths in the same upload call when available; use paths-only arming only when a later trigger is intentional. Use inputRef or element to set a file input directly.", + `target selects browser location (sandbox|host|node). Default: ${opts.targetDefault}.`, + opts.hostHint, + ].join(" "); +} diff --git a/extensions/browser/src/browser-tool.test.ts b/extensions/browser/src/browser-tool.test.ts index 4a02305f5e1..eadf1d02942 100644 --- a/extensions/browser/src/browser-tool.test.ts +++ b/extensions/browser/src/browser-tool.test.ts @@ -503,7 +503,9 @@ describe("browser tool description", () => { it("warns agents about existing-session act timeout limits", () => { const tool = createBrowserTool(); - expect(tool.description).toContain('profile="user"'); + expect(tool.description).toContain("action=profiles"); + expect(tool.description).toContain("Do not assume a profile name"); + expect(tool.description).not.toContain('profile="user"'); expect(tool.description).toContain("omit timeoutMs on act:type"); expect(tool.description).toContain("existing-session profiles"); expect(tool.description).toContain("browser-automation skill"); diff --git a/extensions/browser/src/browser-tool.ts b/extensions/browser/src/browser-tool.ts index 367929e28b8..2c4fab74799 100644 --- a/extensions/browser/src/browser-tool.ts +++ b/extensions/browser/src/browser-tool.ts @@ -5,6 +5,7 @@ * maps high-level actions onto browser control client calls. */ import crypto from "node:crypto"; +import { describeBrowserTool } from "./browser-tool-description.js"; import { executeActAction, executeConsoleAction, @@ -514,20 +515,7 @@ export function createBrowserTool(opts?: { return { label: "Browser", name: "browser", - description: [ - "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: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 tab operations, targetId also accepts tabId handles (t1) and labels from action=tabs.", - "For multi-step browser work, login checks, stale refs, duplicate tabs, or Google Meet flows, use the bundled browser-automation skill when it is available.", - 'For stable, self-resolving refs across calls, use snapshot with refs="aria" (Playwright aria-ref ids). Default refs="role" are role+name-based.', - "Use snapshot+act for UI automation. Avoid act:wait by default; use only in exceptional cases when no reliable UI state exists.", - "For file chooser uploads, pass the trigger ref with paths in the same upload call when available; use paths-only arming only when a later trigger is intentional. Use inputRef or element to set a file input directly.", - `target selects browser location (sandbox|host|node). Default: ${targetDefault}.`, - hostHint, - ].join(" "), + description: describeBrowserTool({ targetDefault, hostHint }), parameters: BrowserToolSchema, execute: async (_toolCallId, args) => { const params = args as Record;