Skip to main content

Core Functions

definePlugin()

Defines a plugin with metadata and setup function.
function definePlugin(
  name: string,
  version: string,
  setup: () => Promise<Plugin> | Plugin
): PluginDefinition
name
string
required
Plugin name (used for module identification)
version
string
required
Plugin version (semver format recommended)
setup
() => Promise<Plugin> | Plugin
required
Function that returns the plugin implementation
Example:
import { definePlugin } from '@proj-airi/plugin-sdk'

export default definePlugin('my-plugin', '1.0.0', () => ({
  async init(context) {
    return true
  },
  async setupModules(context) {
    // Setup logic
  }
}))

Plugin Interface

Plugin

The plugin implementation interface.
interface Plugin {
  init?: (context: ContextInit) => Promise<void | undefined | false>
  setupModules?: (context: ContextInit) => Promise<void | undefined>
}
init
function
Plugin initialization hook. Return false to abort initialization.
setupModules
function
Module setup hook. Called after configuration is applied.

ContextInit

Context passed to plugin hooks.
interface ContextInit {
  channels: {
    host: ChannelHost
  }
  apis: PluginApis
}
channels.host
ChannelHost
required
Event channel for communicating with the plugin host
apis
PluginApis
required
APIs for protocol and client operations

Plugin Host API

PluginHost

Manages plugin lifecycle and orchestration.
class PluginHost {
  constructor(options?: PluginHostOptions)
  
  async load(manifest: ManifestV1, options?: PluginLoadOptions): Promise<PluginHostSession>
  async init(sessionId: string, options?: PluginStartOptions): Promise<PluginHostSession>
  async start(manifest: ManifestV1, options?: PluginStartOptions): Promise<PluginHostSession>
  
  async applyConfiguration(sessionId: string, config: ModuleConfigEnvelope): Promise<PluginHostSession>
  markConfigurationNeeded(sessionId: string, reason?: string): PluginHostSession
  
  announceCapability(key: string, metadata?: Record<string, unknown>): CapabilityDescriptor
  markCapabilityReady(key: string, metadata?: Record<string, unknown>): CapabilityDescriptor
  markCapabilityDegraded(key: string, metadata?: Record<string, unknown>): CapabilityDescriptor
  withdrawCapability(key: string, metadata?: Record<string, unknown>): CapabilityDescriptor
  
  async waitForCapability(key: string, timeoutMs?: number): Promise<CapabilityDescriptor>
  async waitForCapabilities(keys: string[], timeoutMs?: number): Promise<void>
  isCapabilityReady(key: string): boolean
  listCapabilities(): CapabilityDescriptor[]
  
  listSessions(): PluginHostSession[]
  getSession(sessionId: string): PluginHostSession | undefined
  stop(sessionId: string): PluginHostSession | undefined
  async reload(sessionId: string, options?: PluginStartOptions): Promise<PluginHostSession>
  
  setProvidersListResolver(resolver: () => Promise<Array<{ name: string }>> | Array<{ name: string }>): void
}

PluginHostOptions

interface PluginHostOptions {
  runtime?: 'electron' | 'node' | 'web'
  transport?: PluginTransport
  protocolVersion?: string
  apiVersion?: string
  supportedProtocolVersions?: string[]
  supportedApiVersions?: string[]
}
runtime
'electron' | 'node' | 'web'
Runtime environment. Defaults to 'electron'.
transport
PluginTransport
Communication transport. Defaults to { kind: 'in-memory' }.
protocolVersion
string
Preferred protocol version. Defaults to 'v1'.
apiVersion
string
Preferred API version. Defaults to 'v1'.

PluginStartOptions

interface PluginStartOptions {
  cwd?: string
  runtime?: 'electron' | 'node' | 'web'
  requireConfiguration?: boolean
  compatibility?: Omit<ModuleCompatibilityRequest, 'protocolVersion' | 'apiVersion'>
  requiredCapabilities?: string[]
  capabilityWaitTimeoutMs?: number
}
cwd
string
Working directory for loading plugin files
requireConfiguration
boolean
Stop at configuration-needed phase. Defaults to false.
requiredCapabilities
string[]
Capabilities that must be ready before initialization
capabilityWaitTimeoutMs
number
Timeout for waiting on capabilities. Defaults to 15000ms.

PluginHostSession

interface PluginHostSession {
  manifest: ManifestV1
  plugin: Plugin
  id: string
  index: number
  cwd: string
  identity: ModuleIdentity
  phase: PluginSessionPhase
  lifecycle: ActorRefFrom<typeof pluginLifecycleMachine>
  transport: PluginTransport
  runtime: PluginRuntime
  channels: {
    host: ReturnType<typeof createPluginContext>
  }
  apis: PluginApis
}

APIs

Protocol APIs

APIs for plugin-host protocol communication.

capabilities

