fix(frontend): stop calling /api/auth/workspace-session on cloud backends#763
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
all-hands-bot
left a comment
There was a problem hiding this comment.
🟢 Good taste - Clean bug fix that eliminates dead network traffic and properly implements cloud file viewing.
KEY INSIGHT:
The PR correctly identifies that the workspace-session cookie flow is architecturally incompatible with cloud (cross-origin + server-side hop), and solves it by routing file bytes through the existing cloud proxy infrastructure with base64 data URIs.
[RISK ASSESSMENT]
- [Overall PR]
⚠️ Risk Assessment: 🟢 LOW
This is a straightforward bug fix that:
- Eliminates useless network calls on cloud (the cookie never reached the browser and wouldn't work cross-origin)
- Fixes broken file viewing by using the correct cloud proxy pattern with
X-Session-API-Key - Mirrors the proven approach from the original OpenHands frontend
- Leaves local behavior completely unchanged (still uses workspace-session cookie)
- Has proper test coverage with correctly mocked dependencies
The implementation correctly uses AgentServerRuntimeService.downloadFile(), which internally routes through FileClient (from @openhands/typescript-client) for local and callCloudProxy for cloud - following the repository's API access conventions. The arrayBufferToBase64 chunking prevents call-stack issues on larger files.
Minor observation: Data URIs have generous size limits in modern browsers (~100MB+), but very large files could theoretically hit browser limits. This is acceptable since (a) the old behavior was completely broken, and (b) this mirrors the original frontend's pattern.
Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/agent-canvas/actions/runs/26441623781
📸 Snapshot Test Report✅ All snapshots match the main branch baselines.
✅ Unchanged snapshots (73)
Generated by the Snapshot Tests workflow. This comment was created by an AI agent (OpenHands) on behalf of the repo maintainers. |
Why
Description
Summary
When a user opens the Files tab on a conversation running against a cloud backend (e.g.
Production - Personal Workspace), the frontend issues aPOST /api/auth/workspace-sessionagainst the conversation's runtime sandbox (routed server-side throughcallCloudProxy). The call is structurally useless on cloud — it produces dead network traffic on every cloud conversation that opens the Files tab, and the file-content fetch that depends on its result is silently broken on cloud.Steps to reproduce
npm run devinagent-canvas.<BackendSelector />, click Add Backend, and configure:https://app.all-hands.devProduction - Personal Workspace, create a new conversation.Write a hello world script in bash.Current behavior
The endpoint
http://<id>.staging-runtime.all-hands.dev/api/auth/workspace-sessionis called (visible in the/api/cloud-proxyenvelope and in upstream logs).Expected behavior
/api/auth/workspace-sessionshould not be called on cloud. The Files tab should still render file lists and file content.Root cause
useWorkspaceSessionexists to mint anoh_workspace_session_keycookie so the browser can attach it to subsequent same-origin<iframe src>/<img src>/fetch(credentials: "include")calls against the static workspace fileserver. That flow only works for local backends.On cloud:
callCloudProxy, so any upstreamSet-Cookielands at the local agent-server and never reaches the browser jar.*.staging-runtime.all-hands.devand the GUI runs onlocalhost—fetch(staticUrl, { credentials: "include" })cannot carry the cookie cross-origin.So
useWorkspaceSessionfires a POST that does nothing, anduseWorkspaceFileContentbuilds astaticUrlwhose cookie-authenticated fetch can never succeed.Fix
Mirror the pattern used in the original OpenHands frontend: do not mint a workspace session on cloud. Fetch file bytes through
callCloudProxy(which already attachesX-Session-API-Keyserver-side), and render binary kinds as base64data:URIs.Acceptance criteria
/api/auth/workspace-sessioncalls (browser-direct or wrapped in/api/cloud-proxy).data:URIs).Summary
useWorkspaceSessionon cloud backends — the cookie it mints is unusable cross-origin and the POST was pure dead traffic.AgentServerRuntimeService.downloadFile(already proxied withX-Session-API-Key). Binary kinds become base64data:URIs.WorkspaceFileContentshape unchanged —file-content-viewer.tsxandfiles-tab.tsxdon't need to branch on backend kind.Why
POST /api/auth/workspace-sessionexchangesX-Session-API-Keyfor anoh_workspace_session_keycookie scoped to/api/conversations. The browser then auto-attaches that cookie to same-origin<iframe src>/<img src>/fetch(credentials: "include")against the static workspace fileserver.That flow works on local, where the GUI and the agent server share an origin. On cloud it breaks at two points:
callCloudProxy, so the upstreamSet-Cookieis received by the local agent-server, not the browser.*.staging-runtime.all-hands.devand the GUI onlocalhost— a cross-originfetch(credentials: "include")won't attach a cookie set on a different origin.So the POST was dead traffic and
useWorkspaceFileContent's follow-up fetch was silently broken on every cloud conversation that opened the Files tab.The pattern used by the original OpenHands frontend (
/Users/hieple/Documents/openhands/OpenHands/frontend) is to fetch file bytes via authenticated HTTP usingX-Session-API-Keyand render images as base64data:URIs. This PR adopts that pattern on cloud.Changes
src/hooks/query/use-workspace-session.tsenabledonkind === "local". The hook returns{ data: null, ... }on cloud — no POST fires.callCloudProxywithhostOverride).callCloudProxy/buildHttpBaseUrlimports.src/hooks/query/use-workspace-file-content.tsqueryFnthat fetches bytes viaAgentServerRuntimeService.downloadFile(conversationUrl, sessionApiKey, relativePath)(already routes throughcallCloudProxyand suppliesX-Session-API-Key).staticUrlset to adata:${mimeType};charset=utf-8;base64,...URI so the "open in new window" link infiles-tab.tsxstill works.staticUrlset to adata:${mimeType};base64,...URI.enabledno longer requiresbaseUrlon cloud (isCloud || !!baseUrl)."cloud"instead ofbaseUrlon cloud, so cache identity is correct across backend switches.arrayBufferToBase64helper that avoids theString.fromCharCode(...arr)call-stack limit on larger files.src/components/features/files-tab/file-content-viewer.tsx,src/routes/files-tab.tsx— unchanged.data:URIs render in<img src>/<iframe src>/<a href>like any other URL.Tests
Following the project's testing rules (mock underlying services, not the hook; one assertion per behavior; extend existing files):
__tests__/hooks/query/use-workspace-session.test.tsxcallCloudProxyrouting) with one test verifying cloud does not fire any request anddatastaysnull.__tests__/hooks/query/use-workspace-file-content.test.tsxcloud backenddescribeblock.getActiveBackend(set to cloud) andAgentServerRuntimeService.downloadFile— the underlying dependencies, not the hook itself.downloadFileis called with the expected args, no browserfetchcall, returnedtextis decoded,staticUrlis adata:text/markdown;charset=utf-8;base64,...URI.downloadFileis called,kind: "image",staticUrlis adata:image/png;base64,...URI, no browserfetchcall.Issue Number
Resolves #762
How to Test
npm run devinagent-canvas.<BackendSelector />, click Add Backend, and configure:https://app.all-hands.devProduction - Personal Workspace, create a new conversation.Write a hello world script in bash.Type
🐳 Docker images for this PR
• GHCR package: https://github.com/OpenHands/agent-canvas/pkgs/container/agent-canvas
ghcr.io/openhands/agent-canvasghcr.io/openhands/agent-server:1.23.0-pythonopenhands-automation==1.0.0a350e005bde95e0e1b4d0f3a2db39f50d549c0edcdPull (multi-arch manifest)
# Multi-arch manifest — Docker automatically pulls the correct architecture docker pull ghcr.io/openhands/agent-canvas:sha-50e005bRun
All tags pushed for this build
About Multi-Architecture Support
sha-50e005b) is a multi-arch manifest supporting both amd64 and arm64sha-50e005b-amd64) are also available if needed