This page ships a Markdown twin for AI tools.

Guides

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.

Back to overview

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>