From 6ba4a2e609685ed484b3b9aa83560c2de0faf08e Mon Sep 17 00:00:00 2001 From: "liyi.ly" Date: Fri, 7 Aug 2026 21:44:47 +0800 Subject: [PATCH] fix(frontend): improve error code/message presentation and UX across the board - Expose ADK errorCode in SSE events and HTTP errors - Fix silent failures and missing error feedback in multiple components - Replace window.confirm with StudioConfirmDialog for destructive actions - Add role=alert to dynamic error elements for accessibility Co-Authored-By: Claude Opus 5 (1M context) --- frontend/src/adk/client.ts | 78 +++++++++++++------ frontend/src/create/CustomCreate.tsx | 9 ++- frontend/src/create/agentNameValidation.ts | 9 +-- frontend/src/ui/AgentWorkspace.tsx | 53 ++++++++++--- frontend/src/ui/DeploymentErrorMessage.tsx | 2 +- frontend/src/ui/ManageAgents.tsx | 26 ++++++- frontend/src/ui/MyAgents.tsx | 15 ++++ frontend/src/ui/Navbar.tsx | 17 +++- frontend/src/ui/SessionCapabilityDialogs.tsx | 25 +++--- .../tests/debugErrorPresentation.test.mjs | 2 +- frontend/tests/generatedAgentPlanner.test.mjs | 2 +- 11 files changed, 177 insertions(+), 61 deletions(-) diff --git a/frontend/src/adk/client.ts b/frontend/src/adk/client.ts index 11527704d..235137a30 100644 --- a/frontend/src/adk/client.ts +++ b/frontend/src/adk/client.ts @@ -45,6 +45,9 @@ export interface AdkEvent { error?: string; errorMessage?: string; error_message?: string; + // Error code accompanying the error message (snake_case and camelCase variants). + errorCode?: string; + error_code?: string; content?: { role?: string; parts?: AdkPart[]; @@ -433,11 +436,11 @@ function formatErrorDetail(detail: unknown): string { if (Array.isArray(detail)) { return detail .map((item) => { - if (item && typeof item === "object" && "msg" in item) { + if (item && typeof item === "object" && ("msg" in item || "message" in item)) { const loc = Array.isArray((item as { loc?: unknown }).loc) ? (item as { loc?: unknown[] }).loc?.join(".") : ""; - const msg = String((item as { msg?: unknown }).msg ?? ""); + const msg = String((item as { msg?: unknown; message?: unknown }).msg ?? (item as { msg?: unknown; message?: unknown }).message ?? ""); return loc ? `${loc}: ${msg}` : msg; } return String(item); @@ -445,7 +448,11 @@ function formatErrorDetail(detail: unknown): string { .filter(Boolean) .join("\n"); } - if (detail && typeof detail === "object") return JSON.stringify(detail); + if (detail && typeof detail === "object") { + const msg = (detail as Record).message; + if (typeof msg === "string" && msg) return msg; + return JSON.stringify(detail); + } return ""; } @@ -453,9 +460,26 @@ async function httpErrorMessage(res: Response, fallback: string): Promise ""); if (!text) return `${fallback} (${res.status})`; try { - const data = JSON.parse(text) as { detail?: unknown; error?: unknown }; + const data = JSON.parse(text) as { + detail?: unknown; + error?: unknown; + errorCode?: unknown; + error_code?: unknown; + code?: unknown; + }; + // Extract a machine-readable error code when the server provides one. + const errorCode = + typeof data.errorCode === "string" && data.errorCode + ? data.errorCode + : typeof data.error_code === "string" && data.error_code + ? data.error_code + : typeof data.code === "string" && data.code + ? data.code + : ""; const detail = formatErrorDetail(data.detail ?? data.error); - return detail || text || `${fallback} (${res.status})`; + if (!detail && !errorCode) return text || `${fallback} (${res.status})`; + const body = detail || `${fallback} (${res.status})`; + return errorCode ? `[${errorCode}] ${body}` : body; } catch { return text || `${fallback} (${res.status})`; } @@ -463,7 +487,7 @@ async function httpErrorMessage(res: Response, fallback: string): Promise { const res = await apiFetch(`/list-apps`); - if (!res.ok) throw new Error(`list-apps failed: ${res.status}`); + if (!res.ok) throw new Error(await httpErrorMessage(res, '读取 Agent 列表失败')); return res.json(); } @@ -643,7 +667,7 @@ export async function listSessions( ): Promise { const { app, ep } = resolve(appName); const res = await apiFetch(`/apps/${app}/users/${encodeURIComponent(userId)}/sessions`, {}, ep); - if (!res.ok) throw new Error(`list sessions failed: ${res.status}`); + if (!res.ok) throw new Error(await httpErrorMessage(res, "读取会话列表失败")); return res.json(); } @@ -659,8 +683,7 @@ export async function getSession( ep, ); if (!res.ok) { - const detail = await httpErrorMessage(res, "读取会话失败"); - throw new Error(`get session failed: ${res.status}:${detail}`); + throw new Error(await httpErrorMessage(res, "读取会话失败")); } const session = (await res.json()) as AdkSession; if (ep.runtimeId) { @@ -993,7 +1016,7 @@ export async function deleteSession( { method: "DELETE" }, ep, ); - if (!res.ok && res.status !== 404) throw new Error(`delete session failed: ${res.status}`); + if (!res.ok && res.status !== 404) throw new Error(await httpErrorMessage(res, '删除会话失败')); } function decodeArtifactData(value: string): Uint8Array { @@ -1501,7 +1524,7 @@ async function fetchAgentInfo( loadDraft = true, ): Promise { const res = await apiFetch(`/web/agent-info/${app}`, {}, ep); - if (!res.ok) throw new Error(`agent-info failed: ${res.status}`); + if (!res.ok) throw new Error(await httpErrorMessage(res, '读取 Agent 信息失败')); const info = (await res.json()) as Partial; if (loadDraft && !info.draft) { try { @@ -1691,7 +1714,7 @@ export async function webSearch( const res = await apiFetch( `/web/search?source=web&app_name=${encodeURIComponent(app)}&q=${encodeURIComponent(query)}`, ); - if (!res.ok) throw new Error(`web search failed: ${res.status}`); + if (!res.ok) throw new Error(await httpErrorMessage(res, 'Agent 网络搜索失败')); return res.json(); } @@ -1794,12 +1817,25 @@ export async function* runSSE({ } for await (const evt of parseSSE(res)) { const event = evt as AdkEvent; - if (typeof event.error === "string") event.error = formatRunSseError(event.error); + // Normalise the error-code field (camelCase or snake_case) and prepend it + // as a bracketed token so callers always see "[CODE] message" when a code + // is present — rather than having the code silently dropped. + const errorCode = + typeof event.errorCode === "string" && event.errorCode + ? event.errorCode + : typeof event.error_code === "string" && event.error_code + ? event.error_code + : ""; + const prefixCode = (msg: string) => + errorCode && !msg.startsWith(`[${errorCode}]`) ? `[${errorCode}] ${msg}` : msg; + if (typeof event.error === "string") { + event.error = formatRunSseError(prefixCode(event.error)); + } if (typeof event.errorMessage === "string") { - event.errorMessage = formatRunSseError(event.errorMessage); + event.errorMessage = formatRunSseError(prefixCode(event.errorMessage)); } if (typeof event.error_message === "string") { - event.error_message = formatRunSseError(event.error_message); + event.error_message = formatRunSseError(prefixCode(event.error_message)); } yield event; } @@ -2039,8 +2075,7 @@ export async function cancelAgentkitDeployment(taskId: string): Promise { body: JSON.stringify({ taskId }), }); if (!res.ok) { - const text = await res.text().catch(() => ""); - throw new Error(text || `取消部署失败 (${res.status})`); + throw new Error(await httpErrorMessage(res, "取消部署失败")); } deploymentControllers.get(taskId)?.abort(); deploymentControllers.delete(taskId); @@ -2062,7 +2097,7 @@ export async function getMyRuntimes( region = VOLCENGINE_DEFAULT_REGION, ): Promise { const res = await apiFetch(`/web/my-runtimes?region=${encodeURIComponent(region)}`); - if (!res.ok) throw new Error(`加载失败 (${res.status})`); + if (!res.ok) throw new Error(await httpErrorMessage(res, "加载失败")); const d = (await res.json()) as { runtimes?: ManagedRuntime[] }; return d.runtimes ?? []; } @@ -2260,7 +2295,7 @@ export const DEFAULT_STUDIO_ACCESS: StudioAccess = { /** Resolve the signed-in user's Studio role and capabilities. */ export async function getStudioAccess(): Promise { const res = await apiFetch("/web/access"); - if (!res.ok) throw new Error(`加载权限失败 (${res.status})`); + if (!res.ok) throw new Error(await httpErrorMessage(res, '加载权限失败')); const access = (await res.json()) as StudioAccess; if ( !["admin", "developer", "user"].includes(access.role) || @@ -2319,7 +2354,7 @@ export async function getStudioUpdateStatus( if (startedAt) params.set("startedAt", String(startedAt)); const query = params.size ? `?${params.toString()}` : ""; const res = await apiFetch(`/web/studio-update${query}`); - if (!res.ok) throw new Error(`检查 Studio 更新失败 (${res.status})`); + if (!res.ok) throw new Error(await httpErrorMessage(res, '检查 Studio 更新失败')); return (await res.json()) as StudioUpdateStatus; } @@ -2506,8 +2541,7 @@ export async function deleteRuntime( body: JSON.stringify({ runtimeId, region }), }); if (!res.ok) { - const t = await res.text().catch(() => ""); - throw new Error(t || `删除失败 (${res.status})`); + throw new Error(await httpErrorMessage(res, "删除失败")); } } diff --git a/frontend/src/create/CustomCreate.tsx b/frontend/src/create/CustomCreate.tsx index 0f6aabead..03b5bee05 100644 --- a/frontend/src/create/CustomCreate.tsx +++ b/frontend/src/create/CustomCreate.tsx @@ -2879,7 +2879,7 @@ export function CustomCreate({ setUsedAiGeneration(true); } catch (error) { setAiErrorDialog( - error instanceof Error ? error.message : String(error), + (error instanceof Error && error.message) ? error.message : String(error) || '未知错误', ); } finally { setAiGenerating(false); @@ -3125,7 +3125,7 @@ export function CustomCreate({ setProject(generated); setWorkspaceMode("publish"); } catch (error) { - setBuildErr(error instanceof Error ? error.message : String(error)); + setBuildErr((error instanceof Error && error.message) ? error.message : String(error) || '未知错误'); } finally { setBuilding(false); } @@ -3227,7 +3227,7 @@ export function CustomCreate({ ...item, phase: "error", runtimeSnapshot: "", - error: err instanceof Error ? err.message : String(err), + error: (err instanceof Error && err.message) ? err.message : String(err) || '未知错误', } : item, ), @@ -3283,7 +3283,7 @@ export function CustomCreate({ text, })) { const eventError = - event.error || event.errorMessage || event.error_message; + event.error ?? event.errorMessage ?? event.error_message; if (!eventError) acc = applyEvent(acc, event); setDebugVariants((current) => current.map((item) => { @@ -3664,6 +3664,7 @@ export function CustomCreate({ className={`cw-input ${invalidClass(nameInvalid)}`} value={node.name} placeholder="assistant" + maxLength={64} onChange={(e) => patch({ name: e.target.value })} /> {showErrors && nameProblem ? ( diff --git a/frontend/src/create/agentNameValidation.ts b/frontend/src/create/agentNameValidation.ts index 2a6bedd9c..141125a5a 100644 --- a/frontend/src/create/agentNameValidation.ts +++ b/frontend/src/create/agentNameValidation.ts @@ -1,16 +1,15 @@ import type { AgentDraft } from "./types"; -const ADK_AGENT_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/; - type AgentNameNode = Pick; /** Return the Google ADK name validation error, or null when valid. */ export function agentNameProblem(name: string): string | null { if (name.trim().length === 0) return "名称为必填项"; + if (name.length > 64) return '名称不能超过 64 个字符'; if (name === "user") return "user 是 Google ADK 保留名称,请使用其他名称"; - if (!ADK_AGENT_NAME_PATTERN.test(name)) { - return "名称须以英文字母或下划线开头,且只能包含英文字母、数字和下划线"; - } + if (!/^[A-Za-z_]/.test(name)) return '名称须以英文字母或下划线开头'; + const bad = name.match(/[^A-Za-z0-9_]/)?.[0]; + if (bad) return `名称中不允许使用"${bad}",只能包含英文字母、数字和下划线`; return null; } diff --git a/frontend/src/ui/AgentWorkspace.tsx b/frontend/src/ui/AgentWorkspace.tsx index 617e232cd..15446fff7 100644 --- a/frontend/src/ui/AgentWorkspace.tsx +++ b/frontend/src/ui/AgentWorkspace.tsx @@ -964,6 +964,7 @@ export function AgentWorkspace({ } | null>(null); const [updateCapabilityLoading, setUpdateCapabilityLoading] = useState(false); const [updateCapabilityError, setUpdateCapabilityError] = useState(""); + const [runtimeDetailError, setRuntimeDetailError] = useState(""); const [detailAgentInfo, setDetailAgentInfo] = useState(null); const [detailAgentInfoResolved, setDetailAgentInfoResolved] = useState(false); const [query, setQuery] = useState(""); @@ -995,6 +996,7 @@ export function AgentWorkspace({ const [selectedCaseIds, setSelectedCaseIds] = useState>(() => new Set()); const [deletingCases, setDeletingCases] = useState(false); const [caseDeleteError, setCaseDeleteError] = useState(""); + const [caseDeleteConfirmItems, setCaseDeleteConfirmItems] = useState([]); const [focusedCaseId, setFocusedCaseId] = useState(""); const [expandedCaseIds, setExpandedCaseIds] = useState>(() => new Set()); const suppressAgentClickRef = useRef(false); @@ -1541,6 +1543,7 @@ export function AgentWorkspace({ ? getCachedRuntimeDetail(runtimeId, region) : null; setRuntimeDetail(cached); + setRuntimeDetailError(""); if (!runtimeId) return; void getRuntimeDetail( runtimeId, @@ -1550,8 +1553,11 @@ export function AgentWorkspace({ .then((detail) => { if (!cancelled) setRuntimeDetail(detail); }) - .catch(() => { + .catch((error: unknown) => { if (!cancelled && !cached) setRuntimeDetail(null); + if (!cancelled && !cached) { + setRuntimeDetailError(error instanceof Error ? error.message : "加载 Runtime 详情失败"); + } }); return () => { cancelled = true; @@ -1809,17 +1815,23 @@ export function AgentWorkspace({ void onOpenFeedbackCase?.(item); }; - const deleteCases = async (items: AgentCase[]) => { + const deleteCases = (items: AgentCase[]) => { + if ( + !selectedAgent?.runtimeId || + !selectedAgentAppName || + deletingCases || + items.length === 0 + ) return; + setCaseDeleteConfirmItems(items); + }; + + const confirmDeleteCases = async (items: AgentCase[]) => { if ( !selectedAgent?.runtimeId || !selectedAgentAppName || deletingCases || items.length === 0 ) return; - const confirmText = items.length === 1 - ? "确定删除这条反馈案例?原始聊天记录不会被删除。" - : `确定删除选中的 ${items.length} 条反馈案例?原始聊天记录不会被删除。`; - if (!window.confirm(confirmText)) return; const ids = items.map((item) => item.id); const idSet = new Set(ids); setDeletingCases(true); @@ -1850,6 +1862,7 @@ export function AgentWorkspace({ ); if (focusedCaseId && idSet.has(focusedCaseId)) setFocusedCaseId(""); if (items.length > 1) setCaseSelectionMode(false); + setCaseDeleteConfirmItems([]); onFeedbackCasesDeleted?.(items); } catch (cause) { setCaseDeleteError(cause instanceof Error ? cause.message : String(cause)); @@ -2196,7 +2209,7 @@ export function AgentWorkspace({ ) : loadingAgents && listedAgents.length === 0 && filteredDrafts.length === 0 ? (
正在读取云端智能体…
) : agentsError && listedAgents.length === 0 && filteredDrafts.length === 0 ? ( -
+
{agentsError} {onRetryAgents && ( @@ -2549,6 +2562,9 @@ export function AgentWorkspace({

部署配置

配置目标环境与网络访问方式。

+ {runtimeDetailError && ( +

{runtimeDetailError}

+ )}
运行状态
@@ -3042,6 +3058,9 @@ export function AgentWorkspace({ )} + {updateCapabilityError && ( +

{updateCapabilityError}

+ )}
)} @@ -3066,6 +3085,18 @@ export function AgentWorkspace({ onConfirm={() => void confirmDeleteTarget()} /> )} + {caseDeleteConfirmItems.length > 0 && ( + setCaseDeleteConfirmItems([])} + onConfirm={() => void confirmDeleteCases(caseDeleteConfirmItems)} + /> + )} ); } @@ -3180,7 +3211,7 @@ function CaseTable({ {loading ? (
正在读取 AgentKit 评测集…
) : error ? ( -
+
{error} {onRetry && }
@@ -3358,13 +3389,13 @@ function EvaluationWorkspace({

{group.name}

评测组

{selectedAgents.length} 个参评智能体 · {group.caseSet} · {group.history.length} 次运行

-
{section === "config" ? ( diff --git a/frontend/src/ui/DeploymentErrorMessage.tsx b/frontend/src/ui/DeploymentErrorMessage.tsx index aaf68c703..9a390e0c3 100644 --- a/frontend/src/ui/DeploymentErrorMessage.tsx +++ b/frontend/src/ui/DeploymentErrorMessage.tsx @@ -45,7 +45,7 @@ export function DeploymentErrorMessage({ }`} role="alert" > -

{message}

+

{message}

{onRetry && (
); } diff --git a/frontend/src/ui/MyAgents.tsx b/frontend/src/ui/MyAgents.tsx index 3272c0af5..6462730b2 100644 --- a/frontend/src/ui/MyAgents.tsx +++ b/frontend/src/ui/MyAgents.tsx @@ -209,6 +209,7 @@ function AgentCard({ onViewDeploymentTask, onEditDraft, onDeleteDraft, + useError, }: { agent: MyAgentCardData; cloudProvider: CloudProvider; @@ -221,6 +222,7 @@ function AgentCard({ onViewDeploymentTask?: (task: DeploymentTaskUpdate) => void; onEditDraft?: (draft: WorkspaceAgentDraft) => void; onDeleteDraft?: (draft: WorkspaceAgentDraft) => void; + useError?: string; }) { const actionable = Boolean(agent.runtime || agent.sandbox); return ( @@ -328,6 +330,9 @@ function AgentCard({ ) : connected ? "已连接" : "使用"} + {useError && ( +

{useError}

+ )} )} @@ -391,6 +396,8 @@ export function MyAgents({ const [loadingSandboxAgents, setLoadingSandboxAgents] = useState(false); const [sandboxError, setSandboxError] = useState(""); const [connectingAgentId, setConnectingAgentId] = useState(""); + const [useError, setUseError] = useState(""); + const [useErrorAgentId, setUseErrorAgentId] = useState(""); const [draftToDelete, setDraftToDelete] = useState(null); const draftAgents = useMemo(() => drafts.map(draftToAgent), [drafts]); const activeDeploymentTasks = useMemo(() => { @@ -481,6 +488,8 @@ export function MyAgents({ function selectAgentType(type: AgentType) { if (type === activeType) return; + setUseError(""); + setUseErrorAgentId(""); if (type === "general") { runtimeRequestRef.current += 1; setRuntimeAgents([]); @@ -531,6 +540,8 @@ export function MyAgents({ const useAgent = useCallback(async (agent: MyAgentCardData) => { if (connectingAgentId) return; + setUseError(""); + setUseErrorAgentId(""); setConnectingAgentId(agent.id); try { await new Promise((resolve) => requestAnimationFrame(() => resolve())); @@ -539,6 +550,9 @@ export function MyAgents({ } else { await onUseAgent(agent); } + } catch (cause) { + setUseError(cause instanceof Error ? cause.message : String(cause) || '连接智能体失败'); + setUseErrorAgentId(agent.id); } finally { setConnectingAgentId(""); } @@ -759,6 +773,7 @@ export function MyAgents({ showOwnership={runtimeScope === "all"} onEditDraft={onEditDraft} onDeleteDraft={setDraftToDelete} + useError={agent.id === useErrorAgentId ? useError : ""} /> ))}
diff --git a/frontend/src/ui/Navbar.tsx b/frontend/src/ui/Navbar.tsx index 3b6bba251..3ae89dfb0 100644 --- a/frontend/src/ui/Navbar.tsx +++ b/frontend/src/ui/Navbar.tsx @@ -117,6 +117,8 @@ function AgentSelect({ | "onBrowseAgents" >) { const [open, setOpen] = useState(false); + const [changing, setChanging] = useState(false); + const [changeError, setChangeError] = useState(""); const label = (id: string) => (agentLabel ? agentLabel(id) : id); if (agentsSource === "cloud") { @@ -146,7 +148,7 @@ function AgentSelect({ return (
- @@ -162,13 +164,22 @@ function AgentSelect({ currentRuntime={currentRuntime} runtimeScope={runtimeScope} onSelect={async (id) => { - await onAppChange(id); - close(); + setChangeError(""); + setChanging(true); + try { + await onAppChange(id); + close(); + } catch (cause) { + setChangeError(cause instanceof Error ? cause.message : '切换智能体失败'); + } finally { + setChanging(false); + } }} onClose={close} /> )} + {changeError &&

{changeError}

}
); } diff --git a/frontend/src/ui/SessionCapabilityDialogs.tsx b/frontend/src/ui/SessionCapabilityDialogs.tsx index 9d9539353..7d242bf7a 100644 --- a/frontend/src/ui/SessionCapabilityDialogs.tsx +++ b/frontend/src/ui/SessionCapabilityDialogs.tsx @@ -264,7 +264,9 @@ export function SkillCapabilityDialog({ const [skillQuery, setSkillQuery] = useState(""); const [spacesLoading, setSpacesLoading] = useState(true); const [skillsLoading, setSkillsLoading] = useState(false); - const [error, setError] = useState(""); + const [spacesError, setSpacesError] = useState(""); + const [skillsError, setSkillsError] = useState(""); + const [spacesReloadKey, setSpacesReloadKey] = useState(0); const [pending, setPending] = useState(""); const selected = useMemo(() => new Set(selectedNames), [selectedNames]); @@ -300,7 +302,7 @@ export function SkillCapabilityDialog({ if (sourceTab !== "agentkit") return; let active = true; setSpacesLoading(true); - setError(""); + setSpacesError(""); void listSkillSpaces() .then((items) => { if (!active) return; @@ -308,13 +310,13 @@ export function SkillCapabilityDialog({ setSelectedSpace(items[0] ?? null); }) .catch((reason: unknown) => { - if (active) setError(reason instanceof Error ? reason.message : "读取 Skill Space 失败"); + if (active) setSpacesError(reason instanceof Error ? reason.message : "读取 Skill Space 失败"); }) .finally(() => { if (active) setSpacesLoading(false); }); return () => { active = false; }; - }, [sourceTab]); + }, [sourceTab, spacesReloadKey]); useEffect(() => { if (sourceTab !== "agentkit") return; @@ -324,13 +326,13 @@ export function SkillCapabilityDialog({ } let active = true; setSkillsLoading(true); - setError(""); + setSkillsError(""); void listSkillsInSpace(selectedSpace.id, selectedSpace.region) .then((items) => { if (active) setSkills(items); }) .catch((reason: unknown) => { - if (active) setError(reason instanceof Error ? reason.message : "读取技能失败"); + if (active) setSkillsError(reason instanceof Error ? reason.message : "读取技能失败"); }) .finally(() => { if (active) setSkillsLoading(false); @@ -425,7 +427,7 @@ export function SkillCapabilityDialog({
{publicError ? ( -
{publicError}
+
{publicError}
) : publicLoading ? (
正在搜索 Skill Hub…
) : publicSkills.length === 0 ? ( @@ -480,6 +482,11 @@ export function SkillCapabilityDialog({
{spacesLoading ? (
正在读取 Skill Space…
+ ) : spacesError ? ( +
+ {spacesError} + +
) : filteredSpaces.length === 0 ? (
没有匹配的 Skill Space
) : ( @@ -518,8 +525,8 @@ export function SkillCapabilityDialog({ />
- {error ? ( -
{error}
+ {skillsError ? ( +
{skillsError}
) : !selectedSpace ? (
选择一个 Skill Space 查看技能
) : skillsLoading ? ( diff --git a/frontend/tests/debugErrorPresentation.test.mjs b/frontend/tests/debugErrorPresentation.test.mjs index 3719707ce..653d1e3e1 100644 --- a/frontend/tests/debugErrorPresentation.test.mjs +++ b/frontend/tests/debugErrorPresentation.test.mjs @@ -47,7 +47,7 @@ test("creation and deployment keep friendly context and the original error", () ); assert.match( source, - /setBuildErr\(error instanceof Error \? error\.message : String\(error\)\)/, + /setBuildErr\(\s*\(error instanceof Error && error\.message\) \? error\.message : String\(error\) \|\| '未知错误'\s*\)/, ); assert.match( source, diff --git a/frontend/tests/generatedAgentPlanner.test.mjs b/frontend/tests/generatedAgentPlanner.test.mjs index 506161012..fa4b496b4 100644 --- a/frontend/tests/generatedAgentPlanner.test.mjs +++ b/frontend/tests/generatedAgentPlanner.test.mjs @@ -88,7 +88,7 @@ test("names the planner model and preserves generation errors verbatim", () => { ); assert.match( createSource, - /setAiErrorDialog\(\s*error instanceof Error \? error\.message : String\(error\),?\s*\)/, + /setAiErrorDialog\(\s*\(error instanceof Error && error\.message\) \? error\.message : String\(error\) \|\| '未知错误',?\s*\)/, ); assert.doesNotMatch( createSource,