From 670e3fb4ea20e56be99d405d175475105fd741fa Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 29 Sep 2026 10:55:33 -0700 Subject: [PATCH] fix(plugins): preserve CLI environment and worker test routing (#161270) * fix(plugins): preserve setup CLI environment and worker routing * test(plugins): track CLI policy fixture roots from creation --- scripts/lib/vitest-build-prerequisites.mts | 2 +- src/plugins/setup-registry.runtime.test.ts | 89 +++++++++++++++++++ src/plugins/setup-registry.runtime.ts | 5 +- .../vitest.database-worker-core-paths.mjs | 6 ++ .../vitest.media-understanding.config.ts | 2 + 5 files changed, 101 insertions(+), 3 deletions(-) diff --git a/scripts/lib/vitest-build-prerequisites.mts b/scripts/lib/vitest-build-prerequisites.mts index bdc23cf972b1..9dd0b33aca72 100644 --- a/scripts/lib/vitest-build-prerequisites.mts +++ b/scripts/lib/vitest-build-prerequisites.mts @@ -139,7 +139,6 @@ const runtimeConsumers = [ }, ...[ "src/agents/agent-command-local.test.ts", - "src/agents/simple-completion-runtime.plugin-scope.test.ts", "src/agents/runtime-plugins.context-engine.integration.test.ts", "src/agents/tool-surface-plan.provider-catalog.integration.test.ts", ].map((file) => ({ @@ -149,6 +148,7 @@ const runtimeConsumers = [ dir: "src/agents", })), ...[ + "src/agents/simple-completion-runtime.plugin-scope.test.ts", // Compiled catalog workers load the fixture's public SDK through built host artifacts. "src/agents/prepared-model-catalog-worker.custody.integration.test.ts", "src/agents/prepared-model-catalog-worker.integration.test.ts", diff --git a/src/plugins/setup-registry.runtime.test.ts b/src/plugins/setup-registry.runtime.test.ts index 306fb7d330eb..f7663ca477aa 100644 --- a/src/plugins/setup-registry.runtime.test.ts +++ b/src/plugins/setup-registry.runtime.test.ts @@ -1,9 +1,20 @@ // Verifies metadata-backed setup registry descriptor lookup. import { afterEach, describe, expect, it, vi } from "vitest"; +import { useAutoCleanupTempDirTracker } from "../../test/helpers/temp-dir.js"; +import { writeConfigMachineState } from "../state/config-machine-state-write.js"; +import { closeOpenClawStateDatabaseByPath } from "../state/openclaw-state-db-cache.js"; +import { resolveOpenClawStateSqlitePath } from "../state/openclaw-state-db.paths.js"; +import { captureEnv, setTestEnvValue } from "../test-utils/env.js"; +import { observeMainThreadSql } from "../test-utils/main-thread-sql-spies.test-support.js"; +import { + clearBundledDiscoveryModeMemo, + prepareBundledDiscoveryMode, +} from "./bundled-discovery-state.js"; import { withPluginMetadataSnapshotScope } from "./current-plugin-metadata-snapshot.js"; import { setCurrentPluginMetadataSnapshot } from "./current-plugin-metadata.test-support.js"; import { resolveInstalledPluginIndexPolicyHash } from "./installed-plugin-index-policy.js"; import * as installedPluginIndex from "./installed-plugin-index.js"; +import { createPluginCache, retirePluginCache, withPluginCache } from "./plugin-cache.js"; import { clearPluginMetadataLifecycleCaches } from "./plugin-metadata-lifecycle.js"; import { projectPluginMetadataSnapshot, @@ -53,6 +64,17 @@ afterEach(() => { vi.restoreAllMocks(); }); +const tempDirs = useAutoCleanupTempDirTracker((cleanup) => + afterEach(() => { + for (const stateDir of tempDirs.dirs) { + closeOpenClawStateDatabaseByPath( + resolveOpenClawStateSqlitePath({ OPENCLAW_STATE_DIR: stateDir }), + ); + } + cleanup(); + }), +); + function createCurrentSnapshot(params: { manifestHash: string; cliBackends: string[]; @@ -79,6 +101,73 @@ function createCurrentSnapshot(params: { } describe("setup-registry descriptor lookup", () => { + it("keeps prepared CLI activation in the caller's machine-state root", async () => { + const { resolvePluginSetupCliBackendDescriptor, resolvePluginSetupCliBackendIds } = + await import("./setup-registry.runtime.js"); + const compatRoot = tempDirs.make("openclaw-cli-compat-"); + const strictRoot = tempDirs.make("openclaw-cli-strict-"); + const envSnapshot = captureEnv(["OPENCLAW_STATE_DIR"]); + const cache = createPluginCache(); + try { + setTestEnvValue("OPENCLAW_STATE_DIR", strictRoot); + const compatEnv = { ...process.env, OPENCLAW_STATE_DIR: compatRoot }; + const strictEnv = { ...process.env }; + writeConfigMachineState("plugins.bundledDiscovery", "compat", { env: compatEnv }); + writeConfigMachineState("plugins.bundledDiscovery", "allowlist", { env: strictEnv }); + clearBundledDiscoveryModeMemo(); + await withPluginCache(cache, async () => { + const snapshot = createPluginMetadataSnapshotFixture({ + plugins: [ + { + id: "bundled-cli-owner", + origin: "bundled", + providers: ["fixture-provider"], + cliBackends: ["scope-cli"], + enabledByDefault: true, + }, + ], + }); + snapshot.index.plugins[0]!.contributions = { + channels: [], + channelConfigs: [], + providers: snapshot.plugins[0]!.providers, + modelCatalogProviders: [], + modelSupportPrefixes: [], + modelSupportPatterns: [], + autoEnableProviderIds: [], + commandAliases: [], + contracts: {}, + }; + const config = { plugins: { allow: ["other-owner"] } }; + await prepareBundledDiscoveryMode(compatEnv); + await prepareBundledDiscoveryMode(strictEnv); + const sql = observeMainThreadSql(); + try { + sql.calibrate(); + for (const env of [compatEnv, strictEnv, compatEnv]) { + const enabled = env === compatEnv; + const params = { config, env, metadataSnapshot: snapshot }; + expect( + resolvePluginSetupCliBackendDescriptor({ ...params, backend: "scope-cli" }), + ).toEqual( + enabled ? { pluginId: "bundled-cli-owner", backend: { id: "scope-cli" } } : undefined, + ); + expect(resolvePluginSetupCliBackendIds(params)).toEqual(enabled ? ["scope-cli"] : []); + } + sql.expectIdle(); + } finally { + sql.restore(); + } + }); + } finally { + try { + await retirePluginCache(cache); + } finally { + envSnapshot.restore(); + } + } + }); + it("evaluates activation only for owners of the requested CLI backend", async () => { const { resolvePluginSetupCliBackendDescriptor } = await import("./setup-registry.runtime.js"); const snapshot = createPluginMetadataSnapshotFixture({ diff --git a/src/plugins/setup-registry.runtime.ts b/src/plugins/setup-registry.runtime.ts index 68ca48af28f6..80b74781e3cc 100644 --- a/src/plugins/setup-registry.runtime.ts +++ b/src/plugins/setup-registry.runtime.ts @@ -45,7 +45,7 @@ export function resolvePluginSetupCliBackendDescriptor( // The immutable owner map preserves declaration order; only activation uses live policy. const pluginId = snapshot.owners.cliBackends .get(normalized) - ?.find((id) => isInstalledPluginEnabled(snapshot.index, id, params.config)); + ?.find((id) => isInstalledPluginEnabled(snapshot.index, id, params.config, params.env)); const plugin = pluginId ? snapshot.byPluginId.get(pluginId) : undefined; if (!plugin) { return undefined; @@ -63,7 +63,8 @@ export function resolvePluginSetupCliBackendIds( const snapshot = resolveSetupCliBackendSnapshot(params); return snapshot.plugins.flatMap((plugin) => { const ids = plugin.cliBackends.concat(plugin.setup?.cliBackends ?? []); - return ids.length > 0 && isInstalledPluginEnabled(snapshot.index, plugin.id, params.config) + return ids.length > 0 && + isInstalledPluginEnabled(snapshot.index, plugin.id, params.config, params.env) ? ids : []; }); diff --git a/test/vitest/vitest.database-worker-core-paths.mjs b/test/vitest/vitest.database-worker-core-paths.mjs index 2bd8825f5a68..17c492b49913 100644 --- a/test/vitest/vitest.database-worker-core-paths.mjs +++ b/test/vitest/vitest.database-worker-core-paths.mjs @@ -671,6 +671,12 @@ export const databaseWorkerCoreTestFiles = [ "src/tts/tts-summary.static-catalog.test.ts", "src/tts/tts-summary.selection.test.ts", "src/agents/prepared-model-catalog.resources.test.ts", + "src/agents/embedded-agent-runner/compaction-runtime-admission.test.ts", + "src/agents/tools/pdf-tool.resources.test.ts", + "src/media-understanding/image.resources.test.ts", + "src/tts/tts-summary.resources.test.ts", + "src/agents/simple-completion-runtime.plugin-scope.test.ts", + "src/agents/tools/pdf-tool.static-runtime.test.ts", "src/agents/prepared-model-catalog-worker.chat-metadata.integration.test.ts", "src/agents/prepared-model-catalog-worker.custody.integration.test.ts", "src/agents/prepared-model-catalog-worker.directory.test.ts", diff --git a/test/vitest/vitest.media-understanding.config.ts b/test/vitest/vitest.media-understanding.config.ts index efce893eea3e..7009e650f625 100644 --- a/test/vitest/vitest.media-understanding.config.ts +++ b/test/vitest/vitest.media-understanding.config.ts @@ -1,10 +1,12 @@ // Vitest media understanding config wires the media understanding test shard. +import { databaseWorkerCoreTestFiles } from "./vitest.database-worker-core-paths.mjs"; import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; export function createMediaUnderstandingVitestConfig(env?: Record) { return createScopedVitestConfig(["src/media-understanding/**/*.test.ts"], { dir: "src", env, + exclude: databaseWorkerCoreTestFiles, intersectIncludeFile: true, includeOpenClawRuntimeSetup: false, name: "media-understanding",