Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.fluffbuzz.com/llms.txt

Use this file to discover all available pages before exploring further.

Reference for the api.runtime object injected into every plugin during registration. Use these helpers instead of importing host internals directly.
Looking for a walkthrough? See Channel Plugins or Provider Plugins for step-by-step guides that show these helpers in context.
register(api) {
  const runtime = api.runtime;
}

Runtime namespaces

api.runtime.agent

Agent identity, directories, and session management.
// Resolve the agent's working directory
const agentDir = api.runtime.agent.resolveAgentDir(cfg);

// Resolve agent workspace
const workspaceDir = api.runtime.agent.resolveAgentWorkspaceDir(cfg);

// Get agent identity
const identity = api.runtime.agent.resolveAgentIdentity(cfg);

// Get default thinking level
const thinking = api.runtime.agent.resolveThinkingDefault(cfg, provider, model);

// Get agent timeout
const timeoutMs = api.runtime.agent.resolveAgentTimeoutMs(cfg);

// Ensure workspace exists
await api.runtime.agent.ensureAgentWorkspace(cfg);

// Run an embedded agent turn
const agentDir = api.runtime.agent.resolveAgentDir(cfg);
const result = await api.runtime.agent.runEmbeddedAgent({
  sessionId: "my-plugin:task-1",
  runId: crypto.randomUUID(),
  sessionFile: path.join(agentDir, "sessions", "my-plugin-task-1.jsonl"),
  workspaceDir: api.runtime.agent.resolveAgentWorkspaceDir(cfg),
  prompt: "Summarize the latest changes",
  timeoutMs: api.runtime.agent.resolveAgentTimeoutMs(cfg),
});
runEmbeddedAgent(...) is the neutral helper for starting a normal FluffBuzz agent turn from plugin code. It uses the same provider/model resolution and agent-harness selection as channel-triggered replies. runEmbeddedPiAgent(...) remains as a compatibility alias. Session store helpers are under api.runtime.agent.session:
const storePath = api.runtime.agent.session.resolveStorePath(cfg);
const store = api.runtime.agent.session.loadSessionStore(cfg);
await api.runtime.agent.session.saveSessionStore(cfg, store);
const filePath = api.runtime.agent.session.resolveSessionFilePath(cfg, sessionId);

api.runtime.agent.defaults

Default model and provider constants:
const model = api.runtime.agent.defaults.model; // e.g. "anthropic/claude-sonnet-4-6"
const provider = api.runtime.agent.defaults.provider; // e.g. "anthropic"

api.runtime.subagent

Launch and manage background subagent runs.
// Start a subagent run
const { runId } = await api.runtime.subagent.run({
  sessionKey: "agent:main:subagent:search-helper",
  message: "Expand this query into focused follow-up searches.",
  provider: "openai", // optional override
  model: "gpt-4.1-mini", // optional override
  deliver: false,
});

// Wait for completion
const result = await api.runtime.subagent.waitForRun({ runId, timeoutMs: 30000 });

// Read session messages
const { messages } = await api.runtime.subagent.getSessionMessages({
  sessionKey: "agent:main:subagent:search-helper",
  limit: 10,
});

// Delete a session
await api.runtime.subagent.deleteSession({
  sessionKey: "agent:main:subagent:search-helper",
});
Model overrides (provider/model) require operator opt-in via plugins.entries.<id>.subagent.allowModelOverride: true in config. Untrusted plugins can still run subagents, but override requests are rejected.

api.runtime.taskFlow

Bind a Task Flow runtime to an existing FluffBuzz session key or trusted tool context, then create and manage Task Flows without passing an owner on every call.
const taskFlow = api.runtime.taskFlow.fromToolContext(ctx);

const created = taskFlow.createManaged({
  controllerId: "my-plugin/review-batch",
  goal: "Review new pull requests",
});

const child = taskFlow.runTask({
  flowId: created.flowId,
  runtime: "acp",
  childSessionKey: "agent:main:subagent:reviewer",
  task: "Review PR #123",
  status: "running",
  startedAt: Date.now(),
});

