> ## Documentation Index
> Fetch the complete documentation index at: https://docs.standardagents.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# @standardagents/vue

> Vue 3 composables and plugin for Standard Agents Builder - connect to AI agent threads with real-time updates

## Overview

The `@standardagents/vue` package provides Vue 3 composables and a plugin for building UIs that connect to Standard Agents Builder threads. It handles real-time message streaming, custom event listening, and workblock transformation for tool visualization.

> [card] Key Features
>
> * Real-time WebSocket updates for live message streaming
> * Vue plugin with provide/inject architecture
> * Workblock transformation for UI-friendly tool displays
> * Custom event system for backend-to-frontend communication
> * File uploads and attachments for sending images to the LLM
> * TypeScript support with full type definitions
> * Works with any Vue 3 framework (Nuxt, Vite, etc.)

## Installation

```bash npm theme={null}
npm install @standardagents/vue
```

```bash pnpm theme={null}
pnpm add @standardagents/vue
```

```bash yarn theme={null}
yarn add @standardagents/vue
```

**Requirements:** Vue 3.3+

## Quick Start

```typescript theme={null}
// main.ts
import { createApp } from 'vue'
import { StandardAgentsPlugin } from '@standardagents/vue'
import App from './App.vue'

const app = createApp(App)
app.use(StandardAgentsPlugin, { endpoint: 'https://your-api.com' })
app.mount('#app')
```

```vue theme={null}
<!-- App.vue -->
<script setup lang="ts">
import { ThreadProvider } from '@standardagents/vue'
import ChatInterface from './ChatInterface.vue'
</script>

<template>
  <ThreadProvider threadId="123e4567-e89b-12d3-a456-426614174000">
    <ChatInterface />
  </ThreadProvider>
</template>
```

```vue theme={null}
<!-- ChatInterface.vue -->
<script setup lang="ts">
import { ref } from 'vue'
import { useThread } from '@standardagents/vue'

const { messages, sendMessage, status } = useThread()
const input = ref('')

const handleSend = async () => {
  if (input.value.trim()) {
    await sendMessage({ role: 'user', content: input.value })
    input.value = ''
  }
}
</script>

<template>
  <div>
    <p>Status: {{ status }}</p>
    <div v-for="msg in messages" :key="msg.id">
      <strong>{{ msg.role }}:</strong> {{ msg.content }}
    </div>
    <input v-model="input" @keyup.enter="handleSend" />
    <button @click="handleSend">Send</button>
  </div>
</template>
```

## Plugin Setup

### StandardAgentsPlugin

Install the plugin when creating your Vue application to configure the API endpoint.

```typescript theme={null}
import { createApp } from 'vue'
import { StandardAgentsPlugin } from '@standardagents/vue'

const app = createApp(App)
app.use(StandardAgentsPlugin, { endpoint: 'https://api.example.com' })
```

**Options:**

| Option | Type | Required | Description |
| - | - | - | - |
| `endpoint` | `string` | Yes | The API endpoint URL |

## Components

### ThreadProvider

Component that establishes a WebSocket connection to a thread and provides context to child components.

```vue theme={null}
<script setup lang="ts">
import { ThreadProvider } from '@standardagents/vue'
</script>

<template>
  <ThreadProvider
    threadId="123e4567-e89b-12d3-a456-426614174000"
    :preload="true"
    :live="true"
    :useWorkblocks="false"
    :depth="0"
    :includeSilent="false"
  >
    <ChatInterface />
  </ThreadProvider>
</template>
```

**Props:**

| Prop | Type | Default | Description |
| - | - | - | - |
| `threadId` | `string` | Required | The thread ID to connect to |
| `preload` | `boolean` | `true` | Fetch existing messages on mount |
| `live` | `boolean` | `true` | Enable WebSocket for live updates |
| `useWorkblocks` | `boolean` | `false` | Transform tool calls into workblocks |
| `depth` | `number` | `0` | Max message depth (0 = top-level only) |
| `includeSilent` | `boolean` | `false` | Include silent messages |
| `endpoint` | `string` | - | Override endpoint from plugin |

## Composables

### useStandardAgents

Access the Standard Agents client directly for advanced operations.

```vue theme={null}
<script setup lang="ts">
import { useStandardAgents } from '@standardagents/vue'

const { client, endpoint } = useStandardAgents()

// Create a new thread
const createNewThread = async () => {
  const thread = await client.createThread({ agent_id: 'my-agent' })
  console.log('Created:', thread.id)
}
</script>
```

