useDocyrusAgentChat
The complete streaming chat runtime for a Docyrus AI agent — AI SDK transport, threads, attachment uploads and client-side tool execution — ready to drive DocyrusAgent.
Installation
pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-docyrus-agent-chatpnpm add ai @ai-sdk/react zod @tanstack/react-queryNative-first hook (platform-neutral name, so web can adopt it later) that extracts the transport and tool loop the web EditorAgent builds inline. It wires @ai-sdk/react useChat + ai DefaultChatTransport to ${apiBaseUrl}/ai/agents/${agentId}/chat with a per-request bearer token, embeds useDocyrusAgentThreads and useDocyrusAgentAttachments, sends the last maxHistoryLength messages (file parts stripped) with threadId, modelId, files, supportFiles, featureFlags, pageDataSourceId, additionalContext and your extraBody, and auto-executes registered client tools (results go back with addToolOutput; sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls continues the turn).
Streaming on React Native
No polyfills are needed on Expo SDK 57: its WinterCG runtime (expo/src/winter) replaces the global fetch with the streaming expo/fetch and installs TextDecoderStream, TextEncoderStream and structuredClone (Metro injects ReadableStream). If your app sets EXPO_PUBLIC_USE_RN_FETCH=1 or is a bare React Native app, pass a streaming implementation through the fetch option (e.g. import { fetch } from 'expo/fetch') and install those globals yourself (@ungap/structured-clone, @stardazed/streams-text-encoding).
Host singletons
ai and @ai-sdk/react are not host singletons: the Chat instance lives inside the hook and only plain UIMessage data crosses the boundary (the same classification as on web), so the CLI installs them as regular dependencies. ai needs zod (^3.25.76 || ^4.1.8) as a peer.
Usage
import { useDocyrusAuth } from '@docyrus/signin/react-native';
import { DocyrusAgent, DocyrusAgentThreadsSidebar } 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 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,
clientTools: [{ name: 'open_record', execute: input => router.push(`/records/${(input as { id: string }).id}`) }]
});
if (!info.agent) return null;
return (
<DocyrusAgent
agent={info.agent}
suggestions={info.suggestions}
capabilities={info.capabilities}
allowAttachments={info.capabilities.supportFiles}
messages={chat.messages}
chatStatus={chat.chatStatus}
onSendMessage={chat.sendMessage}
onStopGeneration={chat.stop} />
);
}API Reference
UseDocyrusAgentChatArgs
| Option | Type | Default | Description |
|---|---|---|---|
agentId | string | - | Tenant AI agent ID (required) |
apiBaseUrl | string | - | API base URL including /v1 (required) |
getAccessToken | () => string | null | undefined | Promise<…> | - | Bearer token, called on every request |
client | DocyrusAgentChatClient | null | undefined | - | REST client (get / post / patch / delete) for threads + uploads (required) |
userId | string | null | null | Restrict the thread list to this user |
projectId | string | null | null | Scope new threads + the thread list to a project |
deploymentId | string | null | null | Scope new threads to a deployment |
senderName | string | 'User' | sender_name of new threads |
extraBody | () => Record<string, unknown> | null | undefined | - | Extra request-body fields (called per send) |
additionalContext | () => string | null | undefined | - | Per-turn system-prompt context (called per send) |
dataSourceId | string | null | null | Sent as pageDataSourceId |
clientTools | ReadonlyArray<DocyrusAgentClientTool> | - | { name, execute(input) } tools run on the device |
modelId | string | 'default' | Model id sent to the backend |
maxHistoryLength | number | 10 | Trailing messages sent as history |
fetch | typeof fetch | global fetch | Streaming fetch implementation |
throttle | number | 50 | useChat UI update throttle (ms) |
enableThreads | boolean | true | Load the thread list |
UseDocyrusAgentChatResult
| Field | Type | Description |
|---|---|---|
messages | UIMessage[] | Chat messages |
chatStatus | ChatStatus | 'ready' | 'submitted' | 'streaming' | 'error' |
error | Error | undefined | Last transport error |
sendMessage | (payload: AgentMessagePayload) => Promise<void> | Ensures a thread, uploads files, sends — pass to onSendMessage |
stop | () => void | Abort the stream — pass to onStopGeneration |
regenerate | () => Promise<void> | Regenerate the last reply |
newThread | () => void | Clear and start a new thread |
selectThread | (thread | id) => Promise<void> | Load a thread's messages (its tool calls are never re-executed) |
deleteThread | (thread | id) => Promise<boolean> | Delete; resets when it was active |
renameThread | (thread | id, subject) => Promise<boolean> | Rename |
activeThreadId | string | null | Current thread |
setMessages | (messages: UIMessage[]) => void | Replace messages |
threads | UseDocyrusAgentThreadsResult | Thread list + loading flags (for DocyrusAgentThreadsSidebar) |
DocyrusAgentClientTool
| Field | Type | Description |
|---|---|---|
name | string | Tool key — must match the tenant_ai_tool.key registered to the agent |
execute | (input: unknown) => unknown | Promise<unknown> | Local handler; a thrown error is returned as { ok: false, error: { message } } |
useDocyrusAgentAttachments
Uploads chat attachments to a Docyrus agent thread (creating the thread on first use) and returns the storage paths for the chat request.
useDocyrusAgentInfo
Fetches a Docyrus AI agent's profile, suggestions and capabilities from the agent-deployment endpoint and maps them onto the DocyrusAgent props.