# useDocyrusAgentChat URL: /docs/native/hooks/use-docyrus-agent-chat 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 ```bash pnpm dlx @docyrus/cli add @docyrus/rn-hooks-use-docyrus-agent-chat ``` **Dependencies:** - [ai](https://www.npmjs.com/package/ai) - [@ai-sdk/react](https://www.npmjs.com/package/@ai-sdk/react) - [zod](https://www.npmjs.com/package/zod) - [@tanstack/react-query](https://www.npmjs.com/package/@tanstack/react-query) Native-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`](/docs/native/hooks/use-docyrus-agent-threads) and [`useDocyrusAgentAttachments`](/docs/native/hooks/use-docyrus-agent-attachments), 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 ```tsx 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 ( ); } ``` ## 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 \| 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` | - | `{ 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` | Ensures a thread, uploads files, sends — pass to `onSendMessage` | | `stop` | `() => void` | Abort the stream — pass to `onStopGeneration` | | `regenerate` | `() => Promise` | Regenerate the last reply | | `newThread` | `() => void` | Clear and start a new thread | | `selectThread` | `(thread \| id) => Promise` | Load a thread's messages (its tool calls are never re-executed) | | `deleteThread` | `(thread \| id) => Promise` | Delete; resets when it was active | | `renameThread` | `(thread \| id, subject) => Promise` | 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` | Local handler; a thrown error is returned as `{ ok: false, error: { message } }` |