> 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/17.latest/add-ons/agent/ai-agent-service/uai-agent-repository.md).

# UaiAgentRepository

The `UaiAgentRepository` is a lightweight, read-only repository for fetching active agents in frontend components. The repository provides an API for consumers that only need to read active agent data without the complexity of full CRUD operations.

## Overview

This repository is useful when you need to:

* Display a list of active agents in a dropdown or picker
* Filter agents by surface (for example "copilot" agents only)
* Build custom UI components that consume agent data

**Key features:**

* Read-only operations (no create/update/delete)
* Automatic filtering to active agents only
* Surface-based filtering support
* Pagination control
* Observable state that reacts to agent create/update/delete events
* Type-safe agent item models

## Installation

The repository is included in the `@umbraco-ai/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 { UaiAgentRepository, type UaiAgentRepositoryOptions } from "@umbraco-ai/agent";
```

{% endcode %}

## Quick Start

{% code title="Basic Usage" %}

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

@customElement("my-agent-picker")
export class MyAgentPicker extends LitElement {
    #repository: UaiAgentRepository;

    @state()
    private _agents: UaiAgentItemModel[] = [];

    constructor() {
        super();
        this.#repository = new UaiAgentRepository(this);
    }

    async connectedCallback() {
        super.connectedCallback();
        await this.#loadAgents();
    }

    async #loadAgents() {
        const result = await this.#repository.fetchActiveAgents();
        if (result.data) {
            this._agents = result.data.items;
        }
    }

    render() {
        return html`
            <select>
                ${this._agents.map((agent) => html` <option value=${agent.unique}>${agent.name}</option> `)}
            </select>
        `;
    }
}
```

{% endcode %}

## API Reference

### Constructor

#### `new UaiAgentRepository(host)`

Creates a new repository instance.

**Parameters:**

| Parameter | Type                | Required | Description                                                 |
| --------- | ------------------- | -------- | ----------------------------------------------------------- |
| `host`    | `UmbControllerHost` | Yes      | Umbraco controller host (typically `this` in a Lit element) |

**Example:**

```typescript
import { UmbElementMixin } from "@umbraco-cms/backoffice/element-api";

class MyElement extends UmbElementMixin(LitElement) {
    #repository = new UaiAgentRepository(this);
}
```

### Methods

#### `fetchActiveAgents(options?)`

Fetches active agents with optional filtering and pagination. Only returns agents where `isActive` is `true`.

**Parameters:**

| Parameter | Type                        | Required | Description                      |
| --------- | --------------------------- | -------- | -------------------------------- |
| `options` | `UaiAgentRepositoryOptions` | No       | Filtering and pagination options |

**Returns:** `Promise<{ data?: { items: UaiAgentItemModel[], total: number }, error?: any }>`

**Example:**

```typescript
// Fetch all active agents
const result = await repository.fetchActiveAgents();
if (result.data) {
    console.log(`Found ${result.data.total} active agents`);
    console.log(result.data.items);
}

// Fetch copilot agents only
const copilotResult = await repository.fetchActiveAgents({
    surfaceId: "copilot",
});

// Fetch with pagination
const pagedResult = await repository.fetchActiveAgents({
    take: 10,
    surfaceId: "copilot",
});
```

#### `initialize()`

Loads the initial set of active agents and primes the observable state. Call this once before subscribing to `agentItems$`. Subsequent updates come automatically via entity action events (`CREATED`/`UPDATED`/`DELETED`).

**Returns:** `Promise<void>`

### Observable

#### `agentItems$`

An observable `Map<string, UaiAgentItemModel>` keyed by the agent's unique ID. Emits a new map whenever agents are added, updated, or removed in the current backoffice session. Only active agents are ever present in the map.

```typescript
repository.agentItems$.subscribe((items) => {
    console.log(`Active agents: ${items.size}`);
});
```

## Options

### UaiAgentRepositoryOptions

Configuration object for filtering and pagination.

```typescript
interface UaiAgentRepositoryOptions {
    /**
     * Filter agents by surface ID (e.g., "copilot").
     */
    surfaceId?: string;

