From f4f7790d07caeb5de0dce656946d1262f6ea55c4 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Wed, 3 Dec 2025 13:26:28 +0700 Subject: [PATCH] fix: improve terminal init error handling and websocket debug logging --- .../app/src/components/create-agent-modal.tsx | 8 +++-- packages/server/src/server/messages.ts | 2 ++ packages/server/src/server/session.ts | 31 +++++++++++++++++-- .../server/src/server/terminal-mcp/tmux.ts | 11 +++++-- .../server/src/server/websocket-server.ts | 30 ++++++++++++++++++ 5 files changed, 73 insertions(+), 9 deletions(-) diff --git a/packages/app/src/components/create-agent-modal.tsx b/packages/app/src/components/create-agent-modal.tsx index 02c247170..c2164fb7c 100644 --- a/packages/app/src/components/create-agent-modal.tsx +++ b/packages/app/src/components/create-agent-modal.tsx @@ -644,13 +644,15 @@ function AgentFlowModal({ providerModelRequestTimersRef.current.delete(serverId); // Send request directly via WebSocket + const requestId = generateMessageId(); const message: WSInboundMessage = { type: "session", message: { - type: "request_provider_models", + type: "list_provider_models_request", provider: selectedProvider, - cwd: options?.cwd, - } as any, + ...(options?.cwd ? { cwd: options.cwd } : {}), + requestId, + }, }; sessionState.ws?.send(message); }; diff --git a/packages/server/src/server/messages.ts b/packages/server/src/server/messages.ts index c39c10b13..e48f695e2 100644 --- a/packages/server/src/server/messages.ts +++ b/packages/server/src/server/messages.ts @@ -345,6 +345,8 @@ export const ListProviderModelsRequestMessageSchema = z.object({ requestId: z.string().optional(), }); +// Legacy alias used by older clients; keep for compatibility + export const GitRepoInfoRequestMessageSchema = z.object({ type: z.literal("git_repo_info_request"), cwd: z.string(), diff --git a/packages/server/src/server/session.ts b/packages/server/src/server/session.ts index f2cd79ec2..cc9bb7fd4 100644 --- a/packages/server/src/server/session.ts +++ b/packages/server/src/server/session.ts @@ -224,6 +224,8 @@ export class Session { > | null = null; private terminalTools: ToolSet | null = null; private terminalManager: TerminalManager | null = null; + private terminalInitPromise: Promise | null = null; + private terminalInitError: Error | null = null; private agentMcpClient: Awaited< ReturnType > | null = null; @@ -265,8 +267,23 @@ export class Session { this.ttsManager = new TTSManager(this.conversationId); this.sttManager = new STTManager(this.conversationId); - // Initialize terminal + agent MCP clients asynchronously - void this.initializeTerminalMcp(); + // Initialize terminal + agent MCP clients asynchronously, but keep promise handles to avoid orphaned rejections + this.terminalInitPromise = this.initializeTerminalMcp().catch((error) => { + const normalizedError = + error instanceof Error + ? error + : new Error( + typeof error === "string" + ? error + : "Unknown terminal initialization error" + ); + this.terminalInitError = normalizedError; + console.error( + `[Session ${this.clientId}] Terminal MCP init failed:`, + normalizedError + ); + }); + void this.initializeAgentMcp(); this.subscribeToAgentEvents(); @@ -2013,7 +2030,15 @@ export class Session { // Get live commands from terminal manager let commands: any[] = []; - if (this.terminalManager) { + if (this.terminalInitPromise) { + await this.terminalInitPromise; + } + if (this.terminalInitError) { + console.error( + `[Session ${this.clientId}] Skipping command listing due to terminal init failure:`, + this.terminalInitError + ); + } else if (this.terminalManager) { try { commands = await this.terminalManager.listCommands(); } catch (error) { diff --git a/packages/server/src/server/terminal-mcp/tmux.ts b/packages/server/src/server/terminal-mcp/tmux.ts index 3646a2aac..50acbdc2f 100644 --- a/packages/server/src/server/terminal-mcp/tmux.ts +++ b/packages/server/src/server/terminal-mcp/tmux.ts @@ -376,16 +376,21 @@ export async function createSession(name: string): Promise { homeDir, ]); - // Disable automatic window renaming for all windows in the session + const session = await findSessionByName(name); + if (!session) { + return null; + } + + // Disable automatic window renaming for all windows in the session (session target works; ':0' does not) await executeTmux([ "set-window-option", "-t", - name, + session.id, "automatic-rename", "off", ]); - return findSessionByName(name); + return session; } /** diff --git a/packages/server/src/server/websocket-server.ts b/packages/server/src/server/websocket-server.ts index f099f9faa..5fecb72a4 100644 --- a/packages/server/src/server/websocket-server.ts +++ b/packages/server/src/server/websocket-server.ts @@ -211,9 +211,39 @@ export class VoiceAssistantWebSocketServer { // Safe error logging - avoid crashing on large/circular objects const errorMessage = error instanceof Error ? error.message : "Unknown error"; const errorStack = error instanceof Error ? error.stack : undefined; + let rawPayload: string | null = null; + let parsedPayload: unknown = null; + + try { + const buffer = Array.isArray(data) + ? Buffer.concat( + data.map((item) => + Buffer.isBuffer(item) + ? item + : Buffer.from(item as ArrayBuffer) + ) + ) + : Buffer.isBuffer(data) + ? data + : Buffer.from(data as ArrayBuffer); + rawPayload = buffer.toString(); + parsedPayload = JSON.parse(rawPayload); + } catch (payloadError) { + rawPayload = rawPayload ?? ""; + parsedPayload = parsedPayload ?? rawPayload; + console.error("[WS] Failed to decode raw payload:", payloadError); + } + + const trimmedRawPayload = + typeof rawPayload === "string" && rawPayload.length > 2000 + ? `${rawPayload.slice(0, 2000)}... (truncated)` + : rawPayload; + console.error("[WS] Failed to parse/handle message:", { message: errorMessage, stack: errorStack, + rawPayload: trimmedRawPayload, + parsedPayload, }); // Send error to client this.sendToClient(