**Returns:**

| Property | Type | Description |
| - | - | - |
| `client` | `AgentBuilderClient` | The HTTP/WebSocket client instance |
| `endpoint` | `string` | The configured endpoint URL |

### useThread

Composable to access the full thread context. Must be used within a `ThreadProvider`.

```vue theme={null}
<script setup lang="ts">
import { useThread } from '@standardagents/vue'

const {
  // Messages & State
  threadId,
  messages,
  workblocks,
  status,
  loading,
  error,
  options,

  // Actions
  sendMessage,
  stopExecution,
  deleteMessage,

  // Custom Events
  onEvent,

  // File Management (uploads to filesystem)
  files,
  addFiles,
  removeFile,
  getFileUrl,
  getPreviewUrl,

  // Attachment Management (sent to LLM)
  attachments,
  addAttachment,
  removeAttachment,
  clearAttachments,
} = useThread()
</script>
```

## Method Reference

### sendMessage

Send a message to the thread. Automatically includes any pending attachments and clears them after sending.

```vue theme={null}
<script setup lang="ts">
const { sendMessage, attachments } = useThread()

async function handleSend() {
  // Simple text message
  await sendMessage({ role: 'user', content: 'Hello!' })

  // With attachments (call addAttachment first)
  // Attachments are auto-included and cleared after send
  await sendMessage({ role: 'user', content: 'What is in this image?' })
}
</script>
```

**Signature:**

```typescript theme={null}
sendMessage(payload: Omit<SendMessagePayload, 'attachments'>): Promise<Message>

interface SendMessagePayload {
  role: 'user' | 'assistant' | 'system'
  content: string
  silent?: boolean
}
```

**Optimistic UI:** When you call `sendMessage()`, an optimistic message with attachment previews is shown immediately. The real message replaces it when confirmed by the server via WebSocket.

***

### addAttachment

Queue file(s) to be sent with the next message. Files are NOT uploaded immediately - they're held locally with preview URLs and sent inline (base64) when `sendMessage()` is called.

**Use this for images/files that should be sent directly to the LLM.**

```vue theme={null}
<script setup lang="ts">
const { addAttachment, attachments, removeAttachment } = useThread()

function handleFileSelect(event: Event) {
  const input = event.target as HTMLInputElement
  if (input.files?.length) {
    addAttachment(input.files)
  }
}
</script>

<template>
  <input type="file" @change="handleFileSelect" multiple />

  <!-- Show previews for pending attachments -->
  <div v-for="a in attachments" :key="a.id">
    <img v-if="a.isImage && a.previewUrl" :src="a.previewUrl" :alt="a.name" />
    <span>{{ a.name }}</span>
    <button @click="removeAttachment(a.id)">Remove</button>
  </div>
</template>
```

**Signature:**

```typescript theme={null}
addAttachment(files: File | File[] | FileList): void
```

**PendingAttachment Type:**

```typescript theme={null}
interface PendingAttachment {
  id: string              // Temporary ID
  file: File              // Original file object
  name: string
  mimeType: string
  size: number
  isImage: boolean
  previewUrl: string | null  // Object URL for image preview
  width?: number
  height?: number
}
```

***

### removeAttachment

Remove a pending attachment before sending.

```vue theme={null}
<script setup lang="ts">
const { removeAttachment, attachments } = useThread()
</script>

<template>
  <div v-for="a in attachments" :key="a.id">
    <span>{{ a.name }}</span>
    <button @click="removeAttachment(a.id)">Remove</button>
  </div>
</template>
```

**Signature:**

```typescript theme={null}
removeAttachment(id: string): void
```

***

### clearAttachments

Remove all pending attachments.

```vue theme={null}
<script setup lang="ts">
const { clearAttachments, attachments } = useThread()
</script>

<template>
  <button v-if="attachments.length" @click="clearAttachments">
    Clear All
  </button>
</template>
```

**Signature:**

```typescript theme={null}
clearAttachments(): void
```

***

### addFiles

Upload files to the thread's filesystem. Files are uploaded immediately and stored persistently.

**Use this for documents/files that should be stored on the thread but NOT sent directly to the LLM.**

```vue theme={null}
<script setup lang="ts">
const { addFiles, files, getPreviewUrl } = useThread()

function handleUpload(event: Event) {
  const input = event.target as HTMLInputElement
  if (input.files?.length) {
    addFiles(input.files)
  }
}
</script>

<template>
  <input type="file" @change="handleUpload" multiple />

  <div v-for="f in files" :key="f.id">
    <img v-if="f.isImage" :src="getPreviewUrl(f)" :alt="f.name" />
    <span>{{ f.name }}</span>
    <span>{{ f.status }}</span> <!-- 'uploading' | 'ready' | 'committed' | 'error' -->
  </div>
</template>
```

**Signature:**

```typescript theme={null}
addFiles(files: File[] | FileList): void
```

**ThreadFile Type:**

```typescript theme={null}
interface ThreadFile {
  id: string
  name: string
  mimeType: string
  size: number
  isImage: boolean
  localPreviewUrl: string | null
  status: 'uploading' | 'ready' | 'committed' | 'error'
  error?: string
  path?: string
  width?: number
  height?: number
  messageId?: string
}
```

***

### removeFile

Remove a pending file upload (cannot remove already committed files).

```vue theme={null}
<script setup lang="ts">
const { removeFile } = useThread()
</script>

<template>
  <button @click="removeFile(file.id)">Cancel Upload</button>
</template>
```

***

### File URL Helpers

```vue theme={null}
<script setup lang="ts">
const { getFileUrl, getThumbnailUrl, getPreviewUrl } = useThread()

// Full file URL (for download/viewing)
const url = getFileUrl(file)

// Thumbnail URL (for images)
const thumb = getThumbnailUrl(file)

// Preview URL (smart - uses local preview for pending, thumbnail for committed)
const preview = getPreviewUrl(file)
</script>
```

***

### stopExecution

Stop the current agent execution. Calling it when nothing is running is safe: the request succeeds and the thread is left unchanged.

```vue theme={null}
<script setup lang="ts">
const { stopExecution } = useThread()
</script>

<template>
  <button @click="stopExecution">Stop</button>
</template>
```

***

### deleteMessage

Delete a message from the thread. The message is optimistically removed from the UI immediately. If the server request fails, the message is restored.

```vue theme={null}
<script setup lang="ts">
const { deleteMessage, messages } = useThread()

async function handleDelete(messageId: string) {
  try {
    await deleteMessage(messageId)
  } catch (err) {
    console.error('Failed to delete:', err)
  }
}
</script>

<template>
  <div v-for="msg in messages" :key="msg.id">
    <span>{{ msg.content }}</span>
    <button @click="handleDelete(msg.id)">Delete</button>
  </div>
</template>
```

**Signature:**

```typescript theme={null}
deleteMessage(messageId: string): Promise<void>
```

**Note:** Server-side attachment files are automatically cleaned up when a message is deleted.

***

### onEvent / subscribeToEvent

Subscribe to custom events emitted by the agent.

```vue theme={null}
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'

const { onEvent } = useThread()

let unsubscribe: (() => void) | null = null

onMounted(() => {
  unsubscribe = onEvent<{ status: string }>('progress', (data) => {
    console.log('Progress:', data.status)
  })
})

onUnmounted(() => {
  unsubscribe?.()
})
</script>
```

## addFiles vs addAttachment

| Feature | `addFiles()` | `addAttachment()` |
| - | - | - |
| **Purpose** | Filesystem storage | Send to LLM |
| **Upload timing** | Immediate | On `sendMessage()` |
| **Storage** | Thread filesystem (persistent) | Inline base64 |
| **Image processing** | None (raw storage) | Server-side resize/optimize |
| **Use case** | Documents, persistent files | Chat images to LLM |

## Real-time File Synchronization

The `files` computed ref from `useThread()` automatically stays synchronized with the server. When files are added, modified, or deleted on the thread (by the agent, tools, or other clients), the `files` array updates in real-time via WebSocket.

### File Events

The SDK listens for three file-related events:

| Event | Description |
| - | - |
| `file_created` | A new file was added to the thread filesystem |
| `file_updated` | An existing file was modified |
| `file_deleted` | A file was removed from the thread filesystem |

### Custom File Event Handling

You can also subscribe to these events directly for custom handling:

```vue theme={null}
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
import { useThread } from '@standardagents/vue'

const { onEvent } = useThread()

let unsubscribe: (() => void) | null = null

onMounted(() => {
  unsubscribe = onEvent<{ path: string; file: { name: string; mimeType: string; size: number } }>('file_created', (data) => {
    console.log('File created:', data.path)
    // Custom notification, analytics, etc.
  })
})

onUnmounted(() => unsubscribe?.())
</script>
```

