Skip to content

fix(browser): evict dead Profile runtime after a closed-target run failure - #323

Open
rohan911438 wants to merge 2 commits into
agentrhq:mainfrom
rohan911438:fix/browser-run-dead-context-recovery-314
Open

fix(browser): evict dead Profile runtime after a closed-target run failure#323
rohan911438 wants to merge 2 commits into
agentrhq:mainfrom
rohan911438:fix/browser-run-dead-context-recovery-314

Conversation

@rohan911438

Copy link
Copy Markdown

Description:

Summary

Fixes #314.

Raw browser run leased pages directly and ran the program with no
recovery path: CloakSessionManager.getPage() treats a lease as
healthy whenever page.isClosed() is false, but a disconnected
context/CDP session can still report that. dispatchCloakAction's
run case called runBrowserProgram with no try/catch, so a
closed-target failure never invalidated the Profile runtime the way
navigatePage/newPage already do — every subsequent raw browser
command on that Session kept leasing the same dead page.

Changes

  • Export isClosedContextError from session-manager.ts.
  • Add CloakSessionManager.evictDeadRuntime(profileId, context)
    invalidates the Profile runtime only if it still matches the caller's
    context (no-ops if it was already replaced, so it can't evict a
    healthy runtime that raced ahead of the failed lease).
  • Wrap runBrowserProgram in actions.ts's run case in try/catch:
    on the closed-target signature, evict the runtime, then rethrow.

We don't retry the program itself — it may have already caused side
effects, so blind replay isn't safe. The caller sees the original
error; the next command gets a fresh context/page instead of
repeating the same failure forever.

Test plan

  • New run-recovery.test.ts: closed-target run failure evicts the
    runtime and the next run command gets a fresh page; an
    unrelated failure does not evict.
  • npx tsc --noEmit clean.
  • Existing session-manager.test.ts / browser-run.test.ts still pass.

…ilure

raw `browser run` leased pages directly and ran the program with no
recovery path: `CloakSessionManager.getPage()` treats a lease as
healthy whenever `page.isClosed()` is false, but a disconnected
context/CDP session can still report that. `dispatchCloakAction`'s
'run' case called `runBrowserProgram` with no try/catch, so a
closed-target failure never invalidated the Profile runtime the way
`navigatePage`/`newPage` already do — every subsequent raw browser
command on that Session kept leasing the same dead page (agentrhq#314).

Wrap the run in try/catch: on the closed-target signature, evict the
Profile runtime (new `CloakSessionManager.evictDeadRuntime`, guarded
to no-op if the runtime was already replaced) so the next command
gets a fresh context/page. We don't retry the program itself here —
it may have already caused side effects, so blind replay isn't safe;
the caller sees the original error and the next command recovers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 16, 2026 16:11
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

🟠 Maintainer review suggested — low confidence

The automated review could not reach a fully supported conclusion.

This review is advisory and does not block merging.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a recovery gap in the local Cloak browser runtime where browser run could keep leasing a “dead” Playwright page/context (still reporting page.isClosed() === false) after a closed-target failure, causing repeated failures for subsequent commands in the same Session/Profile.

Changes:

  • Export isClosedContextError so other modules can reliably detect the closed-target signature.
  • Add a targeted runtime eviction method to invalidate a Profile runtime only when it matches the failing caller’s context.
  • Wrap runBrowserProgram in dispatchCloakAction’s run path to evict the dead runtime on the closed-target signature and then rethrow.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/browser/runtime/local-cloak/session-manager.ts Exports isClosedContextError and adds evictDeadRuntime for safe Profile runtime eviction.
src/browser/runtime/local-cloak/actions.ts Catches closed-target failures in run and evicts the runtime so the next command gets a fresh context/page.
src/browser/runtime/local-cloak/run-recovery.test.ts Adds regression coverage asserting eviction occurs only for closed-target failures and not for unrelated errors.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +431 to +434
evictDeadRuntime(profileId: string, context: BrowserContext): void {
const runtime = this.profiles.get(profileId);
if (runtime && runtime.context === context) this.invalidateProfileRuntime(profileId, runtime);
}
Comment on lines +47 to +52
if (command === 'Target.createTarget') {
const created = await context.newPage();
allPages.push(created);
queueMicrotask(() => emit('page', created));
return { targetId: targetIds.get(created) };
}
…double-push

Address PR review feedback on agentrhq#323:
- evictDeadRuntime looked up this.profiles by the raw profileId param,
  but the map is keyed by normalizeProfileId(...) everywhere else in
  this class. As a public method it could silently no-op on an
  un-normalized id (whitespace/case). Normalize before the lookup.
- run-recovery.test.ts's Target.createTarget mock pushed the page
  created by context.newPage() into allPages a second time —
  newPage()'s own mock already does that, so this duplicated pages in
  context.pages().

Adds a regression test proving evictDeadRuntime still finds the
runtime when called with an un-normalized profileId.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

browser run: recover when an apparently open page belongs to a dead context

2 participants