diff --git a/src/discord/monitor/message-handler.process.ts b/src/discord/monitor/message-handler.process.ts index 6d502be21..e9901d07e 100644 --- a/src/discord/monitor/message-handler.process.ts +++ b/src/discord/monitor/message-handler.process.ts @@ -302,7 +302,7 @@ export async function processDiscordMessage(ctx: DiscordMessagePreflightContext) ctx: ctxPayload, updateLastRoute: isDirectMessage ? { - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, channel: "discord", to: `user:${author.id}`, accountId: route.accountId, diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index 948d6cefb..5c91c4eb9 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -664,12 +664,25 @@ export function attachGatewayWsMessageHandler(params: { requestId: pairing.request.requestId, reason, }); + const helpMessage = + `Device pairing required. ` + + `On the OpenClaw server machine, run: ` + + `'openclaw devices list' to see pending requests, then ` + + `'openclaw devices approve ${pairing.request.requestId}' to approve this device. ` + + `Alternatively, access the Control UI from the server's localhost to approve.`; send({ type: "res", id: frame.id, ok: false, - error: errorShape(ErrorCodes.NOT_PAIRED, "pairing required", { - details: { requestId: pairing.request.requestId }, + error: errorShape(ErrorCodes.NOT_PAIRED, helpMessage, { + details: { + requestId: pairing.request.requestId, + deviceId: device.id, + instructions: { + list: "openclaw devices list", + approve: `openclaw devices approve ${pairing.request.requestId}`, + } + }, }), }); close(1008, "pairing required"); diff --git a/src/imessage/monitor/monitor-provider.ts b/src/imessage/monitor/monitor-provider.ts index 491071699..05e46436d 100644 --- a/src/imessage/monitor/monitor-provider.ts +++ b/src/imessage/monitor/monitor-provider.ts @@ -510,7 +510,7 @@ export async function monitorIMessageProvider(opts: MonitorIMessageOpts = {}): P updateLastRoute: !isGroup && updateTarget ? { - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, channel: "imessage", to: updateTarget, accountId: route.accountId, diff --git a/src/line/bot-message-context.ts b/src/line/bot-message-context.ts index 8a1c049d2..77b6264b5 100644 --- a/src/line/bot-message-context.ts +++ b/src/line/bot-message-context.ts @@ -282,7 +282,7 @@ export async function buildLineMessageContext(params: BuildLineMessageContextPar if (!isGroup) { await updateLastRoute({ storePath, - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, deliveryContext: { channel: "line", to: userId ?? peerId, @@ -432,7 +432,7 @@ export async function buildLinePostbackContext(params: { if (!isGroup) { await updateLastRoute({ storePath, - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, deliveryContext: { channel: "line", to: userId ?? peerId, diff --git a/src/signal/monitor/event-handler.ts b/src/signal/monitor/event-handler.ts index 72195ff78..eb45e1dd2 100644 --- a/src/signal/monitor/event-handler.ts +++ b/src/signal/monitor/event-handler.ts @@ -156,7 +156,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) { ctx: ctxPayload, updateLastRoute: !entry.isGroup ? { - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, channel: "signal", to: entry.senderRecipient, accountId: route.accountId, diff --git a/src/slack/monitor/message-handler/dispatch.ts b/src/slack/monitor/message-handler/dispatch.ts index 38b69f049..a0c819638 100644 --- a/src/slack/monitor/message-handler/dispatch.ts +++ b/src/slack/monitor/message-handler/dispatch.ts @@ -27,7 +27,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag }); await updateLastRoute({ storePath, - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, deliveryContext: { channel: "slack", to: `user:${message.user}`, diff --git a/src/slack/monitor/message-handler/prepare.ts b/src/slack/monitor/message-handler/prepare.ts index 8a2a9e111..02fc19cae 100644 --- a/src/slack/monitor/message-handler/prepare.ts +++ b/src/slack/monitor/message-handler/prepare.ts @@ -533,7 +533,7 @@ export async function prepareSlackMessage(params: { ctx: ctxPayload, updateLastRoute: isDirectMessage ? { - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, channel: "slack", to: `user:${message.user}`, accountId: route.accountId, diff --git a/src/telegram/bot-message-context.ts b/src/telegram/bot-message-context.ts index 9696e4f1b..41188df50 100644 --- a/src/telegram/bot-message-context.ts +++ b/src/telegram/bot-message-context.ts @@ -617,7 +617,7 @@ export const buildTelegramMessageContext = async ({ ctx: ctxPayload, updateLastRoute: !isGroup ? { - sessionKey: route.mainSessionKey, + sessionKey: route.sessionKey, channel: "telegram", to: String(chatId), accountId: route.accountId, diff --git a/src/utils/provider-utils.ts b/src/utils/provider-utils.ts index e29ffe1ab..25d468a7a 100644 --- a/src/utils/provider-utils.ts +++ b/src/utils/provider-utils.ts @@ -6,21 +6,22 @@ * Returns true if the provider requires reasoning to be wrapped in tags * (e.g. and ) in the text stream, rather than using native * API fields for reasoning/thinking. + * + * NOTE: Only include providers that NATIVELY use and tags. + * Standard Gemini 2.0 (google-gemini-cli, google-generative-ai) does NOT use + * these tags natively, but Google Antigravity (Gemini 3.0) does. */ export function isReasoningTagProvider(provider: string | undefined | null): boolean { if (!provider) return false; const normalized = provider.trim().toLowerCase(); // Check for exact matches or known prefixes/substrings for reasoning providers - if ( - normalized === "ollama" || - normalized === "google-gemini-cli" || - normalized === "google-generative-ai" - ) { + if (normalized === "ollama") { return true; } // Handle google-antigravity and its model variations (e.g. google-antigravity/gemini-3) + // This is Gemini 3.0 which DOES use reasoning tags natively. if (normalized.includes("google-antigravity")) { return true; } diff --git a/src/web/auto-reply/monitor/process-message.ts b/src/web/auto-reply/monitor/process-message.ts index 27b638cfb..af8feaa05 100644 --- a/src/web/auto-reply/monitor/process-message.ts +++ b/src/web/auto-reply/monitor/process-message.ts @@ -295,7 +295,7 @@ export async function processMessage(params: { cfg: params.cfg, backgroundTasks: params.backgroundTasks, storeAgentId: params.route.agentId, - sessionKey: params.route.mainSessionKey, + sessionKey: params.route.sessionKey, channel: "whatsapp", to: dmRouteTarget, accountId: params.route.accountId, diff --git a/ui/src/ui/gateway.ts b/ui/src/ui/gateway.ts index fc8dde08a..122df9fea 100644 --- a/ui/src/ui/gateway.ts +++ b/ui/src/ui/gateway.ts @@ -219,11 +219,13 @@ export class GatewayBrowserClient { this.backoffMs = 800; this.opts.onHello?.(hello); }) - .catch(() => { + .catch((err) => { if (canFallbackToShared && deviceIdentity) { clearDeviceAuthToken({ deviceId: deviceIdentity.deviceId, role }); } - this.ws?.close(CONNECT_FAILED_CLOSE_CODE, "connect failed"); + // Extract the error message to pass to onClose handler + const errorMessage = err instanceof Error ? err.message : "connect failed"; + this.ws?.close(CONNECT_FAILED_CLOSE_CODE, errorMessage); }); }