Skip to content

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

ImportPurpose
@nativepi/extension-apiRenderer definition, protocol definition, context, contribution, and protocol types
@nativepi/extension-api/hostPi-process connect function and host protocol types
@nativepi/extension-api/schemaHost-provided Zod 4 export
@nativepi/extension-api/uiHost-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.