### How It Works

1. **Initial load**: When `ThreadProvider` mounts, it fetches the current file list from the server
2. **Live updates**: WebSocket events update the `files` array as changes occur
3. **Optimistic UI**: Local uploads via `addFiles()` appear immediately as "uploading" then transition to "committed"
4. **Deduplication**: Server files take priority over local pending files with the same path

## useThread Return Value

**Full ThreadContext:**

| Property | Type | Description |
| - | - | - |
| `threadId` | `string` | The current thread ID |
| `messages` | `ComputedRef<Message[]>` | Reactive array of raw messages |
| `workblocks` | `ComputedRef<ThreadMessage[]>` | Messages transformed to workblocks (if `useWorkblocks` is true) |
| `status` | `ComputedRef<ConnectionStatus>` | Connection status (alias: `connectionStatus`) |
| `loading` | `ComputedRef<boolean>` | Loading state (alias: `isLoading`) |
| `error` | `ComputedRef<Error \| null>` | Error state |
| `options` | `ThreadProviderOptions` | Resolved options from ThreadProvider props |
| `sendMessage` | `(payload) => Promise<Message>` | Send a message (auto-includes attachments) |
| `stopExecution` | `() => Promise<void>` | Cancel execution |
| `deleteMessage` | `(messageId: string) => Promise<void>` | Delete a message (optimistic UI) |
| `onEvent` | `<T>(type, callback) => () => void` | Subscribe to events |
| `files` | `ComputedRef<ThreadFile[]>` | All files (pending uploads + committed) |
| `addFiles` | `(files) => void` | Upload files to filesystem |
| `removeFile` | `(id) => void` | Remove a pending file |
| `getFileUrl` | `(file) => string` | Get file URL |
| `getThumbnailUrl` | `(file) => string` | Get thumbnail URL |
| `getPreviewUrl` | `(file) => string \| null` | Get preview URL |
| `attachments` | `Ref<PendingAttachment[]>` | Pending attachments for next message |
| `addAttachment` | `(files) => void` | Add attachment(s) for next message |
| `removeAttachment` | `(id) => void` | Remove a pending attachment |
| `clearAttachments` | `() => void` | Clear all pending attachments |

**ConnectionStatus:**

```typescript theme={null}
type ConnectionStatus = 'connecting' | 'connected' | 'reconnecting' | 'disconnected'
```

### Custom Events

Listen for custom events emitted by the backend using `onEvent` from `useThread()`:

```vue theme={null}
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { useThread } from '@standardagents/vue'

const { onEvent } = useThread()
const gameStatus = ref<{ success: boolean } | null>(null)

let unsubscribe: (() => void) | null = null

onMounted(() => {
  unsubscribe = onEvent<{ success: boolean }>('game_built', (data) => {
    gameStatus.value = data
  })
})

onUnmounted(() => unsubscribe?.())
</script>

<template>
  <div v-if="gameStatus">
    Game ready: {{ gameStatus.success }}
  </div>
</template>
```

**Backend Integration:**

Events are emitted from the backend using `emitThreadEvent()`:

```typescript theme={null}
// In a tool or hook on the backend
import { emitThreadEvent } from "@standardagents/builder"

emitThreadEvent(flow, "game_built", { success: true })
```

## Workblocks

Workblocks are a UI-friendly transformation of tool calls. When `useWorkblocks: true`, consecutive assistant messages with tool calls are grouped into workblocks.

> [tab] Raw Messages
>
> ```
> assistant (with tool_calls) → tool result → tool result → assistant (final)
> ```

> [tab] Transformed Workblocks
>
> ```
> workblock {
>   workItems: [tool_call, tool_result, tool_call, tool_result]
>   status: 'completed' | 'pending' | 'failed'
> } → assistant (final)
> ```

### WorkMessage Type

```typescript theme={null}
interface WorkMessage {
  id: string
  type: "workblock"
  content: string | null
  reasoning_content?: string | null
  workItems: WorkItem[]
  status: "pending" | "completed" | "failed"
  created_at: number
  depth?: number
}

interface WorkItem {
  id: string
  type: "tool_call" | "tool_result"
  name?: string
  content: string | null
  status?: "pending" | "success" | "error" | null
  tool_call_id?: string
}
```

### Rendering Workblocks

Use `ThreadProvider` with `:useWorkblocks="true"` and access via `useThread()`:

