Skip to content

Architecture ​

@cognipeer/chat-ui is structured as a layered UI library:

  1. Components: visual building blocks (Chat, ChatMinimal, EmbedChat, ChatMessage, ChatInput, the approval and question cards, ...).
  2. Hooks: state and side-effect orchestration (useChat, useChatHistory, useI18n, useBackgroundAlerts).
  3. Transport: network abstraction for agents, conversations, streaming, runs, files and the portal routes. AgentServerClient is the default; adapters exist for agent-sdk, Chat Completions, the Responses API and A2A.
  4. Theme and i18n: CSS variables and theme tokens for dark / light / custom mode, and a message catalogue with locale fallback.

Data flow ​

  1. The user submits a message via ChatInput (files are picked, dropped or pasted first; with upload-on-select each is stored as it is picked).
  2. useChat updates optimistic local state.
  3. The transport starts a streaming request to the backend. On agent-server 0.3.0+ the stream is only a subscriber of the run that executes the turn.
  4. Incoming events update the assistant message incrementally. Tool calls are rendered by ToolCall; a pause becomes approval and question cards.
  5. When the turn ends, the stored conversation is the truth: the live view is replaced by what the server kept (the answer, the cards of a pause, the stop reason, the usage).
  6. useChatHistory syncs conversation metadata (including each conversation's runtime) for the sidebar and its status badges.

A page that opens or reloads a conversation while a turn is still running goes the other way: it asks the server where the run stands (runtime) and follows it from the event log, then shows the stored result.

Composition strategy ​

  • Use Chat for a complete UX (history + full controls).
  • Use ChatMinimal for embedded chat surfaces on your own page.
  • Use EmbedChat when the chat is the content of an iframe inside someone else's page and the identity comes from the host.
  • Use lower-level components + hooks only if a custom layout is required; then you wire the actions, the composer state and the file URL resolver yourself (see State Management).

Who decides what ​

ConcernDecided by
What the chat can do (approvals, portal switches, budget actions, attachments)The host page's props, narrowed by what the agent publishes (GET /agents: capabilities, metadata). The agent can only take features away from a switch the host turned on
Whether an action is allowed, who may answer, what a limit isThe server. Chat UI shows the refusal (403, 409, 402, ...) with a localized message
What the composer is doingOne value, composerState, derived from pending cards, a followed run, a used-up limit and readOnly

Rendering model ​

  • Message rendering is componentized to keep customization isolated.
  • Markdown rendering is encapsulated in message components; Markdown the portal controls (welcome, announcements, terms) goes through a stricter renderer (no raw HTML, checked images).
  • Tool call rendering remains visually separate from assistant text.
  • Every URL that ends up in an href or src is checked first (see Defaults and limits).

Performance notes ​

  • The library is optimized for typical chat session sizes. A long history is fetched as its newest page (100 messages on agent-server) and older messages load on request.
  • Keep parent container dimensions explicit to avoid layout thrashing.
  • Raw uploads send the file as the request body: no 33% base64 inflation and no giant strings in memory.

Studio · Pulse · Console · Agent SDK and more — the Cognipeer documentation hub