| Age | Commit message (Collapse) | Author |
|
src/ goes from 84 files to 75 at the same line count — every merge below
had exactly one consumer.
cdp/event-queue.ts and cdp/pending-requests.ts become daemon/queue.ts.
I split these in the daemon-internals commit on the grounds that they
held two unrelated concerns, but the transport commit had already deleted
the second transport, so both halves had one consumer and the split
bought nothing. They also sat in cdp/ while only daemon/ used them.
cdp/transport.ts held only the CdpTransport interface after the second
transport went; five of its six consumers import it type-only, so it
moves to cdp/types.ts. cdp/evaluate.ts moves into cdp/session.ts rather
than into its lone consumer isolated-tab.ts — it reaches session.call
directly, so under src/domains/ it would trip the raw-cdp-call rule.
util/keycodes.ts and util/time.ts fold into their only callers
(keyboard.ts, page.ts); util/bytes.ts into util/truncate.ts, which
already owns output-size formatting; readpage/capture.ts and
search/extract.ts into the tools beside them; profile/picker.ts into
profile/command.ts.
Kept deliberately: mutex.ts and domains/target.ts each have their own
test suite, and folding target.ts into click.ts would make a tool file
export a helper. sharp-shim.ts is an isolation boundary carrying two
allowlisted casts. result.ts, guards.ts, js-template.ts and paths.ts are
small but have 44, 12, 8 and 3 consumers.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Unused exports it found that my own sweep missed, because I had grepped
by name and these names occur as local variables elsewhere: deadline and
the Deadline type in util/time.ts were genuinely unreferenced and are
gone; isWireMessage, resolveUserDataDir, CLEAR_SELECTION_LABEL and the
ExpandableText type are used only inside their own file and no longer
export. isWireMessage became internal when server.ts moved to the shared
serialize/deserialize codec.
Duplication: console-buffer and network-buffer were the same
capacity-bounded map with an overflow flag, sharing eviction, the
drain tail and clear() across three clone groups. That is now
cdp/record-store.ts, with tests — neither buffer had any. The three
Network follow-up events also repeated the same decode-and-find preamble;
one `update` helper covers them, and drops the redundant optional
chaining that survived a `!id` guard. formatBytes existed verbatim in
js.ts and network.ts; it is now util/bytes.ts. Duplication overall:
2.7% to 2.0%, and none of the remainder is in src.
.fallowrc.json records the two entry points the tool cannot infer — the
pi extension (declared under pi.extensions) and the daemon (spawned by
path) — without which five reachable files read as dead. sharp is
ignored because it is loaded through a variable specifier in a dynamic
import so it can stay optional. agentDir is exported solely for a test's
dynamic import, so it is suppressed by name rather than globally
enabling ignoreExportsUsedInFile, which would have hidden four of the
five findings above.
Not acted on: the "split high-impact file" suggestions for session.ts,
client.ts and ref-resolve.ts (119 LOC) are size and fan-in heuristics
with no defect behind them, and splitting them would add the indirection
this refactor set out to remove. The claim that state.ts is 100% dead is
an artifact of the missing entry point.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
ARCHITECTURE.md: the layer map gains domains/cdp-call.ts as the single
door and the helper-module/tool-module split; the "cdpCall vs
session.call" section is replaced, since snapshot.ts no longer mixes
styles — it swallows failures through cdpCall instead of around it. Rule 1
now says tool files export only tools, rule 7 names the enforced scanner
rules, and the two 250-line exceptions are recorded as largely settled
(snapshot 477 to 256, form 409 to 283). Also notes the one honest gap:
session.attach and session.windowId perform CDP calls the rules do not
match.
README: BU_CDP_WS carries its daemon-restart caveat, BH_DEBUG_CLICKS is
documented as the flag's equivalent, and the tool count matches the
registry (38 to 40).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
IdMultiplexer and EventRouter were the only classes in src/; they are now
createIdMultiplexer/createEventRouter closures like every other factory
here. The multiplexer kept {clientId, localId} per daemon id while the
bridge kept {send, clientId, localId, timer, isAttach} under the same key
— two maps of overlapping data, and mux.resolve()'s return value was
discarded at three of four call sites. One map now holds the union.
That merge fixes a real leak: removeClient cleared the multiplexer but
never the callbacks map, so a disconnected client's timers stayed armed
and later fired a response at a socket that was gone. clearClient now
cancels them, and a test pins it.
handleRequest polled every 250ms for up to 15s per request when Chrome
was down; N queued requests now await one connect signal.
server.ts uses its own protocol's serialize/deserialize instead of
hand-rolled JSON, and broadcast no longer routes through `this` — the
only `this` in the codebase, and the one thing that breaks when the
server object is destructured.
cdp/event-queue.ts held two unrelated concerns; the request/timeout half
moves to cdp/pending-requests.ts. cdp/daemon-transport.ts is the client
half of the daemon protocol, so it moves to daemon/transport.ts.
test/daemon/ needs adding to the test glob to run at all — there was no
daemon test directory before because none of this was reachable without
live Chrome.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
cdp/attach.ts and cdp/window.ts were a module each for a single
`map(await session.callBrowser(...))` one-liner that took the session as
its first argument — two files, two imports and two names for what the
session can expose itself. They are now session.attach(targetId) and
session.windowId(targetId).
session.ts also repeated `targetId ? tabs.get(targetId) : undefined`
eight times; that is now currentTab().
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
form.ts exported resolveAndCall and detailsOf to form-batch.ts,
snapshot.ts exported buildTree and collectInteractiveTargets to
ref-resolve.ts, and screenshot.ts exported captureWithCrosshair to
click.ts — so importing one tool pulled in another tool's whole module.
The shared halves move out: ax-tree.ts (the AX tree model, its
construction and the queries over it), element-call.ts (calling a
function on a resolved element, and the PageResult it returns),
screenshot-capture.ts (capture, resize, crosshair overlay). Each tool
file now holds its tools and its own presentation, nothing else.
NavOutcome stops being exported; nothing outside navigate.ts read it.
target.ts stays a module rather than folding into click.ts as planned:
it is already helper-only and has its own test suite, and moving a tested
pure function into a tool file is the thing this commit removes. Having
one consumer is not the defect.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Four routes coexisted: cdpCall, a raw client.session().call, a session
held in a local and called directly, and client.evaluateJs — and five
files mixed two or three of them. The CdpError-to-ToolErr mapping was
hand-copied at nine sites, plus a private copy in isolated-tab.ts.
domains/cdp-call.ts now owns all of it: cdpErrToToolErr, cdpCall,
cdpCallOnTarget, cdpCallBrowser, evalJs. Every other file under
src/domains goes through them, which also removes a pile of
`if (!r.success) return err({ kind: "cdp_error", ... })` boilerplate in
favour of returning the failed Result as-is.
Two scanner rules keep it that way, scoped to src/domains/ so the layer
that legitimately owns the session — cdp/ and client.ts — is unaffected.
Scoping is new to the rule model; without it the rules fired on the
transport itself.
One deliberate behaviour change: a CDP timeout now surfaces as
kind "timeout" rather than "cdp_error". isolated-tab.ts already did this
and the other 13 sites flattened it; the shared mapper uses the honest
label rather than preserving the mislabel.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
createCdpTransport was a second full WebSocket-to-Chrome implementation
reachable only through client.ts's `?? createCdpTransport()` fallback,
which production never took — every session and every automated test
passes the daemon transport. transport.ts now holds only the CdpTransport
interface, and the client requires a transport instead of inventing one.
The "Session with given id not found" match that both transports carried
becomes classifyRemoteError in cdp/errors.ts, next to the error kind it
produces.
test/manual/profile-concurrency-test.ts was the sole caller of the
fallback: it drives its own Chrome on a private port. It now spawns a
daemon per scenario with BU_CDP_WS set, and refuses to run when a daemon
is already up, since that one is bound to a different browser.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Both were documented and inert.
--browser-debug-clicks was registered as a flag and never read; the only
real switch was the undocumented BH_DEBUG_CLICKS env var. The flag now
feeds util/debug.ts, which the click tool consults, and the env var keeps
working as an equivalent.
BU_CDP_WS was read in the client and handed to transport.connect(), but
the daemon transport discards that argument and always dials the daemon
socket — and the daemon's own discovery never looked at the variable, so
setting it did nothing. The override now lives in discoverEndpoint, which
is what both processes go through, and the client's redundant read is
gone. The variable must be set before the daemon spawns.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
isInternalUrl, SPECIAL_KEYS, andThen and mapErr had no callers in src or
test. BrowserState.remoteBrowserId was parsed on restore and never
written — the field that is actually read lives on DaemonStatus.
IpcServer.disconnectClient and IpcServer.clients() were unreachable, as
was EventRouter.allClients and a write-only lineCount counter.
DAEMON_STALE_SOCKET_CLEANUP was a hardcoded true with an if around it, so
the unlink now runs unconditionally.
CdpBridge.isAlive and getSessionOwner look equally dead from src/ alone,
but test/manual/bridge-test.ts drives both — they are the only seams that
suite has, so they stay.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The harness ships browser tools; orchestrating a multi-agent research
workflow on top of them is the agent's job, not this extension's. Drops
the skill, the /deep-research command, and the web-search-researcher
subagent config.
browser_web_search and browser_read_page are untouched — the two tests
that covered their pure parsers move to test/domains/, where the test
glob still picks them up.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The scanner banned as any, as {, as unknown as, non-null assertions and
ts-ignore, but said nothing about `as SomeNamedType` — so a new cast over
CDP or JSON data could be added and CI would stay green, which is the exact
regression the refactor exists to prevent. It also could not see a cast
whose target type sits on the following line.
Adds named-cast, function-cast and trailing-cast. All six surviving
assertions in src/ are now visible to the scanner and individually
exempted with a stated reason, so the allowlist is the complete inventory.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Drops leading blank lines where a file header was removed, collapses
double blank lines, and closes the empty catch/handler blocks whose only
content had been a comment. Whitespace only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Removes suite banners, section dividers, and test-id markers that restate
the test names. Keeps one-line notes for the deleted-binary fixture order,
the never-arriving event consumer, the absent-vs-undefined key distinction,
the unexpected-method-echo rationale, and the real-browser teardown and
Local State flush quirks.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Removes install/command banners and comments restating the code. Keeps
one-line notes for the per-session profile-pin re-read, the deliberately
surviving transport on shutdown, seed-before-attach under a pin, and the
starts-with-return wrap rule. src/prompt.ts is untouched.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Removes module banners, step markers, and comments restating the code.
Keeps one-line notes for safeJs's trust boundary, the select-match null
sentinels, the TUI width clamp, the single-window tab rule, the input
synthesis quirks (char events, keyCode, mousemove, focus, compositor),
the stale AX value, ignored-wrapper recursion, and isolated-tab routing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Removes module banners and comments restating the code. Keeps one-line
notes for the ProcessSingleton user-data-dir rule, the sentinel-URL and
async-spawn-error quirks, the replicated getAgentDir, pi's tilde-expansion
mirroring, the Windows wmic removal, the macOS/Linux process-identification
quirks, the picker's undefined-means-cancel contract, and the deliberately
unpersisted browserContextId.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Removes module banners, section dividers, reference lists, and comments
restating the code. Keeps one-line notes for the Windows named-pipe
endpoint, the Windows unlink no-op, the do-not-unlink-on-rejection rule,
the `.cmd` shell requirement, shell-quoting of spaced paths, and the
in-place client promotion.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Removes module banners, section dividers, and comments restating the code.
Keeps one-line notes for the window.open/browserContextId constraint, the
seed-tab claim, the unverifiable-pin reuse rule, the SeedTargetProvider
rationale, the deliberate dialog retention, the discovery-time browser
quirks, and the guarded CDP decoder assertion.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Removes file-header banners and comments that restate the code. Keeps
one-line notes at the optional-sharp dynamic import, the sharp .default
probe, the `never` contravariance trick, the sanctioned renderCall cast,
and safeJs's interpolation rule.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The audit fixes were required to add no comments; two doc blocks slipped through
review. Removed, leaving only comments that predate this task.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Thirty findings plus six investigated-not-bugs from the read-every-file sweep of
src/, with resolution status for the six the user approved for fixing and the
live-Chrome experiment F12 still needs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
runOnElement checked only whether Runtime.callFunctionOn reached Chrome, not
whether the function it ran threw. A page-side exception left result.value
undefined, the result guard rejected it, and browser_fill, browser_focus and
browser_select_option all reported "No element matched: <ref>" for an element
that was there — sending the agent to re-snapshot a ref that was fine and
discarding the real error. The throw is now surfaced as cdp_error carrying the
exception text, which is distinguishable from both the no-match and stale-ref
paths; those still fire for a genuinely missing or stale element. The selector
path already reported throws correctly via evaluateJs.
Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f2
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
browser_fill and browser_fill_form carried separate in-page implementations that
disagreed on the same element. The worst case was silent: browser_fill on a
checkbox fired the HTMLInputElement value setter, which writes the submit value
and leaves `checked` alone, then reported status ok — a no-op the agent had no
reason to doubt. Both tools now share one engine, with browser_fill's documented
<select> rejection kept as an explicit pre-check and per-tool focus behaviour kept
as a parameter.
Deliberate, user-approved behaviour changes:
- browser_fill on a DISABLED input now returns an error instead of success. The
old success was the bug; browser_fill_form already refused it.
- browser_fill on a checkbox/radio now toggles `checked` instead of the value.
- browser_fill_form on a contenteditable now uses execCommand("insertText"),
so it drives ProseMirror/Slack-style editors as browser_fill already did.
browser_fill keeps appending interactiveDiff and republishing refs; fill_form
still does not.
Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f5
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
interactiveDiff re-assigned refs by tree position after every fill, click and
select, so any element appearing or disappearing shifted the numbering and a ref
the agent was still holding silently resolved to a different element — while the
tools kept promising that refs survive re-renders. Refs are now carried over by
backendNodeId and only genuinely new elements draw a new number.
Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f6
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The tool gate ran access() on DAEMON_SOCKET_PATH, which on Windows is the named
pipe \\.\pipe\pi-browser-daemon. Named pipes are not filesystem entries, so
access() always fails there and every browser_* call returned "Browser harness
not initialized" no matter how healthy the daemon was. spawn.ts already skipped
the identical check for this reason; that knowledge is now one shared
daemonSocketMayExist() used by both call sites, with platform and path as
parameters so the branch is testable off-platform.
Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f1
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The event loop dropped every event whose sessionId was not the active tab's, so
the per-tab console and network buffers could only ever hold data captured while
their tab was in the foreground. The filter now drops only sessions this instance
never attached, and the dialog, frameNavigated and loadEventFired arms resolve
their tab from the event's sessionId instead of assuming the active tab — without
that, a background tab's dialog would have been recorded against the foreground
one. Tabs are also registered before the domain-enable round trips, so events
arriving during attach are no longer dropped as unknown.
Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f4
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Every Target.attachToTarget mints a new sessionId and CDP domain enablement is
per-session, but switchTo only enabled domains on a tab's first visit. Switching
away and back left the refreshed session with Page, Network, Runtime and Log
disabled, so that tab silently stopped collecting console and network records and
stopped reporting dialogs and load events for the rest of the session.
Report: docs/superpowers/reports/2026-07-26-bug-audit.md#f3
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Wires a real CdpSession to a canned-response transport so domain tools can be
driven without Chrome. Unconfigured client methods return an error rather than a
success, so a test cannot pass by accident. Report:
docs/superpowers/reports/2026-07-26-bug-audit.md#f30
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
A clean tsc does not prove the absence of casts. This scans src/ for
as any, ts-ignore, non-null assertions, inline object casts, and double
casts, wired into a new npm run check that CI runs as a single step.
The escape hatch is an external allowlist module rather than an inline
comment marker: comments are not used in this codebase, and an entry
carries a count, so adding a new cast to an already-exempted file still
fails until someone raises a number that shows up in the diff. Exactly
one exemption is needed today, for the dynamic sharp import.
scripts/ was gitignored wholesale, so the two new files are un-ignored
individually and everything else dropped there stays ignored.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Types the event-queue iterator's TReturn as undefined so the terminal
result needs no cast; declares the isError field the pi runtime reads by
duck-typing; binds socket to a local const in the daemon transport;
replaces the @ts-ignore on the optional sharp import with a non-literal
specifier so src/ still typechecks where sharp was not installed.
Adds src/util/guards.ts as the single home for narrowing over unknown —
isRecord plus scalar/array readers — and converts the two private
isObject copies in cdp/types.ts and daemon/protocol.ts along with every
inline `as Record<string, unknown>` in domains/js.ts and domains/render.ts.
Gives Accessibility.getFullAXTree a real result schema, closing the last
unvalidated CDP boundary: only nodeId is required, every object carries
additionalProperties, and each AX value stays Unknown, so the tree
decodes as a whole or fails as a whole rather than being filtered.
Guards the five tool-result `details` readers, the daemon attach
response, the persisted session state, the browser-namespace flag, and
the fs errno narrowing, each feeding an already-present failure path.
The daemon-transport socket binding is not a bug fix: send() runs
synchronously inside sendWithTimeout's promise executor, so socket cannot
be reassigned between the check and the write. It removes the assertion
and makes the write immune to a later refactor that adds an await.
Two assertions survive by design and are documented in the task report:
the never->unknown re-widening in util/tool.ts, which the type-erased
tool registry makes unavoidable, and the AsyncFunction constructor lookup
in domains/js.ts, which JavaScript exposes no typed binding for.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Ten sites cast or implicitly typed JSON.parse output; JSON.parse("null")
would have satisfied all of them. parseJson returns a Result and validates
against a typebox schema. js.ts keeps unknown — user script output has no
fixed shape. daemon/bridge.ts assigned JSON.parse straight to CdpRawMessage
with no guard at all; it now uses isCdpRawMessage like the transport does.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Three review fixes on the restored fill_form:
- FILL_FN matches a <select> option by value, label or text but returns
el.value, so filling by visible label ("Canada" -> value "ca") tripped
the read-back mismatch check and reported a correctly-set field as
"Filled 0/1 fields; issues: ref e4 (value mismatch)". The mismatch test
now also accepts res.text, which FILL_FN sets only for selects, so
mismatch detection is preserved for every other field kind.
- fill_form always returned ok(), so total failure surfaced as a
successful result reading "Filled 0/4 fields". It now returns
invalid_state when okCount is 0, carrying the per-field issue list as
the error message. Partial success stays ok().
- Removed the two comment blocks this task had added to
test/manual/forms-test.ts.
Neither behaviour fix has automated coverage; the manual suite that would
exercise them still needs a live Chrome and was not run.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
domains/forms.ts was a stale fork of form.ts imported only by a manual
test. It held two working tools the registry never registered: fill_form
and set_checked. Ported both onto the current pattern and deleted the
fork; form.ts versions of the shared tools win.
The fork's tools took ref as Type.Integer (a raw CDP backendNodeId).
browser_snapshot has since moved to string refs ('e7') resolved through
the session ref map, so as written the tools were not callable at all.
Ported them onto resolveRefToObjectId and Type.String refs; the only
user-facing strings that changed are the four that documented the old
[ref=N] syntax. All page-side JS is carried byte-for-byte.
form.ts exceeded 250 lines, so fillForm moved to form-batch.ts per the
single-field/batch split. form.ts is still 425 lines — the pre-existing
three tools account for that, so the split does not reach the target.
test/manual/forms-test.ts is repointed and adapted to publish an 'eN'
ref map, but NOT executed — it needs a live Chrome. Type-checked only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
The ref-vs-x/y branch turned out to exist in one tool, not four; it is now
one helper with tests covering the half-specified-coordinate and stale-ref
cases. cdpCall folds the repeated cdp_error mapping at 24 active-session
call sites; target-scoped callOnTarget/callBrowser sites are left alone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
serialized: true was applied in registry.ts, away from the tools it
describes. Now a required concurrency field on each definition, with a
test pinning the serialized set so the classes cannot drift.
The lock check defaults to taking the mutex for any class that is not
explicitly "parallel", so a definition that bypasses defineBrowserTool at
runtime fails safe rather than racing on shared CDP state.
The test script gains --conditions=import: pi-coding-agent publishes no
"require" condition, so importing any tool definition from a CJS-loaded
test could not resolve it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Malformed or unknown events are now dropped rather than hand-narrowed;
consumers get typed params. Unknown methods are ignored, since the
transport delivers every event Chrome emits.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Adopting isFocusProbe inverted the safety default: the guard was ANDed into
the condition gating the check, so a probe result the guard rejected skipped
the check and fell through to Input.insertText — text typed into nothing,
silently, which is the exact failure the block exists to prevent. The guard
now feeds a value that defaults to { ok: false }, so a rejected shape
produces the error instead of bypassing it.
The other four guard adoptions (isFillResult, isFocusResult, isSelectResult,
isPageResult) were already fail-closed: each feeds a value whose rejection
lands on a pre-existing error branch.
Also assert x/y in the box centre test — the fields snapshot's off-viewport
filter reads — and wrap the zero-size message to the surrounding width.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
ref-resolve.ts and snapshot.ts had the same box-model unwrap, both using
data.model! to work around an untyped response. Extracted to domains/box.ts;
the typed schema makes the assertions unnecessary. Both derived the centre
identically (quad corner + half the model width/height), so centreOf keeps
that derivation; the shared zero-size rejection moved into boxOf, and
snapshot keeps its own off-viewport (x/y < 0) filter.
evalInIsolatedTab now goes through evaluateJson with the tab's own sessionId
and a caller-supplied guard; both callers evaluate expressions ending in
JSON.stringify, so they pass isJsonText rather than a check that checks
nothing. Runtime.evaluate/callFunctionOn result casts in form.ts, forms.ts
and keyboard.ts are replaced by named guards over the in-page result shapes.
Runtime.callFunctionOn's params schema gained arguments/returnByValue/
awaitPromise/userGesture: four domain call sites legitimately pass them and
typebox's additionalProperties:true does not widen the static type, so the
params type was rejecting valid CDP fields. Params schemas are type-only
(only results are compiled), so this is a compile-time widening with no
runtime effect.
tsc is green again for both tsconfigs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
|
|
Target.attachToTarget was decoded at 5 sites, Browser.getWindowForTarget
at 5, and Runtime.evaluate unwrapped at another. Each is now one typed
helper. Deletes the dead cdp/window-binding.ts.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
collapse decode into one path
session-call-test.ts previously only exercised decodeResult (Task 3's
surface, and mostly duplicating commands-test.ts) — call, callOnTarget,
callBrowser, and the session's internal req helper had zero coverage.
Add a stub CdpTransport (no socket, no browser) and test: a valid payload
decodes to typed data, a garbage payload becomes invalid_response without
throwing, a transport-level error passes through with its original kind
(not relabeled), call/callOnTarget/callBrowser route the session id
correctly, and opts.timeoutMs reaches the transport only when provided.
Also collapse the four near-identical transport-then-decode bodies (req,
call, callOnTarget, callBrowser) into one: the three public methods now
delegate to req, which is the sole place unknown ends for every CDP call
the session makes, internal or public. Public signatures are unchanged.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
call/callOnTarget/callBrowser now take a known method and return its
decoded result type. The session is where unknown ends; transport keeps
returning unknown. Removes the casts in session.ts and target-factory.ts,
including the session's own internal Target.*/Browser.* calls, which now
decode through the same path as the public call methods.
Also completes two params-schema gaps in commands.ts surfaced by this
migration: Target.createTarget was missing openerId and Runtime.evaluate
was missing userGesture, both real CDP fields src/cdp/ already sends.
tsc is red project-wide (6 errors, all in src/domains/) — the sanctioned
state between Tasks 4 and 7.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
One typebox schema per CDP command is the source of both the TS types and
the runtime validator, so they cannot drift. Malformed responses become
invalid_response errors instead of TypeErrors several frames later.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Mechanical port of the hand-rolled check() harness. Import-after-env
ordering preserved so suites still read their sandbox dirs.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
Task 1 review found three ported checks collapsed two original labels
into one test(), losing the per-assertion granularity the brief's Step 7
requires. Splits P5, P6, and P10 back into two test() blocks each, using
the original labels verbatim, with each block doing its own dynamic
import after before() sets PI_CODING_AGENT_DIR.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|
test/ was never type-checked and had no runner. Adds tsconfig.base.json
shared by a src project and a test project, an npm test script scoped to
non-manual suites, and a CI step. Ports the profile store suite as proof.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|