UI & reference

Agentdock UI

Introduction, installation, live component previews, and source reference for Agentdock UI.

Agentdock UI is an editable React component system for building Agentdock chat experiences. The installer copies the component source into your application, while @agentdock-ai/react provides the store, hooks, event decoding, and render model. Your application owns network requests and authentication.

The data flow is: the Chat component calls your ChatAdapter; the adapter sends the app's request; your server authenticates it and invokes Agentdock; Agentdock streams canonical events; the adapter decodes and yields those events; the UI store updates its render model and Chat displays it. The UI never chooses your endpoint or request format.

Install Agentdock UI

From your React application's directory, run the published installer:

npx @agentdock-ai/agentdock-ui add chat

The installer detects npm, pnpm, Yarn, or Bun, checks required dependencies and versions, and installs missing dependencies with the detected package manager. It copies the matching Radix or Base UI source into your app, where you can edit it normally. Node.js 22 or later, React 18 or later, and TypeScript are required, along with Tailwind CSS 4, a resolvable TypeScript alias, and shadcn semantic theme tokens. An existing components.json selects Radix or Base UI primitives. If that file is missing, the installer can create it using the detected alias and theme.

Use --cwd ./apps/web to target an app in a monorepo, or add --dry-run to preview changes without writing files or installing dependencies. Repeated installs preserve edited source; --overwrite explicitly replaces consumer edits. For a Starter checkout, run npm run ui:add from its root.

The editable Chat files are copied into your project; they are not imported as a prebuilt component package. The runtime dependency is @agentdock-ai/react.

Connect your adapter

Chat has no endpoint prop. Supply a stable ChatAdapter whose sendMessage yields canonical Agentdock events. The request body below is an example of an application-owned API, not a built-in Agentdock envelope.

import { Chat } from "@/components/agentdock-ui/chat";
import { decodeAgentEventStream, type ChatAdapter } from "@agentdock-ai/react";

export function createChatAdapter({
  endpoint,
  threadId,
  request,
}: {
  endpoint: string;
  threadId: string;
  request: typeof fetch;
}): ChatAdapter {
  return {
    async *sendMessage({ text, signal }) {
      const response = await request(endpoint, {
        method: "POST",
        headers: { "content-type": "application/json" },
        body: JSON.stringify({ prompt: text, threadId }),
        signal,
      });
      if (!response.ok || !response.body) throw new Error("Request failed.");
      yield* decodeAgentEventStream(response.body, { signal });
    },
  };
}

// Construct once for the app's current conversation.
<Chat adapter={chatAdapter} />;

Your server validates requests, authorizes and derives thread identity, then calls runtime.pipe() or runtime.toResponse(). Browser consumers receive the canonical event contract; normalize provider and LangGraph events on the server.

Events and trusted context

sendMessage must yield canonical AgentEvent values. For an SSE response, decodeAgentEventStream(response.body, { signal }) parses and validates each event. The Chat component consumes the iterable into its AgentStore, which reduces events into UI state and the ordered render model. Do not yield raw provider or LangGraph events; map them through Agentdock on the server.

The context passed to runtime.pipe() is trusted, server-side graph context. It is separate from browser UI state and from the event stream. Derive identity and permissions on the server, then pass only the authorized context to the graph. For example:

await runtime.pipe(response, {
  threadId: authorizedThreadId,
  input: { messages: [{ role: "user", content: prompt }] },
  context: { userId: authenticatedUser.id },
});

The prompt and threadId in the earlier adapter example show one possible application API shape; use your server's actual contract and never trust an unverified client-supplied thread ID.

Adapter capabilities

sendMessage({ text, attachments?, signal }) is required and returns an AsyncIterable<AgentEvent>. The UI creates and aborts the signal for the operation. Keep the adapter stable while a run is active; recreate it only when its actual configuration changes.

Optional memberImplement it whenAdapter contract
cancelRunYour server can stop active work.Receives { runId, signal }; sends the cancellation request and resolves when it is accepted.
respondToInterruptYour graph supports pending interrupts or approvals.Receives { runId, interruptId, decisions, signal }; returns canonical events for the resumed run. runId can be null for hydrated pending state.
continueRunYour graph can continue a static pause with no pending interrupt.Receives { runId, signal }; returns canonical events. runId can be null after history hydration.
attachmentsYour app supports file or image upload.Set optional accept, maxFiles, and maxFileSize, and implement upload({ file, signal }). Return { id, name, size, content } with an app-owned ID and canonical display content.

Add only capabilities that your server implements. The component hides actions whose adapter methods are absent. The host must authorize each action and verify that uploaded attachment IDs belong to the current user and conversation.

For a server using @agentdock-ai/conversations, createConversationClient() can provide a ready adapter and thread/history methods. It expects that package's documented HTTP routes; it does not create those routes on your server. Supply an authenticated fetcher when needed. See the @agentdock-ai/react README and @agentdock-ai/conversations README.

For example, if those routes are mounted under /api and your app already has an authenticated fetch function:

import { createConversationClient } from "@agentdock-ai/react";

const client = createConversationClient({
  baseUrl: "/api",
  getThreadId: () => selectedThreadId,
  fetcher: authenticatedFetch,
});

<Chat adapter={client.adapter} />;

Use the returned client methods to list or create threads, rename them, and load history. Your app still owns authentication, access checks, route mounting, and the selected thread state.

Configure the Chat component

The copied Chat component includes its own AgentProvider; pass it the adapter and optionally a long-lived AgentStore:

