Plugin UI Contributions
An opt-in plugin contribution: a plugin can add its own buttons to the header toolbar and a session's right-click menu, and navigate the app to a session, project, inbox or virtual project. The buttons are read-only chrome until a click lands back in that plugin's own pane.
What it is
Status: shipped (always on, landed 2026-08-06). A plugin can add its own buttons to the header toolbar and the session right-click menu, and navigate the app to a session, hub, or view — there is no Lab flag for it and nothing to enable.
A plugin that opts in gets a read-only presence in the Omniscio shell. Through its webview pane it can do two things, both mediated by the host:
- Navigate the app —
navigation.goTo({ kind, id? })switches the active view to a core session, project, inbox, or virtual project — the same chokepoints the app itself uses when you click around. It is reversible (it never destroys anything), so it is the most benign primitive. - Contribute chrome —
toolbar.setItems([...])andcontextMenu.setSessionItems([...])set the buttons the plugin wants in the header toolbar and in the right-click menu of any session. Both are replace-semantics per plugin: whatever the plugin last sent is what shows, and sending an empty list removes its items.
The contributed buttons and menu items are read-only chrome until a click lands back in the plugin's own pane — clicking one sends a command:<id> event to that plugin's webview and nothing else. Anything that actually changes data still goes through the plugin's normal approval flow.
Where to find it
There is nothing to switch on. Look at the app's own chrome: a contributing plugin's buttons appear on the header toolbar, namespaced to that plugin, and its items appear in the right-click menu of any session. The plugin's own pane is where the result of a click shows up, and it opens from wherever that plugin sits in the app.
A plugin only gets here if you consented to the navigation and/or chrome permission when you
installed it, from the Plugin Marketplace.
How it behaves
- Nothing to enable — the feature ships always-on (it landed with the plugin-platform parity slice on 2026-08-06).
- A plugin opts in by declaring
navigationand/orchromein its manifestpermissionslist. The marketplace asks you to consent when a plugin requests a permission; a plugin that doesn't declare it can't use it. - Once installed, a contributing plugin's buttons appear (namespaced
plugin:<pluginId>:<id>) on the header toolbar, and its items appear in the right-click menu of any session. - Click one — a
command:<id>event (plus the session id, for menu items) is delivered to that plugin's own webview. Open its pane to see the result. - Desktop only: the paired phone has no plugin webview surface, so plugin navigation and chrome clicks are refused over the phone/WS bridge (see the implementation facts below).
For agents
- Manifest opt-in:
permissions: ['navigation'](navigate),['chrome'](toolbar + session-menu items), or both — declared once in the manifest; consent derives from the manifest label + description. There is no separateui.contributionsmanifest field. - One item shape, no drift: toolbar and context-menu items validate against the same
pluginChromeItemSchemathe outbound push uses. An item hasid(kebab-case),label(text only), and aniconthat is a NAME from an allow-list — never a URL or inline SVG (unknown name → fallback icon). Arrays are capped: 20 toolbar items, 10 menu items. - Guard order in each bridge handler:
requirePluginUiAccess()first (the shipped feature gate — always true now, kept as a fail-closed tripwire), then the per-permission gate (hasNavigationPermission/hasChromePermission), then the wire schema, then (navigation only) the per-plugin rate limit, then emit the validated push. - Navigation: rate-limited per plugin over a window bucket (a runaway plugin can't focus-steal in a loop); a
kind: 'virtual'target id must match/^__[a-z0-9_]+__$/. - Click round-trip: the renderer invokes
IPC.PLUGIN_DISPATCH_COMMAND(plugin:dispatch-command) with{ pluginId, command, sessionId? }; Main relays acommand:<id>event to ONLY that plugin's webview. Navigation emits theIPC.PLUGIN_NAVIGATE_VIEW(plugin:navigate-view) push to the active renderer. - Desktop-only (
plugin-chrome-is-desktop-only): both channels sit inBLOCKED_CHANNELSin web-access-ws-channels.ts — the phone/WS bridge refuses them without even calling the handler. - Files: bridge handlers navigation-handler.ts, toolbar-handler.ts, context-menu-handler.ts + the gate permissions.ts; wire schemas bridge-method-schemas.ts; channel names ipc-channels/plugins.ts; renderer chrome store plugin-chrome-store.ts with renderers plugin-toolbar-items.ts + useSessionContextMenuActions.ts.
- Contract + invariants: plugin-ui-contributions-contract.md, locked by tests in plugin-chrome-bridge.test.ts.
Related
Plugin Marketplace covers installing plugins and the permission-consent UX this page's opt-in rides on. For the higher-power capabilities a plugin can call, see Plugin Bridge Capabilities; for the in-plugin session experience — sessions started from inside a plugin's own surface — see Plugin AI-Native Sessions. The read-only disclosure that a plugin's AI can take actions is Plugin Capability Card, and how a session discovers and drives installed plugins is Plugin CLI Discovery.
Last verified 2026-09-23