interface CapabilityApis {
  wait(key: string, timeoutMs?: number): Promise<CapabilityDescriptor>
  snapshot(): Promise<CapabilityDescriptor[]>
}
wait() Wait for a capability to become ready.
const capability = await apis.protocol.capabilities.wait('llm:openai', 15000)
key
string
required
Capability key to wait for
timeoutMs
number
Timeout in milliseconds. Defaults to 15000.
capability
CapabilityDescriptor
The capability descriptor when ready
snapshot() Get current snapshot of all capabilities.
const capabilities = await apis.protocol.capabilities.snapshot()
capabilities
CapabilityDescriptor[]
Array of all capability descriptors

resources.providers

interface ProviderApis {
  list(): Promise<Array<{ name: string }>>
}
list() List available resource providers.
const providers = await apis.protocol.resources.providers.list()
providers
Array<{ name: string }>
Array of provider names

Client APIs

APIs for client-side functionality.

resources.providers

interface ClientProviderApis {
  list(): Promise<Array<{ name: string }>>
}
list() List available providers from client perspective.
const providers = await apis.client.resources.providers.list()

Types

ManifestV1

Plugin manifest structure.
interface ManifestV1 {
  apiVersion: 'v1'
  kind: 'manifest.plugin.airi.moeru.ai'
  name: string
  entrypoints: {
    default?: string
    electron?: string
    node?: string
    web?: string
  }
}
apiVersion
'v1'
required
Manifest API version
kind
'manifest.plugin.airi.moeru.ai'
required
Manifest kind identifier
name
string
required
Plugin name
entrypoints
object
required
Runtime-specific entrypoint paths

ModuleIdentity

interface ModuleIdentity {
  id: string
  kind: 'plugin'
  plugin: {
    id: string
    labels?: Record<string, string>
  }
  labels?: Record<string, string>
}

CapabilityDescriptor

interface CapabilityDescriptor {
  key: string
  state: 'announced' | 'ready' | 'degraded' | 'withdrawn'
  metadata?: Record<string, unknown>
  updatedAt: number
}
key
string
required
Unique capability identifier
state
'announced' | 'ready' | 'degraded' | 'withdrawn'
required
Current capability state
metadata
Record<string, unknown>
Additional capability metadata
updatedAt
number
required
Unix timestamp of last update

PluginSessionPhase

Plugin lifecycle phases.
type PluginSessionPhase =
  | 'loading'
  | 'loaded'
  | 'authenticating'
  | 'authenticated'
  | 'announced'
  | 'preparing'
  | 'waiting-deps'
  | 'prepared'
  | 'configuration-needed'
  | 'configured'
  | 'ready'
  | 'failed'
  | 'stopped'

ModuleConfigEnvelope

Configuration envelope structure.
interface ModuleConfigEnvelope<C = Record<string, unknown>> {
  configId: string
  revision: number
  schemaVersion: number
  full: C
}
configId
string
required
Unique configuration identifier
revision
number
required
Configuration revision number
schemaVersion
number
required
Configuration schema version
full
C
required
Complete configuration object

Protocol Events

Standard protocol events emitted by the plugin host.

module:authenticate

Request authentication with token.
{
  type: 'module:authenticate',
  payload: {
    token: string
  }
}

module:authenticated

Authentication result.
{
  type: 'module:authenticated',
  payload: {
    authenticated: boolean
  }
}

module:announce

Announce module to registry.
{
  type: 'module:announce',
  payload: {
    name: string
    identity: ModuleIdentity
    possibleEvents: string[]
  }
}

module:prepared

Module is prepared and ready for configuration.
{
  type: 'module:prepared',
  payload: {
    identity: ModuleIdentity
  }
}

module:configuration:needed

Module requires configuration.
{
  type: 'module:configuration:needed',
  payload: {
    identity: ModuleIdentity
    reason?: string
  }
}

module:configuration:configured

Configuration has been applied.
{
  type: 'module:configuration:configured',
  payload: {
    identity: ModuleIdentity
    config: ModuleConfigEnvelope
  }
}

module:configure

Apply configuration to module.
{
  type: 'module:configure',
  payload: {
    config: Record<string, unknown>
  }
}

module:status

Module status update.
{
  type: 'module:status',
  payload: {
    identity: ModuleIdentity
    phase: PluginSessionPhase
    reason?: string
    details?: Record<string, unknown>
  }
}

registry:modules:sync

Sync module registry.
{
  type: 'registry:modules:sync',
  payload: {
    modules: Array<{
      name: string
      index?: number
      identity: ModuleIdentity
    }>
  }
}

Best Practices

Always define TypeScript interfaces for your plugin configuration to catch errors early.
Validate all configuration and event payloads before using them.
Wrap async operations in try-catch blocks and emit appropriate status events.
Use namespaced capability keys like 'tool:weather' or 'llm:provider:openai'.
Listen for stop events and clean up timers, connections, and other resources.

Next Steps

Creating Plugins

Learn how to build plugins step-by-step

Plugin SDK Overview

Understand plugin architecture and concepts

Build docs developers (and LLMs) love