Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion build-an-oracle/develop/enable-bundled-plugins.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Enable bundled plugins"
description: "Toggle the 15 bundled plugins via the features map. Opt out, force on, or let auto-detect handle it."
description: "Toggle the 15 bundled QiForge plugins via the features map — opt out, force on, or let auto-detect handle it — and retune their manifests."
icon: "boxes-stacked"
---

Expand Down Expand Up @@ -31,7 +31,7 @@

<Steps>
<Step title="The runtime starts with the bundled list">
[`BUNDLED_PLUGINS`](https://github.com/ixoworld/ixo-oracles-boilerplate/blob/main/packages/oracle-runtime/src/plugins/index.ts) 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.

Check warning on line 34 in build-an-oracle/develop/enable-bundled-plugins.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/develop/enable-bundled-plugins.mdx#L34

Did you really mean 'firecrawl'?

Check warning on line 34 in build-an-oracle/develop/enable-bundled-plugins.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/develop/enable-bundled-plugins.mdx#L34

Did you really mean 'composio'?

Check warning on line 34 in build-an-oracle/develop/enable-bundled-plugins.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/develop/enable-bundled-plugins.mdx#L34

Did you really mean 'agui'?

You do not import or instantiate the plugins you want at defaults — they are already there.
</Step>
Expand All @@ -53,11 +53,11 @@
</Step>

<Step title="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`).

Check warning on line 56 in build-an-oracle/develop/enable-bundled-plugins.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/develop/enable-bundled-plugins.mdx#L56

Did you really mean 'dedupes'?
</Step>

<Step title="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.

Check warning on line 60 in build-an-oracle/develop/enable-bundled-plugins.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/develop/enable-bundled-plugins.mdx#L60

Did you really mean 'deps'?
</Step>
</Steps>

Expand Down Expand Up @@ -167,6 +167,24 @@
The bundled `editorPlugin` and `creditsPlugin` instances boot in stub form (for testing). For production behaviour, instantiate them yourself and pass the live objects in.
</Note>

## Retune a bundled plugin's manifest

`features` decides **whether** a bundled plugin loads. To change **how it's advertised** — most usefully its [visibility tier](/build-an-oracle/understand/visibility-tiers), but also `summary`, `tags`, or `whenToUse` — use [`manifestOverrides`](/build-an-oracle/reference/createoracleapp#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).

```ts
const app = await createOracleApp({
config,
manifestOverrides: {
// Take a noisy `always` bundled plugin out of the Tier-1 prompt.
'domain-indexer': { visibility: 'on-demand' },
// Hide a transport plugin entirely; its tools still bind.
portal: { visibility: 'silent' },
},
});
```

Override keys that don't match a loaded plugin are logged (`boot.plugin.manifest_override_unknown`) and ignored — so disabling a plugin via `features` and leaving its override entry behind is safe.

## Inspect what loaded

<Steps>
Expand Down
60 changes: 59 additions & 1 deletion build-an-oracle/reference/createoracleapp.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
export interface CreateOracleAppOptions {
config: OracleConfig;
features?: Partial<Record<BundledFeatureName | (string & {}), FeatureToggle>>;
manifestOverrides?: Partial<Record<BundledFeatureName | (string & {}), PluginManifestOverride>>;
plugins?: OraclePlugin[];
nestModules?: Array<Type | DynamicModule>;
authExcludedRoutes?: AuthExcludedRoute[];
Expand Down Expand Up @@ -139,6 +140,63 @@
```
</Accordion>

<Accordion title="manifestOverrides" icon="sliders">
- **Type:** `Partial<Record<BundledFeatureName | (string & {}), PluginManifestOverride>>` where `PluginManifestOverride = Partial<PluginManifest>`
- **Required:** no
- **Added in:** `@ixo/oracle-runtime` 1.2.0

Per-plugin overrides for fields on a loaded plugin's [manifest](/build-an-oracle/reference/manifest-schema). Use this to retune a bundled plugin's discovery — most commonly its [`visibility`](/build-an-oracle/understand/visibility-tiers) — **without forking the plugin source**. Typical uses: flip a noisy `always` plugin to `on-demand`, hide one behind `silent`, or relabel its `summary` / `tags` / `whenToUse`.

Keys are plugin **names** (the same kebab-case identifiers used by [`features`](#features), e.g. `'domain-indexer'`). Values are a `Partial<PluginManifest>` — set only the fields you want to change.

```ts
import { createOracleApp } from '@ixo/oracle-runtime';

const app = await createOracleApp({
config,
manifestOverrides: {
// Drop a noisy bundled plugin out of the Tier-1 prompt.
'domain-indexer': { visibility: 'on-demand' },
// Hide a transport plugin entirely; its tools still bind.
portal: { visibility: 'silent' },
// Sharpen the trigger phrases the agent sees for memory.
memory: {
summary: 'Long-term user memory store.',
whenToUse: ['When the user refers to a past conversation or stored preference.'],
},
},
});
```

**Merge semantics**

Each override is **shallow-merged** onto the plugin's own manifest before validation and registration:

- Keys you set win; keys you omit keep the plugin default.
- `undefined` values are ignored — a sparse override never blanks out a field.
- Arrays and nested objects are replaced wholesale (not deep-merged). For example, supplying `whenToUse: [...]` **replaces** the plugin's full list rather than appending to it.

The merged manifest is what the runtime validates and what every downstream reader sees — the Tier-1 prompt block, the `list_capabilities` / `load_capability` meta-tools, and the agent's visibility index — so an override is the single source of truth from boot onwards.

**Validation**

Because the merged manifest is run through the same `validateManifest` rules as an authored one (see [Manifest schema](/build-an-oracle/reference/manifest-schema)), an override is held to the same constraints. For example, an override that empties `whenToUse` on a non-silent plugin fails boot with a clear `Plugin manifest validation failed` error.

**Unknown keys**

Override keys that don't match a loaded plugin (e.g. the plugin is excluded by `features` or simply misspelt) are **logged and ignored** — they do not fail boot:

```text
[boot] manifestOverrides references 'composio', which is not a loaded plugin — ignored (event: boot.plugin.manifest_override_unknown)
```

Watch for that event when a retune doesn't appear to take effect.

<Note>
The same option is available on [`createTestRuntime`](/build-an-oracle/develop/test-your-oracle) with identical shape and semantics, so a test can exercise a plugin under a retuned visibility (e.g. forcing `silent`) without a bespoke fixture.
</Note>
</Accordion>

<Accordion title="plugins" icon="puzzle-piece">
- **Type:** `OraclePlugin[]`
- **Required:** no
Expand Down Expand Up @@ -306,7 +364,7 @@
</Warning>

<Note>
**Where model IDs come from.** Each role (`main`, `subagent`, `vision`, `guard`, …) maps to a hardcoded model ID per provider. The provider is chosen by env: `LLM_PROVIDER` = `openrouter` (default) or `nebius`, with the matching key (`OPEN_ROUTER_API_KEY` / `NEBIUS_API_KEY`). There is **no env var to change the main model ID** — overriding it is exactly what `resolveModel` is for. Building the model with `getProviderChatModel` (rather than `new ChatOpenAI(...)`) keeps the OpenRouter fallback-models list and latency sort the `'main'` role ships with.

Check warning on line 367 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L367

Did you really mean 'hardcoded'?
</Note>

Prefer a different SDK model? Return your own LangChain model instead — but you lose the provider's fallback/latency wiring, so you own retries and outages:
Expand All @@ -330,13 +388,13 @@
| `validationSkipToolNames` | `string[]` | Tool names whose dangling `ToolMessage` outputs are stripped before the next model call — typically sub-agent tools whose output the caller summarises, not the model. |
| `operationalMode` | `string` | Replaces the default operational-mode prompt block. (The editor plugin's per-page mode still overrides this when a page is open.) |
| `editorSection` | `string` | Editor prompt block — normally populated by the editor plugin; set it only if you compose the section yourself. |
| `composioContext` | `string` | Composio guidance prompt block — normally populated by the composio plugin. |

Check warning on line 391 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L391

Did you really mean 'Composio'?

Check warning on line 391 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L391

Did you really mean 'composio'?
| `userSecretsContext` | `string` | Per-key secret bullet list rendered into the prompt (e.g. `- _USER_SECRET_FOO`). |
| `degradedServicesBlock` | `string` | A "some services are degraded" notice appended to the system prompt body. |

`ModelRole` is `'main' | 'subagent' | 'utility' | (string & {})`. `ChatOpenAIFields` is `Record<string, unknown>` — the optional fields forwarded to the underlying chat model.

**Swap the checkpointer** — supply your own LangGraph saver per user:

Check warning on line 397 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L397

Did you really mean 'checkpointer'?

```ts
import type { BaseCheckpointSaver } from '@langchain/langgraph';
Expand All @@ -348,7 +406,7 @@
}
```

**Turn on the conditional middlewares** — both are added to the stack only when their hook is present:

Check warning on line 409 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L409

Did you really mean 'middlewares'?

```ts
hooks: {
Expand Down Expand Up @@ -424,7 +482,7 @@
</Accordion>

<Accordion title="listen(port?)" icon="play">
Start the HTTP server. Honours `beforeListen` callbacks first. Port resolution: explicit `port` arg → `PORT` env (validated, defaults to **3000**). Throws if called twice.

Check warning on line 485 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L485

Did you really mean 'arg'?
</Accordion>
</AccordionGroup>

Expand All @@ -433,22 +491,22 @@
The function executes these steps in order:

1. Validates `config` (`name` required).
2. Resolves plugin set: bundled + your plugins, deduped by `name`, with `features` toggles and `autoDetect` predicates applied. Excluded plugins surface in `plugins.status().excluded` with their reason.

Check warning on line 494 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L494

Did you really mean 'deduped'?
3. Topologically sorts by `dependsOn`. Cycles or missing hard deps fail boot.

Check warning on line 495 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L495

Did you really mean 'deps'?
4. Validates every plugin's manifest. Hard violations fail boot.
4. Shallow-merges any `manifestOverrides` onto each loaded plugin's manifest, then validates the **merged** manifest. Hard violations fail boot. Override keys that don't match a loaded plugin are logged (`boot.plugin.manifest_override_unknown`) and ignored.
5. Composes env schema (base + all loaded plugins' `configSchema`) and validates `process.env`. Missing required vars fail boot with `[boot-error] Plugin '<name>' env validation failed for '<field>'`.
6. Cross-field check: the API key for the selected `LLM_PROVIDER` is present. (Schema-merge can't express this.)
7. Builds the internal `OracleIdentity` from `config` + validated env.
8. Populates the six registries (tools, sub-agents, middleware, manifests, configSchema, sharedState).

Check warning on line 500 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L500

Did you really mean 'configSchema'?

Check warning on line 500 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L500

Did you really mean 'sharedState'?
9. Collects `getNestModules` from every loaded plugin.
10. Builds the dynamic `RuntimeAppModule` and bootstraps NestJS (`NestFactory.create`). Installs a global `ValidationPipe({ whitelist: true, forbidNonWhitelisted: true, transform: true })`, enables CORS with the framework's allowed headers (`authorization`, `x-auth-type`, `x-ucan-delegation`, …), and mounts Swagger UI at `/docs`.

<Note>
The global `ValidationPipe` applies to **every** controller — including ones you add via `nestModules` or a plugin's `getNestModules`. Annotate request DTOs with `class-validator` decorators: unknown body fields are rejected (`forbidNonWhitelisted`), unannotated props are stripped (`whitelist`), and nested DTOs are instantiated (`transform`). A controller that silently 400s on an extra field is hitting this pipe.

Check warning on line 505 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L505

Did you really mean 'DTOs'?

Check warning on line 505 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L505

Did you really mean 'DTOs'?
</Note>
11. Builds `AmbientServices`.
12. Warms the boot caches (each registry runs its boot-time hooks once).
13. Wires the default checkpointer (`UserMatrixSqliteSyncService` + `SqliteSaver`), then merges host `hooks` over it.

Check warning on line 509 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L509

Did you really mean 'checkpointer'?
14. Populates the `OracleRuntimeBundleHolder` so `MessagesController` can read it on each request.
15. Schedules background Matrix init (unless `skipMatrixInit: true`). Registers a graceful-shutdown handler (unless `skipGracefulShutdown: true`).
16. Returns the `OracleApp`.
Expand All @@ -465,6 +523,6 @@

## Related guides

- [Create your oracle](/build-an-oracle/develop/create-oracle-app) — hands-on walkthrough.

Check warning on line 526 in build-an-oracle/reference/createoracleapp.mdx

View check run for this annotation

Mintlify / Mintlify Validation (ixoworld) - vale-spellcheck

build-an-oracle/reference/createoracleapp.mdx#L526

Did you really mean 'walkthrough'?
- [Using bundled plugins](/build-an-oracle/develop/enable-bundled-plugins) — the `features` field in detail.
- [Plugin API reference](/build-an-oracle/reference/plugin-api) — what plugin instances must implement.
5 changes: 5 additions & 0 deletions build-an-oracle/reference/manifest-schema.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,13 @@ const manifest: PluginManifest = {
};
```

## Retuning a bundled plugin's manifest

You don't have to fork a bundled plugin to change its manifest. `createOracleApp` accepts a [`manifestOverrides`](/build-an-oracle/reference/createoracleapp#manifestoverrides) option that shallow-merges fork-supplied fields onto each loaded plugin's manifest before validation and registration — so the override is what the Tier-1 prompt, the meta-tools, and the visibility index all see. This is the supported way to flip a bundled plugin's `visibility` (e.g. drop a noisy `always` plugin to `on-demand`) or relabel its `summary` / `tags` / `whenToUse` without touching the plugin source.

## Related references

- [Manifest concept](/build-an-oracle/understand/manifest)
- [Visibility tiers](/build-an-oracle/develop/plugin-recipes/set-visibility)
- [Plugin API](/build-an-oracle/reference/plugin-api)
- [`manifestOverrides` on `createOracleApp`](/build-an-oracle/reference/createoracleapp#manifestoverrides)
Loading