@wrongstack/desktop 0.285.0 → 0.287.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -126,7 +126,7 @@ var DesktopAgentBridge = class extends EventEmitter {
126
126
  return;
127
127
  }
128
128
  const baseDelay = Math.min(
129
- RECONNECT_CONFIG.initialDelayMs * Math.pow(RECONNECT_CONFIG.backoffMultiplier, conversation.reconnectAttempt),
129
+ RECONNECT_CONFIG.initialDelayMs * RECONNECT_CONFIG.backoffMultiplier ** conversation.reconnectAttempt,
130
130
  RECONNECT_CONFIG.maxDelayMs
131
131
  );
132
132
  const jitter = baseDelay * RECONNECT_CONFIG.jitterFactor * Math.random();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/main/agent-bridge.ts"],
4
- "sourcesContent": ["/**\n * Desktop Agent Bridge - WebSocket communication with runtimes.\n * Handles conversation management and WebSocket connections with automatic reconnection.\n */\nimport { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport type {\n DesktopConversationMessage,\n DesktopConversationSnapshot,\n DesktopConversationStatus,\n} from '../shared/types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface ConversationInternal {\n runtimeId: string;\n status: DesktopConversationStatus;\n sessionId?: string | undefined;\n error?: string | undefined;\n messages: DesktopConversationMessage[];\n ws: WebSocket | null;\n connectPromise: Promise<void> | null;\n activeAssistantMessageId: string | null;\n /** Reconnection state */\n reconnectAttempt: number;\n reconnectTimer: ReturnType<typeof setTimeout> | null;\n reconnectUrl: string | null;\n}\n\ninterface ServerMessage {\n type: string;\n payload?: Record<string, unknown> | undefined;\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst MAX_MESSAGES = 300;\n\n/** Reconnection configuration */\nconst RECONNECT_CONFIG = {\n /** Maximum number of reconnection attempts (0 = disabled) */\n maxAttempts: 5,\n /** Initial delay in ms before first reconnection */\n initialDelayMs: 1000,\n /** Maximum delay in ms between reconnection attempts */\n maxDelayMs: 30000,\n /** Multiplier for exponential backoff */\n backoffMultiplier: 2,\n /** Jitter factor (0-1) to add randomness to delays */\n jitterFactor: 0.1,\n};\n\n// ============================================================================\n// Bridge Implementation\n// ============================================================================\n\nexport class DesktopAgentBridge extends EventEmitter {\n private readonly conversations = new Map<string, ConversationInternal>();\n\n snapshot(runtimeId: string): DesktopConversationSnapshot {\n return publicConversation(this.getOrCreate(runtimeId));\n }\n\n /**\n * Get reconnection status for a runtime.\n */\n getReconnectStatus(runtimeId: string): { attempt: number; maxAttempts: number } | null {\n const conversation = this.conversations.get(runtimeId);\n if (!conversation) return null;\n return {\n attempt: conversation.reconnectAttempt,\n maxAttempts: RECONNECT_CONFIG.maxAttempts,\n };\n }\n\n /**\n * Force reconnection for a runtime (resets reconnection state).\n */\n forceReconnect(runtimeId: string, wsUrl: string): void {\n const conversation = this.getOrCreate(runtimeId);\n this.cancelReconnect(conversation);\n conversation.reconnectAttempt = 0;\n void this.ensureConnected(runtimeId, wsUrl);\n }\n\n async ensureConnected(runtimeId: string, wsUrl: string): Promise<DesktopConversationSnapshot> {\n const conversation = this.getOrCreate(runtimeId);\n \n // If already connected, return immediately\n if (conversation.ws?.readyState === WebSocket.OPEN) return publicConversation(conversation);\n \n // If currently connecting, wait for it\n if (conversation.connectPromise) {\n await conversation.connectPromise;\n return publicConversation(conversation);\n }\n\n // If reconnecting in background, return current state\n if (conversation.reconnectTimer) {\n return publicConversation(conversation);\n }\n\n // Start fresh connection\n conversation.reconnectUrl = wsUrl;\n conversation.reconnectAttempt = 0;\n await this.connect(runtimeId, wsUrl);\n return publicConversation(conversation);\n }\n\n /**\n * Internal connect method that actually establishes the WebSocket.\n */\n private async connect(runtimeId: string, wsUrl: string): Promise<void> {\n const conversation = this.getOrCreate(runtimeId);\n \n // Cancel any pending reconnect timer\n this.cancelReconnect(conversation);\n\n conversation.status = 'connecting';\n conversation.error = undefined;\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'connecting');\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n conversation.ws = ws;\n\n // Set connection timeout\n const timeout = setTimeout(() => {\n if (conversation.ws === ws) {\n ws.close();\n reject(new Error('Connection timeout'));\n }\n }, 10000);\n\n ws.once('open', () => {\n clearTimeout(timeout);\n conversation.status = 'connected';\n conversation.error = undefined;\n conversation.connectPromise = null;\n conversation.reconnectAttempt = 0;\n conversation.reconnectUrl = null;\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'connected');\n resolve();\n });\n\n ws.on('message', (data) => {\n this.handleServerMessage(conversation, data.toString());\n });\n\n ws.once('error', (err) => {\n clearTimeout(timeout);\n conversation.status = 'error';\n conversation.error = err instanceof Error ? err.message : String(err);\n conversation.connectPromise = null;\n this.appendMessage(conversation, {\n role: 'system',\n text: `Connection error: ${conversation.error}`,\n });\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'error');\n reject(err);\n });\n\n ws.once('close', () => {\n clearTimeout(timeout);\n if (conversation.ws === ws) conversation.ws = null;\n conversation.connectPromise = null;\n \n if (conversation.status !== 'error') {\n conversation.status = 'disconnected';\n }\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n \n // Schedule reconnection if applicable\n if (conversation.reconnectUrl && conversation.status === 'disconnected') {\n this.scheduleReconnect(conversation);\n }\n });\n });\n }\n\n /**\n * Schedule a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(conversation: ConversationInternal): void {\n // Check if reconnection is enabled and within limits\n if (RECONNECT_CONFIG.maxAttempts === 0) return;\n if (conversation.reconnectAttempt >= RECONNECT_CONFIG.maxAttempts) {\n this.emitReconnectEvent(conversation, 'exhausted');\n return;\n }\n\n // Calculate delay with exponential backoff and jitter\n const baseDelay = Math.min(\n RECONNECT_CONFIG.initialDelayMs *\n Math.pow(RECONNECT_CONFIG.backoffMultiplier, conversation.reconnectAttempt),\n RECONNECT_CONFIG.maxDelayMs,\n );\n \n // Add jitter\n const jitter = baseDelay * RECONNECT_CONFIG.jitterFactor * Math.random();\n const delay = Math.floor(baseDelay + jitter);\n\n conversation.reconnectAttempt++;\n this.emitReconnectEvent(conversation, 'scheduled', { delay, attempt: conversation.reconnectAttempt });\n\n conversation.reconnectTimer = setTimeout(() => {\n conversation.reconnectTimer = null;\n if (!conversation.reconnectUrl) return;\n \n // Check if still disconnected\n if (conversation.ws?.readyState !== WebSocket.OPEN) {\n void this.connect(conversation.runtimeId, conversation.reconnectUrl);\n }\n }, delay);\n }\n\n /**\n * Cancel pending reconnection.\n */\n private cancelReconnect(conversation: ConversationInternal): void {\n if (conversation.reconnectTimer) {\n clearTimeout(conversation.reconnectTimer);\n conversation.reconnectTimer = null;\n }\n }\n\n /**\n * Emit reconnection event for UI feedback.\n */\n private emitReconnectEvent(\n conversation: ConversationInternal,\n status: 'connecting' | 'connected' | 'error' | 'scheduled' | 'exhausted',\n data?: { delay?: number; attempt?: number },\n ): void {\n this.emit('reconnect', {\n runtimeId: conversation.runtimeId,\n status,\n attempt: conversation.reconnectAttempt,\n maxAttempts: RECONNECT_CONFIG.maxAttempts,\n ...data,\n });\n }\n\n async sendMessage(\n runtimeId: string,\n wsUrl: string,\n content: string,\n ): Promise<DesktopConversationSnapshot> {\n const trimmed = content.trim();\n if (!trimmed) return this.snapshot(runtimeId);\n \n // Reset reconnection state on manual action\n const conversation = this.getOrCreate(runtimeId);\n conversation.reconnectAttempt = 0;\n \n await this.ensureConnected(runtimeId, wsUrl);\n \n // Refresh conversation after connect\n const conv = this.getOrCreate(runtimeId);\n this.appendMessage(conv, {\n id: `user_${randomUUID()}`,\n role: 'user',\n text: trimmed,\n });\n conv.status = 'running';\n conv.activeAssistantMessageId = null;\n this.emitChanged(conv);\n this.send(conv, {\n type: 'user_message',\n payload: {\n id: `msg_${Date.now()}_${randomUUID().slice(0, 8)}`,\n content: trimmed,\n timestamp: Date.now(),\n ...(conv.sessionId ? { sessionId: conv.sessionId } : {}),\n },\n });\n return publicConversation(conv);\n }\n\n async abort(runtimeId: string, wsUrl: string): Promise<DesktopConversationSnapshot> {\n // Reset reconnection state on manual action\n const conversation = this.getOrCreate(runtimeId);\n conversation.reconnectAttempt = 0;\n \n await this.ensureConnected(runtimeId, wsUrl);\n const conv = this.getOrCreate(runtimeId);\n this.send(conv, {\n type: 'abort',\n payload: conv.sessionId ? { sessionId: conv.sessionId } : {},\n });\n conv.status = 'connected';\n this.appendMessage(conv, { role: 'system', text: 'Abort requested.' });\n return publicConversation(conv);\n }\n\n close(runtimeId: string): void {\n const conversation = this.conversations.get(runtimeId);\n if (!conversation) return;\n \n // Cancel reconnection and clear state\n this.cancelReconnect(conversation);\n conversation.reconnectAttempt = 0;\n conversation.reconnectUrl = null;\n \n conversation.ws?.close();\n conversation.ws = null;\n conversation.connectPromise = null;\n conversation.status = 'disconnected';\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n }\n\n closeAll(): void {\n for (const runtimeId of this.conversations.keys()) {\n this.close(runtimeId);\n }\n }\n\n private handleServerMessage(conversation: ConversationInternal, raw: string): void {\n let message: ServerMessage;\n try {\n message = JSON.parse(raw) as ServerMessage;\n } catch {\n return;\n }\n const payload = message.payload ?? {};\n switch (message.type) {\n case 'session.start': {\n const sessionId = stringValue(payload['sessionId']);\n if (sessionId) conversation.sessionId = sessionId;\n conversation.status = conversation.status === 'running' ? 'running' : 'connected';\n this.emitChanged(conversation);\n break;\n }\n case 'provider.text_delta': {\n conversation.status = 'running';\n this.appendAssistantDelta(conversation, stringValue(payload['text']) ?? '');\n break;\n }\n case 'tool.started': {\n conversation.status = 'running';\n const name = stringValue(payload['name']) ?? 'tool';\n this.appendMessage(conversation, { role: 'tool', text: `Started ${name}` });\n break;\n }\n case 'tool.executed': {\n const name = stringValue(payload['name']) ?? 'tool';\n const ok = payload['ok'] === true;\n this.appendMessage(conversation, {\n role: 'tool',\n text: `${name} ${ok ? 'completed' : 'failed'}`,\n });\n break;\n }\n case 'provider.error':\n case 'provider.stream_error':\n case 'error': {\n const text =\n stringValue(payload['message']) ??\n stringValue(payload['description']) ??\n `${message.type} received`;\n conversation.status = 'error';\n conversation.error = text;\n this.appendMessage(conversation, { role: 'system', text });\n break;\n }\n case 'run.result': {\n const finalText = stringValue(payload['finalText']);\n if (finalText && !this.lastAssistantHasText(conversation)) {\n this.appendMessage(conversation, { role: 'assistant', text: finalText });\n }\n conversation.status = payload['status'] === 'failed' ? 'error' : 'connected';\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n break;\n }\n }\n }\n\n private send(conversation: ConversationInternal, message: Record<string, unknown>): void {\n if (conversation.ws?.readyState !== WebSocket.OPEN) {\n throw new Error('Runtime socket is not connected');\n }\n conversation.ws.send(JSON.stringify(message));\n }\n\n private appendAssistantDelta(conversation: ConversationInternal, text: string): void {\n if (!text) return;\n let message = conversation.messages.find((m) => m.id === conversation.activeAssistantMessageId);\n if (!message) {\n message = {\n id: `assistant_${randomUUID()}`,\n role: 'assistant',\n text: '',\n timestamp: Date.now(),\n };\n conversation.activeAssistantMessageId = message.id;\n conversation.messages.push(message);\n }\n message.text += text;\n this.trimMessages(conversation);\n this.emitChanged(conversation);\n }\n\n private appendMessage(\n conversation: ConversationInternal,\n input: Partial<DesktopConversationMessage> & Pick<DesktopConversationMessage, 'role' | 'text'>,\n ): void {\n conversation.messages.push({\n id: input.id ?? `${input.role}_${randomUUID()}`,\n role: input.role,\n text: input.text,\n timestamp: input.timestamp ?? Date.now(),\n });\n this.trimMessages(conversation);\n this.emitChanged(conversation);\n }\n\n private lastAssistantHasText(conversation: ConversationInternal): boolean {\n const lastAssistant = [...conversation.messages].reverse().find((m) => m.role === 'assistant');\n return Boolean(lastAssistant?.text.trim());\n }\n\n private trimMessages(conversation: ConversationInternal): void {\n if (conversation.messages.length <= MAX_MESSAGES) return;\n conversation.messages.splice(0, conversation.messages.length - MAX_MESSAGES);\n }\n\n private getOrCreate(runtimeId: string): ConversationInternal {\n let conversation = this.conversations.get(runtimeId);\n if (conversation) return conversation;\n conversation = {\n runtimeId,\n status: 'disconnected',\n messages: [],\n ws: null,\n connectPromise: null,\n activeAssistantMessageId: null,\n reconnectAttempt: 0,\n reconnectTimer: null,\n reconnectUrl: null,\n };\n this.conversations.set(runtimeId, conversation);\n return conversation;\n }\n\n private emitChanged(conversation: ConversationInternal): void {\n this.emit('changed', publicConversation(conversation));\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction publicConversation(conversation: ConversationInternal): DesktopConversationSnapshot {\n return {\n runtimeId: conversation.runtimeId,\n status: conversation.status,\n sessionId: conversation.sessionId,\n error: conversation.error,\n messages: conversation.messages.map((message) => ({ ...message })),\n };\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n// ============================================================================\n// Reconnect Event Type (for external consumers)\n// ============================================================================\n\nexport interface DesktopBridgeReconnectEvent {\n runtimeId: string;\n status: 'connecting' | 'connected' | 'error' | 'scheduled' | 'exhausted';\n attempt: number;\n maxAttempts: number;\n delay?: number;\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Desktop Agent Bridge - WebSocket communication with runtimes.\n * Handles conversation management and WebSocket connections with automatic reconnection.\n */\nimport { randomUUID } from 'node:crypto';\nimport { EventEmitter } from 'node:events';\nimport WebSocket from 'ws';\nimport type {\n DesktopConversationMessage,\n DesktopConversationSnapshot,\n DesktopConversationStatus,\n} from '../shared/types.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\ninterface ConversationInternal {\n runtimeId: string;\n status: DesktopConversationStatus;\n sessionId?: string | undefined;\n error?: string | undefined;\n messages: DesktopConversationMessage[];\n ws: WebSocket | null;\n connectPromise: Promise<void> | null;\n activeAssistantMessageId: string | null;\n /** Reconnection state */\n reconnectAttempt: number;\n reconnectTimer: ReturnType<typeof setTimeout> | null;\n reconnectUrl: string | null;\n}\n\ninterface ServerMessage {\n type: string;\n payload?: Record<string, unknown> | undefined;\n}\n\n// ============================================================================\n// Configuration\n// ============================================================================\n\nconst MAX_MESSAGES = 300;\n\n/** Reconnection configuration */\nconst RECONNECT_CONFIG = {\n /** Maximum number of reconnection attempts (0 = disabled) */\n maxAttempts: 5,\n /** Initial delay in ms before first reconnection */\n initialDelayMs: 1000,\n /** Maximum delay in ms between reconnection attempts */\n maxDelayMs: 30000,\n /** Multiplier for exponential backoff */\n backoffMultiplier: 2,\n /** Jitter factor (0-1) to add randomness to delays */\n jitterFactor: 0.1,\n};\n\n// ============================================================================\n// Bridge Implementation\n// ============================================================================\n\nexport class DesktopAgentBridge extends EventEmitter {\n private readonly conversations = new Map<string, ConversationInternal>();\n\n snapshot(runtimeId: string): DesktopConversationSnapshot {\n return publicConversation(this.getOrCreate(runtimeId));\n }\n\n /**\n * Get reconnection status for a runtime.\n */\n getReconnectStatus(runtimeId: string): { attempt: number; maxAttempts: number } | null {\n const conversation = this.conversations.get(runtimeId);\n if (!conversation) return null;\n return {\n attempt: conversation.reconnectAttempt,\n maxAttempts: RECONNECT_CONFIG.maxAttempts,\n };\n }\n\n /**\n * Force reconnection for a runtime (resets reconnection state).\n */\n forceReconnect(runtimeId: string, wsUrl: string): void {\n const conversation = this.getOrCreate(runtimeId);\n this.cancelReconnect(conversation);\n conversation.reconnectAttempt = 0;\n void this.ensureConnected(runtimeId, wsUrl);\n }\n\n async ensureConnected(runtimeId: string, wsUrl: string): Promise<DesktopConversationSnapshot> {\n const conversation = this.getOrCreate(runtimeId);\n \n // If already connected, return immediately\n if (conversation.ws?.readyState === WebSocket.OPEN) return publicConversation(conversation);\n \n // If currently connecting, wait for it\n if (conversation.connectPromise) {\n await conversation.connectPromise;\n return publicConversation(conversation);\n }\n\n // If reconnecting in background, return current state\n if (conversation.reconnectTimer) {\n return publicConversation(conversation);\n }\n\n // Start fresh connection\n conversation.reconnectUrl = wsUrl;\n conversation.reconnectAttempt = 0;\n await this.connect(runtimeId, wsUrl);\n return publicConversation(conversation);\n }\n\n /**\n * Internal connect method that actually establishes the WebSocket.\n */\n private async connect(runtimeId: string, wsUrl: string): Promise<void> {\n const conversation = this.getOrCreate(runtimeId);\n \n // Cancel any pending reconnect timer\n this.cancelReconnect(conversation);\n\n conversation.status = 'connecting';\n conversation.error = undefined;\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'connecting');\n\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n conversation.ws = ws;\n\n // Set connection timeout\n const timeout = setTimeout(() => {\n if (conversation.ws === ws) {\n ws.close();\n reject(new Error('Connection timeout'));\n }\n }, 10000);\n\n ws.once('open', () => {\n clearTimeout(timeout);\n conversation.status = 'connected';\n conversation.error = undefined;\n conversation.connectPromise = null;\n conversation.reconnectAttempt = 0;\n conversation.reconnectUrl = null;\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'connected');\n resolve();\n });\n\n ws.on('message', (data) => {\n this.handleServerMessage(conversation, data.toString());\n });\n\n ws.once('error', (err) => {\n clearTimeout(timeout);\n conversation.status = 'error';\n conversation.error = err instanceof Error ? err.message : String(err);\n conversation.connectPromise = null;\n this.appendMessage(conversation, {\n role: 'system',\n text: `Connection error: ${conversation.error}`,\n });\n this.emitChanged(conversation);\n this.emitReconnectEvent(conversation, 'error');\n reject(err);\n });\n\n ws.once('close', () => {\n clearTimeout(timeout);\n if (conversation.ws === ws) conversation.ws = null;\n conversation.connectPromise = null;\n \n if (conversation.status !== 'error') {\n conversation.status = 'disconnected';\n }\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n \n // Schedule reconnection if applicable\n if (conversation.reconnectUrl && conversation.status === 'disconnected') {\n this.scheduleReconnect(conversation);\n }\n });\n });\n }\n\n /**\n * Schedule a reconnection attempt with exponential backoff.\n */\n private scheduleReconnect(conversation: ConversationInternal): void {\n // Check if reconnection is enabled and within limits\n if (RECONNECT_CONFIG.maxAttempts === 0) return;\n if (conversation.reconnectAttempt >= RECONNECT_CONFIG.maxAttempts) {\n this.emitReconnectEvent(conversation, 'exhausted');\n return;\n }\n\n // Calculate delay with exponential backoff and jitter\n const baseDelay = Math.min(\n RECONNECT_CONFIG.initialDelayMs *\n RECONNECT_CONFIG.backoffMultiplier ** conversation.reconnectAttempt,\n RECONNECT_CONFIG.maxDelayMs,\n );\n \n // Add jitter\n const jitter = baseDelay * RECONNECT_CONFIG.jitterFactor * Math.random();\n const delay = Math.floor(baseDelay + jitter);\n\n conversation.reconnectAttempt++;\n this.emitReconnectEvent(conversation, 'scheduled', { delay, attempt: conversation.reconnectAttempt });\n\n conversation.reconnectTimer = setTimeout(() => {\n conversation.reconnectTimer = null;\n if (!conversation.reconnectUrl) return;\n \n // Check if still disconnected\n if (conversation.ws?.readyState !== WebSocket.OPEN) {\n void this.connect(conversation.runtimeId, conversation.reconnectUrl);\n }\n }, delay);\n }\n\n /**\n * Cancel pending reconnection.\n */\n private cancelReconnect(conversation: ConversationInternal): void {\n if (conversation.reconnectTimer) {\n clearTimeout(conversation.reconnectTimer);\n conversation.reconnectTimer = null;\n }\n }\n\n /**\n * Emit reconnection event for UI feedback.\n */\n private emitReconnectEvent(\n conversation: ConversationInternal,\n status: 'connecting' | 'connected' | 'error' | 'scheduled' | 'exhausted',\n data?: { delay?: number; attempt?: number },\n ): void {\n this.emit('reconnect', {\n runtimeId: conversation.runtimeId,\n status,\n attempt: conversation.reconnectAttempt,\n maxAttempts: RECONNECT_CONFIG.maxAttempts,\n ...data,\n });\n }\n\n async sendMessage(\n runtimeId: string,\n wsUrl: string,\n content: string,\n ): Promise<DesktopConversationSnapshot> {\n const trimmed = content.trim();\n if (!trimmed) return this.snapshot(runtimeId);\n \n // Reset reconnection state on manual action\n const conversation = this.getOrCreate(runtimeId);\n conversation.reconnectAttempt = 0;\n \n await this.ensureConnected(runtimeId, wsUrl);\n \n // Refresh conversation after connect\n const conv = this.getOrCreate(runtimeId);\n this.appendMessage(conv, {\n id: `user_${randomUUID()}`,\n role: 'user',\n text: trimmed,\n });\n conv.status = 'running';\n conv.activeAssistantMessageId = null;\n this.emitChanged(conv);\n this.send(conv, {\n type: 'user_message',\n payload: {\n id: `msg_${Date.now()}_${randomUUID().slice(0, 8)}`,\n content: trimmed,\n timestamp: Date.now(),\n ...(conv.sessionId ? { sessionId: conv.sessionId } : {}),\n },\n });\n return publicConversation(conv);\n }\n\n async abort(runtimeId: string, wsUrl: string): Promise<DesktopConversationSnapshot> {\n // Reset reconnection state on manual action\n const conversation = this.getOrCreate(runtimeId);\n conversation.reconnectAttempt = 0;\n \n await this.ensureConnected(runtimeId, wsUrl);\n const conv = this.getOrCreate(runtimeId);\n this.send(conv, {\n type: 'abort',\n payload: conv.sessionId ? { sessionId: conv.sessionId } : {},\n });\n conv.status = 'connected';\n this.appendMessage(conv, { role: 'system', text: 'Abort requested.' });\n return publicConversation(conv);\n }\n\n close(runtimeId: string): void {\n const conversation = this.conversations.get(runtimeId);\n if (!conversation) return;\n \n // Cancel reconnection and clear state\n this.cancelReconnect(conversation);\n conversation.reconnectAttempt = 0;\n conversation.reconnectUrl = null;\n \n conversation.ws?.close();\n conversation.ws = null;\n conversation.connectPromise = null;\n conversation.status = 'disconnected';\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n }\n\n closeAll(): void {\n for (const runtimeId of this.conversations.keys()) {\n this.close(runtimeId);\n }\n }\n\n private handleServerMessage(conversation: ConversationInternal, raw: string): void {\n let message: ServerMessage;\n try {\n message = JSON.parse(raw) as ServerMessage;\n } catch {\n return;\n }\n const payload = message.payload ?? {};\n switch (message.type) {\n case 'session.start': {\n const sessionId = stringValue(payload['sessionId']);\n if (sessionId) conversation.sessionId = sessionId;\n conversation.status = conversation.status === 'running' ? 'running' : 'connected';\n this.emitChanged(conversation);\n break;\n }\n case 'provider.text_delta': {\n conversation.status = 'running';\n this.appendAssistantDelta(conversation, stringValue(payload['text']) ?? '');\n break;\n }\n case 'tool.started': {\n conversation.status = 'running';\n const name = stringValue(payload['name']) ?? 'tool';\n this.appendMessage(conversation, { role: 'tool', text: `Started ${name}` });\n break;\n }\n case 'tool.executed': {\n const name = stringValue(payload['name']) ?? 'tool';\n const ok = payload['ok'] === true;\n this.appendMessage(conversation, {\n role: 'tool',\n text: `${name} ${ok ? 'completed' : 'failed'}`,\n });\n break;\n }\n case 'provider.error':\n case 'provider.stream_error':\n case 'error': {\n const text =\n stringValue(payload['message']) ??\n stringValue(payload['description']) ??\n `${message.type} received`;\n conversation.status = 'error';\n conversation.error = text;\n this.appendMessage(conversation, { role: 'system', text });\n break;\n }\n case 'run.result': {\n const finalText = stringValue(payload['finalText']);\n if (finalText && !this.lastAssistantHasText(conversation)) {\n this.appendMessage(conversation, { role: 'assistant', text: finalText });\n }\n conversation.status = payload['status'] === 'failed' ? 'error' : 'connected';\n conversation.activeAssistantMessageId = null;\n this.emitChanged(conversation);\n break;\n }\n }\n }\n\n private send(conversation: ConversationInternal, message: Record<string, unknown>): void {\n if (conversation.ws?.readyState !== WebSocket.OPEN) {\n throw new Error('Runtime socket is not connected');\n }\n conversation.ws.send(JSON.stringify(message));\n }\n\n private appendAssistantDelta(conversation: ConversationInternal, text: string): void {\n if (!text) return;\n let message = conversation.messages.find((m) => m.id === conversation.activeAssistantMessageId);\n if (!message) {\n message = {\n id: `assistant_${randomUUID()}`,\n role: 'assistant',\n text: '',\n timestamp: Date.now(),\n };\n conversation.activeAssistantMessageId = message.id;\n conversation.messages.push(message);\n }\n message.text += text;\n this.trimMessages(conversation);\n this.emitChanged(conversation);\n }\n\n private appendMessage(\n conversation: ConversationInternal,\n input: Partial<DesktopConversationMessage> & Pick<DesktopConversationMessage, 'role' | 'text'>,\n ): void {\n conversation.messages.push({\n id: input.id ?? `${input.role}_${randomUUID()}`,\n role: input.role,\n text: input.text,\n timestamp: input.timestamp ?? Date.now(),\n });\n this.trimMessages(conversation);\n this.emitChanged(conversation);\n }\n\n private lastAssistantHasText(conversation: ConversationInternal): boolean {\n const lastAssistant = [...conversation.messages].reverse().find((m) => m.role === 'assistant');\n return Boolean(lastAssistant?.text.trim());\n }\n\n private trimMessages(conversation: ConversationInternal): void {\n if (conversation.messages.length <= MAX_MESSAGES) return;\n conversation.messages.splice(0, conversation.messages.length - MAX_MESSAGES);\n }\n\n private getOrCreate(runtimeId: string): ConversationInternal {\n let conversation = this.conversations.get(runtimeId);\n if (conversation) return conversation;\n conversation = {\n runtimeId,\n status: 'disconnected',\n messages: [],\n ws: null,\n connectPromise: null,\n activeAssistantMessageId: null,\n reconnectAttempt: 0,\n reconnectTimer: null,\n reconnectUrl: null,\n };\n this.conversations.set(runtimeId, conversation);\n return conversation;\n }\n\n private emitChanged(conversation: ConversationInternal): void {\n this.emit('changed', publicConversation(conversation));\n }\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\nfunction publicConversation(conversation: ConversationInternal): DesktopConversationSnapshot {\n return {\n runtimeId: conversation.runtimeId,\n status: conversation.status,\n sessionId: conversation.sessionId,\n error: conversation.error,\n messages: conversation.messages.map((message) => ({ ...message })),\n };\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\n// ============================================================================\n// Reconnect Event Type (for external consumers)\n// ============================================================================\n\nexport interface DesktopBridgeReconnectEvent {\n runtimeId: string;\n status: 'connecting' | 'connected' | 'error' | 'scheduled' | 'exhausted';\n attempt: number;\n maxAttempts: number;\n delay?: number;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }