Commit Graph
47 Commits
Author SHA1 Message Date
b570b4a2f6 fix: buildNpmScriptCommand always uses cmd.exe on Windows (#734)
* fix: buildNpmScriptCommand always uses cmd.exe on Windows

On Windows, npm sets npm_execpath to a path like
  C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js
which contains spaces. buildNpmScriptCommand was returning that
path as a spawn argument with the full node.exe path as the command.
spawnService uses shell:true on Windows, so Node.js passes the
unquoted command to cmd.exe:

  cmd.exe /d /s /c C:\Program Files\nodejs\node.exe ...

cmd.exe splits on the space and fails with
  'C:\Program' is not recognized as an internal or external command
causing Vite to exit with code 1 immediately after npm run dev.

Fix: check platform === 'win32' BEFORE checking npm_execpath so
Windows always uses the safe cmd.exe /d /s /c npm run <script> form.

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

* fix: buildNpmScriptCommand always uses cmd.exe on Windows

On Windows, npm sets npm_execpath to a path like
  C:\Program Files\nodejs\node_modules\npm\bin\npm-cli.js
which contains spaces. buildNpmScriptCommand was returning that
path as a spawn argument with the full node.exe path as the command.
spawnService uses shell:true on Windows, so Node.js passes the
unquoted command to cmd.exe:

  cmd.exe /d /s /c C:\Program Files\nodejs\node.exe ...

cmd.exe splits on the space and fails with
  'C:\Program' is not recognized as an internal or external command
causing Vite to exit with code 1 immediately after npm run dev.

Fix: check platform === 'win32' BEFORE checking npm_execpath so
Windows always uses the safe cmd.exe /d /s /c npm run <script> form.

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

* fix: AnimatePresence mode=wait expects one child, not two

ChatStatusIndicator had two separately-keyed motion.span children inside
<AnimatePresence mode="wait">. framer-motion's wait mode expects exactly ONE
child to exit before the next enters; two children trigger the repeated warning:
  "attempting to animate multiple children within AnimatePresence,
   but its mode is set to 'wait'"

Fix: wrap both elements in a single motion.span with unified key={status}
and className="contents" (CSS display:contents preserves flex layout).

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

* fix: set PYTHONUTF8=1 in agent-server/automation env on Windows

Python on Windows defaults to the system ANSI codepage (cp1252). The agent-server
writes metadata JSON containing emoji (e.g. U+2705 ✅) that cp1252 cannot encode,
producing UnicodeEncodeError → POST /api/conversations 500. Old UTF-8 conversation
files also fail to load at startup (UnicodeDecodeError). PYTHONUTF8=1 enables
Python's UTF-8 mode (PEP 540) for the process, matching Linux/macOS behaviour.

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

* fix: set PYTHONUTF8=1 in agent-server/automation env on Windows

Python on Windows defaults to the system ANSI codepage (cp1252). The agent-server
writes metadata JSON containing emoji (e.g. U+2705 ✅) that cp1252 cannot encode,
producing UnicodeEncodeError → POST /api/conversations 500. Old UTF-8 conversation
files also fail to load at startup (UnicodeDecodeError). PYTHONUTF8=1 enables
Python's UTF-8 mode (PEP 540) for the process, matching Linux/macOS behaviour.

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

* refactor: remove unrelated file

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: hieptl <hieptl.developer@gmail.com>
2026-05-28 16:29:55 +07:00
cbeeee002e fix: inject runtime session key into index.html for published binary (#795)
* fix: inject runtime session key into index.html for published binary

The globally installed agent-canvas binary starts the agent-server with a
persisted session API key (~/.openhands/agent-canvas/session-api-key.txt)
as OH_SESSION_API_KEYS_0, making auth required. However, the pre-built
static frontend in the npm package has a different (or empty)
VITE_SESSION_API_KEY baked in at publish time, so every API request gets
401 Unauthorized.

Fix: static-server.mjs now accepts --session-api-key <key> and injects a
tiny bootstrap <script> before </head> in every index.html response. The
script seeds the key into localStorage['openhands-agent-server-config']
only if no key is already stored there, so explicit user overrides (via
Settings > Agent Server) are always preserved.

dev-with-automation.mjs and dev-static.mjs both pass
--session-api-key ${config.sessionApiKey} when spawning the static server,
so the runtime key is always available regardless of what was baked into
the bundle.

Tests: added 8 new cases to __tests__/scripts/static-server.test.ts
covering parseArgs, injection in direct and SPA-fallback index.html
responses, no injection for non-html assets, cache headers, and null key.

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

* fix(docker): pass runtime session key to static-server so frontend can authenticate

The entrypoint computed EFFECTIVE_SESSION_KEY and forwarded it to the
agent-server (OH_SESSION_API_KEYS_0) and automation backends, but did not
pass it to the static-server. As a result the pre-built index.html served
with no session key injected, so every browser API call received 401.

Wire --session-api-key "$EFFECTIVE_SESSION_KEY" into the static-server
launch command so the runtime key is injected into index.html responses
(via the mechanism added in this branch to static-server.mjs).

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

* fix: address review suggestions on session key injection

- static-server.mjs: add comment clarifying replace() targets first
  </head> only; fall back to inserting before </body> when </head> is
  absent (avoids prepending before <!DOCTYPE html>)
- docker/entrypoint.sh: add comment documenting source of
  EFFECTIVE_SESSION_KEY before the static-server invocation
- static-server.test.ts: add test for </head>-absent fallback path
  confirming injection lands before </body>

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Rohit Malhotra <rohitvinodmalhotra@gmail.com>
2026-05-27 11:12:23 -04:00
Rohit Malhotraandopenhands e2dd1b5f17 fix: unify session and automation API keys into a single credential with consistent header (#681)
* fix: unify session and automation API keys into a single credential

Both the agent-server and automation backend now share the same API key
value. The agent-server validates it via `X-Session-API-Key` and the
automation backend validates it via `Authorization: Bearer …` — different
header formats, same credential.

Changes:
- Frontend: automation axios client reads `VITE_SESSION_API_KEY` instead
  of the now-removed `VITE_AUTOMATION_API_KEY`
- Dev launcher: removed separate `AUTOMATION_LOCAL_API_KEY` generation
  and persistence (`automation-api-key.txt`); `localApiKey` is set to
  `sessionApiKey` so both backends receive the same value
- Static build: stopped baking `VITE_AUTOMATION_API_KEY` (the frontend
  reads from `VITE_SESSION_API_KEY`)
- Docker entrypoint: `OPENHANDS_AUTOMATION_API_KEY`,
  `AUTOMATION_LOCAL_API_KEY`, and `AUTOMATION_AGENT_SERVER_API_KEY` all
  default to the session key when not explicitly overridden
- Tests updated to verify unified key behavior

Fixes the 401 on `/api/automation/v1` when the automation backend is
running but no separate `VITE_AUTOMATION_API_KEY` was configured.

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

* fix: use X-Session-API-Key header for automation backend auth (consistent with agent-server)

Switch automation backend requests from `Authorization: Bearer …` to
`X-Session-API-Key` header, matching the agent-server's auth pattern.
Both backends now authenticate using the same header and the same key
value (`VITE_SESSION_API_KEY`).

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

* fix: address review — remove localApiKey alias, dead constant, add entrypoint guard

- Remove `localApiKey` from config; all call sites now use
  `config.sessionApiKey` directly, making the unified-key intent obvious.
- Delete `DEFAULT_AUTOMATION_API_KEY_PATH` constant and its export
  (no downstream consumers in beta).
- Add fail-fast guard in docker/entrypoint.sh when no session key is
  available, instead of silently exporting empty strings.

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

* fix: update stale comment on AUTOMATION_LOCAL_API_KEY to reflect unified session key

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-26 22:15:27 +00:00
Rohit Malhotraandopenhands 45da5606d6 fix: bump agent-server SDK to 1.23.1 (#782)
Fixes two bugs in the upstream agent-server SDK v1.23.0 that caused
500 Internal Server Error on POST /api/conversations:

1. LLM registry duplicate usage_id: The condenser and main agent LLMs
   both used usage_id='default', causing ValueError on conversation
   creation. v1.23.1 checks for existing usage IDs before registering.

2. Validation error handler crash: The _validation_exception_handler
   tried to JSON-serialize raw ValueError objects from Pydantic
   validation contexts, turning 422 errors into 500s. v1.23.1 properly
   sanitizes validation errors before serializing.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-26 18:42:17 +00:00
cc63f48a07 refactor(acp): source ACP model lists from typescript-client (closes #740) (#775)
* refactor(acp): source model lists from typescript-client registry

Replace the hand-mined CLAUDE_MODELS / CODEX_MODELS / GEMINI_MODELS lists (and
the duplicated provider metadata) with the @openhands/typescript-client ACP
registry, which mirrors the Python SDK source of truth
(openhands.sdk.settings.acp_providers). acp-providers.ts becomes a thin
adapter: it enriches each upstream record with Canvas-only UI fields (brand
icon + onboarding description) and keeps the helper functions + public export
surface unchanged, so no consumers change.

- Bump the @openhands/typescript-client pin to the #187 merge commit
  (082d4d46), which adds available_models / default_model to the registry.
- Delete the three hardcoded model lists; build ACP_PROVIDERS from
  getAcpProvider() + a small ACP_PROVIDER_UI map.
- Incidentally corrects the Gemini default to auto-gemini-2.5 (the CLI's
  auto-router default), matching the merged SDK/client.

Closes #740.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(acp): pin typescript-client to v1.23.2 tag (was SHA)

Now that typescript-client v1.23.2 is tagged/released (includes #187's ACP
registry, mirroring SDK #3389), pin to the tag instead of the raw #187 merge
SHA. v1.23.2 tracks the SDK's v1.23.2 patch line. Resolves to the same commit
as the prior SHA, so no resolved-content change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci(acp): remove obsolete ACP providers sync check

The acp-providers-sync workflow + scripts/check-acp-providers-sync.mjs existed
to keep Canvas's hand-kept ACP registry mirror in sync with the SDK source
(agent-canvas#587). That mirror is gone — acp-providers.ts now sources its
model data from @openhands/typescript-client, which carries its own
SDK-drift check (check-acp-drift.py). So this canvas-side check is redundant
and was failing on the refactored ACP_PROVIDERS (no longer a literal array).

- Delete .github/workflows/acp-providers-sync.yml + the script.
- Drop the docs-version-sync test case that asserted the script's example.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Debug Agent <debug@example.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 19:25:08 +02:00
Rohit Malhotraandopenhands 89abe93a28 chore: bump automation version to 1.0.0a5 (#774)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-26 16:27:21 +00:00
Tim O'Farrellandopenhands c9616c1153 fix: pin openhands-sdk to same version as other SDK packages (#772)
Add openhands-sdk==${VERSION} to the --with list in both PyPI
resolution branches (specific version and default) of
buildAgentServerCommand so all four packages are pinned to the
same versions.agentServer:

  openhands-agent-server, openhands-sdk, openhands-tools, openhands-workspace

Without this pin, openhands-sdk floated to the latest release on
PyPI (a transitive dep with no version bound in the published
openhands-agent-server metadata), causing non-reproducible builds
and version skew between the banner and defaults.json.

The local-path (editable) and git-ref branches are unaffected —
they already source all packages from the same checkout/ref.

Update the two affected unit-test cases to expect the new pinned
openhands-sdk entry in the args array.

Fixes #767

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-26 15:38:53 +00:00
Hiep Le e54b04cf38 chore: refresh stale 1.22.1 doc examples after agent-server bump to 1.23.0 (#694) 2026-05-21 12:49:04 +07:00
Rohit Malhotra eee7fe1b7a feat: use async conversations and interrupt endpoint for local mode (#670) 2026-05-20 22:16:20 -04:00
Rohit Malhotraandopenhands ad5c5533a9 fix(tests): fix two flaky CI tests on Windows (#667)
conversation-panel: createMockConversation() gave all conversations the
same updated_at timestamp (new Date()), making sort order non-deterministic.
Tests that indexed into cards[0] expecting conversation "1" would sometimes
get conversation "2" instead.  Fix: use a monotonic counter so each call
produces a timestamp 1 s older than the previous, guaranteeing array
insertion order matches sort order.

dev-with-automation: the OH_AGENT_SERVER_LOCAL_PATH test created a Unix
shell stub for uvx and joined PATH with ':'.  On Windows the PATH separator
is ';', the stub needs a .cmd extension, and where.exe needs PATHEXT and
SystemRoot to function.  Fix: use path.delimiter, create uvx.cmd on Windows,
and pass through the required Windows env vars.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-20 11:10:48 -04:00
Hiep Le f42d684a03 fix(frontend): validate OH_AGENT_SERVER_LOCAL_PATH in dev-with-automation (#650)
* fix: validate OH_AGENT_SERVER_LOCAL_PATH in dev-with-automation

* fix: failing tests
2026-05-20 15:29:24 +07:00
Rohit Malhotraandopenhands edb998220a Remove Docker dependency from dev workflow (#635)
- Delete scripts/dev-docker.mjs and its test
- Simplify package.json: 'npm run dev' now runs local uvx stack directly
  (agent-server + automation + Vite + ingress), no Docker needed
- Remove dev:docker, dev:docker:dynamic, dev:dangerously-dockerless scripts
- Add dev:static for production-build frontend variant
- Update bin/agent-canvas.mjs CLI to use uvx-based stack
- Rename Docker-specific variables: DOCKER_PROJECTS_PATH → PROJECTS_PATH,
  shouldDefaultToDockerProjects → shouldDefaultToProjectsPath
- Update i18n: HOST_HOME_NOT_MOUNTED_HINT no longer references Docker
- Update all docs (README, DEVELOPMENT, SELF_HOSTING, AGENTS.md, CHANGELOG)
- Rename e2e snapshot: docker-workspace-browser → projects-workspace-browser
- Fix all tests to reflect new script names and remove Docker references

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-19 15:27:25 -04:00
90ad71dbd9 Add recommended automations and MCP marketplace setup flow (#504)
* Add recommended automations marketplace flow

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

* Update GitHub MCP QA findings

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

* Polish MCP and automation marketplace UI

Add a shared MCP logo badge, make marketplace cards more compact, and show required MCP logos prominently on recommended automation cards. Update the extensions package lock to the per-entry catalog split and save QA screenshots/results in .pr/.

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

* Align recommended automations styling

Remove the custom gradient treatment and match the recommended automation cards and setup modal to the existing Automations and MCP page surfaces, spacing, borders, and typography.

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

* Show existing automations before recommendations

Move the recommended automations section below the current automation list and creation guidance so the page prioritizes the user existing automation state.

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

* Add recommendations to onboarding

Show recommended automations below the Say Hello input so new users can launch a curated automation from the final onboarding step.

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

* Use extensions MCP marketplace exports

Remove the local MCP marketplace wrapper and consume MCP catalog data plus logo mappings directly from @openhands/extensions/mcps.

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

* Archive previous PR QA and add refreshed artifacts

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

* Add scheduled automation QA evidence

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

* Streamline recommended automation launch

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

* Refresh QA evidence and remove old artifacts

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

* Ensure canvas tools are on Python path

* Require MCP installs before launching recommendations

* Remove archived PR artifacts

* Drop redundant PYTHONPATH launcher changes

* Inline recommended automation catalog

* Point extensions dependency at main

* Augment recommended automation prompts with explicit API instructions

When a recommended automation is selected, the pre-filled prompt now
includes backend-specific API instructions so the agent calls the
correct endpoint:

- Local backends: directs the agent to use the local automation API
  from <RUNTIME_SERVICES> with $OPENHANDS_AUTOMATION_API_KEY auth,
  and explicitly tells it NOT to call the cloud API at app.all-hands.dev.

- Cloud backends: directs the agent to use the OpenHands Cloud
  Automations API at app.all-hands.dev with Bearer $OPENHANDS_API_KEY.

The buildAutomationPrompt() helper is exported for testability.
Three new unit tests cover both backend kinds and prompt preservation.

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

* Fix recommended automation launch regressions

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

* Expose local automation API key to agent terminals

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

* Stabilize conversation panel stop menu test

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

* chore: Remove PR-only artifacts

* fix: pin @openhands/extensions to specific commit for reproducibility

Updates the dependency from #main to the exact commit SHA
(3bba8e3b) that contains the MCP and automation catalogs
added in extensions#237.

This ensures reproducible builds since npm ci will use the
locked SHA instead of potentially picking up a newer main.

* Address final automation marketplace review comments

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: allhands-bot <allhands-bot@users.noreply.github.com>
2026-05-19 15:04:53 -04:00
Tim O'Farrellandopenhands 0cf58f8657 fix(dev-docker): allow exec on the home tmpfs so stdio MCP servers work (#535)
Docker's `--tmpfs` flag defaults its mount option set to
`rw,noexec,nosuid,nodev`. When dev:docker overlays the agent-server
container's `/home/openhands` with a tmpfs (so the mapped host user can
own it), it was inheriting that `noexec` flag unintentionally.

That breaks any stdio MCP server installed via npx -- e.g.
`npx -y @modelcontextprotocol/server-github` caches its binary under
`~/.npm/_npx/<hash>/node_modules/.bin/mcp-server-github`, npx then tries
to exec it, and the kernel returns EACCES regardless of the 0755 mode
bits on the file. The user sees:

    sh: 1: mcp-server-github: Permission denied
    Failed to connect to MCP server 'github', skipping
    ...
    MCPError: MCP Connection Failure

and the conversation that triggered it aborts during agent init.

Pass `exec` explicitly to override only the `noexec` default. We keep
`nosuid` and `nodev` (the home dir has no business hosting setuid
binaries or device nodes) so we lose no defense-in-depth beyond what's
necessary to make the supported MCP integration actually function.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-16 21:15:42 -06:00
Engel Nystandopenhands 5c42bd3492 Rename PROJECT_PATH to PROJECTS_PATH (#521)
* Rename PROJECT_PATH to PROJECTS_PATH

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

* Apply suggestion from @enyst

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-16 16:20:44 +00:00
Hiep Le 5c33c10b18 feat(frontend): add canvas_ui tool so the agent can drive the UI (#420)
* feat: add canvas_ui tool so the agent can drive the UI

* refactor: update the code based on feedback

* fix: failing tests

* refactor: update the code based on feedback
2026-05-16 16:09:43 +07:00
Tim O'Farrellandopenhands b2b71855c6 feat(dev): surface dev-stack runtime services in agent system prompt (#503)
* feat(dev): surface dev-stack runtime services in agent system prompt

Add a structured 'runtime services' info object that the dev launchers
(`dev:safe`, `dev:automation`, `dev:docker`, and the published
`agent-canvas` binary) propagate to the frontend via
`VITE_RUNTIME_SERVICES_INFO`. The frontend renders it into a
`<RUNTIME_SERVICES>` markdown block and attaches it as
`AgentContext.system_message_suffix` on every `POST /api/conversations`.

This means agents start each conversation knowing exactly what services
exist in the current dev stack (ingress URL, automation backend URL +
`/api/automation` prefix, auth header, etc.), instead of having to probe
or — worse — assume `localhost:8000` is the automation server when it is
actually the Agent Server they are running inside of.

URLs are written from the agent's point of view: dockerless modes use
`localhost`, `dev:docker` uses `host.docker.internal`. When automation
isn't running in the current mode (e.g. `dev:safe`), the block says so
explicitly so agents know to skip `/api/automation` calls.

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

* fix(runtime-services): address review feedback on PR #503

- Validate required `agentServerPort` in `buildRuntimeServicesInfo`;
  previously a missing port baked `http://localhost:undefined` into
  the agent's system prompt.
- Skip the automation entry when the supplied `automation` object has
  no `port` (e.g. a bare `{}` from a misconfigured launcher).
- Rename the JSON service key from `vite` to `frontend` and add a
  `kind: "vite" | "static"` discriminator + mode-aware description,
  so static-build dev stacks (`dev:docker`, the published binary, ...)
  no longer surface a misleading "Vite dev server" line in the agent
  system prompt. The renderer still accepts the legacy `vite` key.
- Anchor the "don't guess" warning to the actual agent-server URL from
  runtime info instead of hardcoded `localhost:8000`, since the
  agent-server uses different ports across dev modes (18000 in
  dev:safe, 8000 in dev:docker, ...).
- Plumb `frontendKind` through `buildAutomationRuntimeServicesInfo`
  and stamp `config.frontendKind` in `dev-with-automation.mjs::main`
  so both Vite spawn and static-build paths describe the frontend
  correctly.
- Expand AGENTS.md with the JSON schema of `VITE_RUNTIME_SERVICES_INFO`
  and a concrete example of the rendered `<RUNTIME_SERVICES>` block.
- Tests: assert the new URL-in-warning behavior, the new `frontend` /
  legacy `vite` rendering, the `agentServerPort`-required guard, the
  `automation: {}` skip, and the legacy `vitePort` alias.

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-15 20:53:45 -06:00
Hiep Le 2d5a52d4be chore: bump agent-server default to 1.22.1 (#477)
* chore: bump agent-server default to 1.22.1

* chore: update OH_AUTOMATION_VERSION

* chore: update DEFAULT_AUTOMATION_SDK_VERSION
2026-05-16 02:38:59 +07:00
2899c7b383 Fix static automation auth and switch LLM tool (#474)
* Fix static automation auth and switch LLM tool

* Allow automation SDK release lag in sync check

* chore: update baseline snapshots [skip ci]

* chore: trigger CI after snapshot update

---------

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-05-15 15:30:29 +00:00
Xingyao Wangandopenhands e5711e74b2 Clean up dev stack process trees on shutdown (#475)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-15 10:48:28 -04:00
Graham Neubig 5b05321483 Fix Windows static asset serving (#456) 2026-05-15 01:24:11 -04:00
Xingyao Wang 0fc85df818 Run Docker dev server with host user and tmpfs home (#443)
* Run Docker dev server as host user

* Use tmpfs for Docker agent home

* Document Docker tmpfs home rationale
2026-05-14 16:15:03 -04:00
Graham Neubigandopenhands 3f28f2d625 Fix static automation agent-server auth (#442)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-14 14:54:04 -04:00
Graham Neubigandopenhands a4089e0e4a Default user launchers to static frontend (#434)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-14 18:18:36 +00:00
Graham Neubigandopenhands 5ccc8e627c Fail fast when frontend deps are missing (#404)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-14 10:32:03 -04:00
Graham Neubigandopenhands 2b40e3425e Clarify Docker permission errors in dev script (#403)
Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-13 00:00:42 -04:00
Rohit Malhotraandopenhands 773cc72324 feat: update SDK to 1.22.0 and add CI version sync check (#333)
* feat: update SDK to 1.22.0 and add CI version sync check

- Update DEFAULT_AGENT_SERVER_VERSION from 1.21.1 to 1.22.0 in dev-safe.mjs
- Update SDK version references in AGENTS.md
- Add scripts/check-sdk-version-sync.mjs to verify automation project uses
  matching SDK versions for openhands-sdk, openhands-tools, openhands-workspace,
  and openhands-agent-server
- Add .github/workflows/sdk-version-sync.yml CI workflow with:
  - Path-filtered PR/push triggers for version-related file changes
  - repository_dispatch triggers (sdk-version-check, sdk-release) for
    external repos to notify when SDK deps change
  - workflow_dispatch with optional version override
  - Scheduled runs every 6 hours to catch upstream changes
  - PyPI version checking support (--check-pypi flag)

The check script supports:
- EXPECTED_SDK_VERSION env var override for CI triggers
- --check-pypi flag to also display latest PyPI versions
- --help for usage documentation

To trigger from external repos (e.g., OpenHands/automation or SDK repo):
  curl -X POST -H "Authorization: token \$GITHUB_TOKEN" \\
    https://api.github.com/repos/OpenHands/agent-canvas/dispatches \\
    -d '{"event_type": "sdk-version-check"}'

* fix: check released PyPI version instead of GitHub main branch

The SDK version sync check now fetches dependencies from the released
openhands-automation package on PyPI (version specified by
DEFAULT_AUTOMATION_VERSION in dev-with-automation.mjs) rather than
fetching pyproject.toml from the GitHub main branch.

This ensures we're checking the actual released version that users
would install, not the development version on main.

* fix: address review feedback for SDK version sync check

- Add env var overrides for automation package name and version
- Add retry logic with exponential backoff for PyPI API failures
- Add semantic version normalization for comparing versions
- Fix repository_dispatch to use client_payload.version
- Improve regex to handle parenthesized dependency formats
- Add comprehensive test coverage for helper functions

* fix: add type casts for dynamic module import in tests

* chore: update automation version to 1.0.0a2

- Update DEFAULT_AUTOMATION_VERSION in dev-with-automation.mjs
- Update AGENTS.md documentation
- Update test expectation

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-11 15:39:17 -04:00
Xingyao Wangandopenhands 18e51fa84d fix: move TMUX_TMPDIR to /tmp to avoid socket errors on mounted volumes (#325)
* fix: move TMUX_TMPDIR to /tmp to avoid socket errors on mounted volumes

Some filesystems (NFS, CIFS, certain FUSE/overlay mounts used by Docker
bind-mounts) do not support Unix domain sockets. When TMUX_TMPDIR pointed
to ~/.openhands/agent-canvas/tmux/ inside a container, tmux failed with:

  error connecting to .../tmux-10001/openhands (Operation not supported)

Move tmux socket directory to /tmp/openhands-agent-canvas-tmux which is
always on a local/tmpfs filesystem that supports Unix sockets. Tmux
sockets are ephemeral and don't need persistence across restarts.

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

* refactor: drop explicit TMUX_TMPDIR from dev-docker.mjs, use system default

Per review feedback — the container's default TMUX_TMPDIR (/tmp) already
supports Unix domain sockets, so there's no need to set it explicitly.

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-11 13:37:25 -04:00
Robert Brennanandopenhands 31302f9282 fix(ingress): handle socket errors so ECONNRESET can't crash the proxy (#280)
A WebSocket flowing through scripts/ingress.mjs would take down the
whole ingress process whenever its underlying TCP socket reset:

  Error: read ECONNRESET
      at TCP.onStreamRead (node:internal/stream_base_commons:216:20)
  Emitted 'error' event on Socket instance at:
      at Socket.onerror (node:internal/streams/readable:1026:14)

proxyWebSocket() only attached an 'error' listener to the outbound
HTTP upgrade request, never to the raw client / upstream sockets that
the bidirectional pipe runs over. ECONNRESET on a long-lived
/sockets/events/... connection (browser tab close, NAT timeout,
mobile network handoff, …) therefore became an unhandled 'error' event
on a Socket and Node aborted the process.

Fix:
  - Attach 'error' (and 'close') listeners to both the client socket
    and the upstream socket in proxyWebSocket; on either side erroring,
    tear the peer down gracefully.
  - Mirror the same defensive handling for plain HTTP in proxyRequest:
    add 'error' handlers on req, res, and proxyRes so a mid-stream
    disconnect aborts the upstream call instead of crashing.
  - Add server.on('clientError') for malformed client requests.
  - Add a narrow uncaughtException guard that swallows benign socket
    teardown errors (ECONNRESET / EPIPE / ECONNABORTED /
    ERR_STREAM_PREMATURE_CLOSE) but rethrows everything else, so real
    bugs stay visible.

Tests:
  - New regression covering an upstream WebSocket that immediately RSTs
    after upgrading; before the fix this took the proxy down (next
    request fails with ECONNREFUSED), after the fix the process keeps
    serving HTTP traffic and stderr never shows "Unhandled 'error' event".
  - New regression covering a client that aborts an in-flight HTTP
    request mid-flight.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-10 14:08:21 -07:00
Hiep Le 137fbae87f fix: pass container workspaces path as VITE_WORKING_DIR (#259) 2026-05-10 15:56:33 +07:00
Robert Brennanandopenhands 5d16a6cbcf Persist dev session API key to disk and re-seed default backend (#240)
Both `npm run dev` (Docker) and `npm run dev:dangerously-dockerless`
previously generated a fresh random SESSION_API_KEY per process. The
key was passed to the agent-server (OH_SESSION_API_KEYS_0) and to Vite
(VITE_SESSION_API_KEY), but the frontend's `openhands-backends`
localStorage entry was seeded only on the very first load. After a
single restart, the persisted entry's `apiKey` no longer matched the
agent-server, leading to 401s until the user manually edited the
backend.

Fix this by giving `buildSafeDevConfig` a stable default:

- `getOrCreatePersistedSessionApiKey()` reads / creates
  `~/.openhands/agent-canvas/session-api-key.txt` (mode 0600). The
  in-memory cache is keyed by path so tests can use `mkdtemp` paths.
- `OH_SESSION_API_KEY_PATH` env var overrides the file location
  (used by tests; can also be used to pin in unusual setups).
- Existing env overrides (SESSION_API_KEY / OH_SESSION_API_KEYS_0 /
  VITE_SESSION_API_KEY) still take precedence.

Because dev:docker and dev:dangerously-dockerless both flow through
the shared `buildSafeDevConfig`, they automatically pick up the
same persisted key and stay in sync with the Vite-baked
VITE_SESSION_API_KEY.

On the frontend, `readStoredBackends` now also re-seeds the default
Local backend when storage parses to `[]` or contains only invalid
entries (previously only `null` triggered seeding). This is safe now
that the persisted key keeps the seed valid across restarts.

Tests:
- New `getOrCreatePersistedSessionApiKey` tests covering creation,
  reuse, whitespace trimming, and empty-file regeneration.
- New `buildSafeDevConfig` / `buildConfig` tests covering the
  on-disk fallback, restart parity (dev:docker vs
  dev:dangerously-dockerless), and env-override precedence.
- New backend-registry storage tests covering re-seed on missing,
  empty, and all-invalid storage states.
- Existing tests that previously hit the real
  `~/.openhands/agent-canvas/session-api-key.txt` were updated to
  use isolated `OH_SESSION_API_KEY_PATH` temp dirs.

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-09 13:54:14 -07:00
Rohit Malhotraandopenhands 3207d90e72 feat: add dynamic port allocation with preferred port fallback (#223)
* feat: add dynamic port allocation with preferred port fallback

Implement dynamic port allocation for dev entrypoint scripts to gracefully
handle port conflicts. When a preferred port is busy, the system automatically
finds an alternative available port.

Changes:
- Add findFreePort() and findFreePorts() utilities to dev-safe.mjs
- Add buildSafeDevConfigAsync() for async config with dynamic allocation
- Update dev-with-automation.mjs to use async buildConfig with dynamic ports
- Update dev-static.mjs to use async buildConfig
- Add strictPort: true to vite.config.ts to fail-fast on conflicts
- Update tests for async buildConfig

The utilities try the preferred/default ports first, falling back to
OS-assigned ports only when needed. This preserves predictable defaults
while gracefully handling port conflicts.

Closes #222

* fix: address review feedback - add max retry, document race condition, improve tests

- Add max retry count (100 attempts) to port allocation loop to prevent
  infinite loops
- Fix findFreePort to handle preferredPort=0 correctly by skipping the
  port check and going straight to OS assignment
- Document race condition limitation in findFreePort JSDoc (accepted
  limitation with guidance on handling EADDRINUSE)
- Clarify JSDoc for buildSafeDevConfig vs buildSafeDevConfigAsync with
  clear guidance on when to use each
- Remove misleading 'must be after prereq check' comment
- Add comprehensive tests for findFreePort, findFreePorts, and
  buildSafeDevConfigAsync using actual port blocking
- Improve buildConfig tests with port uniqueness verification and
  fallback tests using high ports
- Use high ports (19xxx range) in tests to avoid conflicts with
  system services

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-09 14:46:10 -04:00
Rohit Malhotra 9098d9e6df feat: auto-generate random API keys for dev server authentication (#203) 2026-05-08 22:48:58 -04:00
Rohit Malhotraandopenhands 0b45d8c879 chore: default to released PyPI versions instead of git branches (#194)
- Change agent-server SDK default from git main to PyPI 1.21.1
- Change automation default from git main to PyPI 1.0.0a1
- Pin all SDK packages (agent-server, tools, workspace) to same version
- Keep ability to override with OH_AGENT_SERVER_GIT_REF/OH_AUTOMATION_GIT_REF
- Update tests and AGENTS.md documentation

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-08 15:08:01 -04:00
Rohit Malhotraandopenhands 7ebb116475 chore: update automation entrypoint to openhands.automation namespace (#191)
The automation package has been restructured to use the openhands.automation
namespace instead of the root automation namespace. This change updates the
uvicorn entrypoint from 'automation.app:app' to 'openhands.automation.app:app'.

Related: OpenHands/automation#101

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-08 14:23:50 -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
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
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
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 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
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 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
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
Graham Neubigandopenhands 0e60826123 Add isolated dev stack command for OpenHands Cloud debugging (#20)
* Warn about OpenHands Cloud backend conflicts

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

* Add isolated dev stack command

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

* Promote isolated stack to default dev command

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

* Fix dev-safe startup failure and docs

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

* Restructure docs for users and developers

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

* Remove libtmux from install docs

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

* Fix first-load Vite optimize dependency errors

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-04-26 11:09:39 -04:00