From 6925fc3f70ba8bd099383453574a8c1f7e04583c Mon Sep 17 00:00:00 2001 From: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:45:17 -0700 Subject: [PATCH] fix(cdp): return real frame ID in STARTUP getFrameTree when page exists dispatchStartupCommand hard-codes "TID-STARTUP" as the frame ID in Page.getFrameTree. When a driver connects via connectOverCDP after a real page already exists, subsequent lifecycle events (frameNavigated) use the actual page frame ID. The driver's frame tracking was initialized with "TID-STARTUP", causing a mismatch that hangs navigation. Check for an existing browser context with a target_id in dispatchStartupCommand. If present, return the real frame ID and URL. Fall back to "TID-STARTUP" only when no page exists yet. Fixes #1800 This contribution was developed with AI assistance (Claude Code + Codex). Co-Authored-By: Claude Opus 4.6 --- src/cdp/cdp.zig | 55 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index 58ed11b9..4e042fbd 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -191,6 +191,25 @@ pub fn CDPT(comptime TypeProvider: type) type { // Stagehand parses the response and error if we don't return a // correct one for this call. if (std.mem.eql(u8, method, "Page.getFrameTree")) { + // If a real page already exists (connectOverCDP flow), return + // its actual frame ID so that subsequent lifecycle events + // (frameNavigated, etc.) match the driver's frame tracking. + if (command.cdp.browser_context) |*bc| { + if (bc.target_id) |*target_id| { + return command.sendResult(.{ + .frameTree = .{ + .frame = .{ + .id = target_id, + .loaderId = "LOADERID24DD2FD56CF1EF33C965C79C", + .securityOrigin = URL_BASE, + .url = bc.getURL() orelse "about:blank", + .secureContextType = "Secure", + }, + }, + }, .{}); + } + } + // No real page yet - return the synthetic STARTUP frame ID. return command.sendResult(.{ .frameTree = .{ .frame = .{ @@ -987,3 +1006,39 @@ test "cdp: STARTUP sessionId" { try ctx.expectSentResult(null, .{ .id = 4, .index = 0, .session_id = "STARTUP" }); } } + +test "cdp: STARTUP getFrameTree returns real frame ID when page exists" { + var ctx = testing.context(); + defer ctx.deinit(); + + { + // no browser context - should return TID-STARTUP + try ctx.processMessage(.{ .id = 1, .method = "Page.getFrameTree", .sessionId = "STARTUP" }); + try ctx.expectSentResult(.{ + .frameTree = .{ + .frame = .{ + .id = "TID-STARTUP", + .loaderId = "LOADERID24DD2FD56CF1EF33C965C79C", + .url = "about:blank", + .secureContextType = "Secure", + }, + }, + }, .{ .id = 1, .session_id = "STARTUP" }); + } + + { + // browser context with target_id - should return real frame ID + _ = try ctx.loadBrowserContext(.{ .target_id = "TID-000000000X".* }); + try ctx.processMessage(.{ .id = 2, .method = "Page.getFrameTree", .sessionId = "STARTUP" }); + try ctx.expectSentResult(.{ + .frameTree = .{ + .frame = .{ + .id = "TID-000000000X", + .loaderId = "LOADERID24DD2FD56CF1EF33C965C79C", + .url = "about:blank", + .secureContextType = "Secure", + }, + }, + }, .{ .id = 2, .session_id = "STARTUP" }); + } +}