mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:30:42 +00:00
fix(plugins): preserve interactive dedupe on cache restore
This commit is contained in:
@@ -63,7 +63,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Discord/gateway: prevent startup from getting stuck at `awaiting gateway readiness` when Carbon gateway registration races with a lifecycle reconnect. Fixes #52372. (#68159) Thanks @IVY-AI-gif.
|
||||
- Plugins/cache: restore plugin command and interactive handler registries on loader cache hits, so cached external plugins keep slash commands and callback handlers available after reloads. Fixes #71100. Thanks @BomBastikDE.
|
||||
- Plugins/cache: restore plugin command and interactive handler registries on loader cache hits without resetting interactive callback dedupe, so cached external plugins keep slash commands and callback handlers available after reloads. Fixes #71100. Thanks @BomBastikDE.
|
||||
- Gateway/OpenAI-compatible: report non-zero token usage for `/v1/chat/completions` when the agent run has only last-call usage metadata available. Fixes #71118. (#71242) Thanks @RenzoMXD.
|
||||
- Plugin SDK/tool-result transforms: restrict harness tool-result middleware to bundled plugins, fail closed on middleware errors, validate rewritten result shapes, preserve Pi per-call ids, and keep Codex media trust checks anchored to raw tool provenance. Thanks @vincentkoc.
|
||||
- Gateway/MCP loopback: apply owner-only tool policy and run before-tool-call hooks on `127.0.0.1/mcp` `tools/list` and `tools/call`, so non-owner bearer callers can no longer see or invoke owner-only tools such as `cron`, `gateway`, and `nodes`, matching the existing HTTP `/tools/invoke` and embedded-agent paths. (#71159) Thanks @mmaps.
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
validatePluginInteractiveNamespace,
|
||||
} from "./interactive-shared.js";
|
||||
import {
|
||||
clearPluginInteractiveHandlerRegistrationsState,
|
||||
clearPluginInteractiveHandlersState,
|
||||
getPluginInteractiveHandlersState,
|
||||
type RegisteredInteractiveHandler,
|
||||
@@ -62,6 +63,10 @@ export function clearPluginInteractiveHandlers(): void {
|
||||
clearPluginInteractiveHandlersState();
|
||||
}
|
||||
|
||||
export function clearPluginInteractiveHandlerRegistrations(): void {
|
||||
clearPluginInteractiveHandlerRegistrationsState();
|
||||
}
|
||||
|
||||
export function clearPluginInteractiveHandlersForPlugin(pluginId: string): void {
|
||||
const interactiveHandlers = getPluginInteractiveHandlersState();
|
||||
for (const [key, value] of interactiveHandlers.entries()) {
|
||||
@@ -78,7 +83,7 @@ export function listPluginInteractiveHandlers(): RegisteredInteractiveHandler[]
|
||||
export function restorePluginInteractiveHandlers(
|
||||
registrations: readonly RegisteredInteractiveHandler[],
|
||||
): void {
|
||||
clearPluginInteractiveHandlers();
|
||||
clearPluginInteractiveHandlerRegistrations();
|
||||
const interactiveHandlers = getPluginInteractiveHandlersState();
|
||||
for (const registration of registrations) {
|
||||
const namespace = normalizePluginInteractiveNamespace(registration.namespace);
|
||||
|
||||
@@ -110,7 +110,11 @@ export function releasePluginInteractiveCallbackDedupe(dedupeKey: string | undef
|
||||
}
|
||||
|
||||
export function clearPluginInteractiveHandlersState(): void {
|
||||
getPluginInteractiveHandlersState().clear();
|
||||
clearPluginInteractiveHandlerRegistrationsState();
|
||||
getPluginInteractiveCallbackDedupeState().clear();
|
||||
getState().inflightCallbackDedupe.clear();
|
||||
}
|
||||
|
||||
export function clearPluginInteractiveHandlerRegistrationsState(): void {
|
||||
getPluginInteractiveHandlersState().clear();
|
||||
}
|
||||
|
||||
@@ -26,9 +26,14 @@ import { clearPluginCommands, getPluginCommandSpecs } from "./command-registry-s
|
||||
import { getGlobalHookRunner, resetGlobalHookRunner } from "./hook-runner-global.js";
|
||||
import { createHookRunner } from "./hooks.js";
|
||||
import {
|
||||
clearPluginInteractiveHandlerRegistrations,
|
||||
clearPluginInteractiveHandlers,
|
||||
resolvePluginInteractiveNamespaceMatch,
|
||||
} from "./interactive-registry.js";
|
||||
import {
|
||||
claimPluginInteractiveCallbackDedupe,
|
||||
commitPluginInteractiveCallbackDedupe,
|
||||
} from "./interactive-state.js";
|
||||
import {
|
||||
__testing,
|
||||
clearPluginLoaderCache,
|
||||
@@ -3217,8 +3222,16 @@ module.exports = { id: "throws-after-import", register() {} };`,
|
||||
]);
|
||||
expect(resolvePluginInteractiveNamespaceMatch("telegram", "hue:on")).toBeDefined();
|
||||
|
||||
const dedupeKey = "telegram:hue:callback-1";
|
||||
expect(claimPluginInteractiveCallbackDedupe(dedupeKey, 1_000)).toBe(true);
|
||||
commitPluginInteractiveCallbackDedupe(dedupeKey, 1_000);
|
||||
expect(claimPluginInteractiveCallbackDedupe(dedupeKey, 1_001)).toBe(false);
|
||||
|
||||
loadOpenClawPlugins(loadOptions);
|
||||
expect(claimPluginInteractiveCallbackDedupe(dedupeKey, 1_002)).toBe(false);
|
||||
|
||||
clearPluginCommands();
|
||||
clearPluginInteractiveHandlers();
|
||||
clearPluginInteractiveHandlerRegistrations();
|
||||
expect(getPluginCommandSpecs()).toEqual([]);
|
||||
expect(resolvePluginInteractiveNamespaceMatch("telegram", "hue:on")).toBeNull();
|
||||
|
||||
@@ -3234,6 +3247,7 @@ module.exports = { id: "throws-after-import", register() {} };`,
|
||||
namespace: "hue",
|
||||
channel: "telegram",
|
||||
});
|
||||
expect(claimPluginInteractiveCallbackDedupe(dedupeKey, 1_003)).toBe(false);
|
||||
});
|
||||
|
||||
it("clears stale detached task runtime registrations on active reloads when no plugin re-registers one", () => {
|
||||
|
||||
@@ -213,8 +213,8 @@ export class PluginLoadReentryError extends Error {
|
||||
type CachedPluginState = {
|
||||
registry: PluginRegistry;
|
||||
detachedTaskRuntimeRegistration: ReturnType<typeof getDetachedTaskLifecycleRuntimeRegistration>;
|
||||
commands: ReturnType<typeof listRegisteredPluginCommands>;
|
||||
interactiveHandlers: ReturnType<typeof listPluginInteractiveHandlers>;
|
||||
commands?: ReturnType<typeof listRegisteredPluginCommands>;
|
||||
interactiveHandlers?: ReturnType<typeof listPluginInteractiveHandlers>;
|
||||
memoryCapability: ReturnType<typeof getMemoryCapabilityRegistration>;
|
||||
memoryCorpusSupplements: ReturnType<typeof listMemoryCorpusSupplements>;
|
||||
agentHarnesses: ReturnType<typeof listRegisteredAgentHarnesses>;
|
||||
@@ -1883,10 +1883,10 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
const cached = getCachedPluginRegistry(cacheKey);
|
||||
if (cached) {
|
||||
restoreRegisteredAgentHarnesses(cached.agentHarnesses);
|
||||
restorePluginCommands(cached.commands);
|
||||
restorePluginCommands(cached.commands ?? []);
|
||||
restoreRegisteredCompactionProviders(cached.compactionProviders);
|
||||
restoreDetachedTaskLifecycleRuntimeRegistration(cached.detachedTaskRuntimeRegistration);
|
||||
restorePluginInteractiveHandlers(cached.interactiveHandlers);
|
||||
restorePluginInteractiveHandlers(cached.interactiveHandlers ?? []);
|
||||
restoreRegisteredMemoryEmbeddingProviders(cached.memoryEmbeddingProviders);
|
||||
restoreMemoryPluginState({
|
||||
capability: cached.memoryCapability,
|
||||
|
||||
Reference in New Issue
Block a user