Skip to content

fix for windows extension autoload missing side panel#166

Merged
zizhengtai merged 9 commits into
mainfrom
volodymyr/windows-extension-autoload-side-panel-fix
Jul 10, 2026
Merged

fix for windows extension autoload missing side panel#166
zizhengtai merged 9 commits into
mainfrom
volodymyr/windows-extension-autoload-side-panel-fix

Conversation

@volodymyr-narada

@volodymyr-narada volodymyr-narada commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes local browser initialization when Chrome opens the Narada side panel, but Playwright does not expose that side panel as a Page.

On Windows, we observed Chrome reporting the side panel through raw CDP targets:

chrome-extension://.../sidepanel.html?browserWindowId=...

while browser.contexts[*].pages only contained the initialization page. The SDK was waiting for the side panel to appear as a Playwright page, so initialization could hang or time out even though the side panel was already open and usable.

Solution

This PR keeps create_side_panel_url(...) as the source of truth for the expected side panel URL, then checks for that exact URL in two places:

  • Playwright pages across all browser contexts.
  • Raw CDP targets via Target.getTargets.

If the side panel is exposed as a Playwright Page, initialization continues as before. If it is only visible as a raw CDP target, initialization is still accepted using the captured browserWindowId.

Download behavior is fixed in both cases. The SDK now prefers browser-level CDP via Browser.setDownloadBehavior, which does not require the side panel to be exposed as a Playwright Page. If browser-level CDP is unavailable and the side panel is available as a Playwright Page, the SDK falls back to the existing page-level Page.setDownloadBehavior path. If neither path is available, initialization fails rather than silently continuing with broken downloads.

The init-page timeout path also retries by reloading the initialization page, which helps with intermittent “Initializing…” stalls.

Tests

  • Added coverage for finding the side panel across all Playwright browser contexts.
  • Added coverage for accepting a side panel exposed only as a raw CDP target.
  • Added coverage for browser-level download behavior without a Playwright page.
  • Added coverage for page-level download behavior fallback.
  • Added coverage for CDP session cleanup on download-behavior failure paths.

@volodymyr-narada volodymyr-narada changed the title [WIP] fix for windows extension autoload missing side panel fix for windows extension autoload missing side panel Jul 2, 2026
side_panel_page = launch_browser_result.side_panel_page

await self._fix_download_behavior(side_panel_page)
if side_panel_page is not None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This conditional logic causes a problem: I tested the hello world example on Windows, and this check caused the fix download behavior call to be skipped, and the files the SDK downloaded became UUID gibberish instead of real downloads.

@zizhengtai

Copy link
Copy Markdown
Contributor

Could you try updating the Playwright version used by the narada package to see if side panel page is more reliably recognized?

Otherwise, we need to fully switch to always using CDP target for the side panel page to make sure tasks like fixing download behavior are still always performed. Otherwise, the user gets a partially functional environment.

@zizhengtai zizhengtai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found three issues that I think should be addressed before merging. Details are inline.

side_panel_page,
has_side_panel_target,
) = await _find_side_panel_page_or_target(browser, side_panel_url)
if side_panel_page is not None or has_side_panel_target:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P1] Keep reset_agent_state() working for target-only side panels. When this branch exits because has_side_panel_target is true while side_panel_page is None, initialization succeeds with no side-panel entry in self._context.pages. reset_agent_state() still performs next(...) over those pages, so in the exact Windows state this PR enables it deterministically raises RuntimeError: coroutine raised StopIteration. Please retain the target ID and reload it over CDP (or otherwise make reset independent of a Playwright Page) and add a target-only initialization/reset test.

try:
cdp_session = await browser.new_browser_cdp_session()
await cdp_session.send(
"Browser.setDownloadBehavior", {"behavior": "default"}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Apply the download reset to the matched browser context. Browser.setDownloadBehavior without browserContextId affects only the default context. Because _find_page_by_url() deliberately scans every context, this command can succeed for context 0, skip the page-scoped fallback, and leave a side panel found in another context using Playwright's allowAndName behavior (UUID filenames). When a Page exists, use its page-scoped session; for a target-only match, retain targetInfo.browserContextId and pass it here.

)
)
)
except NaradaTimeoutError:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Put an overall bound on initialization retries. This 30-second initialization wait is now retried through the ten-attempt CDP loop. With the 15-second navigation timeout and three-second sleep, a permanently stalled initialization page can keep start() running for roughly 5.5–7.75 minutes instead of failing after about 30 seconds. Please use a smaller, independent initialization retry count or one overall deadline, and add an always-timeout test that asserts the bound.

Copy link
Copy Markdown
Contributor

Implemented the four follow-up fixes in b832693:

  • The launch retry loop now counts only actual browser-window-ID timeouts. Once the ID is known, retries poll only for the side panel and never rerun the ID wait.
  • Target-only resets now await the nested CDP command responses and the main frame's load event for a new loader. CDP errors/detachment are surfaced, and discovery/reload plus best-effort cleanup are bounded so reset cannot hang indefinitely.
  • reset_agent_state() rediscovers the current side-panel Page or raw target on every call instead of retaining a stale match from initialization.
  • Browser-level download behavior now distinguishes the default browser context from created contexts: the default context ID is omitted (as Chrome requires), while non-default targets retain their scoped browserContextId; a scoped failure is not retried unscoped.

I also did a full self-review of the branch and added regression coverage for response/lifecycle event ordering, stale lifecycle filtering, target detachment, timeout/listener cleanup, rediscovery, retry bounds, and default/non-default download behavior.

Validation:

  • 87 passed
  • Ruff lint and format checks pass
  • isolated headless-Chrome protocol probe passes for default-context downloads, non-default-context downloads, and raw-target reload
  • all PR checks are green

@zizhengtai zizhengtai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed the latest head (b832693) after the follow-up fixes. The four issues are addressed, the full self-review found no remaining findings, and both local validation and all PR checks pass.

@zizhengtai zizhengtai merged commit 2c85baa into main Jul 10, 2026
5 checks passed
@zizhengtai zizhengtai deleted the volodymyr/windows-extension-autoload-side-panel-fix branch July 10, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants