[PR #5368] feat(browser): wake or classify discarded CDP page targets before inspection #6900

Open
opened 2026-11-01 21:43:56 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/5368
Author: @aryankeluskar
Created: 10/20/2026
Status: 🔄 Open

Base: main ← Head: agent/wake-discarded-browser-targets


📝 Commits (1)

  • a18dd3e feat(browser): wake or classify discarded CDP page targets before inspection

📊 Changes

21 files changed (+984 additions, -2 deletions)

View changed files

📝 src/application/BrowserEvidence.ts (+15 -0)
📝 src/application/BrowserObservationPort.ts (+6 -0)
📝 src/application/BrowserObservationService.ts (+27 -0)
📝 src/browser/CdpBrowserProvider.ts (+357 -1)
➕ src/browser/CdpBrowserProviderTargetWake.test.ts (+255 -0)
📝 src/browser/CdpTargetSession.ts (+2 -1)
📝 src/cli/browserCommands.ts (+83 -0)
📝 src/cliCommandNames.ts (+1 -0)
📝 src/contracts/browserToolContracts.ts (+31 -0)
📝 src/contracts/toolEffects.ts (+7 -0)
📝 src/domain/browserObservation.ts (+30 -0)
📝 src/domain/browserObservationErrors.ts (+1 -0)
📝 src/domain/browserObservationSchemas.ts (+31 -0)
📝 src/server/registerBrowserTools.ts (+16 -0)
📝 tests/boundary/mcp/browserMcp.test.ts (+53 -0)
📝 tests/boundary/mcp/lateAbortApplicationEvidenceMcp.test.ts (+3 -0)
📝 tests/boundary/mcp/observationPartialEvidenceDelivery.test.ts (+1 -0)
📝 tests/fixtures/fakeCdpBrowser.ts (+53 -0)
📝 tests/fixtures/fakeCdpBrowserResults.ts (+1 -0)
📝 tests/fixtures/fakeCdpBrowserTypes.ts (+10 -0)

...and 1 more files

📄 Description

Summary

Implements the wake/classify prototype proposed in #5360: a bounded
wake_browser_targets browser operation (CLI: wake-browser-targets) that
classifies selected CDP page targets as awake, awake_after_reload, or
unknown before inspection, with explicit per-target failure reasons, so
browser tools stop dying on Page.enable response-deadline errors after a
session restore.

Discussion and the original failure report live in #5360. This PR does not
close it: real discarded-target revival is verified in the fake CDP lane and
left as a recorded proof gap (NOT_ENOUGH_INFO in the issue) until a live
discarded target can be exercised.

Problem and expected behavior

After a browser restart with restored tabs, Chromium can drop a tab's renderer
while list_browser_targets still reports the steady CDP target entry. Any
tool that then opens the target waits out the fixed 20 s command budget; the
observed failure on this base is
CDP command Page.enable exceeded its 20000 ms response deadline.
(see #5360).

Expected behavior: wake_browser_targets reports which selections are
responsive (awake), which were revived after one caller-opted reload
(awake_after_reload), and which remain unknown with a machine-readable
failure_reason (target_not_found | target_not_allowed | protocol_error
| disconnected | timeout), all within caller-set deadlines instead of the
fixed 20 s.

Change and scope

  • Input schema wakeBrowserTargetsInputSchema: cdp_endpoint,
    allowed_origins, target_ids (≥1; duplicates classified once),
    probe_deadline_ms (default 2000), wake_deadline_ms (default 10000),
    revive (none default | reload).
  • Probe: Page.enable raced against the probe deadline; late replies are
    consumed and cannot leak a rejection into a later run.
  • Revival: for a probe failure only, the caller-opted Page.reload is sent
    (its ack bounded by the probe deadline) and the target is re-probed within
    the wake deadline; the default revive: none never navigates anything.
  • Per-target sessions reuse the existing ownership path (authorizeCdpTarget,
    page-socket or shared-browser-socket attach); shared sockets detach between
    selections and release once after the last.
  • Out of scope: revive: "activate" (raised in #5360; browser-socket
    activation complexity — flagged there as possibly cut) and any live
    discarded-tab revival proof (see gaps below).

Follow the contribution scope.
The work stays inside the existing CDP browser-provider boundary; no new
provider. The reload effect mutates page navigation state only on explicit
caller opt-in — that scope decision is pending (draft); see #5360.

Contract and boundary impact

  • Semantic owner and earliest changed stage: browser observation (domain) —
    src/domain/browserObservation.ts input schema; downstream per tool contract.
  • Contribution scope fit and linked maintainer decision: prototype inside the
    existing browser boundary; the reload-mutation scope decision is pending,
    discussed in #5360.
  • CLI and MCP/tool-catalog contract: wake-browser-targets CLI command and
    wake_browser_targets MCP tool with input/output schemas, examples, and a
    declared effect audit (mutatesTarget: true, mutatesSession: true,
    accessesNetwork: true, mayDiscardData: true, idempotent: false).
  • Provider, bridge, target-format, or platform compatibility: CDP browser
    provider only; BrowserObservationPort.wakeTargets added (recording and
    boundary test fakes updated).
  • Evidence, artifact, provenance, or reconstruction contract: predicate
    rea.browser-target-wake; parameters carry target_ids, both deadlines,
    and revive; output schema browserTargetWakeSchema.
  • Target selection, authority, upstream behavior, and cleanup details
    (if applicable): per-target authorization with allowed_origins; bounded
    probes with late replies consumed; pre-aborted and mid-loop cancellation
    surfaces as AnalysisCancelledError; owner cleanup retains resources whose
    cleanup failed.
  • Generated metadata (docs/public/product-catalog.json), package, or
    installation impact: none tracked; CI generates and validates ignored
    catalogs; authored .agents/skills/reverse-engineer-anything/
    instructions unchanged.

Expected effects (check known effects; use documentation-only on its own).
Unchecked items do not establish absence; leave uncertainties visible.

  • Read existing files or captured data.
  • Launch or attach to a process.
  • Inject instrumentation or run scripts/code.
  • Change process memory, registers, files, or configuration. — only page
    navigation state via the caller-opted Page.reload; the default
    revive=none changes nothing (counter-asserted in tests).
  • Contact network services. — the caller-selected loopback CDP endpoint.
  • Recover protected code or work around protections.
  • Documentation or usability only; no target interaction.
  • Not sure yet; investigation or maintainer guidance needed.

Evidence and regression coverage

  • Tests added or updated:
    • src/browser/CdpBrowserProviderTargetWake.test.ts (9): awake
      classification without reload; discarded target → unknown/timeout
      with zero reloads (proved by fixture counter); reload revival →
      awake_after_reload; revival missing the wake deadline → unknown +
      reload_sent: true; target_not_found; target_not_allowed; duplicate
      selections deduped with a limitation; mixed selections classified in
      input order; pre-aborted cancellation stays explicit.
    • Fake CDP fixture: per-target discarded simulation
      (discardedTargetIds, discardedReviveDelayMs, Page.reload ack case,
      reload counter).
    • MCP boundary segment in tests/boundary/mcp/browserMcp.test.ts:
      wake_browser_targets registered, probe-only unknown, revived
      awake_after_reload, and no navigation during classify-only wake.
  • Base reproduction or other evidence: the deadline failure string recorded in
    #5360 was observed on this base against a restored Brave session before the
    change.
  • User-visible CLI/MCP output (if applicable): real-lane output below.
  • Remaining proof gaps: live revival of a genuinely discarded target is
    unmeasured (no discarded tab existed at prototype time; NOT_ENOUGH_INFO in
    #5360); the fixture models browser-process reload-ack and delayed-renderer
    behavior; revive: "activate" is not implemented.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable. — measured
    statuses in tests; fixture behavior declared as simulation.
  • Artifact identity, source provenance, and failed attempts remain
    preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain
    visible. — unknown is a first-class result; limitation strings report
    stale selections.

Validation performed

  • npm run check — typecheck, lint:built, format:check, knip,
    metadata:check, verify:architecture-guards: all green (fixed a knip unused
    export and a max-lines-per-function lint error on the way).
  • npm run verify:test-discovery — all 1715 test files discovered exactly once.
  • npm run test:fast (source group) — 689 files, 11024 passed, 15 skipped.
  • npx vitest run --project mcp-boundary tests/boundary/mcp/browserMcp.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts tests/boundary/mcp/electronMcp.test.ts tests/boundary/mcp/webBundleContainmentMcp.test.ts —
    29 passed (after npm run test:prepare; the same slice fails identically
    on the pristine base without generated artifacts).
  • npx vitest run --project cli-boundary tests/boundary/cli/browserCli.test.ts tests/boundary/cli/browserRedirectShapeCompleteness.test.ts —
    12 passed.
  • npm run docs:check — green.
  • npm run test:prepare, npm run build:cached — green; no tracked
    generated diffs.
  • Live real-provider lane (Brave 155.1.97.59 / Chrome 155.0.8059.85, all tabs
    awake), new CLI against http://127.0.0.1:9222:
    • node scripts/rea.mjs wake-browser-targets http://127.0.0.1:9222 04550BCE1A699B8A1E38E1CA5DA4ABF8 --json →
      "status": "awake", "elapsed_ms": 1, "reload_sent": false with the
      probe-only limitation string.
    • same with --revive reload → still awake, reload_sent: false (the
      opt-in reload only fires on probe failure).
    • node scripts/rea.mjs wake-browser-targets http://127.0.0.1:9222 00000000000000000000000000000000 --json →
      unknown + "failure_reason": "target_not_found".

Compatibility, safety, and release

  • Breaking changes or migration steps: none; additive tool and an additive
    required method on the provider port (internal interface; bundled test
    fakes updated).
  • Real Hopper/Ghidra, browser, or OS coverage: live awake, authorization, and
    cancellation paths on Brave/Chrome 155 (macOS); discarded/revival path via
    the fake CDP lane.
  • Package or release metadata impact: none tracked.
  • Security, privacy, process, or containment review: loopback-only endpoint
    schema unchanged; no JavaScript evaluation, no storage access, no
    configuration change; navigation only on explicit revive: reload;
    origin authorization enforced per selection.

Author readiness

Human owner:

  • I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work.
  • I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps.
  • I declare this PR ready for initial maintainer review.

Before merge

  • This PR addresses a concrete problem or an agreed enhancement.
  • Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/5368 **Author:** [@aryankeluskar](https://github.com/aryankeluskar) **Created:** 10/20/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `agent/wake-discarded-browser-targets` --- ### 📝 Commits (1) - [`a18dd3e`](https://github.com/morluto/rea/commit/a18dd3e7a6c5ea0a1fb5440544d340dd0f19edbe) feat(browser): wake or classify discarded CDP page targets before inspection ### 📊 Changes **21 files changed** (+984 additions, -2 deletions) <details> <summary>View changed files</summary> 📝 `src/application/BrowserEvidence.ts` (+15 -0) 📝 `src/application/BrowserObservationPort.ts` (+6 -0) 📝 `src/application/BrowserObservationService.ts` (+27 -0) 📝 `src/browser/CdpBrowserProvider.ts` (+357 -1) ➕ `src/browser/CdpBrowserProviderTargetWake.test.ts` (+255 -0) 📝 `src/browser/CdpTargetSession.ts` (+2 -1) 📝 `src/cli/browserCommands.ts` (+83 -0) 📝 `src/cliCommandNames.ts` (+1 -0) 📝 `src/contracts/browserToolContracts.ts` (+31 -0) 📝 `src/contracts/toolEffects.ts` (+7 -0) 📝 `src/domain/browserObservation.ts` (+30 -0) 📝 `src/domain/browserObservationErrors.ts` (+1 -0) 📝 `src/domain/browserObservationSchemas.ts` (+31 -0) 📝 `src/server/registerBrowserTools.ts` (+16 -0) 📝 `tests/boundary/mcp/browserMcp.test.ts` (+53 -0) 📝 `tests/boundary/mcp/lateAbortApplicationEvidenceMcp.test.ts` (+3 -0) 📝 `tests/boundary/mcp/observationPartialEvidenceDelivery.test.ts` (+1 -0) 📝 `tests/fixtures/fakeCdpBrowser.ts` (+53 -0) 📝 `tests/fixtures/fakeCdpBrowserResults.ts` (+1 -0) 📝 `tests/fixtures/fakeCdpBrowserTypes.ts` (+10 -0) _...and 1 more files_ </details> ### 📄 Description ## Summary Implements the wake/classify prototype proposed in #5360: a bounded `wake_browser_targets` browser operation (CLI: `wake-browser-targets`) that classifies selected CDP page targets as `awake`, `awake_after_reload`, or `unknown` before inspection, with explicit per-target failure reasons, so browser tools stop dying on `Page.enable` response-deadline errors after a session restore. Discussion and the original failure report live in #5360. This PR does not close it: real discarded-target revival is verified in the fake CDP lane and left as a recorded proof gap (NOT_ENOUGH_INFO in the issue) until a live discarded target can be exercised. ## Problem and expected behavior After a browser restart with restored tabs, Chromium can drop a tab's renderer while `list_browser_targets` still reports the steady CDP target entry. Any tool that then opens the target waits out the fixed 20 s command budget; the observed failure on this base is `CDP command Page.enable exceeded its 20000 ms response deadline.` (see #5360). Expected behavior: `wake_browser_targets` reports which selections are responsive (`awake`), which were revived after one caller-opted reload (`awake_after_reload`), and which remain `unknown` with a machine-readable `failure_reason` (`target_not_found` | `target_not_allowed` | `protocol_error` | `disconnected` | `timeout`), all within caller-set deadlines instead of the fixed 20 s. ## Change and scope - Input schema `wakeBrowserTargetsInputSchema`: `cdp_endpoint`, `allowed_origins`, `target_ids` (≥1; duplicates classified once), `probe_deadline_ms` (default 2000), `wake_deadline_ms` (default 10000), `revive` (`none` default | `reload`). - Probe: `Page.enable` raced against the probe deadline; late replies are consumed and cannot leak a rejection into a later run. - Revival: for a probe failure only, the caller-opted `Page.reload` is sent (its ack bounded by the probe deadline) and the target is re-probed within the wake deadline; the default `revive: none` never navigates anything. - Per-target sessions reuse the existing ownership path (`authorizeCdpTarget`, page-socket or shared-browser-socket attach); shared sockets detach between selections and release once after the last. - Out of scope: `revive: "activate"` (raised in #5360; browser-socket activation complexity — flagged there as possibly cut) and any live discarded-tab revival proof (see gaps below). Follow the [contribution scope](https://github.com/morluto/rea/blob/main/CONTRIBUTING.md#contribution-scope). The work stays inside the existing CDP browser-provider boundary; no new provider. The reload effect mutates page navigation state only on explicit caller opt-in — that scope decision is pending (draft); see #5360. ## Contract and boundary impact - Semantic owner and earliest changed stage: browser observation (domain) — `src/domain/browserObservation.ts` input schema; downstream per tool contract. - Contribution scope fit and linked maintainer decision: prototype inside the existing browser boundary; the reload-mutation scope decision is pending, discussed in #5360. - CLI and MCP/tool-catalog contract: `wake-browser-targets` CLI command and `wake_browser_targets` MCP tool with input/output schemas, examples, and a declared effect audit (`mutatesTarget: true`, `mutatesSession: true`, `accessesNetwork: true`, `mayDiscardData: true`, `idempotent: false`). - Provider, bridge, target-format, or platform compatibility: CDP browser provider only; `BrowserObservationPort.wakeTargets` added (recording and boundary test fakes updated). - Evidence, artifact, provenance, or reconstruction contract: predicate `rea.browser-target-wake`; parameters carry `target_ids`, both deadlines, and `revive`; output schema `browserTargetWakeSchema`. - Target selection, authority, upstream behavior, and cleanup details (if applicable): per-target authorization with `allowed_origins`; bounded probes with late replies consumed; pre-aborted and mid-loop cancellation surfaces as `AnalysisCancelledError`; owner cleanup retains resources whose cleanup failed. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact: none tracked; CI generates and validates ignored catalogs; authored `.agents/skills/reverse-engineer-anything/` instructions unchanged. Expected effects (check known effects; use documentation-only on its own). Unchecked items do not establish absence; leave uncertainties visible. - [ ] Read existing files or captured data. - [ ] Launch or attach to a process. - [ ] Inject instrumentation or run scripts/code. - [x] Change process memory, registers, files, or configuration. — only page navigation state via the caller-opted `Page.reload`; the default `revive=none` changes nothing (counter-asserted in tests). - [x] Contact network services. — the caller-selected loopback CDP endpoint. - [ ] Recover protected code or work around protections. - [ ] Documentation or usability only; no target interaction. - [ ] Not sure yet; investigation or maintainer guidance needed. ## Evidence and regression coverage - Tests added or updated: - `src/browser/CdpBrowserProviderTargetWake.test.ts` (9): awake classification without reload; discarded target → `unknown`/`timeout` with zero reloads (proved by fixture counter); reload revival → `awake_after_reload`; revival missing the wake deadline → `unknown` + `reload_sent: true`; `target_not_found`; `target_not_allowed`; duplicate selections deduped with a limitation; mixed selections classified in input order; pre-aborted cancellation stays explicit. - Fake CDP fixture: per-target discarded simulation (`discardedTargetIds`, `discardedReviveDelayMs`, `Page.reload` ack case, reload counter). - MCP boundary segment in `tests/boundary/mcp/browserMcp.test.ts`: `wake_browser_targets` registered, probe-only `unknown`, revived `awake_after_reload`, and no navigation during classify-only wake. - Base reproduction or other evidence: the deadline failure string recorded in #5360 was observed on this base against a restored Brave session before the change. - User-visible CLI/MCP output (if applicable): real-lane output below. - Remaining proof gaps: live revival of a genuinely discarded target is unmeasured (no discarded tab existed at prototype time; NOT_ENOUGH_INFO in #5360); the fixture models browser-process reload-ack and delayed-renderer behavior; `revive: "activate"` is not implemented. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. — measured statuses in tests; fixture behavior declared as simulation. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. — `unknown` is a first-class result; limitation strings report stale selections. ## Validation performed - `npm run check` — typecheck, lint:built, format:check, knip, metadata:check, verify:architecture-guards: all green (fixed a knip unused export and a max-lines-per-function lint error on the way). - `npm run verify:test-discovery` — all 1715 test files discovered exactly once. - `npm run test:fast` (source group) — 689 files, 11024 passed, 15 skipped. - `npx vitest run --project mcp-boundary tests/boundary/mcp/browserMcp.test.ts tests/boundary/mcp/toolSchemaValidity.test.ts tests/boundary/mcp/electronMcp.test.ts tests/boundary/mcp/webBundleContainmentMcp.test.ts` — 29 passed (after `npm run test:prepare`; the same slice fails identically on the pristine base without generated artifacts). - `npx vitest run --project cli-boundary tests/boundary/cli/browserCli.test.ts tests/boundary/cli/browserRedirectShapeCompleteness.test.ts` — 12 passed. - `npm run docs:check` — green. - `npm run test:prepare`, `npm run build:cached` — green; no tracked generated diffs. - Live real-provider lane (Brave 155.1.97.59 / Chrome 155.0.8059.85, all tabs awake), new CLI against <http://127.0.0.1:9222>: - `node scripts/rea.mjs wake-browser-targets http://127.0.0.1:9222 04550BCE1A699B8A1E38E1CA5DA4ABF8 --json` → `"status": "awake", "elapsed_ms": 1, "reload_sent": false` with the probe-only limitation string. - same with `--revive reload` → still `awake`, `reload_sent: false` (the opt-in reload only fires on probe failure). - `node scripts/rea.mjs wake-browser-targets http://127.0.0.1:9222 00000000000000000000000000000000 --json` → `unknown` + `"failure_reason": "target_not_found"`. ## Compatibility, safety, and release - Breaking changes or migration steps: none; additive tool and an additive required method on the provider port (internal interface; bundled test fakes updated). - Real Hopper/Ghidra, browser, or OS coverage: live awake, authorization, and cancellation paths on Brave/Chrome 155 (macOS); discarded/revival path via the fake CDP lane. - Package or release metadata impact: none tracked. - Security, privacy, process, or containment review: loopback-only endpoint schema unchanged; no JavaScript evaluation, no storage access, no configuration change; navigation only on explicit `revive: reload`; origin authorization enforced per selection. ## Author readiness <!-- The human owner completes these declarations before requesting review. The PR author is the owner by default. A different owner must explicitly accept ownership on this PR. AI agents must leave these declarations to a human to dictate to them or there could be catastrophic consequences for many users of REA. Draft PRs may leave them unchecked. These declarations record initial readiness when opening the PR for review; later pushes do not require a new human sign-off. Keep the description and validation evidence current as the work changes. See CONTRIBUTING.md#pull-request-ownership-and-readiness. --> Human owner: <!-- @GitHub-handle --> - [ ] I reviewed the final diff and relevant existing code, understand the change and its edge cases, and take responsibility for the complete PR, including AI-assisted work. - [ ] I checked the evidence above, confirmed it supports the current change, and recorded meaningful validation gaps. - [ ] I declare this PR ready for initial maintainer review. ## Before merge <!-- Draft PRs may leave pending items unchecked. Resolve material scope and authority questions before integration. --> - [ ] This PR addresses a concrete problem or an agreed enhancement. - [ ] Material scope and authority questions are resolved, and any required maintainer scope decision covers the final diff. - [ ] The PR has one focused outcome and the title follows `type(scope): outcome`. - [ ] Related issue is linked, or the reason for not linking one is stated above. - [ ] Tests cover changed observable behavior and meaningful failure paths. - [ ] Owning docs, contracts, and generated metadata are updated where needed. - [ ] User-visible CLI/MCP changes include representative output. - [ ] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#6900
No description provided.