This repository was archived by the owner on Jul 27, 2026. It is now read-only.
fix: render the VSCode button on self-hosted (local) backends - #1945
Closed
harish-chandramowli wants to merge 3 commits into
Closed
fix: render the VSCode button on self-hosted (local) backends#1945harish-chandramowli wants to merge 3 commits into
harish-chandramowli wants to merge 3 commits into
Conversation
The button was gated on `backend.kind === "cloud"`, so it never mounted on a
self-hosted install. `useUnifiedVSCodeUrl` already has a complete non-cloud
branch (`enabled: !isCloud && …`) that reads the URL from the agent server's
`/api/vscode/url`, so the data path exists — only the render gate blocked it.
The gate looks like a workaround for the agent server advertising an unusable
URL, which the hook's own comment describes ("only knows its internal
localhost:8001"). That is fixed server-side by
OpenHands/software-agent-sdk#4181 (port) and #4222 (base path), so the
premise for gating on backend kind no longer holds.
Also drops the cloud-only `pr-1` padding, since the wrapper now always has
content, and updates the test that asserted the button stays hidden on local.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012g6hxLKWGBmNH9fwfFAyTx
|
Someone is attempting to deploy a commit to the openhands Team on Vercel. A member of the Team first needs to authorize it. |
Rendering the button on local backends exposes a case the cloud-only gate used to hide: a backend with nothing to open still gets a control whose click is a no-op. `enable_vscode: false` makes agent-server answer `GET /vscode/url` with 503, so both resolvers reject and the query settles in `error` carrying no data — `data && !data.url` does not catch it. A successful response with a null URL is a separate path that `isError` alone does not catch. `useUnifiedVSCodeUrl` now folds both into `isUnavailable`, and `DrawerVSCodeLink` renders nothing when it is set. Both conditions are final rather than transient: the query has already exhausted its three retries. Cloud is deliberately excluded. A sandbox that is still STARTING reports no VSCODE entry in `exposed_urls` and will populate one shortly, so the control stays visible and cloud behavior is unchanged by this PR. `retryDelay: 0` in the hook test's query client keeps the new error-path test from spending the default exponential backoff, since the hook's own `retry: 3` overrides the wrapper's `retry: false`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012g6hxLKWGBmNH9fwfFAyTx
Rendering the button on local backends means `useUnifiedVSCodeUrl`'s query now actually runs there — previously it never did, because the only consumer never mounted outside cloud. On a backend with `enable_vscode: false` that query gets a 503, which the global QueryCache handler turns into a user-facing error toast reading "HTTP request failed (503 Service Unavailable)". That is a deployment setting, not a failure the user should be told about, and the intended response to it is already to render no button. Opt this query out via the existing `meta.disableToast` escape hatch so the two agree. Caught while recording the demo video: the toast was visible in the frame that was supposed to show a clean "no editor offered" state. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012g6hxLKWGBmNH9fwfFAyTx
Contributor
✅ Mock-LLM E2E Tests60/60 passed Commit: Details
Posted by the Mock-LLM E2E workflow · results are deterministic (scripted LLM responses) |
harish-chandramowli
marked this pull request as ready for review
July 26, 2026 10:07
Member
|
This repository has moved to https://github.com/OpenHands/OpenHands. We’d appreciate it if you re-opened this pull request there. Thank you! |
6 tasks
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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
HUMAN:
I tested this end to end locally to get VSCode button working from my mac mini / docker setups
AGENT:
See Notes.
Why
DrawerVSCodeLinkis gated onbackend.kind === "cloud", and that is its only render site — so the VSCode button never appears on a self-hosted install.The data path for local backends is already fully implemented.
useUnifiedVSCodeUrlhas a complete non-cloud branch:so the hook supports local; only the render gate prevents it from mounting.
The gate reads as a workaround for the agent server advertising a URL that could not work, which the hook's own comment states:
That is a server-side defect, now fixed there: OpenHands/software-agent-sdk#4181 makes the endpoint advertise the configured port instead of a hardcoded
:8001(merged, released in agent-server 1.37.1), and #4222 makes it includevscode_base_pathfor deployments routed by path. With both, the local URL is correct and the reason for gating on backend kind no longer holds.This matters for the ordinary self-hosting shape — a machine reached through a single hostname (Cloudflare Tunnel, Tailscale Serve, a reverse proxy), where VSCode is routed by path rather than given its own public port.
Summary
DrawerVSCodeLinkunconditionally instead of only whenbackend.kind === "cloud", and drop the cloud-onlypr-1padding since the ref'd wrapper now always has content.isUnavailabletouseUnifiedVSCodeUrlso a self-hosted backend with no editor to offer renders no button rather than a dead one. It covers both terminal cases, which need different checks:enable_vscode: false—GET /vscode/urlanswers 503, so both resolvers reject and the query settles inerrorcarrying no data.data && !data.urldoes not see this.successwithurl: null.isErrordoes not see this.isUnavailable, so cloud behavior is unchanged (see Notes).meta.disableToast. Because this query never ran on local backends before, the 503 above now reaches theQueryCacheonErrorhandler and renders a user-facing "HTTP request failed (503 Service Unavailable)" toast. A deployment that switched VSCode off should get no button and no error.Issue Number
Related: OpenHands/OpenHands#15434 (self-hosted VSCode button unreachable). That issue is written up as the URL/port problem; this PR addresses the render gate, which is the second, independent reason the button is unusable self-hosted.
How to Test
End-to-end against a locally patched agent-server.
OH_AGENT_SERVER_LOCAL_PATHbuilds agent-server from a checkout, so both halves of the fix run together without publishing anything:Then start a conversation and open the right panel.
document.querySelector('[data-testid="drawer-vscode-link"]')returnsnull.OH_ENABLE_VSCODE=false: no button, and no error toast.Two things worth knowing if you reproduce this:
getVSCodeUrlcall sites sendbaseUrl: window.location.origin, so with novscode_base_paththe server is told the origin and returnshttp://localhost:8000/?tkn=…— agent-canvas itself, not the editor. Verified on the patched build:GET /api/vscode/url?base_url=http://localhost:18300→http://localhost:18300/vscode/?tkn=…, and that URL gives302→vscode-tkncookie →200workbench (2504 bytes).openvscode-serverpublishes Linux-only builds (the 1.109.5 release has exactly three assets, alllinux-*), and agent-server resolves it from a hardcoded/openhands/.openvscode-server. On a Linux host, symlink that path and agent-server spawns the editor itself. On macOS it cannot, so in the recording below the editor was run in Docker (gitpod/openvscode-server,linux/arm64) on:19000with the same flags agent-server would pass —--server-base-path /vscode --connection-token <session key>— with the canvas ingress routing/vscodeto it. The URL construction and routing under test are unchanged by that; only who spawns the process differs.Video/Screenshots
Two recordings from the stack described above — same build, same backend, the only difference being
enable_vscode:enable_vscodeon — a conversation on alocalbackend, right panel open, the VSCode button present in the tab bar. Clicking it openshttp://localhost:18300/vscode/?tkn=…&folder=…, which loads the real workbench (.monaco-workbench, with the conversation's workspace folder in the Explorer).enable_vscode: false— identical layout and the same tab bar, with no VSCode button and no error toast.01-button-works-enable_vscode-true.webm
02-button-hidden-enable_vscode-false.webm
Both are also asserted by unit tests, but the recordings are what show the base path actually resolving end to end.
Type
Notes
This changes an existing tested behavior, deliberately.
conversation-tabs.test.tsxassertedshould hide the vscode link when the active backend is local; both it and the gate arrived together in #1288 ("UI polish: drawer tabs, empty states, and browser chrome"). If hiding the button on local backends was an intentional product decision rather than a consequence of the unusable server URL, then this PR is wrong and I would rather know that than have it merged — the alternative is to leave the gate and document that self-hosted users should open VSCode out of band.Please confirm the sequencing. Merging this before software-agent-sdk#4222 would surface a button whose URL omits the base path on path-routed deployments. Happy to hold until that lands, or to rebase if you would prefer the two changes land together.
Cloud is deliberately left alone. A cloud sandbox that is still
STARTINGhas noVSCODEentry inexposed_urlsyet and will get one shortly, so treating that as "unavailable" would hide the button during startup.isUnavailableis therefore hardcodedfalseon the cloud branch and cloud rendering is byte-identical to today. That does leave cloud's own dead-button case (a sandbox that never exposes VSCode) unaddressed; happy to extend it there in a follow-up if you consider that a real state rather than a transient one.The error toast was only found by recording it. Suppressing it is a behavior change to a query you own, so it is worth an explicit look: if you would rather the 503 stayed loud, the alternative is to leave the toast and accept that any self-hosted deployment with VSCode disabled shows an error on every conversation open. I judged silence correct because the button already communicates the state by not being there, but this is your call.
One test-only change outside the feature.
retryDelay: 0was added to the hook test'sQueryClient, because the hook's ownretry: 3overrides the wrapper'sretry: falseand the new 503 test would otherwise spend the default exponential backoff before the query settles.