Documentation

User guide and developer references.

Developer Guide

Manifest fields

The manifest is the one required export. It identifies the extension, declares what it supports, and registers the UI surfaces it wants.

js
export const manifest = {
  // Identity (required)
  apiVersion: 7,
  version: '1.0.0',
  key: 'my-extension',
  name: 'My Extension',
  category: 'integration',
  icon: 'Heart',
  description: 'One sentence shown in lists and the top bar.',

  // Marketplace presentation (optional)
  iconImageUrl: 'https://example.com/my-extension.svg',
  iconForegroundColor: '#FFFFFF',
  iconBackgroundColor: '#475569',
  requiresApiKey: true,

  // Capability flags (optional)
  supportsPromptContributions: true,
  supportsLifecycleHooks: true,

  // Settings form (optional)
  settings: {
    fields: { /* see UI surfaces */ },
    mainLayout: [],
    quickLayout: [],
  },

  // Surfaces (optional)
  showInTopBar: true,
  chatFooter: { order: 100 },
  chatPanel: { order: 100 },
  PanelComponent: MyPanel,
};

manifest.json and the module export

A dev workspace folder needs both a manifest.json and the export const manifest in the JS file. The JSON copy is what the Dev Extensions scanner and the marketplace listing read. The module copy is what the runtime uses once loaded. Keep the shared fields identical. Component references and chatBarActions handlers can only exist in the module copy.

⚠
The scanner looks for the literal text export const manifest. A default export or a renamed constant is reported as a blocker.

Required fields

keystringrequiredStable unique id. Used for storage, tool namespacing, and installs. The workspace folder name must match it. Renaming it abandons saved variables.
namestringrequiredDisplay name.
category'toy' | 'utility' | 'integration' | 'system'requiredGroups the extension in lists. Only toy extensions take part in emergency stop. Marketplace submissions accept toy, utility, and integration; system is stored as integration.
iconstringrequiredA Lucide icon name, for example 'Heart' or 'Music'.
descriptionstringrequiredOne sentence. The marketplace caps it at 320 characters.
apiVersionnumberrequiredThe runtime version you wrote against. New extensions use 7. Missing means v1. Higher than the app supports fails to load. The dev scanner treats a missing value as a blocker.
versionstringYour own semver, for example '1.2.0'. The marketplace uses it for version history and updates. The loader only warns when it is missing.

Marketplace presentation

These change how the extension is shown. They do not change behaviour, except for desktopOnly and installWarning, which gate the install button.

iconImageUrlstringFull URL of a custom SVG or image for marketplace tiles. Falls back to icon.
iconForegroundColorstringHex accent colour on the tile. Defaults to white.
iconBackgroundColorstringHex base colour for the tile's radial gradient.
requiresBluetoothbooleanShows a Bluetooth badge in settings and listings.
requiresApiKeybooleanShows an API-key badge in settings and listings.
desktopOnlybooleanWeb users see the listing but cannot install. Use when you need Web Serial or other desktop-only APIs.
installWarningstringText shown in a confirmation modal before install. Use for extensions with a real risk surface.

Capability flags

Flags tell the app what the module exports so settings and safety UI can reflect it. Setting a flag without exporting the matching function does nothing.

supportsPromptContributionsbooleanSet when you export getPromptContributions() or return promptContributions from onBeforeLLMRequest.
supportsLifecycleHooksbooleanSet when you export any lifecycle hook.
supportsArtifactsbooleanSet when you call runtime.emitImage().
supportsEmergencyStopbooleanToy extensions only. Pair with an exported onEmergencyToyStop(). Lists the extension under Settings → Advanced as stop-capable.
supportsPatternsbooleanv7, built-in toy extensions only. Requires patternCapabilities and a getPatternSink() export, which the JS loader does not read yet.

Settings

settings{ fields, mainLayout, quickLayout }Declares the host-rendered settings form. Field types, scopes, secrets, and persona overrides are covered on the UI surfaces page.

See Settings schema.

UI surfaces

Each surface is opt-in. Registration fields say where to render and in what order. Component fields supply the body. When a body is missing the host falls back to your quickLayout fields or skips the surface.

Registration

showInTopBarbooleanDefault true. Set false to hide the status icon and dropdown from the top bar.
aggregateInstancesInTopBarbooleanDefault false, which shows one top-bar icon per connected instance. Set true to show a single icon for extensions whose instances share one connection.
chatFooter{ order: number }Registers a section in the drawer above the composer. Lower order sorts first.
chatPanel{ order: number }Registers a section in the right-hand extension panel.
overlay{ order: number }v6. Registers a fullscreen layer over the whole app. order must be 0 or higher. Lower renders behind.
chatBarActionsChatBarAction[]Small icon buttons next to the composer. Each has id, icon (Lucide name), label, onClick(ctx), and optional isActive().

Components

SettingsComponentReact componentReplaces the default settings body on the extension detail page.
StatusComponentReact componentBody of the top-bar dropdown. Also the footer and panel fallback when those have no component.
FooterComponentReact componentBody of the chat footer section.
PanelComponentReact componentBody of the chat panel section.
OverlayComponentReact componentBody of the fullscreen overlay. Declaring overlay without it is a validation warning and the slot is skipped.

Props for each component are on the UI surfaces page.

Set by the loader

Do not set these. The loader overwrites them on every load.

source'builtin' | 'user-js' | 'marketplace-js' | 'dev-js'Where the extension came from. Drives badges and the enable path.
contentHashstringSHA-256 of the module source. Used to detect changes across reloads.
installedAtnumberInstall time in epoch ms. Orders extensions when two claim the same tool name: the earlier install keeps the bare name. Built-in tool names always win.

Validation

The loader refuses a module whose manifest is missing key, name, or category, or asks for an apiVersion newer than the app. A missing version is a console warning. Nothing else is checked at load: a negative overlay order or a chatPanel with nothing to render loads without complaint and simply shows nothing. The marketplace submit form runs the fuller validation and reports those as warnings. The Dev Extensions scanner and the dev install endpoint add their own blockers, listed on the Workflow page.