A plugin is a directory whose package.json is the manifest and whose subdirectories are the surfaces it contributes. This page covers that layout and the single public package every surface imports from.
Plugins are in beta. The plugin API (@vellumai/plugin-api) is not yet stable and can change between releases. Pin the peerDependencies range your plugin declares, and expect breaking changes until we cut a 1.0. The authoring convention and the @vellumai/plugin-api surface can both change between releases while plugins are in beta.
The other pages in this section cover each surface on its own. This page is the glue: how those surfaces sit together in one directory, what the manifest declares, and what a plugin is allowed to import from the host.
A plugin lives at <workspaceDir>/plugins/<name>/. The host introspects the directory at load time: the manifest names the plugin, and each named subdirectory is discovered by convention.
my-plugin/
├── package.json # Manifest (required)
├── README.md # Optional plugin docs
├── hooks/ # Lifecycle hooks, one per file
│ ├── init.ts
│ └── pre-model-call.ts
├── tools/ # Model-visible tools, one per file
│ └── example.ts
├── skills/ # On-demand instruction bundles
│ └── my-skill/
│ └── SKILL.md
└── src/ # Internal modules (NOT walked by the loader)
└── state.tsA few rules govern how the loader walks that tree:
Each surface can also be dropped straight into the workspace at /workspace/<surface>/<name>/ without wrapping it in a plugin. A plugin is what lets you ship several surfaces together as one installable unit.
Every plugin has a package.json. The loader reads three fields and passes everything else through untouched, so your editor, linter, and publish tooling keep working as normal.
{
"name": "@you/my-plugin",
"version": "0.0.1",
"peerDependencies": {
"@vellumai/plugin-api": "^0.8.0"
},
"vellum": {}
}name (required). Any npm-style name. The loader strips the scope (@you/) for the in-runtime plugin name, and duplicate names fail registration.version. Informational, and defaults to 0.0.0 when absent.peerDependencies["@vellumai/plugin-api"]. A semver range checked against the running assistant. While plugins are in beta a mismatch is logged but does not block load. Once the install path stabilizes the mismatch will harden into a hard reject, so pin a real range.vellum. Reserved for future use.Plugins import everything they need from a single package, @vellumai/plugin-api. It is the only supported contract: anything not exported from there is assistant-internal and can change without notice. Most of the surface is types (the contexts the host hands your code), with a small set of runtime handles that resolve to the assistant's live singletons. Expand a group to see what it exports.
The context shape the host hands to each lifecycle hook, the hook signature itself, and the wired hook-name constant. Each context's full field contract is documented on the Hooks page. See the Hooks page.
| Export | Kind | Purpose |
|---|---|---|
HOOKS | const | Wired hook names keyed by constant (INIT, PRE_MODEL_CALL, and so on). Reference hooks by this instead of free-form strings. |
HookName | type | Union of every wired hook name declared in HOOKS. |
PluginHookFn | type | Signature every hook implements: (ctx) => Promise<Partial<ctx> | void>. |
PluginInitContext | type | Passed to the init hook at bootstrap. |
PluginShutdownContext | type | Passed to the shutdown hook at teardown. |
UserPromptSubmitContext | type | Passed to user-prompt-submit, before a turn's messages reach the agent loop. |
PreModelCallContext | type | Passed to pre-model-call, before each provider call. |
PostToolUseContext | type | Passed to post-tool-use, once per tool result. |
PostModelCallContext | type | Passed to post-model-call, for each finalized assistant message. |
PostCompactContext | type | Passed to post-compact, after the loop compacts a conversation mid-turn. |
StopContext | type | Passed to stop, when the model yields a response with no tool calls. |
StopDecision | type | Return shape of a stop hook: whether to end the turn or continue. |
The author-facing tool spec and the shapes passed to and returned from a tool's execute method. Each is documented in full on the Tools page. See the Tools page.
| Export | Kind | Purpose |
|---|---|---|
ToolDefinition | type | Author-facing tool spec, the default-export shape for a tools/<name>.ts file. |
ToolContext | type | Runtime context passed as the second argument to a tool's execute. |
ToolExecutionResult | type | Return shape of a tool's execute: { content, isError }. |
RiskLevel | enum | Risk bands (low, medium, high) that drive default permission gating for a tool. |
The logger the host binds to your plugin name and threads onto the contexts. Log through it rather than rolling your own.
| Export | Kind | Purpose |
|---|---|---|
PluginLogger | type | Pino-compatible logger shape, bound to { plugin: <name> } and present on the contexts. |
Values, not just types, that a plugin consumes at module-load or init time. A boot-time shim rebinds each from the assistant's own namespace, so they resolve to the same live singletons the assistant uses.
| Export | Kind | Purpose |
|---|---|---|
assistantEventHub | value | The assistant's pub/sub hub for runtime events. Subscribe to react to activity outside the hook chain. |
getSecureKeyAsync | value | Read a secret from the assistant's secure storage by key. |
AssistantEvent | type | Payload shape of an event published on the hub. |
AssistantEventHub | type | Interface of the event hub itself. |
AssistantEventCallback | type | Subscriber callback invoked for each matching event. |
AssistantEventFilter | type | Filter narrowing which events a subscription receives. |
AssistantEventSubscription | type | Handle returned by subscribing, used to unsubscribe. |