Skip to content

feat(compact): expose upload_file - #2678

Merged
esokullu merged 6 commits into
webbrain-one:mainfrom
alectimison-maker:codex/compact-upload-file
Aug 6, 2026
Merged

feat(compact): expose upload_file#2678
esokullu merged 6 commits into
webbrain-one:mainfrom
alectimison-maker:codex/compact-upload-file

Conversation

@alectimison-maker

@alectimison-maker alectimison-maker commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Problem

Compact Act receives user-attachment handles, but it could not attach them to a page file input. Exposing upload_file alone was not sufficient: an ambiguous selector latches the handler until get_interactive_elements returns a verified file-input selector, and Chrome's full upload schema also exposes local-path and download inputs that Compact cannot safely source.

Closes #413.

Change

  • expose upload_file and its get_interactive_elements recovery path in Compact Act for Chrome and Firefox
  • derive the Compact upload schema from the platform tool and remove downloadId and filePath
  • document direct attachment, exact-selector recovery, one guarded lazy-widget initializer click, and post-attachment verification in both Compact prompts
  • cover catalog/schema/prompt coupling and the corrected retry after ambiguous-selector recovery

Chrome Compact accepts a current user attachmentId. Firefox accepts a current attachmentId or opens WebBrain's existing user-controlled picker. Ask remains read-only, and Compact still has no download tools.

Safety and scope

The schema override deletes hidden properties from a clone of the base schema rather than rebuilding it manually, so future safe base parameters continue to propagate. Compact cannot supply filePath or downloadId, and the prompt does not claim that attaching a file to an input proves remote upload or submission.

No handler semantics or public API are changed. The added recovery tool exposes an existing inspection capability because upload_file already depends on it.

Validation

  • node test/run.js ? 1,453 passed; the only failure is the pre-existing package.json 26.0.10 vs CHANGELOG.md 26.0.0 assertion
  • node test/security/injection-corpus.mjs ? 60/60 passed
  • node --check for the changed Chrome/Firefox tool modules and test/run.js ? passed
  • git diff --check ? passed

@vercel

vercel Bot commented Aug 4, 2026

Copy link
Copy Markdown

@alectimison-maker is attempting to deploy a commit to the esokullu's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Exposes a platform-specific upload_file workflow to Compact Act mode across Chrome and Firefox.

Changes:

  • Adds narrow Compact upload schemas and prompt guidance.
  • Preserves Ask mode and excludes download workflows.
  • Adds cross-browser tool-boundary and guidance tests.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/chrome/src/agent/tools.js Adds Chrome Compact upload support.
src/firefox/src/agent/tools.js Adds Firefox Compact upload and picker support.
test/run.js Tests Compact upload schemas, prompts, and boundaries.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

'click', 'type_text', 'press_keys',
'navigate', 'new_tab', 'wait_for_element',
'fetch_url',
'upload_file',
'click', 'type_text', 'press_keys',
'navigate', 'new_tab', 'wait_for_element',
'fetch_url',
'upload_file',
@esokullu

esokullu commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

PR 2678 — feat(compact): expose upload_file

What it does: adds upload_file to COMPACT_TOOL_NAMES in both browsers, and rewrites the tool schema for the compact tier only (via compactUploadFileTool) so the model sees a narrower parameter set — Chrome: selector/attachmentId/filePath; Firefox: selector/attachmentId. downloadId is hidden in both. Compact prompts get a matching upload_file line, and the attachment-notice test flips to expect upload guidance in compact.

The tier-scoped schema override is a nice pattern — the notice builder (agent.js:20313) already derives canUseUploadTool from the live tool catalog, so the [UNTRUSTED USER ATTACHMENTS] handles start appearing in compact automatically with no extra wiring.

Blocking: compact has no way out of the ambiguous-selector gate

