> ## 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/client

> Framework-agnostic client library for Standard Agents Builder - HTTP client, WebSocket management, and file uploads

## Overview

The `@standardagents/client` package provides the core client library for Standard Agents. It's a framework-agnostic foundation that `@standardagents/react` and `@standardagents/vue` build upon.

> [card] Key Features
>
> * HTTP client for all API operations
> * WebSocket connection management with auto-reconnection
> * File upload handling with thumbnail generation
> * Utility functions for workblocks and attachments
> * Zero framework dependencies

## Installation

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

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

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

> [!NOTE]
>
> Most users should use `@standardagents/react` or `@standardagents/vue` instead, which re-export everything from this package with additional framework-specific features.

## AgentBuilderClient

HTTP client for all Standard Agents API operations.

```typescript theme={null}
import { AgentBuilderClient } from '@standardagents/client'

const client = new AgentBuilderClient({
  endpoint: 'https://your-api.com',
  token: 'your-auth-token',
})

// Create a thread
const thread = await client.createThread({
  agentId: 'support-agent',
})

// Send a message
const message = await client.sendMessage(thread.id, {
  role: 'user',
  content: 'Hello!',
})

// Get message history
const messages = await client.getMessages(thread.id, { limit: 50 })
```

### Methods

| Method | Description |
| - | - |
| `listAgents(options?)` | List the agents you can create a thread for. `{ primary: true }` returns only primary agents |
| `createThread(payload)` | Create a new thread |
| `getThread(threadId)` | Fetch thread metadata |
| `getMessages(threadId, options?)` | Fetch message history |
| `sendMessage(threadId, payload)` | Send a user message |
| `stopExecution(threadId)` | Cancel in-flight execution |
| `uploadFile(threadId, file, path?)` | Upload a file to thread filesystem |
| `listFiles(threadId, path?)` | List files in thread filesystem |
| `getFile(threadId, path)` | Download a file |
| `deleteFile(threadId, path)` | Delete a file |

### Configuration

```typescript theme={null}
interface AgentBuilderConfig {
  endpoint: string    // API endpoint URL
  token?: string      // Optional auth token
}
```

## ThreadSessionStore

`ThreadSessionStore` owns one thread session in `@standardagents/client`.
It has no framework dependency.
Each Vue or React provider owns a separate store.
Authorized subscriptions remain separate.

```typescript theme={null}
import { AgentBuilderClient, ThreadSessionStore } from '@standardagents/client'

const session = new ThreadSessionStore(new AgentBuilderClient('/api'), 'thread-123', {
  preload: true,
  live: true,
})
const unsubscribe = session.subscribe(() => console.log(session.getSnapshot()))
session.start()
await session.sendMessage({ role: 'user', content: 'Hello' })
// A selection change invalidates outstanding reads and commands.
session.setThreadId('thread-456')
// Call during teardown.
unsubscribe()
session.dispose()
```

`getSnapshot()` returns a stable snapshot until the next state change.
It contains messages, committed files, run status, connection status, loading flags, pagination flags, and an error.
`subscribeEvents()` receives custom thread event envelopes.
Event data defaults to `unknown`.

The store owns message upserts, chunks, optimistic sends, deletion rollback, and file events.
It merges the newest message page after each reconnect.
Recovery reads preserve existing messages and operation errors.
Session epochs reject stale reads and command completions.
File snapshots retain lifecycle events received during the read.
An empty selection stays idle.
`stop()` cancels the connection lifecycle.
`start()` resumes a stopped store.
`dispose()` removes its subscriptions.

`loadOlderMessages()` loads an older page through a complete workblock boundary.
`loadMessagesByIds()` loads explicit references.
`refresh()` requests an explicit newest-page reconciliation.
No method schedules a refresh timer.
Vue refs and React's `useSyncExternalStore` adapt this state for rendering.
Framework adapters own upload queues, pending attachments, and display grouping.

## Wire protocol

`@standardagents/client/protocol` defines the shared socket frames.
Durable Objects, client adapters, and the admin UI import this module.
It covers messages, custom thread events, logs, registry activity, files, KV changes, and effects.
Chunk and run-status types reuse `@standardagents/spec` definitions.
Portable message and attachment fields extend the spec declarations.
Serialized attachment columns and optimistic preview URLs remain Builder extensions.
Builder-specific frames remain runtime extensions.
Existing frame names remain available from the client package root.
Registry activity frames carry only changed fields.
An omitted execution-state field retains its previous value.

