Files
reverse-engineering-anything/src/domain/webRuntime.ts
T
Paritosh LahreandClaude Opus 5.5 8db538a7ca fix(browser): keep negative script columns Chrome reports (#8684)
* fix(browser): keep negative script columns Chrome reports

Chrome reported a Debugger.scriptParsed event with startColumn -60 and
endColumn -15 for an inline script in WordPress's helper frame. CDP declares
these columns as plain integers, but REA's admission required min(0), so one
script failed the whole observe-web-execution run with unreadable_output.

CDP resource columns now share one representation, cdpResourceColumnSchema,
for script start/end columns, initiator call frames, parser initiators and
event listeners. Positions on a script's first line include its start column,
so keeping the reported values keeps the documented script-relative
conversion consistent. The domain contract and web runtime docs state that a
negative column does not locate a column in the resource.

Fixes #8674

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(browser): apply the CDP column policy to passive capture

inspect_web_page and discover_webmcp_tools still read CDP columns through
cdpSourceCoordinates, whose own >= 0 check turned a negative column into
null and reported invalid_protocol_value. WebMCP discovery also dropped the
tool's whole registration_source.

cdpResourceColumnSchema now lives with the domain's CDP observation schemas.
Runtime admission, the passive coordinate helper and the runtime, console,
initiator and WebMCP output schemas all use it. Lines keep their
non-negative rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:50:00 +08:00

104 lines
4.6 KiB
TypeScript

import { z } from "zod";
import {
browserAllowedOriginsSchema,
browserEndpointSchema,
} from "./browserObservation.js";
import {
CDP_CLEANUP_GRACE_MS,
DEFAULT_CDP_COMMAND_TIMEOUT_MS,
webRuntimeCommandTimeoutSchema,
} from "./browserDeadline.js";
import { jsonObjectSchema } from "./jsonValue.js";
import { cdpResourceColumnSchema } from "./cdpObservationSchemas.js";
// CDP positions are zero-based and relative to the resource named by url.
// With a script ID that is the script's enclosing resource: the script file,
// or the HTML document for an inline script. An empty script ID marks a
// position a request initiator reported, such as parser markup or a module
// import in a script file; it still locates url, but has no session-script
// association to join captured source or resource_start.
// trace_web_source_location takes one-based, script-relative lines, so the
// advertised units keep callers from passing a position off by one or by an
// inline script's offset. Columns are kept as reported even when Chrome reports
// a negative one, so subtracting resource_start's column stays consistent.
const SCRIPT_ID =
"CDP session script ID; empty for a position a request initiator reported, which has no session-script association for joining captured source or resource_start.";
const RESOURCE_LINE =
"Zero-based line in the resource named by url, as reported by CDP: the script file for an external script (including a module-import initiator), or the HTML document for an inline script or parser markup.";
const RESOURCE_COLUMN =
"Zero-based UTF-16 column on that line of the resource named by url, as reported by CDP. Chrome can report a negative column, for example on the first line of a script whose resource_start column is negative; it then does not locate a column in url.";
const START_LINE =
"Zero-based line where the script text starts in its enclosing resource (the HTML document for an inline script).";
const START_COLUMN =
"Zero-based UTF-16 column where the script text starts on that line of its enclosing resource. Chrome reports a negative column for some inline scripts; it is kept as reported and does not locate a column in that resource.";
/** Scope for one externally owned page; omitted origins select its current origin. */
export const webRuntimeScopeSchema = z.strictObject({
cdp_endpoint: browserEndpointSchema,
allowed_origins: browserAllowedOriginsSchema,
target_id: z.string().min(1),
command_timeout_ms: webRuntimeCommandTimeoutSchema,
});
/** Source evidence is bound by the selected CDP session's script ID, never URL equality. */
export const webRuntimeSourceSchema = z.object({
script_id: z.string().min(1),
url: z.string(),
execution_context_id: z.int().nullable(),
reported_execution_context: jsonObjectSchema.nullable(),
reported_script_context_aux_data: jsonObjectSchema.nullable(),
frame_id: z.string().nullable(),
producer_hash: z.string().nullable(),
source_map_url: z.string().nullable(),
has_source_url: z.boolean().nullable(),
language: z.string().nullable(),
resource_start: z
.object({
line_number: z.int().min(0).describe(START_LINE),
column_number: cdpResourceColumnSchema.describe(START_COLUMN),
})
.nullable(),
source: z.discriminatedUnion("state", [
z.object({
state: z.literal("captured"),
text: z.string(),
sha256: z.string().regex(/^[a-f0-9]{64}$/u),
utf8_bytes: z.int().min(0),
utf16_units: z.int().min(0),
}),
z.object({
state: z.enum(["unavailable", "excluded"]),
reason: z.string().min(1),
}),
]),
});
export type WebRuntimeSource = z.infer<typeof webRuntimeSourceSchema>;
/** Producer callsite coordinates are zero-based resource lines and UTF-16 columns. */
export const webRuntimeLocationSchema = z.object({
script_id: z.string().describe(SCRIPT_ID),
url: z.string().nullable(),
line_number: z.int().min(0).describe(RESOURCE_LINE),
column_number: cdpResourceColumnSchema.nullable().describe(RESOURCE_COLUMN),
function_name: z.string().nullable(),
source_association: z.enum(["script_id", "unknown"]),
});
/** Common identity for a selected live page document. */
export const webRuntimeTargetSchema = z.object({
target_id: z.string().min(1),
origin: z.string().min(1),
initial_url: z.string(),
frame_id: z.string().min(1),
loader_id: z.string().nullable(),
});
/** Complete runtime evidence has explicit byte budgets rather than silent item truncation. */
export const WEB_RUNTIME_LIMITS = {
protocolBytes: 64 * 1024 * 1024,
retainedEventBytes: 8 * 1024 * 1024,
sourceBytes: 32 * 1024 * 1024,
commandTimeoutMs: DEFAULT_CDP_COMMAND_TIMEOUT_MS,
cleanupTimeoutMs: CDP_CLEANUP_GRACE_MS,
} as const;