fix: correct config.data.model access pattern and use dynamic config paths

- Fixed delegate-task/tools.ts to access openCodeConfig.data.model instead of openCodeConfig.model
- Updated error messages to use getOpenCodeConfigPaths() for cross-platform paths
- Fixed 12 test mocks to use correct { data: { model: ... } } structure
- Fixes delegation failing with 'requires a default model' even when model is configured
This commit is contained in:
Kenny 2026-01-18 22:08:27 -05:00
parent f39f77d155
commit 6956ce0a19
3 changed files with 23 additions and 15 deletions

View File

@ -22,6 +22,7 @@ import { loadAllPluginComponents } from "../features/claude-code-plugin-loader";
import { createBuiltinMcps } from "../mcp"; import { createBuiltinMcps } from "../mcp";
import type { OhMyOpenCodeConfig } from "../config"; import type { OhMyOpenCodeConfig } from "../config";
import { log } from "../shared"; import { log } from "../shared";
import { getOpenCodeConfigPaths } from "../shared/opencode-config-dir";
import { migrateAgentConfig } from "../shared/permission-compat"; import { migrateAgentConfig } from "../shared/permission-compat";
import { PROMETHEUS_SYSTEM_PROMPT, PROMETHEUS_PERMISSION } from "../agents/prometheus-prompt"; import { PROMETHEUS_SYSTEM_PROMPT, PROMETHEUS_PERMISSION } from "../agents/prometheus-prompt";
import { DEFAULT_CATEGORIES } from "../tools/delegate-task/constants"; import { DEFAULT_CATEGORIES } from "../tools/delegate-task/constants";
@ -100,9 +101,10 @@ export function createConfigHandler(deps: ConfigHandlerDeps) {
} }
if (!(config.model as string | undefined)?.trim()) { if (!(config.model as string | undefined)?.trim()) {
const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null })
throw new Error( throw new Error(
'oh-my-opencode requires a default model.\n\n' + 'oh-my-opencode requires a default model.\n\n' +
'Add this to ~/.config/opencode/opencode.jsonc:\n\n' + `Add this to ${paths.configJsonc}:\n\n` +
' "model": "anthropic/claude-sonnet-4-5"\n\n' + ' "model": "anthropic/claude-sonnet-4-5"\n\n' +
'(Replace with your preferred provider/model)' '(Replace with your preferred provider/model)'
) )

View File

