* 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
loadChannelOutboundAdapter (via createChannelRegistryLoader) was reading
from getActivePluginRegistry() — the unpinned active registry that gets
replaced whenever loadOpenClawPlugins() runs (config schema reads, plugin
status queries, tool listings, etc.).
After replacement, the active registry may omit channel entries or carry
them in setup mode without outbound adapters, causing:
Outbound not configured for channel: telegram
The channel inbound path already uses the pinned registry
(getActivePluginChannelRegistry) which is frozen at gateway startup and
survives all subsequent registry replacements. This commit aligns the
outbound path to use the same pinned surface.
Adds a regression test that pins a registry with a telegram outbound
adapter, replaces the active registry with an empty one, then asserts
loadChannelOutboundAdapter still resolves the adapter.
Fixes#54745Fixes#54013
* fix(plugins): reuse active registry for sub-agent tool resolution
* test(plugins): harden resolveRuntimePluginRegistry with per-field, caller-shape, and cold-start tests
Add 11 regression tests covering:
- R1: Per-field isolation (coreGatewayHandlers, includeSetupOnlyChannelPlugins,
preferSetupRuntimeForChannelPlugins each independently prevent fallback;
empty onlyPluginIds[] treated as non-gateway-scoped)
- R2: Caller-shape regression (tools.ts, memory-runtime.ts,
channel-resolution.ts shapes fall back; web-search-providers.runtime.ts
with onlyPluginIds does not)
- R3: Cold-start path (null active registry falls through to loadOpenClawPlugins)
Add debug logging to resolveRuntimePluginRegistry recording which exit path
was taken (no-options, cache-key-match, non-gateway-scoped fallback, fresh load).
* refactor: simplify plugin registry resolution tests and trim happy-path debug logs
* fix(plugins): address review comments on registry fallback
- Fix cold-start test assertion: loadOpenClawPlugins always activates
the registry (shouldActivate defaults to true), so getActivePluginRegistry()
is not null after the call. Updated assertion to match actual behavior.
- Add safety comment documenting why the non-gateway-scoped fallback is
safe despite cache-key mismatch: single-gateway-per-process model means
sub-agents share workspaceDir, config, and env with the gateway.
* test(plugins): restructure per-field isolation tests to avoid load timeouts
Test isGatewayScopedLoad directly instead of going through the full
resolveRuntimePluginRegistry path which triggers expensive plugin
discovery. This fixes the includeSetupOnlyChannelPlugins test timing
out in CI while providing more precise coverage of the predicate.
* fix(plugins): expand safety comment to address startup-scoped registry concern
* fix(plugins): scope subagent registry reuse to tool loading
---------
Co-authored-by: Ayaan Zaidi <hi@obviy.us>