diff --git a/src/agents/harness/runtime-plugin.test.ts b/src/agents/harness/runtime-plugin.test.ts index 11ef4ffd4e80..46082b919ee1 100644 --- a/src/agents/harness/runtime-plugin.test.ts +++ b/src/agents/harness/runtime-plugin.test.ts @@ -477,6 +477,28 @@ describe("ensureSelectedAgentHarnessPlugin", () => { expect(mocks.resolveOwningPluginIdsForProvider).not.toHaveBeenCalled(); }); + it("keeps official OpenAI providers on embedded OpenClaw when explicitly configured", async () => { + await ensureSelectedAgentHarnessPlugin({ + provider: "openai", + modelId: "gpt-5.2", + config: { + models: { + providers: { + openai: { + baseUrl: "https://api.openai.com/v1", + agentRuntime: { id: "openclaw" }, + models: [], + }, + }, + }, + } as OpenClawConfig, + workspaceDir: "/tmp/workspace", + }); + + expect(mocks.ensurePluginRegistryLoaded).not.toHaveBeenCalled(); + expect(mocks.resolveOwningPluginIdsForProvider).not.toHaveBeenCalled(); + }); + it("does not treat CLI backend runtime aliases as plugin ids", async () => { await ensureSelectedAgentHarnessPlugin({ provider: "anthropic", diff --git a/src/commands/agent-command-state.test-mocks.ts b/src/commands/agent-command-state.test-mocks.ts new file mode 100644 index 000000000000..591374a94c25 --- /dev/null +++ b/src/commands/agent-command-state.test-mocks.ts @@ -0,0 +1,10 @@ +// Shared hoisted state for the agent command test mocks. +import { vi } from "vitest"; + +const agentHarnessPluginMocks = vi.hoisted(() => ({ + ensureSelectedAgentHarnessPlugin: vi.fn(async () => undefined), +})); + +export function getAgentHarnessPluginMocks() { + return agentHarnessPluginMocks; +} diff --git a/src/commands/agent-command.test-mocks.ts b/src/commands/agent-command.test-mocks.ts index 730ebc69ef31..738d0a6afc93 100644 --- a/src/commands/agent-command.test-mocks.ts +++ b/src/commands/agent-command.test-mocks.ts @@ -1,5 +1,14 @@ // Agent command test mocks replace logging and runtime-heavy modules shared by agent command suites. import { vi } from "vitest"; +import { getAgentHarnessPluginMocks } from "./agent-command-state.test-mocks.js"; + +// Harness/plugin selection has focused owner coverage in runtime-plugin.test.ts. +// Command suites only need to prove their handoff without loading plugin manifests. +const agentHarnessPluginMocks = getAgentHarnessPluginMocks(); + +vi.mock("../agents/harness/runtime-plugin.js", () => ({ + ensureSelectedAgentHarnessPlugin: agentHarnessPluginMocks.ensureSelectedAgentHarnessPlugin, +})); vi.mock("../logging/subsystem.js", () => { const createMockLogger = () => ({ diff --git a/src/commands/agent.test.ts b/src/commands/agent.test.ts index a21bea3bd9ed..58601b870bc9 100644 --- a/src/commands/agent.test.ts +++ b/src/commands/agent.test.ts @@ -5,6 +5,7 @@ import { expectDefined } from "@openclaw/normalization-core"; import { buildChannelOutboundSessionRoute } from "openclaw/plugin-sdk/core"; import { withTempHome as withTempHomeBase } from "openclaw/plugin-sdk/test-env"; import { beforeEach, describe, expect, it, type MockInstance, vi } from "vitest"; +// Register shared mocks before imports bind their production exports. import "./agent-command.test-mocks.js"; import { testing as acpManagerTesting } from "../acp/control-plane/manager.js"; import * as authProfileStoreModule from "../agents/auth-profiles/store.js"; @@ -38,6 +39,7 @@ import { createOutboundTestPlugin, createTestRegistry, } from "../test-utils/channel-plugins.js"; +import { getAgentHarnessPluginMocks } from "./agent-command-state.test-mocks.js"; import { agentCommand, agentCommandFromIngress, testing as agentCommandTesting } from "./agent.js"; import { createThrowingTestRuntime } from "./test-runtime-config-helpers.js"; @@ -45,9 +47,7 @@ const configIoMocks = vi.hoisted(() => ({ loadConfig: vi.fn(), readConfigFileSnapshotForWrite: vi.fn(), })); -const pluginRegistryMocks = vi.hoisted(() => ({ - ensurePluginRegistryLoaded: vi.fn(), -})); +const agentHarnessPluginMocks = getAgentHarnessPluginMocks(); vi.mock("../config/io.js", () => ({ getRuntimeConfig: configIoMocks.loadConfig, @@ -55,10 +55,6 @@ vi.mock("../config/io.js", () => ({ readConfigFileSnapshotForWrite: configIoMocks.readConfigFileSnapshotForWrite, })); -vi.mock("../plugins/runtime/runtime-registry-loader.js", () => ({ - ensurePluginRegistryLoaded: pluginRegistryMocks.ensurePluginRegistryLoaded, -})); - vi.mock("../agents/auth-profiles/store.js", () => { const createEmptyStore = () => ({ version: 1, profiles: {} }); return { @@ -401,46 +397,10 @@ beforeEach(() => { }); describe("agentCommand", () => { - it("enables Codex, provider owner, and memory slot plugins for one-shot OpenAI model overrides", async () => { - await withTempHome(async (home) => { - const storePath = path.join(home, "sessions.json"); - mockConfig(home, storePath, { models: undefined }); - - await agentCommand( - { - message: "hi", - agentId: "main", - model: "openai/gpt-5.2", - allowModelOverride: true, - }, - runtime, - ); - - expect(pluginRegistryMocks.ensurePluginRegistryLoaded).toHaveBeenCalledTimes(1); - for (const [registryLoad] of pluginRegistryMocks.ensurePluginRegistryLoaded.mock.calls) { - expect(registryLoad?.scope).toBe("all"); - expect(registryLoad?.config).toBeTypeOf("object"); - expect(registryLoad?.activationSourceConfig).toBeTypeOf("object"); - expect(registryLoad?.workspaceDir).toBe(path.join(home, "openclaw")); - expect(registryLoad?.onlyPluginIds).toEqual(["codex", "openai", "memory-core"]); - } - expectLastRunProviderModel("openai", "gpt-5.2"); - }); - }); - - it("does not enable Codex for one-shot OpenAI overrides when the provider forces OpenClaw", async () => { + it("passes one-shot OpenAI model overrides to harness plugin preparation", async () => { await withTempHome(async (home) => { const storePath = path.join(home, "sessions.json"); const cfg = mockConfig(home, storePath, { models: undefined }); - cfg.models = { - providers: { - openai: { - baseUrl: "https://api.openai.com/v1", - agentRuntime: { id: "openclaw" }, - models: [], - }, - }, - }; await agentCommand( { @@ -452,7 +412,16 @@ describe("agentCommand", () => { runtime, ); - expect(pluginRegistryMocks.ensurePluginRegistryLoaded).not.toHaveBeenCalled(); + expect(agentHarnessPluginMocks.ensureSelectedAgentHarnessPlugin).toHaveBeenCalledOnce(); + expect(agentHarnessPluginMocks.ensureSelectedAgentHarnessPlugin).toHaveBeenCalledWith( + expect.objectContaining({ + config: cfg, + provider: "openai", + modelId: "gpt-5.2", + agentId: "main", + workspaceDir: path.join(home, "openclaw"), + }), + ); expectLastRunProviderModel("openai", "gpt-5.2"); }); }); diff --git a/src/commands/agent.worktree-race.test.ts b/src/commands/agent.worktree-race.test.ts index 34bff7964d5e..42e6daa77b19 100644 --- a/src/commands/agent.worktree-race.test.ts +++ b/src/commands/agent.worktree-race.test.ts @@ -20,9 +20,6 @@ const configIoMocks = vi.hoisted(() => ({ loadConfig: vi.fn(), readConfigFileSnapshotForWrite: vi.fn(), })); -const pluginRegistryMocks = vi.hoisted(() => ({ - ensurePluginRegistryLoaded: vi.fn(), -})); vi.mock("../config/io.js", () => ({ getRuntimeConfig: configIoMocks.loadConfig, @@ -30,10 +27,6 @@ vi.mock("../config/io.js", () => ({ readConfigFileSnapshotForWrite: configIoMocks.readConfigFileSnapshotForWrite, })); -vi.mock("../plugins/runtime/runtime-registry-loader.js", () => ({ - ensurePluginRegistryLoaded: pluginRegistryMocks.ensurePluginRegistryLoaded, -})); - const execFileAsync = promisify(execFile); const runtime = createThrowingTestRuntime(); const sessionKey = "agent:main:worktree-race";