62 lines
3.3 KiB
TypeScript
62 lines
3.3 KiB
TypeScript
import { LLMError, ToolFailure } from "@opencode-ai/ai"
|
|
import { Tool } from "@opencode-ai/plugin/v2/effect/tool"
|
|
import { SessionError } from "@opencode-ai/schema/session-error"
|
|
import { PermissionV2 } from "../permission"
|
|
import { QuestionV2 } from "../question"
|
|
import { Integration } from "../integration"
|
|
import { ToolOutputStore } from "../tool-output-store"
|
|
import { AgentNotFoundError, StepFailedError, UserInterruptedError } from "./error"
|
|
import { SessionRunnerModel } from "./runner/model"
|
|
|
|
export function toSessionError(cause: unknown): SessionError.Error {
|
|
if (cause instanceof LLMError) {
|
|
switch (cause.reason._tag) {
|
|
case "RateLimit":
|
|
return { type: "provider.rate-limit", message: cause.reason.message }
|
|
case "Authentication":
|
|
return { type: "provider.auth", message: cause.reason.message }
|
|
case "QuotaExceeded":
|
|
return { type: "provider.quota", message: cause.reason.message }
|
|
case "ContentPolicy":
|
|
return { type: "provider.content-filter", message: cause.reason.message }
|
|
case "Transport":
|
|
return { type: "provider.transport", message: cause.reason.message }
|
|
case "ProviderInternal":
|
|
return { type: "provider.internal", message: cause.reason.message }
|
|
case "InvalidProviderOutput":
|
|
return { type: "provider.invalid-output", message: cause.reason.message }
|
|
case "InvalidRequest":
|
|
return { type: "provider.invalid-request", message: cause.reason.message }
|
|
case "NoRoute":
|
|
return { type: "provider.no-route", message: cause.reason.message }
|
|
case "UnknownProvider":
|
|
return { type: "provider.unknown", message: cause.reason.message }
|
|
default: {
|
|
const exhaustive: never = cause.reason
|
|
return exhaustive
|
|
}
|
|
}
|
|
}
|
|
if (cause instanceof PermissionV2.BlockedError) return { type: "permission.rejected", message: cause.message }
|
|
if (cause instanceof QuestionV2.RejectedError) return { type: "aborted", message: cause.message }
|
|
if (cause instanceof ToolFailure || cause instanceof Tool.Failure) {
|
|
if (cause.error === undefined) return { type: "tool.execution", message: cause.message }
|
|
// The canonical error is the sole model-visible representation, so a cause
|
|
// with no message must not erase the tool's curated failure message.
|
|
const unwrapped = toSessionError(cause.error)
|
|
return unwrapped.message === "" ? { ...unwrapped, type: "tool.execution", message: cause.message } : unwrapped
|
|
}
|
|
if (cause instanceof StepFailedError) return cause.error
|
|
if (cause instanceof AgentNotFoundError) return { type: "unknown", message: cause.message }
|
|
if (cause instanceof UserInterruptedError) return { type: "aborted", message: cause.message }
|
|
if (
|
|
cause instanceof SessionRunnerModel.ModelNotSelectedError ||
|
|
cause instanceof SessionRunnerModel.ModelUnavailableError ||
|
|
cause instanceof SessionRunnerModel.VariantUnavailableError ||
|
|
cause instanceof SessionRunnerModel.UnsupportedPackageError
|
|
)
|
|
return { type: "provider.no-route", message: cause.message }
|
|
if (cause instanceof Integration.AuthorizationError) return { type: "provider.auth", message: cause.message }
|
|
if (cause instanceof ToolOutputStore.StorageError) return { type: "unknown", message: cause.message }
|
|
return { type: "unknown", message: cause instanceof Error ? cause.message : String(cause) }
|
|
}
|