`isLogStreamEvent()` checks log-channel envelopes at the JSON boundary.
Log patches retain an `unknown` payload by default.
`LogWebSocketCallbacks.onQueuedTools` receives `log_queued_tools` frames.

## ThreadConnectionManager

Manages WebSocket connections with automatic reconnection and heartbeat.

```typescript theme={null}
import { AgentBuilderClient, ThreadConnectionManager } from '@standardagents/client'

const client = new AgentBuilderClient('https://your-api.com/api')
const manager = new ThreadConnectionManager(client, 'thread-123', {
  onMessage: event => console.log('Message:', event.data),
  onChunk: event => console.log('Chunk:', event.chunk),
  onStatusChange: status => console.log('Status:', status),
  onReconnected: ({ initial }) => console.log('Connected:', { initial }),
  onError: event => console.error('Server error:', event.error),
})
manager.connect()
// Call on teardown.
manager.disconnect()
```

### Connection features

* Recovery uses exponential backoff with jitter.
* Quiet sockets send `stream_ping` protocol keepalives.
* `onReconnected({ initial })` runs after each accepted open.
* Repeated pre-open failures consult the application's auth gate.
* Disconnect invalidates pending sockets and auth decisions.

### Options

```typescript theme={null}
interface ThreadConnectionOptions {
  depth?: number
  includeSilent?: boolean
  heartbeatInterval?: number
  maxReconnectDelay?: number
  reconnectAuthGate?: () => boolean | Promise<boolean>
  preOpenFailureThreshold?: number
  canReconnect?: () => boolean
  onAuthorized?: () => void
}
```

## FileUploadManager

Handles file uploads with thumbnail generation and progress tracking.

```typescript theme={null}
import { FileUploadManager } from '@standardagents/client'

const uploader = new FileUploadManager({
  endpoint: 'https://your-api.com',
  threadId: 'thread-123',
  token: 'your-auth-token',
})

// Upload with progress callback
const result = await uploader.upload(file, {
  path: '/uploads/image.png',
  onProgress: (progress) => console.log(`${progress}%`),
})
```

### Features

* **Thumbnail generation**: Automatic for image files
* **Progress tracking**: Callbacks for upload progress
* **Deferred uploads**: Returns pending refs before upload completes

## Utility Functions

### transformToWorkblocks

Groups tool calls with their results for UI rendering.

```typescript theme={null}
import { transformToWorkblocks } from '@standardagents/client'

const messages = await client.getMessages(threadId)
const workblocks = transformToWorkblocks(messages)

// Workblocks group consecutive tool calls:
// [assistant with tool_calls, tool result, tool result, assistant]
// becomes:
// [workblock { workItems: [...] }, assistant]
```

### parseAttachments

Extract attachment references from message content.

```typescript theme={null}
import { parseAttachments } from '@standardagents/client'

const attachments = parseAttachments(message.attachments)
// Returns: AttachmentRef[]
```

### Image Processing

```typescript theme={null}
import {
  generateImageThumbnail,
  canGenerateThumbnails,
} from '@standardagents/client'

// Check if thumbnails are supported (requires Canvas API)
if (canGenerateThumbnails()) {
  const thumbnail = await generateImageThumbnail(file, {
    maxWidth: 200,
    maxHeight: 200,
  })
}
```

### File Helpers

```typescript theme={null}
import {
  generatePendingFileId,
  readFileAsDataUrl,
  isImageMimeType,
  messagesToFiles,
} from '@standardagents/client'

// Generate unique ID for pending uploads
const id = generatePendingFileId()

// Read file as data URL
const dataUrl = await readFileAsDataUrl(file)

// Check if MIME type is an image
isImageMimeType('image/png') // true

// Extract files from messages
const files = messagesToFiles(messages)
```

## Type Exports

