agent-server-gui

Run this with OpenHands Agent Server first

If you only read one section of this README, read this one. Most users will want to clone this repo, point it at a real openhands-agent-server, and start using the UI immediately.

Prerequisites

  • Node.js 22.12.x or later
  • npm
  • A running OpenHands Agent Server (agent-server)

1. Clone and install the frontend

git clone https://github.com/neubig/agent-server-gui.git
cd agent-server-gui
npm install

2. Install and start OpenHands Agent Server

If you do not already have the backend installed, install uv first (OpenHands SDK recommends uv 0.8.13+):

curl -LsSf https://astral.sh/uv/install.sh | sh

Then install or upgrade the agent server package together with the tool/workspace dependencies that the SDK getting started page lists separately:

uv tool install -U \
  --with openhands-tools \
  --with openhands-workspace \
  --with libtmux \
  openhands-agent-server

uv tool install exposes the server as the agent-server CLI. If ~/.local/bin is not already on your PATH, either add it or run the binary via its full path.

Then start the backend on the default local port:

agent-server --host 127.0.0.1 --port 8000

The frontend expects the backend at 127.0.0.1:8000 by default, so this is the easiest from-scratch setup.

If you start the backend with SESSION_API_KEY or OH_SESSION_API_KEYS_0, every /api/* route is authenticated with X-Session-API-Key. In that case the frontend must send the same key via VITE_SESSION_API_KEY.

If you prefer installing from source or want the full SDK setup flow, see the OpenHands SDK docs: https://docs.openhands.dev/sdk/getting-started

3. Optional: create a .env file

If your backend is not running on 127.0.0.1:8000, or if it requires a session API key, create a .env file:

cp .env.sample .env

Then update the values you need:

VITE_BACKEND_HOST="127.0.0.1:8000"
VITE_BACKEND_BASE_URL="http://127.0.0.1:8000"
VITE_FRONTEND_PORT="3001"
# Use the same value as backend SESSION_API_KEY or OH_SESSION_API_KEYS_0 when auth is enabled.
# VITE_SESSION_API_KEY="your-session-api-key"
# VITE_WORKING_DIR="/absolute/path/to/the-workspace-used-by-agent-server"

Notes:

  • VITE_BACKEND_HOST is used by the Vite dev proxy for /api, /server_info, and /sockets.
  • VITE_BACKEND_BASE_URL is used by browser-side direct requests. Keep it pointed at the same backend.
  • VITE_WORKING_DIR should match the workspace path the backend will use when starting new conversations.
  • If the backend is secured with SESSION_API_KEY or OH_SESSION_API_KEYS_0, set VITE_SESSION_API_KEY to the same value or live requests will fail with 401 Unauthorized.
  • If your backend does not require X-Session-API-Key, leave VITE_SESSION_API_KEY unset.

4. Start the frontend

npm run dev

This starts the frontend on http://localhost:3001.

5. First-run sanity check

After the page opens:

  • / should load without errors
  • /settings should load
    • on secured backends, make sure VITE_SESSION_API_KEY matches the backend session key
    • configure a working LLM model + API key under Settings > LLM before running the first live task
    • if your backend is too old to expose settings schemas, the UI will show an actionable SDK settings schema unavailable. message instead of crashing
  • you should be able to open or create a conversation
  • /conversations/:id should load conversation content
  • if you want the Git / Changes panels to point at this repo, set VITE_WORKING_DIR to the actual repo root (for this checkout that is /workspace/project/agent-server-gui), not just the parent workspace directory

Mock mode

If you want to run the frontend without a live backend, use:

npm run dev:mock
# or
npm run dev:mock:saas

Overview

This repository is a near-direct port of the OpenHands frontend adapted to talk directly to software-agent-sdk / agent_server without the usual OpenHands app backend.

Tech Stack

  • Remix SPA Mode (React + Vite + React Router)
  • TypeScript
  • Redux
  • TanStack Query
  • Tailwind CSS
  • i18next
  • React Testing Library
  • Vitest
  • Mock Service Worker

Building for Production

There is no Makefile in this repository. Use the npm scripts instead:

npm run build
npm run start

Environment Variables

The frontend application uses the following environment variables:

Variable Description Default Value
VITE_BACKEND_BASE_URL Full base URL for the agent server used by direct browser requests current browser origin
VITE_BACKEND_HOST Backend host used by the Vite dev proxy 127.0.0.1:8000
VITE_SESSION_API_KEY Optional X-Session-API-Key header value for authenticated agent_server instances -
VITE_WORKING_DIR Workspace path sent when starting new conversations /workspace/project
VITE_WORKER_URLS Optional comma-separated worker/app URLs for the Browser tab -
VITE_ENABLE_BROWSER_TOOLS Set to false to omit BrowserToolSet from new conversation payloads true
VITE_MOCK_API Enable/disable API mocking with MSW false
VITE_MOCK_SAAS Simulate SaaS mode in development false
VITE_USE_TLS Use HTTPS/WSS for the Vite proxy target false
VITE_FRONTEND_PORT Port to run the frontend application 3001
VITE_INSECURE_SKIP_VERIFY Skip TLS certificate verification for proxied backend requests false
VITE_GITHUB_TOKEN GitHub token for repository access (used in some tests) -

You can create a .env file in the project directory with these variables based on .env.sample.

Project Structure

frontend
├── __tests__ # Tests
├── public
├── src
│   ├── api # API calls
│   ├── assets
│   ├── components
│   ├── context # Local state management
│   ├── hooks # Custom hooks
│   ├── i18n # Internationalization
│   ├── mocks # MSW mocks for development
│   ├── routes # React Router file-based routes
│   ├── services
│   ├── state # Redux state management
│   ├── types
│   ├── utils # Utility/helper functions
│   └── root.tsx # Entry point
└── .env.sample # Sample environment variables

Components

Components are organized into folders based on their domain, feature, or shared functionality.

components
├── features # Domain-specific components
├── layout
├── modals
└── ui # Shared UI components

Features

  • Real-time updates with WebSockets
  • Internationalization
  • Router data loading with Remix
  • User authentication with GitHub OAuth (if saas mode is enabled)

Testing

Testing Framework and Tools

We use the following testing tools:

  • Test Runner: Vitest
  • Rendering: React Testing Library
  • User Interactions: @testing-library/user-event
  • API Mocking: Mock Service Worker (MSW)
  • Code Coverage: Vitest with V8 coverage

Running Tests

To run all tests:

npm run test

To run tests with coverage:

npm run test:coverage

Testing Best Practices

  1. Component Testing

    • Test components in isolation
    • Use our custom renderWithProviders() that wraps the components we want to test in our providers. It is especially useful for components that use Redux
    • Use render() from React Testing Library to render components
    • Prefer querying elements by role, label, or test ID over CSS selectors
    • Test both rendering and interaction scenarios
  2. User Event Simulation

    • Use userEvent for simulating realistic user interactions
    • Test keyboard events, clicks, typing, and other user actions
    • Handle edge cases like disabled states, empty inputs, etc.
  3. Mocking

    • We test components that make network requests by mocking those requests with Mock Service Worker (MSW)
    • Use vi.fn() to create mock functions for callbacks and event handlers
    • Mock external dependencies and API calls (more info)[https://mswjs.io/docs/getting-started]
    • Verify mock function calls using .toHaveBeenCalledWith(), .toHaveBeenCalledTimes()
  4. Accessibility Testing

    • Use toBeInTheDocument() to check element presence
    • Test keyboard navigation and screen reader compatibility
    • Verify correct ARIA attributes and roles
  5. State and Prop Testing

    • Test component behavior with different prop combinations
    • Verify state changes and conditional rendering
    • Test error states and loading scenarios
  6. Internationalization (i18n) Testing

    • Test translation keys and placeholders
    • Verify text rendering across different languages

Example Test Structure:

import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi } from "vitest";

describe("ComponentName", () => {
  it("should render correctly", () => {
    render(<Component />);
    expect(screen.getByRole("button")).toBeInTheDocument();
  });

  it("should handle user interactions", async () => {
    const mockCallback = vi.fn();
    const user = userEvent.setup();

    render(<Component onClick={mockCallback} />);
    const button = screen.getByRole("button");

    await user.click(button);
    expect(mockCallback).toHaveBeenCalledOnce();
  });
});

Example Tests in the Codebase

For real-world examples of testing, check out these test files:

  1. Chat Input Component Test: __tests__/components/chat/chat-input.test.tsx

    • Demonstrates comprehensive testing of a complex input component
    • Covers various scenarios like submission, disabled states, and user interactions
  2. File Explorer Component Test: __tests__/components/file-explorer/file-explorer.test.tsx

    • Shows testing of a more complex component with multiple interactions
    • Illustrates testing of nested components and state management

Test Coverage

  • Aim for high test coverage, especially for critical components
  • Focus on testing different scenarios and edge cases
  • Use code coverage reports to identify untested code paths

Continuous Integration

Tests are automatically run during:

  • Pre-commit hooks
  • Pull request checks
  • CI/CD pipeline

Contributing

Please read the CONTRIBUTING.md file for details on our code of conduct, and the process for submitting pull requests to us.

Troubleshooting

TODO

S
Languages
TypeScript 93.7%
JavaScript 4.7%
Python 0.9%
Shell 0.3%
CSS 0.2%
Other 0.1%