fix: defer catalog validation to session execution (#38258)

This commit is contained in:
Simon Klee 2026-07-22 10:32:52 +02:00 committed by GitHub
commit 8bb1cfaa3b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 229 additions and 304 deletions

View file

@ -211,10 +211,17 @@ export async function runNonInteractivePrompt(input: Input) {
}
if (!promoted && event.type === "session.execution.failed") {
prePromotionError = event.data.error
if (finalizing) return
continue
}
if (
!promoted &&
finalizing &&
(event.type === "session.execution.succeeded" || event.type === "session.execution.interrupted")
)
return
if (!promoted) continue
if (finalizing) continue
if (finalizing && !event.type.startsWith("session.execution.")) continue
if (event.type === "session.step.started") {
const part = {
@ -618,7 +625,10 @@ export async function runNonInteractivePrompt(input: Input) {
if (!emit("error", timestamp, { error: message.error })) UI.error(message.error.message)
}
}
return projected.found
return {
found: projected.found,
responded: projected.messages.some((message) => message.type === "assistant"),
}
}
const interrupt = () => {
@ -708,9 +718,18 @@ export async function runNonInteractivePrompt(input: Input) {
const waiting = input.client.session.wait({ sessionID: input.sessionID })
await Promise.race([waiting, completed.then(() => waiting)])
finalizing = true
controller.abort()
const found = await reconcile()
if (!found && !interrupted && !permissionRejected && !formCancelled && !emittedError) {
const projected = await reconcile()
if (
!projected.responded &&
!interrupted &&
!permissionRejected &&
!formCancelled &&
!emittedError &&
!prePromotionError
) {
await completed
}
if (!projected.found && !interrupted && !permissionRejected && !formCancelled && !emittedError) {
const error = prePromotionError ?? { type: "unknown", message: "Prompt was not promoted" }
emittedError = true
process.exitCode = 1

View file

@ -5,7 +5,6 @@ import { open } from "node:fs/promises"
import path from "node:path"
import { readStdin } from "../util/io"
import { ServerConnection } from "../services/server-connection"
import { waitForCatalogReady } from "../services/catalog"
import { parseSessionTargetModel, resolveSessionTarget } from "../session-target"
import { toolInlineInfo } from "@opencode-ai/tui/mini/tool"
import { runNonInteractivePrompt } from "./noninteractive"
@ -95,9 +94,11 @@ async function execute(input: RunCommandInput, prepared: Prepared, endpoint: End
prepare: async (next) => {
const selected =
next.model ??
(await client.model
.default({ location: { directory: next.location.directory, workspace: next.location.workspaceID } })
.then((result) => result.data))
(options.variant
? await client.model
.default({ location: { directory: next.location.directory, workspace: next.location.workspaceID } })
.then((result) => result.data)
: undefined)
const model = selected
? {
providerID: selected.providerID,
@ -107,25 +108,7 @@ async function execute(input: RunCommandInput, prepared: Prepared, endpoint: End
: undefined
if ((options.variant ?? explicit?.variant) && !model)
throw new RunTargetError("Cannot select a variant before selecting a model", next.session?.id)
if (model) {
await waitForCatalogReady({
sdk: client,
directory: next.location.directory,
workspace: next.location.workspaceID,
model: { providerID: model.providerID, modelID: model.id },
})
const available = await client.model.list({
location: { directory: next.location.directory, workspace: next.location.workspaceID },
})
if (!available.data.some((item) => item.providerID === model.providerID && item.id === model.id))
throw new RunTargetError(`Model unavailable: ${model.providerID}/${model.id}`, next.session?.id)
}
return {
model,
agent: input.agent
? await validateAgent(client, next.location.directory, next.location.workspaceID, input.agent)
: next.agent,
}
return { model, agent: next.agent }
},
}).catch((error) => {
if (!(error instanceof RunTargetError)) throw error
@ -190,28 +173,6 @@ export function parseRunModel(value?: string) {
}
}
async function validateAgent(client: OpenCodeClient, directory: string, workspace: string | undefined, name?: string) {
if (!name) return
const agents = await client.agent
.list({ location: { directory, workspace } })
.then((result) => result.data)
.catch(() => undefined)
if (!agents) {
warning("failed to list agents. Falling back to default agent")
return
}
const agent = agents.find((item) => item.id === name)
if (!agent) {
warning(`agent "${name}" not found. Falling back to default agent`)
return
}
if (agent.mode === "subagent") {
warning(`agent "${name}" is a subagent, not a primary agent. Falling back to default agent`)
return
}
return name
}
async function prepareFile(input: string, directory: string, options: ExecutionOptions): Promise<FilePart> {
const file = path.resolve(directory, input)
const handle = await open(file, "r").catch(() => fail(`File not found: ${input}`))