Copy-paste recipe
The whole API is afeatures map handed to createOracleApp. Each key is a bundled plugin name, each value is true / false / 'auto'.
BUNDLED_PLUGINS; features only controls which ones survive resolution. Reference oracle: apps/qiforge-example/src/main.ts.
How resolution works
1
The runtime starts with the bundled list
BUNDLED_PLUGINS is a fixed 15-plugin tuple — memory, portal, firecrawl, domain-indexer, composio, sandbox, skills, editor, agui, slack, tasks, credits, calls, user-preferences, matrix-group-chats.You do not import or instantiate the plugins you want at defaults — they are already there.2
Each plugin gets a feature decision
3
Your own plugins are added next
Plugins from the
plugins: [] array are always loaded — they’re not gated by features. If a name collides with a bundled plugin, your instance wins (the loader dedupes by name).4
Hard-dep cascades resolve transitively
If a loaded plugin has
dependsOn: ['removed-plugin'] and that dep ended up excluded, the dependent cascades off too. Soft deps (softDependsOn) only log a warning.What every bundled plugin does by default
Each plugin’sautoDetect predicate decides whether to opt in when you leave it on 'auto'.
Full per-plugin env vars: plugin catalog and environment variables reference.
Opt out of a plugin
1
Set the feature flag to false
autoDetect runs. Its configSchema is also removed from the merged env schema, so its env vars become optional.2
Check for dependents
If another loaded plugin lists the disabled plugin in its
dependsOn, boot fails with a boot.plugin.dep_missing error naming both. Disable the dependent too, or keep the dependency loaded.Force a plugin on
1
Set the feature flag to true
autoDetect would skip it.2
Set every env var the plugin needs
With See the plugin’s page in the plugin catalog for its full env requirements.
features: { composio: true } and no COMPOSIO_API_KEY, the runtime throws at boot:Plugins that need constructor args
Two bundled plugins take a live runtime object you provide — instantiate explicitly and pass them viaplugins:
The bundled
editorPlugin and creditsPlugin instances boot in stub form (for testing). For production behaviour, instantiate them yourself and pass the live objects in.Retune a bundled plugin’s manifest
features decides whether a bundled plugin loads. To change how it’s advertised — most usefully its visibility tier, but also summary, tags, or whenToUse — use manifestOverrides instead of forking the plugin. The override is shallow-merged onto the plugin’s own manifest at boot, validated like any authored manifest, and seen by every downstream reader (Tier-1 prompt, list_capabilities / load_capability, visibility index).
boot.plugin.manifest_override_unknown) and ignored — so disabling a plugin via features and leaving its override entry behind is safe.
Inspect what loaded
1
Read app.plugins.status() after boot
excluded entry is { plugin, reason } — surface the reason in your boot logs so operators see why a plugin came up missing. (softDepGaps entries are { plugin, missing }.)Where to read next
Plugin catalog
Every bundled plugin in detail.
Environment variables
Core vars plus per-plugin vars.
createOracleApp reference
Every option, exhaustively.
Write a plugin
Build your own next to the bundled set.