@dudousxd/nestjs-agent-react 0.14.0 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,18 +1,174 @@
1
1
  import * as ai from 'ai';
2
2
  import { ChatTransport, UIMessage, UIMessageChunk, DataUIPart, UIDataTypes } from 'ai';
3
3
  import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
4
- import { StoredMessage, ThreadSummary, SkillCatalogEntry, ToolCatalogEntry, ThreadDetail, MessageAttachment, QuotaView } from '@dudousxd/nestjs-agent-core';
4
+ import { ThreadSummary, ThreadDetail, MessageAttachment, ToolCatalogEntry, SkillCatalogEntry, QuotaView, MessageFeedbackValue, MessageFeedback, StoredMessage } from '@dudousxd/nestjs-agent-core';
5
5
  export { ElicitationInput, ElicitationInputType, validateElicitationAnswer as validateAnswer, validateElicitationValue as validateAnswerValue } from '@dudousxd/nestjs-agent-core';
6
6
  import React__default from 'react';
7
- import { a as TranscriptBlock, U as UsageSummary, b as TimestampInfo, C as ChatStatus, M as MessageUsageInfo, c as ToolCatalog, A as ApproveOptions, d as AnyToolUIPart, e as TranscriptFile, T as TranscriptUiBlock } from './model-CMNhyau6.js';
8
- export { f as ApprovalBlockOptions, B as BuildBlocksOptions, g as CoercibleQuestion, D as DescribeToolCallOptions, E as ElicitationBlockOptions, G as GroupToolActivityOptions, R as RawAnswer, h as ResolvedReading, i as ResolvedResultView, j as RetrievedPassage, S as SettleAction, k as ToolActivityGroup, l as ToolCallDescription, m as ToolCallState, n as ToolCallStatus, o as TranscriptApproval, p as TranscriptApprovalStatus, q as TranscriptElicitationBlock, r as TranscriptElicitationOutcome, s as TranscriptFilesBlock, t as TranscriptQuestion, u as TranscriptQuestionOption, v as TranscriptReasoningBlock, w as TranscriptSettleState, x as TranscriptSource, y as TranscriptSourcesBlock, z as TranscriptTextBlock, F as TranscriptToolBlock, H as TranscriptToolCall, I as buildTranscriptBlocks, J as coerceAnswer, K as correctedCallIds, L as describeTimestamp, N as describeToolCall, O as describeUsage, P as extractMessageText, Q as fillTemplate, V as formatRelativeTime, W as groupToolActivity, X as inferResultView, Y as isActionCall, Z as phraseFor, _ as readPath, $ as resolveResultView, a0 as toolCallState, a1 as toolCatalogFrom } from './model-CMNhyau6.js';
7
+ import { a as TranscriptBlock, U as UsageSummary, b as TimestampInfo, C as ChatStatus, M as MessageUsageInfo, c as ToolCatalog, A as ApproveOptions, d as AnyToolUIPart, e as TranscriptFile, T as TranscriptUiBlock } from './model-gHqjNjLJ.js';
8
+ export { f as ApprovalBlockOptions, B as BuildBlocksOptions, g as CoercibleQuestion, D as DescribeToolCallOptions, E as ElicitationBlockOptions, G as GroupToolActivityOptions, R as RawAnswer, h as ResolvedReading, i as ResolvedResultView, j as RetrievedPassage, S as SettleAction, k as ToolActivityGroup, l as ToolCallDescription, m as ToolCallState, n as ToolCallStatus, o as TranscriptApproval, p as TranscriptApprovalStatus, q as TranscriptElicitationBlock, r as TranscriptElicitationOutcome, s as TranscriptFilesBlock, t as TranscriptQuestion, u as TranscriptQuestionOption, v as TranscriptReasoningBlock, w as TranscriptSettleState, x as TranscriptSource, y as TranscriptSourcesBlock, z as TranscriptTextBlock, F as TranscriptToolBlock, H as TranscriptToolCall, I as buildTranscriptBlocks, J as coerceAnswer, K as correctedCallIds, L as describeTimestamp, N as describeToolCall, O as describeUsage, P as extractMessageText, Q as fillTemplate, V as formatRelativeTime, W as groupToolActivity, X as inferResultView, Y as isActionCall, Z as phraseFor, _ as readPath, $ as resolveResultView, a0 as toolCallState, a1 as toolCatalogFrom } from './model-gHqjNjLJ.js';
9
+
10
+ /**
11
+ * Partial update accepted by `PATCH <base>/threads/:threadId`. `defaultAgent: null` clears a
12
+ * previously-set default back to the module's own default; omitting it leaves the thread's
13
+ * current default untouched.
14
+ */
15
+ interface ThreadPatch {
16
+ title?: string;
17
+ defaultAgent?: string | null;
18
+ }
19
+ /** Starting a turn: the body `POST <base>/chat` takes (see docs/stream-protocol.md). */
20
+ interface ChatStreamRequest {
21
+ /** `{ message, threadId?, agent?, attachments?, pageContext?, regenerate?, … }`. */
22
+ body: Record<string, unknown>;
23
+ /** Per-request headers the AI SDK was handed for this send. */
24
+ headers?: Record<string, string>;
25
+ /** Aborted when the user stops the turn. */
26
+ signal?: AbortSignal;
27
+ }
28
+ /** Attaching to a run that is already streaming: `GET <base>/chat/:runId/stream?after=<seq>`. */
29
+ interface ResumeStreamRequest {
30
+ runId: string;
31
+ /**
32
+ * The sequence number (SSE `id:`) of the last frame the client already has. Omitted → replay
33
+ * from the first frame. A backend that cannot skip may ignore it: the transport also drops frames
34
+ * at or below it.
35
+ */
36
+ after?: number;
37
+ headers?: Record<string, string>;
38
+ signal?: AbortSignal;
39
+ }
40
+ /**
41
+ * An open chat stream: the raw `text/event-stream` bytes in the stream-protocol framing, plus the
42
+ * identity a backend may learn before the first frame (the `X-Agent-Run-Id` / `X-Agent-Thread-Id`
43
+ * headers). The body's own `event: meta` frame supplies them otherwise.
44
+ */
45
+ interface ChatStreamResponse {
46
+ body: ReadableStream<Uint8Array>;
47
+ runId?: string;
48
+ threadId?: string;
49
+ }
50
+ /** What `POST <base>/messages/:id/feedback` takes. `value: null` clears the rating. */
51
+ interface MessageFeedbackInput {
52
+ value: MessageFeedbackValue | null;
53
+ comment?: string;
54
+ }
55
+ interface UploadAttachmentOptions {
56
+ signal?: AbortSignal;
57
+ /** Called with 0..1 as the upload progresses, when the backend can observe it. */
58
+ onProgress?: (fraction: number) => void;
59
+ }
60
+ /**
61
+ * Everything the React layer asks of a server — the seam between `useAgentChat` (and the other
62
+ * hooks) and whatever serves the agent. {@link AgentClient} is the default implementation, over the
63
+ * library's own REST routes with `fetch`. An app with its own client — a generated one, a different
64
+ * auth scheme (cookie session + CSRF header), a backend that is not this library at all but speaks
65
+ * docs/stream-protocol.md — implements this instead and passes it as `useAgentChat({ backend })`.
66
+ *
67
+ * The streaming, thread and cancel members are required: without them there is no chat. The rest
68
+ * are optional; a hook that needs one the backend does not have throws
69
+ * {@link AgentBackendUnsupportedError} when it is called, so a backend only implements what its
70
+ * server supports.
71
+ */
72
+ interface AgentBackend {
73
+ /** Start a turn and return its SSE stream. Throw on a non-2xx answer. */
74
+ openChatStream(request: ChatStreamRequest): Promise<ChatStreamResponse>;
75
+ /** Attach to a streaming run. Resolve `null` when nothing is streaming under that id (HTTP 404). */
76
+ resumeChatStream(request: ResumeStreamRequest): Promise<ChatStreamResponse | null>;
77
+ /** Hard-stop a run server-side. */
78
+ cancelStream(runId: string): Promise<unknown>;
79
+ listThreads(): Promise<ThreadSummary[]>;
80
+ getThread(id: string): Promise<ThreadDetail>;
81
+ updateThread(id: string, patch: ThreadPatch): Promise<unknown>;
82
+ deleteThread(id: string): Promise<unknown>;
83
+ forkFromMessage?(threadId: string, messageId: string): Promise<ThreadSummary>;
84
+ promoteThread?(id: string): Promise<unknown>;
85
+ truncateFromMessage?(threadId: string, messageId: string): Promise<unknown>;
86
+ approveToolCall?(input: {
87
+ toolCallId: string;
88
+ remember?: boolean;
89
+ via?: string;
90
+ }): Promise<unknown>;
91
+ rejectToolCall?(input: {
92
+ toolCallId: string;
93
+ reason?: string;
94
+ via?: string;
95
+ }): Promise<unknown>;
96
+ answerToolCall?(input: {
97
+ toolCallId: string;
98
+ answers?: Record<string, string[]>;
99
+ }): Promise<unknown>;
100
+ skipToolCall?(input: {
101
+ toolCallId: string;
102
+ }): Promise<unknown>;
103
+ uploadAttachment?(file: File, options?: UploadAttachmentOptions): Promise<MessageAttachment>;
104
+ listTools?(agent?: string): Promise<ToolCatalogEntry[]>;
105
+ listSkills?(threadId?: string): Promise<SkillCatalogEntry[]>;
106
+ getQuotaToday?(): Promise<QuotaView>;
107
+ setMessageFeedback?(messageId: string, input: MessageFeedbackInput): Promise<{
108
+ feedback: MessageFeedback | null;
109
+ }>;
110
+ }
111
+ /** An optional {@link AgentBackend} member the bound backend does not implement was called. */
112
+ declare class AgentBackendUnsupportedError extends Error {
113
+ readonly method: string;
114
+ constructor(method: string);
115
+ }
116
+ type OptionalMethod = {
117
+ [K in keyof AgentBackend]-?: undefined extends AgentBackend[K] ? K : never;
118
+ }[keyof AgentBackend];
119
+ /**
120
+ * The optional backend method `name`, bound — or a throw naming it. For hook code that has to call
121
+ * something a backend may not offer.
122
+ */
123
+ declare function requireBackendMethod<K extends OptionalMethod>(backend: AgentBackend, name: K): NonNullable<AgentBackend[K]>;
9
124
 
