> For the complete documentation index, see [llms.txt](https://docs.umbraco.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.umbraco.com/ai-in-umbraco/19.latest/add-ons/agent/frontend-client.md).

# Frontend Client

Build custom agent-driven UIs using the UaiAgentClient base client.

The `UaiAgentClient` is a TypeScript client for building custom agent-driven user interfaces in the Umbraco backoffice. The client provides a callback-based interface for consuming the AG-UI protocol with automatic event handling and message management.

## Overview

`UaiAgentClient` is exported from the `@umbraco-ai/agent` package and serves as the foundation for building agent-based chat interfaces. It's the same client that powers both the Contextual Copilot sidebar and Copilot Workspace.

**Key features:**

* Callback-based event handling for streaming responses
* Automatic message conversion to AG-UI format
* Tool call argument accumulation during streaming
* Interrupt handling for human-in-the-loop workflows
* Dependency injection support for testing

## Installation

The client is included in the Agent package:

{% code title="Package Manager Console" %}

```powershell
Install-Package Umbraco.AI.Agent
```

{% endcode %}

Or via .NET CLI:

{% code title="Terminal" %}

```bash
dotnet add package Umbraco.AI.Agent
```

{% endcode %}

## Import

{% code title="TypeScript" %}

```typescript
import { UaiAgentClient, type UaiChatMessage, type AgentClientCallbacks } from "@umbraco-ai/agent";
```

{% endcode %}

## Quick Start

{% code title="Basic Usage" %}

```typescript
import { LitElement, html } from "lit";
import { customElement, state } from "lit/decorators.js";
import { UaiAgentClient, type UaiChatMessage } from "@umbraco-ai/agent";

@customElement("my-agent-chat")
export class MyAgentChat extends LitElement {
    #client?: UaiAgentClient;

    @state()
    private _messages: UaiChatMessage[] = [];

    @state()
    private _streamingContent = "";

    @state()
    private _isRunning = false;

    connectedCallback() {
        super.connectedCallback();

        // Create client with callbacks
        this.#client = UaiAgentClient.create(
            { agentId: "your-agent-id" },
            {
                onTextStart: () => {
                    this._isRunning = true;
                    this._streamingContent = "";
                },
                onTextDelta: (delta) => {
                    this._streamingContent += delta;
                },
                onTextEnd: () => {
                    // Add completed message
                    this._messages = [
                        ...this._messages,
                        {
                            id: crypto.randomUUID(),
                            role: "assistant",
                            content: this._streamingContent,
                            timestamp: new Date(),
                        },
                    ];
                    this._streamingContent = "";
                },
                onRunFinished: () => {
                    this._isRunning = false;
                },
                onError: (error) => {
                    console.error("Agent error:", error);
                    this._isRunning = false;
                },
            },
        );
    }

    #sendMessage(content: string) {
        if (!this.#client || !content.trim()) return;

        const userMessage: UaiChatMessage = {
            id: crypto.randomUUID(),
            role: "user",
            content,
            timestamp: new Date(),
        };

        this._messages = [...this._messages, userMessage];
        this.#client.sendMessage(this._messages);
    }

    render() {
        return html`
            <div class="chat-container">
                ${this._messages.map((msg) => html` <div class="message ${msg.role}">${msg.content}</div> `)}
                ${this._streamingContent
                    ? html` <div class="message assistant streaming">${this._streamingContent}</div> `
                    : ""}
                <input
                    @keydown=${(e: KeyboardEvent) => {
                        if (e.key === "Enter") {
                            const input = e.target as HTMLInputElement;
                            this.#sendMessage(input.value);
                            input.value = "";
                        }
                    }}
                    ?disabled=${this._isRunning}
                    placeholder="Type your message..."
                />
            </div>
        `;
    }
}
```

{% endcode %}

## API Reference

### Factory Method

#### `UaiAgentClient.create(config, callbacks?)`

Creates a new client instance with the default HTTP transport.

**Parameters:**

| Parameter   | Type                   | Required | Description          |
| ----------- | ---------------------- | -------- | -------------------- |
| `config`    | `UaiAgentClientConfig` | Yes      | Client configuration |
| `callbacks` | `AgentClientCallbacks` | No       | Event callbacks      |

**Returns:** `UaiAgentClient`

**Example:**

```typescript
const client = UaiAgentClient.create(
    { agentId: "550e8400-e29b-41d4-a716-446655440000" },
    {
        onTextDelta: (delta) => console.log(delta),
        onRunFinished: () => console.log("Done"),
    },
);
```

### Constructor

#### `new UaiAgentClient(transport, callbacks?)`

Creates a client with a custom transport. Useful for testing or custom implementations.

**Parameters:**

| Parameter   | Type                   | Required | Description                     |
| ----------- | ---------------------- | -------- | ------------------------------- |
| `transport` | `AgentTransport`       | Yes      | Custom transport implementation |
| `callbacks` | `AgentClientCallbacks` | No       | Event callbacks                 |

**Example:**

```typescript
import { UaiHttpAgent } from "@umbraco-ai/agent";

const transport = new UaiHttpAgent({ agentId: "my-agent" });
const client = new UaiAgentClient(transport, {
    onTextDelta: (delta) => console.log(delta),
});
```

### Instance Methods

#### `sendMessage(messages, tools?, context?)`

Sends messages to the agent and starts a new run.

**Parameters:**

| Parameter  | Type                                          | Required | Description                                                                  |
| ---------- | --------------------------------------------- | -------- | ---------------------------------------------------------------------------- |
| `messages` | `UaiChatMessage[]`                            | Yes      | Conversation messages                                                        |
| `tools`    | `UaiFrontendTool[]`                           | No       | Available frontend tools (extends AG-UI `Tool` with `scope`/`isDestructive`) |
| `context`  | `Array<{description: string, value: string}>` | No       | Context items for LLM awareness                                              |

`UaiFrontendTool` extends the AG-UI `Tool` type with two optional metadata fields used for backend permission filtering:

```typescript
interface UaiFrontendTool extends Tool {
    /** Tool scope for permission grouping (e.g., 'entity-write', 'navigation') */
    scope?: string;
    /** Whether the tool performs destructive operations (e.g., delete, publish) */
    isDestructive?: boolean;
}
```

**Returns:** `void`

**Example:**

```typescript
client.sendMessage(
    [{ id: "1", role: "user", content: "Hello", timestamp: new Date() }],
    undefined, // No tools
    [{ description: "Current content item", value: "Document: Home Page" }],
);
```

#### `setCallbacks(callbacks)`

Updates the callbacks dynamically.

**Parameters:**

| Parameter   | Type                   | Required | Description   |
| ----------- | ---------------------- | -------- | ------------- |
| `callbacks` | `AgentClientCallbacks` | Yes      | New callbacks |

**Returns:** `void`

**Example:**

```typescript
client.setCallbacks({
    onTextDelta: (delta) => console.log("New handler:", delta),
});
```

#### `reset()`

Resets the client state. Clears pending tool arguments and generates a new thread ID so file references and other thread-scoped data from the previous conversation are not reused.

**Returns:** `void`

**Example:**

```typescript
client.reset();
```

## Callbacks

The `AgentClientCallbacks` interface defines all available event handlers:

### Text Streaming

| Callback      | Parameters                    | Description                           |
| ------------- | ----------------------------- | ------------------------------------- |
| `onTextStart` | `(messageId: string) => void` | Called when a new text message starts |
| `onTextDelta` | `(delta: string) => void`     | Called when a text chunk is received  |
| `onTextEnd`   | `() => void`                  | Called when text message is complete  |

### Tool Calls

| Callback            | Parameters                             | Description                                             |
| ------------------- | -------------------------------------- | ------------------------------------------------------- |
| `onToolCallStart`   | `(info: UaiToolCallInfo) => void`      | Called when a tool call starts                          |
| `onToolCallArgsEnd` | `(id: string, args: string) => void`   | Called when tool arguments are complete                 |
| `onToolCallEnd`     | `(id: string) => void`                 | Called when tool call completes (args streamed)         |
| `onToolCallResult`  | `(id: string, result: string) => void` | Called when tool result is received (backend execution) |

### Run Lifecycle

| Callback        | Parameters                          | Description                                                 |
| --------------- | ----------------------------------- | ----------------------------------------------------------- |
| `onRunFinished` | `(event: RunFinishedEvent) => void` | Called when the run finishes (success, interrupt, or error) |
| `onError`       | `(error: Error) => void`            | Called on error                                             |

### State Updates

| Callback             | Parameters                                | Description                               |
| -------------------- | ----------------------------------------- | ----------------------------------------- |
| `onStateSnapshot`    | `(state: UaiAgentState) => void`          | Called when a state snapshot is received  |
| `onStateDelta`       | `(delta: Partial<UaiAgentState>) => void` | Called when a state delta is received     |
| `onMessagesSnapshot` | `(messages: UaiChatMessage[]) => void`    | Called when messages snapshot is received |

### Custom Events

| Callback        | Parameters                               | Description                                                                                  |
| --------------- | ---------------------------------------- | -------------------------------------------------------------------------------------------- |
| `onCustomEvent` | `(name: string, value: unknown) => void` | Called when the agent emits an AG-UI `CUSTOM` event (application-specific streaming events). |

## Message Format

Messages use the `UaiChatMessage` interface:

```typescript
interface UaiChatMessage {
    id: string;
    role: "user" | "assistant" | "tool";
    content: string;
    /** Multimodal content parts (text + binary). When present, `content` is a text summary. */
    contentParts?: UaiInputContent[];
    toolCalls?: UaiToolCallInfo[];
    /** Required for `tool` role messages - the ID of the tool call this is responding to */
    toolCallId?: string;
    /** Optional agent name for attribution (set when auto mode selects an agent) */
    agentName?: string;
    timestamp: Date;
}
```

For multimodal inputs, `contentParts` can contain text or binary parts:

```typescript
type UaiInputContent = UaiTextInputContent | UaiBinaryInputContent;

interface UaiTextInputContent {
    type: "text";
    text: string;
}

interface UaiBinaryInputContent {
    type: "binary";
    mimeType: string;
    /** Base64-encoded binary data (initial upload) */
    data?: string;
    /** URL where the binary content can be retrieved */
    url?: string;
    /** Server-side reference ID (after snapshot, for subsequent turns) */
    id?: string;
    /** Original filename */
    filename?: string;
}
```

## Related

* [Contextual Copilot](/ai-in-umbraco/19.latest/add-ons/agent-copilot.md) - Reference implementation using UaiAgentClient
* [Streaming](/ai-in-umbraco/19.latest/add-ons/agent/streaming.md) - AG-UI protocol and event types
* [Frontend Tools](/ai-in-umbraco/19.latest/add-ons/agent-copilot/frontend-tools.md) - Browser-executable tools


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.umbraco.com/ai-in-umbraco/19.latest/add-ons/agent/frontend-client.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