const waiting = taskFlow.setWaiting({
  flowId: created.flowId,
  expectedRevision: created.revision,
  currentStep: "await-human-reply",
  waitJson: { kind: "reply", channel: "telegram" },
});
Use bindSession({ sessionKey, requesterOrigin }) when you already have a trusted FluffBuzz session key from your own binding layer. Do not bind from raw user input.

api.runtime.tts

Text-to-speech synthesis.
// Standard TTS
const clip = await api.runtime.tts.textToSpeech({
  text: "Hello from FluffBuzz",
  cfg: api.config,
});

// Telephony-optimized TTS
const telephonyClip = await api.runtime.tts.textToSpeechTelephony({
  text: "Hello from FluffBuzz",
  cfg: api.config,
});

// List available voices
const voices = await api.runtime.tts.listVoices({
  provider: "elevenlabs",
  cfg: api.config,
});
Uses core messages.tts configuration and provider selection. Returns PCM audio buffer + sample rate.

api.runtime.mediaUnderstanding

Image, audio, and video analysis.
// Describe an image
const image = await api.runtime.mediaUnderstanding.describeImageFile({
  filePath: "/tmp/inbound-photo.jpg",
  cfg: api.config,
  agentDir: "/tmp/agent",
});

// Transcribe audio
const { text } = await api.runtime.mediaUnderstanding.transcribeAudioFile({
  filePath: "/tmp/inbound-audio.ogg",
  cfg: api.config,
  mime: "audio/ogg", // optional, for when MIME cannot be inferred
});

// Describe a video
const video = await api.runtime.mediaUnderstanding.describeVideoFile({
  filePath: "/tmp/inbound-video.mp4",
  cfg: api.config,
});

// Generic file analysis
const result = await api.runtime.mediaUnderstanding.runFile({
  filePath: "/tmp/inbound-file.pdf",
  cfg: api.config,
});
Returns { text: undefined } when no output is produced (e.g. skipped input).
api.runtime.stt.transcribeAudioFile(...) remains as a compatibility alias for api.runtime.mediaUnderstanding.transcribeAudioFile(...).

api.runtime.imageGeneration

Image generation.
const result = await api.runtime.imageGeneration.generate({
  prompt: "A robot painting a sunset",
  cfg: api.config,
});

const providers = api.runtime.imageGeneration.listProviders({ cfg: api.config });

api.runtime.webSearch

Web search.
const providers = api.runtime.webSearch.listProviders({ config: api.config });

const result = await api.runtime.webSearch.search({
  config: api.config,
  args: { query: "FluffBuzz plugin SDK", count: 5 },
});

api.runtime.media

Low-level media utilities.
const webMedia = await api.runtime.media.loadWebMedia(url);
const mime = await api.runtime.media.detectMime(buffer);
const kind = api.runtime.media.mediaKindFromMime("image/jpeg"); // "image"
const isVoice = api.runtime.media.isVoiceCompatibleAudio(filePath);
const metadata = await api.runtime.media.getImageMetadata(filePath);
const resized = await api.runtime.media.resizeToJpeg(buffer, { maxWidth: 800 });
const terminalQr = await api.runtime.media.renderQrTerminal("https://fluffbuzz.com");
const pngQr = await api.runtime.media.renderQrPngBase64("https://fluffbuzz.com", {
  scale: 6, // 1-12
  marginModules: 4, // 0-16
});
const pngQrDataUrl = await api.runtime.media.renderQrPngDataUrl("https://fluffbuzz.com");
const tmpRoot = resolvePreferredFluffBuzzTmpDir();
const pngQrFile = await api.runtime.media.writeQrPngTempFile("https://fluffbuzz.com", {
  tmpRoot,
  dirPrefix: "my-plugin-qr-",
  fileName: "qr.png",
});

api.runtime.config

Config load and write.
const cfg = await api.runtime.config.loadConfig();
await api.runtime.config.writeConfigFile(cfg);

api.runtime.system

