Commit Graph
62 Commits
Author SHA1 Message Date
Rohit Malhotraandopenhands 4e6eee71fa feat(automations): add backend health check before loading automations UI (#185)
* feat(automations): add backend health check before loading automations UI

- Add checkHealth() method to AutomationService that calls /api/automation/health
- Create useAutomationHealth hook for React Query integration
- Create BackendNotConfigured component to display when backend is unavailable
- Update automations-list.tsx and automation-detail.tsx routes to check health
- Show 'Automations Backend Not Configured' UI with host URL and retry button
- Add i18n translations for new UI strings
- Add tests for hook and component

* fix: perform health check for cloud backends too, update message

- Remove assumption that cloud backends are always healthy
- Call /api/automation/health via cloud proxy for cloud backends
- Update message to generic 'Automations Unavailable' / 'not available right now'
- Remove host URL display (not needed for generic message)
- Rename component to BackendUnavailable (keep BackendNotConfigured as alias)

* fix: disable automation API calls when health check fails

- Add enabled option to useAutomations, useAutomationDetail, and useAutomationRuns hooks
- Only fetch automations data when the backend health check passes
- Update hook call sites to pass enabled flag based on isBackendHealthy
- Update tests to use new options-based API

* test: add checkHealth mock to automation-detail test

The test was failing because the health check now gates automation API calls.
Added checkHealth mock returning { status: 'ok' } so the test can proceed.

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-08 13:00:59 -04:00
465e776522 Home: rework workspace UX for the agent-server backend (#169)
* Rework home workspace UX for agent-server backend

- Drop the Repositories/Workspaces tab treatment on the home page. The
  agent-canvas build only ever talks to an agent-server backend, so
  RepoConnector now renders WorkspaceSelectionForm directly. The
  LaunchTabs component is removed; RepositorySelectionForm is left in
  place for any future cloud-backend variant.
- Add a 'Manage Workspaces' modal accessible from the workspace
  dropdown footer. It lists each saved workspace with a per-row
  Remove button (wired to useWorkspacesStore.removeWorkspace) and
  hides itself when there are no workspaces yet. Removing the
  currently selected workspace clears the form selection.
- Folder browser 'Use this folder' now adds only the chosen
  directory as a single workspace (named by its basename), instead
  of importing every immediate subdirectory. The button is enabled
  whenever a path is selected.
- Add HOME$MANAGE_WORKSPACES, HOME$MANAGE_WORKSPACES_EMPTY,
  HOME$REMOVE_WORKSPACE, HOME$DONE i18n keys and rephrase
  HOME$ADD_WORKSPACES to the singular '+ Add Workspace'.
- Update tests: remove obsolete LaunchTabs cases, rewrite the
  folder-import test to assert single-folder behavior, and add
  coverage for Manage Workspaces remove + hidden-when-empty.

Co-authored-by: openhands <openhands@all-hands.dev>

* Workspace parents: dynamically list subdirectories

Adds a 'workspace parent' concept alongside individual workspaces:

- 'Add Workspace' modal footer now has two action buttons:
    * 'Add this directory' (primary): saves the navigated folder as a
      single workspace, as before.
    * 'Add all subdirectories' (secondary): saves the folder as a
      LocalWorkspaceParent instead. The parent itself is not selectable;
      its immediate subdirectories are listed dynamically wherever
      workspaces are shown.
- New useResolvedWorkspaces() hook merges static workspaces with the
  live subdirectory listing of every saved parent (via FilesService
  .searchSubdirs and useQueries). Static workspaces win on duplicate
  paths.
- ManageWorkspacesModal grows a 'Workspace parents' section that lets
  users remove parents (their dynamic children disappear with them) and
  shows the current resolved children indented underneath.
- WorkspaceDropdown takes an optional showManage prop so the Manage
  entry stays visible when only parents (whose children may not have
  loaded yet) exist.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore(test): remove launch-tabs.test.tsx orphaned by LaunchTabs deletion

The LaunchTabs component was deleted in 'Rework home workspace UX
for agent-server backend', but the test file that imports it from
`#/components/features/home/launch-tabs` was missed in that change
(it was added on main concurrently and pulled in via merge), leaving
the suite unable to resolve the import.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore(lint): prettier nit in manage-workspaces-modal

Co-authored-by: openhands <openhands@all-hands.dev>

* Address workspace UX review feedback

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix cloud repo launcher on home screen

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-05-08 09:21:49 -07:00
Hiep Le 05cb9569d9 fix: route through active backend on environment switch (automations) (#182) 2026-05-08 20:28:39 +07:00
1d3c72ff3a V1 chat: small assorted fixes (#164)
* fix(chat): ignore agent-server's raw 'tool_name: {args}' summary fallback

The SDK's _extract_summary emits f'{tool_name}: {json_args}' when the LLM
omits a summary, which rendered as a huge unreadable JSON blob in the chat
title. Detect that pattern and fall through to the existing localized
titles (e.g. 'Editing <path>', 'Running <cmd>') so users see something
like 'edited file foo.txt' or 'ran command echo' instead.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix(chat): preserve whitespace in event titles by wrapping in span

The Trans-rendered title (e.g. "Editing <path>foo.txt</path>") is a
fragment of a text node + strong element. When inserted directly into
the flex row in GenericEventMessage, those become separate anonymous
flex items and the trailing space between "Editing" and the path is
collapsed, producing 'Editingfoo.txt'. Wrap the title in a span so the
inline content lays out normally.

Co-authored-by: openhands <openhands@all-hands.dev>

* Allow sending messages after a conversation enters error state

The chat input was force-disabled whenever the conversation's execution
status was ERROR or STUCK, so users had no way to send a follow-up
message and recover. The send pipeline itself doesn't reject in those
states (it sends via WebSocket if open, otherwise queues via REST), so
the UI gate was the only blocker.

Drop the isExecutionErrored check in CustomChatInput and remove the
now-unused executionStatus prop pass-through from InteractiveChatBox.

Co-authored-by: openhands <openhands@all-hands.dev>

* Suppress duplicate toast for v1 AgentErrorEvent

The agent error is already shown inline in the chat log (and via the
error banner). The legacy WS event handler was treating any event with
an 'error' field as a server error and firing a toast, double-notifying
on agent errors.

Co-authored-by: openhands <openhands@all-hands.dev>

* Stabilize flaky test timeouts

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-05-08 17:30:12 +07:00
Hiep Le f8ad3fa885 fix: show environment-switch overlay during backend changes (#178) 2026-05-08 17:22:56 +07:00
Hiep Le 498f44359f fix(frontend): align Automations pages with Automation repo color scheme (#176)
* fix: align Automations pages with Automation repo color scheme

* refactor: update the code based on feedback
2026-05-08 15:56:35 +07:00
Robert Brennanandopenhands 580a5e669d Changes tab: sleeker styling and squared tab container (#167)
* Make Changes tab sleeker and square the tab container

- Replace heavy bordered/rounded file-diff cards with subtle border-b
  dividers matching the tab title separator (#474A54)
- Tighten file row padding and recolor view-mode toggle buttons to
  match existing tab-title button hover palette
- Drop rounded corners and side borders from EditorContainer and
  markdown preview so editors flow into the next file row
- Remove p-4/gap-3 from changes-tab so rows form a continuous list
- Square the shared TabContainer/TabContentArea/loading shell

Co-authored-by: openhands <openhands@all-hands.dev>

* Fix test stability issues

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 23:01:23 -07:00
Robert Brennanandopenhands 22aa64f482 Conversation control bar: surface local git remote/branch + diff against HEAD (#166)
* Show local git remote/branch in conversation control bar

For conversations started from a local workspace (no selected_repository
on the conversation), shell out via the agent server's bash-execute
endpoint to read 'git remote get-url origin' and the current branch from
the conversation's working dir, and use them to populate the repo and
branch chips in the chat git control bar.

- New parseGitRemoteUrl util maps remote URLs to {host, owner/repo,
  provider}, recognizing github/gitlab/bitbucket/azure cloud hosts.
- New useLocalGitInfo query hook runs only when the conversation lacks a
  selected_repository, has a working_dir, and the runtime is ready.
  Failures (non-git checkout, etc.) silently fall back to the existing
  'No repo / No branch' display.
- GitControlBar layers the local fallback after conversation/task data
  but keeps pull/push/PR gated on conversation/task data only, so we
  don't enable provider-specific flows for repos the agent didn't clone.
- Repo/branch buttons now render the detected name even when no provider
  is known; the external-link styling is reserved for cases where we can
  build a real URL.

Co-authored-by: openhands <openhands@all-hands.dev>

* Bump typescript-client and request HEAD ref for git changes

Update @openhands/typescript-client from 7d20f11 to 6b9603f (latest main)
to pick up the new optional 'ref' option on RemoteWorkspace.gitChanges
and gitDiff. Pass { ref: 'HEAD' } from both the V1 and legacy git-service
adapters (and the cloud-proxy query strings) so the Changes panel shows
working tree + index versus the latest commit (i.e. staged + unstaged)
instead of diffing against the auto-detected upstream/default branch.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore(lint): prettier nits in use-local-git-info

Co-authored-by: openhands <openhands@all-hands.dev>

* Refactor git remote URL normalization

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 23:01:10 -07:00
Robert Brennanandopenhands adf407b786 V1 chat: group consecutive action/observation events (#165)
* Group consecutive action/observation events in V1 chat

Fold runs of consecutive ActionEvent/ObservationEvent cards into a
single collapsible EventGroup so a long sequence of tool calls no
longer dominates the chat scroll. Default state is collapsed; the
header shows '{n} actions completed' with a check once every action
in the group has its observation, or '{completed} of {total} actions'
plus the currently-running action's title while at least one is still
in flight. Expanding renders the original EventMessage children
verbatim, so each card still expands inline the way it did before.

- New: src/components/v1/chat/group-events.ts (folding helper, with
  EVENT_GROUP_MIN_SIZE threshold and an isGroupableEvent allow-list
  that excludes Finish/Think actions, planning/task-tracker
  observations, hooks, errors, and message events)
- New: src/components/v1/chat/event-message-components/event-group.tsx
- Wired into src/components/v1/chat/messages.tsx; no other call sites
  changed
- i18n keys EVENT_GROUP$ACTIONS_COMPLETED, EVENT_GROUP$ACTIONS_PROGRESS,
  EVENT_GROUP$EXPAND, EVENT_GROUP$COLLAPSE
- Tests: 12 cases for the grouping logic, 4 for the component render

Co-authored-by: openhands <openhands@all-hands.dev>

* Hoist agent thoughts out of action groups

When consecutive ActionEvent/ObservationEvent cards are folded into an
EventGroup, any thought attached to one of those actions used to render
inline inside the (default-collapsed) group, hiding the agent's
reasoning from the message pane.

groupEvents now emits a 'thought' RenderedItem whenever a groupable event
carries (or, for an observation, originates from) a non-empty
ActionEvent.thought. The hoisted thought flushes the current run and
starts a new one, so a thought between actions always shows in the
message pane and breaks the surrounding action group. messages.tsx
renders that item via ThoughtEventMessage and passes a new
suppressThought prop to EventMessage so the inline thought isn't
duplicated when the group is expanded. ThinkAction is excluded because
its thought IS its action body.

Co-authored-by: openhands <openhands@all-hands.dev>

* Group pairs of consecutive actions in the chat stream

Lower EVENT_GROUP_MIN_SIZE from 3 to 2 so two back-to-back groupable
action/observation events also fold into a collapsible EventGroup,
matching the behavior already applied to runs of 3+.

Co-authored-by: openhands <openhands@all-hands.dev>

* Hide inner left bar on events nested inside an EventGroup

Each GenericEventMessage renders its own border-l-2 left bar. When the
message is rendered inside an expanded EventGroup (which already draws
its own outer left bar), the inner bar shows up as a redundant white
bar next to the outer one.

Add an IsInEventGroupContext that EventGroup sets to true around its
expanded children, and have GenericEventMessage drop its left
border/padding when that context is set. Standalone rendering is
unchanged.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore(lint): fix prettier/eslint nits in v1 chat event-grouping files

Co-authored-by: openhands <openhands@all-hands.dev>

* Address PR feedback for event grouping

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 23:00:30 -07:00
Hiep Le 5030a0c9e4 feat: show active-backend sync badge on settings pages (#175) 2026-05-08 13:00:00 +07:00
Hiep Le 593bdc9bbc fix: forward disabled_skills to cloud proxy on skills toggle save (#173) 2026-05-08 11:52:32 +07:00
Graham Neubigandopenhands e8ffdd2829 Render ACP sub-agent tool-call events in chat (port of OpenHands#13994 + #14246 + #14247) (#142)
* Render ACP sub-agent tool-call events in chat (#132)

Port of upstream OpenHands frontend PRs:
  - OpenHands#13994 — initial ACPToolCallEvent rendering
  - OpenHands#14246 — drop the 'ACP · ' prefix from titles
  - OpenHands#14247 — suppress in_progress events to avoid empty-args flash

Adds end-to-end support for the new V1 ACPToolCallEvent surfaced by ACP
sub-agents (Claude Code, Codex, Gemini CLI, …). Each tool call is rendered
through the same GenericEventMessage wrapper used for observation events,
so the resulting card shape, success indicator, and markdown formatting
match the rest of the OpenHands chat.

Highlights:
  - New ACPToolCallEvent type + isACPToolCallEvent type guard, wired into
    the OpenHandsEvent union.
  - getACPToolCallContent + getACPToolCallTitleKey helpers — execute calls
    render as Command:/Output: blocks just like getTerminalObservationContent;
    non-execute calls render their raw_input as a JSON Input: block. Errors
    use **Error:**, missing output falls back to OBSERVATION$COMMAND_NO_OUTPUT,
    and very long output is truncated to MAX_CONTENT_LENGTH.
  - getACPToolCallResult — maps status + is_error to success | error |
    undefined, mirroring getObservationResult so in_progress calls render
    without a check mark.
  - shouldRenderEvent — hides in_progress ACP events to avoid an empty-args
    card flashing before the first populated event arrives (matches #14247).
  - handleEventForUI — dedupes ACP events by tool_call_id so streaming
    in_progress → completed/failed transitions update the card in place
    instead of stacking duplicate cards.
  - EventMessage dispatch — routes ACP events to GenericEventMessageWrapper
    after hook execution events, before generic action handling.
  - Five new ACTION_MESSAGE$ACP_* i18n keys (RUN / EDIT / READ / FETCH /
    TOOL) localised across all bundled languages, with the title rendered
    inline via the existing <cmd> Trans component (no 'ACP · ' prefix per
    #14246).

Tests: 39 new/modified vitest cases across get-acp-tool-call-content,
should-render-event, handle-event-for-ui, and event-message-acp-tool-call.

Visual verification + demo GIF: .pr/issue-132/

This commit was created by an AI agent (OpenHands) on behalf of the user.

Co-authored-by: openhands <openhands@all-hands.dev>

* Capture real ACP card rendering for #142 demo

Addresses @neubig's review feedback on PR #142 ('I did not see any
examples of ACP events being rendered in the interface'):

  - Updates demo.gif to actually show three ACPToolCallEvent cards
    rendering in the live conversation UI (collapsed view + expanded
    view with Command/Output/Input blocks).
  - Adds 04-acp-cards-collapsed.png + 05-acp-cards-expanded.png as the
    individual frames, plus 04-acp-rendering-notes.md explaining what
    each frame shows and why a synthetic event injection was needed
    (no Claude Code / Codex / Gemini CLI binary in the sandbox).
  - Adds capture.mjs (the Playwright script used to record the demo) so
    future re-records are reproducible.
  - In src/stores/use-event-store.ts, exposes the existing Zustand
    store on window.__OH_EVENT_STORE__ when import.meta.env.DEV is
    true. Tree-shaken from production builds; no behaviour change for
    end users. Useful as a general dev affordance for fixture/preview
    tooling beyond this PR.

Functional verification (unchanged from the original PR):

    npx vitest run         __tests__/components/v1/chat/event-content-helpers/get-acp-tool-call-content.test.ts         __tests__/components/v1/chat/event-message-acp-tool-call.test.tsx
    Test Files  2 passed (2)
         Tests  20 passed (20)

This commit was created by an AI agent (OpenHands) on behalf of the user.

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 20:35:11 -04:00
Rohit Malhotraandopenhands 6d1f0a74d9 feat: seed automation API key into agent-server secrets (#160)
* feat: seed automation API key into agent-server secrets

- Add seedAutomationSecret() that calls PUT /api/settings/secrets after
  agent-server is ready, storing the automation API key as
  OPENHANDS_AUTOMATION_API_KEY
- This makes the key available to agents during conversations so they can
  authenticate with the automation backend
- Add sessionApiKey to config for optional auth header
- Update help text and documentation

Co-authored-by: openhands <openhands@all-hands.dev>

* test: add tests for seed automation secret and fix CI failure

- Add tests for localApiKey and sessionApiKey config in buildConfig
- Add tests for secrets documentation in help output
- Fix root-layout-refetch.test.tsx unhandled rejection from framer-motion
  by adding async cleanup with microtask flush

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: detect SESSION_API_KEY when seeding automation secret

The seedAutomationSecret() function was failing with 401 Unauthorized
because it wasn't detecting the SESSION_API_KEY environment variable
that the agent-server uses by default (V0 config).

The agent-server checks these env vars for session API keys:
- SESSION_API_KEY (V0 config, picked up by default factory)
- OH_SESSION_API_KEYS_0 (V1 config)

The original code only checked OH_SESSION_API_KEY and VITE_SESSION_API_KEY,
missing the actual env vars the server reads. In OpenHands Cloud
environments, SESSION_API_KEY is set automatically, causing the 401.

This fix adds SESSION_API_KEY and OH_SESSION_API_KEYS_0 to the
fallback chain, with SESSION_API_KEY taking highest precedence
since it matches the agent-server's default behavior.

Adds tests verifying:
- SESSION_API_KEY detection
- OH_SESSION_API_KEYS_0 detection
- Precedence order (SESSION_API_KEY > OH_SESSION_API_KEYS_0 > others)

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: add retry logic and longer timeout for secret seeding

On slower systems, the agent-server may take longer to start up,
causing the secret seeding to fail with 'fetch failed' errors.

This fix adds:
1. Increased initial wait timeout from 30s to 60s for agent-server startup
2. Retry logic in seedAutomationSecret (5 retries with 2s delay)
3. Better error logging showing elapsed time and last error
4. AbortSignal.timeout on fetch requests to avoid hanging
5. Skip seeding if server fails to start (with warning message)

The retry logic handles transient failures during server warmup
but immediately fails on 401/403 auth errors (no point retrying).

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 17:29:46 -04:00
Hiep Le 951919f2c5 refactor: remove redundant AgentServerSettingsScreen (#162) 2026-05-08 03:06:37 +07:00
Hiep Le 9aebb35cfa fix: always send param to git branches search (#157) 2026-05-08 02:19:03 +07:00
6ebe7b4e7e feat: add automations frontend on /automations subpath (#141)
* feat: add automations frontend on /automations subpath

Port automations frontend from automation-repo to agent-canvas.

## Changes

### New Routes
- /automations - List view of all automations
- /automations/:automationId - Automation detail view

### New Components
- Automation list components: card, card-skeleton, group, empty-state, error-state
- Automation detail components: header, sections (config, prompt, plugins, activity)
- Shared UI components: toggle-switch, metadata-chip, status-badge, kebab-menu, search-input

### API Integration
- automation-service.api.ts - API client for automation CRUD operations
- Uses existing openHands axios client (shared base URL with agent server)

### MSW Mock Server Handlers
- automation-handlers.ts - Mock handlers for testing
- automations.mock.ts - Sample automation data
- automation-runs.mock.ts - Sample automation run data

### Tests
- API tests: automation-service.test.ts, automation-handlers.test.ts
- Component tests: toggle-switch, metadata-chip, search-input, error-state
- Detail component tests: section-card, run-status-badge, not-found-state

### Hooks
- use-automations.ts - React Query hook for fetching automations list
- use-automation-detail.ts - React Query hook for fetching single automation
- use-has-permission.ts - Permission checking utility hook

### Types
- automation.ts - TypeScript types for automation entities

### Icons
- Added SVG icons: activity, bell, calendar, check-circle, chevron-down,
  chevron-left, clock, cog, database, exclamation-circle, git-branch,
  kebab-vertical, power, puzzle, search, sparkle, target, trash, x-circle, x-mark

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: add local API key auth for automation backend

- Use VITE_AUTOMATION_API_KEY env var for frontend to authenticate
- Pass AUTOMATION_LOCAL_API_KEY to automation backend in dev mode
- Use dedicated axios instance with Bearer auth interceptor
- Add --refresh to uvx to ensure latest git commits are fetched
- URL-encode automation IDs in API paths

The default local API key is 'openhands-local-api-key' which matches
between the frontend and backend for local development.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: automation service tests and ingress port conflicts

- Fix automation-service.test.ts to mock axios instance correctly
  (was mocking openHands but service uses automationAxios)
- Use vi.hoisted() for mock functions available during vi.mock hoisting
- Change ingress test ports from 19000-19003 to 29000-29003 to avoid
  conflict with VS Code server on port 19000

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: add CORS origins for automation backend in dev mode

The automation backend defaults CORS origins to app.all-hands.dev,
which blocks localhost requests. Add localhost origins for the
ingress port and Vite dev server port.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: update create-instructions styling and add missing i18n keys

- Use semantic color tokens (text-content, text-basic, bg-base-secondary,
  bg-base, border-default) instead of hardcoded neutral-* colors
- Add all AUTOMATIONS$ i18n keys for the automations frontend

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-05-08 01:49:13 +07:00
Rohit Malhotraandopenhands 48b275a94f feat: track install immediately and use reverse proxy for ad blocker bypass (#155)
* feat: track install immediately without consent, add proxy support

BREAKING CHANGE: Install event (canvas_install) is now sent immediately
on first use, regardless of consent status. Users can still opt out via
VITE_DO_NOT_TRACK=1 or browser's Do Not Track setting.

Changes:
- trackInstall() sends the install event immediately without waiting for consent
- trackFirstUse() is now deprecated, calls trackInstall() for backward compat
- Session/custom events still require user consent
- Add VITE_POSTHOG_UI_HOST env var for reverse proxy support
- PostHog initialization now includes ui_host configuration

This change allows tracking library adoption even if users haven't made
a consent choice yet, while still respecting hard opt-outs.

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: default PostHog host to z.openhands.dev proxy

Use OpenHands' managed reverse proxy by default to bypass ad blockers.
The proxy at z.openhands.dev routes telemetry to PostHog's US region.

Library consumers can still override with VITE_POSTHOG_HOST if needed.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: remove deprecated trackFirstUse function

Only trackInstall() is now exported. No backwards compatibility shim needed.

Co-authored-by: openhands <openhands@all-hands.dev>

* docs: add privacy/GDPR compliance notes to install tracking

Address review feedback by documenting the privacy implications and
GDPR legal basis (legitimate interest under Article 6(1)(f)) for
sending the anonymous install event before consent.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: wait for translations before showing consent modal

- Use useTranslation's 'ready' state to wait for translations to load
- Add small delay (50ms) to ensure DOM is fully hydrated
- Prevents translation keys from flashing on first appearance

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 14:29:32 -04:00
Hiep Le dd744b13f6 feat: cloud backend support with multi-backend selector and SaaS proxy routing (#145)
* feat: multi-backend support with cloud SaaS proxy routing

* feat: route conversation export through cloud proxy on cloud backends

* fix: route conversation delete through cloud proxy on cloud backends

* fix: forward settings diffs verbatim through cloud proxy save

* fix: surface cloud-aware settings sub-pages and gate local-only routes

* fix: route secrets settings through cloud proxy on cloud backends

* fix: route conversation stop runtime through cloud proxy on cloud backends

* fix: re-expose planning agent UI for cloud backends and route plan file reads through cloud proxy

* fix: route Display Cost runtime fetch through cloud proxy and ungate local metrics without session API key

* fix: handle WAITING_FOR_SANDBOX task status from cloud backends to prevent UI crash

* fix: re-expose Public Share in conversation menu for cloud backends

* fix: redirect to home when switching backends from a conversation page

* fix: hide cloud orgs the API key can't access in backend selector

* feat: support running multiple local agent-servers with shared persistence

* fix: lint

* fix: failing tests
2026-05-08 01:17:40 +07:00
Rohit Malhotraandopenhands e2615344df feat: add first-use telemetry tracking with consent (#138)
* feat: add first-use telemetry tracking with consent

- Add telemetry service with consent management (src/services/telemetry.ts)
- Add useTelemetry React hook for easy integration (src/hooks/use-telemetry.ts)
- Add TelemetryConsentBanner component with i18n support
- Add local development server for testing (scripts/telemetry-dev-server.mjs)
- Add comprehensive tests for telemetry service and hook
- Export telemetry utilities from library index
- Respect DO_NOT_TRACK environment variable for privacy
- Uses localhost:8080 endpoint for development

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: address PR review feedback

- Make TELEMETRY_ENDPOINT configurable via VITE_TELEMETRY_ENDPOINT env var
- Make POSTHOG_API_KEY configurable via VITE_POSTHOG_API_KEY env var
- Add validation to skip telemetry if API key not configured (except localhost)
- Fix DO_NOT_TRACK to work in browser environments using VITE_DO_NOT_TRACK
- Also respect browser's navigator.doNotTrack standard
- Update consent banner hint text to reference correct env var
- Add documentation comments for all configuration options

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: hardcode PostHog credentials for centralized telemetry

- Use OpenHands PostHog project API key for all library users
- Use PostHog US Cloud endpoint (https://us.i.posthog.com/capture)
- Remove environment variable configuration for endpoint/API key
- Telemetry now automatically sends to centralized project when consent granted
- Users can still opt out via UI, VITE_DO_NOT_TRACK, or browser DNT setting

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: use separate PostHog API keys for dev and production

- Dev environment: phc_kBtz5nKmxVRRQ7HtPwr2QX9eMC5j65zE86QKocVNwb4U
- Production: phc_BgzfxKdgsYMLFTmJqt424ZoyVHvKFfrwttLimzdYTKFK
- Automatically selects key based on import.meta.env.DEV

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: use single production PostHog API key everywhere

Simplify by using the same API key for all environments.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: rename telemetry events

- library_first_use → canvas_install
- library_session_start → canvas_new_session

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: migrate telemetry to PostHog SDK

Replace raw HTTP requests with PostHog SDK for:
- Automatic event batching
- Built-in retry logic with exponential backoff
- Offline support (queues events, sends when back online)
- Automatic session tracking
- Better device/browser info enrichment

Benefits:
- More reliable event delivery
- Reduced network requests
- Cleaner code with less manual state management
- Future-proof for feature flags, session replay, etc.

Co-authored-by: openhands <openhands@all-hands.dev>

* refactor: remove redundant hasTrackedFirstUse state in hook

The trackFirstUse() function already has built-in deduplication via
localStorage, so the local React state was unnecessary. Simplified
the hook and added a comment explaining the deduplication mechanism.

Co-authored-by: openhands <openhands@all-hands.dev>

* chore: remove obsolete telemetry dev server

The local dev server was used when telemetry used raw HTTP requests
to a configurable endpoint. Now that we use the PostHog SDK with
the real PostHog endpoint, this is no longer needed.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: remove trailing comma in package.json

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: address PR review feedback

- Make POSTHOG_API_KEY configurable via VITE_POSTHOG_API_KEY env var
- Make POSTHOG_HOST configurable via VITE_POSTHOG_HOST env var
- Add session deduplication using sessionStorage to prevent duplicate
  canvas_new_session events from multiple hook instances
- Clear sessionStorage in clearTelemetryData()

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: use dynamic imports for PostHog SSR compatibility

- Convert top-level posthog-js import to dynamic import for SSR safety
- Add getPostHog() lazy loader that only imports in browser context
- Make setTelemetryConsent, clearTelemetryData, getPostHogInstance async
- Update documentation to clarify default telemetry destination
- Update tests for async function signatures

This ensures the library works correctly in SSR frameworks (Next.js, Remix,
etc.) that might import this module server-side.

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: add telemetry consent banner to app layout

The consent banner now appears on all pages until the user explicitly
accepts or declines telemetry. This ensures users are always prompted
for consent on their first visit regardless of which page they land on.

Co-authored-by: openhands <openhands@all-hands.dev>

* refactor: update telemetry consent banner to modal style

- Changed from bottom banner to centered modal overlay (matching OpenHands)
- Uses ModalBackdrop, ModalBody, BaseModalTitle, BaseModalDescription
- Single checkbox with 'Confirm preferences' button pattern
- Full-screen overlay blocks interaction until user makes a choice
- Added i18n keys: TELEMETRY$SEND_ANONYMOUS_DATA, TELEMETRY$CONFIRM_PREFERENCES

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: ensure PostHog is initialized before tracking events

- Made grantConsent/denyConsent in useTelemetry hook async to ensure
  PostHog initialization completes before state update triggers tracking
- Updated tests for async consent functions
- This fixes a race condition where trackFirstUse() could be called before
  PostHog's opt_in_capturing() had been executed

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 13:18:01 -04:00
988cfed5c6 feat: add automation backend integration with standalone ingress proxy (#127)
* feat: add automation backend integration with standalone ingress proxy

- Add scripts/ingress.mjs: standalone HTTP reverse proxy for routing traffic
  to multiple backends based on URL path prefix
- Add scripts/dev-with-automation.mjs: orchestrates full stack with
  agent-server, automation backend (both via uvx), Vite, and ingress
- Make 'npm run dev' run full stack by default (was dev:safe, now dev:automation)
- Rename 'npm run dev:safe' to 'npm run dev:minimal' for agent-server + Vite only
- Update README with new quickstart showing full stack as default
- Update AGENTS.md with architecture documentation

Architecture:
  http://localhost:8000 (Ingress)
  ├── /api/automation/* → Automation Backend (:18001)
  ├── /api/*, /sockets  → Agent Server (:18000)
  └── /* (default)      → Vite Dev Server (:3001)

* test: add tests for ingress and dev-with-automation scripts

- Add __tests__/scripts/ingress.test.ts with 14 tests covering:
  - CLI argument parsing (--help, --port, --route, --default)
  - Route matching (exact, prefix, longest-match-first)
  - Proxy functionality (forwarding, query params, error handling)
  - 502 response when backend unavailable
  - 503 response for unmatched routes with no default

- Add __tests__/scripts/dev-with-automation.test.ts with 19 tests covering:
  - buildAutomationCommand() with various git refs/repos
  - buildConfig() port and path configuration
  - CLI --help output
  - Graceful exit when uvx is missing

- Export testable functions from dev-with-automation.mjs

* fix: prevent dev-with-automation from auto-executing when imported

The script was calling main() unconditionally, which caused test failures
when vitest imported the module. Now check if the module is the main entry
point before executing.

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-05-07 12:02:15 -04:00
Graham Neubigandopenhands 84ed4d1217 Show full model name in conversation header (#135) (#139)
Port of OpenHands/OpenHands#14284. The LLM model badge in the conversation
header was constrained to max-w-[150px] with an inner `truncate`, which
cut off long model identifiers such as `litellm_proxy/claude-sonnet-4-5-20250929`
to `litellm_proxy/cl…`. Drop the width cap and inner truncate, and apply
`whitespace-nowrap` to the outer span so the full name renders inline.

Also adds scripts/record-demo.mjs - a small playwright recorder used to
capture the verification GIF under .pr/issue-135/ - and updates the
existing test to assert the un-truncated structure.

Closes #135.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-07 10:46:05 -04:00
Hiep Le c3433d9a58 feat: temporarily disable planning agent UI and drop /new from slash menu (#126) 2026-05-07 03:29:47 +07:00
Hiep Le 6b4fb427e9 feat: always render Workspaces tab regardless of Git PAT (#125) 2026-05-07 03:06:05 +07:00
Hiep Le 7e221c4b65 fix(frontend): revert HeroUI v3 to v2 and scope body for portal'd popovers (#124)
* feat: revert HeroUI v3 to v2 and scope body for portal'd popovers

* fix: failing tests

* fix: failing tests
2026-05-07 02:24:35 +07:00
Hiep Le 3de362249f feat: derive provider_tokens_set from local git provider cache (#123) 2026-05-07 00:55:55 +07:00
Graham Neubigandopenhands 62871d3a7b rename agent-server-gui to agent-canvas (#121)
- npm package: @openhands/agent-server-gui -> @openhands/agent-canvas
- README/DEVELOPMENT/AGENTS/codereview guide updated to new name
- GitHub URL in onboarding screen + tests updated
- dev launcher env vars renamed: OH_GUI_SAFE_* -> OH_CANVAS_SAFE_*
- default state dir: ~/.openhands/agent-server-gui -> ~/.openhands/agent-canvas
- i18n strings replace 'GUI' phrasing with 'Agent Canvas' across settings,
  upgrade, onboarding, and unavailable copy
- Test fixtures, working-dir paths, and library-consumer smoke updated

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-06 13:07:01 -04:00
Rohit Malhotraandopenhands cccecf1100 Fix uvx command to use --from syntax for PyPI packages (#122)
The openhands-agent-server package exposes an executable named
'agent-server', not 'openhands-agent-server'. When using PyPI versions
(either specific or latest), we need to use the --from syntax:
  uvx --from openhands-agent-server agent-server

This fixes the error:
  An executable named 'openhands-agent-server' is not provided by
  package 'openhands-agent-server'.
  Use 'uvx --from openhands-agent-server agent-server' instead.

Fixes #117

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-06 12:07:41 -04:00
Hiep Le 6a5d1b0049 feat: migrate folder browser to /api/file/search_subdirs (#100) 2026-05-06 21:40:45 +07:00
Rohit Malhotraandopenhands f9006b4bf0 feat: use agent server APIs for settings persistence (#98)
* feat: use agent server APIs for settings persistence

- Replace localStorage with HTTP API for settings storage
- Use `X-Expose-Secrets: encrypted` header for GET /api/settings
  to receive encrypted secrets (not exposing raw values)
- Use `secrets_encrypted: true` in start conversation payload
- Add `getSettingsForConversation()` to build encrypted settings
  payload for conversation start endpoint
- Update secrets service to use /api/settings/secrets endpoints
- Add mock handlers for settings and secrets API endpoints
- Update tests for new API-based settings flow

This integrates with software-agent-sdk PR #3060
(feat/encrypted-secrets-in-transit) which adds server-side
encryption support for secrets in transit.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: update test mocks for encrypted settings API and add OH_SECRET_KEY support

- Update use-create-conversation-metadata.test.ts to mock getSettingsForConversation()
  which is now called by buildStartConversationRequestWithEncryptedSettings
- Skip flaky onOpen websocket test that times out intermittently in CI
- Add OH_SECRET_KEY environment variable support in dev-safe.mjs:
  - Uses default key for local development
  - Can be overridden via OH_SECRET_KEY environment variable
  - Logs secret key source at startup

Co-authored-by: openhands <openhands@all-hands.dev>

* docs: update AGENTS.md for settings API and OH_SECRET_KEY

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: update secrets service to use agent-server API routes

Changes:
- Update SecretsService to use /api/settings/secrets endpoints instead of /api/v1/secrets
- Simplify secrets-service.types.ts to remove unused pagination types
- Update use-get-secrets hook to do client-side filtering (agent-server doesn't support pagination)
- Update mock handlers to only use agent-server API routes
- Update secrets-settings test to mock getSecrets instead of searchSecrets
- Remove pageSize option from useSearchSecrets since agent-server doesn't paginate

The agent-server API routes (per SDK PR #3060):
- GET /api/settings/secrets - List secrets (names/descriptions only)
- GET /api/settings/secrets/{name} - Get secret value
- PUT /api/settings/secrets - Upsert secret
- DELETE /api/settings/secrets/{name} - Delete secret

Co-authored-by: openhands <openhands@all-hands.dev>

* docs: update AGENTS.md for secrets API routes

- Document the agent-server secrets CRUD routes in MSW handlers list
- Update git provider token persistence note to reflect server-side storage

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: update secret name validation to match agent-server requirements

- Change pattern from '^\S*$' (no whitespace) to '^[a-zA-Z][a-zA-Z0-9_]{0,63}$'
- Add title prop to SettingsInput component for validation error messages
- Secret names must: start with letter, contain only letters/numbers/underscores, be 1-64 chars

Co-authored-by: openhands <openhands@all-hands.dev>

* feat: include custom secrets in conversation requests via LookupSecret

Custom secrets configured in Settings > Secrets are now automatically
included in conversation start requests. Instead of exposing secret values
to the frontend, we use LookupSecret entries that point to the agent-server
endpoint /api/settings/secrets/{name}. The agent-server fetches the actual
values at runtime.

Changes:
- Add LookupSecret interface to agent-server-adapter.ts
- Add customSecrets option to StartConversationOptions
- Build LookupSecret entries for each custom secret in buildStartConversationRequest
- Update buildStartConversationRequestWithEncryptedSettings to fetch and include
  custom secrets list from SecretsService.getSecrets()
- Include X-Session-API-Key header in LookupSecret when configured

This ensures secrets never touch the frontend in plaintext while still
making them available to conversations.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: address review comments - no localStorage fallback, retry logic, SDK docs

Review feedback addressed:
1. secrets-service.ts: Server storage MUST succeed before updating localStorage
   - addGitProvider now stores to server FIRST, only updates localStorage on success
   - createSecret/updateSecret/deleteSecret now throw on failure (no silent returns)
   - Added retry logic with exponential backoff for all API calls

2. settings-service.api.ts: No silent fallback for encrypted settings
   - getSettingsForConversation now throws if encrypted fetch fails
   - Conversations should not start with broken/redacted credentials
   - Added retry logic with exponential backoff

3. AGENTS.md: Document SDK dependency
   - Settings persistence APIs require SDK PR #3060
   - Until released, npm run dev defaults to main branch
   - Documented git provider storage design (server + localStorage)

4. dev-safe.mjs: Default to SDK main branch
   - Added DEFAULT_GIT_REF='main' constant
   - npm run dev now uses main until settings APIs are released
   - TODO comment to update once released

Note: Git provider tokens still use localStorage for frontend git API calls
(repo search, branches), but MUST succeed on server first.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: update server secret when only host changes

When updating just the host (empty token), the server secret's description
must also be updated to keep metadata in sync. Previously, only localStorage
was updated, violating the 'server storage must succeed first' principle.

Now the host-only update path also calls createSecret() to update the
server secret's description before updating localStorage.

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-05 20:39:46 -04:00
Rohit Malhotraandopenhands 6fa219cf25 feat: use uvx for temporary agent-server installation in dev mode (#99)
* feat: use uvx for temporary agent-server installation in dev mode

- Replace direct agent-server CLI invocation with uvx temporary install
- Add OH_AGENT_SERVER_VERSION env var for specific PyPI versions
- Add OH_AGENT_SERVER_GIT_REF env var for git commits/branches
- Auto-install uv in .openhands/setup.sh if not present
- Update documentation (README, DEVELOPMENT.md, AGENTS.md)
- Add comprehensive tests for buildAgentServerCommand()

This removes the requirement to permanently install agent-server via
'uv tool install'. Users only need uv installed, and npm run dev will
automatically download and run the appropriate agent-server version.

Co-authored-by: openhands <openhands@all-hands.dev>

* fix: use subdirectory syntax for git ref in uvx monorepo

The software-agent-sdk is a uv workspace monorepo with packages in
subdirectories (openhands-agent-server/, openhands-tools/, etc.).

When installing from git, uvx requires the #subdirectory= fragment to
specify which package to install from the workspace.

Tested with: OH_AGENT_SERVER_GIT_REF=main npm run dev

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-05 13:14:14 -04:00
Hiep Le e90db6fa53 feat(frontend): browser-side git provider integrations (#96)
* feat: browser-side git provider integrations

* feat: add workspaces tab for launching v1 conversations from local folders
2026-05-05 22:44:11 +07:00
Hiep Le 5d85eb0f5d fix: default v1 conversation title (#91) 2026-05-05 01:39:37 +07:00
Hiep Le 1cc616921f feat(frontend): isolate per-conversation working directories (#90)
* feat: isolate per-conversation working directories

* fix: failing tests
2026-05-05 01:32:24 +07:00
Hiep Le ba1d192f92 refactor: remove sandbox concept (#89) 2026-05-04 22:21:02 +07:00
Hiep Le dd8a44231f fix(frontend): remove SaaS and enterprise terminology (#88)
* fix: remove SaaS and enterprise terminology

* refactor: remove unrelated file
2026-05-04 21:27:45 +07:00
Hiep Le c27acde8a0 refactor: remove organization concept (#83) 2026-05-04 20:02:32 +07:00
Graham Neubigandopenhands 25510608a1 Fix scoped UI root foreground inheritance (#66)
Restore the default foreground color on the themed AgentServerUIRoot wrapper so inherited text and currentColor icons match the pre-scoping dark theme again.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-01 12:20:17 -07:00
Graham Neubigandopenhands 8a8288d3f6 Default working dir to workspace/project (#63)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 22:55:49 -07:00
Graham Neubigandopenhands ea2135386b Implement scoped embeddable styles (#55)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 22:46:04 -07:00
Graham Neubigandopenhands 8bfae71939 Namespace library i18n resources (#62)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 22:45:31 -07:00
Graham Neubigandopenhands 35543b406c Improve agent server onboarding and recovery flow (#59)
* Improve agent server onboarding recovery flow

Co-authored-by: openhands <openhands@all-hands.dev>

* Simplify agent server settings UI

Co-authored-by: openhands <openhands@all-hands.dev>

* Trim agent server onboarding directions

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 13:36:53 -07:00
Graham Neubigandopenhands aaecc8961b Add configurable agent server settings (#57)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 10:42:34 -04:00
Graham Neubigandopenhands 22b7d6e842 Add library build mode and component barrel exports (#54)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 07:37:01 -04:00
Graham Neubigandopenhands f62063e413 Extract composable provider stack (#46)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 06:53:03 -04:00
Graham Neubigandopenhands 254e2d7edd Decouple core components from react-router (#48)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 06:51:07 -04:00
Graham Neubigandopenhands 82f27cc661 Restore Git settings with local provider token persistence (#45)
* Disable unsupported Git provider token saving

* Restore local Git settings persistence

Co-authored-by: openhands <openhands@all-hands.dev>

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-30 06:24:08 -04:00
Graham Neubigandopenhands 5459361c24 Add repo-specific PR review guide (#52)
* Add custom PR codereview guide

* Strengthen PR review posting instruction

* Stabilize websocket close callback test

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-29 16:58:16 -04:00
Graham Neubigandopenhands 22013de897 Upgrade HeroUI to v3 (#50)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-28 23:53:34 -04:00
Graham Neubig 0dddd7a6e7 Fix Windows dev startup (#43) 2026-04-28 21:31:37 -04:00
Graham Neubigandopenhands 3b754b5703 Improve missing agent-server setup guidance (#42)
Add actionable dev-safe output when agent-server is missing, including README and uv installation guidance, and update tests plus quickstart docs.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-27 10:40:06 -04:00