fix(acp): surface prompt errors (#34061)

This commit is contained in:
Shoubhit Dash
2026-06-26 17:50:38 +05:30
committed by GitHub
parent 971518c6d9
commit 6e8e772582
3 changed files with 136 additions and 26 deletions
+8 -1
View File
@@ -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,
)
}
}
+57 -9
View File
@@ -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(