Files
oh-my-opencode/src/tools/delegate-task/sync-prompt-sender.ts
T
Ravi Tharuma a77a16c494 feat(config): support object-style fallback_models with per-model settings
Add support for object-style entries in fallback_models arrays, enabling
per-model configuration of variant, reasoningEffort, temperature, top_p,
maxTokens, and thinking settings.

- Zod schema for FallbackModelObject with full validation
- normalizeFallbackModels() and flattenToFallbackModelStrings() utilities
- Provider-agnostic model resolution pipeline with fallback chain
- Session prompt params state management
- Fallback chain construction with prefix-match lookup
- Integration across delegate-task, background-agent, and plugin layers
2026-03-25 11:04:49 +01:00

132 lines
4.7 KiB
TypeScript

import type { DelegateTaskArgs, OpencodeClient, DelegatedModelConfig } from "./types"
import { isPlanFamily } from "./constants"
import { buildTaskPrompt } from "./prompt-builder"
import {
promptSyncWithModelSuggestionRetry,
promptWithModelSuggestionRetry,
} from "../../shared/model-suggestion-retry"
import { formatDetailedError } from "./error-formatting"
import { getAgentToolRestrictions } from "../../shared/agent-tool-restrictions"
import { setSessionTools } from "../../shared/session-tools-store"
import { setSessionPromptParams } from "../../shared/session-prompt-params-state"
import { createInternalAgentTextPart } from "../../shared/internal-initiator-marker"
type SendSyncPromptDeps = {
promptWithModelSuggestionRetry: typeof promptWithModelSuggestionRetry
promptSyncWithModelSuggestionRetry: typeof promptSyncWithModelSuggestionRetry
}
const sendSyncPromptDeps: SendSyncPromptDeps = {
promptWithModelSuggestionRetry,
promptSyncWithModelSuggestionRetry,
}
function isOracleAgent(agentToUse: string): boolean {
return agentToUse.toLowerCase() === "oracle"
}
function isUnexpectedEofError(error: unknown): boolean {
const message = error instanceof Error ? error.message : String(error)
const lowered = message.toLowerCase()
return lowered.includes("unexpected eof") || lowered.includes("json parse error")
}
export async function sendSyncPrompt(
client: OpencodeClient,
input: {
sessionID: string
agentToUse: string
args: DelegateTaskArgs
systemContent: string | undefined
categoryModel: DelegatedModelConfig | undefined
toastManager: { removeTask: (id: string) => void } | null | undefined
taskId: string | undefined
},
deps: SendSyncPromptDeps = sendSyncPromptDeps
): Promise<string | null> {
const allowTask = isPlanFamily(input.agentToUse)
const effectivePrompt = buildTaskPrompt(input.args.prompt, input.agentToUse)
const tools = {
task: allowTask,
call_omo_agent: true,
question: false,
...getAgentToolRestrictions(input.agentToUse),
}
setSessionTools(input.sessionID, tools)
if (input.categoryModel) {
const promptOptions: Record<string, unknown> = {
...(input.categoryModel.reasoningEffort ? { reasoningEffort: input.categoryModel.reasoningEffort } : {}),
...(input.categoryModel.thinking ? { thinking: input.categoryModel.thinking } : {}),
...(input.categoryModel.maxTokens !== undefined ? { maxTokens: input.categoryModel.maxTokens } : {}),
}
if (
input.categoryModel.temperature !== undefined ||
input.categoryModel.top_p !== undefined ||
Object.keys(promptOptions).length > 0
) {
setSessionPromptParams(input.sessionID, {
...(input.categoryModel.temperature !== undefined ? { temperature: input.categoryModel.temperature } : {}),
...(input.categoryModel.top_p !== undefined ? { topP: input.categoryModel.top_p } : {}),
...(Object.keys(promptOptions).length > 0 ? { options: promptOptions } : {}),
})
}
}
const promptArgs = {
path: { id: input.sessionID },
body: {
agent: input.agentToUse,
system: input.systemContent,
tools,
parts: [createInternalAgentTextPart(effectivePrompt)],
...(input.categoryModel
? {
model: {
providerID: input.categoryModel.providerID,
modelID: input.categoryModel.modelID,
},
}
: {}),
...(input.categoryModel?.variant ? { variant: input.categoryModel.variant } : {}),
},
}
try {
await deps.promptWithModelSuggestionRetry(client, promptArgs)
} catch (promptError) {
if (isOracleAgent(input.agentToUse) && isUnexpectedEofError(promptError)) {
try {
await deps.promptSyncWithModelSuggestionRetry(client, promptArgs)
return null
} catch (oracleRetryError) {
promptError = oracleRetryError
}
}
if (input.toastManager && input.taskId !== undefined) {
input.toastManager.removeTask(input.taskId)
}
const errorMessage = promptError instanceof Error ? promptError.message : String(promptError)
if (errorMessage.includes("agent.name") || errorMessage.includes("undefined")) {
return formatDetailedError(new Error(`Agent "${input.agentToUse}" not found. Make sure the agent is registered in your opencode.json or provided by a plugin.`), {
operation: "Send prompt to agent",
args: input.args,
sessionID: input.sessionID,
agent: input.agentToUse,
category: input.args.category,
})
}
return formatDetailedError(promptError, {
operation: "Send prompt",
args: input.args,
sessionID: input.sessionID,
agent: input.agentToUse,
category: input.args.category,
})
}
return null
}