mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-24 11:25:50 -06:00
fix(plugins): retain cold-loaded tool registries
This commit is contained in:
@@ -3118,6 +3118,7 @@ This audited record covers the complete v2026.5.28..v2026.5.31-beta.4 history: 4
|
||||
- Agents/sandbox: honor explicit Docker sandbox env variables with credential-looking names during container creation, and recreate affected sandbox containers when the effective env policy changes. Fixes #82695. (#82763) Thanks @joshavant.
|
||||
- Plugins: accept deprecated `api.on("deactivate")` registrations as a dated compatibility alias for `gateway_stop`, so external plugin cleanup handlers run on Gateway shutdown while authors get migration guidance.
|
||||
- Plugins: resolve bundled entry, dist-runtime, package-state, and public artifact paths from packaged roots, so bundled plugin probes and hardlinked public surfaces no longer fall back to source files or fail during restart. Fixes #78462. Fixes #75797. Refs #76865. Thanks @ginishuh and @ymebosma.
|
||||
- Plugins/tools: retain cold-loaded tool registries used by cached descriptors after runtime registry replacement, so contracted plugin tools keep executing in sub-agent sessions instead of failing with `plugin tool runtime missing`. Fixes #80847.
|
||||
- Media: ignore image MIME and filename hints when bytes sniff as generic containers, so zip/octet-stream payloads mislabeled as images do not become local image media or keep image file extensions when staged.
|
||||
- Update/doctor: avoid materializing `groupAllowFrom` for channel schemas that reject it, so package-swap doctor repairs do not fail on externalized Slack configs.
|
||||
- Gateway/media: prevent image filenames from overriding generic non-image byte sniffing, so zip/octet-stream payloads mislabeled as images are offloaded or rejected before they become inline image attachments.
|
||||
|
||||
@@ -27,7 +27,7 @@ function resolveRuntimeSubagentMode(
|
||||
return "default";
|
||||
}
|
||||
|
||||
function installStandaloneRegistry(
|
||||
export function installStandaloneRuntimePluginRegistry(
|
||||
registry: PluginRegistry,
|
||||
params: {
|
||||
loadOptions: PluginLoadOptions;
|
||||
|
||||
@@ -2147,6 +2147,51 @@ describe("resolvePluginTools optional tools", () => {
|
||||
expect(factory).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("retains cold-loaded plugin tools for cached descriptor execution after active registry replacement", async () => {
|
||||
const factory = vi.fn(() => makeTool("cached_lifecycle_tool"));
|
||||
const gatewayRegistry = setRegistry([
|
||||
{
|
||||
pluginId: "cache-lifecycle-test",
|
||||
optional: false,
|
||||
source: "/tmp/cache-lifecycle-test.js",
|
||||
names: ["cached_lifecycle_tool"],
|
||||
factory,
|
||||
},
|
||||
]);
|
||||
const first = resolvePluginTools(
|
||||
createResolveToolsParams({
|
||||
toolAllowlist: ["cached_lifecycle_tool"],
|
||||
allowGatewaySubagentBinding: true,
|
||||
}),
|
||||
);
|
||||
const [tool] = resolvePluginTools(
|
||||
createResolveToolsParams({
|
||||
toolAllowlist: ["cached_lifecycle_tool"],
|
||||
allowGatewaySubagentBinding: true,
|
||||
}),
|
||||
);
|
||||
expectResolvedToolNames(first, ["cached_lifecycle_tool"]);
|
||||
expect(tool?.name).toBe("cached_lifecycle_tool");
|
||||
expect(factory).toHaveBeenCalledTimes(1);
|
||||
|
||||
const replacementRegistry = createToolRegistry([]);
|
||||
replacementRegistry.plugins.push({ id: "cache-lifecycle-test", status: "loaded" });
|
||||
setActivePluginRegistry?.(replacementRegistry as never, "provider-runtime", "default", "/tmp");
|
||||
resolveRuntimePluginRegistryMock.mockReturnValue(undefined);
|
||||
loadOpenClawPluginsMock.mockReset();
|
||||
loadOpenClawPluginsMock
|
||||
.mockReturnValueOnce(gatewayRegistry)
|
||||
.mockReturnValue(createToolRegistry([]));
|
||||
|
||||
await expect(tool?.execute("call-1", {}, undefined)).resolves.toEqual({
|
||||
content: [{ type: "text", text: "ok" }],
|
||||
});
|
||||
await expect(tool?.execute("call-2", {}, undefined)).resolves.toEqual({
|
||||
content: [{ type: "text", text: "ok" }],
|
||||
});
|
||||
expect(loadOpenClawPluginsMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("does not reuse cached plugin tool descriptors across sandbox context changes", () => {
|
||||
const factory = vi.fn((rawCtx: unknown) => {
|
||||
const ctx = rawCtx as { sandboxed?: boolean };
|
||||
|
||||
+15
-2
@@ -24,7 +24,10 @@ import {
|
||||
buildPluginRuntimeLoadOptions,
|
||||
resolvePluginRuntimeLoadContext,
|
||||
} from "./runtime/load-context.js";
|
||||
import { ensureStandaloneRuntimePluginRegistryLoaded } from "./runtime/standalone-runtime-registry-loader.js";
|
||||
import {
|
||||
ensureStandaloneRuntimePluginRegistryLoaded,
|
||||
installStandaloneRuntimePluginRegistry,
|
||||
} from "./runtime/standalone-runtime-registry-loader.js";
|
||||
import { findUndeclaredPluginToolNames } from "./tool-contracts.js";
|
||||
import {
|
||||
buildPluginToolDescriptorCacheKey,
|
||||
@@ -925,14 +928,24 @@ function resolvePluginToolRegistry(params: {
|
||||
}
|
||||
|
||||
const forceStandaloneLoad = Boolean(channelRegistry || activeRegistry);
|
||||
const shouldRetainColdLoadedToolRegistry =
|
||||
forceStandaloneLoad &&
|
||||
params.loadOptions.activate === false &&
|
||||
params.loadOptions.toolDiscovery === true;
|
||||
const standaloneRegistry = ensureStandaloneRuntimePluginRegistryLoaded({
|
||||
surface: "active",
|
||||
forceLoad: forceStandaloneLoad,
|
||||
installRegistry: !forceStandaloneLoad,
|
||||
installRegistry: shouldRetainColdLoadedToolRegistry ? false : !forceStandaloneLoad,
|
||||
requiredPluginIds: params.onlyPluginIds,
|
||||
loadOptions: params.loadOptions,
|
||||
});
|
||||
if (registryHasScopedPluginTools(standaloneRegistry, params.onlyPluginIds)) {
|
||||
if (shouldRetainColdLoadedToolRegistry) {
|
||||
installStandaloneRuntimePluginRegistry(standaloneRegistry, {
|
||||
loadOptions: params.loadOptions,
|
||||
surface: "active",
|
||||
});
|
||||
}
|
||||
return standaloneRegistry;
|
||||
}
|
||||
return standaloneRegistry ?? channelRegistry ?? activeRegistry;
|
||||
|
||||
Reference in New Issue
Block a user