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.
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.
Hook
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.
Tool declaration
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.
Tool handler
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.
Ambient guidance
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.
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.
Persona enablement
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.
Pre-send mutation
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.
Request-time mutation
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.
Tool observation
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.
Assistant output observation
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 safety hook
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.
Integration safety call (v5)
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.