Skip to content

feat(browser): expose agent download actions#101369

Merged
steipete merged 2 commits into
mainfrom
codex/browser-agent-download-actions
Jul 7, 2026
Merged

feat(browser): expose agent download actions#101369
steipete merged 2 commits into
mainfrom
codex/browser-agent-download-actions

Conversation

@steipete

@steipete steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Resolves a problem where agents could not deliberately save a download to a known guarded path or wait for a download that begins after an asynchronous export completes. The lower-level managed-browser routes and CLI existed, but the model-facing browser tool omitted both operations.

Related: #38519, #12378, #93307, #74411

Why This Change Was Made

This takes @GRD-Chang's explicit-action design from #74411 and ports it to the current Browser plugin action boundary. It exposes download and waitfordownload, keeps one typed host/node dispatch path, carries the browser wait through the HTTP/node/Gateway timeout layers, and keeps all writes behind the existing managed-profile and guarded-download-root controls.

This complements #93307 rather than replacing it: /act still reports downloads caused during an immediate action, while these operations support a caller-selected path and delayed exports that need an explicit waiter. Existing-session profiles remain unsupported. The routes now also enforce the current-tab navigation policy before capturing bytes.

This maintainer replacement preserves the contributor's authorship because the stale fork branch behind #74411 could not accept the current-main rewrite through the verified branch-update path without serializing the repository-wide base gap.

User Impact

Agents can click a snapshot ref and save its download to a chosen relative path, or arm waitfordownload before a delayed export completes. Both return the completed download URL, suggested filename, and guarded local path for later tool use.

Evidence

  • Exact prepared head 1a97a19e434bbe940120ad865109c3f02ea276ec: full hosted CI passed on attempt 2 after one unrelated subagent-fanout-synthesis QA retry. Run: https://github.com/openclaw/openclaw/actions/runs/28842562919
  • Blacksmith Testbox tbx_01kwxavv0ks9wszpejv07qj0dt: 7 focused files, 154/154 tests; full check:changed; targeted oxfmt. Focused run: https://github.com/openclaw/openclaw/actions/runs/28840624922
  • Fresh autoreview after the current-URL guard: clean, no accepted/actionable findings.
  • Playwright 1.61.1 source contract checked directly: Page.waitForEvent applies the supplied timeout and Download.saveAs waits through the artifact save/remote stream.
  • Live patch proof: Crabbox local-container lease cbx_09eff0c13634 (swift-crab-f524), exit 0 in 162.651s. A rebuilt package started a fresh Gateway and Chromium 149, and a real openai/gpt-5.4 agent completed both a named download and a 45-second delayed waitfordownload. The validator independently checked unpredictable file bytes, rejected traversal, and confirmed both model-facing actions in agent state.
  • Boundary proof: required ref/path validation, traversal and symlink rejection, existing-session rejection, current-tab navigation guard, host routing, node proxy path rewriting, and inner/proxy/Gateway timeout layering.

Contributor credit is preserved in the signed maintainer history with Co-authored-by: changcy <[email protected]>.

@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation size: M maintainer Maintainer-authored PR labels Jul 7, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ccb35d08b8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +67 to +72
function resolveBrowserDownloadRequestTimeoutMs(timeoutMs: unknown): number {
const waitTimeoutMs =
normalizePositiveTimeoutMs(timeoutMs) ?? DEFAULT_BROWSER_DOWNLOAD_TIMEOUT_MS;
// Keep the HTTP client alive after the Playwright waiter expires so its
// timeout/error response reaches the caller instead of a transport abort.
return addTimerTimeoutGraceMs(waitTimeoutMs, BROWSER_DOWNLOAD_REQUEST_TIMEOUT_SLACK_MS) ?? 1;

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.

P2 Badge Keep download transports open until saves finish

When a download starts near the end of the requested wait, or when the file itself takes more than 5 seconds to finish, this timeout aborts the client even though the browser route is still inside download.saveAs(...). Playwright emits the download event when the transfer starts, and saveAs waits for completion, so the server can legitimately take longer than waitTimeoutMs + 5s; the new agent action will report a failed browser request for large/slow exports that were successfully detected and are still being saved.

Useful? React with 👍 / 👎.

profile,
});
params.onTabActivity?.(readStringValue((result as { targetId?: unknown }).targetId) ?? targetId);
return jsonResult(result);

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.

P2 Badge Wrap page-controlled download metadata

For downloads from a web page, download.url and download.suggestedFilename are page/header-controlled, but jsonResult(result) puts those strings directly into the model-visible tool output. Other browser-originated surfaces in this module wrap/neutralize their text before returning it; this new action should do the same so a malicious filename or URL cannot inject instructions into the next agent turn.

Useful? React with 👍 / 👎.

@clawsweeper

clawsweeper Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper status: review started.

I am starting a fresh review of this pull request: feat(browser): expose agent download actions This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Land-ready proof for prepared head 1a97a19e434bbe940120ad865109c3f02ea276ec:

  • Native OPENCLAW_TESTBOX=1 scripts/pr prepare-run 101369: passed; exact remote tree verified.
  • Full hosted CI: https://github.com/openclaw/openclaw/actions/runs/28842562919 — attempt 2 green. Attempt 1 had one unrelated subagent-fanout-synthesis miss in Telegram QA; its unchanged-SHA failed-jobs rerun passed.
  • Focused Blacksmith Testbox tbx_01kwxavv0ks9wszpejv07qj0dt: targeted oxfmt; 7 files and 154/154 tests; full check:changed including production/test types, lint, DB-first/sidecar guards, and import-cycle checks. Focused workflow: https://github.com/openclaw/openclaw/actions/runs/28840624922
  • Live Crabbox local-container lease cbx_09eff0c13634 (swift-crab-f524): rebuilt package; fresh Gateway; Chromium 149; real openai/gpt-5.4 agent. It completed a caller-named download and a 45-second delayed waitfordownload; independent byte checks passed, traversal was rejected, and agent state confirmed both model-facing actions. Exit 0 in 162.651s.
  • Security/contract proof: both routes enforce the current-tab navigation guard before capture; guarded-root traversal/symlink rejection remains intact; existing-session profiles still reject interception; node-returned paths use the existing bounded persistence/rewrite path; default timeout layers are 120/125/130 seconds.
  • Direct Playwright 1.61.1 contract inspection confirmed Page.waitForEvent applies the supplied timeout and Download.saveAs waits through artifact save/remote streaming.
  • Fresh mandatory autoreview after the route guard: no accepted/actionable findings.

Known proof gaps: none.

@steipete
steipete merged commit 01d6ea1 into main Jul 7, 2026
157 of 159 checks passed
@steipete
steipete deleted the codex/browser-agent-download-actions branch July 7, 2026 05:17
@steipete

steipete commented Jul 7, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 7, 2026
* feat(browser): expose agent download actions

Co-authored-by: changcy <[email protected]>

* chore: keep release notes in PR context

---------

Co-authored-by: changcy <[email protected]>
sheyanmin pushed a commit to sheyanmin/openclaw that referenced this pull request Jul 8, 2026
* feat(browser): expose agent download actions

Co-authored-by: changcy <[email protected]>

* chore: keep release notes in PR context

---------

Co-authored-by: changcy <[email protected]>
giodl73-repo pushed a commit to giodl73-repo/openclaw that referenced this pull request Jul 8, 2026
* feat(browser): expose agent download actions

Co-authored-by: changcy <[email protected]>

* chore: keep release notes in PR context

---------

Co-authored-by: changcy <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

docs Improvements or additions to documentation maintainer Maintainer-authored PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant