# How the Vue 3 WebSocket Client Manages Real-Time Task Updates in MathModelAgent

> Learn how the Vue 3 WebSocket client in MathModelAgent uses a custom wrapper and Pinia for real-time task updates, leveraging Vue 3 reactivity for seamless UI changes.

- Repository: [Sanjin/mathmodelagent](https://github.com/jihe520/mathmodelagent)
- Tags: how-to-guide
- Published: 2026-03-04

---

**The MathModelAgent frontend uses a custom `TaskWebSocket` wrapper class integrated with a Pinia store to handle WebSocket connections, automatically updating the UI through Vue 3's reactivity system when new task messages arrive.**

The MathModelAgent project implements a clean, reactive pipeline for real-time task updates using Vue 3's Composition API and Pinia state management. The frontend establishes WebSocket connections through a lightweight wrapper that parses JSON payloads and feeds them into reactive store properties, enabling instant UI updates without polling. This architecture decouples connection management from UI components while maintaining type safety and predictable state flow.

## The TaskWebSocket Wrapper Class

The frontend encapsulates the native WebSocket API in a minimal wrapper defined in [[`src/utils/websocket.ts`](https://github.com/jihe520/mathmodelagent/blob/main/src/utils/websocket.ts)](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/utils/websocket.ts). This `TaskWebSocket` class provides a typed interface for connection lifecycle management and message handling.

The wrapper maintains three core properties: a native `WebSocket` instance, the target URL, and an `onMessage` callback function. The `connect()` method opens the socket, attaches event listeners for incoming messages, and parses JSON payloads before forwarding them to the registered callback. For outgoing communication, the `send(data)` method serializes objects to JSON, while `close()` terminates the connection cleanly.

## Pinia Store Integration

State management lives in [[`src/stores/task.ts`](https://github.com/jihe520/mathmodelagent/blob/main/src/stores/task.ts)](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/stores/task.ts), where the `useTaskStore` composable initializes and controls the WebSocket lifecycle.

### Connection Management

The store exposes a `connectWebSocket(taskId)` method that constructs the WebSocket URL using the `VITE_WS_URL` environment variable, appends the specific task ID, and instantiates a `TaskWebSocket` with a message handler. Incoming messages are pushed into a reactive `messages` array that serves as the single source of truth for all real-time updates.

```typescript
import { useTaskStore } from '@/stores/task'

export default {
  setup() {
    const taskStore = useTaskStore()
    const taskId = '12345' // obtained from route or props

    // Open WS connection on component mount
    taskStore.connectWebSocket(taskId)

    return {
      chatMessages: taskStore.chatMessages,
      addUserMessage: taskStore.addUserMessage,
    }
  },
}

```

### Message Filtering and Computed Properties

The store defines computed properties such as `chatMessages`, `coderMessages`, and `writerMessages` that filter the raw `messages` array by `msg_type` and `agent_type` fields. Vue components subscribe to these computed properties rather than accessing the raw array directly, ensuring components receive only relevant data subsets for their specific views.

## Managing the WebSocket Lifecycle

Unlike some auto-connecting implementations, the MathModelAgent store requires explicit lifecycle management. The UI component must call `connectWebSocket(taskId)` when mounting, typically when a user navigates to a specific task page.

```typescript
import { useTaskStore } from '@/stores/task'
import { onBeforeUnmount } from 'vue'

export default {
  setup() {
    const taskStore = useTaskStore()
    const taskId = '12345'

    taskStore.connectWebSocket(taskId)

    // Clean up on component unmount
    onBeforeUnmount(() => {
      taskStore.closeWebSocket()
    })

    return {
      chatMessages: taskStore.chatMessages,
    }
  },
}

```

The `closeWebSocket()` helper method in the store terminates the connection and clears the instance, preventing memory leaks and orphaned connections when users navigate away from task pages.

## Real-Time UI Updates

When the WebSocket receives a new message, the `onMessage` callback pushes the parsed data into the reactive `messages` array. Because the store uses Vue 3's reactivity system, any component subscribing to `chatMessages`, `coderMessages`, or other computed properties automatically re-renders to reflect the updated state.

Type definitions for the various message shapes are located in [[`src/utils/response.ts`](https://github.com/jihe520/mathmodelagent/blob/main/src/utils/response.ts)](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/utils/response.ts), ensuring TypeScript safety when handling different `msg_type` values across the application.

```typescript
// Sending messages back to the backend if needed
taskStore.ws?.send({ type: 'ping' })

```

## Summary

- **Minimal wrapper**: The `TaskWebSocket` class in [`src/utils/websocket.ts`](https://github.com/jihe520/mathmodelagent/blob/main/src/utils/websocket.ts) abstracts native WebSocket complexity while exposing `connect()`, `send()`, and `close()` methods.
- **Centralized state**: The Pinia store in [`src/stores/task.ts`](https://github.com/jihe520/mathmodelagent/blob/main/src/stores/task.ts) manages the connection lifecycle and maintains a reactive `messages` array as the single source of truth.
- **Filtered views**: Computed properties like `chatMessages` and `coderMessages` filter raw messages by type, allowing components to subscribe only to relevant updates.
- **Explicit lifecycle**: Components must manually initiate connections on mount and close them on unmount, preventing resource leaks and unwanted background connections.

## Frequently Asked Questions

### How do I connect to the WebSocket for a specific task?

Import the `useTaskStore` composable from `@/stores/task` and call `connectWebSocket(taskId)`, passing the task identifier obtained from your route parameters or props. The method constructs the full WebSocket URL using the `VITE_WS_URL` environment variable and establishes the connection.

### What triggers UI updates when new messages arrive?

The `TaskWebSocket` wrapper parses incoming JSON and pushes it to the store's reactive `messages` array. Vue 3's reactivity system detects this mutation and automatically updates any component subscribing to the store's computed properties (such as `chatMessages` or `coderMessages`), triggering a re-render without manual polling.

### Where are message type definitions located?

Type definitions for WebSocket message payloads reside in [[`src/utils/response.ts`](https://github.com/jihe520/mathmodelagent/blob/main/src/utils/response.ts)](https://github.com/jihe520/mathmodelagent/blob/main/frontend/src/utils/response.ts). This file contains interfaces for different `msg_type` values and `agent_type` classifications, ensuring TypeScript type safety when processing real-time updates in the store and components.

### How do I properly close the WebSocket connection?

Call `closeWebSocket()` on the task store instance, typically inside an `onBeforeUnmount` lifecycle hook in your Vue component. This method terminates the underlying WebSocket connection and cleans up the instance, preventing memory leaks and ensuring the connection doesn't persist after the user navigates away.