    /**
     * Maximum number of agents to return.
     * Default: 100
     */
    take?: number;
}
```

**Examples:**

{% code title="Filter by Surface" %}

```typescript
// Fetch only copilot agents
const result = await repository.fetchActiveAgents({
    surfaceId: "copilot",
});
```

{% endcode %}

{% code title="Limit Results" %}

```typescript
// Fetch first 5 active agents
const result = await repository.fetchActiveAgents({
    take: 5,
});
```

{% endcode %}

{% code title="Combined Filtering" %}

```typescript
// Fetch 3 copilot agents
const result = await repository.fetchActiveAgents({
    surfaceId: "copilot",
    take: 3,
});
```

{% endcode %}

## Response Model

### UaiAgentItemModel

The repository returns agents as `UaiAgentItemModel` objects. This is a lightweight model used for lists and pickers (use `UaiAgentDetailRepository` when the full config is required).

```typescript
interface UaiAgentItemModel extends UmbEntityModel {
    /** Unique identifier (GUID as string) */
    unique: string;

    /** Umbraco entity type (e.g., "uai-agent") */
    entityType: string;

    /** URL-safe alias */
    alias: string;

    /** Display name */
    name: string;

    /** Optional description */
    description: string | null;

    /** "standard" or "orchestrated" */
    agentType: UaiAgentType;

    /** Associated profile ID (null if using the default chat profile) */
    profileId: string | null;

    /** Surface IDs that categorise this agent (e.g., ["copilot"]) */
    surfaceIds: string[];

    /** Availability scope (null = available in all contexts) */
    scope: UaiAgentScope | null;

    /** Guardrail IDs applied to this agent */
    guardrailIds: string[];

    /** Active status (always true from this repository) */
    isActive: boolean;

    /** ISO timestamp when the agent was created */
    dateCreated: string | null;

    /** ISO timestamp when the agent was last modified */
    dateModified: string | null;
}
```

## Complete Examples

### Agent Dropdown Picker

{% code title="Agent Picker Component" %}

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

@customElement("uai-agent-picker")
export class UaiAgentPicker extends LitElement {
    static styles = css`
        select {
            width: 100%;
            padding: 0.5rem;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        .loading {
            opacity: 0.5;
        }
    `;

    #repository = new UaiAgentRepository(this);

    @property({ type: String })
    surfaceId?: string;

    @property({ type: String })
    value?: string;

    @state()
    private _agents: UaiAgentItemModel[] = [];

    @state()
    private _loading = false;

    async connectedCallback() {
        super.connectedCallback();
        await this.#loadAgents();
    }

    async #loadAgents() {
        this._loading = true;

        const result = await this.#repository.fetchActiveAgents({
            surfaceId: this.surfaceId,
        });

        if (result.data) {
            this._agents = result.data.items;
        } else if (result.error) {
            console.error("Failed to load agents:", result.error);
        }

        this._loading = false;
    }

    #onChange(e: Event) {
        const select = e.target as HTMLSelectElement;
        this.value = select.value;
        this.dispatchEvent(
            new CustomEvent("change", {
                detail: { agentId: this.value },
            }),
        );
    }

    render() {
        return html`
            <select
                class=${this._loading ? "loading" : ""}
                .value=${this.value || ""}
                @change=${this.#onChange}
                ?disabled=${this._loading}
            >
                <option value="">Select an agent...</option>
                ${this._agents.map(
                    (agent) => html`
                        <option value=${agent.unique}>
                            ${agent.name}${agent.description ? ` - ${agent.description}` : ""}
                        </option>
                    `,
                )}
            </select>
        `;
    }
}
```

{% endcode %}

### Agent List with Filtering

{% code title="Filterable Agent List" %}

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

@customElement("uai-agent-list")
export class UaiAgentList extends LitElement {
    static styles = css`
        .filter {
            margin-bottom: 1rem;
        }
        .agent-card {
            border: 1px solid #ddd;
            padding: 1rem;
            margin-bottom: 0.5rem;
            border-radius: 4px;
        }
        .agent-card h3 {
            margin: 0 0 0.5rem 0;
        }
        .surface-tags {
            display: flex;
            gap: 0.5rem;
            margin-top: 0.5rem;
        }
        .surface-tag {
            background: #e3f2fd;
            padding: 0.25rem 0.5rem;
            border-radius: 3px;
            font-size: 0.875rem;
        }
    `;

    #repository = new UaiAgentRepository(this);

    @state()
    private _agents: UaiAgentItemModel[] = [];

    @state()
    private _selectedSurface = "";

    @state()
    private _loading = false;

    async connectedCallback() {
        super.connectedCallback();
        await this.#loadAgents();
    }

    async #loadAgents() {
        this._loading = true;

        const result = await this.#repository.fetchActiveAgents({
            surfaceId: this._selectedSurface || undefined,
        });

        if (result.data) {
            this._agents = result.data.items;
        }

        this._loading = false;
    }

    async #onSurfaceChange(e: Event) {
        const select = e.target as HTMLSelectElement;
        this._selectedSurface = select.value;
        await this.#loadAgents();
    }

    render() {
        return html`
            <div class="filter">
                <label>
                    Filter by surface:
                    <select @change=${this.#onSurfaceChange}>
                        <option value="">All surfaces</option>
                        <option value="copilot">Copilot</option>
                        <option value="custom">Custom</option>
                    </select>
                </label>
            </div>

            ${this._loading ? html`<div>Loading...</div>` : ""}
            ${this._agents.map(
                (agent) => html`
                    <div class="agent-card">
                        <h3>${agent.name}</h3>
                        <p>${agent.description || "No description"}</p>
                        <div><strong>Alias:</strong> ${agent.alias}</div>
                        ${agent.surfaceIds?.length
                            ? html`
                                  <div class="surface-tags">
                                      ${agent.surfaceIds.map((s) => html` <span class="surface-tag">${s}</span> `)}
                                  </div>
                              `
                            : ""}
                    </div>
                `,
            )}
        `;
    }
}
```

{% endcode %}

### Reactive Agent Count

{% code title="Agent Counter" %}

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

@customElement("uai-agent-counter")
export class UaiAgentCounter extends LitElement {
    #repository = new UaiAgentRepository(this);

    @state()
    private _count = 0;

    async connectedCallback() {
        super.connectedCallback();
        await this.#repository.initialize();

        // Observable emits whenever agents are created, updated, or deleted
        this.#repository.agentItems$.subscribe((items) => {
            this._count = items.size;
        });
    }

    render() {
        return html` <div>Active Agents: ${this._count}</div> `;
    }
}
```