upload_file sets a per-tab recovery latch on an ambiguous selector, and the only thing that clears it is a successful get_interactive_elements returning a file-input record:

  • src/chrome/src/agent/agent.js:17031 sets it; agent.js:16964 hard-fails every later upload_file with recoveryRequired: 'get_interactive_elements'
  • agent.js:1684 _clearUploadSelectorRecoveryAfterInspection — clears only on name === 'get_interactive_elements'
  • Firefox is identical: agent.js:13805 / 14055

get_interactive_elements is not in COMPACT_TOOL_NAMES (tools.js:1697-1705). So in compact, one ambiguous selector permanently bricks uploads for that tab until a page replacement, and the error text instructs the model to call a tool it doesn't have — the exact "point at an unavailable tool" problem the PR's schema work is trying to avoid. Also note agent.js:17028 (input-not-found) names get_interactive_elements and get_accessibility_tree; the latter is in compact, so that one is only half-wrong.

Pick one:
- add get_accessibility_tree (or read_page) as a clearing inspection when the tier lacks get_interactive_elements, and make the error text tier-aware; or

  • skip the latch entirely when the compact catalog is active and just return the ambiguity error without persisting it.

Either way it needs a test — the new test only asserts schema/prompt shape, never exercises the handler.

Security: drop filePath from the Chrome compact schema

Compact has no downloads, no list_downloads, no read_downloaded_file — so there is no legitimate in-run source for a local path other than the user literally typing one. Meanwhile compact drops the full-tier hardening that exists precisely to stop invented paths (tools.js:1562, the /Users/Shared/… failure note). filePath on Chrome is a CDP-backed arbitrary local-file read that lands in a page input on an untrusted site; compact Act is also the tier most exposed to page-content injection. The schema string "explicitly supplied by the user" is guidance, not enforcement.

Recommendation: make compact attachmentId-only on Chrome, matching Firefox. That is a one-line schema change and the compact prompt line loses |filePath. If you keep it, add an explicit "never construct or guess a path; only a path the user typed in this conversation" clause.

Related, weaker point: hiding downloadId from the compact schema is prompt-level only — _executeTool still honors it if the model emits it. Harmless today (no downloadIds exist in compact), but it's a soft boundary, not a real one, and the test name (must hide downloadId) reads stronger than what's enforced.

Smaller notes

  • assert.match(prompt, /upload_file[\s\S]{0,180}do not click the page upload control/i) and friends encode character distances between prompt phrases. Any reword of that bullet shifts the offsets and these fail for no real reason. Assert on the extracted upload_file line instead.
  • assert.doesNotMatch(prompt, /download_files|list_downloads|downloadId/i) scans the whole compact prompt, so it now blocks any future unrelated mention. Probably intended, worth being deliberate about.
  • compactUploadFileTool replaces parameters wholesale rather than deleting keys — fine, but it means a future param added to the base upload_file silently won't reach compact. A delete-based transform would fail more loudly.

esokullu and others added 2 commits August 5, 2026 15:53
An ambiguous selector latches upload_file until get_interactive_elements
returns a verified file-input selector, and nothing else clears it. That
tool was not in the compact catalog, so one ambiguous selector left
compact unable to upload for the rest of the page's life while the
handler kept asking for a tool the model had not been given. Compact now
carries get_interactive_elements, and the tool description and prompt
bullet name it as the recovery path. A test asserts the coupling for
every tier that ships upload_file.

Compact also no longer advertises filePath. It has no download tools, so
the only file it can legitimately reach is the one the user attached to
this run; a path could only come from the model inventing one, and on
Chrome filePath is a CDP-backed read of any local file into an untrusted
page's input. Hidden parameters are now deleted from the base schema
rather than rebuilt, so a future upload_file parameter still reaches
compact.

Prompt assertions moved off character-distance regexes onto the
upload_file bullet itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The ambiguity test proved the latch clears but stopped there. Assert the
step it exists for: after get_interactive_elements supplies a unique
selector, the corrected upload dispatches and leaves the latch clear.

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

esokullu commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

