Documentation

User guide and developer references.

Developer Guide

Runtime APIs

The ctx.runtime object is the extension's app-scoped toolbox for prompt instructions, artifacts, variables, memories, chat context, persona data, media resolution, and extension-owned UI.

Extensions do not need to care whether the app is in local or cloud storage mode. The runtime keeps these APIs scoped to the current session and persona.

Instruction and artifact APIs

These APIs are the bridge between extension logic and the current request. They let the extension add artifacts or prompt instructions without bypassing the host runtime.

Artifact API

emitImage(input)
Add an image artifact to the app-owned gallery or inline fallback.

The authoring guide explicitly recommends this when the extension wants to emit an image after a tool or hook runs.

Common inputs include a generated URL or blob, an optional prompt, and source metadata such as the extension key, tool name, and display mode.

It resolves to the created artifact record instead of only fire-and-forget behavior.

Prompt API

emitPromptInstruction(instruction)
Queue a full prompt-contribution object for the current round.

Use this when you already know the exact contribution shape you want to append, including placement and priority.

Prompt helper

appendAssistantInstruction(text, placement?, priority?)
Convenience wrapper for adding plain-text prompt guidance, defaulting to system_post.

This is the lighter-weight version of emitPromptInstruction(). Use it when the extension just needs to append a short instruction string.

State and context APIs

These APIs let the extension read and write runtime data that belongs to the active persona, active chat, or extension-owned persistent state.

Extension-owned state

variables.get / set / delete / list
Persist JSON runtime state in user or persona scope without abusing settings.

The authoring guide recommends variables for counters, flags, disposition, and other structured scene state that should survive across turns.

Reads are non-throwing: missing keys return found: false. Writes support both scope: 'user' and scope: 'persona', plus visibility values such as private and shared.

Persona memory API

memories.create / search / delete
Read and write memories scoped to the active persona in the current chat.

Use create to store a new memory, search to query relevant memories by text, and delete to remove one.

The markdown examples use this for turn-level reflection after the assistant reply is complete.

Session history API

chat.list(options)
Read chronological messages from the current session with position and speaker filters.

The authoring guide documents position values of first or last, plus speaker filters such as user, persona, both, or all.

Transcript injection

chat.sendMessage(input)
Post a user, assistant, or system message into the active chat — the same path built-ins like findom and auto-advance use.

Available to every extension tier. Pass { role, content }; resolves with { ok, messageId }. Returns an error result when no chat session is active.

ts
// Inside a tool executor or lifecycle hook
await ctx.runtime.chat.sendMessage({
  role: 'system',
  content: 'Webhook fired — escalating heart rate alert.',
});

Programmatic advance

chat.runTurn(options?)
Trigger an LLM turn from extension code — the auto-advance pattern, now exposed to all extensions.

Calls into the same chat-turn runner the app uses for user messages. Omit userMessage to silently advance (no new user-visible message is added). Defaults kind to 'extension'.

Calling from inside a tool executor mid-turn can produce runaway loops. Prefer lifecycle hooks like onAfterAssistantMessage or async UI events (timers, webhook callbacks, settings buttons).
ts
// From onAfterAssistantMessage to silently advance the conversation
await ctx.runtime.chat.runTurn();

// Or from a UI event with a user-visible prompt
await ctx.runtime.chat.runTurn({
  kind: 'extension',
  userMessage: 'Schedule fired: please continue the previous instruction.',
});

Active persona API

persona.get()
Return the normalized persona record for the active chat.

Use this when extension logic needs persona metadata without duplicating that data into extension variables or settings.

Media and UI APIs

These APIs help extensions bridge from host-managed assets and modals into the custom UI or tool flows they own.

My Media bridge

media.get(mediaId) and media.getUrl(mediaId)
Resolve media ids from My Media into metadata or a signed URL for direct use.

The authoring guide recommends this when the user stores media ids in settings or variables and the extension needs to turn them into actual runtime assets.

media.get(...) returns metadata, while media.getUrl(...) returns a direct URL-oriented result for tags like <img> or <audio>.

ts
const introTrack = await ctx.runtime.media.getUrl('media-id-from-settings');

if (introTrack.ok && introTrack.url) {
  const audio = new Audio(introTrack.url);
  await audio.play();
}

Host UI bridge

ui.showModal(request)
Open an app-owned modal and await the chosen action.

Use this when the extension needs a modal decision point but should still render it inside the host app's modal system instead of inventing its own overlay.

The promise resolves with the chosen action id or dismiss result, so the extension can continue its own flow after the user responds.

Transient notification

ui.toast(input)
Show a non-blocking toast in the corner of the app.

Fire-and-forget. Pass { message, level?, durationMs?, actionLabel?, onAction? }. Levels are info, success, warning, error. Default duration is 4000 ms; pass 0 to require manual dismiss.

ts
ctx.runtime.ui.toast({
  message: 'Spotify connected',
  level: 'success',
});

ctx.runtime.ui.toast({
  message: 'Reconnect needed',
  level: 'warning',
  actionLabel: 'Open settings',
  onAction: () => ctx.runtime.ui.showModal({ title: 'Spotify', body: 'Re-link your account.' }),
});