From 599beba44a159c92bddb54aead9efe20a03dc8d4 Mon Sep 17 00:00:00 2001 From: Eva Date: Fri, 1 May 2026 23:01:52 +0700 Subject: [PATCH] fix: unretire restored plugin registries --- .../matrix/src/matrix/monitor/handler.ts | 26 ++++++++++++--- src/commands/onboard-non-interactive.ts | 8 ++--- .../run-context-lifecycle.contract.test.ts | 33 +++++++++++++++++++ src/plugins/host-hook-cleanup.config.test.ts | 2 ++ src/plugins/registry-lifecycle.ts | 6 ++++ src/plugins/runtime.ts | 3 +- 6 files changed, 68 insertions(+), 10 deletions(-) diff --git a/extensions/matrix/src/matrix/monitor/handler.ts b/extensions/matrix/src/matrix/monitor/handler.ts index 472fa2d6097..da1ee646f23 100644 --- a/extensions/matrix/src/matrix/monitor/handler.ts +++ b/extensions/matrix/src/matrix/monitor/handler.ts @@ -1940,11 +1940,27 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam onIdle: typingCallbacks.onIdle, }); const pinnedMainDmOwner = isDirectMessage - ? resolvePinnedMainDmOwnerFromAllowlist({ - dmScope: cfg.session?.dmScope, - allowFrom: effectiveAllowFrom, - normalizeEntry: normalizeMatrixUserId, - }) + ? await (async () => { + const livePinnedCfg = core.config.current() as CoreConfig; + const livePinnedAllowlists = resolveMatrixAccountAllowlistConfig({ + cfg: livePinnedCfg, + accountId, + }); + const livePinnedDmAllowFrom = await resolveCachedLiveAllowlist({ + cfg: livePinnedCfg, + entries: livePinnedAllowlists.dmAllowFrom, + startupResolvedEntries: allowFromResolvedEntries, + cache: liveDmAllowlistCache, + updateCache: (next) => { + liveDmAllowlistCache = next; + }, + }); + return resolvePinnedMainDmOwnerFromAllowlist({ + dmScope: cfg.session?.dmScope, + allowFrom: livePinnedDmAllowFrom, + normalizeEntry: normalizeMatrixUserId, + }); + })() : null; const turnResult = await core.channel.turn.run({ diff --git a/src/commands/onboard-non-interactive.ts b/src/commands/onboard-non-interactive.ts index e77a23f2b3f..ecf2d550917 100644 --- a/src/commands/onboard-non-interactive.ts +++ b/src/commands/onboard-non-interactive.ts @@ -27,16 +27,16 @@ function createNonInteractiveMigrationPrompter(runtime: RuntimeEnv): WizardPromp async note(message, title) { runtime.log(title ? `${title}\n${message}` : message); }, - select(params) { + async select(params) { return unavailable(params.message); }, - multiselect(params) { + async multiselect(params) { return unavailable(params.message); }, - text(params) { + async text(params) { return unavailable(params.message); }, - confirm(params) { + async confirm(params) { return unavailable(params.message); }, progress(label) { diff --git a/src/plugins/contracts/run-context-lifecycle.contract.test.ts b/src/plugins/contracts/run-context-lifecycle.contract.test.ts index 0cacb520963..ceb8b56ffd1 100644 --- a/src/plugins/contracts/run-context-lifecycle.contract.test.ts +++ b/src/plugins/contracts/run-context-lifecycle.contract.test.ts @@ -85,6 +85,39 @@ describe("plugin run context lifecycle", () => { ).toEqual({ live: true }); }); + it("allows run-context mutations after a previous registry is restored active", () => { + const { config, registry } = createPluginRegistryFixture(); + let capturedApi: OpenClawPluginApi | undefined; + registerTestPlugin({ + registry, + config, + record: createPluginRecord({ + id: "restored-run-context-plugin", + name: "Restored Run Context Plugin", + }), + register(api) { + capturedApi = api; + }, + }); + setActivePluginRegistry(registry.registry); + setActivePluginRegistry(createEmptyPluginRegistry()); + setActivePluginRegistry(registry.registry); + + expect( + capturedApi?.setRunContext({ + runId: "restored-run", + namespace: "state", + value: { restored: true }, + }), + ).toBe(true); + expect( + getPluginRunContext({ + pluginId: "restored-run-context-plugin", + get: { runId: "restored-run", namespace: "state" }, + }), + ).toEqual({ restored: true }); + }); + it("does not let delayed non-terminal subscriptions resurrect closed run context", async () => { let releaseToolHandler: (() => void) | undefined; let delayedToolHandlerSawContext: unknown; diff --git a/src/plugins/host-hook-cleanup.config.test.ts b/src/plugins/host-hook-cleanup.config.test.ts index 024bd277f5d..9194a3bcc58 100644 --- a/src/plugins/host-hook-cleanup.config.test.ts +++ b/src/plugins/host-hook-cleanup.config.test.ts @@ -18,9 +18,11 @@ describe("plugin host cleanup config fallback", () => { it("records session store config failures while continuing runtime cleanup", async () => { const registry = createEmptyPluginRegistry(); const cleanup = vi.fn(); + registry.runtimeLifecycles ??= []; registry.runtimeLifecycles.push({ pluginId: "cleanup-plugin", pluginName: "Cleanup Plugin", + source: "test", lifecycle: { id: "runtime-cleanup", cleanup, diff --git a/src/plugins/registry-lifecycle.ts b/src/plugins/registry-lifecycle.ts index d5b9fef5a3e..18dbdcbf48d 100644 --- a/src/plugins/registry-lifecycle.ts +++ b/src/plugins/registry-lifecycle.ts @@ -8,6 +8,12 @@ export function markPluginRegistryRetired(registry: PluginRegistry | null | unde } } +export function markPluginRegistryActive(registry: PluginRegistry | null | undefined): void { + if (registry) { + retiredRegistries.delete(registry); + } +} + export function isPluginRegistryRetired(registry: PluginRegistry): boolean { return retiredRegistries.has(registry); } diff --git a/src/plugins/runtime.ts b/src/plugins/runtime.ts index 2d86af508b1..c354ccc448c 100644 --- a/src/plugins/runtime.ts +++ b/src/plugins/runtime.ts @@ -5,7 +5,7 @@ import { dispatchPluginAgentEventSubscriptions, } from "./host-hook-runtime.js"; import { createEmptyPluginRegistry } from "./registry-empty.js"; -import { markPluginRegistryRetired } from "./registry-lifecycle.js"; +import { markPluginRegistryActive, markPluginRegistryRetired } from "./registry-lifecycle.js"; import type { PluginRegistry } from "./registry-types.js"; import { PLUGIN_REGISTRY_STATE, @@ -133,6 +133,7 @@ export function setActivePluginRegistry( if (previousRegistry && previousRegistry !== registry) { markPluginRegistryRetired(previousRegistry); } + markPluginRegistryActive(registry); state.activeRegistry = registry; state.activeVersion += 1; syncTrackedSurface(state.httpRoute, registry, true);