{% endcode %}

## Error Handling

The repository returns errors in the result object rather than throwing exceptions:

{% code title="Error Handling" %}

```typescript
const result = await repository.fetchActiveAgents();

if (result.error) {
    console.error("Failed to fetch agents:", result.error);
    // Handle error (show notification, fallback UI, etc.)
} else if (result.data) {
    console.log("Loaded agents:", result.data.items);
    // Use the data
}
```

{% endcode %}

## When to Use This Repository

**Use `UaiAgentRepository` when:**

* You need a read-only list of active agents
* You're building pickers, dropdowns, or read-only lists
* You want to filter agents by surface
* You don't need full CRUD operations

**Use `UaiAgentDetailRepository` instead when:**

* You need to create, update, or delete agents
* You need full agent details with all properties
* You're building the agent management UI

**Use `UaiAgentCollectionRepository` instead when:**

* You're implementing a full collection view with Umbraco's collection patterns
* You need advanced filtering and sorting

## Related Repositories

| Repository                     | Purpose                  | Use Case                               |
| ------------------------------ | ------------------------ | -------------------------------------- |
| `UaiAgentRepository`           | Read-only active agents  | Pickers, dropdowns, lists              |
| `UaiAgentDetailRepository`     | Full CRUD operations     | Agent editor, management dashboard     |
| `UaiAgentCollectionRepository` | Collection view patterns | Agent list view with sorting/filtering |

## Related

* Agent Detail Repository - Full CRUD repository
* [UaiAgentClient](/ai-in-umbraco/17.latest/add-ons/agent/frontend-client.md) - Client for running agents
* [Agent Concepts](/ai-in-umbraco/17.latest/add-ons/agent/concepts.md) - Understanding agents and surfaces
* [Surfaces and Scopes](/ai-in-umbraco/17.latest/add-ons/agent/scopes.md) - Agent categorisation and availability


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.umbraco.com/ai-in-umbraco/17.latest/add-ons/agent/ai-agent-service/uai-agent-repository.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
