Contribution model
integrations maps package resources onto Host extension points. Each row has a stable id, a Host-known kind, and an in-package resource. Runtime may bind only declared rows.
Agent-side contributions (Skill, managed MCP, Hook) inject into conversations created or rebound after the plugin is enabled. UI and Provider contributions appear on enable and vanish atomically on disable.
Handler ids
Worker handler ids must match:
^[a-z][A-Za-z0-9]*(?:[.-][A-Za-z0-9]*)*$The id starts with a lowercase letter. Every segment after . or - also starts with a lowercase letter. Valid: hello, surface.createSession, office-preview. Invalid: Hello, save.XML, task.1. JavaScript, Python, and Rust SDKs share this regex. CLI validation requires surface.createSession for an editable file-tab App surface and for structure surfaces.
Kinds
| kind | Role | Author notes |
|---|---|---|
content.skill |
Project contents/skills/... to compatible agents as a read-only native Skill entry |
resource must exist in-package |
content.mcp |
Host-managed MCP started per Agent session | See managedRuntime below |
content.hook |
Hook resource | Manifest validates; the Host has no Hook runtime yet and inspect warns |
workflow.binding |
Expose contents/workflows/ to Composer and Automation |
resource is workflow JSON |
file.opener |
Open by extension, fileNameSuffixes, or mediaTypes |
Exactly one previewProvider or exactly one editorSurface |
artifact.preview |
Broker-managed preview process | handler, optional runtime and process.argv |
app.surface |
Full Trust App | appEntrypoint is app; editable text uses slot: artifact.editor; detail panel uses plugin.detail.panel |
app.command |
Command palette | title, handler; icon from the controlled set |
app.toolbar |
Toolbar | Omit slot for toolbar.main; add-only |
app.status |
Status bar | text or handler; poll only with refreshSeconds; extras go to an overflow menu |
app.composer.slash |
Composer slash command | command defaults to id; prompt is inserted |
app.timeline.card |
Timeline card | handler is surface.createSession; the App fetches via bridge.invoke |
app.settings.section |
Inline block on General settings | handler is surface.createSession; config.json remains the config fact |
app.settings.page |
Full settings sidebar page | title, handler, optional remote |
app.tab |
Top-level central tab | Optional Module Federation remote |
app.panel |
Workspace Dockview panel | defaultPosition is left or center |
app.kanban.view |
One view inside the Kanban tab | The Kanban tab leaves the bar when every view is disabled |
app.composer.action |
Pre-submit Composer action | Requires handler or prompt |
host.service |
Periodic Worker handler | intervalSeconds minimum 5, default 30; at most eight per package |
provider.model.importSource |
Model-provider import source | Handler returns { providers } or an array; import writes presets only |
provider.remote.provisioner |
Remote Host provisioner | provisionKind is a Host-owned source tag such as ssh |
Validation rejects missing, ambiguous, and wrong-slot references. Stable app.surface.slot values are plugin.detail.panel and artifact.editor. Timeline cards and settings sections are synthesized from app.timeline.card / app.settings.section. icon names come from @vibex/plugin-contract/catalog/icons.
Stable surface (init templates and the author Skill describe these): every kind in the table; content.hook validates on the manifest only. Templates: skill, mcp, hooks, file-tab, editor-tab, full, ts-worker, node-worker, python-worker, rust-worker, host-service, host-chrome, provider-import, panel, kanban-view. Official samples: vibex.host-chrome (six chrome slots), vibex.host-surface (tab / panel / kanban view / Composer action / settings page), vibex.provider-import (provider import), vibex.remote-ssh (remote provisioner).
Six chrome slots
vibex-plugin init x --template host-chrome writes all six; delete the ones you do not need.
| kind | Required | What the Host reads from the return value |
|---|---|---|
app.command |
title, handler |
Invoke |
app.toolbar |
title, handler |
Invoke |
app.status |
text or handler |
text and tooltip; refreshSeconds starts polling |
app.composer.slash |
command (optional), prompt |
prompt is inserted into the draft |
app.timeline.card |
label, handler, allowedMethods |
The App fetches via bridge.invoke |
app.settings.section |
title, handler, allowedMethods |
Same |
Toolbar and status bar are add-only: a plugin may add items and leaves built-in indicators in place. Extra status items beyond three go to an overflow menu. Chrome slots refresh through the activation generation.
Structure surfaces and Federation
app.tab, app.panel, app.kanban.view, and app.settings.page default to Module Federation remotes. remote declares name, entry (usually dist/remoteEntry.js), and module. init --template panel and kanban-view write a Vite Federation project. During vibex plugin run dev, Host loadRemote points at Vite; leaving dev falls back to dist/remoteEntry.js. High-frequency panels use the Federation track.
Structure mount(root, environment) receives workspaceId and invoke(handler, input). invoke reaches this plugin's Worker, which then calls host.call.
app.panel defaultPosition is left (left sidebar) or center (center-group tab). Panel IDs are namespaced as plugin:<pluginId>/<panelId>. After disable, the layout slot remains as a placeholder and restores on enable.
The Kanban tab aggregates every enabled app.kanban.view. Arrow keys switch views. When every view is disabled, the Kanban tab leaves the central bar.
Skill
resource points at an existing path under contents/. Official packages use a skill directory such as contents/skills/office-docx with SKILL.md inside. The CLI accepts that directory and also accepts a path to SKILL.md. The content index items[].path names a file:
{
"path": "contents/skills/office-docx/SKILL.md",
"kind": "skill",
"title": "Word documents"
}A user Skill of the same name stays. targets may restrict agents; the default is the all-compatible-agents binding intent. SKILL.md is written for the Agent: when to use it, steps, inputs and outputs, hard limits. An external CLI that is not declared as a Runtime requirement still imports; readiness marks the dependency unknown.
MCP
Managed MCP uses managedRuntime:
{
"managedRuntime": {
"source": "runtime/mcp-server.mjs",
"entrypoint": "dist/mcp/server.mjs",
"protocolRevision": "2026-07-28",
"defaultBinding": "all-compatible-agents"
}
}protocolRevision must be 2026-07-28. entrypoint must exist in-package. source is optional; when present, build compiles it to entrypoint. You may also declare kind: "hostFamilyBinary" with binaryId so a Host-family binary supplies the process.
The Host injects connection context bound to the Workspace and parent Conversation. The plugin omits server addresses and credentials from storage. New MCP prefers protocol revision 2026-07-28 and negotiates a compatible version. contents/mcps/*.json may also describe MCP projected into agent-native config; native config remains the Agent Runtime authority.
Official product faces: session and delegation use binaryId: "vibex-mcp"; Workflow Creator uses binaryId: "vibex-workflow-mcp".
Provider seams
provider.model.importSource appears in Settings → Agent → Model Provider under Import. The handler returns { providers: [...] } or an array. Each item needs name and apiUrl; a missing apiKey is listed and cannot be checked. Import writes presets only. Binding uses provider.presets.bind and always shows a Host confirmation. See vibex.provider-import.
provider.remote.provisioner declares provisionKind (for example ssh), label, and handler. Connecting from the saved-Host list with only a profileId calls the matching provisioner. provisionKind is a Host-owned source tag. Uninstalling the plugin keeps saved Hosts by default. See vibex.remote-ssh.
Workflow and file surfaces
A Workflow reference carries identity and dependency evidence. Publish, debug, and run use Workflow Core on the Host. Those entries close when the Host is offline.
artifact.preview declares handler, optional runtime, process.argv, and a ready timeout. After idle timeout the preview process exits and the next open starts it again. The Worker opens a preview with environment.host.call("artifact.preview", "open", { artifactHandle, providerId }). artifactHandle is issued by the Host, lasts about 30 seconds, and is single-use. The current Host routes every artifact.preview operation to open; close is reclaimed when the lease expires or the generation is withdrawn.
Editable file-tab steps:
file.openerfills one ofextensions/fileNameSuffixes/mediaTypesand setseditorSurfaceto an App surfaceid.- That
app.surfaceusesslot: artifact.editor,appEntrypoint: app,handler: surface.createSession. - The Worker registers
surface.createSession. - The App calls
bridge.artifact.readText()and saves withwriteText(content, expectedRevision).
The Host owns the canonical path. The App receives the file name, revision, and bridge.artifact. Writes use the expected revision and atomic replacement. An external edit yields a recoverable conflict, code artifact_revision_conflict.
init --template file-tab writes a .txt artifact.preview plus a plugin.detail.panel. init --template editor-tab writes the four steps above for an editable UTF-8 file tab.
Commands and background services
app.command and app.composer.slash keep Plugin/Command identity and coexist with agent-native commands by source. host.service fits periodic probes or background work. The handler must be registered on the Worker. Interval reads intervalSeconds, minimum 5 seconds, default 30; at most eight services tick per package. A tick is skipped while the previous call is still running.

