Files
openclaw/src/plugins/plugin-registration-transaction.ts
Peter Steinberger be5e427f56 feat(dashboard): plugin-declared widget data bindings and action verbs (#112083)
* feat(dashboard): add plugin capability declarations

* docs(dashboard): describe plugin capabilities

* fix(plugins): preserve registry map cloning

* fix(dashboard): make plugin grant ids unambiguous

* fix(dashboard): align generated plugin grant ids

* chore(boards): internalize verb ids and refresh protocol snapshots
2026-07-20 22:43:04 -07:00

150 lines
5.6 KiB
TypeScript

// Owns atomic plugin registration state across registry and process-global capabilities.
import {
listRegisteredAgentHarnesses,
restoreRegisteredAgentHarnesses,
} from "../agents/harness/registry.js";
import {
getDetachedTaskLifecycleRuntimeRegistration,
restoreDetachedTaskLifecycleRuntimeRegistration,
} from "../tasks/detached-task-runtime-state.js";
import { listRegisteredPluginCommands, restorePluginCommands } from "./command-registry-state.js";
import {
listRegisteredCompactionProviders,
restoreRegisteredCompactionProviders,
} from "./compaction-provider.js";
import {
listRegisteredEmbeddingProviders,
restoreRegisteredEmbeddingProviders,
} from "./embedding-providers.js";
import {
listPluginInteractiveHandlers,
restorePluginInteractiveHandlers,
} from "./interactive-registry.js";
import {
listRegisteredMemoryEmbeddingProviders,
restoreRegisteredMemoryEmbeddingProviders,
} from "./memory-embedding-providers.js";
import {
getMemoryCapabilityRegistration,
listMemoryCorpusSupplements,
listMemoryPromptPreparations,
listMemoryPromptSupplements,
restoreMemoryPluginState,
} from "./memory-state.js";
import type { PluginRegistry } from "./registry-types.js";
import {
getSessionDiscussionProvider,
restoreSessionDiscussionProvider,
} from "./session-discussion-registry.js";
export type PluginProcessGlobalState = {
agentHarnesses: ReturnType<typeof listRegisteredAgentHarnesses>;
commands: ReturnType<typeof listRegisteredPluginCommands>;
compactionProviders: ReturnType<typeof listRegisteredCompactionProviders>;
detachedTaskRuntimeRegistration: ReturnType<typeof getDetachedTaskLifecycleRuntimeRegistration>;
embeddingProviders: ReturnType<typeof listRegisteredEmbeddingProviders>;
interactiveHandlers: ReturnType<typeof listPluginInteractiveHandlers>;
memoryCapability: ReturnType<typeof getMemoryCapabilityRegistration>;
memoryCorpusSupplements: ReturnType<typeof listMemoryCorpusSupplements>;
memoryEmbeddingProviders: ReturnType<typeof listRegisteredMemoryEmbeddingProviders>;
memoryPromptPreparations: ReturnType<typeof listMemoryPromptPreparations>;
memoryPromptSupplements: ReturnType<typeof listMemoryPromptSupplements>;
sessionDiscussionProvider: ReturnType<typeof getSessionDiscussionProvider>;
};
export function snapshotPluginProcessGlobalState(): PluginProcessGlobalState {
return {
agentHarnesses: listRegisteredAgentHarnesses(),
commands: listRegisteredPluginCommands(),
compactionProviders: listRegisteredCompactionProviders(),
detachedTaskRuntimeRegistration: getDetachedTaskLifecycleRuntimeRegistration(),
embeddingProviders: listRegisteredEmbeddingProviders(),
interactiveHandlers: listPluginInteractiveHandlers(),
memoryCapability: getMemoryCapabilityRegistration(),
memoryCorpusSupplements: listMemoryCorpusSupplements(),
memoryEmbeddingProviders: listRegisteredMemoryEmbeddingProviders(),
memoryPromptPreparations: listMemoryPromptPreparations(),
memoryPromptSupplements: listMemoryPromptSupplements(),
sessionDiscussionProvider: getSessionDiscussionProvider(),
};
}
export function restorePluginProcessGlobalState(state: PluginProcessGlobalState): void {
restoreRegisteredAgentHarnesses(state.agentHarnesses);
restorePluginCommands(state.commands);
restoreRegisteredCompactionProviders(state.compactionProviders);
restoreDetachedTaskLifecycleRuntimeRegistration(state.detachedTaskRuntimeRegistration);
restoreRegisteredEmbeddingProviders(state.embeddingProviders);
restorePluginInteractiveHandlers(state.interactiveHandlers);
restoreRegisteredMemoryEmbeddingProviders(state.memoryEmbeddingProviders);
restoreMemoryPluginState({
capability: state.memoryCapability,
corpusSupplements: state.memoryCorpusSupplements,
promptPreparations: state.memoryPromptPreparations,
promptSupplements: state.memoryPromptSupplements,
});
restoreSessionDiscussionProvider(state.sessionDiscussionProvider);
}
function snapshotPluginRegistry(registry: PluginRegistry): PluginRegistry {
return Object.fromEntries(
Object.entries(registry).map(([key, value]) => {
if (Array.isArray(value)) {
return [key, [...value]];
}
if (value instanceof Map) {
return [key, new Map(value as ReadonlyMap<unknown, unknown>)];
}
if (value && typeof value === "object") {
return [key, { ...value }];
}
return [key, value];
}),
) as PluginRegistry;
}
function restorePluginRegistry(registry: PluginRegistry, snapshot: PluginRegistry): void {
Object.assign(registry, snapshot);
}
type PluginRegistrationTransaction = {
commit: (params: { activate: boolean }) => void;
rollback: () => void;
};
export function createPluginRegistrationTransaction(params: {
registry?: PluginRegistry;
rollbackGlobalSideEffects?: () => void;
}): PluginRegistrationTransaction {
const registrySnapshot = params.registry ? snapshotPluginRegistry(params.registry) : undefined;
const processGlobalState = snapshotPluginProcessGlobalState();
let settled = false;
const settle = (action: () => void): void => {
if (settled) {
return;
}
action();
settled = true;
};
return {
commit: ({ activate }) => {
settle(() => {
if (!activate) {
restorePluginProcessGlobalState(processGlobalState);
}
});
},
rollback: () => {
settle(() => {
params.rollbackGlobalSideEffects?.();
if (params.registry && registrySnapshot) {
restorePluginRegistry(params.registry, registrySnapshot);
}
restorePluginProcessGlobalState(processGlobalState);
});
},
};
}