Document migration from OpenHands OSS Local GUI to Agent Canvas
#1,054 创建于 2026年6月3日
仓库指标
- 星标
- (43 个星标)
- PR 合并指标
- (平均合并 2天 10小时) (30 天内合并 106 个 PR)
描述
Context
Agent Canvas is intended to replace the OSS Local GUI experience from OpenHands/OpenHands. This issue tracks the verified migration behavior and documentation/work needed for users moving from the old OSS Local GUI to Agent Canvas.
Investigation snapshots:
OpenHands/agent-canvas:10d0a1bf(2026-06-03, "Add frontend-only and backend-only agent-canvas modes")OpenHands/OpenHands:8fdf41d(2026-06-02, "Avoid Keycloak full scan in Resend sync")OpenHands/software-agent-sdk:e3c9828, package version1.24.0, matchingagent-canvas/config/defaults.json
This issue was created and updated by an AI agent (OpenHands) on behalf of the user.
TL;DR
Agent Canvas does not perform a complete in-place migration from OpenHands OSS Local GUI state. It reuses a narrow subset of the old ~/.openhands/settings.json data, ignores several old top-level settings, stores LLM profiles in a different location/shape, and currently does not reuse old plaintext secret values when Agent Canvas starts with its default generated OH_SECRET_KEY.
The migration guide needs to be explicit about the following:
- Back up
~/.openhandsbefore first Agent Canvas launch. - Re-enter or migrate LLM API keys and custom secret values unless a migration helper is added.
- Recreate old Local GUI LLM profiles in Agent Canvas, except for the single auto-created profile described below.
- Recreate git provider tokens / repository integrations; old
provider_tokensare ignored by the local agent-server used by Agent Canvas. - Review app preferences (language, analytics consent, sound notifications, git identity), disabled skills, search/Tavily setup, workspace paths, and conversation history.
Verified persistence behavior
OpenHands OSS Local GUI persistence
OpenHands Local GUI stores durable single-user state through the app-server file store:
- Persistence directory selection:
OH_PERSISTENCE_DIR->FILE_STORE_PATH->~/.openhandsOpenHands/openhands/app_server/config.py
- Settings file:
settings.jsonOpenHands/openhands/app_server/settings/file_settings_store.py
- Secrets file:
secrets.jsonOpenHands/openhands/app_server/secrets/file_secrets_store.py
- Docker compose mounts host
~/.openhandsinto the app container.OpenHands/docker-compose.yml
The old file stores serialize secrets in plaintext by passing context={'expose_secrets': True}.
Agent Canvas / agent-server persistence
Agent Canvas launches openhands-agent-server version 1.24.0 by default:
- Version source of truth:
agent-canvas/config/defaults.json - Dev/npm launcher sets
OH_PERSISTENCE_DIRto the parent of~/.openhands/agent-canvas, i.e.~/.openhands.agent-canvas/scripts/dev-safe.mjs
- Docker entrypoint sets
OH_PERSISTENCE_DIR=${HOME}/.openhandsand stores Agent Canvas conversations/bash events under~/.openhands/agent-canvas/....agent-canvas/docker/entrypoint.sh
- Agent Canvas always configures an
OH_SECRET_KEYin the normal launcher paths: generated/persisted in~/.openhands/agent-canvas/secret-key.txtunless the user supplies one.agent-canvas/scripts/dev-safe.mjsagent-canvas/docker/entrypoint.sh
This means Agent Canvas points at the old Local GUI settings.json / secrets.json files by default, but the new agent-server model only imports the fields listed below.
Data compatibility matrix
| Old Local GUI data | Verified Agent Canvas behavior | Source evidence |
|---|---|---|
settings.json.agent_settings |
Reused. The agent-server persisted settings model includes agent_settings; SDK migrations accept older agent settings, add agent_kind, canonicalize legacy agent_kind: "llm" to "openhands", and drop deprecated verification fields moved to conversation settings. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; software-agent-sdk/openhands-sdk/openhands/sdk/settings/model.py |
LLM API key inside agent_settings.llm.api_key |
Not reused under default Agent Canvas launch when the old value is plaintext. The new server loads settings with a cipher because OH_SECRET_KEY is configured. validate_secret() attempts Fernet decrypt and returns None on plaintext/non-decryptable values. |
software-agent-sdk/openhands-sdk/openhands/sdk/utils/pydantic_secrets.py; software-agent-sdk/openhands-sdk/openhands/sdk/utils/cipher.py; agent-canvas/scripts/dev-safe.mjs |
settings.json.conversation_settings |
Reused. The persisted model includes conversation_settings; SDK migration v0 -> v1 is applied. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; software-agent-sdk/openhands-sdk/openhands/sdk/settings/model.py |
settings.json.llm_profiles |
Not imported. Agent-server PersistedSettings has schema_version, agent_settings, conversation_settings, and active_profile; it does not have llm_profiles. Pydantic ignores old extra top-level fields. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py |
| Legacy single default LLM profile | Conditionally auto-created in the new profile store when /api/profiles is listed and no profile files exist, but only if settings.llm_api_key_is_set is true after settings load. With old plaintext API keys plus default Agent Canvas OH_SECRET_KEY, that condition is false. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/profiles_router.py |
| New Agent Canvas LLM profiles | Stored as separate JSON files under ~/.openhands/profiles/*.json, not embedded in settings.json. Profile names must match ^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$. |
software-agent-sdk/openhands-sdk/openhands/sdk/llm/llm_profile_store.py |
secrets.json.custom_secrets names/descriptions |
Loaded into the new Secrets model if the JSON shape is {custom_secrets: {NAME: {secret, description}}} or {custom_secrets: {NAME: "value"}}. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py |
secrets.json.custom_secrets.*.secret plaintext values |
Not usable under default Agent Canvas launch for the same reason as LLM API keys: OH_SECRET_KEY is configured, validate_secret() tries to decrypt, and plaintext values become None. Listing still returns names/descriptions; value lookup returns 404 when the loaded secret value is None. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/store.py; software-agent-sdk/openhands-agent-server/openhands/agent_server/settings_router.py |
secrets.json.provider_tokens |
Ignored by the local agent-server. The new Secrets model explicitly stores custom secrets only and comments that it does not store provider tokens because the agent-server does not integrate with OAuth providers directly. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py |
settings.json.secrets_store.provider_tokens legacy field |
Ignored by Agent Canvas / agent-server. Old OpenHands app-server migrated this field into old secrets.json.provider_tokens; the new agent-server does not read secrets_store and does not read provider tokens. |
OpenHands/openhands/app_server/settings/settings_router.py; software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py |
Top-level old settings: language, enable_sound_notifications, user_consents_to_analytics, git_user_name, git_user_email |
Not reused from old settings.json. Agent Canvas persists these locally in browser localStorage key openhands-agent-server-app-preferences because local /api/settings only persists nested agent/conversation diffs. |
agent-canvas/src/api/app-preferences-store.ts; agent-canvas/src/api/settings-service/settings-service.api.ts |
disabled_skills |
Not reused from old settings.json. Agent Canvas stores local disabled skills in browser localStorage key openhands-agent-server-disabled-skills. |
agent-canvas/src/api/settings-service/settings-service.api.ts |
search_api_key, sandbox_api_key, max_budget_per_task, remote_runtime_resource_factor, sandbox_grouping_strategy, email fields |
Not reused by local Agent Canvas because the local agent-server persisted model does not include these top-level old app-server fields and Agent Canvas local settings transform only derives fields from agent_settings and conversation_settings. |
software-agent-sdk/openhands-agent-server/openhands/agent_server/persistence/models.py; agent-canvas/src/api/settings-service/settings-service.api.ts |
| Old Local GUI conversation history and metadata | Not automatically listed/imported by Agent Canvas. Old app-server event files use v1_conversations/{conversation_id} and SQL-backed app conversation metadata; Agent Canvas agent-server uses its own OH_CONVERSATIONS_PATH, defaulting to ~/.openhands/agent-canvas/conversations in launchers. |
OpenHands/openhands/app_server/event/event_service_base.py; OpenHands/openhands/app_server/conversation_paths.py; agent-canvas/scripts/dev-safe.mjs; agent-canvas/docker/entrypoint.sh |
config.toml |
Not read by Agent Canvas or openhands-agent-server. Repository search finds no runtime TOML config loader in Agent Canvas or agent-server; old OpenHands docs describe config.toml setup mainly for headless/CLI defaults. |
agent-canvas/; software-agent-sdk/openhands-agent-server/; OpenHands/Development.md |
Environment-variable mapping
| OpenHands Local GUI env/config | Agent Canvas equivalent / status |
|---|---|
OH_PERSISTENCE_DIR |
Still honored by openhands-agent-server. Agent Canvas defaults it to ~/.openhands. |
FILE_STORE_PATH |
Old Local GUI fallback only. Use OH_PERSISTENCE_DIR for Agent Canvas. |
SESSION_API_KEY |
Agent-server still supports this as a legacy fallback, but Agent Canvas launchers use user-facing LOCAL_BACKEND_API_KEY and pass it to the server as OH_SESSION_API_KEYS_0. |
VITE_BACKEND_BASE_URL="localhost:3000" |
Agent Canvas normal entry point is http://localhost:8000; frontend-only/dev setups use VITE_BACKEND_BASE_URL pointed at the active agent-server/ingress URL. |
VITE_BACKEND_HOST="127.0.0.1:3000" |
Agent Canvas Vite proxy defaults to 127.0.0.1:8000. |
WORKSPACE_BASE, workspace_base in config.toml |
Agent Canvas uses VITE_WORKING_DIR, stored local workspaces, per-conversation working dirs, and Docker /projects mounts. |
RUNTIME=local/process/remote, SANDBOX_*, AGENT_SERVER_IMAGE_* |
These are old app-server sandbox orchestration knobs. Agent Canvas talks directly to openhands-agent-server; npm/local mode runs without a sandbox, Docker mode runs the all-in-one container, and custom agent-server versions are selected with OH_AGENT_SERVER_LOCAL_PATH, OH_AGENT_SERVER_GIT_REF, or OH_AGENT_SERVER_VERSION. |
OPENHANDS_PROVIDER_BASE_URL, LLM_BASE_URL |
Agent Canvas primary user path is LLM Profiles in the UI. The direct SDK default model remains gpt-5.5, but Agent Canvas explicitly sends its own frontend default when no model is configured. |
TAVILY_API_KEY / SEARCH_API_KEY |
Old OpenHands app-server had a Tavily MCP proxy path. Agent Canvas exposes MCP through the /mcp page and marketplace entries; Tavily is configured as a regular MCP server entry. |
VITE_ENABLE_BROWSER_TOOLS |
Agent Canvas-specific. Set to false to omit browser_tool_set from new conversation payloads. |
VITE_LOAD_PUBLIC_SKILLS |
Agent Canvas-specific. Defaults to enabled; set to false to stop loading public skills from OpenHands/extensions. |
LOCAL_BACKEND_API_KEY |
Agent Canvas user-facing API key variable. Required in --public mode; auto-generated/persisted in local mode when unset. |
OH_SECRET_KEY |
Critical in Agent Canvas. Generated/persisted by default. Also causes old plaintext secret fields to fail decryption unless a migration/import path handles them. |
Launch and behavior changes users see
Old OpenHands Local GUI
- Source/dev:
make run - Backend:
127.0.0.1:3000 - Frontend:
127.0.0.1:3001 - Docker:
make docker-run, compose exposes3000 - Default runtime orchestration is the OpenHands app-server managing sandboxed agent-server instances
Agent Canvas
- npm/global:
npm install -g @openhands/agent-canvasthenagent-canvas - Source/dev:
npm run dev - Main entry point:
http://localhost:8000/ - Full stack defaults: ingress
8000, agent-server18000, automation backend18001, Vite3001in dev - Partial modes:
agent-canvas --frontend-only,agent-canvas --backend-only - Docker all-in-one image exposes
8000, persists under~/.openhands, and expects project folders mounted at/projects
Additional changed behavior
- Agent Canvas is a port of the OpenHands frontend that talks directly to
openhands-agent-server/software-agent-sdk, not the full OpenHands app backend. - Hosted/account/org management UI is removed or hidden in the OSS Agent Canvas path.
- Agent Canvas local mode auto-generates and injects a session API key. Public mode (
--public) requiresLOCAL_BACKEND_API_KEYand prompts the browser user for it. - OpenHands frontend default model in the inspected repo is
openhands/claude-opus-4-5-20251101; Agent Canvas frontend default isopenhands/minimax-m2.7; SDK/agent-server direct default isgpt-5.5. - Agent Canvas npm/local mode runs without a sandbox and has host filesystem access. Docker mode is the isolated path documented in the README.
- Agent Canvas full stack includes the automation backend by default.
- Agent Canvas has a top-level
/mcppage and marketplace data from@openhands/extensions; local install filters out OAuth-only marketplace options. - Terminal tab is a read-only transcript of agent events, not an interactive shell.
- Browser tab is screenshot/URL state from browser-tool events, not a full manually-driven browser UI.
- Agent Canvas adds the
canvas_uitool and conditionally includesbrowser_tool_set/task_tool_setbased on env, server tool metadata, and the sub-agent setting. - Local LLM profile switching and ACP model switching are local-backend features; cloud repository/search calls use the cloud proxy path.
Required migration work
- Add a migration guide: "Migrating from OpenHands Local GUI to Agent Canvas".
- Add a backup-first step for
~/.openhands. - Document that old plaintext LLM API keys and custom secret values are not reused under the default generated
OH_SECRET_KEYflow. - Provide a one-time migration/import path for plaintext old secrets into encrypted agent-server storage, or explicitly require users to re-enter LLM API keys and custom secrets.
- Provide a one-time importer for old embedded
llm_profilesinto~/.openhands/profiles/*.json, or explicitly require users to recreate profiles. - Document that old
provider_tokensare not consumed by local Agent Canvas and provide the replacement workflow for repo/git access. - Document app preferences and disabled skills as browser-local Agent Canvas settings that do not import from old
settings.json. - Document the env-var mapping table above.
- Document changed launch commands, ports, workspace behavior, sandbox/security model, default model, MCP/Tavily setup, and automation availability.
- State that old conversation history is not automatically migrated/listed.
- Add a fixture-based regression test with an old-style
settings.jsonandsecrets.jsonto lock the chosen migration behavior.
Suggested implementation approach
- Build a migration checker that runs before or during first Agent Canvas startup when
~/.openhands/settings.jsonor~/.openhands/secrets.jsonexists. - Detect old app-server fields:
llm_profiles- plaintext
agent_settings.llmsecret fields secrets.json.custom_secretsplaintext secret fieldssecrets.json.provider_tokens- app preference fields
disabled_skills
- For sensitive fields, import through agent-server APIs with plaintext exposure limited to local startup code, then save back through the new encrypted stores.
- Write imported LLM profiles through
/api/profiles/{name}so files land in~/.openhands/profiles/*.jsonwith the current profile-store validation rules. - Leave old files backed up or write a migration marker; do not silently overwrite old files with partial data.