UI Contributions
Plugins can declare UI contributions in their manifest to appear in different parts of the desktop app. The desktop app renders generic views based on the plugin’s kind and never imports plugin-specific React components.
How It Works
- Your
plugin.jsondeclaresui_contributionswith slot names, icons, and labels - The desktop app reads these contributions from installed plugins at runtime
- Based on your plugin’s
kind(e.g.,agent_provider), the app selects a generic renderer - The renderer provides the UI framework; your plugin provides the data via JSON-RPC
The desktop app has no compile-time knowledge of your plugin. All UI is rendered dynamically based on the plugin kind and contributions declared in the manifest.
Available Slots
sidebar.item
Adds an icon to the main app sidebar, between the core navigation items and the bottom controls. Clicking the icon navigates to your plugin’s full-page view.
{
"slot": "sidebar.item",
"icon": "wand-sparkles",
"label": "My Plugin"
}| Field | Type | Required | Description |
|---|---|---|---|
icon | string | No | Lucide icon name. Defaults to "puzzle". |
label | string | No | Display label. Defaults to the plugin name. |
order | number | No | Sort order among plugin sidebar items. Lower values appear first. |
Supported icon names: wand-sparkles, bot, wrench, puzzle
page.main
Declares that your plugin provides a full-page view accessible at /plugins/:pluginId. Requires a matching sidebar.item contribution for users to navigate to it.
{
"slot": "page.main",
"component": "my-page"
}The component value is a label for your own reference. The desktop app selects the renderer based on your plugin’s kind, not this field.
Renderers by kind:
| Kind | Renderer | Description |
|---|---|---|
agent_provider | Agent Plugin Page | Full-page agent view with lab selector and chat interface |
tool_provider | (coming soon) | Generic tool page |
lab.sidepanel
Adds a tab to the lab detail right sidebar. Users can switch between Run, Agent, and your plugin’s tab while working in a lab.
{
"slot": "lab.sidepanel",
"component": "my-panel",
"icon": "wrench",
"label": "My Tool"
}| Field | Type | Required | Description |
|---|---|---|---|
component | string | No | Label for your reference. |
icon | string | No | Lucide icon name for the tab button. |
label | string | No | Tab button label. Defaults to plugin name. |
Renderers by kind:
| Kind | Renderer | Description |
|---|---|---|
agent_provider | Agent Plugin Panel | Compact agent chat scoped to the current lab |
tool_provider | (coming soon) | Generic tool panel |
settings.section
Declares that your plugin contributes a section to the Settings page.
{
"slot": "settings.section",
"component": "my-settings"
}Settings section rendering is planned for a future release. Currently, plugin settings are managed through the Plugins section in Settings (enable/disable, permissions, updates).
Example: Agent Provider
An agent_provider plugin that appears in the sidebar, has a full-page view, and contributes a lab sidepanel tab:
{
"id": "openai.codex",
"name": "Codex Workspace Agent",
"kind": "agent_provider",
"ui_contributions": [
{ "slot": "sidebar.item", "icon": "wand-sparkles", "label": "Codex" },
{ "slot": "page.main", "component": "codex-page" },
{ "slot": "lab.sidepanel", "component": "codex-panel", "icon": "wand-sparkles", "label": "Codex" }
]
}This results in:
┌──────────────────────────────────────────────────┐
│ Sidebar (72px) │ Main Content │
│ │ │
│ Dashboard │ (route-dependent) │
│ Labs │ │
│ Models │ │
│ Runs │ │
│ Hub │ │
│ ───────── │ │
│ ✨ Codex ←plugin │
│ ───────── │ │
│ 🧩 Plugins │ │
│ Help │ │
│ Settings │ │
└──────────────────────────────────────────────────┘Example: Tool Provider
A tool_provider plugin with no sidebar icon, only accessible from the Plugins page:
{
"id": "acme.data-exporter",
"name": "Data Exporter",
"kind": "tool_provider",
"ui_contributions": []
}This plugin is visible in Settings > Plugins and Plugins marketplace but does not add a sidebar icon or lab panel tab.
Not Installed Behavior
When a user visits the Plugins marketplace and your plugin is available but not installed, the desktop app shows a generic install card with your plugin’s name, description, version, and an Install button. No custom UI is needed for this. The marketplace card is generated from your manifest metadata.