API reference
The define* helpers and the types they use. Source of truth:
decisive-sdk/src/define.ts.
Each define* is an identity function — it exists for type inference and to give
Decisive's manifest extractor a stable shape. Import everything from @decisive/sdk.
definePlugin
The default export of every plugin.ts. Bundles capabilities + metadata + declared secrets.
interface PluginDefinition {
name: string; // unique slug within the workspace
title?: string;
description?: Md;
icon?: string; // Lucide icon name
apiVersion: 1; // host API version you target
secrets?: SecretSpec[];
actions?: ActionDefinition[];
contextSources?: ContextSourceDefinition[];
panels?: PanelDefinition[];
webhooks?: WebhookDefinition[];
}
defineAction / defineTool
A capability. defineTool is defineAction with surfaces defaulting to ['ai-tool'].
interface ActionDefinition<I = unknown> {
name: string; // snake_case; the tool name the AI sees
description: string; // write it like a tool description
input?: JsonSchema; // JSON Schema for the argument
surfaces?: Surface[]; // default ['ai-tool'] via defineTool
needs?: string[]; // secret keys this action requires
destructive?: boolean; // requires a consent gate before running
run(input: I, ctx: HostContext): Promise<Result>;
onAction?(event: ActionEvent, ctx: HostContext): Promise<Result>;
}
defineTool<{ id: string }>({
name: 'lookup_order',
description: 'Look up an order by id.',
input: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
needs: ['SHOP_API_KEY'],
async run(input, ctx) { /* ... */ return { data: {/*...*/} }; }
});
The generic types run's input. input is JSON Schema — same shape Decisive's
core tools use — so the schema is what the AI sees and what validates the call.
defineContextSource
Pull external data into Decisive's context on a schedule. After syncing, records are ambient — the AI retrieves them automatically, no tool call needed.
interface ContextSourceDefinition {
name: string;
description: string;
needs?: string[];
schedule?: string; // cron, e.g. '0 * * * *'; omit for manual/webhook-driven
sync(ctx: HostContext): Promise<{ upserted: number }>;
}
defineContextSource({
name: 'notion', description: 'Sync Notion docs into context.',
needs: ['NOTION_TOKEN'], schedule: '0 * * * *',
async sync(ctx) {
const since = await ctx.store.get<string>('cursor');
const docs = await fetchDocs(ctx, since);
const { count } = await ctx.context.upsert(docs.map(toRecord));
return { upserted: count };
}
});
definePanel
A workspace tab/overlay that renders blocks (App-Home style).
interface PanelDefinition {
name: string;
title: string;
icon?: string; // Lucide icon name
render(ctx: HostContext): Promise<{ blocks: Block[] }>;
onAction?(event: ActionEvent, ctx: HostContext): Promise<Result>;
}
defineWebhook
An inbound, signature-verified webhook at /api/extensions/:workspace/:plugin/hooks/:name.
interface WebhookDefinition {
name: string;
verify?: { type: 'hmac-sha256'; header: string; secret: string }; // secret = a declared secret key
handle(event: WebhookEvent, ctx: HostContext): Promise<{ status?: number }>;
}
interface WebhookEvent { headers: Record<string, string>; body: string; json: Json; }
Shared types
Surface
type Surface =
| 'ai-tool'
| 'agent-runner'
| 'panel'
| `slash-command:/${string}`
| 'chat-action';
Result
What every handler returns. All fields optional — combine as needed.
interface Result {
data?: Json; // returned to the AI/caller (the tool result)
blocks?: Block[]; // rendered to humans
openModal?: ModalView; // open a dialog
updateView?: { blocks: Block[] }; // replace the current view in place
toast?: { text: string; style?: 'success' | 'error' | 'info' };
error?: string; // mark failure (AI sees it; humans get an error toast)
}
ActionEvent
Passed to onAction when a block element fires.
interface ActionEvent {
id: string; // the element id that fired
value?: Json; // a button's value, if any
inputs: Record<string, Json>; // current form state, keyed by input id
state?: Json; // private metadata from the producing view/modal
}
SecretSpec
interface SecretSpec { key: string; label: string; help?: Md; required?: boolean; }
JsonSchema
The input shape — identical to Decisive's core tools:
interface JsonSchema {
type: 'object';
properties?: Record<string, JsonSchemaProperty>;
required?: string[];
}
Block types
See the Block Kit reference for Block, ModalView, inputs, and
buttons.
The resolved manifest
At sync time Decisive extracts a declarative manifest (tool schemas, surfaces,
secrets, panels, webhooks) from your plugin and bundles the executable handlers
separately. You don't write this — it's the contract between the sync pipeline and
the runtime — but it's typed in
decisive-sdk/src/manifest.ts if you want to see
exactly what Decisive stores and renders from.