Plugins (Extensions)

Quick start

A plugin is either:
  • a native Pllan plugin (pllan.plugin.json + runtime module), or
  • a compatible bundle (.codex-plugin/plugin.json or .claude-plugin/plugin.json)
Both show up under pllan plugins, but only native Pllan plugins execute runtime code in-process.
  1. See what is already loaded:
pllan plugins list
  1. Install an official plugin (example: Voice Call):
pllan plugins install @pllan/voice-call
Npm specs are registry-only. See install rules for details on pinning, prerelease gating, and supported spec formats.
  1. Restart the Gateway, then configure under plugins.entries.<id>.config.
See Voice Call for a concrete example plugin. Looking for third-party listings? See Community plugins. Need the bundle compatibility details? See Plugin bundles. For compatible bundles, install from a local directory or archive:
pllan plugins install ./my-bundle
pllan plugins install ./my-bundle.tgz
For Claude marketplace installs, list the marketplace first, then install by marketplace entry name:
pllan plugins marketplace list <marketplace-name>
pllan plugins install <plugin-name>@<marketplace-name>
Pllan resolves known Claude marketplace names from ~/.claude/plugins/known_marketplaces.json. You can also pass an explicit marketplace source with --marketplace.

Available plugins (official)

Installable plugins

These are published to npm and installed with pllan plugins install:
PluginPackageDocs
Matrix@pllan/matrixMatrix
Microsoft Teams@pllan/msteamsMicrosoft Teams
Nostr@pllan/nostrNostr
Voice Call@pllan/voice-callVoice Call
Zalo@pllan/zaloZalo
Zalo Personal@pllan/zalouserZalo Personal
Microsoft Teams is plugin-only as of 2026.1.15. Packaged installs also ship install-on-demand metadata for heavyweight official plugins. Today that includes WhatsApp and memory-lancedb: onboarding, pllan channels add, pllan channels login --channel whatsapp, and other channel setup flows prompt to install them when first used instead of shipping their full runtime trees inside the main npm tarball.

Bundled plugins

These ship with Pllan and are enabled by default unless noted. Memory:
  • memory-core — bundled memory search (default via plugins.slots.memory)
  • memory-lancedb — install-on-demand long-term memory with auto-recall/capture (set plugins.slots.memory = "memory-lancedb")
Model providers (all enabled by default): anthropic, byteplus, cloudflare-ai-gateway, github-copilot, google, huggingface, kilocode, kimi-coding, minimax, mistral, modelstudio, moonshot, nvidia, openai, opencode, opencode-go, openrouter, qianfan, qwen-portal-auth, synthetic, together, venice, vercel-ai-gateway, volcengine, xiaomi, zai Speech providers (enabled by default): elevenlabs, microsoft Other bundled:
  • copilot-proxy — VS Code Copilot Proxy bridge (disabled by default)

Compatible bundles

Pllan also recognizes compatible external bundle layouts:
  • Codex-style bundles: .codex-plugin/plugin.json
  • Claude-style bundles: .claude-plugin/plugin.json or the default Claude component layout without a manifest
  • Cursor-style bundles: .cursor-plugin/plugin.json
They are shown in the plugin list as format=bundle, with a subtype of codex, claude, or cursor in verbose/inspect output. See Plugin bundles for the exact detection rules, mapping behavior, and current support matrix.

Config

{
  plugins: {
    enabled: true,
    allow: ["voice-call"],
    deny: ["untrusted-plugin"],
    load: { paths: ["~/Projects/oss/voice-call-extension"] },
    entries: {
      "voice-call": { enabled: true, config: { provider: "twilio" } },
    },
  },
}
Fields:
  • enabled: master toggle (default: true)
  • allow: allowlist (optional)
  • deny: denylist (optional; deny wins)
  • load.paths: extra plugin files/dirs
  • slots: exclusive slot selectors such as memory and contextEngine
  • entries.<id>: per-plugin toggles + config
Config changes require a gateway restart. See Configuration reference for the full config schema. Validation rules (strict):
  • Unknown plugin ids in entries, allow, deny, or slots are errors.
  • Unknown channels.<id> keys are errors unless a plugin manifest declares the channel id.
  • Native plugin config is validated using the JSON Schema embedded in pllan.plugin.json (configSchema).
  • Compatible bundles currently do not expose native Pllan config schemas.
  • If a plugin is disabled, its config is preserved and a warning is emitted.

Disabled vs missing vs invalid

These states are intentionally different:
  • disabled: plugin exists, but enablement rules turned it off
  • missing: config references a plugin id that discovery did not find
  • invalid: plugin exists, but its config does not match the declared schema
Pllan preserves config for disabled plugins so toggling them back on is not destructive.

