diff --git a/src/browser/chrome.profile-decoration.ts b/src/browser/chrome.profile-decoration.ts index 7c5d96d6f..49192466c 100644 --- a/src/browser/chrome.profile-decoration.ts +++ b/src/browser/chrome.profile-decoration.ts @@ -180,3 +180,11 @@ export function decorateClawdProfile( // ignore } } + +export function ensureProfileCleanExit(userDataDir: string) { + const preferencesPath = path.join(userDataDir, "Default", "Preferences"); + const prefs = safeReadJson(preferencesPath) ?? {}; + setDeep(prefs, ["exit_type"], "Normal"); + setDeep(prefs, ["exited_cleanly"], true); + safeWriteJson(preferencesPath, prefs); +} diff --git a/src/browser/chrome.test.ts b/src/browser/chrome.test.ts index a8a42ae95..da8e384da 100644 --- a/src/browser/chrome.test.ts +++ b/src/browser/chrome.test.ts @@ -7,6 +7,7 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { decorateClawdProfile, + ensureProfileCleanExit, findChromeExecutableMac, findChromeExecutableWindows, isChromeReachable, @@ -103,6 +104,18 @@ describe("browser chrome profile decoration", () => { } }); + it("writes clean exit prefs to avoid restore prompts", async () => { + const userDataDir = await fsp.mkdtemp(path.join(os.tmpdir(), "clawdbot-chrome-test-")); + try { + ensureProfileCleanExit(userDataDir); + const prefs = await readJson(path.join(userDataDir, "Default", "Preferences")); + expect(prefs.exit_type).toBe("Normal"); + expect(prefs.exited_cleanly).toBe(true); + } finally { + await fsp.rm(userDataDir, { recursive: true, force: true }); + } + }); + it("is idempotent when rerun on an existing profile", async () => { const userDataDir = await fsp.mkdtemp(path.join(os.tmpdir(), "clawdbot-chrome-test-")); try { diff --git a/src/browser/chrome.ts b/src/browser/chrome.ts index eebf399bc..6f610bcc4 100644 --- a/src/browser/chrome.ts +++ b/src/browser/chrome.ts @@ -13,7 +13,11 @@ import { type BrowserExecutable, resolveBrowserExecutableForPlatform, } from "./chrome.executables.js"; -import { decorateClawdProfile, isProfileDecorated } from "./chrome.profile-decoration.js"; +import { + decorateClawdProfile, + ensureProfileCleanExit, + isProfileDecorated, +} from "./chrome.profile-decoration.js"; import type { ResolvedBrowserConfig, ResolvedBrowserProfile } from "./config.js"; import { DEFAULT_CLAWD_BROWSER_COLOR, DEFAULT_CLAWD_BROWSER_PROFILE_NAME } from "./constants.js"; @@ -26,7 +30,11 @@ export { findChromeExecutableWindows, resolveBrowserExecutableForPlatform, } from "./chrome.executables.js"; -export { decorateClawdProfile, isProfileDecorated } from "./chrome.profile-decoration.js"; +export { + decorateClawdProfile, + ensureProfileCleanExit, + isProfileDecorated, +} from "./chrome.profile-decoration.js"; function exists(filePath: string) { try { @@ -178,6 +186,8 @@ export async function launchClawdChrome( "--disable-background-networking", "--disable-component-update", "--disable-features=Translate,MediaRouter", + "--disable-session-crashed-bubble", + "--hide-crash-restore-bubble", "--password-store=basic", ]; @@ -246,6 +256,12 @@ export async function launchClawdChrome( } } + try { + ensureProfileCleanExit(userDataDir); + } catch (err) { + log.warn(`clawd browser clean-exit prefs failed: ${String(err)}`); + } + const proc = spawnOnce(); // Wait for CDP to come up. const readyDeadline = Date.now() + 15_000;