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:
@@ -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">
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1106,19 +1106,12 @@ export function useConnectionStore(): ConnectionStoreApi {
|
||||
|
||||
// ── Actions context (unchanged interface) ──
|
||||
|
||||
export type ConnectSource = "manual" | "auto_link"
|
||||
|
||||
export interface ConnectOptions {
|
||||
source?: ConnectSource
|
||||
}
|
||||
|
||||
export interface AcpActionsValue {
|
||||
connect(
|
||||
contextKey: string,
|
||||
agentType: AgentType,
|
||||
workingDir?: string,
|
||||
sessionId?: string,
|
||||
options?: ConnectOptions
|
||||
sessionId?: string
|
||||
): Promise<void>
|
||||
disconnect(contextKey: string): Promise<void>
|
||||
disconnectAll(): Promise<void>
|
||||
@@ -1211,7 +1204,7 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
// Keys whose disconnect was requested while connect was still in flight
|
||||
const abandonedKeysRef = useRef(new Set<string>())
|
||||
|
||||
type AutoLinkBlockState =
|
||||
type ConnectBlockState =
|
||||
| { kind: "none"; reason: "" }
|
||||
| {
|
||||
kind: "missing_config" | "disabled" | "unavailable" | "sdk_missing"
|
||||
@@ -1236,8 +1229,8 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
[t]
|
||||
)
|
||||
|
||||
const resolveAutoLinkBlockState = useCallback(
|
||||
(agent: AcpAgentStatus | null): AutoLinkBlockState => {
|
||||
const resolveConnectBlockState = useCallback(
|
||||
(agent: AcpAgentStatus | null): ConnectBlockState => {
|
||||
if (!agent) {
|
||||
return { kind: "missing_config", reason: t("blocked.missingConfig") }
|
||||
}
|
||||
@@ -1714,23 +1707,54 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
case "error": {
|
||||
flushStreamingQueue()
|
||||
dispatch({ type: "ERROR", contextKey, message: e.message })
|
||||
pushAlertRef.current("error", t("eventErrorTitle"), e.message)
|
||||
// Send OS notification for agent errors
|
||||
{
|
||||
const nc = storeRef.current.connections.get(contextKey)
|
||||
if (nc) {
|
||||
const agentLabel = AGENT_LABELS[nc.agentType]
|
||||
const fn = folderNameRef.current
|
||||
const title = fn ? `${fn} - Codeg` : "Codeg"
|
||||
sendSystemNotification(
|
||||
title,
|
||||
t("notificationError", {
|
||||
const nc = storeRef.current.connections.get(contextKey)
|
||||
const agentLabel = nc
|
||||
? AGENT_LABELS[nc.agentType]
|
||||
: (e.agent_type as string)
|
||||
|
||||
// Localize backend errors via their stable `code` identifier.
|
||||
// Unknown codes fall back to the raw English message so we
|
||||
// never swallow a useful stack trace.
|
||||
const localizedMessage = (() => {
|
||||
switch (e.code) {
|
||||
case "initialize_timeout":
|
||||
return t("backendErrors.initializeTimeout", {
|
||||
agent: agentLabel,
|
||||
})
|
||||
case "sdk_not_installed":
|
||||
return t("blocked.sdkMissing", { agent: agentLabel })
|
||||
case "platform_not_supported":
|
||||
return t("blocked.unavailable", { agent: agentLabel })
|
||||
case "process_exited":
|
||||
return t("backendErrors.processExited", { agent: agentLabel })
|
||||
case "spawn_failed":
|
||||
return t("backendErrors.spawnFailed", {
|
||||
agent: agentLabel,
|
||||
message: e.message,
|
||||
})
|
||||
).catch(() => {})
|
||||
case "download_failed":
|
||||
return t("backendErrors.downloadFailed", {
|
||||
agent: agentLabel,
|
||||
message: e.message,
|
||||
})
|
||||
default:
|
||||
return e.message
|
||||
}
|
||||
})()
|
||||
|
||||
dispatch({ type: "ERROR", contextKey, message: localizedMessage })
|
||||
pushAlertRef.current("error", t("eventErrorTitle"), localizedMessage)
|
||||
// Send OS notification for agent errors
|
||||
if (nc) {
|
||||
const fn = folderNameRef.current
|
||||
const title = fn ? `${fn} - Codeg` : "Codeg"
|
||||
sendSystemNotification(
|
||||
title,
|
||||
t("notificationError", {
|
||||
agent: agentLabel,
|
||||
message: localizedMessage,
|
||||
})
|
||||
).catch(() => {})
|
||||
}
|
||||
break
|
||||
}
|
||||
@@ -1821,11 +1845,7 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
for (const [contextKey, conn] of storeRef.current.connections) {
|
||||
if (contextKey === currentActiveKey) continue
|
||||
if (currentOpenTabKeys.has(contextKey)) continue
|
||||
if (
|
||||
conn.status === "prompting" ||
|
||||
conn.status === "connecting" ||
|
||||
conn.status === "downloading"
|
||||
) {
|
||||
if (conn.status === "prompting" || conn.status === "connecting") {
|
||||
continue
|
||||
}
|
||||
if (conn.status !== "connected") continue
|
||||
@@ -1865,60 +1885,54 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
contextKey: string,
|
||||
agentType: AgentType,
|
||||
workingDir?: string,
|
||||
sessionId?: string,
|
||||
options?: ConnectOptions
|
||||
sessionId?: string
|
||||
) => {
|
||||
const source = options?.source ?? "manual"
|
||||
const isAutoLink = source === "auto_link"
|
||||
|
||||
if (connectingKeysRef.current.has(contextKey)) return
|
||||
connectingKeysRef.current.add(contextKey)
|
||||
|
||||
try {
|
||||
if (isAutoLink) {
|
||||
let configuredAgent: AcpAgentStatus | null = null
|
||||
try {
|
||||
configuredAgent = await acpGetAgentStatus(agentType)
|
||||
} catch (error) {
|
||||
const reason = t("unableReadAgentConfig", {
|
||||
message: normalizeErrorMessage(error),
|
||||
})
|
||||
const autoLinkFailedTitle = t("autoLinkFailedTitle", {
|
||||
agent: AGENT_LABELS[agentType],
|
||||
})
|
||||
pushAlertRef.current(
|
||||
"error",
|
||||
autoLinkFailedTitle,
|
||||
`${reason}\n${t("agentsSetupHint")}`,
|
||||
[buildOpenAgentsSettingsAction(agentType)]
|
||||
)
|
||||
throw createAlertedError(reason)
|
||||
}
|
||||
// Preflight: read agent status and block if the SDK / binary is
|
||||
// not installed. The session page must never trigger a download
|
||||
// or install — if the agent is not ready, prompt the user to
|
||||
// install it from Agent Settings instead.
|
||||
let configuredAgent: AcpAgentStatus | null = null
|
||||
try {
|
||||
configuredAgent = await acpGetAgentStatus(agentType)
|
||||
} catch (error) {
|
||||
const reason = t("unableReadAgentConfig", {
|
||||
message: normalizeErrorMessage(error),
|
||||
})
|
||||
const failedTitle = t("connectFailedTitle", {
|
||||
agent: AGENT_LABELS[agentType],
|
||||
})
|
||||
pushAlertRef.current(
|
||||
"error",
|
||||
failedTitle,
|
||||
`${reason}\n${t("agentsSetupHint")}`,
|
||||
[buildOpenAgentsSettingsAction(agentType)]
|
||||
)
|
||||
throw createAlertedError(reason)
|
||||
}
|
||||
|
||||
const blocked = resolveAutoLinkBlockState(configuredAgent)
|
||||
if (blocked.kind !== "none") {
|
||||
const autoLinkFailedTitle = t("autoLinkFailedTitle", {
|
||||
agent: AGENT_LABELS[agentType],
|
||||
})
|
||||
const detail =
|
||||
blocked.kind === "sdk_missing"
|
||||
? t("withSetupHint", {
|
||||
message: blocked.reason,
|
||||
hint: t("agentsSetupHint"),
|
||||
})
|
||||
: `${blocked.reason}\n${t("agentsSetupHint")}`
|
||||
pushAlertRef.current(
|
||||
"error",
|
||||
blocked.kind === "sdk_missing"
|
||||
? blocked.reason
|
||||
: autoLinkFailedTitle,
|
||||
detail,
|
||||
[buildOpenAgentsSettingsAction(agentType)]
|
||||
)
|
||||
throw createAlertedError(
|
||||
blocked.kind === "sdk_missing" ? blocked.reason : blocked.reason
|
||||
)
|
||||
}
|
||||
const blocked = resolveConnectBlockState(configuredAgent)
|
||||
if (blocked.kind !== "none") {
|
||||
const failedTitle = t("connectFailedTitle", {
|
||||
agent: AGENT_LABELS[agentType],
|
||||
})
|
||||
const detail =
|
||||
blocked.kind === "sdk_missing"
|
||||
? t("withSetupHint", {
|
||||
message: blocked.reason,
|
||||
hint: t("agentsSetupHint"),
|
||||
})
|
||||
: `${blocked.reason}\n${t("agentsSetupHint")}`
|
||||
pushAlertRef.current(
|
||||
"error",
|
||||
blocked.kind === "sdk_missing" ? blocked.reason : failedTitle,
|
||||
detail,
|
||||
[buildOpenAgentsSettingsAction(agentType)]
|
||||
)
|
||||
throw createAlertedError(blocked.reason)
|
||||
}
|
||||
|
||||
const existing = storeRef.current.connections.get(contextKey)
|
||||
@@ -1970,11 +1984,37 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
} catch (err) {
|
||||
if (!isAlertedError(err)) {
|
||||
const message = normalizeErrorMessage(err)
|
||||
pushAlertRef.current(
|
||||
"error",
|
||||
t("connectFailedTitle", { agent: agentType }),
|
||||
message
|
||||
)
|
||||
const agentLabel = AGENT_LABELS[agentType]
|
||||
// Backend safety net: if the agent turned out to be not
|
||||
// installed (e.g. the binary was removed between preflight
|
||||
// and spawn), surface the same install prompt with a direct
|
||||
// "Open Agent Settings" action. Title is localized via the
|
||||
// same i18n key the preflight path uses.
|
||||
//
|
||||
// INVARIANT: `AcpError::SdkNotInstalled` renders its payload
|
||||
// unchanged, and both producers
|
||||
// (`src-tauri/src/commands/acp.rs::verify_agent_installed`
|
||||
// and `src-tauri/src/acp/connection.rs::build_agent` Binary
|
||||
// branch) format the message with the literal English
|
||||
// substring "is not installed". Do NOT translate those two
|
||||
// format strings — this branch matches on them as a stable
|
||||
// identifier, since `AcpError::Serialize` flattens to a bare
|
||||
// message string and does not expose the error `code` for
|
||||
// synchronous Tauri command rejections.
|
||||
if (message.includes("is not installed")) {
|
||||
pushAlertRef.current(
|
||||
"error",
|
||||
t("blocked.sdkMissing", { agent: agentLabel }),
|
||||
t("agentsSetupHint"),
|
||||
[buildOpenAgentsSettingsAction(agentType)]
|
||||
)
|
||||
} else {
|
||||
pushAlertRef.current(
|
||||
"error",
|
||||
t("connectFailedTitle", { agent: agentLabel }),
|
||||
message
|
||||
)
|
||||
}
|
||||
}
|
||||
throw err
|
||||
} finally {
|
||||
@@ -1987,7 +2027,7 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
consumeBufferedEvents,
|
||||
dispatch,
|
||||
handleMappedEvent,
|
||||
resolveAutoLinkBlockState,
|
||||
resolveConnectBlockState,
|
||||
t,
|
||||
waitForListenerReady,
|
||||
]
|
||||
|
||||
@@ -33,7 +33,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
|
||||
}
|
||||
@@ -77,12 +77,10 @@ export function useConnectionLifecycle({
|
||||
const modeLoading =
|
||||
!hasCachedSelectors &&
|
||||
(status === "connecting" ||
|
||||
status === "downloading" ||
|
||||
(isInteractiveStatus && !effectiveSelectorsReady))
|
||||
const configOptionsLoading =
|
||||
!hasCachedSelectors &&
|
||||
(status === "connecting" ||
|
||||
status === "downloading" ||
|
||||
(isInteractiveStatus && !effectiveSelectorsReady))
|
||||
// Gate for send button: block until the backend session is fully
|
||||
// initialized (selectorsReady from the real backend event, not cache).
|
||||
@@ -141,9 +139,7 @@ export function useConnectionLifecycle({
|
||||
const s = statusRef.current
|
||||
if (!s || s === "disconnected" || s === "error") {
|
||||
connConnectRef
|
||||
.current(agentTypeRef.current, workingDir, sessionIdRef.current, {
|
||||
source: "auto_link",
|
||||
})
|
||||
.current(agentTypeRef.current, workingDir, sessionIdRef.current)
|
||||
.then(() => {
|
||||
if (!cancelled) {
|
||||
setLastAutoConnectError(null)
|
||||
@@ -157,7 +153,7 @@ export function useConnectionLifecycle({
|
||||
message: normalizeErrorMessage(e),
|
||||
})
|
||||
}
|
||||
if (!isExpectedAutoLinkError(e)) {
|
||||
if (!isExpectedConnectError(e)) {
|
||||
console.error("[ConnLifecycle] auto-connect:", e)
|
||||
}
|
||||
})
|
||||
@@ -170,7 +166,7 @@ export function useConnectionLifecycle({
|
||||
// Manage task status for connection progress
|
||||
const taskIdRef = useRef<string | null>(null)
|
||||
useEffect(() => {
|
||||
if (status === "connecting" || status === "downloading") {
|
||||
if (status === "connecting") {
|
||||
if (!taskIdRef.current) {
|
||||
const id = `acp-connect-${Date.now()}`
|
||||
taskIdRef.current = id
|
||||
@@ -271,10 +267,8 @@ export function useConnectionLifecycle({
|
||||
touchActivity(contextKey)
|
||||
if (!status || status === "disconnected" || status === "error") {
|
||||
setLastAutoConnectError(null)
|
||||
connConnect(agentType, workingDir, sessionId, {
|
||||
source: "auto_link",
|
||||
}).catch((e: unknown) => {
|
||||
if (!isExpectedAutoLinkError(e)) {
|
||||
connConnect(agentType, workingDir, sessionId).catch((e: unknown) => {
|
||||
if (!isExpectedConnectError(e)) {
|
||||
console.error("[ConnLifecycle] connect:", e)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
useConnectionStore,
|
||||
getCachedSelectors,
|
||||
type ConnectionState,
|
||||
type ConnectOptions,
|
||||
type LiveMessage,
|
||||
type PendingPermission,
|
||||
type PendingQuestion,
|
||||
@@ -45,8 +44,7 @@ export interface UseConnectionReturn {
|
||||
connect: (
|
||||
agentType: AgentType,
|
||||
workingDir?: string,
|
||||
sessionId?: string,
|
||||
options?: ConnectOptions
|
||||
sessionId?: string
|
||||
) => Promise<void>
|
||||
disconnect: () => Promise<void>
|
||||
sendPrompt: (blocks: PromptInputBlock[]) => Promise<void>
|
||||
@@ -96,12 +94,8 @@ export function useConnection(contextKey: string): UseConnectionReturn {
|
||||
const error = connection?.error ?? null
|
||||
|
||||
const connect = useCallback(
|
||||
(
|
||||
agentType: AgentType,
|
||||
workingDir?: string,
|
||||
sessionId?: string,
|
||||
options?: ConnectOptions
|
||||
) => actions.connect(contextKey, agentType, workingDir, sessionId, options),
|
||||
(agentType: AgentType, workingDir?: string, sessionId?: string) =>
|
||||
actions.connect(contextKey, agentType, workingDir, sessionId),
|
||||
[actions, contextKey]
|
||||
)
|
||||
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "متصل",
|
||||
"connecting": "جارٍ الاتصال...",
|
||||
"downloading": "جارٍ التنزيل...",
|
||||
"prompting": "جارٍ الرد...",
|
||||
"error": "خطأ في الاتصال",
|
||||
"disconnected": "غير متصل",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} غير متاح على المنصة الحالية.",
|
||||
"sdkMissing": "لم يتم تثبيت SDK الخاص بـ {agent}"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "انتهت مهلة مصافحة اتصال {agent} (لا استجابة بعد 60 ثانية). افتح الإعدادات للتحقق من إعدادات الوكيل والشبكة.",
|
||||
"processExited": "انتهت عملية {agent} بشكل غير متوقع.",
|
||||
"spawnFailed": "تعذر بدء تشغيل {agent}: {message}",
|
||||
"downloadFailed": "فشل تنزيل {agent}: {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "تعذر قراءة إعدادات الوكيل: {message}",
|
||||
"autoLinkFailedTitle": "فشل الربط التلقائي لـ {agent}",
|
||||
"preflightCheckFailedDefault": "فشلت فحوصات ما قبل التشغيل. تحقق من إعدادات الوكلاء.",
|
||||
"preflightFailedTitle": "فشل فحص ما قبل التشغيل لـ {agent}",
|
||||
"autoLinkPreflightFailed": "فشل فحص ما قبل التشغيل للربط التلقائي: {message}",
|
||||
"connectFailedTitle": "فشل اتصال {agent}",
|
||||
"toolFallbackTitle": "أداة",
|
||||
"eventErrorTitle": "خطأ الوكيل",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "Verbunden",
|
||||
"connecting": "Verbinde...",
|
||||
"downloading": "Lade herunter...",
|
||||
"prompting": "Antworte...",
|
||||
"error": "Verbindungsfehler",
|
||||
"disconnected": "Getrennt",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} ist auf der aktuellen Plattform nicht verfügbar.",
|
||||
"sdkMissing": "{agent} SDK ist nicht installiert"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "Der Verbindungs-Handshake von {agent} hat nach 60 Sekunden das Zeitlimit überschritten. Öffnen Sie die Einstellungen, um Agenten- und Netzwerkkonfiguration zu prüfen.",
|
||||
"processExited": "{agent}-Prozess wurde unerwartet beendet.",
|
||||
"spawnFailed": "{agent} konnte nicht gestartet werden: {message}",
|
||||
"downloadFailed": "{agent}-Download fehlgeschlagen: {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "Agenten-Konfiguration kann nicht gelesen werden: {message}",
|
||||
"autoLinkFailedTitle": "{agent} Auto-Link fehlgeschlagen",
|
||||
"preflightCheckFailedDefault": "Preflight-Prüfungen fehlgeschlagen. Prüfen Sie die Agenten-Einstellungen.",
|
||||
"preflightFailedTitle": "{agent} Preflight fehlgeschlagen",
|
||||
"autoLinkPreflightFailed": "Auto-Link-Preflight fehlgeschlagen: {message}",
|
||||
"connectFailedTitle": "{agent} Verbindung fehlgeschlagen",
|
||||
"toolFallbackTitle": "Werkzeug",
|
||||
"eventErrorTitle": "Agentenfehler",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "Connected",
|
||||
"connecting": "Connecting...",
|
||||
"downloading": "Downloading...",
|
||||
"prompting": "Responding...",
|
||||
"error": "Connection error",
|
||||
"disconnected": "Disconnected",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} is unavailable on the current platform.",
|
||||
"sdkMissing": "{agent} SDK is not installed"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "{agent} connection handshake timed out after 60 seconds. Please open Settings to check Agent configuration and network settings.",
|
||||
"processExited": "{agent} process exited unexpectedly.",
|
||||
"spawnFailed": "Failed to start {agent}: {message}",
|
||||
"downloadFailed": "{agent} download failed: {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "Unable to read Agent config: {message}",
|
||||
"autoLinkFailedTitle": "{agent} auto-link failed",
|
||||
"preflightCheckFailedDefault": "Preflight checks failed. Check Agent settings.",
|
||||
"preflightFailedTitle": "{agent} preflight failed",
|
||||
"autoLinkPreflightFailed": "Auto-link preflight failed: {message}",
|
||||
"connectFailedTitle": "{agent} connection failed",
|
||||
"toolFallbackTitle": "Tool",
|
||||
"eventErrorTitle": "Agent Error",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "Conectado",
|
||||
"connecting": "Conectando...",
|
||||
"downloading": "Descargando...",
|
||||
"prompting": "Respondiendo...",
|
||||
"error": "Error de conexión",
|
||||
"disconnected": "Desconectado",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} no está disponible en la plataforma actual.",
|
||||
"sdkMissing": "El SDK de {agent} no está instalado"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "Se agotó el tiempo del handshake de conexión de {agent} (sin respuesta tras 60 segundos). Abre Ajustes para revisar la configuración del agente y de la red.",
|
||||
"processExited": "El proceso de {agent} terminó inesperadamente.",
|
||||
"spawnFailed": "No se pudo iniciar {agent}: {message}",
|
||||
"downloadFailed": "La descarga de {agent} falló: {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "No se puede leer la configuración del agente: {message}",
|
||||
"autoLinkFailedTitle": "Falló el autovínculo de {agent}",
|
||||
"preflightCheckFailedDefault": "Fallaron las comprobaciones previas. Revisa los ajustes de agentes.",
|
||||
"preflightFailedTitle": "Falló la verificación previa de {agent}",
|
||||
"autoLinkPreflightFailed": "Falló la verificación previa del autovínculo: {message}",
|
||||
"connectFailedTitle": "Falló la conexión de {agent}",
|
||||
"toolFallbackTitle": "Herramienta",
|
||||
"eventErrorTitle": "Error del agente",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "Connecté",
|
||||
"connecting": "Connexion...",
|
||||
"downloading": "Téléchargement...",
|
||||
"prompting": "Réponse...",
|
||||
"error": "Erreur de connexion",
|
||||
"disconnected": "Déconnecté",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} n'est pas disponible sur la plateforme actuelle.",
|
||||
"sdkMissing": "Le SDK de {agent} n'est pas installé"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "Le handshake de connexion de {agent} a expiré (aucune réponse après 60 secondes). Ouvrez Paramètres pour vérifier la configuration de l'agent et du réseau.",
|
||||
"processExited": "Le processus {agent} s'est arrêté de manière inattendue.",
|
||||
"spawnFailed": "Impossible de démarrer {agent} : {message}",
|
||||
"downloadFailed": "Échec du téléchargement de {agent} : {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "Impossible de lire la configuration de l'agent : {message}",
|
||||
"autoLinkFailedTitle": "Échec de l'auto-liaison de {agent}",
|
||||
"preflightCheckFailedDefault": "Les vérifications préalables ont échoué. Vérifiez les paramètres des agents.",
|
||||
"preflightFailedTitle": "Échec de la vérification préalable de {agent}",
|
||||
"autoLinkPreflightFailed": "Échec de la vérification préalable de l'auto-liaison : {message}",
|
||||
"connectFailedTitle": "Échec de la connexion de {agent}",
|
||||
"toolFallbackTitle": "Outil",
|
||||
"eventErrorTitle": "Erreur de l'agent",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "接続済み",
|
||||
"connecting": "接続中...",
|
||||
"downloading": "ダウンロード中...",
|
||||
"prompting": "応答中...",
|
||||
"error": "接続エラー",
|
||||
"disconnected": "未接続",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} は現在のプラットフォームでは利用できません。",
|
||||
"sdkMissing": "{agent} SDK がインストールされていません"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "{agent} の接続ハンドシェイクがタイムアウトしました(60 秒以内に応答なし)。設定を開いてエージェントとネットワーク設定を確認してください。",
|
||||
"processExited": "{agent} プロセスが予期せず終了しました。",
|
||||
"spawnFailed": "{agent} の起動に失敗しました: {message}",
|
||||
"downloadFailed": "{agent} のダウンロードに失敗しました: {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "エージェント設定を読み取れません: {message}",
|
||||
"autoLinkFailedTitle": "{agent} の自動リンクに失敗しました",
|
||||
"preflightCheckFailedDefault": "事前チェックに失敗しました。エージェント設定を確認してください。",
|
||||
"preflightFailedTitle": "{agent} の事前チェックに失敗しました",
|
||||
"autoLinkPreflightFailed": "自動リンクの事前チェックに失敗しました: {message}",
|
||||
"connectFailedTitle": "{agent} の接続に失敗しました",
|
||||
"toolFallbackTitle": "ツール",
|
||||
"eventErrorTitle": "エージェントエラー",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "연결됨",
|
||||
"connecting": "연결 중...",
|
||||
"downloading": "다운로드 중...",
|
||||
"prompting": "응답 중...",
|
||||
"error": "연결 오류",
|
||||
"disconnected": "연결 끊김",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent}은(는) 현재 플랫폼에서 사용할 수 없습니다.",
|
||||
"sdkMissing": "{agent} SDK가 설치되어 있지 않습니다"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "{agent} 연결 핸드셰이크가 시간 초과되었습니다(60초 동안 응답 없음). 설정을 열어 에이전트 및 네트워크 구성을 확인하세요.",
|
||||
"processExited": "{agent} 프로세스가 예기치 않게 종료되었습니다.",
|
||||
"spawnFailed": "{agent} 시작 실패: {message}",
|
||||
"downloadFailed": "{agent} 다운로드 실패: {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "에이전트 구성을 읽을 수 없습니다: {message}",
|
||||
"autoLinkFailedTitle": "{agent} 자동 연결 실패",
|
||||
"preflightCheckFailedDefault": "사전 점검에 실패했습니다. 에이전트 설정을 확인하세요.",
|
||||
"preflightFailedTitle": "{agent} 사전 점검 실패",
|
||||
"autoLinkPreflightFailed": "자동 연결 사전 점검 실패: {message}",
|
||||
"connectFailedTitle": "{agent} 연결 실패",
|
||||
"toolFallbackTitle": "도구",
|
||||
"eventErrorTitle": "에이전트 오류",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "Conectado",
|
||||
"connecting": "Conectando...",
|
||||
"downloading": "Baixando...",
|
||||
"prompting": "Respondendo...",
|
||||
"error": "Erro de conexão",
|
||||
"disconnected": "Desconectado",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} está indisponível na plataforma atual.",
|
||||
"sdkMissing": "O SDK de {agent} não está instalado"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "O handshake de conexão de {agent} expirou (sem resposta após 60 segundos). Abra Configurações para verificar a configuração do agente e da rede.",
|
||||
"processExited": "O processo de {agent} encerrou inesperadamente.",
|
||||
"spawnFailed": "Falha ao iniciar {agent}: {message}",
|
||||
"downloadFailed": "Falha no download de {agent}: {message}"
|
||||
},
|
||||
"unableReadAgentConfig": "Não foi possível ler a configuração do agente: {message}",
|
||||
"autoLinkFailedTitle": "Falha no vínculo automático de {agent}",
|
||||
"preflightCheckFailedDefault": "As verificações de pré-voo falharam. Verifique as configurações de agentes.",
|
||||
"preflightFailedTitle": "Pré-voo de {agent} falhou",
|
||||
"autoLinkPreflightFailed": "Pré-voo do vínculo automático falhou: {message}",
|
||||
"connectFailedTitle": "Falha na conexão de {agent}",
|
||||
"toolFallbackTitle": "Ferramenta",
|
||||
"eventErrorTitle": "Erro do agente",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "已连接",
|
||||
"connecting": "连接中...",
|
||||
"downloading": "下载中...",
|
||||
"prompting": "响应中...",
|
||||
"error": "连接异常",
|
||||
"disconnected": "未连接",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} 当前平台不可用。",
|
||||
"sdkMissing": "{agent} SDK 尚未安装"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "{agent} 连接握手超时(60 秒未响应),请前往设置页面检查智能体和网络配置。",
|
||||
"processExited": "{agent} 进程意外退出。",
|
||||
"spawnFailed": "启动 {agent} 失败:{message}",
|
||||
"downloadFailed": "{agent} 下载失败:{message}"
|
||||
},
|
||||
"unableReadAgentConfig": "无法读取 Agent 配置:{message}",
|
||||
"autoLinkFailedTitle": "{agent} 自动链接失败",
|
||||
"preflightCheckFailedDefault": "预检查未通过,请检查 Agent 配置。",
|
||||
"preflightFailedTitle": "{agent} 预检查失败",
|
||||
"autoLinkPreflightFailed": "自动链接预检查失败:{message}",
|
||||
"connectFailedTitle": "{agent} 连接失败",
|
||||
"toolFallbackTitle": "工具",
|
||||
"eventErrorTitle": "Agent 错误",
|
||||
|
||||
@@ -851,7 +851,6 @@
|
||||
"connection": {
|
||||
"connected": "已連線",
|
||||
"connecting": "連線中...",
|
||||
"downloading": "下載中...",
|
||||
"prompting": "回應中...",
|
||||
"error": "連線異常",
|
||||
"disconnected": "未連線",
|
||||
@@ -1422,11 +1421,13 @@
|
||||
"unavailable": "{agent} 目前平台不可用。",
|
||||
"sdkMissing": "{agent} SDK 尚未安裝"
|
||||
},
|
||||
"backendErrors": {
|
||||
"initializeTimeout": "{agent} 連線交握逾時(60 秒未回應),請前往設定頁面檢查智能體與網路設定。",
|
||||
"processExited": "{agent} 處理程序意外結束。",
|
||||
"spawnFailed": "啟動 {agent} 失敗:{message}",
|
||||
"downloadFailed": "{agent} 下載失敗:{message}"
|
||||
},
|
||||
"unableReadAgentConfig": "無法讀取 Agent 設定:{message}",
|
||||
"autoLinkFailedTitle": "{agent} 自動連結失敗",
|
||||
"preflightCheckFailedDefault": "預檢查未通過,請檢查 Agent 設定。",
|
||||
"preflightFailedTitle": "{agent} 預檢查失敗",
|
||||
"autoLinkPreflightFailed": "自動連結預檢查失敗:{message}",
|
||||
"connectFailedTitle": "{agent} 連線失敗",
|
||||
"toolFallbackTitle": "工具",
|
||||
"eventErrorTitle": "Agent 錯誤",
|
||||
|
||||
@@ -256,7 +256,6 @@ export const AGENT_COLORS: Record<AgentType, string> = {
|
||||
// ACP connection status (matches Rust ConnectionStatus)
|
||||
export type ConnectionStatus =
|
||||
| "connecting"
|
||||
| "downloading"
|
||||
| "connected"
|
||||
| "prompting"
|
||||
| "disconnected"
|
||||
@@ -442,7 +441,14 @@ export type AcpEvent =
|
||||
connection_id: string
|
||||
status: ConnectionStatus
|
||||
}
|
||||
| { type: "error"; connection_id: string; message: string }
|
||||
| {
|
||||
type: "error"
|
||||
connection_id: string
|
||||
message: string
|
||||
agent_type: string
|
||||
/** Stable backend error identifier for localization (e.g. "initialize_timeout"). */
|
||||
code: string | null
|
||||
}
|
||||
| {
|
||||
type: "available_commands"
|
||||
connection_id: string
|
||||
|
||||
Reference in New Issue
Block a user