Extension reference
API reference
The public version 1 contract exported by @nativepi/extension-api. Signatures on this page track the latest published package rather than unreleased source changes.
Package entrypoints
| Import | Purpose |
|---|---|
@nativepi/extension-api | Renderer definition, protocol definition, context, contribution, and protocol types |
@nativepi/extension-api/host | Pi-process connect function and host protocol types |
@nativepi/extension-api/schema | Host-provided Zod 4 export |
@nativepi/extension-api/ui | Host-provided React components and prop types |
Versions
const extensionApiVersion: 1;
const version: string; // npm package version, for example "1.0.0"apiVersion: 1 is the renderer contract checked at load time. version is informational package metadata. Write the API version as a literal in the renderer; do not derive it from either export, because the embedded literal lets a newer host identify an old bundle before executing it.
defineRenderer
function defineRenderer<
const Protocol extends ExtensionProtocol = ExtensionProtocol,
>(renderer: NativePiRenderer<Protocol>): NativePiRenderer<Protocol>;
interface NativePiRenderer<Protocol extends ExtensionProtocol = ExtensionProtocol> {
apiVersion: 1;
protocol?: Protocol;
tools?: Record<string, ToolRenderer<Protocol>>;
entries?: Record<string, EntryRenderer<Protocol>>;
composerWidgets?: ComposerWidget<Protocol>[];
composerControls?: ComposerControl<Protocol>[];
conversationViews?: ConversationView<Protocol>[];
panels?: ContextPanel<Protocol>[];
settings?: SettingsSection<Protocol>[];
}An identity function that contextually types the renderer definition. The default export of nativepi.renderer must be the returned object.
RendererContext
interface RendererContext<Protocol extends ExtensionProtocol = ExtensionProtocol> {
extension: { id: string; name: string };
project: { path: string; name: string };
session: { file: string | null; name?: string };
agent: {
status: "idle" | "starting" | "ready" | "error" | "exited";
running: boolean;
model?: RendererModel;
thinkingLevel: string;
};
channel: RendererChannel<Protocol>;
actions: RendererActions;
}
interface RendererModel {
provider: string;
id: string;
name?: string;
reasoning?: boolean;
contextWindow?: number;
}RendererActions
interface RendererActions {
notify(message: string, tone?: "info" | "warning" | "error"): void;
insertIntoComposer(text: string): void;
openExternal(url: string): Promise<void>;
openFile(
file: string,
location?: { line?: number; column?: number },
): Promise<void>;
revealFile(file: string): Promise<void>;
copyText(text: string): Promise<void>;
}ToolRenderer
type ToolRenderer<
Protocol extends ExtensionProtocol = ExtensionProtocol,
Arguments extends Record<string, unknown> = Record<string, unknown>,
Details = unknown,
> = (props: {
call: ToolCall<Arguments>;
result?: ToolResult<Details>;
context: RendererContext<Protocol>;
}) => ReactNode;
interface ToolCall<Arguments extends Record<string, unknown> = Record<string, unknown>> {
id: string;
name: string;
arguments: Arguments;
}
interface ToolResult<Details = unknown> {
toolName: string;
text: string;
details?: Details;
isError: boolean;
}EntryRenderer
type EntryRenderer<
Protocol extends ExtensionProtocol = ExtensionProtocol,
Entry extends SessionEntry = SessionEntry,
> = (props: {
entry: Entry;
context: RendererContext<Protocol>;
}) => ReactNode;
interface SessionEntry {
id: string;
type: string;
[key: string]: unknown;
}Array contributions
interface ComposerWidget<Protocol extends ExtensionProtocol = ExtensionProtocol> {
id: string;
placement: "aboveComposer" | "belowComposer";
render: (context: RendererContext<Protocol>) => ReactNode;
}
interface ComposerControl<Protocol extends ExtensionProtocol = ExtensionProtocol> {
id: string;
render: (context: RendererContext<Protocol>) => ReactNode;
}
interface ConversationView<Protocol extends ExtensionProtocol = ExtensionProtocol> {
id: string;
label: string;
control?: (context: RendererContext<Protocol>) => ReactNode;
render: (context: RendererContext<Protocol>) => ReactNode;
}
interface ContextPanel<Protocol extends ExtensionProtocol = ExtensionProtocol> {
id: string;
title: string;
render: (context: RendererContext<Protocol>) => ReactNode;
}
interface SettingsSection<Protocol extends ExtensionProtocol = ExtensionProtocol> {
id: string;
heading: string;
description?: string;
render: (context: RendererContext<Protocol>) => ReactNode;
}Protocol API
interface ValueSchema<Output extends JsonValue | undefined = JsonValue | undefined> {
parse(value: unknown): Output;
}
interface MethodSchema<
Params extends ValueSchema = ValueSchema,
Result extends ValueSchema<JsonValue> = ValueSchema<JsonValue>,
> {
params?: Params;
result: Result;
}
interface ExtensionProtocol {
methods: Readonly<Record<string, MethodSchema>>;
events: Readonly<Record<string, ValueSchema | undefined>>;
}
function defineProtocol<const Protocol extends ExtensionProtocol>(
protocol: Protocol,
): Protocol;RendererChannel
type SchemaOutput<Schema> = Schema extends ValueSchema<infer Output> ? Output : never;
type Methods<Protocol extends ExtensionProtocol> = Protocol["methods"];
type Events<Protocol extends ExtensionProtocol> = Protocol["events"];
type MethodParams<Method> = Method extends { params: infer Schema }
? SchemaOutput<Schema>
: undefined;
type MethodResult<Method> = Method extends { result: infer Schema }
? SchemaOutput<Schema>
: never;
type EventPayload<Event> = SchemaOutput<Event>;
type OptionalArguments<Value> = [Value] extends [undefined]
? []
: undefined extends Value
? [value?: Value]
: [value: Value];
type MethodArguments<Method> = OptionalArguments<MethodParams<Method>>;
type EventArguments<Event> = OptionalArguments<EventPayload<Event>>;
interface RendererChannel<Protocol extends ExtensionProtocol = ExtensionProtocol> {
call<Name extends keyof Methods<Protocol> & string>(
method: Name,
...args: MethodArguments<Methods<Protocol>[Name]>
): Promise<MethodResult<Methods<Protocol>[Name]>>;
on<Name extends keyof Events<Protocol> & string>(
event: Name,
handler: (...args: EventArguments<Events<Protocol>[Name]>) => void,
): () => void;
}Public helper types MethodArguments and EventArguments derive optional or required tuple arguments from schema outputs. Method result types are inferred directly on call.
connect
function connect<Protocol extends ExtensionProtocol>(
extension: string,
protocol: Protocol,
handlers: ExtensionMethodHandlers<Protocol>,
): ExtensionHost<Protocol>;
interface ExtensionHost<Protocol extends ExtensionProtocol> {
readonly connected: boolean;
emit<Name extends keyof Events<Protocol> & string>(
event: Name,
...args: EventArguments<Events<Protocol>[Name]>
): void;
}
type ExtensionMethodHandlers<Protocol extends ExtensionProtocol> = {
[Name in keyof Methods<Protocol> & string]: (
...args: MethodArguments<Methods<Protocol>[Name]>
) =>
| MethodResult<Methods<Protocol>[Name]>
| Promise<MethodResult<Methods<Protocol>[Name]>>;
};Import from @nativepi/extension-api/host. Registration is atomic for the package name. connected is false and valid emissions are no-ops outside NativePi.
JsonValue
type JsonValue =
| null
| boolean
| number
| string
| JsonValue[]
| { [key: string]: JsonValue };UI exports
@nativepi/extension-api/ui exports these component values and their specialized prop interfaces:
- Actions:
Button,Badge - Inputs:
Input,Textarea,Label,Switch,Separator - Fields:
Field,FieldContent,FieldDescription,FieldError,FieldGroup,FieldLabel - Dialogs:
Dialog,DialogTrigger,DialogClose,DialogContent,DialogHeader,DialogFooter,DialogTitle,DialogDescription - Menus:
Menu,MenuTrigger,MenuContent,MenuGroup,MenuLabel,MenuItem,MenuSeparator - Selects:
Select,SelectTrigger,SelectValue,SelectContent,SelectGroup,SelectLabel,SelectItem,SelectSeparator - Settings:
SettingsActionRow,SettingsSwitchRow,SettingsSelectRow,SettingsTextRow,SettingsSliderRow
Standard elements extend their corresponding React component props. Specialized interfaces include ButtonProps, BadgeProps, DialogProps, MenuProps, MenuContentProps, MenuItemProps, SwitchProps, SelectProps, SelectTriggerProps, SelectContentProps, SelectItemProps, FieldProps, FieldErrorProps, and every settings row's props.
Runtime limits
- Renderer code is trusted and not sandboxed.
- Method calls time out after thirty seconds.
- Calls reject when their chat is no longer active.
- The first configured tool or entry renderer for a key wins.
- Array contribution IDs must be unique within their slot.
- React and API host modules are supplied by NativePi; other renderer dependencies are bundled.
- Renderer Tailwind source is not scanned by NativePi.