fix(acp): harden session-page connection and localize backend errors

- Session-page connect never triggers download/install; returns
  SdkNotInstalled immediately and prompts the user to install from
  Agent Settings instead
- Binary agents now accept any cached version via
  find_best_cached_binary_for_agent so stale caches still connect
- Bound Initialize handshake with a 60s timeout and convert it to
  AcpError::InitializeTimeout via a sentinel in run_connection
- Spawn background task owns ConnectionManager map insertion and
  removes the entry on exit through an RAII guard that survives
  panics, preventing leaked stale entries
- AcpError gains SdkNotInstalled and InitializeTimeout variants plus
  a stable code() identifier; AcpEvent::Error carries code so the
  frontend can render localized messages by key
- Frontend preflight now runs for all connect sources; error event
  handler switches on code to show translated text for
  initialize_timeout, sdk_not_installed, platform_not_supported,
  process_exited, spawn_failed and download_failed
- Remove ConnectionStatus::Downloading enum variant, all frontend
  branches, and i18n strings; drop obsolete autoLinkFailedTitle,
  autoLinkPreflightFailed, preflightCheckFailedDefault and
  preflightFailedTitle keys across 10 locales
- Add backendErrors.* translations in 10 languages
- Diagnostic logging: always log agent stderr plus binary
  path/size/args/env keys and Initialize timing; gate stdin/stdout
  JSON-RPC tracing behind CODEG_ACP_DEBUG to avoid persisting user
  content into OS log files

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
xintaofei
2026-04-12 03:36:08 +08:00
parent 5bda7d06e9
commit 1c1738298b
25 changed files with 595 additions and 255 deletions

View File

@@ -84,7 +84,7 @@ export function ChatInput({
const t = useTranslations("Folder.chat.chatInput")
const isConnected = status === "connected"
const isPrompting = status === "prompting"
const isConnecting = status === "connecting" || status === "downloading"
const isConnecting = status === "connecting"
return (
<div className="p-4 pt-0">

View File

@@ -125,7 +125,7 @@ function normalizeErrorMessage(error: unknown): string {
return String(error)
}
function isExpectedAutoLinkError(error: unknown): boolean {
function isExpectedConnectError(error: unknown): boolean {
if (!error || typeof error !== "object") return false
return (error as { alerted?: unknown }).alerted === true
}
@@ -310,8 +310,7 @@ const ConversationTabView = memo(function ConversationTabView({
useEffect(() => {
connStatusRef.current = connStatus
}, [connStatus])
const isConnecting =
connStatus === "connecting" || connStatus === "downloading"
const isConnecting = connStatus === "connecting"
const connectionModes = useMemo(
() => conn.modes?.available_modes ?? [],
[conn.modes?.available_modes]
@@ -756,15 +755,13 @@ const ConversationTabView = memo(function ConversationTabView({
const s = connStatusRef.current
const doConnect = () => {
if (!workingDirForConnection) return
connConnect(nextAgentType, workingDirForConnection, undefined, {
source: "auto_link",
})
connConnect(nextAgentType, workingDirForConnection, undefined)
.then(() => {
setAgentConnectError(null)
})
.catch((e) => {
setAgentConnectError(normalizeErrorMessage(e))
if (!isExpectedAutoLinkError(e)) {
if (!isExpectedConnectError(e)) {
console.error("[ConversationTabView] switch agent:", e)
}
})

View File

@@ -19,7 +19,6 @@ import { cn } from "@/lib/utils"
type ConnectionStatusLabelKey =
| "connected"
| "connecting"
| "downloading"
| "prompting"
| "error"
@@ -32,10 +31,6 @@ const STATUS_STYLE: Record<
className: "opacity-100 animate-pulse",
labelKey: "connecting",
},
downloading: {
className: "opacity-100 animate-pulse",
labelKey: "downloading",
},
prompting: {
className: "opacity-100 animate-pulse",
labelKey: "prompting",