# Agent chat docs

> Install @texuli/chat, stream replies with Stop, read message Markdown safely, autocomplete mentions and commands, and attach validated blueprints. Autocomplete, stopped replies and ChatAttachment are in development.

Language: en
Canonical: https://texuli.com/guides/solutions/agent-chat/
Markdown: https://texuli.com/guides/solutions/agent-chat.md (this page). Every texuli.com page has a Markdown twin at its URL plus `.md` (the home page is https://texuli.com/index.md), and any page URL answers `Accept: text/markdown` with its twin instead of HTML.
Last Updated: 2026-11-13

---

Guides

Install @texuli/chat, stream replies with Stop, read message Markdown safely, autocomplete mentions and commands, and attach validated blueprints. Autocomplete, stopped replies and ChatAttachment are in development.

[Back to overview](https://texuli.com/solutions/agent-chat/)

## Install and status

Install the published chat package with `pnpm add @texuli/chat`. It is pre-1.0, so a new minor version may break the API. The thread, message list, composer, typing indicator and streaming events are on npm.

Composer autocomplete, stopped replies and `ChatAttachment` were released in `0.18.0`. Message text renders emoji with `EmojiText` from `texuli/emoji`, loaded the first time a message holds one. Texuli's own emoji art is still landing, so emoji show as Unicode.

```
<script lang="ts">
import { type ChatMessage, ChatThread } from '@texuli/chat';

let messages = $state<ChatMessage[]>([]);
</script>

<ChatThread {messages} currentUserId="me" onSend={(body) => console.log(body)} />
```

## Streamed replies and Stop

Apply stream events with `applyChatStreamEvent(messages, event)`: `start`, `delta` and `end` (or `error`). Events are checked, so a malformed event changes nothing. While a reply streams, its partial text is hidden from the live log, and the finished message is announced once.

Pass `streaming` and `onStop` to the thread. Stop, or Escape in the composer, calls `onStop`. Send `{ type: 'end', id, stopped: true }` to keep the partial text and mark the reply as stopped.

```
messages = applyChatStreamEvent(messages, { type: 'start', id, author: agent, at: Date.now() });
messages = applyChatStreamEvent(messages, { type: 'delta', id, text: 'Here is the **summary**.' });
messages = applyChatStreamEvent(messages, { type: 'end', id, stopped: true });
```

## Markdown, mentions and dialect export

Message text is read by the shared CommonMark inline parser and filtered down to the chat vocabulary: strong, emphasis, code, links, mentions (`@[Name](mention:id)`), dates (`[[date:ISO]]`) and status chips (`[[status:tone|Label]]`). Nothing becomes HTML. Links pass the untrusted URL policy, and a refused link stays as the text that was written.

To post a reply in another chat, read it with `parseMarkdown` from `@texuli/core/markdown` and write it with `writeMarkdownDialect` from `@texuli/core/markdown-dialects`. The writer supports Slack, Discord, WhatsApp, Telegram, Teams, Google Chat, Mattermost and plain text, and reports every construct it had to adapt.

```
import { parseMarkdown } from '@texuli/core/markdown';
import { writeMarkdownDialect } from '@texuli/core/markdown-dialects';

const { value, diagnostics } = writeMarkdownDialect(parseMarkdown(reply).tree, 'slack');
```

## Composer autocomplete

Pass `suggestions` to the composer or the thread. In development: released in `0.18.0`.

- `mentionSuggestions(people)` uses the `@` trigger and inserts a mention token.
- `commandSuggestions(commands)` uses the `/` trigger at the start of a line.
- `emojiSuggestions(search)` uses the `:` trigger after one character. You pass the search, for example `searchEmojis` from `@texuli/emojis/search`, so the chat package carries no emoji data.
- Any other source is `{ trigger, label, search }`.

The list follows the APG combobox list-autocomplete pattern: arrows choose, Enter or Tab inserts, and Escape closes.

```
import { commandSuggestions, mentionSuggestions } from '@texuli/chat';

const suggestions = [
  mentionSuggestions([{ id: 'jordan', name: 'Jordan Lee' }]),
  commandSuggestions([{ name: 'report', description: 'Build the weekly report' }]),
];
```

## Blueprint attachments

`ChatAttachment` shows a blueprint an agent attached. In development: released in `0.18.0`.

The host passes `check` and `preview`, because `@texuli/chat` sits below the catalog and the player:

- `check` validates the artifact, usually with `validateDraft` from `@texuli/catalog/validate` at the untrusted tier.
- `preview` paints the validated value, usually with `Player` from `@texuli/player`.

The card never paints an artifact that was refused. It lists the reasons as text instead.

```
<ChatAttachment artifact={blueprint} title="Weekly report" {check}>
  {#snippet preview(value)}<Player blueprint={value} label="Weekly report" />{/snippet}
</ChatAttachment>
```
