Tim O'Farrellandopenhands 0206d0e9ee fix(skills): save disabled_skills when server omits the field (#837)
* fix(skills): save disabled_skills when settings has no prior disabled_skills field

The hydration effect in SkillsSettingsScreen only set hasHydratedInitialSettings=true
when settings?.disabled_skills was truthy. When no skills had ever been disabled,
the server omits the field entirely (undefined), so the condition was always false:

  if (settings?.disabled_skills) { ... }   // skipped when field is absent

Because settings?.disabled_skills is undefined both before *and* after settings
load, the dependency array [settings?.disabled_skills] never changed value on
load either, so the effect never ran a second time. hasHydratedInitialSettings
stayed false, and the save effect's early-return guard blocked every toggle.

Fix by gating on settingsLoading instead and defaulting the missing field with
?? []. Also add a localStorage stub for Node.js 25+ which ships a built-in
localStorage that is non-functional without --localstorage-file, breaking the
zustand persist middleware in the test environment.

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

* fix(tests): use mockResolvedValue(true) for saveSettings spy

saveSettings returns Promise<boolean>, not Promise<void>, so
mockResolvedValue(undefined) caused TS2345 type errors in CI.

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

* fix(skills): persist disabled_skills to localStorage for local backend

The local agent-server has no concept of disabled_skills and its
PATCH /api/settings strips the field before sending. As a result,
toggling a skill off appeared to work in the UI but was never
persisted -- the value was silently discarded on every save and
page refresh reverted the toggle.

Fix by mirroring the existing app-preferences pattern:

- saveSettings (local path): call writeStoredDisabledSkills before
  stripping the field from the PATCH payload.
- transformApiResponse: call readStoredDisabledSkills and overlay it
  onto the partial Settings returned from the API, so every getSettings
  call (cached or fresh) surfaces the stored value.

Export DISABLED_SKILLS_STORAGE_KEY so tests can reference the key
without duplicating the string literal.

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
2026-05-27 17:49:25 -06:00
2026-05-20 14:26:36 +00:00
2026-04-24 17:33:22 -04:00

agent-canvas

Warning

This project is in alpha phase. It may be vibecoded, untested, or out of date. Learn more.

OpenHands is a platform for orchestrating coding agents across different environments. You can:

  • ⌨️ prompt agents manually
  • 🕐 run agents on a schedule
  • ⚡ trigger agents automatically — e.g. from Slack, GitHub, or Datadog.

Agents can run anywhere:

  • 🧑‍💻 on your laptop
  • 🖥️ on a remote virtual machine
  • ☁️ in our hosted cloud
  • 🏢 or inside your company’s infrastructure

The same Agent Canvas frontend can swap between each of these environments, so you can see everything in one place.

OpenHands works with any agent harness (e.g. Claude Code, Codex) or connect directly to an LLM (e.g. Anthropic, OpenAI, Gemini, Mistral, Minimax, Kimi).

If you have questions or feedback, please open a GitHub issue or join the #proj-agent-canvas channel in Slack

Screenshot 2026-05-11 at 10 13 19 AM

Quickstart

You can install OpenHands to run agents on any machine: on your laptop, on a dedicated computer like a Mac Mini, or on a server in the cloud.

The most powerful way to run OpenHands is on a server in the cloud. This allows your agents to continue running even when your laptop is shut, and makes it easier to trigger your agents through third-party services like Slack, GitHub, and Datadog. See SELF_HOSTING.md for details, especially with respect to security hardening.

Notably, you can run the backend in multiple different environments, and switch between them from the same Agent Canvas frontend. E.g. you can share an Agent Server with your team for agents doing code review and dependency updates, then have your personal agents running on your laptop.

Warning

This runs the agent-server directly on the machine you're installing on — the agent will have full access to your filesystem!

Option 1: Docker

docker pull ghcr.io/openhands/agent-canvas:1.0.0-alpha.6
export PROJECTS_PATH=~/projects  # directory containing your project folders
docker run -it --rm \
  -p 8000:8000 \
  -v ~/.openhands:/home/openhands/.openhands \
  -v ${PROJECTS_PATH}:/projects \
  ghcr.io/openhands/agent-canvas:1.0.0-alpha.6

The agent will be able to access any project under PROJECTS_PATH.

Option 2: NPM

Prerequisites: Node.js 22.12.x or later, uv

npm install -g @openhands/agent-canvas
export PROJECTS_PATH=~/projects  # directory containing your project folders
agent-canvas

Option 3: From Source

Prerequisites: Node.js 22.12.x or later, npm, uv (for running the agent server via uvx)

git clone https://github.com/OpenHands/agent-canvas.git
cd agent-canvas
npm install
npm run dev

Access the UI at http://localhost:8000. You can add additional backends directly from the UI.

Architecture

Agent Canvas is powered by the OpenHands Agent Server, a REST API for running multiple agents on a single machine. Each Agent Server runs on a single host/port; the Agent Canvas can connect to multiple Agent Servers and easily flip between them.

You can run an Agent Server anywhere:

  • Directly on your laptop (be careful!)
  • On a dedicated machine like a Mac Mini
  • On a virtual machine in the cloud
  • Inside OpenHands Cloud (our commercial offering)

The Agent Server is often paired with an Automation Server, which lets you set up agents that run on a schedule or in response to events.

image

More documentation

For contributor and developer workflows, including frontend-only mode, mock mode, environment variables, and build/test commands, see DEVELOPMENT.md.

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