Documentation

User guide and developer references.

Developer Guide

Build API v5 extensions

This guide is for extension authors shipping browser-side JS modules. It covers the runtime contract, app-owned UI surfaces, local dev flow, and marketplace packaging without dumping every internal type on one page.

Who this guide is for

Use these docs if you are writing a JS extension that runs in the browser, reads the active chat or persona context, exposes tools to the model, or renders UI inside the app.

Good fit

You want to build an extension in devextensions/, test it in the app, and later publish it.

You need the public runtime contract, not a tour of internal app code.

Not this guide

It does not document every frontend implementation detail or the internal built-in extension codebase.

It does not document every internal frontend implementation detail or the private authoring notes used by repo maintainers.

Build flow

Most extensions follow the same path: define a manifest, add runtime behavior, test fromdevextensions/, then turn the same manifest and JS source into a marketplace draft.

  1. 1Create a manifest and JS module that target API v5.
  2. 2Add tools, hooks, and optional app UI only where the extension actually needs them.
  3. 3Load the extension through the Dev Extensions screen, then refresh it from disk while you iterate.
  4. 4Submit the same code as an unpublished marketplace draft when you want broader testing.

Start here

This is the smallest useful extension shape: a manifest, useExtension(), one tool, and one executeTool() handler.

js
export const manifest = {
  apiVersion: 6,
  version: '1.0.0',
  key: 'hello-extension',
  name: 'Hello Extension',
  category: 'integration',
  icon: 'Sparkles',
  description: 'Adds one simple browser-side tool.',
};

export function useExtension() {
  return {
    isInstalled: true,
    isAvailable: true,
    isEnabled: true,
    isActive: false,
    statusIndicator: { color: 'yellow', label: 'Ready' },
    globalSettings: {},
    personaSettings: {},
    resolvedSettings: {},
    enable: async () => {},
    disable: async () => {},
    updateGlobalSettings: async () => {},
    updatePersonaSettings: async () => {},
    updateSettings: async () => {},
  };
}

export function getTools() {
  return [
    {
      type: 'function',
      function: {
        name: 'say_hello',
        description: 'Return a short greeting.',
        parameters: {
          type: 'object',
          properties: {
            name: { type: 'string' },
          },
          required: ['name'],
        },
      },
    },
  ];
}

export async function executeTool(name, args) {
  if (name !== 'say_hello') {
    throw new Error(`Unknown tool: ${name}`);
  }

  return { message: `Hello, ${args.name}.` };
}
Start with the smallest extension that proves the behavior you need. Add lifecycle hooks, variables, or custom UI after the basic tool path works.

Guide map