diff --git a/src/agents/workspace.ts b/src/agents/workspace.ts index 57bb14fae68..486dff87cc0 100644 --- a/src/agents/workspace.ts +++ b/src/agents/workspace.ts @@ -30,6 +30,9 @@ export const DEFAULT_BOOTSTRAP_FILENAME = "BOOTSTRAP.md"; export const DEFAULT_MEMORY_FILENAME = "MEMORY.md"; export const DEFAULT_MEMORY_ALT_FILENAME = "memory.md"; +const workspaceTemplateCache = new Map>(); +let gitAvailabilityPromise: Promise | null = null; + function stripFrontMatter(content: string): string { if (!content.startsWith("---")) { return content; @@ -45,15 +48,30 @@ function stripFrontMatter(content: string): string { } async function loadTemplate(name: string): Promise { - const templateDir = await resolveWorkspaceTemplateDir(); - const templatePath = path.join(templateDir, name); + const cached = workspaceTemplateCache.get(name); + if (cached) { + return cached; + } + + const pending = (async () => { + const templateDir = await resolveWorkspaceTemplateDir(); + const templatePath = path.join(templateDir, name); + try { + const content = await fs.readFile(templatePath, "utf-8"); + return stripFrontMatter(content); + } catch { + throw new Error( + `Missing workspace template: ${name} (${templatePath}). Ensure docs/reference/templates are packaged.`, + ); + } + })(); + + workspaceTemplateCache.set(name, pending); try { - const content = await fs.readFile(templatePath, "utf-8"); - return stripFrontMatter(content); - } catch { - throw new Error( - `Missing workspace template: ${name} (${templatePath}). Ensure docs/reference/templates are packaged.`, - ); + return await pending; + } catch (error) { + workspaceTemplateCache.delete(name); + throw error; } } @@ -99,12 +117,20 @@ async function hasGitRepo(dir: string): Promise { } async function isGitAvailable(): Promise { - try { - const result = await runCommandWithTimeout(["git", "--version"], { timeoutMs: 2_000 }); - return result.code === 0; - } catch { - return false; + if (gitAvailabilityPromise) { + return gitAvailabilityPromise; } + + gitAvailabilityPromise = (async () => { + try { + const result = await runCommandWithTimeout(["git", "--version"], { timeoutMs: 2_000 }); + return result.code === 0; + } catch { + return false; + } + })(); + + return gitAvailabilityPromise; } async function ensureGitRepo(dir: string, isBrandNewWorkspace: boolean) { diff --git a/src/commands/auth-choice.default-model.e2e.test.ts b/src/commands/auth-choice.default-model.e2e.test.ts deleted file mode 100644 index cea387d705d..00000000000 --- a/src/commands/auth-choice.default-model.e2e.test.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { describe, expect, it, vi } from "vitest"; -import type { OpenClawConfig } from "../config/config.js"; -import type { WizardPrompter } from "../wizard/prompts.js"; -import { applyDefaultModelChoice } from "./auth-choice.default-model.js"; - -function makePrompter(): WizardPrompter { - return { - intro: async () => {}, - outro: async () => {}, - note: async () => {}, - select: async () => "", - multiselect: async () => [], - text: async () => "", - confirm: async () => false, - progress: () => ({ update: () => {}, stop: () => {} }), - }; -} - -describe("applyDefaultModelChoice", () => { - it("ensures allowlist entry exists when returning an agent override", async () => { - const defaultModel = "vercel-ai-gateway/anthropic/claude-opus-4.6"; - const noteAgentModel = vi.fn(async () => {}); - const applied = await applyDefaultModelChoice({ - config: {}, - setDefaultModel: false, - defaultModel, - // Simulate a provider function that does not explicitly add the entry. - applyProviderConfig: (config: OpenClawConfig) => config, - applyDefaultConfig: (config: OpenClawConfig) => config, - noteAgentModel, - prompter: makePrompter(), - }); - - expect(noteAgentModel).toHaveBeenCalledWith(defaultModel); - expect(applied.agentModelOverride).toBe(defaultModel); - expect(applied.config.agents?.defaults?.models?.[defaultModel]).toEqual({}); - }); - - it("adds canonical allowlist key for anthropic aliases", async () => { - const defaultModel = "anthropic/opus-4.6"; - const applied = await applyDefaultModelChoice({ - config: {}, - setDefaultModel: false, - defaultModel, - applyProviderConfig: (config: OpenClawConfig) => config, - applyDefaultConfig: (config: OpenClawConfig) => config, - noteAgentModel: async () => {}, - prompter: makePrompter(), - }); - - expect(applied.config.agents?.defaults?.models?.[defaultModel]).toEqual({}); - expect(applied.config.agents?.defaults?.models?.["anthropic/claude-opus-4-6"]).toEqual({}); - }); - - it("uses applyDefaultConfig path when setDefaultModel is true", async () => { - const defaultModel = "openai/gpt-5.1-codex"; - const applied = await applyDefaultModelChoice({ - config: {}, - setDefaultModel: true, - defaultModel, - applyProviderConfig: (config: OpenClawConfig) => config, - applyDefaultConfig: () => ({ - agents: { - defaults: { - model: { primary: defaultModel }, - }, - }, - }), - noteDefault: defaultModel, - noteAgentModel: async () => {}, - prompter: makePrompter(), - }); - - expect(applied.agentModelOverride).toBeUndefined(); - expect(applied.config.agents?.defaults?.model).toEqual({ primary: defaultModel }); - }); -}); diff --git a/src/commands/google-gemini-model-default.e2e.test.ts b/src/commands/google-gemini-model-default.e2e.test.ts deleted file mode 100644 index 82df89bc785..00000000000 --- a/src/commands/google-gemini-model-default.e2e.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { OpenClawConfig } from "../config/config.js"; -import { - applyGoogleGeminiModelDefault, - GOOGLE_GEMINI_DEFAULT_MODEL, -} from "./google-gemini-model-default.js"; - -describe("applyGoogleGeminiModelDefault", () => { - it("sets gemini default when model is unset", () => { - const cfg: OpenClawConfig = { agents: { defaults: {} } }; - const applied = applyGoogleGeminiModelDefault(cfg); - expect(applied.changed).toBe(true); - expect(applied.next.agents?.defaults?.model).toEqual({ - primary: GOOGLE_GEMINI_DEFAULT_MODEL, - }); - }); - - it("overrides existing model", () => { - const cfg: OpenClawConfig = { - agents: { defaults: { model: "anthropic/claude-opus-4-5" } }, - }; - const applied = applyGoogleGeminiModelDefault(cfg); - expect(applied.changed).toBe(true); - expect(applied.next.agents?.defaults?.model).toEqual({ - primary: GOOGLE_GEMINI_DEFAULT_MODEL, - }); - }); - - it("no-ops when already gemini default", () => { - const cfg: OpenClawConfig = { - agents: { defaults: { model: GOOGLE_GEMINI_DEFAULT_MODEL } }, - }; - const applied = applyGoogleGeminiModelDefault(cfg); - expect(applied.changed).toBe(false); - expect(applied.next).toEqual(cfg); - }); -}); diff --git a/src/commands/openai-codex-model-default.e2e.test.ts b/src/commands/openai-codex-model-default.e2e.test.ts deleted file mode 100644 index ac8ceccd381..00000000000 --- a/src/commands/openai-codex-model-default.e2e.test.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { OpenClawConfig } from "../config/config.js"; -import { - applyOpenAICodexModelDefault, - OPENAI_CODEX_DEFAULT_MODEL, -} from "./openai-codex-model-default.js"; -import { OPENAI_DEFAULT_MODEL } from "./openai-model-default.js"; - -describe("applyOpenAICodexModelDefault", () => { - it("sets openai-codex default when model is unset", () => { - const cfg: OpenClawConfig = { agents: { defaults: {} } }; - const applied = applyOpenAICodexModelDefault(cfg); - expect(applied.changed).toBe(true); - expect(applied.next.agents?.defaults?.model).toEqual({ - primary: OPENAI_CODEX_DEFAULT_MODEL, - }); - }); - - it("sets openai-codex default when model is openai/*", () => { - const cfg: OpenClawConfig = { - agents: { defaults: { model: OPENAI_DEFAULT_MODEL } }, - }; - const applied = applyOpenAICodexModelDefault(cfg); - expect(applied.changed).toBe(true); - expect(applied.next.agents?.defaults?.model).toEqual({ - primary: OPENAI_CODEX_DEFAULT_MODEL, - }); - }); - - it("does not override openai-codex/*", () => { - const cfg: OpenClawConfig = { - agents: { defaults: { model: OPENAI_CODEX_DEFAULT_MODEL } }, - }; - const applied = applyOpenAICodexModelDefault(cfg); - expect(applied.changed).toBe(false); - expect(applied.next).toEqual(cfg); - }); - - it("does not override non-openai models", () => { - const cfg: OpenClawConfig = { - agents: { defaults: { model: "anthropic/claude-opus-4-5" } }, - }; - const applied = applyOpenAICodexModelDefault(cfg); - expect(applied.changed).toBe(false); - expect(applied.next).toEqual(cfg); - }); -}); diff --git a/src/commands/openai-model-default.e2e.test.ts b/src/commands/openai-model-default.e2e.test.ts index 4065e2ac338..b270b1008f2 100644 --- a/src/commands/openai-model-default.e2e.test.ts +++ b/src/commands/openai-model-default.e2e.test.ts @@ -1,9 +1,128 @@ -import { describe, expect, it } from "vitest"; +import { describe, expect, it, vi } from "vitest"; +import type { OpenClawConfig } from "../config/config.js"; +import type { WizardPrompter } from "../wizard/prompts.js"; +import { applyDefaultModelChoice } from "./auth-choice.default-model.js"; +import { + applyGoogleGeminiModelDefault, + GOOGLE_GEMINI_DEFAULT_MODEL, +} from "./google-gemini-model-default.js"; +import { + applyOpenAICodexModelDefault, + OPENAI_CODEX_DEFAULT_MODEL, +} from "./openai-codex-model-default.js"; import { applyOpenAIConfig, applyOpenAIProviderConfig, OPENAI_DEFAULT_MODEL, } from "./openai-model-default.js"; +import { + applyOpencodeZenModelDefault, + OPENCODE_ZEN_DEFAULT_MODEL, +} from "./opencode-zen-model-default.js"; + +function makePrompter(): WizardPrompter { + return { + intro: async () => {}, + outro: async () => {}, + note: async () => {}, + select: async () => "", + multiselect: async () => [], + text: async () => "", + confirm: async () => false, + progress: () => ({ update: () => {}, stop: () => {} }), + }; +} + +describe("applyDefaultModelChoice", () => { + it("ensures allowlist entry exists when returning an agent override", async () => { + const defaultModel = "vercel-ai-gateway/anthropic/claude-opus-4.6"; + const noteAgentModel = vi.fn(async () => {}); + const applied = await applyDefaultModelChoice({ + config: {}, + setDefaultModel: false, + defaultModel, + // Simulate a provider function that does not explicitly add the entry. + applyProviderConfig: (config: OpenClawConfig) => config, + applyDefaultConfig: (config: OpenClawConfig) => config, + noteAgentModel, + prompter: makePrompter(), + }); + + expect(noteAgentModel).toHaveBeenCalledWith(defaultModel); + expect(applied.agentModelOverride).toBe(defaultModel); + expect(applied.config.agents?.defaults?.models?.[defaultModel]).toEqual({}); + }); + + it("adds canonical allowlist key for anthropic aliases", async () => { + const defaultModel = "anthropic/opus-4.6"; + const applied = await applyDefaultModelChoice({ + config: {}, + setDefaultModel: false, + defaultModel, + applyProviderConfig: (config: OpenClawConfig) => config, + applyDefaultConfig: (config: OpenClawConfig) => config, + noteAgentModel: async () => {}, + prompter: makePrompter(), + }); + + expect(applied.config.agents?.defaults?.models?.[defaultModel]).toEqual({}); + expect(applied.config.agents?.defaults?.models?.["anthropic/claude-opus-4-6"]).toEqual({}); + }); + + it("uses applyDefaultConfig path when setDefaultModel is true", async () => { + const defaultModel = "openai/gpt-5.1-codex"; + const applied = await applyDefaultModelChoice({ + config: {}, + setDefaultModel: true, + defaultModel, + applyProviderConfig: (config: OpenClawConfig) => config, + applyDefaultConfig: () => ({ + agents: { + defaults: { + model: { primary: defaultModel }, + }, + }, + }), + noteDefault: defaultModel, + noteAgentModel: async () => {}, + prompter: makePrompter(), + }); + + expect(applied.agentModelOverride).toBeUndefined(); + expect(applied.config.agents?.defaults?.model).toEqual({ primary: defaultModel }); + }); +}); + +describe("applyGoogleGeminiModelDefault", () => { + it("sets gemini default when model is unset", () => { + const cfg: OpenClawConfig = { agents: { defaults: {} } }; + const applied = applyGoogleGeminiModelDefault(cfg); + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: GOOGLE_GEMINI_DEFAULT_MODEL, + }); + }); + + it("overrides existing model", () => { + const cfg: OpenClawConfig = { + agents: { defaults: { model: "anthropic/claude-opus-4-5" } }, + }; + const applied = applyGoogleGeminiModelDefault(cfg); + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: GOOGLE_GEMINI_DEFAULT_MODEL, + }); + }); + + it("no-ops when already gemini default", () => { + const cfg: OpenClawConfig = { + agents: { defaults: { model: GOOGLE_GEMINI_DEFAULT_MODEL } }, + }; + const applied = applyGoogleGeminiModelDefault(cfg); + expect(applied.changed).toBe(false); + expect(applied.next).toEqual(cfg); + }); +}); describe("applyOpenAIProviderConfig", () => { it("adds allowlist entry for default model", () => { @@ -38,3 +157,102 @@ describe("applyOpenAIConfig", () => { expect(next.agents?.defaults?.model).toEqual({ primary: OPENAI_DEFAULT_MODEL, fallback: [] }); }); }); + +describe("applyOpenAICodexModelDefault", () => { + it("sets openai-codex default when model is unset", () => { + const cfg: OpenClawConfig = { agents: { defaults: {} } }; + const applied = applyOpenAICodexModelDefault(cfg); + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: OPENAI_CODEX_DEFAULT_MODEL, + }); + }); + + it("sets openai-codex default when model is openai/*", () => { + const cfg: OpenClawConfig = { + agents: { defaults: { model: OPENAI_DEFAULT_MODEL } }, + }; + const applied = applyOpenAICodexModelDefault(cfg); + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: OPENAI_CODEX_DEFAULT_MODEL, + }); + }); + + it("does not override openai-codex/*", () => { + const cfg: OpenClawConfig = { + agents: { defaults: { model: OPENAI_CODEX_DEFAULT_MODEL } }, + }; + const applied = applyOpenAICodexModelDefault(cfg); + expect(applied.changed).toBe(false); + expect(applied.next).toEqual(cfg); + }); + + it("does not override non-openai models", () => { + const cfg: OpenClawConfig = { + agents: { defaults: { model: "anthropic/claude-opus-4-5" } }, + }; + const applied = applyOpenAICodexModelDefault(cfg); + expect(applied.changed).toBe(false); + expect(applied.next).toEqual(cfg); + }); +}); + +describe("applyOpencodeZenModelDefault", () => { + it("sets opencode default when model is unset", () => { + const cfg: OpenClawConfig = { agents: { defaults: {} } }; + const applied = applyOpencodeZenModelDefault(cfg); + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: OPENCODE_ZEN_DEFAULT_MODEL, + }); + }); + + it("overrides existing model", () => { + const cfg = { + agents: { defaults: { model: "anthropic/claude-opus-4-5" } }, + } as OpenClawConfig; + const applied = applyOpencodeZenModelDefault(cfg); + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: OPENCODE_ZEN_DEFAULT_MODEL, + }); + }); + + it("no-ops when already opencode-zen default", () => { + const cfg = { + agents: { defaults: { model: OPENCODE_ZEN_DEFAULT_MODEL } }, + } as OpenClawConfig; + const applied = applyOpencodeZenModelDefault(cfg); + expect(applied.changed).toBe(false); + expect(applied.next).toEqual(cfg); + }); + + it("no-ops when already legacy opencode-zen default", () => { + const cfg = { + agents: { defaults: { model: "opencode-zen/claude-opus-4-5" } }, + } as OpenClawConfig; + const applied = applyOpencodeZenModelDefault(cfg); + expect(applied.changed).toBe(false); + expect(applied.next).toEqual(cfg); + }); + + it("preserves fallbacks when setting primary", () => { + const cfg: OpenClawConfig = { + agents: { + defaults: { + model: { + primary: "anthropic/claude-opus-4-5", + fallbacks: ["google/gemini-3-pro"], + }, + }, + }, + }; + const applied = applyOpencodeZenModelDefault(cfg); + expect(applied.changed).toBe(true); + expect(applied.next.agents?.defaults?.model).toEqual({ + primary: OPENCODE_ZEN_DEFAULT_MODEL, + fallbacks: ["google/gemini-3-pro"], + }); + }); +}); diff --git a/src/commands/opencode-zen-model-default.e2e.test.ts b/src/commands/opencode-zen-model-default.e2e.test.ts deleted file mode 100644 index 67fbc5b9d80..00000000000 --- a/src/commands/opencode-zen-model-default.e2e.test.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { describe, expect, it } from "vitest"; -import type { OpenClawConfig } from "../config/config.js"; -import { - applyOpencodeZenModelDefault, - OPENCODE_ZEN_DEFAULT_MODEL, -} from "./opencode-zen-model-default.js"; - -describe("applyOpencodeZenModelDefault", () => { - it("sets opencode default when model is unset", () => { - const cfg: OpenClawConfig = { agents: { defaults: {} } }; - const applied = applyOpencodeZenModelDefault(cfg); - expect(applied.changed).toBe(true); - expect(applied.next.agents?.defaults?.model).toEqual({ - primary: OPENCODE_ZEN_DEFAULT_MODEL, - }); - }); - - it("overrides existing model", () => { - const cfg = { - agents: { defaults: { model: "anthropic/claude-opus-4-5" } }, - } as OpenClawConfig; - const applied = applyOpencodeZenModelDefault(cfg); - expect(applied.changed).toBe(true); - expect(applied.next.agents?.defaults?.model).toEqual({ - primary: OPENCODE_ZEN_DEFAULT_MODEL, - }); - }); - - it("no-ops when already opencode-zen default", () => { - const cfg = { - agents: { defaults: { model: OPENCODE_ZEN_DEFAULT_MODEL } }, - } as OpenClawConfig; - const applied = applyOpencodeZenModelDefault(cfg); - expect(applied.changed).toBe(false); - expect(applied.next).toEqual(cfg); - }); - - it("no-ops when already legacy opencode-zen default", () => { - const cfg = { - agents: { defaults: { model: "opencode-zen/claude-opus-4-5" } }, - } as OpenClawConfig; - const applied = applyOpencodeZenModelDefault(cfg); - expect(applied.changed).toBe(false); - expect(applied.next).toEqual(cfg); - }); - - it("preserves fallbacks when setting primary", () => { - const cfg: OpenClawConfig = { - agents: { - defaults: { - model: { - primary: "anthropic/claude-opus-4-5", - fallbacks: ["google/gemini-3-pro"], - }, - }, - }, - }; - const applied = applyOpencodeZenModelDefault(cfg); - expect(applied.changed).toBe(true); - expect(applied.next.agents?.defaults?.model).toEqual({ - primary: OPENCODE_ZEN_DEFAULT_MODEL, - fallbacks: ["google/gemini-3-pro"], - }); - }); -}); diff --git a/src/config/config.broadcast.test.ts b/src/config/config.broadcast.test.ts index 614e91e64a9..cab0cdf12b1 100644 --- a/src/config/config.broadcast.test.ts +++ b/src/config/config.broadcast.test.ts @@ -1,9 +1,8 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; +import { validateConfigObject } from "./config.js"; describe("broadcast", () => { - it("accepts a broadcast peer map with strategy", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("accepts a broadcast peer map with strategy", () => { const res = validateConfigObject({ agents: { list: [{ id: "alfred" }, { id: "baerbel" }], @@ -16,18 +15,14 @@ describe("broadcast", () => { expect(res.ok).toBe(true); }); - it("rejects invalid broadcast strategy", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("rejects invalid broadcast strategy", () => { const res = validateConfigObject({ broadcast: { strategy: "nope" }, }); expect(res.ok).toBe(false); }); - it("rejects non-array broadcast entries", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("rejects non-array broadcast entries", () => { const res = validateConfigObject({ broadcast: { "120363403215116621@g.us": 123 }, }); diff --git a/src/config/config.gateway-remote-transport.test.ts b/src/config/config.gateway-remote-transport.test.ts index f1ed6f62d78..a729a163772 100644 --- a/src/config/config.gateway-remote-transport.test.ts +++ b/src/config/config.gateway-remote-transport.test.ts @@ -1,9 +1,8 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; +import { validateConfigObject } from "./config.js"; describe("gateway.remote.transport", () => { - it("accepts direct transport", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("accepts direct transport", () => { const res = validateConfigObject({ gateway: { remote: { @@ -15,9 +14,7 @@ describe("gateway.remote.transport", () => { expect(res.ok).toBe(true); }); - it("rejects unknown transport", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("rejects unknown transport", () => { const res = validateConfigObject({ gateway: { remote: { diff --git a/src/config/config.gateway-tools-config.test.ts b/src/config/config.gateway-tools-config.test.ts index 87531ffc426..022dfc79abd 100644 --- a/src/config/config.gateway-tools-config.test.ts +++ b/src/config/config.gateway-tools-config.test.ts @@ -1,9 +1,8 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; +import { validateConfigObject } from "./config.js"; describe("gateway.tools config", () => { - it("accepts gateway.tools allow and deny lists", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("accepts gateway.tools allow and deny lists", () => { const res = validateConfigObject({ gateway: { tools: { @@ -15,9 +14,7 @@ describe("gateway.tools config", () => { expect(res.ok).toBe(true); }); - it("rejects invalid gateway.tools values", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("rejects invalid gateway.tools values", () => { const res = validateConfigObject({ gateway: { tools: { diff --git a/src/config/config.msteams.test.ts b/src/config/config.msteams.test.ts index dfc739620fc..aef0fe8bf08 100644 --- a/src/config/config.msteams.test.ts +++ b/src/config/config.msteams.test.ts @@ -1,9 +1,8 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; +import { validateConfigObject } from "./config.js"; describe("config msteams", () => { - it("accepts replyStyle at global/team/channel levels", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("accepts replyStyle at global/team/channel levels", () => { const res = validateConfigObject({ channels: { msteams: { @@ -29,9 +28,7 @@ describe("config msteams", () => { } }); - it("rejects invalid replyStyle", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("rejects invalid replyStyle", () => { const res = validateConfigObject({ channels: { msteams: { replyStyle: "nope" } }, }); diff --git a/src/config/config.sandbox-docker.test.ts b/src/config/config.sandbox-docker.test.ts index 2bc0ff43e0f..9f3d66e1571 100644 --- a/src/config/config.sandbox-docker.test.ts +++ b/src/config/config.sandbox-docker.test.ts @@ -1,9 +1,8 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; +import { validateConfigObject } from "./config.js"; describe("sandbox docker config", () => { - it("accepts binds array in sandbox.docker config", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("accepts binds array in sandbox.docker config", () => { const res = validateConfigObject({ agents: { defaults: { @@ -38,9 +37,7 @@ describe("sandbox docker config", () => { } }); - it("rejects non-string values in binds array", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("rejects non-string values in binds array", () => { const res = validateConfigObject({ agents: { defaults: { diff --git a/src/config/config.talk-voicealiases.test.ts b/src/config/config.talk-voicealiases.test.ts index 3b9b677d4c1..e7e32c5b698 100644 --- a/src/config/config.talk-voicealiases.test.ts +++ b/src/config/config.talk-voicealiases.test.ts @@ -1,9 +1,8 @@ -import { describe, expect, it, vi } from "vitest"; +import { describe, expect, it } from "vitest"; +import { validateConfigObject } from "./config.js"; describe("talk.voiceAliases", () => { - it("accepts a string map of voice aliases", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("accepts a string map of voice aliases", () => { const res = validateConfigObject({ talk: { voiceAliases: { @@ -15,9 +14,7 @@ describe("talk.voiceAliases", () => { expect(res.ok).toBe(true); }); - it("rejects non-string voice alias values", async () => { - vi.resetModules(); - const { validateConfigObject } = await import("./config.js"); + it("rejects non-string voice alias values", () => { const res = validateConfigObject({ talk: { voiceAliases: { diff --git a/test/setup.ts b/test/setup.ts index 53e7fe8d151..a7eb44f9ead 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -162,7 +162,6 @@ beforeEach(() => { }); afterEach(() => { - setActivePluginRegistry(createDefaultRegistry()); // Guard against leaked fake timers across test files/workers. vi.useRealTimers(); });