Request for Shaping Feedback: Addons for Agent Canvas UI
I’ve been experimenting with local UI add-ons for Agent Canvas in this branch:
This is not ready as a merge PR yet. I’m opening this first to get shaping feedback on the direction, the user experience, and where add-ons should live long term.
What The Prototype Explores
The prototype adds a trusted local, frontend-only add-on system for Agent Canvas.
The current implementation discovers add-ons from a repo-local folder:
addons/<addon-id>/.openhands/addon.json
Then npm run make-addons generates a registry that Agent Canvas uses to
include the add-ons in the frontend app.
The important thing about the current prototype: add-on files are compiled by Agent Canvas as part of the Vite/React build. When an add-on is already registered, Vite HMR works well for source changes, which makes local iteration feel good. Adding/removing an add-on or changing manifest metadata still requires regenerating the registry and restarting/rebuilding the UI.
Proof Of Concept Examples
1. Routed UI Add-on
The first use case is a normal add-on page that appears in the Agent Canvas sidebar and mounts under:
Example manifest:
{
"name": "agent-status",
"title": "Agent Status",
"frontend": {
"entry": "src/index.tsx"
},
"sidebar": {
"order": 350
}
}
The add-on exports a register(api) function and returns a React component for
the route.
This proves that a local add-on can add a dedicated UI page without adding backend routes or changing agent-server behavior.
2. Style-only Shell Add-on
The second use case is a route-less add-on that customizes the Agent Canvas shell with CSS but does not add a sidebar icon or page.
Example manifest:
{
"name": "canvas-polish",
"title": "Canvas Polish",
"frontend": {
"route": false
},
"sidebar": {
"visible": false
},
"styling": {
"appCss": ["src/agent-canvas.css"]
}
}
The CSS can scope to the Agent Canvas root:
[data-agent-server-ui][data-agent-canvas-addons~="canvas-polish"]
> [data-theme] {
--oh-color-primary: #7dd3fc;
}
This proves that an add-on can enhance or customize the whole UI without introducing a new route.
The Next Unclear Use Case: Route-less JavaScript
The third case I’d like feedback on is a UI add-on that needs JavaScript but still should not add a route.
Examples:
- global keyboard shortcuts
- small floating UI elements
- shell badges/status indicators
- command-palette entries, if we add shell slots later
- small app-wide UI behavior paired with CSS customization
I’m unsure which direction is better here.
Option A: Component-based Root Add-ons
A route-less add-on could still provide a normal .ts, .tsx, or .js entry:
{
"name": "canvas-polish",
"title": "Canvas Polish",
"frontend": {
"route": false,
"entry": "src/index.tsx"
},
"sidebar": {
"visible": false
}
}
Then it could return a root-level React component:
export default function register(api) {
return {
AppComponent: CanvasPolishRoot,
};
}
Agent Canvas would mount that component near the app root, outside the route outlet.
This keeps add-ons in the React/Vite/TypeScript world and preserves HMR in dev, but it still assumes Agent Canvas is compiling the add-on source.
Option B: Compiled Runtime Scripts
Another option is to support compiled JavaScript loaded on top of the running UI, for example an IIFE:
{
"name": "canvas-polish",
"title": "Canvas Polish",
"frontend": {
"route": false
},
"runtime": {
"script": "dist/canvas-polish.iife.js",
"style": "dist/canvas-polish.css"
}
}
The script could register itself through a small global API.
This may fit future installs better because the running Agent Canvas app would not need to compile user source code. But it is less React-native, harder to typecheck, and opens more questions around cleanup, lifecycle, dependency duplication, and security.
Where Should Add-ons Live?
The prototype currently uses a repo-local addons/ folder because that was easiest for proving the concept.
For a real user-facing flow, I think local add-ons probably belong in:
~/.openhands/agent-canvas/addons/<addon-id>/
That would keep user customization with other local Agent Canvas/OpenHands data rather than mixing it into the Agent Canvas source checkout.
Possible layout:
~/.openhands/agent-canvas/addons/my-addon/
.openhands/addon.json
src/index.tsx
src/styles.css
I’d like feedback on whether this is the right default.
Future Install Question
The current build-time model works naturally when developing Agent Canvas from source:
npm run dev
- generate the add-on registry
- Vite compiles add-on source
- HMR works for already registered add-ons
But the future install story is less obvious.
If users install Agent Canvas from npm or run a prebuilt static package, then user-local .ts / .tsx add-ons cannot simply be compiled into the already-built app unless we add a specific addon development/build flow.
Possible directions:
-
Source/dev mode only:
- source add-ons work when running
npm run dev
- good for contributors and local experimentation
- not a complete installed-user story
-
Add-on dev mode:
- support something like a
dev-addons or
~/.openhands/agent-canvas/addons folder only in dev mode
- HMR remains a local development feature
- installed/static Agent Canvas would not promise source add-on compilation
-
Local add-on build/cache:
- Agent Canvas could eventually provide a command that compiles user add-ons
into a local cache under ~/.openhands/agent-canvas
- more complex, but could support installed usage without committing add-on
source into the main repo
-
Runtime compiled add-ons:
- use precompiled JS/CSS assets loaded by the running app
- possibly better for installed users
- bigger API and security design
I’m not sure which of these should be the target yet.
Current Leaning
My current feelings on some of these:
Keep the first mergeable version focused on trusted local UI add-ons
For the first real PR, I think add-ons should be treated as local code that the user intentionally places on their own machine. They would not be sandboxed, reviewed, or installed from a marketplace.
Keep backend/agent runtime plugins out of scope
This proposal is only about the Agent Canvas browser UI. It should not add anything else.
Those may be useful someday, but they are a different design problem with a larger security and compatibility surface.
Support routed add-on pages and style-only shell add-ons
The first mergeable version should support the two use cases already proved in the prototype:
-
Routed add-on pages:
- add a sidebar entry
- mount under
/addons/:addonId/*
- render a React component returned by
register(api)
-
Style-only shell add-ons:
- add app-level CSS
- do not add a route
- do not add a sidebar icon
- scope CSS through the Agent Canvas root marker
This keeps the first implementation understandable. Addons could be full single page apps with their own nav item and route which gives a lot of surface area when combined with the existing apis.
Move user add-ons toward ~/.openhands/agent-canvas/addons
The repo-local addons/ folder is useful for proving the concept, but it is probably not the right final location for user add-ons. Local user customization should likely live beside other Agent Canvas/OpenHands local state:
~/.openhands/agent-canvas/addons/<addon-id>/
This would keep user-installed add-ons out of the Agent Canvas source checkout and avoid making local add-on files show up as repo changes.
The open implementation question is how to do this cleanly while still letting Vite compile and watch those files. A polished version may need:
- a configurable add-on root
- Vite
server.fs.allow support for the external directory
- a generated registry that does not commit machine-local absolute paths
- Docker path handling for the mounted
~/.openhands directory
- clear docs for source/dev mode versus installed/static mode
Preserve Vite HMR for already registered add-ons in dev mode
One nice property of the prototype is that once an add-on is registered, editing its source files can update through Vite HMR. That makes local add-on development feel much better than a full rebuild loop.
I think that is worth preserving for npm run dev or any future add-on dev mode.
However, HMR should not be over-promised. Adding or removing an add-on, or changing manifest fields like route/sidebar/style metadata, would still require regenerating the add-on registry and restarting/rebuilding the UI unless we add a more dynamic discovery layer later.
Get feedback before deciding route-less JavaScript
I do not think route-less JavaScript should be rushed into the first merge unless there is clear agreement on the shape...but I would really like to see it.
The main options seem to be:
- root-mounted React components returned from
register(api)
- compiled IIFE/runtime scripts loaded by the running app
- explicit shell slots first, then add-ons can target those slots
- no route-less JavaScript yet
This choice affects the install story, security model, cleanup lifecycle, typing, testing, HMR, and how much of Agent Canvas internals add-ons can touch.
I would like feedback on that direction before turning it into implementation. See below
Feedback Requested
I’d especially like feedback on:
- Does the general local UI addon direction make sense for Agent Canvas?
- Are routed add-ons under
/addons/:addonId/* the right first scope?
- Should user add-ons live under
~/.openhands/agent-canvas/addons?
- Should source-based add-ons be dev/source-build only?
- What should the installed/npm package story be?
- For route-less JavaScript, should we prefer: root-mounted React components, compiled IIFE/runtime scripts, both eventually, or neither until we define shell slots?
- What security/support concerns would block this from becoming a real PR?
Request for Shaping Feedback: Addons for Agent Canvas UI
I’ve been experimenting with local UI add-ons for Agent Canvas in this branch:
This is not ready as a merge PR yet. I’m opening this first to get shaping feedback on the direction, the user experience, and where add-ons should live long term.
What The Prototype Explores
The prototype adds a trusted local, frontend-only add-on system for Agent Canvas.
The current implementation discovers add-ons from a repo-local folder:
Then
npm run make-addonsgenerates a registry that Agent Canvas uses toinclude the add-ons in the frontend app.
The important thing about the current prototype: add-on files are compiled by Agent Canvas as part of the Vite/React build. When an add-on is already registered, Vite HMR works well for source changes, which makes local iteration feel good. Adding/removing an add-on or changing manifest metadata still requires regenerating the registry and restarting/rebuilding the UI.
Proof Of Concept Examples
1. Routed UI Add-on
The first use case is a normal add-on page that appears in the Agent Canvas sidebar and mounts under:
Example manifest:
{ "name": "agent-status", "title": "Agent Status", "frontend": { "entry": "src/index.tsx" }, "sidebar": { "order": 350 } }The add-on exports a
register(api)function and returns a React component forthe route.
This proves that a local add-on can add a dedicated UI page without adding backend routes or changing agent-server behavior.
2. Style-only Shell Add-on
The second use case is a route-less add-on that customizes the Agent Canvas shell with CSS but does not add a sidebar icon or page.
Example manifest:
{ "name": "canvas-polish", "title": "Canvas Polish", "frontend": { "route": false }, "sidebar": { "visible": false }, "styling": { "appCss": ["src/agent-canvas.css"] } }The CSS can scope to the Agent Canvas root:
This proves that an add-on can enhance or customize the whole UI without introducing a new route.
The Next Unclear Use Case: Route-less JavaScript
The third case I’d like feedback on is a UI add-on that needs JavaScript but still should not add a route.
Examples:
I’m unsure which direction is better here.
Option A: Component-based Root Add-ons
A route-less add-on could still provide a normal
.ts,.tsx, or.jsentry:{ "name": "canvas-polish", "title": "Canvas Polish", "frontend": { "route": false, "entry": "src/index.tsx" }, "sidebar": { "visible": false } }Then it could return a root-level React component:
Agent Canvas would mount that component near the app root, outside the route outlet.
This keeps add-ons in the React/Vite/TypeScript world and preserves HMR in dev, but it still assumes Agent Canvas is compiling the add-on source.
Option B: Compiled Runtime Scripts
Another option is to support compiled JavaScript loaded on top of the running UI, for example an IIFE:
{ "name": "canvas-polish", "title": "Canvas Polish", "frontend": { "route": false }, "runtime": { "script": "dist/canvas-polish.iife.js", "style": "dist/canvas-polish.css" } }The script could register itself through a small global API.
This may fit future installs better because the running Agent Canvas app would not need to compile user source code. But it is less React-native, harder to typecheck, and opens more questions around cleanup, lifecycle, dependency duplication, and security.
Where Should Add-ons Live?
The prototype currently uses a repo-local
addons/folder because that was easiest for proving the concept.For a real user-facing flow, I think local add-ons probably belong in:
That would keep user customization with other local Agent Canvas/OpenHands data rather than mixing it into the Agent Canvas source checkout.
Possible layout:
I’d like feedback on whether this is the right default.
Future Install Question
The current build-time model works naturally when developing Agent Canvas from source:
npm run devBut the future install story is less obvious.
If users install Agent Canvas from npm or run a prebuilt static package, then user-local
.ts/.tsxadd-ons cannot simply be compiled into the already-built app unless we add a specific addon development/build flow.Possible directions:
Source/dev mode only:
npm run devAdd-on dev mode:
dev-addonsor~/.openhands/agent-canvas/addonsfolder only in dev modeLocal add-on build/cache:
into a local cache under
~/.openhands/agent-canvassource into the main repo
Runtime compiled add-ons:
I’m not sure which of these should be the target yet.
Current Leaning
My current feelings on some of these:
Keep the first mergeable version focused on trusted local UI add-ons
For the first real PR, I think add-ons should be treated as local code that the user intentionally places on their own machine. They would not be sandboxed, reviewed, or installed from a marketplace.
Keep backend/agent runtime plugins out of scope
This proposal is only about the Agent Canvas browser UI. It should not add anything else.
Those may be useful someday, but they are a different design problem with a larger security and compatibility surface.
Support routed add-on pages and style-only shell add-ons
The first mergeable version should support the two use cases already proved in the prototype:
Routed add-on pages:
/addons/:addonId/*register(api)Style-only shell add-ons:
This keeps the first implementation understandable. Addons could be full single page apps with their own nav item and route which gives a lot of surface area when combined with the existing apis.
Move user add-ons toward
~/.openhands/agent-canvas/addonsThe repo-local
addons/folder is useful for proving the concept, but it is probably not the right final location for user add-ons. Local user customization should likely live beside other Agent Canvas/OpenHands local state:This would keep user-installed add-ons out of the Agent Canvas source checkout and avoid making local add-on files show up as repo changes.
The open implementation question is how to do this cleanly while still letting Vite compile and watch those files. A polished version may need:
server.fs.allowsupport for the external directory~/.openhandsdirectoryPreserve Vite HMR for already registered add-ons in dev mode
One nice property of the prototype is that once an add-on is registered, editing its source files can update through Vite HMR. That makes local add-on development feel much better than a full rebuild loop.
I think that is worth preserving for
npm run devor any future add-on dev mode.However, HMR should not be over-promised. Adding or removing an add-on, or changing manifest fields like route/sidebar/style metadata, would still require regenerating the add-on registry and restarting/rebuilding the UI unless we add a more dynamic discovery layer later.
Get feedback before deciding route-less JavaScript
I do not think route-less JavaScript should be rushed into the first merge unless there is clear agreement on the shape...but I would really like to see it.
The main options seem to be:
register(api)This choice affects the install story, security model, cleanup lifecycle, typing, testing, HMR, and how much of Agent Canvas internals add-ons can touch.
I would like feedback on that direction before turning it into implementation. See below
Feedback Requested
I’d especially like feedback on:
/addons/:addonId/*the right first scope?~/.openhands/agent-canvas/addons?