Documentation

User guide and developer references.

Developer Guide

Runtime exports

These are the public exports the host runtime can read from your JS module. The authoring markdown lists them as optional, so the real rule is simple: export only the pieces your extension needs.

A minimal extension can stop at manifest, useExtension(), getTools(), and executeTool().

Core exports

These exports cover the normal extension path: expose state to the UI, expose tools to the model, and execute those tools in the browser.

Hook

useExtension()
Returns the ExtensionState consumed by host and custom UI surfaces.

This is your primary React-facing export. It returns install state, enabled state, settings, prompt state, status indicators, and helper actions such as enable, disable, or settings updates.

The authoring guide explicitly calls out that useExtension() returns ExtensionState.

Tool declaration

getTools()
Returns the browser-side tools the model may call.

Use this to publish function-calling tool definitions. The tool schema should describe the arguments clearly and stay stable enough that the model can learn when to use it.

Tool execution stays local to the browser runtime. The host namespaces tool names before the model sees them, so your module-level tool names only need to be stable within the extension itself.

Each tool may include an optional displayLabels sibling field (alongside type and function) with pending, success, and error strings. The chat UI uses these for the tool-call card — e.g. "Sending photo…" while running and "Sent you a photo" on success. The field is stripped before tools are sent to the model, so it cannot affect model behaviour. When omitted the card falls back to a Title-Cased tool name.

Tool handler

executeTool(name, args, ctx)
Runs the tool implementation for the tool call chosen by the model.

The handler receives the tool name, parsed arguments, and a runtime context with APIs for prompt instructions, variables, chat state, persona data, memories, media, and UI.

Return the tool result you want the model to see. Throw only for real errors or unknown tools; do not use exceptions as normal control flow.

Ambient guidance

getPromptContributions(ctx)
Returns prompt contribution blocks that should exist even when the extension's tool is never called.

This is the steady-state prompt policy hook from the markdown guide. It is the right place for extension-owned ambient guidance such as style defaults, image rules, or follow-up instructions that should be present before tool selection.

ts
export function getPromptContributions() {
  return [
    {
      placement: 'system_pre',
      priority: 10,
      text: 'When a visual beat would help, create an image near the end of the turn.',
    },
  ];
}

export async function onAfterAssistantMessage(ctx) {
  const disposition = await ctx.runtime.variables.get('disposition', {
    scope: 'persona',
    defaultValue: { score: 0 },
  });

  const nextScore = Number((disposition.value as any)?.score || 0)
    + (ctx.assistantText?.includes('good girl') ? 1 : 0);

  await ctx.runtime.variables.set('disposition', { score: nextScore }, {
    scope: 'persona',
    visibility: 'private',
  });
}

Lifecycle hooks

Lifecycle hooks let the extension react to chat events before, during, and after the main assistant turn. The authoring guide treats these as best-effort reactions rather than a replacement conversation loop.

Persona enablement

onEnable(personaId) and onDisable(personaId)
Run when the extension is enabled or disabled for a persona.

Use these for setup or teardown tied to persona enablement, not per-message logic. They return a promise and do not contribute prompt text directly.

Pre-send mutation

onBeforeUserMessageSend(ctx)
Inspect, rewrite, or block the outgoing user message before the turn begins.

Return { content } to rewrite the outgoing message. Return { blocked: true, message } to stop the send and surface a message to the user.

Return nothing when the extension only wants to observe.

Request-time mutation

onBeforeLLMRequest(ctx)
Modify the tool list for one round or append request-specific prompt contributions.

Return { tools } to replace the tool list for the round, or { promptContributions } to add request-time instructions.

This is the dynamic counterpart to getPromptContributions().

Tool observation

onAfterToolCall(ctx) and onAfterToolResult(ctx)
Observe the selected tool and the resulting success or failure without changing the original tool call.

Use these when you need telemetry, follow-up state updates, or post-tool prompt logic that depends on what the model chose and what the tool returned.

Assistant output observation

onAfterAssistantChunk(ctx) and onAfterAssistantMessage(ctx)
Observe streaming text as it arrives or react once the final assistant message is complete.

onAfterAssistantChunk is for streaming-time observation. onAfterAssistantMessage is the better place for end-of-turn bookkeeping such as memory writes or variable updates.

Emergency stop

The authoring guide calls out emergency stop separately because it is a safety contract, not just another lifecycle convenience.

Toy safety hook

onEmergencyToyStop(ctx)
Receive the host Toy E-Stop event and halt active toy output immediately.

Toy extensions should pair this hook with manifest.supportsEmergencyStop = true.

The host only dispatches the stop event to enabled toy extensions that both declare support and are currently active or connected.

The recommended default is to stop output immediately while keeping the underlying connection intact unless your hardware or safety model requires a harder reset.

Integration safety call (v5)

runtime.emergencyStop({ reason? })
Dispatch the same Toy E-Stop the host top-bar button uses. Returns synchronously once every ready toy extension has run its onEmergencyToyStop hook.

Available on both the surface runtime (panel, footer, settings) and the lifecycle runtime inside hooks. Integration extensions that want to halt toy output in response to user state (e.g. arousal-state cumming or spent transitions) should call this directly instead of writing to a variable and hoping the LLM intercepts it.

Resolves to { ok, stoppedCount, error? }. stoppedCount is the number of toy extensions that successfully completed their stop hook.