System-level utilities.
await api.runtime.system.enqueueSystemEvent(event);
api.runtime.system.requestHeartbeatNow();
const output = await api.runtime.system.runCommandWithTimeout(cmd, args, opts);
const hint = api.runtime.system.formatNativeDependencyHint(pkg);

api.runtime.events

Event subscriptions.
api.runtime.events.onAgentEvent((event) => {
  /* ... */
});
api.runtime.events.onSessionTranscriptUpdate((update) => {
  /* ... */
});

api.runtime.logging

Logging.
const verbose = api.runtime.logging.shouldLogVerbose();
const childLogger = api.runtime.logging.getChildLogger({ plugin: "my-plugin" }, { level: "debug" });

api.runtime.modelAuth

Model and provider auth resolution.
const auth = await api.runtime.modelAuth.getApiKeyForModel({ model, cfg });
const providerAuth = await api.runtime.modelAuth.resolveApiKeyForProvider({
  provider: "openai",
  cfg,
});

api.runtime.state

State directory resolution.
const stateDir = api.runtime.state.resolveStateDir();

api.runtime.tools

Memory tool factories and CLI.
const getTool = api.runtime.tools.createMemoryGetTool(/* ... */);
const searchTool = api.runtime.tools.createMemorySearchTool(/* ... */);
api.runtime.tools.registerMemoryCli(/* ... */);

api.runtime.channel

Channel-specific runtime helpers (available when a channel plugin is loaded). api.runtime.channel.mentions is the shared inbound mention-policy surface for bundled channel plugins that use runtime injection:
const mentionMatch = api.runtime.channel.mentions.matchesMentionWithExplicit(text, {
  mentionRegexes,
  mentionPatterns,
});

const decision = api.runtime.channel.mentions.resolveInboundMentionDecision({
  facts: {
    canDetectMention: true,
    wasMentioned: mentionMatch.matched,
    implicitMentionKinds: api.runtime.channel.mentions.implicitMentionKindWhen(
      "reply_to_bot",
      isReplyToBot,
    ),
  },
  policy: {
    isGroup,
    requireMention,
    allowTextCommands,
    hasControlCommand,
    commandAuthorized,
  },
});
Available mention helpers:
  • buildMentionRegexes
  • matchesMentionPatterns
  • matchesMentionWithExplicit
  • implicitMentionKindWhen
  • resolveInboundMentionDecision
api.runtime.channel.mentions intentionally does not expose the older resolveMentionGating* compatibility helpers. Prefer the normalized { facts, policy } path.

Storing runtime references

Use createPluginRuntimeStore to store the runtime reference for use outside the register callback:
import { createPluginRuntimeStore } from "fluffbuzz/plugin-sdk/runtime-store";
import type { PluginRuntime } from "fluffbuzz/plugin-sdk/runtime-store";

const store = createPluginRuntimeStore<PluginRuntime>({
  pluginId: "my-plugin",
  errorMessage: "my-plugin runtime not initialized",
});

// In your entry point
export default defineChannelPluginEntry({
  id: "my-plugin",
  name: "My Plugin",
  description: "Example",
  plugin: myPlugin,
  setRuntime: store.setRuntime,
});

// In other files
export function getRuntime() {
  return store.getRuntime(); // throws if not initialized
}

export function tryGetRuntime() {
  return store.tryGetRuntime(); // returns null if not initialized
}
Prefer pluginId for the runtime-store identity. The lower-level key form is for uncommon cases where one plugin intentionally needs more than one runtime slot.

Other top-level api fields

Beyond api.runtime, the API object also provides:
FieldTypeDescription
api.idstringPlugin id
api.namestringPlugin display name
api.configFluffBuzzConfigCurrent config snapshot (active in-memory runtime snapshot when available)
api.pluginConfigRecord<string, unknown>Plugin-specific config from plugins.entries.<id>.config
api.loggerPluginLoggerScoped logger (debug, info, warn, error)
api.registrationModePluginRegistrationModeCurrent load mode; "setup-runtime" is the lightweight pre-full-entry startup/setup window
api.resolvePath(input)(string) => stringResolve a path relative to the plugin root