10
125
  /** Identity surfaced by the backend's `meta` SSE frame / response headers. */
11
126
  interface AgentStreamMeta {
12
127
  runId: string;
13
128
  threadId: string;
14
129
  }
130
+ /**
131
+ * How the transport retries a stream that dropped mid-run. It reconnects only to a server that
132
+ * numbers its frames (SSE `id:`) — without a cursor a reconnect would replay the run into the
133
+ * message it is already writing.
134
+ */
135
+ interface ReconnectOptions {
136
+ /** Attempts before giving up with an error chunk. Default 6. */
137
+ maxAttempts?: number;
138
+ /** Delay before the first attempt, doubled each attempt. Default 500 ms. */
139
+ baseDelayMs?: number;
140
+ /** Cap on the delay between attempts. Default 8000 ms. */
141
+ maxDelayMs?: number;
142
+ }
143
+ /** Where a live stream stands. `reconnecting` → it dropped and the transport is retrying. */
144
+ type StreamConnectionState = {
145
+ status: 'live';
146
+ } | {
147
+ status: 'reconnecting';
148
+ runId: string;
149
+ attempt: number;
150
+ after: number;
151
+ }
152
+ /** The run ended while the client was away (the resume answered 404): reload the thread. */
153
+ | {
154
+ status: 'gone';
155
+ runId: string;
156
+ }
157
+ /** Every attempt failed; the stream ended with an error chunk. */
158
+ | {
159
+ status: 'failed';
160
+ runId: string;
161
+ };
15
162
  interface AgentChatTransportOptions {
163
+ /**
164
+ * What the transport talks to. Default: an {@link AgentClient} built from `baseUrl`, `headers`,
165
+ * `getHeaders`, `credentials` and `fetch` below — which are ignored when a backend is given.
166
+ */
167
+ backend?: AgentBackend;
168
+ /** Retry a dropped stream from its last frame. Default on; `false` turns it off. */
169
+ reconnect?: ReconnectOptions | false;
170
+ /** Fires as a stream drops, reconnects, or gives up. */
171
+ onConnectionChange?: (state: StreamConnectionState) => void;
16
172
  /**
17
173
  * Origin + base path the agent endpoints hang off, e.g.
18
174
  * `https://api.example.com`. Endpoints are appended as
@@ -96,6 +252,7 @@ declare class AgentChatTransport implements ChatTransport<UIMessage> {
96
252
  * several entries under each id — which React renders as duplicate keys.
97
253
  */
98
254
  private attemptLive;
255
+ private readonly backend;
99
256
  constructor(options?: AgentChatTransportOptions);
100
257
  /** Run id of the most recent stream — HITL approve/reject target this. */
101
258
  get runId(): string | undefined;
@@ -110,10 +267,8 @@ declare class AgentChatTransport implements ChatTransport<UIMessage> {
110
267
  get isAttemptLive(): boolean;
111
268
  sendMessages(options: Parameters<ChatTransport<UIMessage>['sendMessages']>[0]): Promise<ReadableStream<UIMessageChunk>>;
112
269
  reconnectToStream(options: Parameters<ChatTransport<UIMessage>['reconnectToStream']>[0]): Promise<ReadableStream<UIMessageChunk> | null>;
113
- private fetchImpl;
114
- private baseUrl;
115
- private resolveHeaders;
116
270
  private captureHeaderMeta;
271
+ private reconnectPolicy;
117
272
  private recordMeta;
118
273
  /**
119
274
  * Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.
@@ -131,6 +286,84 @@ declare class AgentChatTransport implements ChatTransport<UIMessage> {
131
286
  private toChunkStream;
132
287
  }
133
288
 
289
+ interface UseMessageFeedbackOptions {
290
+ /** `useAgentChat(...).backend`, or your own. Must implement `setMessageFeedback`. */
291
+ backend: AgentBackend;
292
+ /**
293
+ * The thread the messages belong to — `useAgentChat(...).getThreadId`, or an id. Needed to rate a
294
+ * message streamed in this session: its id is the client's own, so the persisted row it became is
295
+ * looked up by the run that wrote it.
296
+ */
297
+ threadId?: string | (() => string | undefined);
298
+ }
299
+ interface MessageFeedbackState {
300
+ /** The rating to show: the one set in this session, else the persisted one, else `null`. */
301
+ feedbackOf: (message: UIMessage) => MessageFeedback | null;
302
+ /** A rating for this message is on its way to the server. */
303
+ isPending: (message: UIMessage) => boolean;
304
+ /** Set `'up'`/`'down'` (optionally with a comment), or clear with `null`. Optimistic. */
305
+ rate: (message: UIMessage, value: MessageFeedbackValue | null, comment?: string) => Promise<void>;
306
+ /** Thumbs-button semantics: the same value again clears it, another value replaces it. */
307
+ toggle: (message: UIMessage, value: MessageFeedbackValue) => Promise<void>;
308
+ /** The last failure; cleared by the next successful rating. */
309
+ error: Error | null;
310
+ }
311
+ /**
312
+ * Thumbs-up/down (+ comment) on assistant messages, over `POST <base>/messages/:id/feedback`.
313
+ * Headless: it holds the state and the calls, the host draws the buttons. A rating shows at once
314
+ * and rolls back if the server refuses it.
315
+ */
316
+ declare function useMessageFeedback(options: UseMessageFeedbackOptions): MessageFeedbackState;
317
+
318
+ /**
319
+ * What changed about a backend's thread list. `changed` → refetch (a thread was created, or a run
320
+ * settled and the server wrote its title); `title` → the stream announced a title, patch in place.
321
+ */
322
+ type ThreadsEvent = {
323
+ type: 'changed';
324
+ } | {
325
+ type: 'title';
326
+ threadId: string;
327
+ title: string;
328
+ } | {
329
+ type: 'removed';
330
+ threadId: string;
331
+ };
332
+ type Listener = (event: ThreadsEvent) => void;
333
+ /** Tell every {@link useThreads} on `backend` that its list moved. */
334
+ declare function notifyThreads(backend: AgentBackend, event: ThreadsEvent): void;
335
+ /** Subscribe to {@link notifyThreads} for `backend`; returns the unsubscribe. */
336
+ declare function onThreadsEvent(backend: AgentBackend, listener: Listener): () => void;
337
+
338
+ interface UseThreadsOptions {
339
+ /**
340
+ * The backend to list — `useAgentChat(...).backend`, or your own. Must be STABLE across renders:
341
+ * the list refreshes itself when a chat on the SAME backend creates a thread, settles a run or
342
+ * streams a title.
343
+ */
344
+ backend: AgentBackend;
345
+ /** `false` holds the first request (e.g. until the user is signed in). Default `true`. */
346
+ enabled?: boolean;
347
+ }
348
+ interface ThreadsState {
349
+ /** Newest first, as the server orders them. `[]` until loaded. */
350
+ threads: ThreadSummary[];
351
+ isLoading: boolean;
352
+ error: Error | null;
353
+ /** Ask the server again. */
354
+ refresh: () => Promise<void>;
355
+ /** Rename on the server; the list shows the new title at once and rolls back on failure. */
356
+ rename: (id: string, title: string) => Promise<void>;
357
+ /** Delete on the server; the thread leaves the list at once and comes back on failure. */
358
+ remove: (id: string) => Promise<void>;
359
+ }
360
+ /**
361
+ * The actor's conversations, kept current without the host wiring refetches: a chat on the same
362
+ * backend reports thread creation and settled runs (refetch) and streamed titles (patched in
363
+ * place). Headless — it renders nothing.
364
+ */
365
+ declare function useThreads(options: UseThreadsOptions): ThreadsState;
366
+
134
367
  /**
135
368
  * A sub-agent the conversation started and did not wait for. The chat is free while it works, so a
136
369
  * client has to be able to say what is still running and to show the answer when it lands — neither
@@ -180,34 +413,46 @@ interface CancelResult {
180
413
  interface OkResult {
181
414
  ok: boolean;
182
415
  }
183
- /**
184
- * Partial update accepted by `PATCH /agent/threads/:threadId`. `defaultAgent: null` clears a
185
- * previously-set default back to the module's own default; omitting it leaves the thread's
186
- * current default untouched.
187
- */
188
- interface ThreadPatch {
189
- title?: string;
190
- defaultAgent?: string | null;
191
- }
192
416
  interface AgentClientOptions {
193
417
  /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
194
418
  baseUrl?: string;
195
419
  /** Static headers merged into every request. */
196
420
  headers?: Record<string, string>;
197
- /** Resolved per request — for short-lived bearer tokens. */
421
+ /**
422
+ * Resolved per request — for short-lived bearer tokens, or a CSRF header read from a cookie
423
+ * (`{ 'X-XSRF-TOKEN': readCookie('XSRF-TOKEN') }`), which has to be read at request time because
424
+ * the server may rotate it.
425
+ */
198
426
  getHeaders?: () => Record<string, string> | Promise<Record<string, string>>;
199
- /** Forwarded to fetch so cookie auth works. */
427
+ /**
428
+ * Forwarded to fetch so cookie auth works. Same-origin requests send cookies by default; set
429
+ * `'include'` when the API lives on another origin (and have it answer with credentialed CORS).
430
+ */
200
431
  credentials?: RequestCredentials;
201
432
  /** Injectable for tests / non-browser runtimes. */
202
433
  fetch?: typeof fetch;
203
434
  }
204
435
  /**
205
- * Framework-agnostic REST client for the nestjs-agent endpoints. Used by
206
- * `useAgentChat`, but standalone-usable (vanilla fetch, no React).
436
+ * Framework-agnostic REST client for the nestjs-agent endpoints — the default {@link AgentBackend}.
437
+ * Used by `useAgentChat`, but standalone-usable (vanilla fetch, no React).
207
438
  */
208
- declare class AgentClient {
439
+ declare class AgentClient implements AgentBackend {
209
440
  private readonly options;
210
441
  constructor(options?: AgentClientOptions);
442
+ /** `POST /agent/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
443
+ openChatStream(request: ChatStreamRequest): Promise<ChatStreamResponse>;
444
+ /**
445
+ * `GET /agent/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
446
+ * streaming under that id (404).
447
+ */
448
+ resumeChatStream(request: ResumeStreamRequest): Promise<ChatStreamResponse | null>;
449
+ /**
450
+ * Rate a message (`'up'`/`'down'`, optional comment) or clear its rating (`value: null`). Answers
451
+ * the stored rating.
452
+ */
453
+ setMessageFeedback(messageId: string, input: MessageFeedbackInput): Promise<{
454
+ feedback: MessageFeedback | null;
455
+ }>;
211
456
  listThreads(): Promise<ThreadSummary[]>;
212
457
  /**
213
458
  * The skills this caller can invoke right now, scope-resolved — the same list, built by the same
@@ -233,7 +478,7 @@ declare class AgentClient {
233
478
  * mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
234
479
  * what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
235
480
  */
236
- uploadAttachment(file: File): Promise<MessageAttachment>;
481
+ uploadAttachment(file: File, options?: UploadAttachmentOptions): Promise<MessageAttachment>;
237
482
  promoteThread(id: string): Promise<OkResult>;
238
483
  truncateFromMessage(threadId: string, messageId: string): Promise<OkResult>;
239
484
  getQuotaToday(): Promise<QuotaToday>;
@@ -270,6 +515,10 @@ declare class AgentClient {
270
515
  skipToolCall(input: {
271
516
  toolCallId: string;
272
517
  }): Promise<void>;
518
+ private fetchImpl;
519
+ private baseUrl;
520
+ private resolveHeaders;
521
+ private credentials;
273
522
  private request;
274
523
  private handleResponse;
275
524
  }
@@ -785,7 +1034,8 @@ interface SkillSuggestionData {
785
1034
  shadows?: string[];
786
1035
  }
787
1036
  interface SkillsSourceOptions {
788
- client: AgentClient;
1037
+ /** Any backend with `listSkills` — `useAgentChat(...).backend`. */
1038
+ client: Pick<AgentBackend, 'listSkills'>;
789
1039
  /**
790
1040
  * Read at query time, not captured once: a threadless chat learns its id only when the backend
791
1041
  * creates one on the first send, and a skill may be scoped to a conversation.
@@ -879,12 +1129,15 @@ interface ComposerAutocomplete {
879
1129
  */
880
1130
  declare function useComposerAutocomplete(options: UseComposerAutocompleteOptions): ComposerAutocomplete;
881
1131
 
1132
+ /** What the catalog needs of a backend. */
1133
+ type ToolsBackend = Pick<AgentBackend, 'listTools'>;
1134
+
882
1135
  interface UseToolCatalogOptions {
883
1136
  /**
884
1137
  * The client to ask — `useAgentChat(...).client`, or one of your own. Must be STABLE across
885
1138
  * renders (the shared copy is keyed by it): a client constructed inline refetches every render.
886
1139
  */
887
- client: AgentClient;
1140
+ client: ToolsBackend;
888
1141
  /** The agent whose tools to list. Omitted → the server's default agent. */
889
1142
  agent?: string;
890
1143
  /** `false` holds the request (e.g. until the user is signed in). Default `true`. */
@@ -978,6 +1231,16 @@ interface AggregatedTurnUsage {
978
1231
  interface StoredTurnMetadata {
979
1232
  usage: AggregatedTurnUsage;
980
1233
  }
1234
+ /**
1235
+ * Everything a message's `metadata` may carry from this library: `usage` on a merged replayed turn,
1236
+ * `feedback` on a replayed message that was rated (a merged turn is rated through its last row,
1237
+ * whose id it carries), `runId` on a live-streamed one, `costUsd` from the stream's step ends.
1238
+ */
1239
+ interface AgentMessageMetadata extends Partial<StoredTurnMetadata> {
1240
+ feedback?: MessageFeedback;
1241
+ runId?: string;
1242
+ costUsd?: number | null;
1243
+ }
981
1244
  /**
982
1245
  * Convert a thread's full `StoredMessage[]` history into `UIMessage[]`, merging consecutive
983
1246
  * assistant rows (no user message between them) into ONE `UIMessage` per turn.
@@ -1018,7 +1281,19 @@ interface StoredTurnMetadata {
1018
1281
  */
1019
1282
  declare function storedThreadToUiMessages(messages: StoredMessage[]): UIMessage[];
1020
1283
 
1021
- interface UseAgentChatOptions {
1284
+ interface UseAgentChatOptions<B extends AgentBackend = AgentClient> {
1285
+ /**
1286
+ * What the chat talks to — see {@link AgentBackend}. Omitted → an {@link AgentClient} over this
1287
+ * library's REST routes, built from `baseUrl`, `headers`, `getHeaders`, `credentials` and `fetch`
1288
+ * (which are ignored when a backend is given). Must be stable across renders.
1289
+ */
1290
+ backend?: B;
1291
+ /**
1292
+ * Retry a stream that dropped mid-run from its last frame (`GET <base>/chat/:runId/stream?after=`),
1293
+ * with exponential backoff. `status` reads `'reconnecting'` meanwhile. Default on; `false` turns
1294
+ * it off.
1295
+ */
1296
+ reconnect?: ReconnectOptions | false;
1022
1297
  /** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
1023
1298
  baseUrl?: string;
1024
1299
  /** Static headers merged into every request. */
@@ -1029,8 +1304,8 @@ interface UseAgentChatOptions {
1029
1304
  credentials?: RequestCredentials;
1030
1305
  /** Injectable for tests / non-browser runtimes. */
1031
1306
  fetch?: typeof fetch;
1032
- /** Reuse an existing client instead of constructing one. */
1033
- client?: AgentClient;
1307
+ /** Reuse an existing client instead of constructing one. Prefer {@link backend}. */
1308
+ client?: B;
1034
1309
  /** Named agent to run each turn. */
1035
1310
  agent?: string;
1036
1311
  /** Thread this chat is bound to. Omitted → backend creates one on send. */
@@ -1138,10 +1413,13 @@ interface AddToolResultArgs {
1138
1413
  * fork, quota, cancel, and HITL approve/reject — all driven through
1139
1414
  * `AgentClient`. Mirrors flip's `useAdminChat`, generalized.
1140
1415
  */
1141
- declare function useAgentChat(options: UseAgentChatOptions): {
1416
+ declare function useAgentChat<B extends AgentBackend = AgentClient>(options: UseAgentChatOptions<B>): {
1417
+ status: ChatStatus;
1418
+ /** Where the live stream stands — see {@link StreamConnectionState}. */
1419
+ connection: StreamConnectionState;
1142
1420
  sendMessage: (message?: (Omit<UIMessage<unknown, UIDataTypes, ai.UITools>, "id" | "role"> & {
1143
1421
  id?: string;
1144
- role?: "system" | "user" | "assistant";
1422
+ role?: "user" | "assistant" | "system";
1145
1423
  } & {
1146
1424
  text?: never;
1147
1425
  files?: never;
@@ -1164,7 +1442,15 @@ declare function useAgentChat(options: UseAgentChatOptions): {
1164
1442
  runId: string | undefined;
1165
1443
  /** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
1166
1444
  activeRunId: string | null;
1167
- client: AgentClient;
1445
+ /**
1446
+ * The thread this chat is on right now: the `threadId` option, else the one the backend created
1447
+ * on the first send. A getter, because the created id is learned mid-stream.
1448
+ */
1449
+ getThreadId: () => string | undefined;
1450
+ /** The backend this chat talks to — pass it to `useThreads`, `useMessageFeedback`, … */
1451
+ backend: B;
1452
+ /** Same object as {@link backend}; kept for callers that read it under its old name. */
1453
+ client: B;
1168
1454
  threads: ThreadSummary[];
1169
1455
  loadThreads: () => Promise<ThreadSummary[]>;
1170
1456
  loadThread: (id: string) => Promise<ThreadDetail>;
@@ -1199,7 +1485,6 @@ declare function useAgentChat(options: UseAgentChatOptions): {
1199
1485
  setMessages: (messages: UIMessage<unknown, UIDataTypes, ai.UITools>[] | ((messages: UIMessage<unknown, UIDataTypes, ai.UITools>[]) => UIMessage<unknown, UIDataTypes, ai.UITools>[])) => void;
1200
1486
  error: Error | undefined;
1201
1487
  stop: () => Promise<void>;
1202
- status: ai.ChatStatus;
1203
1488
  messages: UIMessage<unknown, UIDataTypes, ai.UITools>[];
1204
1489
  resumeStream: (options?: ai.ChatRequestOptions) => Promise<void>;
1205
1490
  addToolOutput: ai.ChatAddToolOutputFunction<UIMessage<unknown, UIDataTypes, ai.UITools>>;
@@ -1207,4 +1492,4 @@ declare function useAgentChat(options: UseAgentChatOptions): {
1207
1492
  clearError: () => void;
1208
1493
  };
1209
1494
 
1210
- export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AggregatedTurnUsage, AnyToolUIPart, type ApprovalCountdown, ApproveOptions, type AutocompleteInputProps, type AutocompleteItem, type AutocompleteListboxProps, type AutocompleteOptionProps, type AutocompleteSource, type BackgroundRun, type CancelResult, type ChatBackground, ChatInput, type ChatInputProps, ChatStatus, type ChatTranscript, type CompletionEdit, type ComposerAutocomplete, DEFAULT_SPEECH_LANG, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageItemView, type MessageItemViewProps, MessageList, type MessageListClassNames, type MessageListProps, MessageUsageInfo, type QuotaToday, type RenderFilesFn, type RenderReasoningFn, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, type RenderUiFn, SPEECH_LANGUAGES, type SkillSuggestionData, type SkillsSourceOptions, type SpeechLanguage, type SpeechRecognitionHook, type StickToBottom, type StickToBottomOptions, type StoredTurnMetadata, type ThreadPatch, TimestampInfo, ToolCatalog, type ToolCatalogState, type TranscriptActionState, TranscriptBlock, type TranscriptCopyState, type TranscriptEditState, TranscriptFile, type TranscriptItem, type TranscriptItemOptions, type TranscriptStopState, TranscriptUiBlock, type TranscriptWindow, type TriggerMatch, type TriggerPosition, UsageSummary, type UseAgentChatOptions, type UseApprovalCountdownOptions, type UseChatTranscriptOptions, type UseComposerAutocompleteOptions, type UseElapsedOptions, type UseSpeechRecognitionOptions, type UseToolCatalogOptions, type UseTranscriptItemOptions, applyCompletion, approvalCountdown, backgroundRunsFromThread, createSkillsSource, filterAutocompleteItems, findActiveTrigger, formatElapsed, loadStoredSpeechLang, persistSpeechLang, readReasoningMs, storedMessageToUiMessage, storedThreadToUiMessages, useAgentChat, useApprovalCountdown, useChatTranscript, useComposerAutocomplete, useElapsed, useSpeechRecognition, useStickToBottom, useToolCatalog, useTranscriptItem };
1495
+ export { type AgentBackend, AgentBackendUnsupportedError, AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentMessageMetadata, type AgentStreamMeta, type AggregatedTurnUsage, AnyToolUIPart, type ApprovalCountdown, ApproveOptions, type AutocompleteInputProps, type AutocompleteItem, type AutocompleteListboxProps, type AutocompleteOptionProps, type AutocompleteSource, type BackgroundRun, type CancelResult, type ChatBackground, ChatInput, type ChatInputProps, ChatStatus, type ChatStreamRequest, type ChatStreamResponse, type ChatTranscript, type CompletionEdit, type ComposerAutocomplete, DEFAULT_SPEECH_LANG, type MessageFeedbackInput, type MessageFeedbackState, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageItemView, type MessageItemViewProps, MessageList, type MessageListClassNames, type MessageListProps, MessageUsageInfo, type QuotaToday, type ReconnectOptions, type RenderFilesFn, type RenderReasoningFn, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, type RenderUiFn, type ResumeStreamRequest, SPEECH_LANGUAGES, type SkillSuggestionData, type SkillsSourceOptions, type SpeechLanguage, type SpeechRecognitionHook, type StickToBottom, type StickToBottomOptions, type StoredTurnMetadata, type StreamConnectionState, type ThreadPatch, type ThreadsEvent, type ThreadsState, TimestampInfo, ToolCatalog, type ToolCatalogState, type TranscriptActionState, TranscriptBlock, type TranscriptCopyState, type TranscriptEditState, TranscriptFile, type TranscriptItem, type TranscriptItemOptions, type TranscriptStopState, TranscriptUiBlock, type TranscriptWindow, type TriggerMatch, type TriggerPosition, type UploadAttachmentOptions, UsageSummary, type UseAgentChatOptions, type UseApprovalCountdownOptions, type UseChatTranscriptOptions, type UseComposerAutocompleteOptions, type UseElapsedOptions, type UseMessageFeedbackOptions, type UseSpeechRecognitionOptions, type UseThreadsOptions, type UseToolCatalogOptions, type UseTranscriptItemOptions, applyCompletion, approvalCountdown, backgroundRunsFromThread, createSkillsSource, filterAutocompleteItems, findActiveTrigger, formatElapsed, loadStoredSpeechLang, notifyThreads, onThreadsEvent, persistSpeechLang, readReasoningMs, requireBackendMethod, storedMessageToUiMessage, storedThreadToUiMessages, useAgentChat, useApprovalCountdown, useChatTranscript, useComposerAutocomplete, useElapsed, useMessageFeedback, useSpeechRecognition, useStickToBottom, useThreads, useToolCatalog, useTranscriptItem };