The package exports all types needed for building clients:

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

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

  // Configuration
  AgentBuilderConfig,

  // Request/Response types
  GetMessagesOptions,
  SendMessagePayload,
  CreateThreadPayload,

  // WebSocket event types
  MessageDataEvent,
  MessageChunkEvent,
  ErrorEvent,
  ThreadEvent,
  MessageStreamEvent,
  LogDataEvent,
  CustomEvent,
  StoppedByUserEvent,
  LogStreamEvent,

  // Callback types
  MessageWebSocketCallbacks,
  LogWebSocketCallbacks,
  ThreadConnectionCallbacks,

  // Connection status
  ConnectionStatus,
} from '@standardagents/client'
```

### Key Types

**Message:**

```typescript theme={null}
interface Message {
  id: string
  role: 'system' | 'user' | 'assistant' | 'tool'
  content: string | null
  name?: string | null
  tool_calls?: string | null
  tool_call_id?: string | null
  created_at: number
  status?: 'pending' | 'completed' | 'failed'
  attachments?: string | null
}
```

**PendingAttachment:**

```typescript theme={null}
interface PendingAttachment {
  id: string
  file: File
  name: string
  mimeType: string
  size: number
  isImage: boolean
  previewUrl: string | null
  width?: number
  height?: number
}
```

**AttachmentRef:**

```typescript theme={null}
interface AttachmentRef {
  id: string
  type: 'file'
  path: string
  name: string
  mimeType: string
  size: number
  width?: number
  height?: number
  description?: string
}
```

**ConnectionStatus:**

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

## Authentication

The client reads auth tokens from localStorage:

```typescript theme={null}
// Token is read from this key
const token = localStorage.getItem('agentbuilder_auth_token')

// Included in requests as:
// Authorization: Bearer {token}
```

You can also pass the token directly to the client:

```typescript theme={null}
const client = new AgentBuilderClient({
  endpoint: 'https://your-api.com',
  token: 'your-auth-token',
})
```

## Building Custom Integrations

Use `@standardagents/client` directly when:

* Building for a framework without an official SDK
* Creating custom tooling or CLI applications
* Building server-side integrations
* Needing fine-grained control over connections

```typescript theme={null}
import {
  AgentBuilderClient,
  ThreadConnectionManager,
  transformToWorkblocks,
} from '@standardagents/client'

class CustomChatClient {
  private client: AgentBuilderClient
  private connection: ThreadConnectionManager | null = null
  private messages: Message[] = []

  constructor(endpoint: string, token: string) {
    this.client = new AgentBuilderClient({ endpoint, token })
  }

  async connect(threadId: string) {
    // Load existing messages
    this.messages = await this.client.getMessages(threadId)

    // Set up live connection
    this.connection = new ThreadConnectionManager({
      endpoint: this.client.endpoint,
      threadId,
      token: this.client.token,
      onMessage: (msg) => {
        this.messages.push(msg)
        this.onUpdate?.(transformToWorkblocks(this.messages))
      },
    })

    this.connection.connect()
  }

  async send(content: string) {
    await this.client.sendMessage(this.threadId, {
      role: 'user',
      content,
    })
  }

  onUpdate?: (workblocks: ThreadMessage[]) => void
}
```

## Next Steps

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

> [card] [Vue SDK](https://docs.standardagents.ai/user-interfaces/vue.md)
>
> Vue 3 composables and components

> [card] [WebSocket API](https://docs.standardagents.ai/api-reference/websocket/stream.md)
>
> WebSocket protocol documentation

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

## Reconnect recovery

`ThreadConnectionManager` calls `onReconnected({ initial })` after each successful open.
The first open has `initial: true`.
SDK providers read the newest message page on subsequent opens.
When `preload` is enabled, a snapshot after the first socket open closes the initial subscription gap.
`reconcileMessages(current, snapshot, baseline, hasMore?)` preserves mutations received during the read.
`hasMore: false` marks a complete history snapshot.

## Shared HTTP transport

Pass a fetch-compatible function as the second constructor argument.
HTTP requests and WebSocket ticket requests will use this transport.

```typescript theme={null}
const client = new AgentBuilderClient('/api', requestFetch)
```

## Shared live connections

`LiveConnection` owns one authorized subscription's recovery.
Its socket factory can use `mintWebSocketUrl()` with the application's fetch transport.
The lifecycle owns jittered backoff, quiet `stream_ping` keepalives, stale-socket guards, and auth gating.
`AuthReconnectSupervisor` shares one auth probe across application channels.
Registry, message, and privileged log subscriptions remain separate.
`AgentBuilderClient.connectLogWebSocket()` connects to `/threads/:id/logs`.
That subscription requires log-read authorization.

A `LiveConnection` accepts a socket factory or a `LiveConnectionUrlSource` with a `url(signal)` function. URL sources resolve ticket exchange before socket construction. A retired URL result cannot create a socket. Existing raw-socket factories remain supported.

Socket factories and URL sources receive an `AbortSignal`. SDK and admin adapters forward it to ticket exchange. Disconnect aborts that signal. The raw message and log connection methods accept `signal` in their options.


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