From f05aeaa63a603fb5bc5ef0bac24310a687558ae1 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 12 May 2026 14:22:30 +0900 Subject: [PATCH] test(plugin): remove unsafe test assertions Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/plugin-handlers/config-handler.test.ts | 196 +++++++++--------- .../mcp-config-handler.test.ts | 14 +- src/plugin/chat-message.test.ts | 8 +- src/plugin/event.model-fallback-2941.test.ts | 22 +- .../event.model-fallback-pin-agent.test.ts | 12 +- src/plugin/event.model-fallback.test.ts | 56 ++--- .../fallback.cliproxyapi-matrix.test.ts | 26 +-- src/plugin/hooks/create-session-hooks.test.ts | 4 +- .../tool-execute-before.ulw-loop.test.ts | 22 +- src/plugin/ultrawork-model-override.test.ts | 12 +- 10 files changed, 186 insertions(+), 186 deletions(-) diff --git a/src/plugin-handlers/config-handler.test.ts b/src/plugin-handlers/config-handler.test.ts index d2b0b9ea9..b5608df66 100644 --- a/src/plugin-handlers/config-handler.test.ts +++ b/src/plugin-handlers/config-handler.test.ts @@ -46,36 +46,36 @@ beforeEach(async () => { mock.restore() configErrors.clearConfigLoadErrors() - spyOn(agents, "createBuiltinAgents" as any).mockResolvedValue({ + spyOn(agents, testCoerce("createBuiltinAgents")).mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, oracle: { name: "oracle", prompt: "test", mode: "subagent" }, }) - spyOn(commandLoader, "loadUserCommands" as any).mockResolvedValue({}) - spyOn(commandLoader, "loadProjectCommands" as any).mockResolvedValue({}) - spyOn(commandLoader, "loadOpencodeGlobalCommands" as any).mockResolvedValue({}) - spyOn(commandLoader, "loadOpencodeProjectCommands" as any).mockResolvedValue({}) + spyOn(commandLoader, testCoerce("loadUserCommands")).mockResolvedValue({}) + spyOn(commandLoader, testCoerce("loadProjectCommands")).mockResolvedValue({}) + spyOn(commandLoader, testCoerce("loadOpencodeGlobalCommands")).mockResolvedValue({}) + spyOn(commandLoader, testCoerce("loadOpencodeProjectCommands")).mockResolvedValue({}) - spyOn(builtinCommands, "loadBuiltinCommands" as any).mockReturnValue({}) + spyOn(builtinCommands, testCoerce("loadBuiltinCommands")).mockReturnValue({}) - spyOn(skillLoader, "loadUserSkills" as any).mockResolvedValue({}) - spyOn(skillLoader, "loadProjectSkills" as any).mockResolvedValue({}) - spyOn(skillLoader, "loadOpencodeGlobalSkills" as any).mockResolvedValue({}) - spyOn(skillLoader, "loadOpencodeProjectSkills" as any).mockResolvedValue({}) - spyOn(skillLoader, "discoverUserClaudeSkills" as any).mockResolvedValue([]) - spyOn(skillLoader, "discoverProjectClaudeSkills" as any).mockResolvedValue([]) - spyOn(skillLoader, "discoverOpencodeGlobalSkills" as any).mockResolvedValue([]) - spyOn(skillLoader, "discoverOpencodeProjectSkills" as any).mockResolvedValue([]) + spyOn(skillLoader, testCoerce("loadUserSkills")).mockResolvedValue({}) + spyOn(skillLoader, testCoerce("loadProjectSkills")).mockResolvedValue({}) + spyOn(skillLoader, testCoerce("loadOpencodeGlobalSkills")).mockResolvedValue({}) + spyOn(skillLoader, testCoerce("loadOpencodeProjectSkills")).mockResolvedValue({}) + spyOn(skillLoader, testCoerce("discoverUserClaudeSkills")).mockResolvedValue([]) + spyOn(skillLoader, testCoerce("discoverProjectClaudeSkills")).mockResolvedValue([]) + spyOn(skillLoader, testCoerce("discoverOpencodeGlobalSkills")).mockResolvedValue([]) + spyOn(skillLoader, testCoerce("discoverOpencodeProjectSkills")).mockResolvedValue([]) - spyOn(agentLoader, "loadUserAgents" as any).mockReturnValue({}) - spyOn(agentLoader, "loadProjectAgents" as any).mockReturnValue({}) - spyOn(agentLoader, "loadOpencodeGlobalAgents" as any).mockReturnValue({}) - spyOn(agentLoader, "loadOpencodeProjectAgents" as any).mockReturnValue({}) + spyOn(agentLoader, testCoerce("loadUserAgents")).mockReturnValue({}) + spyOn(agentLoader, testCoerce("loadProjectAgents")).mockReturnValue({}) + spyOn(agentLoader, testCoerce("loadOpencodeGlobalAgents")).mockReturnValue({}) + spyOn(agentLoader, testCoerce("loadOpencodeProjectAgents")).mockReturnValue({}) - spyOn(mcpLoader, "loadMcpConfigs" as any).mockResolvedValue({ servers: {} }) + spyOn(mcpLoader, testCoerce("loadMcpConfigs")).mockResolvedValue({ servers: {} }) setAdditionalAllowedMcpEnvVarsSpy = spyOn(mcpLoader, "setAdditionalAllowedMcpEnvVars").mockImplementation(() => {}) - spyOn(pluginLoader, "loadAllPluginComponents" as any).mockResolvedValue({ + spyOn(pluginLoader, testCoerce("loadAllPluginComponents")).mockResolvedValue({ commands: {}, skills: {}, agents: {}, @@ -85,54 +85,54 @@ beforeEach(async () => { errors: [], }) - spyOn(mcpModule, "createBuiltinMcps" as any).mockReturnValue({}) + spyOn(mcpModule, testCoerce("createBuiltinMcps")).mockReturnValue({}) - spyOn(shared, "log" as any).mockImplementation(() => {}) - spyOn(shared, "fetchAvailableModels" as any).mockResolvedValue(new Set(["anthropic/claude-opus-4-7"])) - spyOn(shared, "readConnectedProvidersCache" as any).mockReturnValue(null) + spyOn(shared, testCoerce("log")).mockImplementation(() => {}) + spyOn(shared, testCoerce("fetchAvailableModels")).mockResolvedValue(new Set(["anthropic/claude-opus-4-7"])) + spyOn(shared, testCoerce("readConnectedProvidersCache")).mockReturnValue(null) - spyOn(configDir, "getOpenCodeConfigPaths" as any).mockReturnValue({ + spyOn(configDir, testCoerce("getOpenCodeConfigPaths")).mockReturnValue({ global: "/tmp/.config/opencode", project: "/tmp/.opencode", }) - spyOn(permissionCompat, "migrateAgentConfig" as any).mockImplementation((config: Record) => config) + spyOn(permissionCompat, testCoerce("migrateAgentConfig")).mockImplementation((config: Record) => config) - spyOn(modelResolver, "resolveModelWithFallback" as any).mockReturnValue({ model: "anthropic/claude-opus-4-7" }) + spyOn(modelResolver, testCoerce("resolveModelWithFallback")).mockReturnValue({ model: "anthropic/claude-opus-4-7" }) ;({ createConfigHandler } = await importFreshConfigHandlerModule()) }) afterEach(() => { - (agents.createBuiltinAgents as any)?.mockRestore?.() - ;(sisyphusJunior.createSisyphusJuniorAgentWithOverrides as any)?.mockRestore?.() - ;(commandLoader.loadUserCommands as any)?.mockRestore?.() - ;(commandLoader.loadProjectCommands as any)?.mockRestore?.() - ;(commandLoader.loadOpencodeGlobalCommands as any)?.mockRestore?.() - ;(commandLoader.loadOpencodeProjectCommands as any)?.mockRestore?.() - ;(builtinCommands.loadBuiltinCommands as any)?.mockRestore?.() - ;(skillLoader.loadUserSkills as any)?.mockRestore?.() - ;(skillLoader.loadProjectSkills as any)?.mockRestore?.() - ;(skillLoader.loadOpencodeGlobalSkills as any)?.mockRestore?.() - ;(skillLoader.loadOpencodeProjectSkills as any)?.mockRestore?.() - ;(skillLoader.discoverUserClaudeSkills as any)?.mockRestore?.() - ;(skillLoader.discoverProjectClaudeSkills as any)?.mockRestore?.() - ;(skillLoader.discoverOpencodeGlobalSkills as any)?.mockRestore?.() - ;(skillLoader.discoverOpencodeProjectSkills as any)?.mockRestore?.() - ;(agentLoader.loadUserAgents as any)?.mockRestore?.() - ;(agentLoader.loadProjectAgents as any)?.mockRestore?.() - ;(agentLoader.loadOpencodeGlobalAgents as any)?.mockRestore?.() - ;(agentLoader.loadOpencodeProjectAgents as any)?.mockRestore?.() - ;(mcpLoader.loadMcpConfigs as any)?.mockRestore?.() + (testCoerce(agents.createBuiltinAgents))?.mockRestore?.() + ;(testCoerce(sisyphusJunior.createSisyphusJuniorAgentWithOverrides))?.mockRestore?.() + ;(testCoerce(commandLoader.loadUserCommands))?.mockRestore?.() + ;(testCoerce(commandLoader.loadProjectCommands))?.mockRestore?.() + ;(testCoerce(commandLoader.loadOpencodeGlobalCommands))?.mockRestore?.() + ;(testCoerce(commandLoader.loadOpencodeProjectCommands))?.mockRestore?.() + ;(testCoerce(builtinCommands.loadBuiltinCommands))?.mockRestore?.() + ;(testCoerce(skillLoader.loadUserSkills))?.mockRestore?.() + ;(testCoerce(skillLoader.loadProjectSkills))?.mockRestore?.() + ;(testCoerce(skillLoader.loadOpencodeGlobalSkills))?.mockRestore?.() + ;(testCoerce(skillLoader.loadOpencodeProjectSkills))?.mockRestore?.() + ;(testCoerce(skillLoader.discoverUserClaudeSkills))?.mockRestore?.() + ;(testCoerce(skillLoader.discoverProjectClaudeSkills))?.mockRestore?.() + ;(testCoerce(skillLoader.discoverOpencodeGlobalSkills))?.mockRestore?.() + ;(testCoerce(skillLoader.discoverOpencodeProjectSkills))?.mockRestore?.() + ;(testCoerce(agentLoader.loadUserAgents))?.mockRestore?.() + ;(testCoerce(agentLoader.loadProjectAgents))?.mockRestore?.() + ;(testCoerce(agentLoader.loadOpencodeGlobalAgents))?.mockRestore?.() + ;(testCoerce(agentLoader.loadOpencodeProjectAgents))?.mockRestore?.() + ;(testCoerce(mcpLoader.loadMcpConfigs))?.mockRestore?.() setAdditionalAllowedMcpEnvVarsSpy?.mockRestore() - ;(pluginLoader.loadAllPluginComponents as any)?.mockRestore?.() - ;(mcpModule.createBuiltinMcps as any)?.mockRestore?.() - ;(shared.log as any)?.mockRestore?.() - ;(shared.fetchAvailableModels as any)?.mockRestore?.() - ;(shared.readConnectedProvidersCache as any)?.mockRestore?.() - ;(configDir.getOpenCodeConfigPaths as any)?.mockRestore?.() - ;(permissionCompat.migrateAgentConfig as any)?.mockRestore?.() - ;(modelResolver.resolveModelWithFallback as any)?.mockRestore?.() - ;(agentPriorityOrder.reorderAgentsByPriority as any)?.mockRestore?.() + ;(testCoerce(pluginLoader.loadAllPluginComponents))?.mockRestore?.() + ;(testCoerce(mcpModule.createBuiltinMcps))?.mockRestore?.() + ;(testCoerce(shared.log))?.mockRestore?.() + ;(testCoerce(shared.fetchAvailableModels))?.mockRestore?.() + ;(testCoerce(shared.readConnectedProvidersCache))?.mockRestore?.() + ;(testCoerce(configDir.getOpenCodeConfigPaths))?.mockRestore?.() + ;(testCoerce(permissionCompat.migrateAgentConfig))?.mockRestore?.() + ;(testCoerce(modelResolver.resolveModelWithFallback))?.mockRestore?.() + ;(testCoerce(agentPriorityOrder.reorderAgentsByPriority))?.mockRestore?.() configErrors.clearConfigLoadErrors() mock.restore() }) @@ -230,10 +230,10 @@ describe("MCP env allowlist initialization", () => { describe("Plan agent demote behavior", () => { test("orders core agents as sisyphus -> hephaestus -> prometheus -> atlas", async () => { // #given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void mock: { calls: unknown[][] } - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, hephaestus: { name: "hephaestus", prompt: "test", mode: "primary" }, @@ -275,17 +275,17 @@ describe("Plan agent demote behavior", () => { test("assembles core agents first before priority reorder runs", async () => { // #given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void mock: { calls: unknown[][] } - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, hephaestus: { name: "hephaestus", prompt: "test", mode: "primary" }, oracle: { name: "oracle", prompt: "test", mode: "subagent" }, atlas: { name: "atlas", prompt: "test", mode: "primary" }, }) - const reorderSpy = spyOn(agentPriorityOrder, "reorderAgentsByPriority") as any + const reorderSpy = testCoerce(spyOn(agentPriorityOrder, "reorderAgentsByPriority")) const pluginConfig = createPluginConfig({ sisyphus_agent: { planner_enabled: true, @@ -321,9 +321,9 @@ describe("Plan agent demote behavior", () => { test("backfills runtime core agent names when builtin configs omit name", async () => { // #given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { prompt: "test", mode: "primary" }, hephaestus: { prompt: "test", mode: "primary" }, @@ -485,9 +485,9 @@ describe("Plan agent demote behavior", () => { describe("Agent permission defaults", () => { test("hephaestus should allow task", async () => { // #given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, hephaestus: { name: "hephaestus", prompt: "test", mode: "primary" }, @@ -1054,7 +1054,7 @@ describe("Plan agent model inheritance from prometheus", () => { test("plan agent inherits temperature, reasoningEffort, and other model settings from prometheus", async () => { //#given - prometheus configured with category that has temperature and reasoningEffort - spyOn(shared, "resolveModelPipeline" as any).mockReturnValue({ + spyOn(shared, testCoerce("resolveModelPipeline")).mockReturnValue({ model: "openai/gpt-5.4", provenance: "override", variant: "high", @@ -1109,7 +1109,7 @@ describe("Plan agent model inheritance from prometheus", () => { test("plan agent user override takes priority over prometheus inherited settings", async () => { //#given - prometheus resolves to opus, but user has plan override for gpt-5.4 - spyOn(shared, "resolveModelPipeline" as any).mockReturnValue({ + spyOn(shared, testCoerce("resolveModelPipeline")).mockReturnValue({ model: "anthropic/claude-opus-4-7", provenance: "provider-fallback", variant: "max", @@ -1152,7 +1152,7 @@ describe("Plan agent model inheritance from prometheus", () => { test("plan agent does NOT inherit prompt, description, or color from prometheus", async () => { //#given - spyOn(shared, "resolveModelPipeline" as any).mockReturnValue({ + spyOn(shared, testCoerce("resolveModelPipeline")).mockReturnValue({ model: "anthropic/claude-opus-4-7", provenance: "provider-fallback", variant: "max", @@ -1229,8 +1229,8 @@ describe("Deadlock prevention - fetchAvailableModels must not receive client", ( describe("config-handler plugin loading error boundary (#1559)", () => { test("returns empty defaults when loadAllPluginComponents throws", async () => { //#given - ;(pluginLoader.loadAllPluginComponents as any).mockRestore?.() - spyOn(pluginLoader, "loadAllPluginComponents" as any).mockRejectedValue(new Error("crash")) + ;(testCoerce(pluginLoader.loadAllPluginComponents)).mockRestore?.() + spyOn(pluginLoader, testCoerce("loadAllPluginComponents")).mockRejectedValue(new Error("crash")) const pluginConfig = createPluginConfig({}) const config: Record = { model: "anthropic/claude-opus-4-7", @@ -1255,8 +1255,8 @@ describe("config-handler plugin loading error boundary (#1559)", () => { test("returns empty defaults when loadAllPluginComponents times out", async () => { //#given - ;(pluginLoader.loadAllPluginComponents as any).mockRestore?.() - spyOn(pluginLoader, "loadAllPluginComponents" as any).mockImplementation( + ;(testCoerce(pluginLoader.loadAllPluginComponents)).mockRestore?.() + spyOn(pluginLoader, testCoerce("loadAllPluginComponents")).mockImplementation( () => new Promise(() => {}) ) const pluginConfig = createPluginConfig({ @@ -1285,8 +1285,8 @@ describe("config-handler plugin loading error boundary (#1559)", () => { test("records a config load error when loadAllPluginComponents fails", async () => { //#given - ;(pluginLoader.loadAllPluginComponents as any).mockRestore?.() - spyOn(pluginLoader, "loadAllPluginComponents" as any).mockRejectedValue(new Error("crash")) + ;(testCoerce(pluginLoader.loadAllPluginComponents)).mockRestore?.() + spyOn(pluginLoader, testCoerce("loadAllPluginComponents")).mockRejectedValue(new Error("crash")) const pluginConfig = createPluginConfig({}) const config: Record = { model: "anthropic/claude-opus-4-7", @@ -1314,8 +1314,8 @@ describe("config-handler plugin loading error boundary (#1559)", () => { test("passes through plugin data on successful load (identity test)", async () => { //#given - ;(pluginLoader.loadAllPluginComponents as any).mockRestore?.() - spyOn(pluginLoader, "loadAllPluginComponents" as any).mockResolvedValue({ + ;(testCoerce(pluginLoader.loadAllPluginComponents)).mockRestore?.() + spyOn(pluginLoader, testCoerce("loadAllPluginComponents")).mockResolvedValue({ commands: { "test-cmd": { description: "test", template: "test" } }, skills: {}, agents: {}, @@ -1351,16 +1351,16 @@ describe("config-handler plugin loading error boundary (#1559)", () => { describe("command agent routing coherence", () => { test("keeps start-work aligned with the exported Atlas list key opencode matches exactly", async () => { //#given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, atlas: { name: "atlas", prompt: "test", mode: "primary" }, }) - ;(builtinCommands.loadBuiltinCommands as unknown as { + ;(testCoerce<{ mockReturnValue: (value: Record) => void - }).mockReturnValue({ + }>(builtinCommands.loadBuiltinCommands)).mockReturnValue({ "start-work": { name: "start-work", description: "(builtin) Start work", @@ -1404,9 +1404,9 @@ describe("per-agent todowrite/todoread deny when task_system enabled", () => { test("denies todowrite and todoread for primary agents when task_system is enabled", async () => { //#given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, hephaestus: { name: "hephaestus", prompt: "test", mode: "primary" }, @@ -1445,10 +1445,10 @@ describe("per-agent todowrite/todoread deny when task_system enabled", () => { test("does not deny todowrite/todoread when task_system is disabled", async () => { //#given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void mock: { calls: unknown[][] } - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, hephaestus: { name: "hephaestus", prompt: "test", mode: "primary" }, @@ -1487,10 +1487,10 @@ describe("per-agent todowrite/todoread deny when task_system enabled", () => { test("does not deny todowrite/todoread when task_system is undefined", async () => { //#given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void mock: { calls: unknown[][] } - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "test", mode: "primary" }, }) @@ -1526,10 +1526,10 @@ describe("per-agent todowrite/todoread deny when task_system enabled", () => { describe("disable_omo_env pass-through", () => { test("passes disable_omo_env=true to createBuiltinAgents", async () => { //#given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void mock: { calls: unknown[][] } - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "without-env", mode: "primary" }, }) @@ -1563,10 +1563,10 @@ describe("disable_omo_env pass-through", () => { test("passes disable_omo_env=false to createBuiltinAgents when omitted", async () => { //#given - const createBuiltinAgentsMock = agents.createBuiltinAgents as unknown as { + const createBuiltinAgentsMock = testCoerce<{ mockResolvedValue: (value: Record) => void mock: { calls: unknown[][] } - } + }>(agents.createBuiltinAgents) createBuiltinAgentsMock.mockResolvedValue({ sisyphus: { name: "sisyphus", prompt: "with-env", mode: "primary" }, }) @@ -1600,14 +1600,14 @@ describe("disable_omo_env pass-through", () => { describe("Agent merge priority — project-local overrides global", () => { test("project-local Claude agent overrides global Claude agent with same name", async () => { // #given — same agent name in both global (user) and project scopes - ;(agentLoader.loadUserAgents as any).mockReturnValue({ + ;(testCoerce(agentLoader.loadUserAgents)).mockReturnValue({ "my-custom-agent": { description: "(user) global version", mode: "subagent", prompt: "I am the global agent", }, }) - ;(agentLoader.loadProjectAgents as any).mockReturnValue({ + ;(testCoerce(agentLoader.loadProjectAgents)).mockReturnValue({ "my-custom-agent": { description: "(project) project version", mode: "subagent", @@ -1640,14 +1640,14 @@ describe("Agent merge priority — project-local overrides global", () => { test("opencode project agent overrides opencode global agent with same name", async () => { // #given — same agent name in opencode global vs opencode project - ;(agentLoader.loadOpencodeGlobalAgents as any).mockReturnValue({ + ;(testCoerce(agentLoader.loadOpencodeGlobalAgents)).mockReturnValue({ "my-custom-agent": { description: "(opencode) global version", mode: "subagent", prompt: "I am the opencode global agent", }, }) - ;(agentLoader.loadOpencodeProjectAgents as any).mockReturnValue({ + ;(testCoerce(agentLoader.loadOpencodeProjectAgents)).mockReturnValue({ "my-custom-agent": { description: "(opencode-project) project version", mode: "subagent", @@ -1680,14 +1680,14 @@ describe("Agent merge priority — project-local overrides global", () => { test("project Claude agent overrides opencode global agent with same name", async () => { // #given — project-scope Claude agent vs global-scope opencode agent - ;(agentLoader.loadOpencodeGlobalAgents as any).mockReturnValue({ + ;(testCoerce(agentLoader.loadOpencodeGlobalAgents)).mockReturnValue({ "my-custom-agent": { description: "(opencode) global version", mode: "subagent", prompt: "I am the opencode global agent", }, }) - ;(agentLoader.loadProjectAgents as any).mockReturnValue({ + ;(testCoerce(agentLoader.loadProjectAgents)).mockReturnValue({ "my-custom-agent": { description: "(project) project version", mode: "subagent", @@ -1720,7 +1720,7 @@ describe("Agent merge priority — project-local overrides global", () => { test("plugin agents have lowest priority — overridden by all other sources", async () => { // #given — same agent in plugin, global, and project scopes - ;(pluginLoader.loadAllPluginComponents as any).mockResolvedValue({ + ;(testCoerce(pluginLoader.loadAllPluginComponents)).mockResolvedValue({ commands: {}, skills: {}, agents: { @@ -1735,7 +1735,7 @@ describe("Agent merge priority — project-local overrides global", () => { plugins: [], errors: [], }) - ;(agentLoader.loadUserAgents as any).mockReturnValue({ + ;(testCoerce(agentLoader.loadUserAgents)).mockReturnValue({ "my-custom-agent": { description: "(user) global version", mode: "subagent", diff --git a/src/plugin-handlers/mcp-config-handler.test.ts b/src/plugin-handlers/mcp-config-handler.test.ts index f9fc6472f..a22cc827f 100644 --- a/src/plugin-handlers/mcp-config-handler.test.ts +++ b/src/plugin-handlers/mcp-config-handler.test.ts @@ -11,17 +11,17 @@ let loadMcpConfigsSpy: ReturnType let createBuiltinMcpsSpy: ReturnType beforeEach(() => { - loadMcpConfigsSpy = spyOn(mcpLoader, "loadMcpConfigs" as any).mockResolvedValue({ + loadMcpConfigsSpy = spyOn(mcpLoader, testCoerce("loadMcpConfigs")).mockResolvedValue({ servers: {}, }) - createBuiltinMcpsSpy = spyOn(mcpModule, "createBuiltinMcps" as any).mockReturnValue({}) - spyOn(shared, "log" as any).mockImplementation(() => {}) + createBuiltinMcpsSpy = spyOn(mcpModule, testCoerce("createBuiltinMcps")).mockReturnValue({}) + spyOn(shared, testCoerce("log")).mockImplementation(() => {}) }) afterEach(() => { loadMcpConfigsSpy.mockRestore() createBuiltinMcpsSpy.mockRestore() - ;(shared.log as any)?.mockRestore?.() + ;(testCoerce(shared.log))?.mockRestore?.() }) function createPluginConfig(overrides: Partial = {}): OhMyOpenCodeConfig { @@ -82,7 +82,7 @@ describe("applyMcpConfig", () => { }) const config: Record = { mcp: {} } - const pluginConfig = createPluginConfig({ disabled_mcps: ["playwright"] as any }) + const pluginConfig = createPluginConfig({ disabled_mcps: testCoerce(["playwright"]) }) //#when const { applyMcpConfig } = await import("./mcp-config-handler") @@ -107,7 +107,7 @@ describe("applyMcpConfig", () => { test("passes disabled_mcps to loadMcpConfigs", async () => { //#given const config: Record = { mcp: {} } - const pluginConfig = createPluginConfig({ disabled_mcps: ["firecrawl", "exa"] as any }) + const pluginConfig = createPluginConfig({ disabled_mcps: testCoerce(["firecrawl", "exa"]) }) //#when const { applyMcpConfig } = await import("./mcp-config-handler") @@ -145,7 +145,7 @@ describe("applyMcpConfig", () => { test("deletes plugin MCPs that are in disabled_mcps", async () => { //#given const config: Record = { mcp: {} } - const pluginConfig = createPluginConfig({ disabled_mcps: ["plugin:custom"] as any }) + const pluginConfig = createPluginConfig({ disabled_mcps: testCoerce(["plugin:custom"]) }) //#when const { applyMcpConfig } = await import("./mcp-config-handler") diff --git a/src/plugin/chat-message.test.ts b/src/plugin/chat-message.test.ts index ab6725bec..ecde2d9d0 100644 --- a/src/plugin/chat-message.test.ts +++ b/src/plugin/chat-message.test.ts @@ -56,13 +56,13 @@ function createMockHandlerArgs(overrides?: { }) { const appliedSessions: string[] = [] return { - ctx: { client: { tui: { showToast: async () => {} } } } as any, - pluginConfig: (overrides?.pluginConfig ?? {}) as any, + ctx: testCoerce({ client: { tui: { showToast: async () => {} } } }), + pluginConfig: testCoerce((overrides?.pluginConfig ?? {})), firstMessageVariantGate: { shouldOverride: () => overrides?.shouldOverride ?? false, markApplied: (sessionID: string) => { appliedSessions.push(sessionID) }, }, - hooks: { + hooks: testCoerce({ stopContinuationGuard: null, backgroundNotificationHook: null, keywordDetector: null, @@ -70,7 +70,7 @@ function createMockHandlerArgs(overrides?: { autoSlashCommand: null, startWork: null, ralphLoop: null, - } as any, + }), _appliedSessions: appliedSessions, } } diff --git a/src/plugin/event.model-fallback-2941.test.ts b/src/plugin/event.model-fallback-2941.test.ts index 2b97d2cb7..0904d6bbf 100644 --- a/src/plugin/event.model-fallback-2941.test.ts +++ b/src/plugin/event.model-fallback-2941.test.ts @@ -13,27 +13,27 @@ type EventHandlerInput = Parameters>[0] type ChatMessageHandlerArgs = Parameters[0] function asEventHandlerInput(input: EventInput): EventHandlerInput { - return input as unknown as EventHandlerInput + return testCoerce(input) } function asEventHandlerContext(ctx: unknown): EventHandlerArgs["ctx"] { - return ctx as unknown as EventHandlerArgs["ctx"] + return testCoerce(ctx) } function asPluginConfig(config: unknown): EventHandlerArgs["pluginConfig"] { - return config as unknown as EventHandlerArgs["pluginConfig"] + return testCoerce(config) } function asChatMessageHandlerContext(ctx: unknown): ChatMessageHandlerArgs["ctx"] { - return ctx as unknown as ChatMessageHandlerArgs["ctx"] + return testCoerce(ctx) } function asChatPluginConfig(config: unknown): ChatMessageHandlerArgs["pluginConfig"] { - return config as unknown as ChatMessageHandlerArgs["pluginConfig"] + return testCoerce(config) } function createEventHandlerManagers(): EventHandlerArgs["managers"] { - return { + return testCoerce({ tmuxSessionManager: { onSessionCreated: async () => {}, onSessionDeleted: async () => {}, @@ -41,17 +41,17 @@ function createEventHandlerManagers(): EventHandlerArgs["managers"] { skillMcpManager: { disconnectSession: async () => {}, }, - } as unknown as EventHandlerArgs["managers"] + }) } function createEventHandlerHooks(modelFallback: ReturnType): EventHandlerArgs["hooks"] { - return { + return testCoerce({ modelFallback, - } as unknown as EventHandlerArgs["hooks"] + }) } function createChatMessageHandlerHooks(modelFallback: ReturnType): ChatMessageHandlerArgs["hooks"] { - return { + return testCoerce({ modelFallback, stopContinuationGuard: null, keywordDetector: null, @@ -59,7 +59,7 @@ function createChatMessageHandlerHooks(modelFallback: ReturnType void } | undefined diff --git a/src/plugin/event.model-fallback-pin-agent.test.ts b/src/plugin/event.model-fallback-pin-agent.test.ts index db29bcb0e..059455655 100644 --- a/src/plugin/event.model-fallback-pin-agent.test.ts +++ b/src/plugin/event.model-fallback-pin-agent.test.ts @@ -50,16 +50,16 @@ describe("createEventHandler - model-fallback auto-continuation pins agent/model } const handler = createEventHandler({ - ctx: { + ctx: testCoerce({ directory: "/tmp", client: { session: sessionClient }, - } as any, - pluginConfig: (args?.pluginConfig ?? {}) as any, + }), + pluginConfig: testCoerce((args?.pluginConfig ?? {})), firstMessageVariantGate: { markSessionCreated: () => {}, clear: () => {}, }, - managers: { + managers: testCoerce({ tmuxSessionManager: { onSessionCreated: async () => {}, onSessionDeleted: async () => {}, @@ -67,8 +67,8 @@ describe("createEventHandler - model-fallback auto-continuation pins agent/model skillMcpManager: { disconnectSession: async () => {}, }, - } as any, - hooks: args?.hooks ?? ({} as any), + }), + hooks: args?.hooks ?? (testCoerce({})), }) return { handler, promptAsyncBodies, promptBodies } diff --git a/src/plugin/event.model-fallback.test.ts b/src/plugin/event.model-fallback.test.ts index 3ff82ae20..a31f4c2b3 100644 --- a/src/plugin/event.model-fallback.test.ts +++ b/src/plugin/event.model-fallback.test.ts @@ -22,7 +22,7 @@ describe("createEventHandler - model fallback", () => { const promptCalls: string[] = [] const handler = createEventHandler({ - ctx: { + ctx: testCoerce({ directory: "/tmp", client: { session: { @@ -36,13 +36,13 @@ describe("createEventHandler - model fallback", () => { }, }, }, - } as any, - pluginConfig: (args?.pluginConfig ?? {}) as any, + }), + pluginConfig: testCoerce((args?.pluginConfig ?? {})), firstMessageVariantGate: { markSessionCreated: () => {}, clear: () => {}, }, - managers: { + managers: testCoerce({ tmuxSessionManager: { onSessionCreated: async () => {}, onSessionDeleted: async () => {}, @@ -50,8 +50,8 @@ describe("createEventHandler - model fallback", () => { skillMcpManager: { disconnectSession: async () => {}, }, - } as any, - hooks: args?.hooks ?? ({} as any), + }), + hooks: args?.hooks ?? (testCoerce({})), }) return { handler, abortCalls, promptCalls } @@ -148,19 +148,19 @@ describe("createEventHandler - model fallback", () => { const { handler, abortCalls, promptCalls } = createHandler({ hooks: { modelFallback } }) const chatMessageHandler = createChatMessageHandler({ - ctx: { + ctx: testCoerce({ client: { tui: { showToast: async () => ({}), }, }, - } as any, - pluginConfig: {} as any, + }), + pluginConfig: testCoerce({}), firstMessageVariantGate: { shouldOverride: () => false, markApplied: () => {}, }, - hooks: { + hooks: testCoerce({ modelFallback, stopContinuationGuard: null, keywordDetector: null, @@ -168,7 +168,7 @@ describe("createEventHandler - model fallback", () => { autoSlashCommand: null, startWork: null, ralphLoop: null, - } as any, + }), }) await handler({ @@ -358,19 +358,19 @@ describe("createEventHandler - model fallback", () => { const { handler, abortCalls, promptCalls } = createHandler({ hooks: { modelFallback }, pluginConfig }) const chatMessageHandler = createChatMessageHandler({ - ctx: { + ctx: testCoerce({ client: { tui: { showToast: async () => ({}), }, }, - } as any, - pluginConfig: {} as any, + }), + pluginConfig: testCoerce({}), firstMessageVariantGate: { shouldOverride: () => false, markApplied: () => {}, }, - hooks: { + hooks: testCoerce({ modelFallback, stopContinuationGuard: null, keywordDetector: null, @@ -378,7 +378,7 @@ describe("createEventHandler - model fallback", () => { autoSlashCommand: null, startWork: null, ralphLoop: null, - } as any, + }), }) await handler({ @@ -449,7 +449,7 @@ describe("createEventHandler - model fallback", () => { setupConnectedProviderCacheMocks() const eventHandler = createEventHandler({ - ctx: { + ctx: testCoerce({ directory: "/tmp", client: { session: { @@ -463,13 +463,13 @@ describe("createEventHandler - model fallback", () => { }, }, }, - } as any, - pluginConfig: {} as any, + }), + pluginConfig: testCoerce({}), firstMessageVariantGate: { markSessionCreated: () => {}, clear: () => {}, }, - managers: { + managers: testCoerce({ tmuxSessionManager: { onSessionCreated: async () => {}, onSessionDeleted: async () => {}, @@ -477,14 +477,14 @@ describe("createEventHandler - model fallback", () => { skillMcpManager: { disconnectSession: async () => {}, }, - } as any, - hooks: { + }), + hooks: testCoerce({ modelFallback, - } as any, + }), }) const chatMessageHandler = createChatMessageHandler({ - ctx: { + ctx: testCoerce({ client: { tui: { showToast: async ({ body }: { body: { title?: string } }) => { @@ -493,13 +493,13 @@ describe("createEventHandler - model fallback", () => { }, }, }, - } as any, - pluginConfig: {} as any, + }), + pluginConfig: testCoerce({}), firstMessageVariantGate: { shouldOverride: () => false, markApplied: () => {}, }, - hooks: { + hooks: testCoerce({ modelFallback, stopContinuationGuard: null, keywordDetector: null, @@ -507,7 +507,7 @@ describe("createEventHandler - model fallback", () => { autoSlashCommand: null, startWork: null, ralphLoop: null, - } as any, + }), }) const triggerRetryCycle = async () => { diff --git a/src/plugin/fallback.cliproxyapi-matrix.test.ts b/src/plugin/fallback.cliproxyapi-matrix.test.ts index 3d1b5fd4c..1497ab285 100644 --- a/src/plugin/fallback.cliproxyapi-matrix.test.ts +++ b/src/plugin/fallback.cliproxyapi-matrix.test.ts @@ -18,42 +18,42 @@ type HarnessContext = EventHandlerArgs["ctx"] & RuntimeFallbackPluginInput type HarnessEventInput = Parameters["eventHandler"]>[0] function asHarnessEventInput(input: unknown): HarnessEventInput { - return input as unknown as HarnessEventInput + return testCoerce(input) } function asHarnessContext(ctx: unknown): HarnessContext { - return ctx as unknown as HarnessContext + return testCoerce(ctx) } function createEventHandlerManagers( overrides: Record = {}, ): EventHandlerArgs["managers"] { - return { + return testCoerce({ ...({} as EventHandlerArgs["managers"]), tmuxSessionManager: { onSessionCreated: async () => {}, onSessionDeleted: async () => {}, }, ...overrides, - } as unknown as EventHandlerArgs["managers"] + }) } function createEventHandlerHooks( overrides: Record, ): EventHandlerArgs["hooks"] { - return { + return testCoerce({ ...({} as EventHandlerArgs["hooks"]), ...overrides, - } as unknown as EventHandlerArgs["hooks"] + }) } function createChatMessageHandlerHooks( overrides: Record, ): ChatMessageHandlerArgs["hooks"] { - return { + return testCoerce({ ...({} as ChatMessageHandlerArgs["hooks"]), ...overrides, - } as unknown as ChatMessageHandlerArgs["hooks"] + }) } const PRIMARY_MODEL = { @@ -87,7 +87,7 @@ let readConnectedProvidersCacheSpy: { mockRestore: () => void } | undefined let readProviderModelsCacheSpy: { mockRestore: () => void } | undefined function createPluginConfig(mode: HarnessMode) { - return { + return testCoerce({ agents: { sisyphus: { fallback_models: CLIPROXYAPI_FALLBACKS, @@ -100,7 +100,7 @@ function createPluginConfig(mode: HarnessMode) { }, } : {}), - } as unknown as EventHandlerArgs["pluginConfig"] + }) } function createHarness(args: { @@ -187,14 +187,14 @@ function createHarness(args: { timeout_seconds: args.sessionTimeoutMs ? 30 : 0, notify_on_fallback: false, }, - pluginConfig: pluginConfig as unknown as EventHandlerArgs["pluginConfig"], + pluginConfig: testCoerce(pluginConfig), ...(args.sessionTimeoutMs ? { session_timeout_ms: args.sessionTimeoutMs } : {}), }) } const eventHandler = createEventHandler({ ctx, - pluginConfig: pluginConfig as unknown as EventHandlerArgs["pluginConfig"], + pluginConfig: testCoerce(pluginConfig), firstMessageVariantGate: { markSessionCreated: () => {}, clear: () => {}, @@ -209,7 +209,7 @@ function createHarness(args: { const chatMessageHandler = createChatMessageHandler({ ctx, - pluginConfig: pluginConfig as unknown as ChatMessageHandlerArgs["pluginConfig"], + pluginConfig: testCoerce(pluginConfig), firstMessageVariantGate: { shouldOverride: () => false, markApplied: () => {}, diff --git a/src/plugin/hooks/create-session-hooks.test.ts b/src/plugin/hooks/create-session-hooks.test.ts index ab6b5ad3b..6c4abf3ea 100644 --- a/src/plugin/hooks/create-session-hooks.test.ts +++ b/src/plugin/hooks/create-session-hooks.test.ts @@ -4,7 +4,7 @@ import type { ModelCacheState } from "../../plugin-state" import type { PluginContext } from "../types" import { createSessionHooks } from "./create-session-hooks" -const mockContext = { +const mockContext = testCoerce({ directory: "/tmp", client: { tui: { @@ -15,7 +15,7 @@ const mockContext = { update: async () => ({}), }, }, -} as unknown as PluginContext +}) const mockModelCacheState = {} as ModelCacheState diff --git a/src/plugin/tool-execute-before.ulw-loop.test.ts b/src/plugin/tool-execute-before.ulw-loop.test.ts index d4283c044..9df6c61df 100644 --- a/src/plugin/tool-execute-before.ulw-loop.test.ts +++ b/src/plugin/tool-execute-before.ulw-loop.test.ts @@ -56,7 +56,7 @@ describe("tool.execute.before ultrawork oracle verification", () => { }) const handler = createToolExecuteBeforeHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) const output = { args: createOracleTaskArgs("Check it") } @@ -78,7 +78,7 @@ describe("tool.execute.before ultrawork oracle verification", () => { const directory = join(tmpdir(), `tool-before-ulw-${Date.now()}-plain`) mkdirSync(directory, { recursive: true }) const handler = createToolExecuteBeforeHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) const output = { args: createOracleTaskArgs("Check it") } @@ -96,8 +96,8 @@ describe("tool.execute.before ultrawork oracle verification", () => { mkdirSync(directory, { recursive: true }) const startLoopCalls: Array<{ sessionID: string; prompt: string; options: Record }> = [] const handler = createToolExecuteBeforeHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], - hooks: { + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), + hooks: testCoerce[0]["hooks"]>({ ralphLoop: { startLoop: (sessionID: string, prompt: string, options?: Record) => { startLoopCalls.push({ sessionID, prompt, options: options ?? {} }) @@ -106,7 +106,7 @@ describe("tool.execute.before ultrawork oracle verification", () => { cancelLoop: () => true, getState: () => null, }, - } as unknown as Parameters[0]["hooks"], + }), }) const output = { args: { @@ -148,7 +148,7 @@ describe("tool.execute.before ultrawork oracle verification", () => { }) const beforeHandler = createToolExecuteBeforeHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) const beforeOutput = { args: createOracleTaskArgs("Check it") } @@ -156,7 +156,7 @@ describe("tool.execute.before ultrawork oracle verification", () => { const metadataFromSyncTask = createSyncTaskMetadata(beforeOutput.args, "ses-oracle") const handler = createToolExecuteAfterHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) @@ -191,7 +191,7 @@ describe("tool.execute.before ultrawork oracle verification", () => { }) const handler = createToolExecuteAfterHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) @@ -230,7 +230,7 @@ describe("tool.execute.before ultrawork oracle verification", () => { }) const handler = createToolExecuteAfterHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) @@ -269,11 +269,11 @@ describe("tool.execute.before ultrawork oracle verification", () => { }) const beforeHandler = createToolExecuteBeforeHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) const afterHandler = createToolExecuteAfterHandler({ - ctx: createCtx(directory) as unknown as Parameters[0]["ctx"], + ctx: testCoerce[0]["ctx"]>(createCtx(directory)), hooks: {} as Parameters[0]["hooks"], }) diff --git a/src/plugin/ultrawork-model-override.test.ts b/src/plugin/ultrawork-model-override.test.ts index b37dc1285..eb9d255a2 100644 --- a/src/plugin/ultrawork-model-override.test.ts +++ b/src/plugin/ultrawork-model-override.test.ts @@ -70,11 +70,11 @@ describe("resolveUltraworkOverride", () => { } function createConfig(agentName: string, ultrawork: { model?: string; variant?: string }) { - return { + return testCoerce[0]>({ agents: { [agentName]: { ultrawork }, }, - } as unknown as Parameters[0] + }) } test("should resolve override when ultrawork keyword detected", () => { @@ -139,9 +139,9 @@ describe("resolveUltraworkOverride", () => { test("should return null when agent has no ultrawork config", () => { //#given - const config = { + const config = testCoerce[0]>({ agents: { sisyphus: { model: "anthropic/claude-sonnet-4-6" } }, - } as unknown as Parameters[0] + }) const output = createOutput("ultrawork do something") //#when @@ -278,11 +278,11 @@ describe("applyUltraworkModelOverrideOnMessage", () => { } function createConfig(agentName: string, ultrawork: { model?: string; variant?: string }) { - return { + return testCoerce[0]>({ agents: { [agentName]: { ultrawork }, }, - } as unknown as Parameters[0] + }) } test("should schedule deferred DB override without variant when SDK unavailable", () => {