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 chatThe 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 member | Implement it when | Adapter contract |
|---|---|---|
cancelRun | Your server can stop active work. | Receives { runId, signal }; sends the cancellation request and resolves when it is accepted. |
respondToInterrupt | Your 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. |
continueRun | Your graph can continue a static pause with no pending interrupt. | Receives { runId, signal }; returns canonical events. runId can be null after history hydration. |
attachments | Your 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.tsxMessage · Assistant
An assistant response with optional reasoning and actions.
message.tsxMessageContent
The content container shared by message variants.
message-content.tsxKeep the conversation readable, keep activity compact, and make the next action clear.
MarkdownContent
Renders assistant text as formatted Markdown.
markdown-content.tsxReadable Markdown\n\n- Lists\n- Inline code
MessageList
Lays out conversation messages and activity in order.
message-list.tsxAllow 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.tsxComposerAttachments
Displays files attached to the composer.
composer-attachments.tsxnotes.md
1 KB
outline.txt
Uploading…
brief.pdf
Upload failed
ComposerAttachmentPicker
Opens the file picker for new attachments.
composer-attachment-picker.tsxActivity
StreamingText
Presents text while a response is still streaming.
streaming-text.tsxI’m putting together a compact, readable interface…
ThinkingIndicator
Communicates that the assistant is working.
thinking-indicator.tsxReasoning
Shows expandable model reasoning content.
reasoning.tsxToolMessage
Displays a tool result as a conversation item.
tool-message.tsxToolCall
Shows one tool call and its current status.
tool-call.tsxToolTimeline
Groups tool activity into a compact timeline.
tool-timeline.tsxApprovalCard
Requests and presents a user approval decision.
approval-card.tsxAllow 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.tsxConnection interrupted
Your answer has been preserved. Try again when you’re ready.
Layout
ChatShell
Provides the full chat surface and composer layout.
chat-shell.tsxChatViewport
Owns the scrollable conversation viewport.
chat-viewport.tsxThreadSidebar
Lists threads and exposes thread navigation.
thread-sidebar.tsxEmptyState
Introduces a conversation before the first message.
empty-state.tsxHow can I help you today?
Ask a question or choose a suggestion.
Suggestions
Offers ready-to-send prompts for an empty conversation.
empty-state.tsxControls
ChatActionBar
Adds compact actions beneath a response.
chat-action-bar.tsxScrollToLatest
Returns the conversation viewport to the latest item.
scroll-to-latest.tsxChatIcon
Renders the icon treatment used by chat controls.
icon.tsxButton
The shared button primitive used by chat actions.
ui/button.tsxTextarea
The shared multiline text input primitive.
ui/textarea.tsxCollapsible
Provides accessible expandable and collapsible content.
ui/collapsible.tsxThe 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 reactThen use AgentProvider, AgentStore, useAgentState, useAgentStore,
useAgentActions, and selectRenderModel. See the
UI README for the full surface.