```vue theme={null}
<!-- Parent component sets up ThreadProvider with useWorkblocks -->
<template>
  <ThreadProvider threadId="thread-id" :useWorkblocks="true">
    <MessageList />
  </ThreadProvider>
</template>
```

```vue theme={null}
<!-- MessageList.vue -->
<script setup lang="ts">
import { useThread } from '@standardagents/vue'

const { workblocks } = useThread()
</script>

<template>
  <div v-for="msg in workblocks" :key="msg.id">
    <template v-if="msg.type === 'workblock'">
      <div :class="['workblock', msg.status]">
        <div v-for="item in msg.workItems" :key="item.id">
          <span v-if="item.type === 'tool_call'">Calling {{ item.name }}...</span>
          <span v-else>{{ item.content }}</span>
        </div>
      </div>
    </template>
    <template v-else>
      <div :class="msg.role">{{ msg.content }}</div>
    </template>
  </div>
</template>
```

## TypeScript Support

The package is written in TypeScript and includes full type definitions.

```typescript theme={null}
import type {
  // Core types
  Message,
  WorkMessage,
  WorkItem,
  ThreadMessage,
  Thread,

  // Configuration
  StandardAgentsConfig,
  UseThreadOptions,
  ThreadContext,

  // Attachment types
  PendingAttachment,
  AttachmentPayload,
  AttachmentRef,
  ThreadFile,

  // Connection
  ConnectionStatus,
} from "@standardagents/vue"
```

### Message Type

```typescript theme={null}
interface Message {
  id: string
  role: "system" | "user" | "assistant" | "tool"
  content: string | null
  name?: string | null
  tool_calls?: string | null          // JSON array
  tool_call_id?: string | null
  created_at: number                  // microseconds
  status?: "pending" | "completed" | "failed"
  silent?: boolean
  tool_status?: "success" | "error" | null
  reasoning_content?: string | null
  parent_id?: string | null
  depth?: number
  attachments?: string | null         // JSON array of AttachmentRef
}
```

## Complete Example

> [accordion] View Complete Chat Interface Example with Image Attachments
>
> ```vue theme={null}
> <!-- App.vue -->
> <script setup lang="ts">
> import { ref, onMounted } from 'vue'
> import { ThreadProvider } from '@standardagents/vue'
> import ChatInterface from './ChatInterface.vue'
>
> // Set auth token
> onMounted(() => {
>   localStorage.setItem('agentbuilder_auth_token', 'your-token')
> })
>
> const threadId = ref('123e4567-e89b-12d3-a456-426614174000')
> </script>
>
> <template>
>   <ThreadProvider :threadId="threadId" :useWorkblocks="true">
>     <ChatInterface />
>   </ThreadProvider>
> </template>
> ```
>
> ```vue theme={null}
> <!-- ChatInterface.vue -->
> <script setup lang="ts">
> import { ref, computed } from 'vue'
> import { useThread } from '@standardagents/vue'
>
> const input = ref('')
>
> const {
>   workblocks,
>   status,
>   loading,
>   sendMessage,
>   stopExecution,
>   // Attachments for sending to LLM
>   attachments,
>   addAttachment,
>   removeAttachment,
> } = useThread()
>
> const isRunning = computed(() => workblocks.value.some((m) => m.status === 'pending'))
>
> const handleSend = async () => {
>   if (!input.value.trim() || isRunning.value) return
>   await sendMessage({ role: 'user', content: input.value })
>   input.value = ''
> }
>
> const handleFileSelect = (event: Event) => {
>   const target = event.target as HTMLInputElement
>   if (target.files?.length) {
>     addAttachment(target.files)
>   }
> }
> </script>
>
> <template>
>   <div class="chat-container">
>     <p>Status: {{ status }} <span v-if="loading">(loading...)</span></p>
>
>     <!-- Message list -->
>     <div class="messages">
>       <div v-for="item in workblocks" :key="item.id">
>         <template v-if="item.type === 'workblock'">
>           <div class="workblock" :class="item.status">
>             <p v-if="item.content">{{ item.content }}</p>
>             <div v-for="work in item.workItems" :key="work.id" class="work-item">
>               <span v-if="work.type === 'tool_call'">Tool: {{ work.name }}</span>
>               <span v-if="work.type === 'tool_result'">{{ work.status }}</span>
>             </div>
>           </div>
>         </template>
>         <template v-else>
>           <div class="message" :class="item.role">
>             <strong>{{ item.role }}:</strong> {{ item.content }}
>           </div>
>         </template>
>       </div>
>     </div>
>
>     <!-- Pending attachments preview -->
>     <div v-if="attachments.length" class="attachment-preview">
>       <div v-for="a in attachments" :key="a.id" class="attachment-item">
>         <img v-if="a.isImage && a.previewUrl" :src="a.previewUrl" :alt="a.name" width="100" />
>         <span>{{ a.name }}</span>
>         <button @click="removeAttachment(a.id)">×</button>
>       </div>
>     </div>
>
>     <!-- Input area -->
>     <div class="input-area">
>       <input
>         type="file"
>         accept="image/*"
>         multiple
>         @change="handleFileSelect"
>       />
>       <input
>         v-model="input"
>         @keyup.enter="handleSend"
>         :disabled="isRunning"
>         placeholder="Type a message..."
>       />
>       <button @click="handleSend" :disabled="isRunning">Send</button>
>       <button v-if="isRunning" @click="stopExecution">Stop</button>
>     </div>
>   </div>
> </template>
>
> <style scoped>
> .chat-container {
>   display: flex;
>   flex-direction: column;
>   gap: 1rem;
>   padding: 1rem;
> }
>
> .messages {
>   display: flex;
>   flex-direction: column;
>   gap: 0.5rem;
> }
>
> .message.user {
>   align-self: flex-end;
>   background: #e3f2fd;
>   padding: 0.5rem 1rem;
>   border-radius: 1rem;
> }
>
> .message.assistant {
>   align-self: flex-start;
>   background: #f5f5f5;
>   padding: 0.5rem 1rem;
>   border-radius: 1rem;
> }
>
> .workblock {
>   background: #fff3e0;
>   padding: 1rem;
>   border-radius: 0.5rem;
> }
>
> .workblock.pending {
>   opacity: 0.7;
> }
>
> .attachment-preview {
>   display: flex;
>   gap: 0.5rem;
>   flex-wrap: wrap;
> }
>
> .attachment-item {
>   display: flex;
>   align-items: center;
>   gap: 0.5rem;
>   padding: 0.5rem;
>   background: #f0f0f0;
>   border-radius: 0.25rem;
> }
>
> .input-area {
>   display: flex;
>   gap: 0.5rem;
> }
>
> .input-area input[type="text"] {
>   flex: 1;
>   padding: 0.5rem;
> }
> </style>
> ```