@ -125,7 +125,7 @@ describe("sisyphus-task", () => {
) )
// #then returns descriptive error message // #then returns descriptive error message
expect(result).toContain("No default model configured") expect(result).toContain("oh-my-opencode requires a default model")
}) })
}) })
@ -304,7 +304,7 @@ describe("sisyphus-task", () => {
const mockClient = { const mockClient = {
app: { agents: async () => ({ data: [] }) }, app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
session: { session: {
create: async () => ({ data: { id: "test-session" } }), create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }), prompt: async () => ({ data: {} }),
@ -363,7 +363,7 @@ describe("sisyphus-task", () => {
const mockManager = { launch: async () => ({}) } const mockManager = { launch: async () => ({}) }
const mockClient = { const mockClient = {
app: { agents: async () => ({ data: [] }) }, app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
session: { session: {
create: async () => ({ data: { id: "test-session" } }), create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }), prompt: async () => ({ data: {} }),
@ -406,7 +406,7 @@ describe("sisyphus-task", () => {
const mockManager = { launch: async () => ({}) } const mockManager = { launch: async () => ({}) }
const mockClient = { const mockClient = {
app: { agents: async () => ({ data: [] }) }, app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
session: { session: {
create: async () => ({ data: { id: "test-session" } }), create: async () => ({ data: { id: "test-session" } }),
prompt: async () => ({ data: {} }), prompt: async () => ({ data: {} }),
@ -453,7 +453,7 @@ describe("sisyphus-task", () => {
const mockManager = { launch: async () => ({}) } const mockManager = { launch: async () => ({}) }
const mockClient = { const mockClient = {
app: { agents: async () => ({ data: [] }) }, app: { agents: async () => ({ data: [] }) },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
session: { session: {
get: async () => ({ data: { directory: "/project" } }), get: async () => ({ data: { directory: "/project" } }),
create: async () => ({ data: { id: "test-session" } }), create: async () => ({ data: { id: "test-session" } }),
@ -528,7 +528,7 @@ describe("sisyphus-task", () => {
], ],
}), }),
}, },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
app: { app: {
agents: async () => ({ data: [] }), agents: async () => ({ data: [] }),
}, },
@ -586,7 +586,7 @@ describe("sisyphus-task", () => {
data: [], data: [],
}), }),
}, },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
} }
const tool = createDelegateTask({ const tool = createDelegateTask({
@ -638,7 +638,7 @@ describe("sisyphus-task", () => {
messages: async () => ({ data: [] }), messages: async () => ({ data: [] }),
status: async () => ({ data: {} }), status: async () => ({ data: {} }),
}, },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
app: { app: {
agents: async () => ({ data: [{ name: "ultrabrain", mode: "subagent" }] }), agents: async () => ({ data: [{ name: "ultrabrain", mode: "subagent" }] }),
}, },
@ -698,7 +698,7 @@ describe("sisyphus-task", () => {
}), }),
status: async () => ({ data: { "ses_sync_success": { type: "idle" } } }), status: async () => ({ data: { "ses_sync_success": { type: "idle" } } }),
}, },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
app: { app: {
agents: async () => ({ data: [{ name: "ultrabrain", mode: "subagent" }] }), agents: async () => ({ data: [{ name: "ultrabrain", mode: "subagent" }] }),
}, },
@ -751,7 +751,7 @@ describe("sisyphus-task", () => {
messages: async () => ({ data: [] }), messages: async () => ({ data: [] }),
status: async () => ({ data: {} }), status: async () => ({ data: {} }),
}, },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
app: { app: {
agents: async () => ({ data: [{ name: "ultrabrain", mode: "subagent" }] }), agents: async () => ({ data: [{ name: "ultrabrain", mode: "subagent" }] }),
}, },
@ -805,7 +805,7 @@ describe("sisyphus-task", () => {
}), }),
status: async () => ({ data: {} }), status: async () => ({ data: {} }),
}, },
config: { get: async () => ({ model: SYSTEM_DEFAULT_MODEL }) }, config: { get: async () => ({ data: { model: SYSTEM_DEFAULT_MODEL } }) },
app: { agents: async () => ({ data: [] }) }, app: { agents: async () => ({ data: [] }) },
} }

View File

@ -11,7 +11,7 @@ import { discoverSkills } from "../../features/opencode-skill-loader"
import { getTaskToastManager } from "../../features/task-toast-manager" import { getTaskToastManager } from "../../features/task-toast-manager"
import type { ModelFallbackInfo } from "../../features/task-toast-manager/types" import type { ModelFallbackInfo } from "../../features/task-toast-manager/types"
import { subagentSessions, getSessionAgent } from "../../features/claude-code-session-state" import { subagentSessions, getSessionAgent } from "../../features/claude-code-session-state"
import { log, getAgentToolRestrictions, resolveModel } from "../../shared" import { log, getAgentToolRestrictions, resolveModel, getOpenCodeConfigPaths } from "../../shared"
type OpencodeClient = PluginInput["client"] type OpencodeClient = PluginInput["client"]
@ -415,7 +415,7 @@ ${textContent || "(No text output)"}`
let systemDefaultModel: string | undefined let systemDefaultModel: string | undefined
try { try {
const openCodeConfig = await client.config.get() const openCodeConfig = await client.config.get()
systemDefaultModel = (openCodeConfig as { model?: string })?.model systemDefaultModel = (openCodeConfig as { data?: { model?: string } })?.data?.model
} catch { } catch {
// Config fetch failed, proceed without system default // Config fetch failed, proceed without system default
systemDefaultModel = undefined systemDefaultModel = undefined
@ -434,7 +434,13 @@ ${textContent || "(No text output)"}`
if (args.category) { if (args.category) {
// Guard: require system default model for category delegation // Guard: require system default model for category delegation
if (!systemDefaultModel) { if (!systemDefaultModel) {
return `No default model configured. Set a model in your OpenCode config (model field).` const paths = getOpenCodeConfigPaths({ binary: "opencode", version: null })
return (
'oh-my-opencode requires a default model.\n\n' +
`Add this to ${paths.configJsonc}:\n\n` +
' "model": "anthropic/claude-sonnet-4-5"\n\n' +
'(Replace with your preferred provider/model)'
)
} }
const resolved = resolveCategoryConfig(args.category, { const resolved = resolveCategoryConfig(args.category, {