feat(core): add setInputFiles to act() and observe()#2316
Conversation
🦋 Changeset detectedLatest commit: ef2bc4d The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
This PR is from an external contributor and must be approved by a stagehand team member with write access before CI can run. |
There was a problem hiding this comment.
6 issues found across 25 files
Confidence score: 3/5
- The highest-risk area is
packages/core/lib/prompt.ts(act()) together withpackages/core/lib/v3/handlers/handlerUtils/fileUploadActUtils.ts: lookup-style phrasing can be misrouted intosetInputFiles, while non-upload|attachwording (e.g., select/choose) can miss the intended local upload path, creating inconsistent and potentially wrong file actions for users — tighten the act/step-two prompt gating and broaden intent matching before merging. - In
packages/core/lib/v3/understudy/a11y/snapshot/a11yTree.ts, scoped observe/extract can fall back to a full-page snapshot when a selector targets a file input omitted from the AX tree, which can cause the model to act on the wrong DOM context — persist/use the resolved backend id even without an AX-node match so scoped targeting is preserved. packages/core/lib/v3/handlers/handlerUtils/fileUploadActUtils.tslooksLikeFilePath()is overly broad (any dotted token), so normal values like emails/domains/versions may be treated as file paths and pushact()down invalid upload handling — require stricter path signals (extension/path separators or existence checks) before routing as upload.packages/core/tests/unit/timeout-handlers.test.tscurrently allows false positives by not asserting the returned result in two cases, so regressions (success: false) could ship undetected;packages/evals/tasks/bench/act/act_file_upload_variables.tsalso has minor temp-file cleanup leakage on teardown failure — add explicit result assertions and move fixture cleanup into afinallyblock to de-risk merge.
Architecture diagram
sequenceDiagram
participant User
participant Stagehand as Stagehand V3
participant ActCache as act cache
participant RemoteAPI as Remote observe/act
participant Snapshot as Snapshot/A11y pipeline
participant FileUtils as fileUploadActUtils
participant ActHandler as ActHandler
participant Locator as page.locator
User->>Stagehand: act("upload %resume% to the resume field", {variables: {resume: "/tmp/resume.pdf"}})
Stagehand->>Stagehand: shouldResolveFileUploadLocally(instruction, variables)
Note over Stagehand: detects upload verb + file path variables<br/>(NEW: fileUploadActUtils)
alt Hosted + file upload (isFileUploadAct = true)
Stagehand->>ActCache: skip (bypass cache for uploads)
Stagehand->>RemoteAPI: observe(instruction)
RemoteAPI->>Snapshot: getTree(focusFrame)
Note over Snapshot: NEW: preserve file inputs in a11y tree<br/>(inputTypeMap, appendMissingFileInputNodes)
Snapshot-->>RemoteAPI: accessibility tree with setInputFiles actions
RemoteAPI-->>Stagehand: actions[] (including setInputFiles)
Stagehand->>FileUtils: selectFileUploadAction(actions, instruction, variables)
Note over FileUtils: disambiguates multiple file inputs<br/>(throws if ambiguous)
FileUtils-->>Stagehand: selected action (e.g. "xpath=//input#resume")
Stagehand->>ActHandler: takeDeterministicAction(action, page, timeout, llmClient, variables, instruction)
ActHandler->>FileUtils: resolveSetInputFilesArguments(action, variables, instruction)
Note over FileUtils: substitutes %resume% → "/tmp/resume.pdf"<br/>or infers from description/instruction
FileUtils-->>ActHandler: resolved file paths ["/tmp/resume.pdf"]
ActHandler->>Locator: setInputFiles(["resume.pdf"])
Locator-->>ActHandler: success
ActHandler-->>Stagehand: ActResult {success: true, method: "setInputFiles"}
else Local execution or non-upload instruction
Stagehand->>ActCache: prepareContext (if enabled)
Stagehand->>RemoteAPI: act(input, options) — normal path
Note over RemoteAPI: LLM inference uses updated prompt<br/>(NEW: prefers setInputFiles for file inputs)
RemoteAPI-->>Stagehand: ActResult
end
Stagehand-->>User: ActResult
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
1 issue found across 7 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…tilities. Update imports and streamline file upload action resolution in `act()` and `observe()`. Add comprehensive tests for new file upload routing functionalities.
Fixes #972
Thanks @shrey150 for the base #1718 for the snapshot/a11y groundwork there.
It's worked in a real job application flow - https://www.browserbase.com/sessions/a18ed533-de39-4684-b2bf-eaf8faeb04d3
This PR takes the next step
setInputFilesas a first-class AI action so you can upload files throughact()without manually unpacking an observe result and callingpage.locator().setInputFiles().why
observe()on file inputs was returningclickon a wrapper div instead of the actual<input type="file">(see #972). Even after the observe context fix in #1718, there was still no way to say "upload this file" in oneact()call — you had to wire up the selector yourself.On Browserbase, remote
act()also can't read files off your laptop, so uploads need a local execution path even when observe runs server-side.what changed
setInputFilesto the supported action methods in inference + prompts (act + observe)input[type]metadata through the snapshot pipeline; keep file inputs during a11y pruning and synthesize them when AX drops them (carried over from Fix observe context for file inputs; add upload eval/example #1718)fileUploadActUtils.ts: detect upload instructions with file-path variables, resolve%key%placeholders, disambiguate when multiple file inputs matchobserve()to find the input, localsetInputFilesto push the file from the developer machineobserve_file_uploadsbench eval to assertmethod === "setInputFiles"act_file_upload_variablesbench eval — ran it on Browserbase, session360fc05f-30b1-4a2c-a1f5-e0597bbfd1cbact.mdx@browserbasehq/stagehandUsage:
test plan
file-upload-act-utils.test.ts,v3-file-upload-act.test.ts, snapshot/a11y tests, prompt testssetinputfiles.spec.ts(locator, deepLocator iframe, act with variables, payload objects)act_file_upload_variables— green on Browserbase (session)pnpm --filter @browserbasehq/stagehand test(unit)pnpm --filter @browserbasehq/stagehand test:integration(if you want the browser suite)Summary by cubic
Adds file upload support to
act()andobserve()via the newsetInputFilesaction with smarter variable resolution, input disambiguation, and local execution when using the hosted API. This improves reliability and makes uploads a first-class action in@browserbasehq/stagehand.New Features
setInputFilesinact()/observe(); accepts one or more file paths or%var%placeholders.%tokens%and whole-word variable names; supports existing extensionless paths; clear errors when multiple inputs tie.observe()remotely and performssetInputFileslocally; also executes locally when anActionwithmethod: "setInputFiles"is passed.setInputFilesfor “input, file” (neverclick) and pass file paths/placeholders; schema descriptions updated; docs updated with an uploads section; new bench evalact_file_upload_variables.Bug Fixes
<input type="file">(including within scoped trees) soobserve()targets the real input instead of wrappers.input[type]and XPath metadata and respects scoping/ignored nodes, improving file input detection and upload targeting.Written for commit ef2bc4d. Summary will update on new commits.