DocyrusAgent
Mobile AI assistant chat for Docyrus agents — streamed markdown, reasoning, tool calls, adaptive cards, sources and attachments, a composer with feature toggles and voice input, plus threads, projects, memories, an action panel and an inline text assistant.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-docyrus-agentpnpm add ai expo-document-picker (optional) expo-image-picker (optional) expo-clipboard (optional) expo-speech-recognition (optional, dev build)The React Native port of the web DocyrusAgent family. Every component, type and hook keeps its web name. Like web, the components are headless about the AI backend: they render the AI SDK UIMessage[] / ChatStatus you pass in and call onSendMessage. The runtime is either your own useChat or useDocyrusAgentChat, which bundles the Docyrus transport, threads, attachment uploads and client tools.
- Messages: assistant text renders through the built-in markdown renderer.
reasoningparts become a collapsible block that opens while it streams.tool-*/dynamic-toolparts become a collapsible card with the tool name, a state badge (Pending / Running / Awaiting approval / Completed / Error / Denied) and the JSON input and output. A tool output (or adata-*part) that is an Adaptive Card payload ({ type: 'AdaptiveCard' }, or{ card }/{ adaptiveCard }) renders through the nativeAdaptiveCard.source-url/source-documentparts become source chips, andfileparts become image thumbnails or file chips. - List: a
FlatListthat sticks to the bottom while the reply streams. Streamed updates are batched at ~50 ms. A scroll-to-bottom button appears when you scroll up, and a "Thinking…" row shows until the first chunk arrives. Long-press a message, or tap the copy button under a reply, to copy it. Copy usesexpo-clipboardwhen installed and falls back to the share sheet. - Composer: an auto-growing input capped at ~6 lines. Attach opens a Files / Photos / Camera sheet with only the installed pickers (
expo-document-picker/expo-image-picker). Attachments show as a thumbnail strip. The five feature toggles appear as one sheet of switches (toolbarLayout="menu") or as separate buttons ("expanded"). The send button becomes Stop while streaming. The mic and memory menu are optional tools. - Keyboard:
DocyrusAgentChatis aKeyboardAvoidingView(paddingon iOS). PasskeyboardVerticalOffsetwhen a navigation header sits above it.
Streaming, polyfills and host singletons
Expo SDK 57 needs no polyfills. Its WinterCG runtime already makes the global fetch the streaming expo/fetch and installs TextDecoderStream, TextEncoderStream and structuredClone. ai (and @ai-sdk/react for useDocyrusAgentChat) are regular dependencies, not host singletons: only plain UIMessage data crosses the component boundary, which is the same classification as on web. ai needs zod as a peer.
Voice input
DocyrusAgentChatInputMic (or DocyrusAgentChatInputMicButton + useSpeechRecognition + useDocyrusAgentMicTranscription) streams interim text into the input. It needs expo-speech-recognition and a development build — see Speech Recognition. In Expo Go the button hides itself.
Usage
import { useDocyrusAuth } from '@docyrus/signin/react-native';
import {
DocyrusAgent,
DocyrusAgentChatInput,
DocyrusAgentChatInputDefaultBody,
DocyrusAgentChatInputMemoryMenu,
DocyrusAgentChatInputMic,
DocyrusAgentThreadsSidebar,
DocyrusAgentToolbarButton
} from '@/components/docyrus-native/docyrus-agent';
import { useDocyrusAgentChat } from '@/hooks/docyrus-native/use-docyrus-agent-chat';
import { useDocyrusAgentInfo } from '@/hooks/docyrus-native/use-docyrus-agent-info';
export function AssistantScreen() {
const { client, user } = useDocyrusAuth();
const [threadsOpen, setThreadsOpen] = useState(false);
const info = useDocyrusAgentInfo({ client, agentId: AGENT_ID });
const chat = useDocyrusAgentChat({
agentId: AGENT_ID,
apiBaseUrl: 'https://api.docyrus.com/v1',
getAccessToken: () => client?.getAccessToken() ?? null,
client,
userId: user?.id
});
if (!info.agent) return null;
return (
<>
<DocyrusAgent
agent={info.agent}
messages={chat.messages}
chatStatus={chat.chatStatus}
suggestions={info.suggestions}
capabilities={info.capabilities}
allowAttachments
acceptFileTypes="image/*,.pdf,.docx"
onSendMessage={chat.sendMessage}
onStopGeneration={chat.stop}
headerLeading={<DocyrusAgentToolbarButton icon="fal sidebar" label="Threads" onPress={() => setThreadsOpen(true)} />}
chatInput={(
<DocyrusAgentChatInput>
<DocyrusAgentChatInputDefaultBody extraTools={<DocyrusAgentChatInputMic />} />
</DocyrusAgentChatInput>
)} />
<DocyrusAgentThreadsSidebar
agent={info.agent}
open={threadsOpen}
onClose={() => setThreadsOpen(false)}
threads={chat.threads.threads}
activeThreadId={chat.activeThreadId}
onNewThread={chat.newThread}
onSelectThread={thread => chat.selectThread(thread)}
onRenameThread={(thread, subject) => chat.renameThread(thread, subject)}
onDeleteThread={thread => chat.deleteThread(thread)} />
</>
);
}Compact trigger card that opens the chat in a full-height sheet:
<DocyrusAgentTrigger agent={agent} messages={messages} chatStatus={status} onSendMessage={send} onStopGeneration={stop} />API Reference
DocyrusAgent
| Prop | Type | Default | Description |
|---|---|---|---|
agent | AgentProfile | - | Agent identity: name, description, welcomeMessage (markdown), avatar { name, color, icon, image }, model (required) |
messages | UIMessage[] | [] | AI SDK messages |
chatStatus | ChatStatus | - | 'ready' | 'submitted' | 'streaming' | 'error' |
actions | AgentAction[] | [] | Actions for the action panel |
sources | AgentSource[] | [] | Context sources (action panel badges) |
onSendMessage | (payload: AgentMessagePayload) => void | Promise<void> | - | Submit — { text, files?, featureFlags? } |
onStopGeneration | () => void | - | Stop button |
onExecuteAction | (payload: AgentActionPayload) => void | Promise<void> | - | Action panel execute |
allowAttachments | boolean | false | Attach button + attachment strip |
acceptFileTypes | string | string[] | - | Web accept string ('image/*,.pdf', extensions mapped to MIME) or a MIME array |
suggestions | string[] | [] | Empty-state suggestion chips |
emptyState | ReactNode | - | Replaces the empty-state hero |
showMessageActions | boolean | true | Copy button under assistant replies |
headerLeading | ReactNode | - | Slot before the header avatar |
headerTrailing | ReactNode | - | Slot at the end of the header |
chatInput | ReactNode | - | Replaces the chat input |
chatTrailing | ReactNode | - | Slot below the chat input |
capabilities | AgentCapabilities | {} | support* flags that decide which feature toggles show |
initialFeatureFlags | Partial<AgentFeatureFlags> | - | Initial toggle state |
featureFlags | AgentFeatureFlags | - | Controlled toggle state (pair with onFeatureFlagsChange) |
onFeatureFlagsChange | (flags: AgentFeatureFlags) => void | - | Toggle change |
onAdaptiveCardAction | (event: AdaptiveCardActionEvent) => void | Promise<void> | - | Native only: action tapped inside an Adaptive Card tool output |
className | string | - | Root classes (flex-1 bg-background) |
DocyrusAgentTrigger
All DocyrusAgent props (forwarded to the chat), plus:
| Prop | Type | Default | Description |
|---|---|---|---|
open | boolean | - | Controlled open state of the chat sheet |
onOpenChange | (open: boolean) => void | - | Open state change |
dialogContainer | 'modal' | 'sheet' | 'drawer' | 'sheet' | AwesomeDialog container |
dialogSide | 'left' | 'right' | 'top' | 'bottom' | 'bottom' | Sheet edge (web default 'right') |
dialogSize | 'sm' | 'default' | 'lg' | 'xl' | 'full' | 'full' | Sheet size (web default 'lg') |
DocyrusAgentChat
| Prop | Type | Default | Description |
|---|---|---|---|
headerLeading / headerTrailing | ReactNode | - | Header slots |
chatInput | ReactNode | <DocyrusAgentChatInput /> | Input area |
chatTrailing | ReactNode | - | Slot below the input |
hideHeader | boolean | false | Native only: hide the agent header |
keyboardVerticalOffset | number | 0 | Native only: KeyboardAvoidingView offset |
className | string | - | Root classes |
DocyrusAgentChatMessages
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | - | Root classes |
contentContainerClassName | string | - | Native only: list content classes |
DocyrusAgentHeader
| Prop | Type | Default | Description |
|---|---|---|---|
agent | AgentProfile | - | Agent (required) |
leading / trailing | ReactNode | - | Slots |
className | string | - | Classes |
DocyrusAgentChatInput
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | <DocyrusAgentChatInputDefaultBody /> | Custom body. The draft provider (submit → onSendMessage) is always mounted |
className | string | - | Outer classes |
DocyrusAgentChatInputDefaultBody takes toolbarLayout?: 'menu' \| 'expanded' (default 'menu') and native-only extraTools?: ReactNode (appended to the toolbar). DocyrusAgentChatInputFooter (native; web PromptInputFooter) takes children (tools), submit? (default <DocyrusAgentChatInputSubmit />) and className.
Chat input parts
| Component | Props | Notes |
|---|---|---|
DocyrusAgentChatInputTextarea | TextInputProps except value / onChangeText / multiline / style, placeholder?, maxHeight?: number (default 140), className? | Auto-growing, bound to the draft |
DocyrusAgentChatInputSubmit | status?: ChatStatus, onStop?: () => void, disabled?: boolean, className? | Send, or Stop while submitted / streaming |
DocyrusAgentChatInputAttachButton | children?: ReactNode (icon), label?: string, className? | Hidden when no picker is installed |
DocyrusAgentChatInputAttachments | renderChip?: (file, remove) => ReactNode, className? | Horizontal chip strip |
DocyrusAgentChatInputFeatureToggle | feature: keyof AgentFeatureFlags, icon: string | ReactNode, tooltip: string, activeClassName?, className? | Generic toggle |
DocyrusAgentChatInputWebSearchToggle / …DocumentSearchToggle / …ThinkingToggle / …DeepResearchToggle / …WorkCanvasToggle | className? | Preset toggles |
DocyrusAgentToolbarButton | icon: string | ReactNode, label: string, onPress?, onLongPress?, disabled?, active?, activeTone?: 'primary' | 'destructive', badge?: number, overlay?: ReactNode, activeClassName?, className? | Native stand-in for web PromptInputButton |
DocyrusAgentChatInputFeatureMenu
| Prop | Type | Default | Description |
|---|---|---|---|
rows | ReadonlyArray<FeatureMenuRow> | DEFAULT_FEATURE_ROWS filtered by capabilities | Replace the rows |
extraRows | ReadonlyArray<FeatureMenuRow> | - | Rows appended after the defaults |
hideFeatures | ReadonlyArray<string> | - | Hide default rows by key |
renderRow | (row, { active, onChange }) => ReactNode | - | Custom row |
triggerIcon | string | 'fal sliders' | Trigger DocyrusIcon name (web: lucide component) |
activeClassName | string | - | Trigger active classes |
tooltip | string | 'Options' | Sheet title / accessibility label |
className | string | - | Trigger classes |
FeatureMenuRow: { key, icon?: string \| ReactNode, label?, labelKey?, labelFallback?, flag?, capability?, active?, onChange? }.
DocyrusAgentChatInputMicButton / DocyrusAgentChatInputMic
| Prop | Type | Default | Description |
|---|---|---|---|
isRecording | boolean | - | Recording state (button only, required) |
onToggle | () => void | - | Toggle (button only, required) |
isSupported | boolean | true | Hide when false (button only) |
disabled | boolean | - | Disable |
icon | string | 'fal microphone' | DocyrusIcon name |
recordingBadge | ReactNode | null | pulsing dot | Badge while recording (null hides it) |
recordingClassName | string | - | Active classes |
tooltipIdle / tooltipRecording | string | 'Microphone' / 'Stop recording' | Accessibility labels |
lang | string | device locale | DocyrusAgentChatInputMic only |
separator / merge / clearOnStart | see useDocyrusAgentMicTranscription | DocyrusAgentChatInputMic only | |
className | string | - | Classes |
useDocyrusAgentMicTranscription({ separator = ' ', merge?, clearOnStart = false }) returns { speechHandlers, resetBaseline }. Spread speechHandlers into useSpeechRecognition. It must run inside DocyrusAgentChatInput.
DocyrusAgentChatInputMemoryMenu
| Prop | Type | Default | Description |
|---|---|---|---|
onExtract | () => void | - | "Extract Memories from Session" |
onManageMemories | () => void | - | "Manage Memories" |
canExtract / canManage | boolean | true | Enable the items |
icon | string | 'fal brain-circuit' | Trigger DocyrusIcon name |
tooltip | string | 'Extract memories' | Sheet title / label |
children | ReactNode | - | Replaces the two items |
extractLabel / manageLabel | string | - | Item labels (web: ReactNode) |
className | string | - | Classes |
Prompt input (native)
DocyrusAgentPromptInputProvider (onSubmit({ text, files }), accept?: string[], multiple? = true, initialInput?) is what DocyrusAgentChatInput mounts. useDocyrusAgentPromptInput() is the native counterpart of the web usePromptInputController() + usePromptInputAttachments(). It returns { textInput: { value, setInput, clear }, attachments: { files, add, remove, clear, openFileDialog, availableSources }, submit, inputRef }, and attachments.add accepts NativeFiles or FileUIParts.
Sidebar & threads
| Component | Key props |
|---|---|
DocyrusAgentThreadsSidebar | threads, activeThreadId, isLoading, onSelectThread, onNewThread, onRenameThread, onDeleteThread, agent, header, onClose, open (true), presentation: 'inline' | 'drawer' (native, default 'drawer'), width (native, 288), navExtra, projects, activeProjectId, maxVisibleProjects (3), onSelectProject, onNewProject, onRenameProject, onDeleteProject, onShowMoreProjects, sessionsLabel, defaultCollapsed (false), emptyState, className |
DocyrusAgentSidebar | open (true), onClose, widthClassName ('w-72'), width (native, 288), presentation (native, 'inline'), children, className |
DocyrusAgentSidebarHeader | agent, title ('Threads'), onClose, closeIcon, trailing, className |
DocyrusAgentSidebarNewChatButton | onClick, onPress (native alias), icon, label ('New Chat'), className |
DocyrusAgentSidebarThreadsSection | threads, activeThreadId, isLoading, label ('Recent Sessions'), defaultCollapsed, onSelectThread, onRenameThread, onDeleteThread, emptyState, loadingState, renderItem, className |
DocyrusAgentSidebarProjectsSection | projects, activeProjectId, maxVisibleProjects (3), label, onSelectProject, onNewProject, onRenameProject, onDeleteProject, onShowMoreProjects, emptyState, renderItem, header, renderShowMore, className |
DocyrusAgentThreadItem | thread, isActive, onSelect, onRename, onDelete, hideActions, meta, className |
Rows: tap selects the row. Long-press or "⋯" opens an action sheet with Rename (inline edit) and Delete (confirm dialog). presentation="drawer" needs a SafeAreaProvider above it.
Projects
| Component | Key props |
|---|---|
DocyrusAgentProjectsView | projects, onBack, onNewProject, onSelectProject, onRenameProject, onDeleteProject, onEditClick, onDeleteClick, title ('Projects'), subtitle ('Manage projects'), compact, search (null hides), renderCard, headerLeading, className |
DocyrusAgentProjectCard | project, onSelect, onEdit, onDelete, header, description, body, actions, className |
DocyrusAgentProjectItem | project, isActive, onSelect, onRename, onDelete, hideActions, className |
DocyrusAgentCreateProjectView | onCancel, onCreate({ name, description }) (both required), isCreating (false), title, nameLabel, descriptionLabel, namePlaceholder, descriptionPlaceholder, headerLeading, extraFields, actions, className |
DocyrusAgentProjectDetailView | project, agent, threads (required), onBack, allProjectsLabel, headerLeading, onSendMessage, onStopGeneration, chatStatus, allowAttachments, acceptFileTypes (string | string[]), inputPlaceholder, isLoadingThreads, activeThreadId, onSelectThread, sessionsContent, worksContent, documentsContent, renderThreadRow, activeTab ('sessions'), onTabChange, footerText, className |
DocyrusAgentProjectHeader | project, onBack, allProjectsLabel ('All Projects'), leading, className |
DocyrusAgentProjectTabs | sessionsContent (required), worksContent, documentsContent, sessionsLabel, worksLabel, documentsLabel, value, onValueChange, className |
DocyrusAgentProjectThreadRow | thread, isActive, onSelect, className |
DocyrusAgentProjectPromptInput | agent (required), onSendMessage, onStopGeneration, chatStatus, allowAttachments, acceptFileTypes, placeholder ('Ask about this project...'), footerText (null hides), children, className |
Memories
| Component | Key props |
|---|---|
DocyrusAgentMemoriesView | memories, loading, saving, deletingId, error, levels (DEFAULT_MEMORY_LEVELS), defaultMemoryFormValue (EMPTY_MEMORY_FORM), onBack, onCreate, onUpdate, onDelete, searchQuery, onSearchQueryChange, hideSearch, levelFilter / onLevelFilterChange / hideLevelFilter (native), hideCreateButton, hideBackButton, renderRow, title, emptyState, loadingState, errorState, beforeList, headerTrailing, className |
DocyrusAgentMemoryRow | memory, onClick, onDelete, isDeleting, children, trailingMeta, actions, confirmDelete (native, true), className |
DocyrusAgentMemoryForm | value, onChange, levels, disabled, header, footer, className |
DocyrusAgentMemoryFormDialog | open, onOpenChange, memory (null = create), levels, saving (false), onSubmit, defaultValue (EMPTY_MEMORY_FORM), title(editing), footer(helpers), className |
DocyrusAgentMemoryExtractionDialog | open, onOpenChange, candidates, loading, saving, error, levels, onSave, onSkip, onSaveAll, onSkipAll, onLevelChange, renderRow, emptyState, loadingState, errorState, title, banner, footer, className |
DocyrusAgentMemoryCandidateRow | candidate, levels, onSave, onSkip, onLevelChange, children, className |
The dialogs are bottom AwesomeDialog sheets. Candidate rows slide out on save or skip (Reanimated). Back them with useDocyrusAgentMemories and useDocyrusAgentMemoryCandidates.
Action panel & inline
| Component | Props | Notes |
|---|---|---|
DocyrusAgentActionPanel | className | Mount inside DocyrusAgentProvider mode="action-panel". State machine: action list → params → executing (chat when opensChat). A single isDefault action is auto-selected |
DocyrusAgentActionList | className, contentContainerClassName (native) | FlatList of action cards |
DocyrusAgentActionParams | className | text → Input, textarea → Textarea, number → numeric Input, boolean → Switch, select → Select. Required fields are validated (native) |
DocyrusAgentInline | DocyrusAgentInlineProps — agent, messages, chatStatus, onSendMessage, onStopGeneration, onResubmit, selectedText, documentText, selectionStart, selectionEnd, onDocumentTextChange, onSelectedTextChange, promptTemplates, open, onOpenChange, side / align (ignored), className, children (trigger) | Bottom sheet. The host passes the selection from its own TextInput. Insert / Replace write back through onDocumentTextChange |
DocyrusAgentProvider
mode: 'chat' \| 'action-panel' \| 'trigger' (required), plus every DocyrusAgent data prop. useDocyrusAgent() reads the context (throws outside a provider); useOptionalDocyrusAgent() returns null instead.
Components
| Component | Description |
|---|---|
DocyrusAgent | Chat root (provider + chat) |
DocyrusAgentTrigger | Compact card that opens the chat in a sheet |
DocyrusAgentChat / DocyrusAgentChatMessages / DocyrusAgentHeader | Chat layout pieces |
DocyrusAgentMessageParts | Assistant part renderer (native export) |
DocyrusAgentAvatar | Agent avatar (image / icon / initials on the agent colour, native export) |
DocyrusAgentChatInput* | Composer and its parts |
DocyrusAgentToolbarButton, DocyrusAgentPromptInputProvider | Native composer primitives |
DocyrusAgent*Sidebar*, DocyrusAgentThreadItem | Threads drawer |
DocyrusAgentProject*, DocyrusAgentCreateProjectView, DocyrusAgentProjectsView | Projects |
DocyrusAgentMemor* | Memories |
DocyrusAgentActionPanel / …ActionList / …ActionParams | Action panel |
DocyrusAgentInline | Inline text assistant |
DocyrusAgentProvider | Context provider |
Type Exports
| Type | Description |
|---|---|
AgentProfile, AgentAction, AgentActionParam, AgentActionPayload, AgentMessagePayload, AgentSource, AgentSourceType, AgentCapabilities, AgentFeatureFlags, AgentMode, ActionPanelView | Data model (same as web) |
DocyrusAgentProps, DocyrusAgentTriggerProps, DocyrusAgentInlineProps, DocyrusAgentInlinePromptTemplate, DocyrusAgentContextValue | Props / context |
FeatureMenuRow, DocyrusAgentChatInputToolbarLayout, DocyrusAgentMicSpeechHandlers, UseDocyrusAgentMicTranscriptionArgs / Result | Composer |
DocyrusAgentMemory, DocyrusAgentMemoryCandidate, DocyrusAgentMemoryLevel, DocyrusAgentMemoryFormState | Memories |
DocyrusAgentProjectDetailTab, DocyrusAgentSidebarPresentation | Projects / sidebar |
DocyrusAgentPromptInputController, DocyrusAgentPromptInputFile, DocyrusAgentPromptInputMessage | Native prompt input |
Constants: DEFAULT_FEATURE_ROWS, DEFAULT_MEMORY_LEVELS, EMPTY_MEMORY_FORM. Helpers: extractAdaptiveCard, normalizeAcceptFileTypes, getAgentInitials.
Translation Keys
The components use the web ui.agent.* keys, including askPrompt, sendMessage, stopGenerating, attachFile, copyMessage, thinking, defaultWelcome, sources, tools.*, memories.*, memory.* and every sidebar / project key. Native-only keys:
| Key | Fallback |
|---|---|
ui.agent.attachSource.files / .photos / .camera | Files / Photos / Camera |
ui.agent.removeAttachment | Remove attachment |
ui.agent.copied | Copied |
ui.agent.scrollToBottom | Scroll to bottom |
ui.agent.reasoning.title / .thinking | Reasoning / Thinking… |
ui.agent.toolState.pending / .running / .awaitingApproval / .responded / .completed / .error / .denied | Pending / Running / Awaiting approval / Responded / Completed / Error / Denied |
ui.agent.tool.parameters / .result / .error | Parameters / Result / Error |
ui.agent.back | Back |
ui.agent.fieldRequired | This field is required |
ui.agent.inline.yourPrompt / .previousVersion / .nextVersion / .close / .optionsGenerated / .thinking / .insert / .replace / .prompts | Your prompt... / Previous version / Next version / Close / options generated: / Thinking… / Insert / Replace / Prompts |
ui.agent.memories.actions / .objectName / .allLevels | Memory actions / memory / All |
Differences from web
- Icons: props that take a lucide component on web (
triggerIcon,iconon the mic / memory menu,FeatureMenuRow.icon) take a DocyrusIcon name here ('fal globe'). - Menus: dropdowns and hover menus become bottom action sheets. Hover actions become long-press or an always-visible "⋯" button.
acceptFileTypesalso accepts a MIME array.FileUIPart.urlis a local file URI, and uploads send the RN FormData{ uri, name, type }shape.- Trigger defaults to a full-height bottom sheet (
dialogSide='bottom',dialogSize='full') and forwards capabilities, feature flags and header / input slots. - Sidebar has a
presentationprop:DocyrusAgentThreadsSidebaris a left drawer by default. - Inline is a bottom sheet.
side/alignare ignored, and the host supplies the selection. - Message rendering adds adaptive cards, sources and file parts, which the web chat does not render.
DeleteConfirmDialog
Modal confirmation dialog for delete operations with customizable title, description, and pending state.
DocyrusDataExportMenu
Toolbar export button that opens an action sheet (visible columns, all columns, or a column picker with Name / ID / Autonumber ID variants for relation, user and choice fields) and reports the selection for a server-side Docyrus export. API-aligned with the web DocyrusDataExportMenu.