diff --git a/src/agents/model-auth.test.ts b/src/agents/model-auth.test.ts index df4dfeb6f82..8704a7e92ce 100644 --- a/src/agents/model-auth.test.ts +++ b/src/agents/model-auth.test.ts @@ -9,6 +9,11 @@ import { } from "./model-auth-markers.js"; vi.mock("../plugins/plugin-registry.js", () => ({ + loadPluginRegistrySnapshotWithMetadata: () => ({ + source: "derived", + snapshot: { plugins: [] }, + diagnostics: [], + }), loadPluginManifestRegistryForPluginRegistry: () => ({ diagnostics: [], plugins: [ diff --git a/src/agents/models-config.providers.auth-aliases.test.ts b/src/agents/models-config.providers.auth-aliases.test.ts index 5a93a3bf7f7..ef2aa9a0c7c 100644 --- a/src/agents/models-config.providers.auth-aliases.test.ts +++ b/src/agents/models-config.providers.auth-aliases.test.ts @@ -63,9 +63,15 @@ vi.mock("../plugins/manifest-registry.js", () => ({ })); vi.mock("../plugins/manifest-registry-installed.js", () => ({ loadPluginManifestRegistryForInstalledIndex: loadPluginManifestRegistry, + resolveInstalledManifestRegistryIndexFingerprint: () => "test-index", })); vi.mock("../plugins/plugin-registry.js", () => ({ loadPluginRegistrySnapshot: () => ({ plugins: [] }), + loadPluginRegistrySnapshotWithMetadata: () => ({ + source: "derived", + snapshot: { plugins: [] }, + diagnostics: [], + }), loadPluginManifestRegistryForPluginRegistry: () => loadPluginManifestRegistry(), })); vi.mock("../plugins/provider-runtime.js", () => ({ diff --git a/src/agents/sessions-spawn-hooks.test.ts b/src/agents/sessions-spawn-hooks.test.ts index a66ec1f3300..e7c84265bfd 100644 --- a/src/agents/sessions-spawn-hooks.test.ts +++ b/src/agents/sessions-spawn-hooks.test.ts @@ -70,6 +70,7 @@ async function spawn(params?: { agentAccountId?: string; agentTo?: string; agentThreadId?: string | number; + context?: "isolated"; }) { return await spawnSubagentDirect( { @@ -80,6 +81,7 @@ async function spawn(params?: { : {}), ...(params?.thread ? { thread: true } : {}), ...(params?.mode ? { mode: params.mode } : {}), + ...(params?.context ? { context: params.context } : {}), }, { agentSessionKey: params?.agentSessionKey ?? "main", @@ -207,6 +209,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { agentAccountId: "work", agentTo: "channel:123", agentThreadId: 456, + context: "isolated", }); expect(result).toMatchObject({ status: "accepted", runId: "run-1" }); @@ -285,6 +288,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { thread: true, mode: "run", agentTo: "channel:123", + context: "isolated", }); expect(result).toMatchObject({ status: "accepted", runId: "run-1", mode: "run" }); @@ -308,6 +312,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { mode: "session", agentAccountId: "work", agentTo: "channel:123", + context: "isolated", }); expectThreadBindFailureCleanup(result, /thread/i); @@ -325,6 +330,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { mode: "session", agentAccountId: "work", agentTo: "channel:123", + context: "isolated", }); expectThreadBindFailureCleanup(result, /unable to create or bind a thread/i); @@ -348,6 +354,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { mode: "session", agentChannel: "signal", agentTo: "+123", + context: "isolated", }); expectErrorResultMessage(result, /only discord/i); @@ -364,6 +371,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { agentAccountId: "work", agentTo: "channel:123", agentThreadId: "456", + context: "isolated", }); expect(result).toMatchObject({ status: "error" }); @@ -397,6 +405,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { agentAccountId: "work", agentTo: "channel:123", agentThreadId: "456", + context: "isolated", }); expect(result).toMatchObject({ status: "error" }); @@ -441,6 +450,7 @@ describe("sessions_spawn subagent lifecycle hooks", () => { agentAccountId: "work", agentTo: "channel:123", agentThreadId: "456", + context: "isolated", }); expect(result.status).toBe("error"); diff --git a/src/agents/subagent-spawn.thread-binding.test.ts b/src/agents/subagent-spawn.thread-binding.test.ts index 80aef248f02..2dd23d02d3d 100644 --- a/src/agents/subagent-spawn.thread-binding.test.ts +++ b/src/agents/subagent-spawn.thread-binding.test.ts @@ -134,6 +134,7 @@ describe("spawnSubagentDirect thread binding delivery", () => { agentId: "bot-alpha", thread: true, mode: "session", + context: "isolated", }, { agentSessionKey: "agent:main:main", @@ -201,6 +202,7 @@ describe("spawnSubagentDirect thread binding delivery", () => { task: "reply with a marker", thread: true, mode: "session", + context: "isolated", }, { agentSessionKey: "agent:main:main", diff --git a/src/commands/channel-setup/workspace-shadow-bypass.test.ts b/src/commands/channel-setup/workspace-shadow-bypass.test.ts index 7c919993f74..a3749cbd865 100644 --- a/src/commands/channel-setup/workspace-shadow-bypass.test.ts +++ b/src/commands/channel-setup/workspace-shadow-bypass.test.ts @@ -44,6 +44,11 @@ vi.mock("../../plugins/plugin-registry.js", () => ({ loadPluginManifestRegistryForPluginRegistry: (...args: unknown[]) => loadPluginManifestRegistry(...args), loadPluginRegistrySnapshot: (...args: unknown[]) => loadPluginRegistrySnapshot(...args), + loadPluginRegistrySnapshotWithMetadata: (...args: unknown[]) => ({ + source: "derived", + snapshot: loadPluginRegistrySnapshot(...args), + diagnostics: [], + }), listPluginContributionIds: (...args: unknown[]) => listPluginContributionIds(...args), })); vi.mock("../../config/plugin-auto-enable.js", () => ({ diff --git a/src/plugins/capability-provider-runtime.test.ts b/src/plugins/capability-provider-runtime.test.ts index 09c694f7031..1349f11bbf3 100644 --- a/src/plugins/capability-provider-runtime.test.ts +++ b/src/plugins/capability-provider-runtime.test.ts @@ -70,11 +70,28 @@ vi.mock("./plugin-registry.js", async (importOriginal) => { return { ...actual, loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot, - loadPluginRegistrySnapshotWithMetadata: (params?: { index?: unknown }) => ({ - snapshot: params?.index ?? mocks.loadPluginRegistrySnapshot(), - source: params?.index ? "provided" : "derived", - diagnostics: [], - }), + loadPluginRegistrySnapshotWithMetadata: (params?: { index?: unknown }) => { + const snapshot = + params?.index ?? + (mocks.loadPluginRegistrySnapshot() as { plugins?: Array> }); + return { + snapshot: { + ...snapshot, + plugins: + snapshot.plugins && snapshot.plugins.length > 0 + ? snapshot.plugins + : [ + { + pluginId: "__test_manifest_registry_fixture__", + origin: "bundled", + enabled: true, + }, + ], + }, + source: params?.index ? "provided" : "derived", + diagnostics: [], + }; + }, loadPluginManifestRegistryForPluginRegistry: ( ...args: Parameters ) => { diff --git a/src/plugins/migration-provider-runtime.test.ts b/src/plugins/migration-provider-runtime.test.ts index e4179853558..f152d030344 100644 --- a/src/plugins/migration-provider-runtime.test.ts +++ b/src/plugins/migration-provider-runtime.test.ts @@ -34,6 +34,11 @@ const mocks = vi.hoisted(() => ({ () => createEmptyMockManifestRegistry(), ), loadPluginRegistrySnapshot: vi.fn<() => MockPluginIndex>(() => createMockPluginIndex([])), + loadPluginRegistrySnapshotWithMetadata: vi.fn((params?: { index?: MockPluginIndex }) => ({ + source: params?.index ? "provided" : "derived", + snapshot: params?.index ?? createMockPluginIndex([]), + diagnostics: [], + })), withBundledPluginAllowlistCompat: vi.fn( ({ config }: { config?: OpenClawConfig; pluginIds: string[] }) => config, ), @@ -51,10 +56,12 @@ vi.mock("./loader.js", () => ({ vi.mock("./plugin-registry.js", () => ({ loadPluginRegistrySnapshot: mocks.loadPluginRegistrySnapshot, + loadPluginRegistrySnapshotWithMetadata: mocks.loadPluginRegistrySnapshotWithMetadata, })); vi.mock("./manifest-registry-installed.js", () => ({ loadPluginManifestRegistryForInstalledIndex: mocks.loadPluginManifestRegistry, + resolveInstalledManifestRegistryIndexFingerprint: () => "test-index", })); vi.mock("./bundled-compat.js", () => ({ @@ -81,6 +88,13 @@ describe("migration provider runtime", () => { mocks.resolveRuntimePluginRegistry.mockReturnValue(createEmptyPluginRegistry()); mocks.loadPluginManifestRegistry.mockReturnValue(createEmptyMockManifestRegistry()); mocks.loadPluginRegistrySnapshot.mockReturnValue(createMockPluginIndex([])); + mocks.loadPluginRegistrySnapshotWithMetadata.mockImplementation( + (params?: { index?: MockPluginIndex }) => ({ + source: params?.index ? "provided" : "derived", + snapshot: params?.index ?? mocks.loadPluginRegistrySnapshot(), + diagnostics: [], + }), + ); const runtime = await import("./migration-provider-runtime.js"); resolvePluginMigrationProvider = runtime.resolvePluginMigrationProvider; resolvePluginMigrationProviders = runtime.resolvePluginMigrationProviders; @@ -143,7 +157,7 @@ describe("migration provider runtime", () => { const resolved = resolvePluginMigrationProvider({ providerId: "external-import", cfg }); expect(resolved).toBe(provider); - expect(mocks.loadPluginRegistrySnapshot).toHaveBeenCalledWith({ + expect(mocks.loadPluginRegistrySnapshotWithMetadata).toHaveBeenCalledWith({ config: cfg, env: process.env, preferPersisted: false, @@ -202,18 +216,20 @@ describe("migration provider runtime", () => { const resolved = resolvePluginMigrationProvider({ providerId: "hermes" }); expect(resolved).toBe(provider); - expect(mocks.loadPluginRegistrySnapshot).toHaveBeenCalledWith({ - config: undefined, + expect(mocks.loadPluginRegistrySnapshotWithMetadata).toHaveBeenCalledWith({ + config: {}, env: process.env, preferPersisted: false, + workspaceDir: undefined, }); expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith({ index: expect.objectContaining({ plugins: [expect.objectContaining({ pluginId: "migrate-hermes" })], }), - config: undefined, + config: {}, env: process.env, includeDisabled: true, + workspaceDir: undefined, }); expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith({ onlyPluginIds: ["migrate-hermes"], diff --git a/src/plugins/web-fetch-providers.runtime.test.ts b/src/plugins/web-fetch-providers.runtime.test.ts index c6ec39a141b..7d80b69a36d 100644 --- a/src/plugins/web-fetch-providers.runtime.test.ts +++ b/src/plugins/web-fetch-providers.runtime.test.ts @@ -165,7 +165,7 @@ describe("resolvePluginWebFetchProviders", () => { expect(inFlightSpy).toHaveBeenCalledWith( expect.objectContaining({ activate: false, - cache: false, + cache: true, onlyPluginIds: ["firecrawl"], workspaceDir: DEFAULT_WORKSPACE, }), diff --git a/src/plugins/web-search-providers.runtime.test.ts b/src/plugins/web-search-providers.runtime.test.ts index 6f1d9ea6651..6ae9ea573b2 100644 --- a/src/plugins/web-search-providers.runtime.test.ts +++ b/src/plugins/web-search-providers.runtime.test.ts @@ -8,6 +8,8 @@ type WebSearchProvidersSharedModule = typeof import("./web-search-providers.shar type PluginManifestRegistry = import("./manifest-registry.js").PluginManifestRegistry; type LoadPluginManifestRegistryForPluginRegistry = typeof import("./plugin-registry.js").loadPluginManifestRegistryForPluginRegistry; +type LoadPluginManifestRegistryForInstalledIndex = + typeof import("./manifest-registry-installed.js").loadPluginManifestRegistryForInstalledIndex; const BUNDLED_WEB_SEARCH_PROVIDERS = [ { pluginId: "brave", id: "brave", order: 10 }, @@ -25,6 +27,9 @@ let createEmptyPluginRegistry: RegistryModule["createEmptyPluginRegistry"]; let loadPluginManifestRegistryMock: ReturnType< typeof vi.fn >; +let loadInstalledPluginManifestRegistryMock: ReturnType< + typeof vi.fn +>; let setActivePluginRegistry: RuntimeModule["setActivePluginRegistry"]; let resolvePluginWebSearchProviders: WebSearchProvidersRuntimeModule["resolvePluginWebSearchProviders"]; let resolveRuntimeWebSearchProviders: WebSearchProvidersRuntimeModule["resolveRuntimeWebSearchProviders"]; @@ -181,7 +186,7 @@ function expectLoaderCallCount(count: number) { } function expectScopedWebSearchCandidates(pluginIds: readonly string[]) { - expect(loadPluginManifestRegistryMock).toHaveBeenCalled(); + expect(loadInstalledPluginManifestRegistryMock).toHaveBeenCalled(); expect(loadOpenClawPluginsMock).toHaveBeenCalledWith( expect.objectContaining({ onlyPluginIds: [...pluginIds], @@ -320,16 +325,41 @@ function expectRuntimeProviderResolution( describe("resolvePluginWebSearchProviders", () => { beforeAll(async () => { loadPluginManifestRegistryMock = vi.fn(); + loadInstalledPluginManifestRegistryMock = vi.fn(); vi.doMock("./plugin-registry.js", async () => { const actual = await vi.importActual("./plugin-registry.js"); return { ...actual, + loadPluginRegistrySnapshotWithMetadata: () => ({ + source: "derived", + snapshot: { + plugins: [ + { + pluginId: "__test_manifest_registry_fixture__", + origin: "bundled", + enabled: true, + }, + ], + }, + diagnostics: [], + }), loadPluginManifestRegistryForPluginRegistry: ( ...args: Parameters ) => loadPluginManifestRegistryMock(...args), }; }); + vi.doMock("./manifest-registry-installed.js", async () => { + const actual = await vi.importActual( + "./manifest-registry-installed.js", + ); + return { + ...actual, + loadPluginManifestRegistryForInstalledIndex: ( + ...args: Parameters + ) => loadInstalledPluginManifestRegistryMock(...args), + }; + }); ({ createEmptyPluginRegistry } = await import("./registry-empty.js")); loaderModule = await import("./loader.js"); @@ -354,6 +384,8 @@ describe("resolvePluginWebSearchProviders", () => { ); loadPluginManifestRegistryMock.mockReset(); loadPluginManifestRegistryMock.mockReturnValue(createManifestRegistryFixture()); + loadInstalledPluginManifestRegistryMock.mockReset(); + loadInstalledPluginManifestRegistryMock.mockReturnValue(createManifestRegistryFixture()); loadOpenClawPluginsMock = vi .spyOn(loaderModule, "loadOpenClawPlugins") .mockImplementation((params) => { @@ -435,7 +467,7 @@ describe("resolvePluginWebSearchProviders", () => { env, }); - expect(loadPluginManifestRegistryMock).toHaveBeenCalledWith( + expect(loadInstalledPluginManifestRegistryMock).toHaveBeenCalledWith( expect.objectContaining({ workspaceDir: "/tmp/runtime-workspace", }),