Browser toolbar
Only frequent current-page actions belong here: navigation, address input, saving the current page, opening bookmarks, external opening, and Browser ⋯.
Clicks, scrolling, keyboard input, navigation, and closing a page never pause an Agent. This applies equally to user input and automation, including automation of OpenProgram itself. Only the conversation execution controls pause or cancel the task. Browser resources do not maintain a separate write prohibition.
The existing page input notification path incorrectly requests execution pauses. A retained page prohibition can outlive the execution that created it. Remove that path and the separate prohibition checks while retaining execution dispatch guards, ownership checks, and current-frame validation.
When a target disappears, discard its session and return actionable recovery information with its last URL. Recover by observing a newly opened exact URL, never by replaying a mutating action or selecting an unrelated tab. The Agent continues the task using a fresh observation. Missing authentication or unsaved state is reported rather than invented.
Browser screenshots preserve ToolReturn images and structured errors. Executor shutdown must not leave a live server submitting work into a closed default executor. Recovery must respect task cancellation and unresolved external effects.
Implemented and independently reviewed: page events leave execution running; explicit conversation pause still stops dispatch; old page prohibition cannot block a new execution; closed targets provide usable recovery; screenshots retain images; shutdown errors are handled at their owning lifecycle boundary. Verify affected desktop and Python components, contracts, documentation links, and installed App. Independent specification and quality reviews precede completion.
New Pane only selects the content type; web navigation, bookmarks, History, and data import appear after entering Browser. The browser toolbar handles the current page and browsing data; the window menu handles OpenProgram and panes. Both settings entry points use the same Settings page.
Content entry points, browser home, and data management currently share one page. The design separates their responsibilities before defining the controls on each page.
Bookmark sites share the tab favicon renderer: saved image, then the site origin’s /favicon.ico, then the Lucide browser fallback. Only HTTP(S) and image data sources are accepted; requests omit referrers and never use a third-party favicon service. New bookmarks retain the current page icon, and revisiting an exact bookmarked URL refreshes its saved icon without erasing it on empty loading state. The bar keeps its existing height and shows bar items, a double-chevron hidden-items menu, a divider, and All bookmarks at the right. All bookmarks opens the existing manager, which retains Other bookmarks and Mobile bookmarks in their original hierarchy. Folder icons remain folders. Acceptance requires actual imported leaves, failed image fallback, icon replacement, overflow actions and manager access. Reference: Chrome bookmark help and the supplied Chrome screenshots.
During native page loading, the Reload control becomes an accented Lucide X with the accessible name Stop loading. A thin indeterminate progress indicator appears below the browser toolbar, without changing its height. Stop cancels loading; completion, failure, and cancellation remove the indicator and restore Reload. The indicator reflects native loading events, does not invent a percentage, and stays static under reduced-motion preferences.
When a webpage prevents reload or navigation with beforeunload, its current owning window displays a native confirmation. Stay on page is the default and cancel action; Leave page permits only the blocked operation. Missing ownership, destruction, or dialog failure keeps the page. Ordinary reloads need no confirmation. This uses Electron's will-prevent-unload event; tab-close and application-shutdown policy are outside this change.
Ordinary tabs and split members use the site favicon when it decodes to visible pixels, with the existing animated Lucide browser icon while loading or on failure. The desktop process reads favicon bytes through the page session, limits requests to HTTP(S) and image data, bounds downloads to 256 KiB and five seconds, and sends image data URLs so the renderer can detect fully transparent icons. Navigation and destruction cancel pending reads; stale results cannot replace the current page icon. State snapshots include the current favicon when a retained page is reopened. Browsing history keeps the original site icon URL.
An empty pane only shows Files, New chat, Browser, and Terminal. The address bar, quick sites, Bookmarks, and History do not appear here.
Files, New chat, Browser, and Terminal all use equally sized rounded icon containers and Lucide outline icons. Files uses blue, New chat green, Browser orange and purple, and Terminal purple. Color only identifies content types; it does not change card hierarchy, interaction states, or accessible names, and introduces no brand logos, external assets, or extra animation.
Selecting Browser opens browser home in place. Home and actual pages share the address bar and bookmarks bar structure. Back, Forward, Reload, Home, Bookmark, and external opening remain visible but disabled when no current page applies; address input, Bookmarks, Import, and Browser ⋯ remain available.
Ordinary links, form submissions, script assignments, and same-document navigation continue to commit in the current WebContentsView and only update that WebTab's URL, title, and History; they must not create a tab. Only when the page explicitly requests a new browsing context (target="_blank" or window.open) does the main process convert a validated HTTP(S) URL into a WebTab with its own id. The new WebTab immediately becomes active; when already at /chat or /s/<id> , do not push /chatagain, so session-route synchronization cannot overwrite popup activation.
The native page area directly uses Electron context-menu hit parameters. HTTP(S) links show Open Link in New Tab and Copy Link Address; editable content shows Undo, Redo, Cut, Copy, Paste, and Select All; ordinary pages show Back, Forward, and Reload. Open Link in New Tab reuses the popup entry point and therefore has the same semantics as the page's own window.open for URL validation, unique tabs, and immediate activation. Menu actions only affect the WebTab that produced the menu and is still owned by the same window.
A floating web preview opened by an agent in chat records its session tab at creation. It appears only while that session is the current center content. Switching to another chat, a regular Browser tab, or a non-chat page must remove the WebTab from the renderer's native visible set and restore the user's web zoom. Returning to the original session reuses the same WebTab, URL, scroll position, and form state without reloading. After the user closes the preview, its restore entry also appears only in that session.
The star at the right of the address bar only saves or removes the current page; the adjacent Bookmarks button opens the complete bookmarks page. Browser home shows only user-maintained quick sites, not all bookmarks.
The complete bookmarks page has a fixed page title, search field, and page menu at the top. The left column contains only a collapsible folder tree with the current directory highlighted; the right column contains only that directory's direct children. Bookmark rows have a favicon, title, and single ⋮ menu; folder rows open the corresponding directory. The entire tree, URLs, and edit/delete buttons no longer share one list. Rows in both lists use 36px; the search field uses the global 30px control height, inline icon buttons use 28px, and the header uses 52px. The right list's initial viewport padding is 16px. In a narrow pane, the directory tree moves above the content list rather than disappearing.
Browser home and actual pages show the same bookmarks row below the address bar by default. Users can disable it through Browser ⋯ or Settings. Narrow split panes temporarily hide the row, while all content remains accessible through Bookmarks in Browser ⋯ .
Chromium's Bookmarks bar is the bookmarks-bar container, not an ordinary folder button. Its direct children enter the bar in original order;Other bookmarks and Mobile bookmarks remain in the manager, reached through All bookmarks after the overflow control and divider. Ordinary root bookmarks remain bar items. Items placed in overflow must not leave partially clipped buttons in the visible bar; the last visible button must retain its full background and both rounded ends.
The single-row bookmarks bar has no horizontal scrollbar; items that exceed its width appear in the right-side overflow menu. Menus have a fixed width of 280px and ellipsize long titles. Folder rows show a Folder icon in the existing 14px icon slot and a right arrow to enter the next menu, rather than concatenating full paths. Site rows show bookmark favicons; when imported data lacks a favicon, fetch it from the URL and show a Bookmark icon on failure, never an empty slot. In the desktop App, hovering over a bookmarks-bar folder opens it immediately, and moving horizontally to an adjacent folder switches menus without extra delay. Consecutive folder switches reuse one menu view and replace only its data, without rebuilding the view or decoding previously displayed favicons again. The native overlay starts at the bookmarks bar's lower edge and must not cover the buttons' pointer hit areas. Each menu level is constrained by the current window height and supports mouse, trackpad, and keyboard interaction.
Settings in the window menu opens OpenProgram settings; Browser's ⋯ opens its Browser section directly. The toolbar adds no persistent gear button and no second browser-settings store.
Splitting, leaving split view, and pane arrangement apply to Files, Chat, Browser, and Terminal; they are not current-page actions. They belong in Tab/Pane controls or the tab context menu, not the browser toolbar.
Chromium's bookmark bar, other bookmarks, and nested folders retain their hierarchy and order in the OpenProgram bookmark tree. Import must not flatten bookmarks into separate entries below home.
Browser ⋯ and the window ⋯ use their respective trigger buttons' actual screen rectangles. The menu's right edge aligns with the button's right edge, with a 4px vertical gap; the two menus cannot share placement or content.
Records are grouped by local date. Each row shows only time, favicon, title, domain, and actions; the full URL no longer occupies a second line.
The source browser, profile, and data types are confirmed separately. OpenProgram does not read or modify the source profile actively used by the user.
The browser area has three levels: current page, browsing data, and entire window. Whether an action remains visible depends on frequency, relevance to the current page, and space in narrow split panes.
| Position | Persistent actions | Narrow-pane rules |
|---|---|---|
| Left of address bar | Back · Forward · Reload/Stop · Home | 520–719px Move Home into ⋯; below 520px , also move Forward into ⋯. Back and Reload/Stop always remain. |
| Address bar | URL, search input, and security state; Reload becomes Stop in place during loading. | Always occupies remaining width, with a minimum usable width of 160px; no wrapping. |
| Right of address bar | Bookmark current page · Bookmarks · Open externally · Browser menu | 520–719px Move Bookmarks and Open externally into ⋯; the star and ⋯ always remain. |
| Below address bar | An optional single-row bookmarks bar directly displays the children of Bookmarks bar ; overflow comes next, followed by a divider and All bookmarks opening the manager with all source folders. | Below 560px , do not render this row; open Bookmarks through Browser ⋯ . Horizontal scrollbars are prohibited. Folder and overflow menus must not exceed the current available window height and must allow vertical scrolling. |
Only frequent current-page actions belong here: navigation, address input, saving the current page, opening bookmarks, external opening, and Browser ⋯.
⋯New browser tab, Find in page, Zoom, Print, Bookmarks, History, Downloads, Show bookmarks bar, Import browser data, Clear browsing data, Browser settings.
New pane/window, pane layout, OpenProgram Settings, Help, and About. It no longer directly contains Bookmarks, History, or page-level settings.
| Active content | Floating preview | Native WebTab constraints |
|---|---|---|
| The chat that created the preview | Visible; returning to that chat restores its previous position and size. | Uses PiP bounds and layout zoom; retains the same WebContentsView. |
| Another chat | Hidden; no restore entry. | Remove it from the syncVisible set and restore the user's zoom factor. |
| Regular Browser tab or the same page in split view | Hidden. | The ordinary pane exclusively owns that WebTab's visible bounds; PiP layout zoom does not persist. |
| The user closes the preview in its owning chat | Hidden; the owning chat shows a restore entry. | Retain the background WebTab without exposing it as another session's turn surface. |
The following information-architecture acceptance diagram lets you switch states to verify that controls appear at the correct level.
Reference products establish visible behavior; platform documentation establishes implementation boundaries. OpenProgram does not duplicate the complete Chromium importer.
Files, New chat, Browser, and Terminal are top-level content types; browser controls appear only inside Browser.
AdoptedThe built-in browser maintains its own profile. Users explicitly initiate import when existing data is needed.
Adopted with changesThe star saves the current page, the bookmarks bar can be toggled, and the complete manager is separate. OpenProgram retains these three levels with stricter collapse rules for split panes.
Adopted with changesThe built-in browser maintains independent state; Cookies and history clearing belong to desktop App Settings. OpenProgram adds a Browser ⋯ deep link without duplicating settings storage.
Navigation in the current browsing context commits in place; explicit requests for a new context create and activate a new tab. OpenProgram preserves this distinction while converting native popups into WebTabs managed by center-tab state.
Adopted with changeswebContents provides link, selection, edit-state, and frame hit parameters;Menu.popup provides the current OS's native menu. OpenProgram adopts a limited action set without exposing DevTools or arbitrary protocol opening.
components are not exposed as Electron JavaScript APIs and provide no general Cookies import implementation.
Not duplicatedsetWindowOpenHandler receives resolved popup URLs, andcontext-menu provides link, selection, and edit hit parameters.Menu.buildFromTemplate and BaseWindow bound to their owning menu.popup are native context-menu entry points in the main process.The renderer passes only opaque source/profile ids issued by the main process. It cannot freely specify file paths, source-browser executable paths, or Cookie values.
| Page intent | Main process | Renderer state |
|---|---|---|
| Current-context navigation | did-navigate / did-navigate-in-page Report the same record's state; send no popup event. | updateWebTab Update URL, title, and favicon in place; retain id and active tab. |
target="_blank" / window.open | setWindowOpenHandler Reject native child windows; send only valid HTTP(S) URLs and opener ids to the window that owns the View. | openPopupWebTab Create a unique id and activate it immediately; retain the current route when already on a chat surface. |
| Web link context menu → Open Link in New Tab | context-menu Validate exact ownership and URL, then reuse the popup event above; bind Copy/Edit/Navigation directly to the menu-producing webContents. | The new tab behaves like a page popup; do not execute cross-tab actions after menu dismissal or with a stale owner. |
Web content, renderer, and main process exchange only bounded state required for an action. Data import is not profile migration; it copies only explicitly selected data.
| Boundary | Constraint | Failure outcome |
|---|---|---|
| renderer → main | source/profile uses opaque ids returned by main; data types use an allowlist; renderer cannot pass file or executable paths. | Reject the request |
| Web content → main | Popup and context-menu links accept only resolved HTTP(S) URLs; actions bind to the exact WebTab owner that produced the event.file:, javascript:, transferred/closed records, and forged openers must not create tabs or navigate other Views. | deny / no-op |
| Source profile | Read-only discovery; real paths must remain under discovered browser roots; reject symlink escapes. | source_invalid |
| Temporary data | Directories use 0700 and files 0600; delete after success, failure, or timeout. Logs contain only source labels, types, counts, and error codes. | cleanup + stable error |
| Target profile | Merge History and Bookmarks; write Cookies through the Electron API without replacing the target database. | Preserve pre-import data |
| Concurrency and cancellation | Only one import job may run across the App at a time. The initiating renderer creates a bounded request id; only that renderer may cancel. Check cancellation at parser phase boundaries, during temporary-browser startup/CDP waits, and before and after each Cookie write. | import_busy / import_cancelled |
| Target state after cancellation | After accepting cancellation, do not merge History or Bookmarks; terminate the temporary browser and delete temporary files. Electron's Cookie API has no transaction: writes completed before cancellation remain, but no further writes may occur afterward. | cooperative stop |
Pages are grouped by session and collapsed into the right-side session resources panel by default. The complete entry-point and resource-type design is in Session resources. Page records retain actual session ownership and pin state; manual pages and explicit split views retain top-level placement.
Each acceptance item maps to an externally observable result. Design status and implementation evidence are separate; the existence of this document does not prove completion.
| Observable result | Current status | Implementation evidence |
|---|---|---|
Ordinary navigation retains the current WebTab id and active state;target="_blank" / window.open creates a unique WebTab that remains active after creation without being overwritten by the original chat route. Link context menus show Open Link in New Tab and Copy Link Address; ordinary/editable contexts show corresponding Navigation or Edit actions, all restricted to the exact owner. | Implemented and accepted in the default App | desktop-bridge.ts popup route contract; apps/desktop/main.js WebContentsView context menu; check-web-split.mjs; check-webtab-navigation.js. Independent specification and quality reviews passed. In the installed App, the popup remained active throughout a continuous 2-second observation and /s/<id> stayed unchanged; ordinary navigation retained tab count and id; native HTTP(S) link context menus displayed Open/Copy and page navigation actions. |
| New Pane shows only Files, New chat, Browser, and Terminal. All four use equally sized rounded icon containers with different semantic colors; browser-only controls are absent from the DOM. | Implemented and accepted in light and dark themes in the default App | new-tab-page.tsx; check-built-in-browser.mjs effective-cascade and semantic-color mutation checks |
| Selecting Browser displays the same address-bar and bookmarks-bar structure as web tabs, with import and quick-site entries. Actions without a current page remain visible but disabled. | Implemented and accepted in the default App | browser-home-page.tsx; check-built-in-browser.mjs; toolbar / bookmarks bar in the actual App measure 40px / 30px |
At standard width, the Browser toolbar shows Back, Forward, Reload/Stop, Home, address bar, current-page star, Bookmarks, external opening, and Browser ⋯in that order; no split controls or Settings gear. | Implemented | web-tab-pane.tsx; check-built-in-browser.mjs; check-web-split.mjs |
Browser ⋯ provides Bookmarks, History, Downloads, bookmarks-bar toggle, Import, Clear browsing data, and Browser settings; the window menu contains only App, window, and pane actions. Desktop popovers report intrinsic size using the menu's scrollWidth/scrollHeight , without recursively shrinking to the current clipped height. | Implemented | browser-controls.tsx; menu-overlay/context-menu/page.tsx; check-built-in-browser.mjs |
The bookmarks bar appears by default and remains off after the user disables it. It directly displays the children of Bookmarks bar , with an overflow entry for hidden items followed by a separated All bookmarks manager entry at the right. Overflowed items leave no clipped button behind; the last visible button retains its full background and both rounded ends. Desktop folder hover opens menus and supports horizontal switching. Menus stay open while moving from the trigger into a popover or submenu, and close within 120ms after leaving the trigger and every menu panel, without an extra click. Folder menus are 280px wide, ellipsize titles, and open nested folders level by level. Folder rows show Folder icons; site rows show favicons with the shared browser-icon fallback on failure and no empty icon slots. Below 560px pane width, the bar hides but Bookmarks remains reachable. | Acceptance criteria | test_tab_favicons.py; bookmark-favicons.test.mjs; check-built-in-browser.mjs; default App visual verification of icons, overflow and manager access. |
Bookmarks manager uses top search, a left folder tree, and the current directory list on the right. Ordinary rows contain only a favicon/folder icon, title, and ⋮. Rows in both lists are 36px, and the header uses 52px, and narrow panes stack vertically. | Implemented and density-accepted in the default App | builtin-tab-pane.tsx; center-tabs.module.css; check-bookmarks.mjs; measured header, folder row, content row, search field, and icon button sizes in the default App are respectively 52 / 36 / 36 / 30 / 28px |
Browser settings and window Settings share a route and configuration source; Browser ⋯ opens the Browser section directly, with no persistent toolbar gear. | Implemented | /settings/browser; browser-settings.tsx; browser-prefs.ts |
| The import dialog lists source browsers, profiles, and supported data types; closing before starting causes no target changes. During import, Import becomes Cancel in place and closing is disabled. Accepted cancellation shows a stable result and releases the global import lease so another import can start. | Implemented and accepted in the default App | browser-import:run/cancel IPC; browser-profile-import.js cancellation self-check; check-webtab-navigation.js sender-ownership, lease-release, and window-cleanup checks;check-built-in-browser.mjs. The installed App detected 3 Chrome profiles. While running, it displayed Cancel and disabled closing; cancellation returned import_cancelled, and the next request no longer returned import_busy. |
| History groups records by date, retains 36–40px rows with at least 1000 records, and supports search and scrolling. | Implemented and scale-accepted in the default App | web-history.css is 38px; History pagination caps at 5000 and search runs in main. Storage self-check covers the 5000-record limit. The installed App's existing 5000 records were incrementally displayed up to 1000 rows; three incremental renders took 241.6 / 235.1 / 256.2ms, all row heights were 38px, and the last row was visible after scrolling to the bottom;google search returned 250 matches within 299.4ms . |
| Built-in page downloads go to the system Downloads directory. The Downloads page shows live progress and final status and supports opening files, revealing them in Finder, cancelling active jobs, and clearing records. | Implemented; installed-App checks passed | apps/desktop/main.js bound to their owning will-download and downloads:* IPC; builtin-tab-pane.tsx; check-webtab-navigation.js. The installed App completed a 42 B attachment download and cancelled an active 32 MB job; the page displayed Completed, Cancelledrespectively. After cancellation, active=false, and test records could be cleared. In another installed-App download check, Open used the system-associated application to open a 58 B text file, and Show in Finder located and selected that same file; test records were then cleared and the file moved to Trash. |
The current page supports Find in page, Zoom in/out/reset, and Print; commands target only the exact WebTab owned by the requesting window. Print first invokes system printing. If printing fails or no destination is available, the same action opens the owning window's native Save PDF dialog. Cancelling system printing or saving returns incomplete and writes no file; successful system printing must not additionally generate a PDF. Write the PDF to a unique temporary file in the target directory, then replace the selected path on completion; preserve existing targets on failure. Find provides previous/next match, count, and close; closing clears page selection;⌘/Ctrl+F, ⌘/Ctrl++/-/0, ⌘/Ctrl+P matches menu actions. When frontend and desktop-shell versions temporarily differ, enable actions according to capabilities actually exposed by preload. An older shell lacking new IPC must disable corresponding actions without breaking the page. | Implemented; installed-App checks passed | apps/desktop/main.js bound to their owning webtab:find/zoom/print IPC; web-tab-pane.tsx; check-webtab-navigation.js; check-built-in-browser.mjs. The installed App found 35 matches in a local documentation page and advanced to match 2; Zoom returned 100 → 110 → 100 → 100. The acceptance machine had no print destination: after system Print failed, the owning window's native Save PDF dialog appeared and generated a 1-page, 8455 B PDF from the current documentation WebTab. IPC returned true; the test file was moved to Trash. Automated checks cover system-print cancellation, save cancellation, preserving existing files on write failure, and avoiding writes when exact ownership is lost during PDF rendering. Persistent toolbar and tab-bar styling did not change. |
| Bookmarks preserve source roots, up to 64 folder levels, and sibling order; invalid URLs are filtered, and repeated imports create neither duplicate URLs nor empty folders. | Implemented | apps/web/lib/tabs/bookmarks.ts; check-bookmarks.mjs |
In desktop App and Web, the window menu right-aligns with its trigger with a 4px gap below the button. For desktop main, Browser, bookmark, and tab context menus, CSS-pixel coordinates from the renderer must be converted to Electron DIP using the sender page's zoom factor. Menus remain beside their triggers at 80%, 100%, and 125% zoom. Renderer-provided Browser, bookmark, and tab context menus render a left icon slot only when a row actually contains a checkmark, folder, or favicon. Rows without icons reserve no empty slot; their text starts at the menu row's default 10px padding. | Implemented | main-menu.tsx; menu-overlay/context-menu/page.tsx; check-built-in-browser.mjs; check-center-tabs.mjs; check-webtab-zoom-bounds.js |
| Chat-composer, model, context, and other popovers must not hide or reset a native Web Page when they do not cover it. Suspend the corresponding WebContentsView only when a visible popover rectangle actually intersects the page rectangle. | Implemented | web-tab-bounds.ts rectangle-intersection checks;web-tab-pane.tsx; check-web-split.mjs |
| Floating previews created in chat appear only in their owning session. Switching to another chat, ordinary Browser tab, or non-chat page hides them and restores user web zoom. Returning reuses the original WebTab state; the restore entry after closing also belongs only to that session. | Implemented; automated checks and runtime review passed | web-tab-pip-store.ts; web-tab-pip.tsx; web-tab-pane.tsx; desktop-bridge.ts; apps/desktop/main.js; check-web-split.mjs covers owner chat, inventory, and switching without pointer-up;check-webtab-navigation.js covers first navigation commit and zoom restoration after transfer commit. The installed production build and default worker health checks passed. |
Cookies only enter persist:webtabs; Cookie values do not appear in IPC, logs, History, or renderer. | Implemented | browser-profile-import.js self-check; apps/desktop/main.js |
| Corrupt sources, missing browsers, temporary-process timeouts, and partial Cookie failures return stable error codes and preserve original data. | Implemented | browser-profile-import.js and browsing-history-store.js self-check |
| Full-screen terminal programs that depend on PTY. | Current limitation | Product boundary, not a current acceptance gap. |
| Passwords, autofill, payment/address data, and source-browser takeover. | Explicitly excluded | browser-profile-import.js import-type allowlist. |
| Chrome Web Store and Edge Add-ons open only as ordinary pages. The product provides no store installation, local-directory loading, CRX downloads, extension runtime, or management page. | Explicitly excluded; code removed | browser-extensions.html; check-no-browser-extensions.js; extensions are outside profile data import. |
Visible acceptance uses only the installed /Applications/OpenProgram.app, default worker on 18100, and ~/.openprogram. Documentation updates do not change the agent web-control protocol.
This page is the normative description of the current design. Update it directly when implementation changes; Git preserves revision history.
Bookmark menus retain one detached cascade view per desktop window between openings. Reopening attaches that view and updates its menu data without loading a new document. Ordinary menus retain their existing close behavior. Window cleanup destroys both visible and detached views; detached senders cannot dispatch menu actions.
A renderer-owned favicon cache deduplicates concurrent image loads, retains decoded 32-pixel surfaces for 30 minutes, and retains failures for 5 minutes. At most 256 source entries remain in memory. Components draw cached surfaces without starting another image request; a changed source selects a different cache entry. Loading is bounded to 5 seconds and uses no referrer or third-party favicon service. Cross-origin images remain drawable without reading their pixels. Transparent readable images retain the existing fallback behavior. Application restart may load icons again through the browser cache; no additional persistent icon database is introduced.
bookmark-bar.tsx owns bookmark layout and menus, browser-controls.tsx owns browser commands, favicon-cache.ts owns loading and bounded reuse, and tab-favicon.tsx owns presentation. Repeated native favicon events for the current successful source do not clear or reload the icon; page navigation still invalidates ownership.
Acceptance: a real bookmark-folder close/reopen issues no second icon request even when the server sends Cache-Control: no-store. Concurrent instances share one request, failed and transparent icons fall back, changed sources recover, hidden overflow stays hidden, and cache expiry/eviction permits retry. Desktop menu tests prove view reuse, inactive sender rejection, geometry updates and final disposal.
References: Canvas drawImage; Electron WebContentsView. Implementation evidence: the renderer cache and retained menu lifecycle pass independent specification and quality review. favicon-cache.test.mjs covers coalescing, expiry, timeout and eviction; check-bookmark-menu-cache.cjs covers document reuse, hidden senders and disposal; test_tab_favicons.py covers actual React rendering, remounts and folder reopening. The task verification ledger separately records full-suite and installed-App acceptance.
The browser workflow owns the outer observe, action and verification loop. Each planner request enables stop_after_tool_round: the shared Agent loop executes its normal validated and permission-checked tool round, retains its receipts, then returns to the workflow without requesting another model response. This is independent of the safety limit max_iterations. Default Agent calls still continue until final output. Exhausting a safety limit is non-retryable so completed actions cannot be replayed by transport recovery. A structured final-output contract cannot be combined with this early return.
Every BrowserPageController submission to its Playwright owner thread carries the current ContextVars, including cleanup and final verification. Context is copied per submission, not retained as a permanent authority; other conversations do not inherit access. Existing Page isolation, binding validation, permission checks and operation leases remain authoritative.
A legacy browser task uses an explicitly bound current Page when available, without reloading it because the task also supplies a URL hint. An invalid or stale explicit binding fails rather than opening a replacement Page. Without an explicit Page, existing URL/open behavior is unchanged.
Acceptance uses the public browser task with a fake provider and real Runtime/Agent loop: one action executes once, the next request verifies, no iteration error or replay occurs, and the controller closes with the same conversation identity. Default safety caps still fail without replay; an explicit current Page is reused without open/navigation. Implementation evidence: independent specification and quality reviews pass. The real-Runtime browser regressions cover one action followed by verification, explicit deny rules, preserved cleanup identity, and stale binding rejection. Agent-loop regressions cover sync/async option propagation, final-output incompatibility, and non-retryable cap exhaustion with exactly one tool effect. The task verification record separately records full-gate and installed-App acceptance.
The planner uses explicit upload with an approved local path and a fresh file-input ref. Upload remains subject to sandbox reads, permission approval, Page ownership, cancellation and step limits. File inputs expose their type and selected file names, never bytes or local paths. Selecting a file is separate from server acceptance; file_selected checks the exact current input and final evidence is rechecked. MCP-backed planners use the same controller for file selection without switching backend ownership.
Verification uses value, not typing text. Invalid calls return an actionable error. Repeated failed calls on an unchanged frame stop after three consecutive failures and retain the concrete error. A planner can stop with a blocker; this never grants success. Calls that succeed after correcting arguments continue normally. Operation counts still count Page mutations, not model requests.
Acceptance uses real Runtime planner calls and an owned headless Chromium file-input fixture. It covers corrective retries, persistent invalid verification, stop, file selection/readback, explicit denial, stale refs, sandbox reads, cancellation, and independent server-response verification. No user registration or private-file upload is part of acceptance. Implementation evidence: independent specification and quality reviews pass. Regression coverage includes both Runtime planner paths and real Chromium file selection, server acknowledgement, and cancellation after file read with zero transmission. Full release verification and installed-App acceptance remain recorded in the task ledger.