diff --git a/CHANGELOG.md b/CHANGELOG.md index d461cc2bf70..efd209d9c62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -50,6 +50,7 @@ Docs: https://docs.openclaw.ai - Slack/Bolt startup compatibility: remove invalid `message.channels` and `message.groups` event registrations so Slack providers no longer crash on startup with Bolt 4.6+; channel/group traffic continues through the unified `message` handler (`channel_type`). (#32033) Thanks @mahopan. - Telegram: guard duplicate-token checks and gateway startup token normalization when account tokens are missing, preventing `token.trim()` crashes during status/start flows. (#31973) Thanks @ningding97. - Skills/sherpa-onnx-tts: run the `sherpa-onnx-tts` bin under ESM (replace CommonJS `require` imports) and add regression coverage to prevent `require is not defined in ES module scope` startup crashes. (#31965) Thanks @bmendonca3. +- Browser/default profile selection: default `browser.defaultProfile` behavior now prefers `openclaw` (managed standalone CDP) when no explicit default is configured, while still auto-provisioning the `chrome` relay profile for explicit opt-in use. (#32031) Fixes #31907. Thanks @liuxiaopai-ai. - Sandbox/Docker setup command parsing: accept `agents.*.sandbox.docker.setupCommand` as either a string or a string array, and normalize arrays to newline-delimited shell scripts so multi-step setup commands no longer concatenate without separators. (#31953) Thanks @liuxiaopai-ai. - Gateway/Plugin HTTP route precedence: run explicit plugin HTTP routes before the Control UI SPA catch-all so registered plugin webhook/custom paths remain reachable, while unmatched paths still fall through to Control UI handling. (#31885) Thanks @Sid-Qin. - Security/Node exec approvals: preserve shell/dispatch-wrapper argv semantics during approval hardening so approved wrapper commands (for example `env sh -c ...`) cannot drift into a different runtime command shape, and add regression coverage for both approval-plan generation and approved runtime execution paths. Thanks @tdjackey for reporting. diff --git a/docs/tools/browser.md b/docs/tools/browser.md index 13eaf3203f8..70c420b6c33 100644 --- a/docs/tools/browser.md +++ b/docs/tools/browser.md @@ -97,7 +97,7 @@ Notes: - `browser.ssrfPolicy.allowPrivateNetwork` remains supported as a legacy alias for compatibility. - `attachOnly: true` means “never launch a local browser; only attach if it is already running.” - `color` + per-profile `color` tint the browser UI so you can see which profile is active. -- Default profile is `chrome` (extension relay). Use `defaultProfile: "openclaw"` for the managed browser. +- Default profile is `openclaw` (OpenClaw-managed standalone browser). Use `defaultProfile: "chrome"` to opt into the Chrome extension relay. - Auto-detect order: system default browser if Chromium-based; otherwise Chrome → Brave → Edge → Chromium → Chrome Canary. - Local `openclaw` profiles auto-assign `cdpPort`/`cdpUrl` — set those only for remote CDP. diff --git a/src/browser/config.test.ts b/src/browser/config.test.ts index b891f8b3d98..ec1c40cd66e 100644 --- a/src/browser/config.test.ts +++ b/src/browser/config.test.ts @@ -12,15 +12,19 @@ describe("browser config", () => { expect(resolved.cdpHost).toBe("127.0.0.1"); expect(resolved.cdpProtocol).toBe("http"); const profile = resolveProfile(resolved, resolved.defaultProfile); - expect(profile?.name).toBe("chrome"); - expect(profile?.driver).toBe("extension"); - expect(profile?.cdpPort).toBe(18792); - expect(profile?.cdpUrl).toBe("http://127.0.0.1:18792"); + expect(profile?.name).toBe("openclaw"); + expect(profile?.driver).toBe("openclaw"); + expect(profile?.cdpPort).toBe(18800); + expect(profile?.cdpUrl).toBe("http://127.0.0.1:18800"); const openclaw = resolveProfile(resolved, "openclaw"); expect(openclaw?.driver).toBe("openclaw"); expect(openclaw?.cdpPort).toBe(18800); expect(openclaw?.cdpUrl).toBe("http://127.0.0.1:18800"); + const chrome = resolveProfile(resolved, "chrome"); + expect(chrome?.driver).toBe("extension"); + expect(chrome?.cdpPort).toBe(18792); + expect(chrome?.cdpUrl).toBe("http://127.0.0.1:18792"); expect(resolved.remoteCdpTimeoutMs).toBe(1500); expect(resolved.remoteCdpHandshakeTimeoutMs).toBe(3000); }); @@ -239,31 +243,30 @@ describe("browser config", () => { expect(resolved.ssrfPolicy).toEqual({}); }); - // Tests for headless/noSandbox profile preference (issue #14895) - describe("headless/noSandbox profile preference", () => { - it("defaults to chrome profile when headless=false and noSandbox=false", () => { + describe("default profile preference", () => { + it("defaults to openclaw profile when defaultProfile is not configured", () => { const resolved = resolveBrowserConfig({ headless: false, noSandbox: false, }); - expect(resolved.defaultProfile).toBe("chrome"); + expect(resolved.defaultProfile).toBe("openclaw"); }); - it("prefers openclaw profile when headless=true", () => { + it("keeps openclaw default when headless=true", () => { const resolved = resolveBrowserConfig({ headless: true, }); expect(resolved.defaultProfile).toBe("openclaw"); }); - it("prefers openclaw profile when noSandbox=true", () => { + it("keeps openclaw default when noSandbox=true", () => { const resolved = resolveBrowserConfig({ noSandbox: true, }); expect(resolved.defaultProfile).toBe("openclaw"); }); - it("prefers openclaw profile when both headless and noSandbox are true", () => { + it("keeps openclaw default when both headless and noSandbox are true", () => { const resolved = resolveBrowserConfig({ headless: true, noSandbox: true, @@ -271,7 +274,7 @@ describe("browser config", () => { expect(resolved.defaultProfile).toBe("openclaw"); }); - it("explicit defaultProfile config overrides headless preference", () => { + it("explicit defaultProfile config overrides defaults in headless mode", () => { const resolved = resolveBrowserConfig({ headless: true, defaultProfile: "chrome", @@ -279,7 +282,7 @@ describe("browser config", () => { expect(resolved.defaultProfile).toBe("chrome"); }); - it("explicit defaultProfile config overrides noSandbox preference", () => { + it("explicit defaultProfile config overrides defaults in noSandbox mode", () => { const resolved = resolveBrowserConfig({ noSandbox: true, defaultProfile: "chrome", diff --git a/src/browser/config.ts b/src/browser/config.ts index 417c97f7118..336049e8c69 100644 --- a/src/browser/config.ts +++ b/src/browser/config.ts @@ -264,17 +264,13 @@ export function resolveBrowserConfig( ); const cdpProtocol = cdpInfo.parsed.protocol === "https:" ? "https" : "http"; - // In headless/noSandbox environments (servers), prefer "openclaw" profile over "chrome" - // because Chrome extension relay requires a GUI browser which isn't available headless. - // Issue: https://github.com/openclaw/openclaw/issues/14895 - const preferOpenClawProfile = headless || noSandbox; const defaultProfile = defaultProfileFromConfig ?? - (preferOpenClawProfile && profiles[DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME] - ? DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME - : profiles[DEFAULT_BROWSER_DEFAULT_PROFILE_NAME] - ? DEFAULT_BROWSER_DEFAULT_PROFILE_NAME - : DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME); + (profiles[DEFAULT_BROWSER_DEFAULT_PROFILE_NAME] + ? DEFAULT_BROWSER_DEFAULT_PROFILE_NAME + : profiles[DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME] + ? DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME + : "chrome"); const extraArgs = Array.isArray(cfg?.extraArgs) ? cfg.extraArgs.filter((a): a is string => typeof a === "string" && a.trim().length > 0) diff --git a/src/browser/constants.ts b/src/browser/constants.ts index 5a420360ed3..952bf9190a5 100644 --- a/src/browser/constants.ts +++ b/src/browser/constants.ts @@ -2,7 +2,7 @@ export const DEFAULT_OPENCLAW_BROWSER_ENABLED = true; export const DEFAULT_BROWSER_EVALUATE_ENABLED = true; export const DEFAULT_OPENCLAW_BROWSER_COLOR = "#FF4500"; export const DEFAULT_OPENCLAW_BROWSER_PROFILE_NAME = "openclaw"; -export const DEFAULT_BROWSER_DEFAULT_PROFILE_NAME = "chrome"; +export const DEFAULT_BROWSER_DEFAULT_PROFILE_NAME = "openclaw"; export const DEFAULT_AI_SNAPSHOT_MAX_CHARS = 80_000; export const DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS = 10_000; export const DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH = 6;