From a58adfdd57858afe19f4974dd3ccd89eb27e4558 Mon Sep 17 00:00:00 2001 From: tyanxie <494966054@qq.com> Date: Sat, 25 Jul 2026 11:26:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(protocol):=20=E6=96=B0=E5=A2=9E=E5=AE=9E?= =?UTF-8?q?=E4=BE=8B=E6=93=8D=E4=BD=9C=E9=94=99=E8=AF=AF=E5=9B=9E=E4=BC=A0?= =?UTF-8?q?=E9=93=BE=E8=B7=AF=EF=BC=88Extension=20=E2=86=92=20Edge=20?= =?UTF-8?q?=E2=86=92=20Hub=20=E2=86=92=20Browser=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 协议层新增 ExtErrorMessage、EdgeInstanceErrorMessage,扩展 HubErrorMessage 支持 instanceId + action - Extension compact 失败时通过 onError 回调发送 error 消息 - Edge/Hub 逐层转发,前端收到后以 toast 形式展示 - 前端区分系统级错误(整页错误态)和实例操作错误(toast 提示) - i18n 支持按 action 本地化错误文案,保留 SDK 原始 message 作为插值 --- src/edge/router.ts | 13 +++++++ src/extensions/paimon/index.ts | 8 +++++ src/hub/router.ts | 17 +++++++++ src/protocol/types.ts | 35 ++++++++++++++++++- .../InstanceView/useConversation.ts | 17 +++++++++ src/web/src/i18n/en.ts | 3 ++ src/web/src/i18n/zh-CN.ts | 3 ++ 7 files changed, 95 insertions(+), 1 deletion(-) diff --git a/src/edge/router.ts b/src/edge/router.ts index 721a206..2b0184e 100644 --- a/src/edge/router.ts +++ b/src/edge/router.ts @@ -111,6 +111,19 @@ export function handleExtensionMessage( }); break; } + case "error": { + const id = edgeRegistry.findInstanceByWs(ws); + if (!id) return; + upstream.send({ + type: "instance_error", + payload: { + instanceId: id, + message: msg.payload.message, + action: msg.payload.action, + }, + }); + break; + } case "quit": { const id = edgeRegistry.findInstanceByWs(ws); if (id) { diff --git a/src/extensions/paimon/index.ts b/src/extensions/paimon/index.ts index 7c30ed1..d04bd59 100644 --- a/src/extensions/paimon/index.ts +++ b/src/extensions/paimon/index.ts @@ -468,6 +468,14 @@ function handleHubMessage( if (ctx && ctx.isIdle()) { ctx.compact({ customInstructions: msg.payload?.customInstructions, + onError: (error: Error) => { + if (client.connected) { + client.send({ + type: "error", + payload: { message: error.message, action: "compact" }, + }); + } + }, }); } break; diff --git a/src/hub/router.ts b/src/hub/router.ts index 00cf49e..3a4f093 100644 --- a/src/hub/router.ts +++ b/src/hub/router.ts @@ -130,6 +130,23 @@ export function handleEdgeMessage( hubRegistry.unregisterInstance(msg.payload.instanceId); break; } + case "instance_error": { + const subscribers = hubRegistry.getSubscribers(msg.payload.instanceId); + if (subscribers.length > 0) { + const errorMsg = JSON.stringify({ + type: "instance_error", + payload: { + instanceId: msg.payload.instanceId, + message: msg.payload.message, + action: msg.payload.action, + }, + }); + for (const browser of subscribers) { + browser.send(errorMsg); + } + } + break; + } case "spawn_result": { hubRegistry.resolveSpawn( msg.payload.token, diff --git a/src/protocol/types.ts b/src/protocol/types.ts index 9c1f469..c926ecb 100644 --- a/src/protocol/types.ts +++ b/src/protocol/types.ts @@ -117,8 +117,19 @@ export type ExtensionToEdgeMessage = | ExtStateMessage | ExtHistoryMessage | ExtSessionListMessage + | ExtErrorMessage | ExtQuitMessage; +/** 操作失败通知 */ +export interface ExtErrorMessage { + type: "error"; + payload: { + message: string; + /** 触发错误的操作 */ + action?: string; + }; +} + /** 主动退出通知(跳过 grace period) */ export interface ExtQuitMessage { type: "quit"; @@ -491,6 +502,7 @@ export type HubToBrowserMessage = | HubForwardedEventMessage | HubHistoryMessage | HubSessionListMessage + | HubInstanceErrorMessage | HubErrorMessage; /** 实例列表 */ @@ -547,7 +559,17 @@ export interface HubSessionListMessage { }; } -/** 错误 */ +/** 实例操作错误 */ +export interface HubInstanceErrorMessage { + type: "instance_error"; + payload: { + instanceId: InstanceId; + message: string; + action?: string; + }; +} + +/** 系统级错误 */ export interface HubErrorMessage { type: "error"; payload: { @@ -582,6 +604,7 @@ export type EdgeToHubMessage = | EdgeInstanceStateMessage | EdgeInstanceHistoryMessage | EdgeInstanceSessionListMessage + | EdgeInstanceErrorMessage | EdgeInstanceQuitMessage | EdgeSpawnResultMessage | EdgeBrowseResultMessage; @@ -662,6 +685,16 @@ export interface EdgeInstanceSessionListMessage { }; } +/** 转发:pi 操作失败 */ +export interface EdgeInstanceErrorMessage { + type: "instance_error"; + payload: { + instanceId: InstanceId; + message: string; + action?: string; + }; +} + /** 转发:pi 主动退出 */ export interface EdgeInstanceQuitMessage { type: "instance_quit"; diff --git a/src/web/src/components/InstanceView/useConversation.ts b/src/web/src/components/InstanceView/useConversation.ts index 1a2f658..b90beaf 100644 --- a/src/web/src/components/InstanceView/useConversation.ts +++ b/src/web/src/components/InstanceView/useConversation.ts @@ -9,6 +9,7 @@ // 6. 暴露 loadMore() 加载更多历史 import { useState, useEffect, useCallback, useRef } from "react"; +import { useTranslation } from "react-i18next"; import type { InstanceId, InstanceInfo, @@ -17,6 +18,7 @@ import type { } from "../../../../protocol/types"; import { useWebSocket } from "../../stores/useWebSocket"; import type { SessionEntry, ConversationLoadState } from "../../stores/types"; +import { showToast } from "../ui/Toast"; // ── 返回值类型 ── @@ -63,6 +65,7 @@ export function useConversation( instanceId: InstanceId, instance: InstanceInfo | undefined, ): ConversationState { + const { t } = useTranslation(); const send = useWebSocket((s) => s.send); const subscribe = useWebSocket((s) => s.subscribe); const connected = useWebSocket((s) => s.connectionState === "connected"); @@ -140,6 +143,20 @@ export function useConversation( setErrorMessage(msg.payload.message); break; } + case "instance_error": { + if (msg.payload.instanceId !== instanceId) return; + const actionKey = msg.payload.action + ? `error.action.${msg.payload.action}` + : null; + const localizedMsg = actionKey + ? t(actionKey, { + message: msg.payload.message, + defaultValue: msg.payload.message, + }) + : msg.payload.message; + showToast(localizedMsg); + break; + } } }); }, [instanceId, subscribe]); diff --git a/src/web/src/i18n/en.ts b/src/web/src/i18n/en.ts index 5262ff1..4a87317 100644 --- a/src/web/src/i18n/en.ts +++ b/src/web/src/i18n/en.ts @@ -136,6 +136,9 @@ const en: LocaleResource = { message: "Error message", type: "Error type", requestId: "Request ID", + action: { + compact: "Compaction failed: {{message}}", + }, }, } as const; diff --git a/src/web/src/i18n/zh-CN.ts b/src/web/src/i18n/zh-CN.ts index e6f6657..b909d95 100644 --- a/src/web/src/i18n/zh-CN.ts +++ b/src/web/src/i18n/zh-CN.ts @@ -134,6 +134,9 @@ const zhCN = { message: "错误信息", type: "错误类型", requestId: "请求 ID", + action: { + compact: "压缩失败:{{message}}", + }, }, } as const;