## Next Steps

> [card] [Core Concepts](https://docs.standardagents.ai/core-concepts/agents.md)
>
> Learn about agents, prompts, and tools

> [card] [React SDK](https://docs.standardagents.ai/user-interfaces/react.md)
>
> React hooks and components

> [card] [Architecture](https://docs.standardagents.ai/introduction/architecture.md)
>
> How the backend framework works

> [card] [API Reference](https://docs.standardagents.ai/api-reference/rest/create-thread.md)
>
> REST and WebSocket APIs

## Reconnect recovery

A thread provider reads the newest message page after a socket reconnect.
`preload` controls the initial history reads.
When `preload` is enabled, a snapshot after the first socket open closes the initial subscription gap.
Recovery also reads history when `preload` is false.
Recovery runs in the background.
Snapshot results preserve in-flight events and optimistic mutations.
Thread switches invalidate pending history responses.
Deletions reconcile within the newest page window.
Older cached rows outside that window remain until history is read again.

## Shared HTTP transport

Pass a fetch-compatible function as `fetch` in `StandardAgentsPlugin` options.
Thread requests and endpoint overrides will inherit this transport.

```typescript theme={null}
app.use(StandardAgentsPlugin, { endpoint: '/api', fetch: requestFetch })
```

An empty thread ID keeps a provider idle. Its children keep their component instances when the selection changes.

`canReconnect` checks an application auth halt before each retry. `onAuthorized` runs after an authorized socket opens. Both options are available on `ThreadProvider` and `useThreadSetup`.

Thread providers adapt a framework-free `ThreadSessionStore` from `@standardagents/client`. The store owns message history, live reducers, run status, committed files, and optimistic commands. Reconnect recovery runs once in that shared store. Upload queues and display grouping stay in the framework adapter. Socket frame types are available from `@standardagents/client/protocol`.

Store-owned thread state uses read-only computed refs.
This includes messages, pagination flags, connection status, run status, loading and errors.
Assignments to these refs fail typechecking.
Thread commands update the shared session state.
Pending attachment queues retain writable refs.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.