Summary
normalizePluginsConfigWithResolver() preserves plugins.slots.memory but silently drops plugins.slots.contextEngine from the normalized plugin config. That causes slot-selected context-engine plugins to lose explicit-selection credit during loader diagnostics.
One visible symptom is a false warning from warnWhenAllowlistIsOpen() saying plugins.allow is empty even when the on-disk config has a populated allowlist and the context engine was selected via plugins.slots.contextEngine.
Repro
Given config like:
{
"plugins": {
"allow": ["cortex", "eva-xt", "lossless-claw"],
"slots": {
"memory": "cortex",
"contextEngine": "lossless-claw"
}
}
}
At runtime:
loadConfig() returns cfg.plugins.slots.contextEngine = "lossless-claw"
normalizePluginsConfig(cfg.plugins) drops that field and returns only slots.memory
- loader diagnostics then treat the discovered non-bundled context-engine plugin as not explicitly selected
warnWhenAllowlistIsOpen() can emit:
[plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load ...
Evidence / call trace
- Config load:
src/config/config.ts / runtime loadConfig() path
- Plugin normalization entrypoint:
src/plugins/config-state.ts:142
- Buggy normalizer:
src/plugins/config-normalization-shared.ts:133-145
- Misleading warning:
src/plugins/loader.ts:993-1021
Current normalizer only carries slots.memory:
const memorySlot = normalizeSlotValue(config?.slots?.memory);
return {
...
slots: {
memory: memorySlot === undefined ? defaultSlotIdForKey("memory") : memorySlot,
},
...
};
But the schema/docs/tests already support plugins.slots.contextEngine, so this field is schema-valid input that gets lost later.
Blast radius
Non-blocking for many current setups, but still real:
- false operator/security warnings about an "empty" allowlist
- slot-selected context-engine plugins are under-credited as explicitly selected
- any future logic that relies on normalized
slots.contextEngine will silently misbehave
Proposed fix
Preserve contextEngine during normalization and slightly tighten the warning text so it describes the effective loader allowlist instead of the raw config.
diff --git a/src/plugins/config-normalization-shared.ts b/src/plugins/config-normalization-shared.ts
@@
export type NormalizedPluginsConfig = {
@@
slots: {
memory?: string | null;
+ contextEngine?: string | null;
};
@@
): NormalizedPluginsConfig {
const memorySlot = normalizeSlotValue(config?.slots?.memory);
+ const contextEngineSlot = normalizeSlotValue(config?.slots?.contextEngine);
return {
@@
slots: {
memory: memorySlot === undefined ? defaultSlotIdForKey("memory") : memorySlot,
+ ...(contextEngineSlot !== undefined ? { contextEngine: contextEngineSlot } : {}),
},
and
diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts
@@
- `[plugins] plugins.allow is empty; discovered non-bundled plugins may auto-load: ...`
+ `[plugins] effective plugin allowlist is empty; discovered non-bundled plugins may auto-load: ...`
Verification
I verified the fix locally against source by adding a regression test in src/plugins/config-policy.test.ts that asserts contextEngine survives normalization, then running:
pnpm exec vitest run src/plugins/config-policy.test.ts src/plugins/slots.test.ts
Both files passed.
Summary
normalizePluginsConfigWithResolver()preservesplugins.slots.memorybut silently dropsplugins.slots.contextEnginefrom the normalized plugin config. That causes slot-selected context-engine plugins to lose explicit-selection credit during loader diagnostics.One visible symptom is a false warning from
warnWhenAllowlistIsOpen()sayingplugins.allow is emptyeven when the on-disk config has a populated allowlist and the context engine was selected viaplugins.slots.contextEngine.Repro
Given config like:
{ "plugins": { "allow": ["cortex", "eva-xt", "lossless-claw"], "slots": { "memory": "cortex", "contextEngine": "lossless-claw" } } }At runtime:
loadConfig()returnscfg.plugins.slots.contextEngine = "lossless-claw"normalizePluginsConfig(cfg.plugins)drops that field and returns onlyslots.memorywarnWhenAllowlistIsOpen()can emit:Evidence / call trace
src/config/config.ts/ runtimeloadConfig()pathsrc/plugins/config-state.ts:142src/plugins/config-normalization-shared.ts:133-145src/plugins/loader.ts:993-1021Current normalizer only carries
slots.memory:But the schema/docs/tests already support
plugins.slots.contextEngine, so this field is schema-valid input that gets lost later.Blast radius
Non-blocking for many current setups, but still real:
slots.contextEnginewill silently misbehaveProposed fix
Preserve
contextEngineduring normalization and slightly tighten the warning text so it describes the effective loader allowlist instead of the raw config.and
Verification
I verified the fix locally against source by adding a regression test in
src/plugins/config-policy.test.tsthat assertscontextEnginesurvives normalization, then running:pnpm exec vitest run src/plugins/config-policy.test.ts src/plugins/slots.test.tsBoth files passed.