Pushed three commits to this branch addressing the ambiguity dead-end and tightening the compact schema. Copilot flagged the same recovery problem on both tools.js files, so this covers that too.

The dead-end. An ambiguous selector latches upload_file via _uploadSelectorRecoveryRequired, and the only thing that clears it is a get_interactive_elements response carrying a verified file-input selector (agent.js:1684). That tool wasn't in the compact catalog, so one ambiguous selector left compact unable to upload for the rest of the page's life, with the handler repeatedly asking for a tool the model had never been given.

I checked whether the latch could clear on something compact already had. It can't: read_page's forms output carries type/name/id/placeholder/value but no CSS selector (content.js:186), and get_accessibility_tree returns ref_id lines. get_interactive_elements is the only tool that produces a verified unique selector, so clearing on anything else would just let the model resume guessing.

So it's in COMPACT_TOOL_NAMES now, in both browsers, with a comment tying its presence to upload_file. Both compact prompts gained a - get_interactive_elements bullet — the compact prompt is a closed "use ONLY these" list, so without it the model wouldn't know it had the tool. I also restored the recovery sentence the PR had stripped from the compact description; it was right to strip when the tool was missing, and it's accurate now.

filePath dropped from the Chrome compact schema. Compact has no download tools, so the only file it can legitimately reach is the one the user attached to this run. A path could only come from the model inventing one — and compact drops the full-tier guidance that exists to stop exactly that (tools.js:1562, the /Users/Shared/… note). On Chrome filePath is a CDP-backed read of any local file into an untrusted page's input, and compact Act is the tier most exposed to page-content injection. Compact is now selector + attachmentId in both browsers; full tier is unchanged.

While there I switched the transform from rebuilding parameters to deleting a named COMPACT_UPLOAD_HIDDEN_PARAMS list off the base schema, so a future upload_file parameter still reaches compact instead of vanishing silently.

Tests. The character-distance regexes (/upload_file[\s\S]{0,180}…/) are gone — those break when neighbouring bullets are reworded, not when behaviour changes. Assertions now run against the prompt's own - upload_file( line. Added an exact-keys check on the compact schema, and a test for the invariant rather than the instance: every tier exposing upload_file also exposes get_interactive_elements, iterating compact/mid/full across both browsers, so it catches the regression class.

Copilot also asked for coverage of the ambiguous-selector retry itself. The existing handler test proved the latch clears but stopped there, so I extended it through the step the recovery exists for: after the inspection supplies a unique selector, the corrected upload dispatches and leaves the latch clear.

Full suite 1451 passed, security corpus 60/60. The one failure is the pre-existing package.json 26.0.10 vs CHANGELOG 26.0.0 mismatch, unrelated to this PR — worth noting it only surfaces in npm test, which PR CI doesn't run, so it'll bite at release time rather than here.

One judgment call worth a second opinion: adding get_interactive_elements grows the compact catalog by one tool that returns every interactive element, which isn't cheap for the tier whose whole point is a small context. The alternative was dropping the latch in compact and accepting selector-guessing. I went with the catalog change because the guess-prevention seemed worth more than the tokens, but it's your call.

@alectimison-maker

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review and the follow-up commits. I reviewed the current head (384efba) against the upload handler's recovery path and agree with both changes.

get_interactive_elements now travels with upload_file in Compact, so the ambiguous-selector latch has a reachable recovery path. The Compact schema is derived from the base schema and then hides both downloadId and filePath; Chrome therefore accepts only the current user attachment, while Firefox retains its user-controlled picker. The new handler regression also verifies that inspection clears the latch and that the corrected upload actually dispatches.

I reran the repository test runner (1,453 passed, with only the existing package/CHANGELOG version mismatch), the security corpus (60/60), syntax checks, and git diff --check. I've also replaced the stale PR description so it no longer says Compact gains only upload_file or that Chrome Compact accepts filePath.

@esokullu
esokullu merged commit 743d64c into webbrain-one:main Aug 6, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Expose upload_file in Compact mode

3 participants