Skip to content

PluginManifest

export default definePlugin({ /* … */ });
Field Type Notes
id string Globally unique, kebab-case, 2–40 chars. It’s the key everything is stored under: renaming it is a new app.
apiVersion number Extension API targeted. The host refuses versions it can’t run.
version string Semver of the app. Bump it when the settings change shape.
name string Shown in the storefront and the panel.
summary string One line for the storefront card.
description string? Longer text for the detail page.
icon PluginIcon? A name from a closed list, never a URL or an SVG.
accent PluginAccent? A tint from a closed list, used where the app’s work shows.
highlights string[]? Three or four terse lines: what someone reads before installing.
verticals, audience string[]?, string? Who it’s for, in the words a business uses about its own trade. Used to sort and suggest.

The closed lists for icon and accent are the same idea as everything else here: an app declares which one to draw, not how. The day manifests come from third parties, that’s what stops a storefront card from loading resources from outside or injecting markup into the panel.

Field Type Notes
allowedHosts string[]? The only hosts ctx.http may reach. Absent or empty means no network at all.
capabilities ("image-input" | "document-input")[]? Chat abilities switched on while installed. Each costs tokens the customer pays for, so they’re opt-in.
secrets string[]? Names of the host’s environment variables readable through ctx.secrets. Reserved for core apps.
Field Type Notes
settingGroups PluginSettingGroup[]? Sections of the panel, in drawing order.
settings PluginSettingField[]? The fields. See Setting fields.
onboarding PluginOnboardingStep[]? Guided setup after install, one step per screen.
tools PluginTool[]? Functions the model can call.
jobs PluginJob[]? Daily work, once for the whole platform.
prompt (ctx) => string | null Synchronous, cheap: it’s rebuilt on every request.
migrateSettings (stored, fromVersion) => stored Rewrites saved values into the shape today’s fields expect. Pure and synchronous.
onboarding: [
{
key: "trade", // stable: it's stored as how far they got
title: "Which items are yours",
body: "A general bill has more of other trades' work than yours…",
fields: ["trade", "ownWorks"], // or `group: "…"` for a whole section
tryPrompt: "I have the client's bill of quantities as a PDF…",
},
]

Renaming a key restarts everyone who was midway. And a guide that never asks for a required field is rejected: it would declare itself finished while the panel still reads “needs configuring”.

It runs when the module loads, so these are startup failures rather than customer-facing ones: malformed id, unknown API version, non-semver version, missing name or summary, unknown icon or accent, duplicate tool or job names, a tool without a handler, duplicate setting keys, a field pointing at an undeclared section, a field both hideable and required, empty options, an illustration that doesn’t cover every choice, an invalid job time or an unknown time zone.