<Chat
  adapter={chatAdapter}
  welcomeTitle="How can I help?"
  welcomeDescription="Ask a question or choose a suggestion."
  suggestions={["Summarize this", "Find an answer"]}
  placeholder="Message your assistant..."
  showReasoning={false}
  onActivityChange={(active) => setAgentBusy(active)}
/>

Chat also accepts className and disabled. Omit optional props to use the defaults. Use AgentProvider directly when building a custom interface with hooks such as useAgentState and useAgentActions; those hooks must be rendered inside the provider. See the React package README for hook and store details.

Supported experiences

The panel includes safe Markdown, streaming, optional reasoning, compact tool disclosures, approvals, scoped errors, cancellation, an anchored composer, and scroll-to-latest. Light and dark styles inherit host semantic tokens.

Provide respondToInterrupt, continueRun, and cancelRun only when your application supports them. Approval values stay opaque and are returned unchanged. Optional adapter.attachments enables file/image selection, previews, upload status, retry, and removal. Your application owns storage, permissions, limits, and conversion into model input.

Component reference

The editable UI is made from small components that you can use, replace, or customize in your application. Each preview below renders the same source files shipped by the Agentdock UI installer. Switch to Code to inspect the complete component implementation, including its internal markup and behavior. The previews use sample content and do not connect to an agent.

Conversation

Message · User

A user-authored message bubble.

message.tsx
Help me design a simple chat interface.

Message · Assistant

An assistant response with optional reasoning and actions.

message.tsx

Keep the conversation readable, keep activity compact, and make the next action clear.

MessageContent

The content container shared by message variants.

message-content.tsx

Keep the conversation readable, keep activity compact, and make the next action clear.

MarkdownContent

Renders assistant text as formatted Markdown.

markdown-content.tsx

Readable Markdown\n\n- Lists\n- Inline code

MessageList

Lays out conversation messages and activity in order.

message-list.tsx
Help me design a simple chat interface.

Keep the conversation readable, keep activity compact, and make the next action clear.

Allow file access?

The assistant wants to read README.md.

This app can’t respond to this request here.

Composer

Composer

The primary input area for sending a conversation message.

composer.tsx

Shift + Enter for a new line

ComposerAttachments

Displays files attached to the composer.

composer-attachments.tsx
  • notes.md

    1 KB

  • outline.txt

    Uploading…

  • brief.pdf

    Upload failed

ComposerAttachmentPicker

Opens the file picker for new attachments.

composer-attachment-picker.tsx

Activity

StreamingText

Presents text while a response is still streaming.

streaming-text.tsx

I’m putting together a compact, readable interface…

ThinkingIndicator

Communicates that the assistant is working.

thinking-indicator.tsx

Reasoning

Shows expandable model reasoning content.

reasoning.tsx

ToolMessage

Displays a tool result as a conversation item.

tool-message.tsx

ToolCall

Shows one tool call and its current status.

tool-call.tsx

ToolTimeline

Groups tool activity into a compact timeline.

tool-timeline.tsx

ApprovalCard

Requests and presents a user approval decision.

approval-card.tsx

Allow file access?

The assistant wants to read README.md.

The agent is paused until you respond.

ErrorState

Explains a recoverable conversation or connection error.

error-state.tsx

Layout

ChatShell

Provides the full chat surface and composer layout.

chat-shell.tsx

Keep the conversation readable, keep activity compact, and make the next action clear.

Shift + Enter for a new line

ChatViewport

Owns the scrollable conversation viewport.

chat-viewport.tsx
Help me design a simple chat interface.

Keep the conversation readable, keep activity compact, and make the next action clear.

Allow file access?

The assistant wants to read README.md.

This app can’t respond to this request here.

ThreadSidebar

Lists threads and exposes thread navigation.

thread-sidebar.tsx

EmptyState

Introduces a conversation before the first message.

empty-state.tsx

How can I help you today?

Ask a question or choose a suggestion.

Suggestions

Offers ready-to-send prompts for an empty conversation.

empty-state.tsx

Controls

ChatActionBar

Adds compact actions beneath a response.

chat-action-bar.tsx

ScrollToLatest

Returns the conversation viewport to the latest item.

scroll-to-latest.tsx

ChatIcon

Renders the icon treatment used by chat controls.

icon.tsx

Button

The shared button primitive used by chat actions.

ui/button.tsx

Textarea

The shared multiline text input primitive.

ui/textarea.tsx

Collapsible

Provides accessible expandable and collapsible content.

ui/collapsible.tsx

The Chat component composes these building blocks for a complete conversation. Install the Chat source with the CLI, then edit the copied files in your app. For custom interfaces, use the components that fit your design and connect them to the AgentStore and hooks from @agentdock-ai/react.

Restore conversations

Load and normalize saved messages in the consuming app, then hydrate a fresh store before streaming:

import { AgentStore } from "@agentdock-ai/react";

const store = new AgentStore();
store.hydrateHistory({ messages: normalizedMessages, resumeState });
<Chat adapter={chatAdapter} store={store} />;

Messages use canonical messageId, role, and content. Optional resumeState is a fresh native waiting-state seed, such as runtime.getResumeState(threadId), with runId: null. The adapter targets your authorized thread for approvals and static continuation. Hydration is rejected during active execution.

For a custom interface, install the runtime package with your app's package manager. With npm:

npm install @agentdock-ai/react react

Then use AgentProvider, AgentStore, useAgentState, useAgentStore, useAgentActions, and selectRenderModel. See the UI README for the full surface.

On this page