feat(vom): vom fix

This commit is contained in:
Ljy-0827
2026-07-27 14:53:10 +08:00
parent 4fb2e331a6
commit c002c394e6
6 changed files with 154 additions and 24 deletions
@@ -424,6 +424,73 @@ describe("buildVomScene", () => {
);
});
it("annotates external links from the explicit page URL without reading window.location", () => {
const axNodes: CdpAxNode[] = [
{
nodeId: "1",
role: { type: "role", value: "RootWebArea" },
backendDOMNodeId: 100,
childIds: ["2", "3"],
},
{
nodeId: "2",
parentId: "1",
role: { type: "role", value: "Link" },
name: { type: "computedString", value: "Docs" },
backendDOMNodeId: 200,
},
{
nodeId: "3",
parentId: "1",
role: { type: "role", value: "link" },
name: { type: "computedString", value: "Local" },
backendDOMNodeId: 300,
},
];
const captured: CapturedViewModel = {
viewport: { width: 1000, height: 800 },
iframeNodes: new Map(),
excludedBackendNodeIds: new Set(),
nodes: [
{
backendNodeId: 100,
parentBackendNodeId: null,
tag: "body",
attrs: {},
rect: { x: 0, y: 0, w: 1000, h: 800 },
paintOrder: 0,
position: "static",
pointerEvents: "auto",
},
{
backendNodeId: 200,
parentBackendNodeId: 100,
tag: "a",
attrs: { href: "https://docs.example.org/start" },
rect: { x: 10, y: 10, w: 60, h: 20 },
paintOrder: 1,
position: "static",
pointerEvents: "auto",
},
{
backendNodeId: 300,
parentBackendNodeId: 100,
tag: "a",
attrs: { href: "/local" },
rect: { x: 80, y: 10, w: 60, h: 20 },
paintOrder: 2,
position: "static",
pointerEvents: "auto",
},
],
};
const scene = buildVomScene(axNodes, captured, { pageUrl: "https://app.example.test/home" });
expect(scene.nodes.find((node) => node.id === 200)?.href).toBe("docs.example.org");
expect(scene.nodes.find((node) => node.id === 300)?.href).toBeUndefined();
});
it("uses the nearest backend AX ancestor as parentId", () => {
const axNodes: CdpAxNode[] = [
{
+27 -11
View File
@@ -969,7 +969,29 @@ function axNodeInOverlaySubtree(
);
}
export function buildVomScene(axNodes: CdpAxNode[], captured: CapturedViewModel): VomScene {
export interface BuildVomSceneOptions {
pageUrl?: string;
}
function externalHrefHost(
href: string | undefined,
pageUrl: string | undefined,
): string | undefined {
if (!href || !pageUrl) return undefined;
try {
const page = new URL(pageUrl);
const target = new URL(href, page);
return target.origin !== page.origin ? target.hostname : undefined;
} catch {
return undefined;
}
}
export function buildVomScene(
axNodes: CdpAxNode[],
captured: CapturedViewModel,
options: BuildVomSceneOptions = {},
): VomScene {
const signals = capturedOnlySignals(captured.nodes);
const { capturedByBackendId } = signals;
const axSignals = buildAxSignals(axNodes);
@@ -993,15 +1015,9 @@ export function buildVomScene(axNodes: CdpAxNode[], captured: CapturedViewModel)
// For link nodes, attach external hostname so the renderer can annotate it.
// Relative hrefs and same-origin hrefs are omitted to avoid noise.
if (role === "link" && capturedNode?.attrs.href) {
try {
const target = new URL(capturedNode.attrs.href, window.location.href);
if (target.origin !== window.location.origin) {
vomNode.href = target.hostname;
}
} catch {
// Relative or non-URL href (e.g. javascript:) — skip
}
if (role?.toLowerCase() === "link") {
const hrefHost = externalHrefHost(capturedNode?.attrs.href, options.pageUrl);
if (hrefHost) vomNode.href = hrefHost;
}
seenBackendIds.add(axNode.backendDOMNodeId);
@@ -1243,7 +1259,7 @@ async function handleVomObservation(
);
const axNodes = result.nodes ?? [];
const captured = await captureForVom(deps.cdp, target.tabId, conditionalSurfaceProbe);
const scene = buildVomScene(axNodes, captured);
const scene = buildVomScene(axNodes, captured, { pageUrl: target.url });
const rendered = renderVom(scene, {
maxDepth: params.max_depth,
maxTokens: params.max_tokens,