mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-12 09:41:11 +00:00
fix(plugins): preserve contextEngine slot through config normalization (#64192)
Merged via squash.
Prepared head SHA: ae8bd9f09d
Co-authored-by: hclsys <7755017+hclsys@users.noreply.github.com>
Co-authored-by: jalehman <550978+jalehman@users.noreply.github.com>
Reviewed-by: @jalehman
This commit is contained in:
@@ -147,6 +147,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Sandbox/security: auto-derive CDP source-range from Docker network gateway and refuse to start the socat relay without one, so peer containers cannot reach CDP unauthenticated. (#61404) Thanks @dims.
|
||||
- Daemon/launchd: keep `openclaw gateway stop` persistent without uninstalling the macOS LaunchAgent, re-enable it on explicit restart or repair, and harden launchd label handling. (#64447) Thanks @ngutman.
|
||||
- Agents/Slack: preserve threaded announce delivery when `sessions.list` rows lack stored thread metadata by falling back to the thread id encoded in the session key. (#63143) Thanks @mariosousa-finn.
|
||||
- Plugins/context engines: preserve `plugins.slots.contextEngine` through normalization and keep explicitly selected workspace context-engine plugins enabled, so loader diagnostics and plugin activation stop dropping that slot selection. (#64192) Thanks @hclsys.
|
||||
|
||||
## 2026.4.9
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ export type NormalizedPluginsConfig = {
|
||||
loadPaths: string[];
|
||||
slots: {
|
||||
memory?: string | null;
|
||||
contextEngine?: string | null;
|
||||
};
|
||||
entries: Record<
|
||||
string,
|
||||
@@ -142,6 +143,7 @@ export function normalizePluginsConfigWithResolver(
|
||||
loadPaths: normalizeList(config?.load?.paths, identityNormalizePluginId),
|
||||
slots: {
|
||||
memory: memorySlot === undefined ? defaultSlotIdForKey("memory") : memorySlot,
|
||||
contextEngine: normalizeSlotValue(config?.slots?.contextEngine),
|
||||
},
|
||||
entries: normalizePluginEntries(config?.entries, normalizePluginId),
|
||||
};
|
||||
|
||||
@@ -51,6 +51,9 @@ function resolveExplicitPluginSelection(params: {
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return { explicitlyEnabled: true, reason: "selected memory slot" };
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return { explicitlyEnabled: true, reason: "selected context engine slot" };
|
||||
}
|
||||
if (params.origin !== "bundled" && params.config.allow.includes(params.id)) {
|
||||
return { explicitlyEnabled: true, reason: "selected in allowlist" };
|
||||
}
|
||||
@@ -103,7 +106,12 @@ export function resolvePluginActivationState(params: {
|
||||
};
|
||||
}
|
||||
const explicitlyAllowed = params.config.allow.includes(params.id);
|
||||
if (params.origin === "workspace" && !explicitlyAllowed && entry?.enabled !== true) {
|
||||
if (
|
||||
params.origin === "workspace" &&
|
||||
!explicitlyAllowed &&
|
||||
entry?.enabled !== true &&
|
||||
explicitSelection.reason !== "selected context engine slot"
|
||||
) {
|
||||
return {
|
||||
enabled: false,
|
||||
activated: false,
|
||||
@@ -121,6 +129,15 @@ export function resolvePluginActivationState(params: {
|
||||
reason: "selected memory slot",
|
||||
};
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return {
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
reason: "selected context engine slot",
|
||||
};
|
||||
}
|
||||
if (params.config.allow.length > 0 && !explicitlyAllowed) {
|
||||
return {
|
||||
enabled: false,
|
||||
|
||||
@@ -54,6 +54,16 @@ describe("normalizePluginsConfig", () => {
|
||||
expect(normalizePluginsConfig(config).slots.memory).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
[{}, undefined],
|
||||
[{ slots: { contextEngine: "lossless-claw" } }, "lossless-claw"],
|
||||
[{ slots: { contextEngine: "none" } }, null],
|
||||
[{ slots: { contextEngine: " cortex " } }, "cortex"],
|
||||
[{ slots: { contextEngine: "" } }, undefined],
|
||||
] as const)("preserves contextEngine slot for %o (#64170)", (config, expected) => {
|
||||
expect(normalizePluginsConfig(config).slots.contextEngine).toBe(expected);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "normalizes plugin hook policy flags",
|
||||
@@ -432,6 +442,32 @@ describe("resolveEffectivePluginActivationState", () => {
|
||||
reason: "enabled by effective config",
|
||||
});
|
||||
});
|
||||
|
||||
it("treats an explicitly selected workspace context engine as explicit activation", () => {
|
||||
const rawConfig = {
|
||||
plugins: {
|
||||
slots: {
|
||||
contextEngine: "lossless-claw",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(
|
||||
resolveEffectivePluginActivationState({
|
||||
id: "lossless-claw",
|
||||
origin: "workspace",
|
||||
config: normalizePluginsConfig(rawConfig.plugins),
|
||||
rootConfig: rawConfig,
|
||||
activationSource: createPluginActivationSource({ config: rawConfig }),
|
||||
}),
|
||||
).toEqual({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
reason: "selected context engine slot",
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveEnableState", () => {
|
||||
@@ -525,6 +561,20 @@ describe("resolveEnableState", () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("keeps an explicitly selected workspace context engine enabled when omitted from plugins.allow", () => {
|
||||
expectNormalizedEnableState({
|
||||
id: "lossless-claw",
|
||||
origin: "workspace",
|
||||
config: {
|
||||
allow: ["telegram"],
|
||||
slots: { contextEngine: "lossless-claw" },
|
||||
},
|
||||
expected: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveMemorySlotDecision", () => {
|
||||
|
||||
@@ -23,6 +23,7 @@ export type PluginExplicitSelectionCause =
|
||||
| "enabled-in-config"
|
||||
| "bundled-channel-enabled-in-config"
|
||||
| "selected-memory-slot"
|
||||
| "selected-context-engine-slot"
|
||||
| "selected-in-allowlist";
|
||||
|
||||
export type PluginActivationCause =
|
||||
@@ -104,6 +105,7 @@ const PLUGIN_ACTIVATION_REASON_BY_CAUSE: Record<PluginActivationCause, string> =
|
||||
"enabled-in-config": "enabled in config",
|
||||
"bundled-channel-enabled-in-config": "channel enabled in config",
|
||||
"selected-memory-slot": "selected memory slot",
|
||||
"selected-context-engine-slot": "selected context engine slot",
|
||||
"selected-in-allowlist": "selected in allowlist",
|
||||
"plugins-disabled": "plugins disabled",
|
||||
"blocked-by-denylist": "blocked by denylist",
|
||||
@@ -231,6 +233,9 @@ function resolveExplicitPluginSelection(params: {
|
||||
if (params.config.slots.memory === params.id) {
|
||||
return { explicitlyEnabled: true, cause: "selected-memory-slot" };
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return { explicitlyEnabled: true, cause: "selected-context-engine-slot" };
|
||||
}
|
||||
if (params.origin !== "bundled" && params.config.allow.includes(params.id)) {
|
||||
return { explicitlyEnabled: true, cause: "selected-in-allowlist" };
|
||||
}
|
||||
@@ -288,7 +293,12 @@ export function resolvePluginActivationState(params: {
|
||||
});
|
||||
}
|
||||
const explicitlyAllowed = params.config.allow.includes(params.id);
|
||||
if (params.origin === "workspace" && !explicitlyAllowed && entry?.enabled !== true) {
|
||||
if (
|
||||
params.origin === "workspace" &&
|
||||
!explicitlyAllowed &&
|
||||
entry?.enabled !== true &&
|
||||
explicitSelection.cause !== "selected-context-engine-slot"
|
||||
) {
|
||||
return toPluginActivationState({
|
||||
enabled: false,
|
||||
activated: false,
|
||||
@@ -306,6 +316,15 @@ export function resolvePluginActivationState(params: {
|
||||
cause: "selected-memory-slot",
|
||||
});
|
||||
}
|
||||
if (params.config.slots.contextEngine === params.id) {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
activated: true,
|
||||
explicitlyEnabled: true,
|
||||
source: "explicit",
|
||||
cause: "selected-context-engine-slot",
|
||||
});
|
||||
}
|
||||
if (explicitSelection.cause === "bundled-channel-enabled-in-config") {
|
||||
return toPluginActivationState({
|
||||
enabled: true,
|
||||
|
||||
Reference in New Issue
Block a user