Discovery and precedence

Pllan scans, in order:
  1. Config paths
  • plugins.load.paths (file or directory)
  1. Workspace extensions
  • <workspace>/.pllan/extensions/*.ts
  • <workspace>/.pllan/extensions/*/index.ts
  1. Global extensions
  • ~/.pllan/extensions/*.ts
  • ~/.pllan/extensions/*/index.ts
  1. Bundled extensions (shipped with Pllan; mixed default-on/default-off)
  • <pllan>/dist/extensions/* in packaged installs
  • <workspace>/dist-runtime/extensions/* in local built checkouts
  • <workspace>/extensions/* in source/Vitest workflows
Many bundled provider plugins are enabled by default so model catalogs/runtime hooks stay available without extra setup. Others still require explicit enablement via plugins.entries.<id>.enabled or pllan plugins enable <id>. Bundled plugin runtime dependencies are owned by each plugin package. Packaged builds stage opted-in bundled dependencies under dist/extensions/<id>/node_modules instead of requiring mirrored copies in the root package. Very large official plugins can ship as metadata-only bundled entries and install their runtime package on demand. npm artifacts ship the built dist/extensions/* tree; source extensions/* directories stay in source checkouts only. Installed plugins are enabled by default, but can be disabled the same way. Workspace plugins are disabled by default unless you explicitly enable them or allowlist them. This is intentional: a checked-out repo should not silently become production gateway code. If multiple plugins resolve to the same id, the first match in the order above wins and lower-precedence copies are ignored.

Enablement rules

Enablement is resolved after discovery:
  • plugins.enabled: false disables all plugins
  • plugins.deny always wins
  • plugins.entries.<id>.enabled: false disables that plugin
  • workspace-origin plugins are disabled by default
  • allowlists restrict the active set when plugins.allow is non-empty
  • allowlists are id-based, not source-based
  • bundled plugins are disabled by default unless:
    • the bundled id is in the built-in default-on set, or
    • you explicitly enable it, or
    • channel config implicitly enables the bundled channel plugin
  • exclusive slots can force-enable the selected plugin for that slot

Plugin slots (exclusive categories)

Some plugin categories are exclusive (only one active at a time). Use plugins.slots to select which plugin owns the slot:
{
  plugins: {
    slots: {
      memory: "memory-core", // or "none" to disable memory plugins
      contextEngine: "legacy", // or a plugin id such as "lossless-claw"
    },
  },
}
Supported exclusive slots:
  • memory: active memory plugin ("none" disables memory plugins)
  • contextEngine: active context engine plugin ("legacy" is the built-in default)
If multiple plugins declare kind: "memory" or kind: "context-engine", only the selected plugin loads for that slot. Others are disabled with diagnostics. Declare kind in your plugin manifest.

Plugin IDs

Default plugin ids:
  • Package packs: package.json name
  • Standalone file: file base name (~/.../voice-call.ts -> voice-call)
If a plugin exports id, Pllan uses it but warns when it does not match the configured id.

Inspection

pllan plugins inspect openai        # deep detail on one plugin
pllan plugins inspect openai --json # machine-readable
pllan plugins list                  # compact inventory
pllan plugins status                # operational summary
pllan plugins doctor                # issue-focused diagnostics

CLI

pllan plugins list
pllan plugins inspect <id>
pllan plugins install <path>                 # copy a local file/dir into ~/.pllan/extensions/<id>
pllan plugins install ./extensions/voice-call # relative path ok
pllan plugins install ./plugin.tgz           # install from a local tarball
pllan plugins install ./plugin.zip           # install from a local zip
pllan plugins install -l ./extensions/voice-call # link (no copy) for dev
pllan plugins install @pllan/voice-call   # install from npm
pllan plugins install @pllan/voice-call --pin # store exact resolved name@version
pllan plugins update <id-or-npm-spec>
pllan plugins update --all
pllan plugins enable <id>
pllan plugins disable <id>
pllan plugins doctor
See pllan plugins CLI reference for full details on each command (install rules, inspect output, marketplace installs, uninstall). Plugins may also register their own top-level commands (example: pllan voicecall).

Plugin API (overview)

Plugins export either:
  • A function: (api) => { ... }
  • An object: { id, name, configSchema, register(api) { ... } }
register(api) is where plugins attach behavior. Common registrations include:
  • registerTool
  • registerHook
  • on(...) for typed lifecycle hooks
  • registerChannel
  • registerProvider
  • registerSpeechProvider
  • registerMediaUnderstandingProvider
  • registerWebSearchProvider
  • registerHttpRoute
  • registerCommand
  • registerCli
  • registerContextEngine
  • registerService
See Plugin manifest for the manifest file format.

Further reading