[ Agent Infrastructure ]

Energent AI

Panel de agente IA de nivel productivo con uso de computadora en vivo

EL DESAFÍO

La demo de computer-use del Vercel AI SDK ofrece una prueba de concepto mínima, pero no está lista para producción. El estado de VNC y el del chat están acoplados, lo que provoca que el visor se reinicialice con cada mensaje. Los límites de tipos entre paquetes del SDK son inconsistentes, generando aserciones de tipos en toda la base de código. No hay observabilidad sobre la duración de llamadas individuales a herramientas ni sobre el ciclo de vida de la sesión.

ROL:Full-Stack Developer
AÑO:2026
TIPO:Assessment Project
ESTADO:Public

STACK TECNOLÓGICO

Next.jsTypeScriptVercel AI SDKZustandReact.memoVNCWebSocketsTailwind CSS
I

Enfoque de ingeniería

Aislamiento de stores por diseño

Separé el estado de la sesión VNC del estado de chat y eventos en dos stores completamente independientes en Zustand antes de escribir cualquier UI. Los límites de stores fueron una decisión arquitectónica tomada de antemano, no una refactorización tras detectar problemas de rendimiento.

Sistema de tipos rastreado, no parcheado

En lugar de suprimir errores de TypeScript con aserciones de tipos, rastreé la jerarquía completa de tipos del AI SDK a través de @ai-sdk/ui-utils para encontrar el límite de tipo correcto. Refactoricé el manejo de mensajes para usar UIMessage de forma consistente en la capa UI.

Control explícito de renders

Envolví VNCViewer en React.memo con un comparador de igualdad personalizado en lugar de depender de la comparación superficial por defecto, dando control preciso sobre cuándo se reinicializa la conexión VNC.

Prevención de closures obsoletos

Usé useRef para el seguimiento de duración por llamada a herramienta en lugar de useState, asegurando que los callbacks siempre lean los valores actuales sin provocar re-renders ni capturar closures obsoletos.

II

Decisiones técnicas clave

Las elecciones que dieron forma a la arquitectura y determinaron la mantenibilidad a largo plazo.

Dos stores aisladas en Zustand

El pipeline de eventos y la gestión de sesión viven en stores completamente separadas sin suscripciones cruzadas.

POR QUÉ

Los re-renders de VNC son costosos y disruptivos — cortan la conexión en vivo. Una store compartida significa que cualquier actualización del chat dispara un diff de VNC. El aislamiento garantiza que el visor solo se re-renderice cuando el estado de sesión cambia genuinamente.

React.memo con comparador personalizado

VNCViewer está envuelto en React.memo con una función de igualdad explícita en lugar de la comparación superficial por defecto.

POR QUÉ

La comparación superficial por defecto sigue disparando re-renders cuando cambia la identidad de las props de objeto aunque los valores sean idénticos. El comparador explícito da control determinista sobre la reinicialización.

UIMessage en la capa UI

Estandaricé UIMessage de @ai-sdk/ui-utils como tipo usado en todos los componentes UI que manejan mensajes.

POR QUÉ

El tipo Message vive en la capa core del SDK y no lleva campos específicos de UI. Usarlo en la capa UI fuerza aserciones de tipos. UIMessage es el tipo de límite correcto y elimina el conflicto por completo.

useRef para seguimiento de duración

Los timestamps de inicio de llamadas a herramientas se almacenan en un ref, actualizados via partes de mensaje onFinish.

POR QUÉ

Almacenar timestamps en estado dispararía re-renders con cada inicio de llamada a herramienta. Un ref mantiene valores mutables sin afectar el ciclo de render, y siempre da a los callbacks acceso al valor actual.

III

Desafíos y soluciones

[ DESAFÍO ]

El visor VNC se reinicializaba con cada mensaje del chat, causando cortes de conexión en vivo durante sesiones activas del agente.

[ SOLUCIÓN ]

Aislé el estado de sesión VNC en una store Zustand dedicada sin suscripciones al estado del chat. Añadí React.memo con comparador explícito a VNCViewer para asegurar re-renders solo ante cambios genuinos de sesión.

[ DESAFÍO ]

El conflicto de tipos entre Message y UIMessage de @ai-sdk/react y @ai-sdk/ui-utils causaba errores TypeScript en todo el pipeline de manejo de mensajes.

[ SOLUCIÓN ]

Rastreé la jerarquía completa de tipos a través de los paquetes del AI SDK para identificar el límite de tipo correcto. Refactoricé todo el manejo de mensajes en la capa UI para usar UIMessage de forma consistente, eliminando cada aserción de tipos.

[ DESAFÍO ]

El seguimiento de duración por llamada a herramienta via callbacks de eventos leía timestamps de inicio obsoletos debido a la captura del closure en el momento del registro.

[ SOLUCIÓN ]

Moví el almacenamiento de timestamps de estado a useRef, dando a todos los callbacks una referencia mutable que siempre apunta al valor actual independientemente de cuándo se creó el closure.

IV

Resultados e impacto

El panel ejecuta sesiones de agente estables con cero cortes de conexión VNC ante actualizaciones del chat, un pipeline de mensajes completamente seguro en tipos sin aserciones, y datos precisos de duración por llamada a herramienta visibles en el timeline de eventos.

0
Type assertions in codebase
2
Isolated Zustand stores
0
VNC drops on chat update

APRENDIZAJES CLAVE

Las decisiones arquitectónicas sobre límites de estado deben tomarse antes de escribir UI, no después. Refactorizar el aislamiento de stores en un sistema acoplado cuesta mucho más que diseñar el límite desde el principio.