mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:40:44 +00:00
fix(plugins): preserve memory capability across snapshot plugin loads
Preserve the active memory capability when non-activating plugin snapshot loads run, and add a regression test.\n\nThanks @zeroaltitude.
This commit is contained in:
@@ -12,6 +12,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Plugins/memory: preserve the active memory capability when read-only snapshot plugin loads run, so status and provider discovery paths no longer wipe memory public artifacts. (#69219) Thanks @zeroaltitude.
|
||||
- fix(security): block MINIMAX_API_HOST workspace env injection and remove env-driven URL routing [AI-assisted]. (#67300) Thanks @pgondhi987.
|
||||
- Cron/delivery: treat explicit `delivery.mode: "none"` runs as not requested even if the runner reports `delivered: false`, so no-delivery cron jobs no longer persist false delivery failures or errors. (#69285) Thanks @matsuri1987.
|
||||
- Plugins/install: repair active and default-enabled bundled plugin runtime dependencies before import in packaged installs, so bundled Discord, WhatsApp, Slack, Telegram, and provider plugins work without putting their dependency trees in core.
|
||||
|
||||
@@ -2527,6 +2527,87 @@ module.exports = { id: "throws-after-import", register() {} };`,
|
||||
);
|
||||
});
|
||||
|
||||
it("preserves previously registered memory capability across activate:false snapshot loads", async () => {
|
||||
useNoBundledPlugins();
|
||||
const workspaceDir = makeTempDir();
|
||||
const absolutePath = path.join(workspaceDir, "MEMORY.md");
|
||||
fs.writeFileSync(absolutePath, "# Memory\n");
|
||||
const memoryPlugin = writePlugin({
|
||||
id: "capability-survives-memory",
|
||||
filename: "capability-survives-memory.cjs",
|
||||
body: `module.exports = {
|
||||
id: "capability-survives-memory",
|
||||
kind: "memory",
|
||||
register(api) {
|
||||
api.registerMemoryCapability({
|
||||
publicArtifacts: {
|
||||
async listArtifacts() {
|
||||
return [{
|
||||
kind: "memory-root",
|
||||
workspaceDir: ${JSON.stringify(workspaceDir)},
|
||||
relativePath: "MEMORY.md",
|
||||
absolutePath: ${JSON.stringify(absolutePath)},
|
||||
agentIds: ["main"],
|
||||
contentType: "markdown",
|
||||
}];
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
};`,
|
||||
});
|
||||
const sidecarPlugin = writePlugin({
|
||||
id: "capability-survives-sidecar",
|
||||
filename: "capability-survives-sidecar.cjs",
|
||||
body: `module.exports = {
|
||||
id: "capability-survives-sidecar",
|
||||
register() {},
|
||||
};`,
|
||||
});
|
||||
|
||||
const activateConfig = {
|
||||
plugins: {
|
||||
load: { paths: [memoryPlugin.file, sidecarPlugin.file] },
|
||||
allow: ["capability-survives-memory", "capability-survives-sidecar"],
|
||||
slots: { memory: "capability-survives-memory" },
|
||||
},
|
||||
};
|
||||
loadOpenClawPlugins({
|
||||
cache: false,
|
||||
workspaceDir: memoryPlugin.dir,
|
||||
config: activateConfig,
|
||||
});
|
||||
|
||||
const expectedArtifacts = [
|
||||
{
|
||||
kind: "memory-root",
|
||||
workspaceDir,
|
||||
relativePath: "MEMORY.md",
|
||||
absolutePath,
|
||||
agentIds: ["main"],
|
||||
contentType: "markdown" as const,
|
||||
},
|
||||
];
|
||||
|
||||
await expect(listActiveMemoryPublicArtifacts({ cfg: {} as never })).resolves.toEqual(
|
||||
expectedArtifacts,
|
||||
);
|
||||
|
||||
// Simulate what resolvePluginWebSearchProviders and similar read-only paths do:
|
||||
// load plugins again with activate:false. Each per-plugin snapshot/rollback must
|
||||
// preserve the previously registered memory capability.
|
||||
loadOpenClawPlugins({
|
||||
cache: false,
|
||||
activate: false,
|
||||
workspaceDir: memoryPlugin.dir,
|
||||
config: activateConfig,
|
||||
});
|
||||
|
||||
await expect(listActiveMemoryPublicArtifacts({ cfg: {} as never })).resolves.toEqual(
|
||||
expectedArtifacts,
|
||||
);
|
||||
});
|
||||
|
||||
it("throws when activate:false is used without cache:false", () => {
|
||||
expect(() => loadOpenClawPlugins({ activate: false })).toThrow(
|
||||
"activate:false requires cache:false",
|
||||
|
||||
@@ -2253,6 +2253,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
const previousAgentHarnesses = listRegisteredAgentHarnesses();
|
||||
const previousCompactionProviders = listRegisteredCompactionProviders();
|
||||
const previousDetachedTaskRuntimeRegistration = getDetachedTaskLifecycleRuntimeRegistration();
|
||||
const previousMemoryCapability = getMemoryCapabilityRegistration();
|
||||
const previousMemoryEmbeddingProviders = listRegisteredMemoryEmbeddingProviders();
|
||||
const previousMemoryFlushPlanResolver = getMemoryFlushPlanResolver();
|
||||
const previousMemoryPromptBuilder = getMemoryPromptSectionBuilder();
|
||||
@@ -2269,6 +2270,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
restoreDetachedTaskLifecycleRuntimeRegistration(previousDetachedTaskRuntimeRegistration);
|
||||
restoreRegisteredMemoryEmbeddingProviders(previousMemoryEmbeddingProviders);
|
||||
restoreMemoryPluginState({
|
||||
capability: previousMemoryCapability,
|
||||
corpusSupplements: previousMemoryCorpusSupplements,
|
||||
promptBuilder: previousMemoryPromptBuilder,
|
||||
promptSupplements: previousMemoryPromptSupplements,
|
||||
@@ -2286,6 +2288,7 @@ export function loadOpenClawPlugins(options: PluginLoadOptions = {}): PluginRegi
|
||||
restoreDetachedTaskLifecycleRuntimeRegistration(previousDetachedTaskRuntimeRegistration);
|
||||
restoreRegisteredMemoryEmbeddingProviders(previousMemoryEmbeddingProviders);
|
||||
restoreMemoryPluginState({
|
||||
capability: previousMemoryCapability,
|
||||
corpusSupplements: previousMemoryCorpusSupplements,
|
||||
promptBuilder: previousMemoryPromptBuilder,
|
||||
promptSupplements: previousMemoryPromptSupplements,
|
||||
|
||||
Reference in New Issue
Block a user