Repository navigation
feat(browser): browser-recording/v1 with frames after each agent action, keyed to the action log (FD-4971) - #633
Merged
earakely-scale merged 5 commits intoNov 12, 2026
Conversation
…on, keyed to the action log (FD-4971) The first slice of browser-recording/v1 (M8.22), with M8.23's frames as its content. The browser takes a frame of the tab an agent last acted in at the start, after each burst of its acting entries settles and its page loads, and at the end, uploading each under the recording's grant and keying it to the log entries it follows; at the end it uploads the log's lines since the start and a manifest. start_browser_recording and end_browser_recording run it and register the frames, log and manifest as one universe; the end fails the run when the log or a frame is missing. Teardown ends what a run left open. The recording grant and namespace helpers are shared with the computer recording. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…alogs and closing tabs, resets end it - An end waits up to 10 s for the entries still in flight to be written, and lists any it still doesn't hold as log_missing, which fails the end step. - A frame taken while a dialog keeps the tab from painting is the last frame the recording took of it, in its own format. - A tab that closes while its page loads is framed as the active tab. - A computer's reset or close ends its browser apps' recordings (end_all), as it ends the driver's, so their log and manifest upload first. - Errors are cut to ERROR_CHARS. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Chrome answers a capture of a tab behind the active one only once another capture of it arrives, and then answers both, so a CDP agent's own background tab lost every other frame to the capture timeout. A frame's capture still unanswered after 0.5 s is followed by a second, and the first answer is the frame. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its end read the log with an unnamed ceiling for a limit; the count of entries between its start and its end is the limit it needs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… in common Both recordings' steps now take from one module, task_steps/recordings.py: - where a recording uploads and the grant it uploads under - the error its entry keeps (why, ERROR_CHARS) - teardown's two passes over the recordings a run left open A manifest rebuilt from a browser recording's namespace now names each frame by the index and type its file is named by, with its kind and time unknown (None), rather than as action frames taken at "". The grant's room beside the frames is named, and the browser computer tests share one CDP client. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
deleted the
edgararakelyan/fd-4971-m823-browser-recording-frames
branch
November 12, 2026 17:47
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
M8.23 (FD-4971), built as the first slice of
browser-recording/v1(M8.22, FD-4970).A run can now record the website browser with frames:
Each frame is keyed to the action-log entries it follows. The recording also keeps the log's lines between its start and its end, and a manifest. Missing frames, or a missing log, fail the run as an infrastructure failure.
This is what Online-Mind2Web's exporter (M8.46) and WebJudge (M8.47) grade from.
What changes
browser-recording/v1(agentenv_protocol.website_browser.recording), on the browser's card, framework-only:max_duration_s,max_framesandframes.start {request_id, upload, frames?}records under the write namespace grantupload. A start again with the samerequest_idanswers the recording it began, so a retried step joins it.end {recording_id, upload?, reason?}ends it, under a fresh grant when one is given, and answers its manifest.end_all {reason?}ends every open recording.<recording_id>/frames/. Its manifest entry haskind(first,actionorlast),after(the entries it follows),tab,url, anderrorif it couldn't be taken.log.jsonl(the log's lines from the start's entry to the end's, actions and tab events) andmanifest.json(BrowserRecordingManifest).BrowserLog.watcherstells the recording each action as it's written.BrowserControl.frame(target, load_wait_s)captures a given tab once its page has loaded; the control now tracks each tab's commit andloadEventFired.Steps (
task_steps/browser/recording.py)start_browser_recording {env_id, name, frames=true, browser?}finds the browser as the other browser steps do: an env's child, or a multi env's computer's. It hands the browser a grant underagentenv-browser-recordings/.end_browser_recording {name}ends the recording and registers the manifest, the log and every frame as one file artifact universe.task_steps/recordings.py, holds what both recordings' steps use. That's where a recording uploads and its grant (recording_namespace,recording_grant,refuse_no_network), the error an entry keeps (why), and teardown's two passes over the recordings a run left open (end_open,register_open).kindandatare None, since they aren't known.Not in this slice: the rest of M8.22 goes into the same extension and steps later:
fps)network.har(M8.20)The end step takes
name, asend_computer_recordingdoes, rather than M8.22's sketchedstarted_by.Decisions (in chat with Edgar)
browser-recording/v1's first slice, rather than a browser setting delivered throughdata/get, since M8.22 needs the extension, its steps and its upload path anyway.Test plan
test_browser_recording.py):endanswers.end_allends everything open.loadEventFired, then captures a JPEG at quality 80. A closed tab's frame is the active tab's. A tab that answers only a second capture is framed once that one is sent.browser_recording_test.py):fail_task_on_erroris offtst/unit/task_step,task,driversandbundle: 5,615 passed, 12 skipped, plus the episode rules' test.browser_typeandbrowser_navigatebursts keyed to those calls' entries, and a last frame on the page the agent ended on.modal_vm, end to end: the chromium computer composed natively on a Modal VM, passed its probe there, and ran two Online-Mind2Web tasks ("find the latest MacBook Air's tech specs on apple.com") on Claude Haiku 5.5. The stores were throwaway S3 and ECR, and the agents ran locally.about:blankto apple.com to the specs page, each keyed to Playwright MCP's CDP commands, which are credited to the agent'sbrowser_*calls. The last frame shows the 15-inch tab the agent had just clicked. The log has 100 lines, none missing.Docs
The user guide's browser page should cover:
start_browser_recordingandend_browser_recording🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no new actionable issues were found.Summary
Adds browser recordings with frames tied to agent actions, an action log, and a manifest.
No new actionable issues were found. This review used source checks; no tests were run.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR Start[Start recording] --> First[Capture first frame] First --> Actions[Watch agent actions] Actions --> Burst[Wait for burst to settle] Burst --> Frame[Capture and upload frame] Frame --> Actions Actions --> End[End recording] End --> Upload[Upload last frame, log and manifest] Upload --> Register[Register artifact universe] Register --> Check[Reject missing frames or log entries]Reviews (6) · Last reviewed commit: "refactor(recordings): share what compute..." · Reviewed by Greptile