















@@ -7,6 +7,8 @@ const mocks = vi.hoisted(() => ({
77vi.fn<typeof import("../loader.js").resolveCompatibleRuntimePluginRegistry>(),
88resolveRuntimePluginRegistry: vi.fn<typeof import("../loader.js").resolveRuntimePluginRegistry>(),
99getActivePluginRegistry: vi.fn<typeof import("../runtime.js").getActivePluginRegistry>(),
10+getActivePluginRegistryWorkspaceDir:
11+vi.fn<typeof import("../runtime.js").getActivePluginRegistryWorkspaceDir>(),
1012resolveConfiguredChannelPluginIds:
1113vi.fn<typeof import("../channel-plugin-ids.js").resolveConfiguredChannelPluginIds>(),
1214resolveDiscoverableScopedChannelPluginIds:
@@ -76,7 +78,9 @@ vi.mock("../runtime.js", () => ({
7678getActivePluginHttpRouteRegistry: () => null,
7779getActivePluginRegistry: (...args: Parameters<typeof mocks.getActivePluginRegistry>) =>
7880mocks.getActivePluginRegistry(...args),
79-getActivePluginRegistryWorkspaceDir: () => undefined,
81+getActivePluginRegistryWorkspaceDir: (
82+ ...args: Parameters<typeof mocks.getActivePluginRegistryWorkspaceDir>
83+) => mocks.getActivePluginRegistryWorkspaceDir(...args),
8084}));
81858286vi.mock("../channel-plugin-ids.js", () => ({
@@ -119,6 +123,7 @@ describe("ensurePluginRegistryLoaded", () => {
119123mocks.resolveCompatibleRuntimePluginRegistry.mockReset();
120124mocks.resolveRuntimePluginRegistry.mockReset();
121125mocks.getActivePluginRegistry.mockReset();
126+mocks.getActivePluginRegistryWorkspaceDir.mockReset();
122127mocks.resolveConfiguredChannelPluginIds.mockReset();
123128mocks.resolveDiscoverableScopedChannelPluginIds.mockReset();
124129mocks.resolveChannelPluginIds.mockReset();
@@ -129,6 +134,7 @@ describe("ensurePluginRegistryLoaded", () => {
129134resetPluginRegistryLoadedForTests();
130135131136mocks.getActivePluginRegistry.mockReturnValue(null);
137+mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue(undefined);
132138mocks.resolveCompatibleRuntimePluginRegistry.mockReturnValue(undefined);
133139mocks.loadOpenClawPlugins.mockReturnValue(createEmptyPluginRegistry());
134140mocks.resolveRuntimePluginRegistry.mockImplementation(
@@ -333,6 +339,33 @@ describe("ensurePluginRegistryLoaded", () => {
333339expect(load.workspaceDir).toBe("/resolved-workspace");
334340});
335341342+it("does not reuse non-empty all-scope registries without loader compatibility", () => {
343+mocks.resolveEffectivePluginIds.mockReturnValue(["demo"]);
344+345+ensurePluginRegistryLoaded({
346+scope: "all",
347+config: { plugins: { allow: ["demo"] } } as never,
348+});
349+const activeRegistry = createEmptyPluginRegistry();
350+activeRegistry.plugins.push({
351+id: "demo",
352+source: "/tmp/demo.js",
353+origin: "workspace",
354+enabled: true,
355+status: "loaded",
356+} as never);
357+mocks.getActivePluginRegistry.mockReturnValue(activeRegistry);
358+mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue("/resolved-workspace");
359+mocks.loadOpenClawPlugins.mockClear();
360+361+ensurePluginRegistryLoaded({
362+scope: "all",
363+config: { plugins: { allow: ["demo"], entries: { demo: { value: "changed" } } } } as never,
364+});
365+366+expect(loadOptions().onlyPluginIds).toEqual(["demo"]);
367+});
368+336369it("preserves empty all-scope loads instead of widening to all discovered plugins", () => {
337370mocks.resolveEffectivePluginIds.mockReturnValue([]);
338371@@ -344,6 +377,149 @@ describe("ensurePluginRegistryLoaded", () => {
344377expect(loadOptions().onlyPluginIds).toEqual([]);
345378});
346379380+it("reuses an active empty registry for repeated empty all-scope loads", () => {
381+mocks.resolveEffectivePluginIds.mockReturnValue([]);
382+383+ensurePluginRegistryLoaded({
384+scope: "all",
385+config: { plugins: { enabled: true } } as never,
386+});
387+const emptyRegistry = createEmptyPluginRegistry();
388+mocks.getActivePluginRegistry.mockReturnValue(emptyRegistry);
389+mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue("/resolved-workspace");
390+mocks.loadOpenClawPlugins.mockClear();
391+392+ensurePluginRegistryLoaded({
393+scope: "all",
394+config: { plugins: { enabled: true } } as never,
395+});
396+397+expect(mocks.loadOpenClawPlugins).not.toHaveBeenCalled();
398+});
399+400+it("does not reuse an empty active registry from another workspace", () => {
401+mocks.resolveEffectivePluginIds.mockReturnValue([]);
402+403+ensurePluginRegistryLoaded({
404+scope: "all",
405+config: { plugins: { enabled: true } } as never,
406+});
407+const emptyRegistry = createEmptyPluginRegistry();
408+mocks.getActivePluginRegistry.mockReturnValue(emptyRegistry);
409+mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue("/other-workspace");
410+mocks.loadOpenClawPlugins.mockClear();
411+412+ensurePluginRegistryLoaded({
413+scope: "all",
414+config: { plugins: { enabled: true } } as never,
415+});
416+417+expect(loadOptions().onlyPluginIds).toEqual([]);
418+});
419+420+it("does not reuse a non-empty active registry for empty all-scope loads", () => {
421+mocks.resolveEffectivePluginIds.mockReturnValue([]);
422+423+ensurePluginRegistryLoaded({
424+scope: "all",
425+config: { plugins: { enabled: true } } as never,
426+});
427+const staleRegistry = createEmptyPluginRegistry();
428+staleRegistry.plugins.push({
429+id: "stale",
430+source: "/tmp/stale.js",
431+origin: "workspace",
432+enabled: true,
433+status: "loaded",
434+} as never);
435+mocks.getActivePluginRegistry.mockReturnValue(staleRegistry);
436+mocks.loadOpenClawPlugins.mockClear();
437+438+ensurePluginRegistryLoaded({
439+scope: "all",
440+config: { plugins: { enabled: true } } as never,
441+});
442+443+expect(loadOptions().onlyPluginIds).toEqual([]);
444+});
445+446+it("does not reuse a disabled-record registry for empty all-scope loads", () => {
447+mocks.resolveEffectivePluginIds.mockReturnValue([]);
448+449+ensurePluginRegistryLoaded({
450+scope: "all",
451+config: { plugins: { enabled: true } } as never,
452+});
453+const disabledRegistry = createEmptyPluginRegistry();
454+disabledRegistry.plugins.push({
455+id: "disabled",
456+source: "/tmp/disabled.js",
457+origin: "workspace",
458+enabled: false,
459+status: "disabled",
460+} as never);
461+mocks.getActivePluginRegistry.mockReturnValue(disabledRegistry);
462+mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue("/resolved-workspace");
463+mocks.loadOpenClawPlugins.mockClear();
464+465+ensurePluginRegistryLoaded({
466+scope: "all",
467+config: { plugins: { enabled: true } } as never,
468+});
469+470+expect(loadOptions().onlyPluginIds).toEqual([]);
471+});
472+473+it("does not reuse a failed diagnostic registry for explicit plugin scopes", () => {
474+const failedRegistry = createEmptyPluginRegistry();
475+failedRegistry.plugins.push({
476+id: "failed",
477+source: "/tmp/failed.js",
478+origin: "workspace",
479+enabled: true,
480+status: "error",
481+} as never);
482+failedRegistry.diagnostics.push({
483+level: "error",
484+pluginId: "failed",
485+message: "failed to load",
486+} as never);
487+mocks.getActivePluginRegistry.mockReturnValue(failedRegistry);
488+mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue("/resolved-workspace");
489+490+ensurePluginRegistryLoaded({
491+scope: "all",
492+config: { plugins: { enabled: true } } as never,
493+onlyPluginIds: ["failed"],
494+});
495+496+expect(loadOptions().onlyPluginIds).toEqual(["failed"]);
497+});
498+499+it("does not reuse a setup-only registry for explicit plugin scopes", () => {
500+const setupRegistry = createEmptyPluginRegistry();
501+setupRegistry.plugins.push({
502+id: "setup-only",
503+source: "/tmp/setup-only.js",
504+origin: "workspace",
505+enabled: false,
506+status: "disabled",
507+} as never);
508+setupRegistry.channelSetups.push({
509+pluginId: "setup-only",
510+} as never);
511+mocks.getActivePluginRegistry.mockReturnValue(setupRegistry);
512+mocks.getActivePluginRegistryWorkspaceDir.mockReturnValue("/resolved-workspace");
513+514+ensurePluginRegistryLoaded({
515+scope: "all",
516+config: { plugins: { enabled: true } } as never,
517+onlyPluginIds: ["setup-only"],
518+});
519+520+expect(loadOptions().onlyPluginIds).toEqual(["setup-only"]);
521+});
522+347523it("reuses a compatible active registry instead of forcing a broad reload", () => {
348524const activeRegistry = createEmptyPluginRegistry();
349525activeRegistry.plugins.push({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。