← Home
teach-me · OpenHands/agent-canvas cloud UI

Learn the cloud-specific Canvas surface as one coherent system

Agent Canvas is not a second Cloud product hidden inside the repo. It is one UI that switches lanes: local mode works with folders and a nearby agent-server; cloud mode works with OpenHands Cloud control-plane records, Git integrations, org-scoped settings, and short-lived sandbox runtimes.

Pinned to 413d7f1a. Prepared by OpenHands (AI) on behalf of the user, following the SmolPaws teach-me skill.

1Background: one Canvas, two backend kinds

★ Key takeaway. Almost every “cloud-specific” UI feature is a place where Canvas replaces a local assumption — local folders, local session API keys, local runtime endpoints — with a Cloud control-plane concept and, only when needed, a sandbox runtime URL.
Skippable beginner layer. If you already know Agent Canvas, local agent-server, and OpenHands Cloud, jump to “Narrow background”.

Agent Canvas is the browser shell for conversations with software agents. In local development, the browser talks to a local agent-server and asks it about local workspaces, local settings, files, Git state, and WebSocket streams.

Cloud mode adds another layer. The browser first talks to the durable OpenHands Cloud app backend: organizations, app-conversations, API keys, Git providers, profiles, secrets, suggested tasks, automations, and sandbox metadata live there. A running conversation also has a sandbox runtime: a temporary agent-server inside the workspace where terminal/file/browser events happen.

Local backend

Stored as kind: "local". Auth uses X-Session-API-Key. New conversations can start from a local folder/workspace. Settings are SDK agent-server settings.

Cloud backend

Stored as kind: "cloud". Auth uses Authorization: Bearer .... New conversations can start from hosted Git repositories. Settings/profiles/secrets route to Cloud APIs.

Narrow background: the switch is explicit

The active backend kind is real UI state. Auth headers branch in backend-registry/auth.ts:1-14. The backend selector flattens local rows and cloud org rows in backend-selector.tsx:61-112. Most service wrappers ask one question: is the active backend local or cloud?

if (active.backend.kind === "cloud") {
  // call a Cloud app endpoint, or a sandbox runtime through the proxy
} else {
  // call the local agent-server / local automation sidecar
}

2Intuition: cloud mode swaps “where is my computer?” for “where is my control plane?”

A good mental model is to imagine the same user action in two modes.

Local: “Open this folder”

User selects /Users/me/repo. Canvas creates a local agent-server conversation with a working directory. File, terminal, and Git queries go to that local server.

Cloud: “Open this repository”

User selects OpenHands/agent-canvas from connected Git providers. Canvas asks Cloud to start an app-conversation, waits for a sandbox, then streams from the runtime.

The Cloud lane has two stops. First, durable app state on the Cloud backend. Second, a runtime sandbox only after provisioning succeeds.

