test(plugin): remove unsafe test assertions

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

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