## Summary
Puppeteer already subscribes to `Audits.issueAdded`.
`overrideDevToolsGlobals` did not stop the DevTools frontend from
enabling its own `Audits` domain, so a second consumer of the same
stream was created on session setup. Against a target holding a large
retained issue backlog, that second subscription replays the entire
backlog through `IssuesManager`, logging `No handler registered for
issue code PerformanceIssue` per unsupported entry and delaying
unrelated page work.
This stubs `Audits.invoke_enable` on the agent prototype exactly the way
the Network emulation commands are already stubbed, so the redundant
subscription is never enabled. The four identical no-op command bodies
are collapsed into one `noopAgentCommand` helper while adding the
fourth.
## Why this matters
Closes#2556
The guard added in #2535 covers `PageEventSubscriber.#onIssueAdded`, the
collector's own subscription. It does not cover the frontend's
`IssuesManager` path, which is the other consumer of the same CDP event,
so the warning and the backlog replay both survive it. Fixing it at the
`invoke_enable` boundary means the frontend never receives the replay at
all, rather than filtering each issue after the fact.
## Testing
`node scripts/test.js tests/devtools/DevtoolsUtils.test.ts` -> 20
passing. `npm run build` and `npm run typecheck` both clean.
The added case builds a real 500-issue `PerformanceIssue` backlog on a
page, detaches, then constructs the target universe and asserts the
warning is never emitted while `Runtime.evaluate` still works.
It pins the defect rather than just covering the path. Reverting only
`src/devtools/DevtoolsUtils.ts`, rebuilding, and re-running:
```
warn('No handler registered for issue code PerformanceIssue') at createIssuesFromProtocolIssue
(build/third_party/devtools-frontend/front_end/models/issues_manager/IssuesManager.js:164:13)
... (repeated)
at assert2.<computed> [as neverCalledWithMatch]
✖ createTargetUniverse (1533.363458ms)
'1 subtest failed'
```
Not tested: no measurement of the tool-call timeout the report describes
on a long-lived session. The test asserts the backlog is no longer
replayed, which is the mechanism, not the end-to-end latency.
AI was used for assistance.
## Why
`SymbolizedError.fromDetails`/`fromError` in
`src/devtools/DevtoolsUtils.ts` implement the message-extraction rules
used to surface page exceptions to agent users, but were only exercised
indirectly through one browser-based test. This adds direct coverage of
the extraction permutations (222 added lines, tests only — no `src/`
changes).
## Coverage
- `fromDetails`: message taken from the exception description up to the
first stack frame; full description kept when it has no frames;
multi-line messages preserved; exception with only a value → empty
message; no exception object → details text used verbatim (including
non-"Uncaught" text); url/line/column/raw stack trace ignored for the
message; resolved stack trace and cause retained
- `fromError`: message from the error description; full description
without frames; empty-message fallback when no description exists
## Testing
- `node scripts/test.js tests/devtools/DevtoolsUtils.test.ts` → pass
(exit 0)
- `npm run test:no-build` → full suite pass (exit 0)
- `npm run check-format` → eslint + prettier clean
Co-authored-by: ZayanKhan-12 <khanzayan200@gmail.com>
`PuppeteerDevToolsConnection.send()` is declared to return a promise of
a result or an error, but it throws synchronously when the session id no
longer resolves. The generated agent code does not catch that, so it
escapes as an uncaught exception and ends the process.
A session going away while commands for it are in flight is normal
rather than exceptional. The way I kept hitting it is closing a page
while its source maps are still loading:
```
Error: Unknown session 5C39E3164EE6C2C4AB1D3788F5AE03DA
at PuppeteerDevToolsConnection.send (build/src/devtools/DevToolsConnectionAdapter.js:31:19)
at AgentPrototype.invoke [as invoke_close]
at IOModel.close
at PageResourceLoader.loadFromTarget
at async PageResourceLoader.dispatchLoad
at async PageResourceLoader.loadResource
at async loadSourceMap
```
Source maps are just the most likely way to be holding a load open long
enough to notice; any in-flight command to a session that goes away does
the same thing. In the server this means closing a tab at the wrong
moment can take everything down.
This returns an error response instead, which is how a failed command is
already reported a few lines below, so callers see it through the path
they already handle.
The other synchronous throw in that method, for `sessionId ===
undefined`, is left alone: it guards an invariant the callers are
supposed to uphold, so failing loudly there seems right. Happy to change
that too if you would rather have both consistent.
The test fails on `main` with `Error: Unknown session
session-that-went-away` and passes with the change.
Found while working on #2431, but unrelated to it and independent of
#2463.
- remove obsolete devtools page detection step
- move isolated context processing out of the browser pages fetching
- move filtering of pages out of getPages(). That should just be a
getter.
This PR introduces the HostBindingAdapter to utilize the functions
usually available to DevTools.
Additionally I moved all the DevTools related files under a `devtools`
directory to better separate the extractor logic.
The patch scripts for DevTools were moved under a function to remove the
side-effect nature of the file.
Now gets called in a the creation of the McpContext (and a before hook
in test.)