18 KiB
Repository Notes
-
This repository is a near-direct port of the OpenHands frontend, adapted to talk straight to
software-agent-sdk/agent_serverwithout the usual OpenHands app backend. -
Frontend API adaptation lives mainly in
src/api/:option-servicefabricates an OSS web-client config and reads models/providers fromagent_serverLLM endpoints.settings-servicestores settings locally in browser localStorage and reads schemas fromagent_server/api/settings/*endpoints.v1-conversation-service,event-service,sandbox-service,git-service, andskills-serviceare mapped directly toagent_serverREST endpoints.open-hands-axiosinjects the optionalX-Session-API-Keyfrom env/local config for all requests.
-
Supported env vars for deployment:
VITE_BACKEND_BASE_URLfor the agent server base URL.VITE_SESSION_API_KEYfor optional session auth.VITE_WORKING_DIRfor the default workspace path sent when starting conversations.VITE_WORKER_URLSas a comma-separated list of browser worker URLs if you want the Browser tab to probe exposed app hosts.VITE_ENABLE_BROWSER_TOOLS=falseto omitBrowserToolSetfrom new conversation payloads.
-
The UI keeps most OpenHands routes/layout intact, but SaaS/org/billing/integration behavior is intentionally hidden or stubbed via the fabricated OSS config because there is no separate app backend.
-
Verification command:
npm run typecheck && npm run build. -
GitHub automation now includes
.github/workflows/ci.ymlfornpm ci,npm test, andnpm run build, plus.github/dependabot.ymlwith weekly npm/github-actions updates gated by a 7-day cooldown. -
Direct
dependenciesanddevDependenciesinpackage.jsonare exact-pinned (no caret ranges); reproducible installs should use the committedpackage-lock.jsonplusnpm ci, and targeted transitive fixes still belong inoverrides. -
package-lock.jsonmust also retain the optional peer entry fornode_modules/vite-tsconfig-paths/node_modules/typescript@5.9.3; without that nested lock entry, cleannpm ciinstalls on CI fail withMissing: typescript@5.9.3 from lock file. -
npm testnow runsnpm run make-i18nfirst so clean environments generatesrc/i18n/declaration.tsbefore Vitest loads aliased imports. -
__tests__/vite-config.test.tsshould importvite.configdirectly under// @vitest-environment node; spawning plainnode -e 'import ./vite.config.ts'is not portable across Node patch releases in CI. -
vitest.setup.tsmust guard DOM-specific globals (HTMLCanvasElement,HTMLElement,window) because some suites run in the Node environment instead of jsdom. -
__tests__/components/providers/posthog-wrapper.test.tsxmust wrapPostHogWrapperin aQueryClientProvider; the wrapper now reads its client from React Query context instead of importing the global singleton. -
@openhands/typescript-clientis consumed directly fromgithub:OpenHands/typescript-client#4716d2e; that package ships the needed subpath exports forclient/http-client,events/remote-events-list, andworkspace/remote-workspace. -
Shared TypeScript-client adapters live in
src/api/typescript-client.ts; prefer those helpers for agent-server-backed REST/workspace/event/VS Code calls before falling back toopen-hands-axios. -
Local verification/build gotchas:
npm run typecheckassumes generated translation types exist; runnpm run make-i18nfirst ifsrc/i18n/declaration.tsis missing.
-
Phase-1 OSS cleanup removed SaaS-only auth/org/billing/onboarding/payment/invitation codepaths, routes, and tests. Keep
integrations,git-settings,secrets, MCP settings, and other local/self-hosted flows intact when simplifying OSS behavior. -
When merging main into the Phase-1 OSS cleanup branch, keep the new agent-server compatibility bootstrap in
src/root.tsx, but do not reintroduce SaaS invitation cleanup or enterprise CTA chrome in the OSS user menu; the OSS account menu should just render settings links plus Docs. -
During the Phase-1 OSS cleanup audit, the runtime SaaS removals held up, but route-level regression coverage for still-active OSS settings pages had been deleted too aggressively. Keep focused tests for local/self-hosted screens like
app-settings,llm-settings,git-settings,mcp-settings, andsecrets-settingseven when stripping SaaS-only code. -
npm run dev:mockneeds MSW handlers for the direct agent-server routes used by the adapted frontend, not the original OpenHands mock paths. Key routes that must stay covered are:- bootstrap/model loading:
/server_info,/api/llm/models/verified,/api/llm/providers - settings schemas:
/api/settings/agent-schema,/api/settings/conversation-schema - conversation browsing/loading:
/api/conversations/search,/api/conversations?ids=...,/api/conversations/:id,/api/conversations/:id/events/* - runtime git panels:
/api/git/changes,/api/git/diff
- bootstrap/model loading:
-
Static mock verification needs a build created with
VITE_MOCK_API=true(usenpm run build:mock); the client must start MSW whenever that flag is enabled, even in production/static builds, otherwise routes like/settingsand the conversations pane fall through to the static server and crash on undefined.filter/.mapassumptions. -
Frontend compatibility guard:
OptionService.getConfig()now uses/server_info.versionto block unsupported agent-server versions before the app loads. Git history insoftware-agent-sdkshows/api/settings/agent-schemaand/api/settings/conversation-schemafirst shipped in tagv1.17.0, so the GUI currently treats< 1.17.0(or unknown/unparseable versions) as incompatible,useConfigstops retrying that case, andsrc/root.tsxrenders a blocking unsupported-version notice on every route. -
Useful regression tests for mock mode live in
__tests__/api/option-service.test.ts,__tests__/api/mock-conversation-handlers.test.ts, and__tests__/api/mock-settings-handlers.test.ts. -
Browser-verified mock-mode tour artifact was generated at
artifacts/frontend-tour.gif. -
Live
agent_servercompatibility quirks discovered during browser verification:- Latest
openhands-agent-serverlive-mode notes (verified against 1.18.1):/api/settings/agent-schemaand/api/settings/conversation-schemaexist on recent servers, but they return401when the server was started withSESSION_API_KEYorOH_SESSION_API_KEYS_0; the frontend must send the same value asVITE_SESSION_API_KEY/X-Session-API-Key.- The provider/model picker should use
/api/llm/providers,/api/llm/models, and/api/llm/models/verified;/api/v1/config/providers/searchand/api/v1/config/models/search404 on current live agent-server releases. - When the browser is accessing the frontend through a remote host (for example an All Hands work URL) but
VITE_BACKEND_BASE_URLpoints at127.0.0.1/localhost, browser-side REST calls must fall back to the frontend origin so Vite can proxy/apiand/socketsto the local backend.
GET /api/conversationsexpects repeatedidsparams (?ids=a&ids=b), not Axios's default bracket form (ids[]=a), so the shared Axios client needs a custom params serializer.- Runtime git panels should prefer the conversation's reported
workspace.working_dirwhen present; falling back to/workspace/projectcan produce 500s likeNot a git repositoryfor direct local workspaces such as/workspace/project/agent-server-gui. - For the current
openhands-agent-serverPyPI/uv-tool flow,uv tool install -U openhands-agent-serveralone was not sufficient in this environment. A working install was:uv tool install -U --with openhands-tools --with openhands-workspace openhands-agent-serveruv tool installexposes the executable asagent-server, notopenhands-agent-server, and may require adding~/.local/bintoPATH.
- Current SDK / agent-server conversation start payloads must use SDK-registered snake_case tool names, not the old class-style names. Working names against SDK v1.18.1 were:
terminalfile_editortask_trackerbrowser_tool_setUsingTerminalTool/FileEditorTool/TaskTrackerTool/BrowserToolSetcaused live/api/conversations/{id}/eventsruns to fail withToolDefinition '<name>' is not registered.
- The root compatibility bootstrap now treats
/server_infonetwork/timeout failures as a first-classAgentServerUnavailableError, uses a short 5s timeout for that probe, and disables React Query retries/toasts for the initial config fetch so missing backends fail fast with an explicit full-screen notice. - For local verification in this repo, setting
VITE_WORKING_DIR=/workspace/project/agent-server-guiavoids initial Changes-tab 500s from pointing conversations at the non-repo parent/workspace/project. - OpenHands Cloud sandbox development note: do not reuse the sandbox's existing agent-server for this frontend. Current agent-server releases use a shared
openhandstmux socket and defaultworkspace/conversations, so a naive second server in the same sandbox can kill the cloud session's tmux state or attach to the same persisted conversations.npm run devis now the recommended local workflow and starts an isolated local agent-server for the checkout by overridingTMUX_TMPDIR,OH_CONVERSATIONS_PATH,OH_BASH_EVENTS_DIR, andOH_VSCODE_PORTunder.openhands-dev/; usenpm run dev:frontendonly when intentionally pointing at a separately managed backend, ornpm run dev:mockfor mock mode. - A successful end-to-end live run in this environment required a real LLM config (
LLM_MODEL+LLM_API_KEY). The defaultlitellm_proxy/...model with nollm_api_keyfailed at runtime with alitellm.AuthenticationError.
- Latest
-
Agent-server recovery UX gotchas:
- Keep
/settings/agent-serverin the intermediate-page bypass path (use-is-on-intermediate-page) souseConfig()-driven layout/sidebar queries do not block the recovery screen behind a global spinner. PostHogWrappershould treat config-fetch failures as silent/optional (no user-facing toast), otherwise onboarding/recovery screens show a duplicate incompatible-server toast on top of the friendly guidance.- Keep the settings route on the compact
AgentServerConnectionFormvariant withshowSectionHeader={false}and no checklist; the blocked root onboarding should stay similarly minimal, with only the status card plus a single sentence that links to the repo setup instructions. - For local screenshot/GIF capture of SPA routes, serve
build/with an SPA fallback (for examplesirv build --single) and restart the static server after each rebuild so hashed asset URLs stay in sync.
- Keep
-
Git provider token persistence note: this direct-agent-server frontend now persists
Settings > Gitprovider tokens locally in browser storage instead of posting to an app-backend secrets route.src/api/secrets-service.tswrites the token payload to localStorage, mirrors provider hosts intoprovider_tokens_setthroughSettingsService.saveSettings(), anduse-delete-git-providersclears that local state. -
Agent server connection settings now live at
Settings > Agent Server(/settings/agent-server). The page reads deployment defaults fromVITE_BACKEND_BASE_URL/VITE_SESSION_API_KEY, saves user overrides in theopenhands-agent-server-configlocalStorage key, and must stay reachable even when the backend compatibility probe fails so users can recover from missing or wrong backend configuration. -
README expectation: keep the first section as a concrete, chronological from-scratch quickstart for running this frontend against a real
openhands-agent-server(clone, install backend, optional.env, runnpm run dev). -
Keep README user-focused and move contributor/developer-specific workflows (Cloud sandbox debugging,
dev:safe, mock mode, detailed env vars/build-test notes) intoDEVELOPMENT.md. -
scripts/dev-safe.mjsshould fail fast ifagent-servercannot be spawned (for example missing PATH entries). -
Vite dev mode can black-screen on first load with
504 Outdated Optimize Depif core client-entry deps are not prebundled; keepreact,react/jsx-runtime,react-dom/client, andreact-router/dominoptimizeDeps.include. -
Vercel deployment note: React Router builds for this repo must keep
build/clientintact on actual Vercel builds and includepresets: [vercelPreset()]from@vercel/react-router/vite; flatteningbuild/clientduring a Vercel build produces deployments with empty outputs (routes: null, no static files) and a production 404. -
As an OpenHands incubator Sandbox project, the repo should carry the standard sandbox warning badge in
README.mdand include a rootLICENSEfile to satisfy the incubator-program requirements. -
OpenHands repo bootstrap files live under
.openhands/:.openhands/setup.shinstalls frontend dependencies withnpm ciwhen needed, creates.envfrom.env.sampleif missing, appendsVITE_WORKING_DIRfor this repo when unset, and generatessrc/i18n/declaration.tsvianpm run make-i18n..openhands/pre-commit.shmirrors the repo's local quality gate withnpm run lint && npm run test.
-
GitHub PR-review automation should stay aligned with the current OpenHands repo conventions: keep the review workflow at
.github/workflows/pr-review-by-openhands.yml, keep the companion.github/workflows/pr-review-evaluation.yml, auto-run on newly opened non-draft PRs andready_for_reviewevents from established contributors, still support thereview-thislabel /openhands-agent/all-hands-botreviewer triggers, use the OpenHands app LLM proxy defaults, and use the dual-trigger pattern (pull_requestfor same-repo PRs,pull_request_targetfor forks) so workflow changes can self-verify without widening fork secret exposure. -
The repo now includes
.agents/skills/custom-codereview-guide.md, adapted fromOpenHands/software-agent-sdk, to force PR reviews to always leave either an APPROVE or COMMENT review instead of silently finishing with no review object. -
HeroUI v3 migration notes:
- The repo now uses
@heroui/react@3.0.3plus@heroui/styles@3.0.3. src/tailwind.cssshould import@heroui/styles; the oldhero.tsTailwind plugin file was removed and should not be reintroduced.- Settings selectors no longer use the v2
AutocompleteAPIs.settings-dropdown-input.tsxandmodel-selector.tsxnow use HeroUI v3ComboBox+ListBoxpatterns, and the model/provider comboboxes must keep theirnameattributes soFormDatasubmission still populates settings diffs. - Tooltip consumers that used the old monolithic
Tooltipprops should prefer the sharedStyledTooltipwrapper so HeroUI v3 trigger/content typing stays centralized. - Visual verification artifacts for the migration live under
.pr/issue-44/; to reproduce the static screenshots, usenpm run build:mock, servebuild/(for example withpython -m http.server --directory build), and navigate client-side before capturing screenshots because a plain static server will not provide SPA deep-link fallbacks.
- The repo now uses
-
Library i18n is now namespace-scoped under
openhands:src/i18n/index.tsexportsOPENHANDS_I18N_NAMESPACE,translationResources, andwaitForI18n(),scripts/make-i18n-translations.cjsemitspublic/locales/<lang>/openhands.json, standalonesrc/entry.client.tsxexplicitly awaits i18n init, and host apps can register bundles via the@openhands/agent-server-gui/i18nsubpath export. -
Route decoupling note:
src/components/should stay free of directreact-routerimports. Route state now flows throughsrc/context/navigation-context.tsx, the standalone app bridges router state withsrc/routes/react-router-navigation-provider.tsx, and link-like UI should usesrc/components/shared/navigation-link.tsx. -
Test helper note:
test-utils.tsxnow wraps renders with a defaultNavigationProvider(currentPath: "/",conversationId: "test-conversation-id"). Navigation-sensitive tests can override that viarenderWithProviders(..., { navigation: { ... } }). -
CSS isolation for embeddable/hosted use now relies on a scoped wrapper attribute: all bundled CSS is prefixed under
[data-agent-server-ui]viapostcss-prefix-selectorinvite.config.ts, with selector exceptions handled bytransformAgentServerUISelector()insrc/styles/agent-server-ui-style-scope.ts. That transform must remap global selectors like:root,html, andbodydirectly onto the scoped shell instead of emitting impossible descendants such as[data-agent-server-ui] :root. -
Public embedding entry points should use
AgentServerUIProviders(scoped root on by default) orAgentServerUIRootfor manual control. The standalone app already renders its own scoped root insrc/root.tsx, sosrc/entry.client.tsxmust passwithStyleRoot={false}to avoid nesting duplicate shells. KeepAgentServerUIRootand the scoping constants re-exported fromsrc/lib/index.tsso library consumers can customize the host wrapper without reaching into private paths. -
Theme/customization tokens for the embedded shell are exposed as
--oh-*CSS variables. Override them throughstyleOverrides,style, or host CSS targeting[data-agent-server-ui]; Tailwind theme tokens insrc/tailwind.cssshould continue to reference those variables with@theme inlineso host apps can restyle the UI without reworking component class names. -
Regression coverage for the CSS isolation work lives in
__tests__/agent-server-ui-providers.test.tsx,__tests__/agent-server-ui-style-scope.test.ts, and the browser-leveltests/css-isolation.spec.tsPlaywright test. -
Library packaging notes:
- Public npm entrypoints now come from
src/index.ts→src/lib/index.ts, with domain barrels undersrc/components/{conversation,terminal,browser,files,settings,sidebar}/index.ts. npm run buildremains the standalone app build (react-router build), whilenpm run build:librunsvite buildin library mode plustsc -p tsconfig.lib.jsonto emit.d.tsfiles intodist/.- The library build relies on
vite.config.tswithBUILD_LIB=true, preserved modules indist/, and packageexportsentries that map root/subpaths todist/**/*.jsplus matching declaration files. - Declaration emit needs
src/library-env.d.tsand the narrowedtsconfig.lib.json; broadsrc/**/*.tsxdeclaration builds pulled in route-only files and missed?react/window globals.
- Public npm entrypoints now come from