This page is the shortest conservative path for authoring a Sero plugin during the source-only OSS alpha. It connects the starter examples, app-runtime hooks, file-backed state model, host capabilities, and Module Federation rules without pretending the plugin API is frozen.
If you only want to install or manage plugins, start with App Store, Favorites, and Installed Plugins. If you want the broad model first, read Plugins and Apps. Use App Runtime Reference for the source-checked hook/API table.
During alpha:
Use explicit manifests, small state contracts, and conservative compatibility requirements.
Start with the smallest example that matches your plugin shape:
| Goal | Start here |
|---|---|
| UI + extension, fastest path | Plugin Quickstart |
| UI + extension + background runtime/widgets | Plugin End-to-End Example |
| Existing Pi extension to convert | repo source docs under docs/plugins/ |
In this checkout, useful in-repo references include:
plugins/sero-admin-plugin — standard app metadata, UI, extension, and app
state shapeplugins/sero-git-plugin — larger UI + extension integrationplugins/sero-cron-plugin — UI + extension + widget metadataTreat these as examples of current implementation patterns, not as a frozen public API contract. For example, the Scheduler/Cron plugin demonstrates how a plugin can combine a dedicated UI with tools, commands, and widget metadata.
A typical Sero plugin has these parts:
Add runtime/ only when you need long-lived workspace orchestration. Extension
only plugins can omit the UI and Vite files.
package.jsonThe package manifest does three jobs:
pi.extensionssero.appsero.pluginImportant fields to understand:
| Field | Purpose |
|---|---|
pi.extensions |
points Pi at your extension entry |
sero.app.id / name / icon |
app identity and sidebar metadata |
sero.app.scope |
whether state is workspace or global in the host model |
sero.app.stateFile |
plugin-owned state file name/path hint |
sero.app.ui / component / devPort |
UI remote entry, exposed component, and dev server port |
sero.app.runtime |
optional background runtime entry |
sero.app.widgets |
optional static widget metadata |
sero.plugin.minSeroVersion |
minimum host version expectation |
sero.plugin.requiredHostCapabilities |
host seams your plugin needs |
sero.plugin.bridgeTools |
whether extension tools bridge to CLI commands |
Keep manifest requirements specific. Unknown or unavailable host capabilities should be treated as unmet.
Keep plugin-owned durable data contracts in shared/types.ts.
Good shared state:
DEFAULT_STATEDate, Map, Set, functions, or class instancesOnly move a type into @sero-ai/common when it is truly neutral and shared
across multiple plugins or host surfaces.
The extension is standard Pi-facing code. It is the right place for tools, commands, and Pi-safe logic.
Authoring guidance:
extension/index.ts entry firstpi.registerTool() for plugin toolsSero can bridge plugin tools into the sero-cli tool surface. The current policy
is controlled by sero.plugin.bridgeTools:
| Value | Meaning |
|---|---|
omitted or true |
bridge all plugin tools |
false |
bridge none |
string[] |
bridge selected tool names |
If your plugin depends on bridged CLI behavior, declare the canonical host capability:
Do not register app tools as host-level custom tools. Let the plugin package and manifest describe the bridge policy.
Plugin UIs are React modules loaded by Sero as federated remotes. Current alpha-safe app-runtime hooks include the table below; see App Runtime Reference for source paths, host caveats, and widget registry APIs.
| Hook | Use for |
|---|---|
useAppInfo() |
current appId, workspaceId, and workspacePath |
useAppState(defaultState) |
file-backed reactive app state |
useAgentPrompt() |
send text to the active agent session |
useAppTools() |
call plugin/app tools through the host bridge |
useWidgetRegistration() |
register runtime widgets for the current renderer session |
useTheme() |
read effective theme information when available |
Other exports may exist, but document them only after verifying current host behavior and support intent.
Use useAppState(defaultState) for plugin UI state that should persist.
The host reads, watches, and writes the state file. Do not use
localStorage or sessionStorage for plugin app state.
Current docs describe app state as profile/workspace scoped:
<SERO_HOME>/apps/<app-id>/state.json<workspace>/.sero/apps/<app-id>/state.jsonSee State and Folders for the canonical storage map.
If the UI needs to trigger plugin-owned logic, expose that behavior as a plugin
tool and call it with useAppTools():
Declare the host capability when relying on this bridge:
Do not add plugin-specific window.sero.myPlugin.* host bridges for ordinary
plugin behavior.
A UI plugin should expose a React component through Vite Module Federation. The host resolves the remote entry, loads the exposed component, and mounts it inside the active app area.
Important rules:
base: './'Published/prebuilt plugins are expected to include their UI build output, such
as dist/ui/remoteEntry.js, when their manifest says they are prebuilt. Source
installs may rebuild locally depending on the install path.
Use a plugin runtime only for long-lived Sero-specific orchestration, such as:
Do not use runtime code for simple CRUD or one-shot UI actions. Put Pi-safe logic in the extension and call it through tools when needed.
When using a runtime, declare the runtime entry and capability:
Plugins can expose compact dashboard widgets through static manifest metadata or runtime registration.
Keep widget claims narrow:
Before sharing a plugin publicly:
package.json has explicit pi.extensions, sero.app, and sero.plugin
fields.requiredHostCapabilities lists only canonical capabilities the plugin
actually needs.useAppState, not browser storage.useAppTools.vite.config.ts uses base: './' for production.Do not claim during alpha that: