mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-04 08:30:25 +00:00
fix: support multi-kind plugins for dual slot ownership (#57507) (thanks @fuller-stack-dev)
* feat(plugins): support multi-kind plugins for dual slot ownership * fix: address review feedback on multi-kind plugin support - Use sorted normalizeKinds() for kind-mismatch comparison in loader.ts (fixes order-sensitive JSON.stringify for arrays) - Derive slot-to-kind reverse mapping from SLOT_BY_KIND in slots.ts (removes hardcoded ternary that would break for future slot types) - Use shared hasKind() helper in config-state.ts instead of inline logic * fix: don't disable dual-kind plugin that still owns another slot When a new plugin takes over one slot, a dual-kind plugin that still owns the other slot must not be disabled — otherwise context engine resolution fails at runtime. * fix: exempt dual-kind plugins from memory slot disablement A plugin with kind: ["memory", "context-engine"] must stay enabled even when it loses the memory slot, so its context engine role can still load. * fix: address remaining review feedback - Pass manifest kind (not hardcoded "memory") in early memory gating - Extract kindsEqual() helper for DRY kind comparison in loader.ts - Narrow slotKeyForPluginKind back to single PluginKind with JSDoc - Reject empty array in parsePluginKind - Add kindsEqual tests * fix: use toSorted() instead of sort() per lint rules * plugins: include default slot ownership in disable checks and gate dual-kind memory registration
This commit is contained in:
@@ -24,7 +24,7 @@ export type PluginManifest = {
|
||||
legacyPluginIds?: string[];
|
||||
/** Provider ids that should auto-enable this plugin when referenced in auth/config/models. */
|
||||
autoEnableWhenConfiguredProviders?: string[];
|
||||
kind?: PluginKind;
|
||||
kind?: PluginKind | PluginKind[];
|
||||
channels?: string[];
|
||||
providers?: string[];
|
||||
/** Cheap startup activation lookup for plugin-owned CLI inference backends. */
|
||||
@@ -233,6 +233,16 @@ export function resolvePluginManifestPath(rootDir: string): string {
|
||||
return path.join(rootDir, PLUGIN_MANIFEST_FILENAME);
|
||||
}
|
||||
|
||||
function parsePluginKind(raw: unknown): PluginKind | PluginKind[] | undefined {
|
||||
if (typeof raw === "string") {
|
||||
return raw as PluginKind;
|
||||
}
|
||||
if (Array.isArray(raw) && raw.length > 0 && raw.every((k) => typeof k === "string")) {
|
||||
return raw.length === 1 ? (raw[0] as PluginKind) : (raw as PluginKind[]);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function loadPluginManifest(
|
||||
rootDir: string,
|
||||
rejectHardlinks = true,
|
||||
@@ -282,7 +292,7 @@ export function loadPluginManifest(
|
||||
return { ok: false, error: "plugin manifest requires configSchema", manifestPath };
|
||||
}
|
||||
|
||||
const kind = typeof raw.kind === "string" ? (raw.kind as PluginKind) : undefined;
|
||||
const kind = parsePluginKind(raw.kind);
|
||||
const enabledByDefault = raw.enabledByDefault === true;
|
||||
const legacyPluginIds = normalizeStringList(raw.legacyPluginIds);
|
||||
const autoEnableWhenConfiguredProviders = normalizeStringList(
|
||||
|
||||
Reference in New Issue
Block a user