Fix edit revert + leak de unidad de red · tablas markdown editoriales · runtime Python robusto
La versión 7.9.0 corrige tres bugs críticos: (1) al editar un mensaje y regenerar la respuesta, el cliente "volvía" a mostrar la respuesta antigua al acabar streaming hasta cambiar de chat — fix en el effect de message_versions; (2) la herramienta de unidad de red seguía activándose en regen aunque el cliente la enviara como false — fix en chat/route.ts para no inferir intent ni hacer fallback a attachments del histórico cuando hay regenerate_message_id; (3) el code interpreter fallaba con "No hay runtime de Python" cuando Python sí estaba instalado pero no en PATH del proceso Node — detección robusta con sweep de rutas, where/which y caché. También rediseña las tablas markdown con estética editorial profesional (zebra, hover ámbar, tipografía optical, dark mode).
Nuevo
- Fallback a `where python`/`where py` en Windows y `which python3`/`which python` en Unix antes de rendirse.
- attachmentFileIdsForRag = [] en regen sin attachments explícitos: ni siquiera se consulta /files de la conversación. Antes se llevaba todos los attachments_json del histórico al RAG aunque el usuario no los hubiera mencionado.
- effectiveDbQuery, effectiveGmailRead, effectiveGmailSend también respetan flags explícitas en regen (no se infieren del input editado).
Mejorado
- BUG CRÍTICO 1 — Edit message revert: tras editar un mensaje y recibir nueva respuesta, al acabar streaming la UI volvía a la respuesta anterior. Sólo cambiar de chat y volver mostraba la nueva. Fix en MessageBubble.tsx: el useEffect que cachea message_versions sólo se disparaba al montar; ahora también refetchea cuando cambia message.content / updated_at / edit_version / forcedAssistantVersion.
- Tablas markdown rediseñadas con estética editorial: borde sutil, header zinc-50 con border-bottom 2px, zebra striping suave, hover ámbar, tipografía optical (uppercase 10.5px en headers con tracking 0.08em, 13.5px tabular-nums en cuerpo), primera columna en negrita, dark mode nativo, scroll horizontal limpio.
- Caché del binario Python resuelto durante 5 minutos: la primera ejecución prueba candidatos, las siguientes van directas al binario que funcionó. Auto-renovación tras instalar.
Arreglado
- BUG CRÍTICO 2 — Network drive en edit: aunque la v7.7.0 ya enviaba network_drive_rag: false desde el cliente, el backend SEGUÍA activándolo por dos rutas: (a) inferredNetworkDriveIntent del texto del input editado, (b) fallback a recentConversationAttachmentIds del histórico de la conversación. Fix en chat/route.ts: cuando regenerate_message_id está presente, NO se infieren intents ni se hace fallback al histórico — sólo se respetan las flags y attachments explícitos del cliente.
- BUG 3 — Runtime de Python: el code interpreter fallaba con "No hay runtime de Python disponible" en Windows incluso con Python instalado, porque el spawn no encontraba `python`/`py` en el PATH del proceso Node (típico tras instalar sin reiniciar el shell del dev server).
- Detección de Python robusta: sweep de rutas habituales en Windows (LOCALAPPDATA\Programs\Python\Python313/312/.../38, Program Files\Python*) y macOS/Linux (/usr/local/bin, /opt/homebrew/bin, framework de macOS, pyenv).
- Mensaje de error mejorado: lista los candidatos probados, incluye el path típico para CODE_INTERPRETER_PYTHON_BIN según SO, y recuerda que tras instalar Python hay que reiniciar el dev server.
Fix CRÍTICO — Edit message revert
- Síntoma: usuario envía mensaje → recibe respuesta A → edita el mensaje → streaming muestra respuesta B correctamente → al acabar streaming la UI vuelve a A → cambiar de chat y volver muestra B (la real, ya persistida en DB).
- Causa raíz: el useEffect que carga `message_versions` desde Supabase sólo se ejecutaba al montar el componente (deps `[isAssistant, message.id]`). Tras editar, message.id no cambia (se reusa el regen_target_message_id) — el effect no refetchea. assistantVersions queda con `[{version_index: 1, content: respuestaA}]`.
- displayContent (línea 2535) prioriza `assistantVersions[idx].content` sobre `message.content`. Como assistantVersions está cacheada con respuestaA, ése es el que se renderiza, aunque message.content de loadMessages ya sea respuestaB.
- Fix: añadir `message.content`, `message.updated_at`, `message.edit_version` y `forcedAssistantVersion` a las dependencias del effect. Cualquier señal de regeneración refresca el cache local.
- Fix bonus: el effect ahora usa flag de cancelación (cancelled=true en cleanup) para evitar setState tras unmount durante navegación entre chats.
- Fix bonus: si ya hay un override `forcedAssistantVersion`, lo respeta al reposicionar el activeAssistantVersionIdx (antes saltaba siempre al último, lo que rompía la navegación entre versiones del usuario).
- Fix bonus: comparación previMax vs nextMax para evitar reemplazar el array si la query devuelve menos versiones (race con loadMessages).
Fix CRÍTICO — Network drive leak en regen
- Síntoma: aunque la v7.7.0 corrigió el cliente para mandar `network_drive_rag: false` y `attachments: []` cuando ninguna herramienta los necesita, el backend seguía adjuntando archivos del contexto a la respuesta editada (chips "generame-un-docx-sobre…" bajo el mensaje).
- Causa raíz 1 — inferencia textual: chat/route.ts línea 5429 calculaba `effectiveNetworkDriveRag = network_drive_rag || inferredNetworkDriveIntent || explicitFileReferences.length > 0`. Si el contenido editado contenía palabras como "documento", "lista", "manual", "explica" + scope, se reactivaba la herramienta a espaldas del cliente.
- Causa raíz 2 — fallback a attachments del histórico: chat/route.ts línea 6872 calculaba `attachmentFileIdsForRag = explicitAttachmentFileIds.length > 0 ? explicitAttachmentFileIds : recentConversationAttachmentIds`. En regen el cliente manda explicitAttachmentFileIds=[] → caía al histórico → traía PDFs antiguos al contexto RAG.
- Fix 1: cuando `regenerate_message_id` está presente, las inferencias `inferredNetworkDriveIntent`, `inferredDbQueryIntent`, `inferredGmailReadIntent`, `inferredGmailSendIntent` se ignoran. Sólo cuentan las flags explícitas del cliente.
- Fix 2: cuando `regenerate_message_id` está presente Y el cliente NO mandó attachments explícitos, attachmentFileIdsForRag = []. No se hace fallback al histórico.
- Resultado: editar "¿Qué puedes hacer?" devuelve respuesta limpia sin chips de PDFs viejos, sin reactivar herramientas que el usuario no había marcado.
Fix — Code interpreter Python runtime
- Síntoma: en Windows, pulsar "Ejecutar" en un bloque de código Python devuelve "No hay runtime de Python disponible en el servidor (python3/python/py)" aunque Python esté instalado en C:\Python313\python.exe o en LOCALAPPDATA.
- Causa raíz: el proceso Node del dev server se inició antes de añadir Python al PATH (o tras instalar Python sin reiniciar el shell), por lo que `spawn("python")` falla con ENOENT en los 3 candidatos típicos.
- Fix 1 — sweep de rutas habituales: nueva función `discoverExtraPythonPaths()` que busca en LOCALAPPDATA\Programs\Python\Python{313,312,311,310,39,38}\python.exe y Program Files\Python*\python.exe en Windows, y /usr/local/bin/python3, /opt/homebrew/bin/python3, /Library/Frameworks/Python.framework/... en macOS/Linux.
- Fix 2 — fallback con `where`/`which`: si los candidatos comunes fallan, hace `where python`, `where py` (Windows) o `which python3`, `which python` (Unix) y añade los resultados a la lista de candidatos antes de rendirse.
- Fix 3 — caché del binario resuelto: una vez encontrado un Python que funciona, se cachea durante 5 minutos. Las siguientes ejecuciones van directas al binario sin probar candidatos. Auto-renovación tras expirar.
- Fix 4 — mensaje de error mejorado: lista los candidatos probados, incluye el path típico para CODE_INTERPRETER_PYTHON_BIN por SO (C:\Python312\python.exe / /usr/local/bin/python3) y recuerda reiniciar `npm run dev` tras instalar Python.
- Sin breaking changes: si CODE_INTERPRETER_PYTHON_BIN está configurado, sigue siendo prioridad absoluta.
Tablas markdown — rediseño editorial
- Antes: tabla con borde rounded-2xl, header bg-zinc-100/90, sin zebra, sin hover, sin dark mode, fuente sm uniforme, primera columna sin distinción.
- Ahora: borde rounded-lg con border zinc-200/80 dark:zinc-700/60 + shadow-sm.
- Header: bg-zinc-50 dark:bg-zinc-800/60 con border-bottom 2px gruesa, tipografía uppercase 10.5px tracking 0.08em color zinc-500.
- Body: zebra striping sutil con `[&>tr:nth-child(even)]:bg-zinc-50/60` (light) y `dark:[&>tr:nth-child(even)]:bg-zinc-800/30`.
- Hover de fila: bg-amber-50/40 (light) / bg-amber-500/5 (dark) — destaca la fila apuntada sin saturar.
- Bordes internos finos zinc-100 dark:zinc-800/60, last:border-b-0 (no double border al final).
- Padding cómodo px-3.5 py-2.5, primer y último td/th con first:pl-4 / last:pr-4 para respiración.
- Tipografía del cuerpo: 13.5px tabular-nums, `<strong>` se renderiza zinc-900 dark:zinc-50 font-semibold (datos resaltados), primera columna automáticamente font-medium text-zinc-900 dark:text-zinc-50 (parece label).
- Soporte dark mode nativo en todos los rincones de la tabla.
- Scroll horizontal cuando la tabla excede el contenedor (overflow-x-auto + min-width 360px sm 480px).
Calidad técnica
- Typecheck verde tras todas las modificaciones.
- Sin breaking changes: el contrato de la API de chat sigue igual, sólo cambia la priorización interna de flags en regen.
- Cache de python bin con expiración: evita filtraciones de rutas obsoletas tras desinstalar/mover Python.
- Effect de message_versions con cancellation flag: previene setState on unmounted component al cambiar rápido de chat.
