mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 11:10:45 +00:00
fix(plugins): preserve gateway hook runner
Co-authored-by: lanzhi-lee <36190508+lanzhi-lee@users.noreply.github.com>
This commit is contained in:
@@ -20,6 +20,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/failover: stop body-less HTTP 400/422 proxy failures from defaulting to `"format"` classification, so embedded retries surface the opaque provider failure instead of falling into a compaction loop. Fixes #66462. (#67024) Thanks @altaywtf and @HongzhuLiu.
|
||||
- Plugins/loader: use cached discovery-mode snapshot loads for read-only plugin capability lookups, keep snapshot caches isolated from active Gateway registries, and make same-plugin channel/HTTP route re-registration idempotent so repeated snapshot or hot-reload paths no longer rerun full plugin side effects or accumulate duplicate surfaces. Fixes #51781, #52031, #54181, and #57514. Thanks @livingghost, @okuyam2y, @ShionEria, and @bbshih.
|
||||
- Plugins/loader: reuse the compatible active Gateway registry for broad runtime plugin ensure calls after a gateway-bindable boot load, so non-bundled plugins no longer re-run `register()` during the same boot path. Fixes #69250. Thanks @markthebest12.
|
||||
- Plugins/hooks: keep the gateway-bindable hook runner installed when later default-mode plugin loads activate a different registry, preserving Gateway subagent lifecycle hooks across runtime cache misses. Fixes #63166.
|
||||
|
||||
## 2026.4.24
|
||||
|
||||
|
||||
@@ -23,7 +23,11 @@ import {
|
||||
} from "../tasks/detached-task-runtime-state.js";
|
||||
import { withEnv } from "../test-utils/env.js";
|
||||
import { clearPluginCommands, getPluginCommandSpecs } from "./command-registry-state.js";
|
||||
import { getGlobalHookRunner, resetGlobalHookRunner } from "./hook-runner-global.js";
|
||||
import {
|
||||
getGlobalHookRunner,
|
||||
getGlobalPluginRegistry,
|
||||
resetGlobalHookRunner,
|
||||
} from "./hook-runner-global.js";
|
||||
import { createHookRunner } from "./hooks.js";
|
||||
import {
|
||||
clearPluginInteractiveHandlerRegistrations,
|
||||
@@ -3594,6 +3598,66 @@ module.exports = { id: "throws-after-import", register() {} };`,
|
||||
resetGlobalHookRunner();
|
||||
});
|
||||
|
||||
it("preserves the gateway-bindable hook runner across later default-mode activating loads", () => {
|
||||
useNoBundledPlugins();
|
||||
const gatewayPlugin = writePlugin({
|
||||
id: "gateway-hook-surface",
|
||||
filename: "gateway-hook-surface.cjs",
|
||||
body: `module.exports = { id: "gateway-hook-surface", register(api) {
|
||||
api.on("subagent_ended", () => undefined);
|
||||
} };`,
|
||||
});
|
||||
const defaultPlugin = writePlugin({
|
||||
id: "default-hook-surface",
|
||||
filename: "default-hook-surface.cjs",
|
||||
body: `module.exports = { id: "default-hook-surface", register(api) {
|
||||
api.on("message_sent", () => undefined);
|
||||
} };`,
|
||||
});
|
||||
|
||||
const gatewayRegistry = loadOpenClawPlugins({
|
||||
workspaceDir: gatewayPlugin.dir,
|
||||
config: {
|
||||
plugins: {
|
||||
load: { paths: [gatewayPlugin.file] },
|
||||
allow: ["gateway-hook-surface"],
|
||||
entries: {
|
||||
"gateway-hook-surface": {
|
||||
enabled: true,
|
||||
hooks: { allowConversationAccess: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
runtimeOptions: {
|
||||
allowGatewaySubagentBinding: true,
|
||||
},
|
||||
});
|
||||
expect(getGlobalPluginRegistry()).toBe(gatewayRegistry);
|
||||
expect(getGlobalHookRunner()?.hasHooks("subagent_ended")).toBe(true);
|
||||
|
||||
const defaultRegistry = loadOpenClawPlugins({
|
||||
workspaceDir: defaultPlugin.dir,
|
||||
config: {
|
||||
plugins: {
|
||||
load: { paths: [defaultPlugin.file] },
|
||||
allow: ["default-hook-surface"],
|
||||
entries: {
|
||||
"default-hook-surface": {
|
||||
enabled: true,
|
||||
hooks: { allowConversationAccess: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(getActivePluginRegistry()).toBe(defaultRegistry);
|
||||
expect(getGlobalPluginRegistry()).toBe(gatewayRegistry);
|
||||
expect(getGlobalHookRunner()?.hasHooks("subagent_ended")).toBe(true);
|
||||
expect(getGlobalHookRunner()?.hasHooks("message_sent")).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "does not reuse cached bundled plugin registries across env changes",
|
||||
|
||||
@@ -59,7 +59,7 @@ import {
|
||||
type PluginActivationState,
|
||||
} from "./config-state.js";
|
||||
import { discoverOpenClawPlugins } from "./discovery.js";
|
||||
import { initializeGlobalHookRunner } from "./hook-runner-global.js";
|
||||
import { getGlobalHookRunner, initializeGlobalHookRunner } from "./hook-runner-global.js";
|
||||
import {
|
||||
clearPluginInteractiveHandlers,
|
||||
listPluginInteractiveHandlers,
|
||||
@@ -1953,8 +1953,14 @@ function activatePluginRegistry(
|
||||
runtimeSubagentMode: "default" | "explicit" | "gateway-bindable",
|
||||
workspaceDir?: string,
|
||||
): void {
|
||||
const preserveGatewayHookRunner =
|
||||
runtimeSubagentMode === "default" &&
|
||||
getActivePluginRuntimeSubagentMode() === "gateway-bindable" &&
|
||||
getGlobalHookRunner() !== null;
|
||||
setActivePluginRegistry(registry, cacheKey, runtimeSubagentMode, workspaceDir);
|
||||
initializeGlobalHookRunner(registry);
|
||||
if (!preserveGatewayHookRunner) {
|
||||
initializeGlobalHookRunner(registry);
|
||||
}
|
||||
}
|
||||
|
||||
export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegistry {
|
||||
|
||||
Reference in New Issue
Block a user