Cloud Canvas has control-plane and runtime lanesBackend selector and settings talk to the Cloud app backend; conversation streaming and live runtime operations use the sandbox runtime once ready.Browser UIAgent CanvasCloud app backendorgs · settings · reposapp-conversationsSandbox runtimesession_api_keyevents · terminal · filesbearertask readyBackend selectorbackend.id + orgIdCloud service adapters/api/v1/* + /api/automation
Fig 1 · Most cloud UI talks to the Cloud app backend first; runtime URLs only matter after a sandbox exists.
ⓘ Reading hint. Blue boxes are durable Cloud/app state. Orange is the live sandbox. Dashed grey is the browser shell that can switch between local and cloud backends.

3Code walkthrough: five cloud seams to recognize

Seam A — connecting to Cloud is a backend-registry feature

The Add Backend UI offers manual local/self-hosted connection plus a Cloud login column. The Cloud login starts OAuth device flow and stores a backend as kind: "cloud" with the returned API key backend-form-modal.tsx:687-1000. The device flow itself calls /oauth/device/authorize and /oauth/device/token device-flow-client.ts:104-185.

Once connected, Cloud rows become org-aware. The selector fetches organizations and prefers the personal workspace when it can identify one backend-selector.tsx:206-232.

Seam B — creating a Cloud conversation is asynchronous

Local conversation creation returns a ready conversation. Cloud creation posts an app-conversation start request and may return a task before a runtime exists cloud/conversation-service.api.ts:104-130. The UI uses a temporary /conversations/task-... URL until useTaskPolling() sees READY and redirects to the real conversation id use-task-polling.ts:19-33, use-task-polling.ts:92-125.

Cloud create:
POST /api/v1/app-conversations
  -> task id first
  -> route /conversations/task-123
  -> poll /api/v1/app-conversations/start-tasks?ids=123
  -> READY: navigate /conversations/{app_conversation_id}

Seam C — sandbox status shapes the conversation UI

Cloud sandboxes can pause, resume, disappear, or fail. Stopping a Cloud conversation pauses its sandbox; local stop interrupts the agent-server immediately conversation-mutation-utils.ts:36-61. Opening a paused Cloud conversation resumes the sandbox conversation.tsx:142-177. While paused, WebSocket connection is deliberately suppressed so the browser does not hit a stale runtime URL websocket-provider-wrapper.tsx:24-33.

Seam D — Cloud replaces workspace picking with repository picking

The New Conversation button chooses a different menu by backend kind: local shows a workspace picker; cloud shows a repository picker new-conversation-button.tsx:28-39, new-conversation-button.tsx:87-99. Repository search, installations, and branches all route through Cloud Git APIs cloud/git-service.api.ts:20-104. GitHub is installation-scoped only in Cloud mode utils.ts:176-204.

Seam E — settings are shared UI, different backend contracts

The same Settings UI is reused, but Cloud settings are mostly flat Cloud app fields. The adapter derives the nested shape the UI expects cloud/settings-service.api.ts:12-56. Saving settings also branches: app preferences are flattened for Cloud, while local stores them under agent-server misc settings settings-service.api.ts:398-437.

LLM profiles are org-scoped on Cloud, with mutation permission based on Cloud org role/permissions profiles-service.api.ts:12-43, use-can-manage-llm-profiles.ts:12-53.

4Feature map: what is cloud-specific today?

Identity and control plane

Cloud loginBearer authorg selectionlocked-to-cloud mode

Cloud backend connection, OAuth device flow, health checks, API-key org binding, and Cloud settings link.

Conversation lifecycle

start taskssandbox pause/resumeruntime URLarchived status

Async app-conversation creation, task polling, pending first messages, deferred attachments, paused WebSocket suppression, sandbox-exposed VS Code URLs.

Repository and Git integrations

repo pickerinstallationsbranchessuggested tasks

Cloud starts work from connected Git repositories, not local folders. Suggested tasks are Cloud-only and route to hosted Git provider issues/PRs.

Settings and user resources

flat Cloud settingsorg profilesCloud secretsCloud skills

Settings pages look shared, but their data comes from Cloud app endpoints when the active backend is cloud.

Cloud-only or Cloud-gated UI

Planner tabpublic sharingCloud VS Code URLCloud automations routing

The Planner tab is hidden locally. Public sharing only appears for Cloud. VS Code URL comes from sandbox exposed URLs.

Explicit non-goals in Canvas

billingaccount managementfull integrations admin

Those are not embedded as Canvas screens. The UI mostly links out to hosted Cloud settings/integrations when needed, for example cloud-settings-link.tsx:14-50.

Four important examples

FeatureCloud-specific behaviorSource
Public sharingOnly shown on cloud; updates the app-conversation public flag and builds URLs under the Cloud host.context-menu.tsx:147-153, use-conversation-name-context-menu.ts:155-165
Planner tabHidden unless backend.kind === "cloud"; plan conversations are sub-conversations with agentType: "plan".conversation-tabs.tsx:137-145, use-handle-plan-click.ts:61-82
AutomationsThe automation API can route through Cloud, but recommended automation creation is hidden on cloud and detail editing is local-only in MVP.automation-service.api.ts:56-68, recommended-automations-launcher.tsx:220-222
MCP marketplaceCloud skips pre-conversation connectivity tests because MCP runs inside a future sandbox, not in the browser-reachable local agent-server.mcp-service.api.ts:35-50, custom-server-editor.tsx:56-62
⚠ Boundary to remember. “Cloud-specific” does not mean “SaaS-only billing/account UI”. In this repo it mostly means backend-aware API routing and UI affordances for Cloud conversations, organizations, Git integrations, settings, and sandboxes.

5Quiz: prove the model stuck

Choose an answer. Feedback explains the reasoning, not just the result.

1. Why does Cloud conversation creation use a task-... URL?

2. Which auth header does a Cloud backend use for app API calls?

3. Why does the New Conversation UI show repositories on Cloud but workspaces locally?

4. What does the UI do before connecting WebSocket to a paused Cloud sandbox?

5. Which feature is not embedded as a full Canvas cloud screen?

Score: 0 / 5 answered