fix(acp): surface prompt errors (#34061)
This commit is contained in:
@@ -46,6 +46,7 @@ export class UnsupportedOperationError extends Schema.TaggedErrorClass<Unsupport
|
||||
export class ServiceFailureError extends Schema.TaggedErrorClass<ServiceFailureError>()("ACPServiceFailureError", {
|
||||
safeMessage: Schema.String,
|
||||
service: Schema.optional(Schema.String),
|
||||
errorName: Schema.optional(Schema.String),
|
||||
}) {}
|
||||
|
||||
export type Error =
|
||||
@@ -81,7 +82,13 @@ export function toRequestError(error: Error) {
|
||||
case "ACPUnsupportedOperationError":
|
||||
return RequestError.methodNotFound(error.method)
|
||||
case "ACPServiceFailureError":
|
||||
return RequestError.internalError({ service: error.service }, error.safeMessage)
|
||||
return RequestError.internalError(
|
||||
{
|
||||
...(error.service ? { service: error.service } : {}),
|
||||
...(error.errorName ? { errorName: error.errorName } : {}),
|
||||
},
|
||||
error.safeMessage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
type SetSessionModeResponse,
|
||||
} from "@agentclientprotocol/sdk"
|
||||
import { InstallationVersion } from "@opencode-ai/core/installation/version"
|
||||
import type { Message, OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
||||
import type { AssistantMessage, Message, OpencodeClient, SessionMessageResponse } from "@opencode-ai/sdk/v2"
|
||||
import { Context, Effect, Layer, ManagedRuntime } from "effect"
|
||||
import * as ACPError from "./error"
|
||||
import { buildConfigOptions, parseModelSelection } from "./config-option"
|
||||
@@ -521,7 +521,7 @@ export function make(input: {
|
||||
"session",
|
||||
)
|
||||
yield* sendUsageUpdate(input.usage, input.sdk, input.connection, current.id, current.cwd)
|
||||
return promptResponse(response.info, params.messageId)
|
||||
return yield* promptResponse(response.info, params.messageId)
|
||||
}
|
||||
|
||||
const known = snapshot.availableCommands.find((item) => item.name === command.name)
|
||||
@@ -543,7 +543,7 @@ export function make(input: {
|
||||
"session",
|
||||
)
|
||||
yield* sendUsageUpdate(input.usage, input.sdk, input.connection, current.id, current.cwd)
|
||||
return promptResponse(response.info, params.messageId)
|
||||
return yield* promptResponse(response.info, params.messageId)
|
||||
}
|
||||
|
||||
if (command.name === "compact") {
|
||||
@@ -563,7 +563,7 @@ export function make(input: {
|
||||
}
|
||||
|
||||
yield* sendUsageUpdate(input.usage, input.sdk, input.connection, current.id, current.cwd)
|
||||
return promptResponse(undefined, params.messageId)
|
||||
return yield* promptResponse(undefined, params.messageId)
|
||||
}),
|
||||
cancel,
|
||||
}
|
||||
@@ -695,7 +695,8 @@ type MessageInfo = {
|
||||
readonly agent?: Message["agent"]
|
||||
}
|
||||
|
||||
type AssistantInfo = UsageService.AssistantTokenCost | undefined
|
||||
type AssistantError = NonNullable<AssistantMessage["error"]>
|
||||
type AssistantInfo = (UsageService.AssistantTokenCost & Pick<AssistantMessage, "error">) | undefined
|
||||
|
||||
function request<T>(fn: () => Promise<T | SdkResponse<T>>, service?: string) {
|
||||
return Effect.tryPromise({
|
||||
@@ -811,13 +812,60 @@ function detectSlashCommand(parts: ReturnType<typeof promptContentToParts>) {
|
||||
return { name, args: rest.join(" ").trim() }
|
||||
}
|
||||
|
||||
function promptResponse(info: AssistantInfo, messageId: string | null | undefined): PromptResponse {
|
||||
return {
|
||||
stopReason: "end_turn",
|
||||
...(info ? { usage: UsageService.buildUsage(info) } : {}),
|
||||
const promptResponse = Effect.fn("ACP.promptResponse")(function* (
|
||||
info: AssistantInfo,
|
||||
messageId: string | null | undefined,
|
||||
) {
|
||||
if (!info?.error) {
|
||||
return {
|
||||
stopReason: "end_turn" as const,
|
||||
...(info ? { usage: UsageService.buildUsage(info) } : {}),
|
||||
...(messageId ? { userMessageId: messageId } : {}),
|
||||
_meta: {},
|
||||
}
|
||||
}
|
||||
|
||||
const base = {
|
||||
usage: UsageService.buildUsage(info),
|
||||
...(messageId ? { userMessageId: messageId } : {}),
|
||||
_meta: {},
|
||||
}
|
||||
|
||||
if (info.error.name === "MessageAbortedError") {
|
||||
return {
|
||||
stopReason: "cancelled" as const,
|
||||
...base,
|
||||
}
|
||||
}
|
||||
|
||||
if (info.error.name === "MessageOutputLengthError") {
|
||||
return {
|
||||
stopReason: "max_tokens" as const,
|
||||
...base,
|
||||
}
|
||||
}
|
||||
|
||||
if (info.error.name === "ContentFilterError") {
|
||||
return {
|
||||
stopReason: "refusal" as const,
|
||||
...base,
|
||||
}
|
||||
}
|
||||
|
||||
if (info.error.name === "ProviderAuthError") {
|
||||
return yield* new ACPError.AuthRequiredError({ providerId: info.error.data.providerID })
|
||||
}
|
||||
|
||||
return yield* new ACPError.ServiceFailureError({
|
||||
service: "session",
|
||||
safeMessage: promptErrorMessage(info.error),
|
||||
errorName: info.error.name,
|
||||
})
|
||||
})
|
||||
|
||||
function promptErrorMessage(error: AssistantError) {
|
||||
if ("message" in error.data && typeof error.data.message === "string") return error.data.message
|
||||
return "OpenCode prompt failed"
|
||||
}
|
||||
|
||||
function sendUsageUpdate(
|
||||
|
||||
Reference in New Issue
Block a user