

























@@ -6,11 +6,17 @@ import { loadSessionStore, saveSessionStore, type SessionEntry } from "../config
66import { CURRENT_SESSION_VERSION } from "../config/sessions/version.js";
77import type { OpenClawConfig } from "../config/types.openclaw.js";
889+type ProviderModelNormalizationParams = { provider: string; context: { modelId: string } };
10+911const state = vi.hoisted(() => ({
1012cfg: undefined as OpenClawConfig | undefined,
1113workspaceDir: undefined as string | undefined,
1214agentDir: undefined as string | undefined,
1315runAgentAttemptMock: vi.fn(),
16+loadManifestModelCatalogMock: vi.fn(() => []),
17+normalizeProviderModelIdWithRuntimeMock: vi.fn(
18+(_params: ProviderModelNormalizationParams) => undefined,
19+),
1420deliveryFreshEntries: [] as Array<SessionEntry | undefined>,
1521}));
1622@@ -51,7 +57,14 @@ vi.mock("../plugins/manifest-contract-eligibility.js", () => ({
5157}));
52585359vi.mock("./model-catalog.js", () => ({
54-loadManifestModelCatalog: () => [],
60+loadManifestModelCatalog: (...args: unknown[]) => state.loadManifestModelCatalogMock(...args),
61+}));
62+63+vi.mock("./provider-model-normalization.runtime.js", () => ({
64+normalizeProviderModelIdWithRuntime: (params: {
65+provider: string;
66+context: { modelId: string };
67+}) => state.normalizeProviderModelIdWithRuntimeMock(params),
5568}));
56695770vi.mock("./harness/runtime-plugin.js", () => ({
@@ -145,6 +158,8 @@ beforeAll(async () => {
145158146159beforeEach(async () => {
147160vi.clearAllMocks();
161+state.loadManifestModelCatalogMock.mockReturnValue([]);
162+state.normalizeProviderModelIdWithRuntimeMock.mockImplementation(() => undefined);
148163state.deliveryFreshEntries = [];
149164const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-rotation-e2e-"));
150165state.workspaceDir = path.join(tmpDir, "workspace");
@@ -223,6 +238,78 @@ function requireStorePath(): string {
223238}
224239225240describe("agentCommand compaction transcript rotation", () => {
241+it("does not re-normalize an exact configured custom provider through plugin runtime", async () => {
242+state.normalizeProviderModelIdWithRuntimeMock.mockImplementation(
243+({ provider }: ProviderModelNormalizationParams) => {
244+if (provider === "tui-pty-mock") {
245+throw new Error("custom provider should not use plugin runtime normalization");
246+}
247+return undefined;
248+},
249+);
250+state.cfg = {
251+ ...state.cfg,
252+plugins: {
253+enabled: false,
254+},
255+agents: {
256+defaults: {
257+model: { primary: "tui-pty-mock/gpt-5.5" },
258+models: {
259+"tui-pty-mock/gpt-5.5": {},
260+},
261+},
262+},
263+models: {
264+mode: "replace",
265+providers: {
266+"tui-pty-mock": {
267+baseUrl: "http://127.0.0.1:9/v1",
268+apiKey: "test",
269+request: { allowPrivateNetwork: true },
270+models: [
271+{
272+id: "gpt-5.5",
273+name: "GPT 5.5",
274+api: "openai-responses",
275+reasoning: true,
276+input: ["text"],
277+cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
278+contextWindow: 128_000,
279+maxTokens: 16_384,
280+},
281+],
282+},
283+},
284+},
285+} as OpenClawConfig;
286+state.runAgentAttemptMock.mockResolvedValueOnce(
287+makeResult({
288+sessionId: "custom-provider-session",
289+text: "custom answer",
290+}),
291+);
292+293+await agentCommand({
294+message: "custom provider prompt",
295+sessionId: "custom-provider-session",
296+cwd: state.workspaceDir,
297+});
298+299+const attempt = state.runAgentAttemptMock.mock.calls[0]?.[0] as
300+| { providerOverride?: string; modelOverride?: string; pluginsEnabled?: boolean }
301+| undefined;
302+expect(attempt).toMatchObject({
303+providerOverride: "tui-pty-mock",
304+modelOverride: "gpt-5.5",
305+pluginsEnabled: false,
306+});
307+expect(state.normalizeProviderModelIdWithRuntimeMock).not.toHaveBeenCalledWith(
308+expect.objectContaining({ provider: "tui-pty-mock" }),
309+);
310+expect(state.loadManifestModelCatalogMock).not.toHaveBeenCalled();
311+});
312+226313it("keeps sessions.json on the rotated successor", async () => {
227314const storePath = requireStorePath();
228315const sessionsDir = await fs.realpath(path.dirname(storePath));
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。