@dudousxd/nestjs-agent-react 0.3.3 → 0.4.1
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.cjs +134 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +56 -3
- package/dist/index.d.ts +56 -3
- package/dist/index.js +140 -15
- package/dist/index.js.map +1 -1
- package/package.json +7 -2
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ai from 'ai';
|
|
2
2
|
import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart } from 'ai';
|
|
3
3
|
import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
|
|
4
|
-
import { ThreadSummary, ThreadDetail, QuotaView } from '@dudousxd/nestjs-agent-core';
|
|
4
|
+
import { ThreadSummary, ThreadDetail, MessageAttachment, QuotaView, StoredMessage } from '@dudousxd/nestjs-agent-core';
|
|
5
5
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
@@ -112,6 +112,15 @@ interface CancelResult {
|
|
|
112
112
|
interface OkResult {
|
|
113
113
|
ok: boolean;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Partial update accepted by `PATCH /agent/threads/:threadId`. `defaultAgent: null` clears a
|
|
117
|
+
* previously-set default back to the module's own default; omitting it leaves the thread's
|
|
118
|
+
* current default untouched.
|
|
119
|
+
*/
|
|
120
|
+
interface ThreadPatch {
|
|
121
|
+
title?: string;
|
|
122
|
+
defaultAgent?: string | null;
|
|
123
|
+
}
|
|
115
124
|
interface AgentClientOptions {
|
|
116
125
|
/** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
|
|
117
126
|
baseUrl?: string;
|
|
@@ -136,6 +145,14 @@ declare class AgentClient {
|
|
|
136
145
|
deleteThread(id: string): Promise<void>;
|
|
137
146
|
forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary>;
|
|
138
147
|
renameThread(id: string, title: string): Promise<OkResult>;
|
|
148
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
149
|
+
updateThread(id: string, patch: ThreadPatch): Promise<OkResult>;
|
|
150
|
+
/**
|
|
151
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
152
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
153
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
154
|
+
*/
|
|
155
|
+
uploadAttachment(file: File): Promise<MessageAttachment>;
|
|
139
156
|
promoteThread(id: string): Promise<OkResult>;
|
|
140
157
|
truncateFromMessage(threadId: string, messageId: string): Promise<OkResult>;
|
|
141
158
|
getQuotaToday(): Promise<QuotaToday>;
|
|
@@ -148,8 +165,31 @@ declare class AgentClient {
|
|
|
148
165
|
reason?: string;
|
|
149
166
|
}): Promise<void>;
|
|
150
167
|
private request;
|
|
168
|
+
private handleResponse;
|
|
151
169
|
}
|
|
152
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Convert a persisted `StoredMessage` (the agent-lib's on-disk shape) into an AI SDK v7
|
|
173
|
+
* `UIMessage` so a loaded thread can seed `useChat`'s `initialMessages` / `messages`.
|
|
174
|
+
*
|
|
175
|
+
* The lib stores a message as flat `content` text plus `toolCalls`/`toolResults` arrays; the SDK
|
|
176
|
+
* renders from `parts`. This maps:
|
|
177
|
+
* - `content` → a single `text` part (skipped when empty).
|
|
178
|
+
* - each `attachment` → a `file` part (image/PDF) so the user bubble re-renders its
|
|
179
|
+
* thumbnails on a reloaded thread.
|
|
180
|
+
* - each `toolCall` → a `tool-<name>` part, pairing its `toolResult` (matched by id):
|
|
181
|
+
* - a result found → `output-available` state, carrying `output`.
|
|
182
|
+
* - no result found → `input-available` state (the call never finished, e.g. the run was
|
|
183
|
+
* interrupted) — `output` is omitted, never a fabricated value.
|
|
184
|
+
* When the store reports the tool's kind (`'read' | 'action'`), it rides along as
|
|
185
|
+
* `toolMetadata.toolKind` so a UI can render an approval affordance for `action` tools
|
|
186
|
+
* without hardcoding tool-name sets, matching how live-streamed tool parts carry it.
|
|
187
|
+
*
|
|
188
|
+
* Live streaming still arrives as fully-typed SDK tool parts (see `AgentChatTransport`); this
|
|
189
|
+
* converter only feeds replayed history, so the tool cards render the same either way.
|
|
190
|
+
*/
|
|
191
|
+
declare function storedMessageToUiMessage(message: StoredMessage): UIMessage;
|
|
192
|
+
|
|
153
193
|
interface UseAgentChatOptions {
|
|
154
194
|
/** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
|
|
155
195
|
baseUrl?: string;
|
|
@@ -173,8 +213,19 @@ interface UseAgentChatOptions {
|
|
|
173
213
|
* Run id of a buffered stream to reconnect to on mount — wire this to the thread's
|
|
174
214
|
* `activeStreamId`. Its presence both gates the SDK's `resume` and names the run, so a normal
|
|
175
215
|
* mount (no id) never fires a doomed resume GET. Omitted → no resume.
|
|
216
|
+
*
|
|
217
|
+
* For the common case (no id in hand yet), prefer {@link UseAgentChatOptions.resume} — it does
|
|
218
|
+
* this fetch-and-wire for you.
|
|
176
219
|
*/
|
|
177
220
|
resumeRunId?: string;
|
|
221
|
+
/**
|
|
222
|
+
* When `true` and the hook (re)mounts with a `threadId`, the hook fetches the thread and, if its
|
|
223
|
+
* `activeRunId` is non-null (a turn was still streaming when the page loaded/reloaded), attaches
|
|
224
|
+
* to that run's `GET /agent/chat/:runId/stream` automatically — the in-flight turn streams into
|
|
225
|
+
* this session's message state exactly as if the send had happened here. A no-op when `threadId`
|
|
226
|
+
* is omitted (nothing to resume) or the fetched thread has no active run. Default `false`.
|
|
227
|
+
*/
|
|
228
|
+
resume?: boolean;
|
|
178
229
|
/** Read at every send to capture a page snapshot for the page-assistant. */
|
|
179
230
|
getPageContext?: () => Record<string, unknown> | null;
|
|
180
231
|
/** Fired after each streamed turn finishes (e.g. to refetch the sidebar). */
|
|
@@ -201,6 +252,8 @@ interface AddToolResultArgs {
|
|
|
201
252
|
declare function useAgentChat(options: UseAgentChatOptions): {
|
|
202
253
|
addToolResult: ({ tool, toolCallId, output }: AddToolResultArgs) => void | PromiseLike<void>;
|
|
203
254
|
runId: string | undefined;
|
|
255
|
+
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
256
|
+
activeRunId: string | null;
|
|
204
257
|
client: AgentClient;
|
|
205
258
|
threads: ThreadSummary[];
|
|
206
259
|
loadThreads: () => Promise<ThreadSummary[]>;
|
|
@@ -229,7 +282,7 @@ declare function useAgentChat(options: UseAgentChatOptions): {
|
|
|
229
282
|
status: ai.ChatStatus;
|
|
230
283
|
sendMessage: (message?: (Omit<UIMessage<unknown, ai.UIDataTypes, ai.UITools>, "id" | "role"> & {
|
|
231
284
|
id?: string;
|
|
232
|
-
role?: "
|
|
285
|
+
role?: "user" | "assistant" | "system";
|
|
233
286
|
} & {
|
|
234
287
|
text?: never;
|
|
235
288
|
files?: never;
|
|
@@ -437,4 +490,4 @@ interface SpeechRecognitionHook {
|
|
|
437
490
|
*/
|
|
438
491
|
declare function useSpeechRecognition({ lang, onFinalTranscript, onInterimTranscript, }: UseSpeechRecognitionOptions): SpeechRecognitionHook;
|
|
439
492
|
|
|
440
|
-
export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AnyToolUIPart, type CancelResult, ChatInput, type ChatInputProps, type ChatStatus, DEFAULT_SPEECH_LANG, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, SPEECH_LANGUAGES, type SpeechLanguage, type SpeechRecognitionHook, type UseAgentChatOptions, type UseSpeechRecognitionOptions, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, useAgentChat, useSpeechRecognition };
|
|
493
|
+
export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AnyToolUIPart, type CancelResult, ChatInput, type ChatInputProps, type ChatStatus, DEFAULT_SPEECH_LANG, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, SPEECH_LANGUAGES, type SpeechLanguage, type SpeechRecognitionHook, type ThreadPatch, type UseAgentChatOptions, type UseSpeechRecognitionOptions, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, storedMessageToUiMessage, useAgentChat, useSpeechRecognition };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as ai from 'ai';
|
|
2
2
|
import { ChatTransport, UIMessage, UIMessageChunk, ToolUIPart, DynamicToolUIPart } from 'ai';
|
|
3
3
|
import * as _dudousxd_nestjs_agent_core from '@dudousxd/nestjs-agent-core';
|
|
4
|
-
import { ThreadSummary, ThreadDetail, QuotaView } from '@dudousxd/nestjs-agent-core';
|
|
4
|
+
import { ThreadSummary, ThreadDetail, MessageAttachment, QuotaView, StoredMessage } from '@dudousxd/nestjs-agent-core';
|
|
5
5
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
6
|
import React from 'react';
|
|
7
7
|
|
|
@@ -112,6 +112,15 @@ interface CancelResult {
|
|
|
112
112
|
interface OkResult {
|
|
113
113
|
ok: boolean;
|
|
114
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Partial update accepted by `PATCH /agent/threads/:threadId`. `defaultAgent: null` clears a
|
|
117
|
+
* previously-set default back to the module's own default; omitting it leaves the thread's
|
|
118
|
+
* current default untouched.
|
|
119
|
+
*/
|
|
120
|
+
interface ThreadPatch {
|
|
121
|
+
title?: string;
|
|
122
|
+
defaultAgent?: string | null;
|
|
123
|
+
}
|
|
115
124
|
interface AgentClientOptions {
|
|
116
125
|
/** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
|
|
117
126
|
baseUrl?: string;
|
|
@@ -136,6 +145,14 @@ declare class AgentClient {
|
|
|
136
145
|
deleteThread(id: string): Promise<void>;
|
|
137
146
|
forkFromMessage(threadId: string, messageId: string): Promise<ThreadSummary>;
|
|
138
147
|
renameThread(id: string, title: string): Promise<OkResult>;
|
|
148
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
149
|
+
updateThread(id: string, patch: ThreadPatch): Promise<OkResult>;
|
|
150
|
+
/**
|
|
151
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
152
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
153
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
154
|
+
*/
|
|
155
|
+
uploadAttachment(file: File): Promise<MessageAttachment>;
|
|
139
156
|
promoteThread(id: string): Promise<OkResult>;
|
|
140
157
|
truncateFromMessage(threadId: string, messageId: string): Promise<OkResult>;
|
|
141
158
|
getQuotaToday(): Promise<QuotaToday>;
|
|
@@ -148,8 +165,31 @@ declare class AgentClient {
|
|
|
148
165
|
reason?: string;
|
|
149
166
|
}): Promise<void>;
|
|
150
167
|
private request;
|
|
168
|
+
private handleResponse;
|
|
151
169
|
}
|
|
152
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Convert a persisted `StoredMessage` (the agent-lib's on-disk shape) into an AI SDK v7
|
|
173
|
+
* `UIMessage` so a loaded thread can seed `useChat`'s `initialMessages` / `messages`.
|
|
174
|
+
*
|
|
175
|
+
* The lib stores a message as flat `content` text plus `toolCalls`/`toolResults` arrays; the SDK
|
|
176
|
+
* renders from `parts`. This maps:
|
|
177
|
+
* - `content` → a single `text` part (skipped when empty).
|
|
178
|
+
* - each `attachment` → a `file` part (image/PDF) so the user bubble re-renders its
|
|
179
|
+
* thumbnails on a reloaded thread.
|
|
180
|
+
* - each `toolCall` → a `tool-<name>` part, pairing its `toolResult` (matched by id):
|
|
181
|
+
* - a result found → `output-available` state, carrying `output`.
|
|
182
|
+
* - no result found → `input-available` state (the call never finished, e.g. the run was
|
|
183
|
+
* interrupted) — `output` is omitted, never a fabricated value.
|
|
184
|
+
* When the store reports the tool's kind (`'read' | 'action'`), it rides along as
|
|
185
|
+
* `toolMetadata.toolKind` so a UI can render an approval affordance for `action` tools
|
|
186
|
+
* without hardcoding tool-name sets, matching how live-streamed tool parts carry it.
|
|
187
|
+
*
|
|
188
|
+
* Live streaming still arrives as fully-typed SDK tool parts (see `AgentChatTransport`); this
|
|
189
|
+
* converter only feeds replayed history, so the tool cards render the same either way.
|
|
190
|
+
*/
|
|
191
|
+
declare function storedMessageToUiMessage(message: StoredMessage): UIMessage;
|
|
192
|
+
|
|
153
193
|
interface UseAgentChatOptions {
|
|
154
194
|
/** Origin + base path, e.g. `https://api.example.com`. Defaults to `''`. */
|
|
155
195
|
baseUrl?: string;
|
|
@@ -173,8 +213,19 @@ interface UseAgentChatOptions {
|
|
|
173
213
|
* Run id of a buffered stream to reconnect to on mount — wire this to the thread's
|
|
174
214
|
* `activeStreamId`. Its presence both gates the SDK's `resume` and names the run, so a normal
|
|
175
215
|
* mount (no id) never fires a doomed resume GET. Omitted → no resume.
|
|
216
|
+
*
|
|
217
|
+
* For the common case (no id in hand yet), prefer {@link UseAgentChatOptions.resume} — it does
|
|
218
|
+
* this fetch-and-wire for you.
|
|
176
219
|
*/
|
|
177
220
|
resumeRunId?: string;
|
|
221
|
+
/**
|
|
222
|
+
* When `true` and the hook (re)mounts with a `threadId`, the hook fetches the thread and, if its
|
|
223
|
+
* `activeRunId` is non-null (a turn was still streaming when the page loaded/reloaded), attaches
|
|
224
|
+
* to that run's `GET /agent/chat/:runId/stream` automatically — the in-flight turn streams into
|
|
225
|
+
* this session's message state exactly as if the send had happened here. A no-op when `threadId`
|
|
226
|
+
* is omitted (nothing to resume) or the fetched thread has no active run. Default `false`.
|
|
227
|
+
*/
|
|
228
|
+
resume?: boolean;
|
|
178
229
|
/** Read at every send to capture a page snapshot for the page-assistant. */
|
|
179
230
|
getPageContext?: () => Record<string, unknown> | null;
|
|
180
231
|
/** Fired after each streamed turn finishes (e.g. to refetch the sidebar). */
|
|
@@ -201,6 +252,8 @@ interface AddToolResultArgs {
|
|
|
201
252
|
declare function useAgentChat(options: UseAgentChatOptions): {
|
|
202
253
|
addToolResult: ({ tool, toolCallId, output }: AddToolResultArgs) => void | PromiseLike<void>;
|
|
203
254
|
runId: string | undefined;
|
|
255
|
+
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
256
|
+
activeRunId: string | null;
|
|
204
257
|
client: AgentClient;
|
|
205
258
|
threads: ThreadSummary[];
|
|
206
259
|
loadThreads: () => Promise<ThreadSummary[]>;
|
|
@@ -229,7 +282,7 @@ declare function useAgentChat(options: UseAgentChatOptions): {
|
|
|
229
282
|
status: ai.ChatStatus;
|
|
230
283
|
sendMessage: (message?: (Omit<UIMessage<unknown, ai.UIDataTypes, ai.UITools>, "id" | "role"> & {
|
|
231
284
|
id?: string;
|
|
232
|
-
role?: "
|
|
285
|
+
role?: "user" | "assistant" | "system";
|
|
233
286
|
} & {
|
|
234
287
|
text?: never;
|
|
235
288
|
files?: never;
|
|
@@ -437,4 +490,4 @@ interface SpeechRecognitionHook {
|
|
|
437
490
|
*/
|
|
438
491
|
declare function useSpeechRecognition({ lang, onFinalTranscript, onInterimTranscript, }: UseSpeechRecognitionOptions): SpeechRecognitionHook;
|
|
439
492
|
|
|
440
|
-
export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AnyToolUIPart, type CancelResult, ChatInput, type ChatInputProps, type ChatStatus, DEFAULT_SPEECH_LANG, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, SPEECH_LANGUAGES, type SpeechLanguage, type SpeechRecognitionHook, type UseAgentChatOptions, type UseSpeechRecognitionOptions, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, useAgentChat, useSpeechRecognition };
|
|
493
|
+
export { AgentChatTransport, type AgentChatTransportOptions, AgentClient, type AgentClientOptions, AgentHttpError, type AgentStreamMeta, type AnyToolUIPart, type CancelResult, ChatInput, type ChatInputProps, type ChatStatus, DEFAULT_SPEECH_LANG, MessageItem, type MessageItemClassNames, type MessageItemProps, MessageList, type MessageListClassNames, type MessageListProps, type MessageUsageInfo, type QuotaToday, type RenderTextFn, type RenderToolGroupFn, type RenderToolPartFn, SPEECH_LANGUAGES, type SpeechLanguage, type SpeechRecognitionHook, type ThreadPatch, type UseAgentChatOptions, type UseSpeechRecognitionOptions, formatRelativeTime, loadStoredSpeechLang, persistSpeechLang, storedMessageToUiMessage, useAgentChat, useSpeechRecognition };
|
package/dist/index.js
CHANGED
|
@@ -177,6 +177,14 @@ var AgentChatTransport = class {
|
|
|
177
177
|
break;
|
|
178
178
|
case "step-finish":
|
|
179
179
|
closeStep();
|
|
180
|
+
if (event.costUsd !== void 0) {
|
|
181
|
+
controller.enqueue({
|
|
182
|
+
type: "message-metadata",
|
|
183
|
+
messageMetadata: {
|
|
184
|
+
costUsd: event.costUsd
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
180
188
|
break;
|
|
181
189
|
case "text":
|
|
182
190
|
ensureStep();
|
|
@@ -213,7 +221,15 @@ var AgentChatTransport = class {
|
|
|
213
221
|
controller.enqueue({
|
|
214
222
|
type: "tool-input-start",
|
|
215
223
|
toolCallId: event.id,
|
|
216
|
-
toolName: event.name
|
|
224
|
+
toolName: event.name,
|
|
225
|
+
// `toolKind` is absent on older backends — omitted (not `undefined`-valued) so
|
|
226
|
+
// `toolMetadata` itself is only present when there's something to say, letting a
|
|
227
|
+
// UI gate approval affordances on `kind === 'action'` without hardcoding tool names.
|
|
228
|
+
...event.toolKind !== void 0 ? {
|
|
229
|
+
toolMetadata: {
|
|
230
|
+
toolKind: event.toolKind
|
|
231
|
+
}
|
|
232
|
+
} : {}
|
|
217
233
|
});
|
|
218
234
|
break;
|
|
219
235
|
case "tool-input-delta":
|
|
@@ -230,7 +246,12 @@ var AgentChatTransport = class {
|
|
|
230
246
|
type: "tool-input-available",
|
|
231
247
|
toolCallId: event.id,
|
|
232
248
|
toolName: event.name,
|
|
233
|
-
input: event.input
|
|
249
|
+
input: event.input,
|
|
250
|
+
...event.toolKind !== void 0 ? {
|
|
251
|
+
toolMetadata: {
|
|
252
|
+
toolKind: event.toolKind
|
|
253
|
+
}
|
|
254
|
+
} : {}
|
|
234
255
|
});
|
|
235
256
|
break;
|
|
236
257
|
case "tool-output":
|
|
@@ -412,10 +433,39 @@ var AgentClient = class {
|
|
|
412
433
|
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
413
434
|
}
|
|
414
435
|
renameThread(id, title) {
|
|
415
|
-
return this.
|
|
436
|
+
return this.updateThread(id, {
|
|
416
437
|
title
|
|
417
438
|
});
|
|
418
439
|
}
|
|
440
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
441
|
+
updateThread(id, patch) {
|
|
442
|
+
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
446
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
447
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
448
|
+
*/
|
|
449
|
+
async uploadAttachment(file) {
|
|
450
|
+
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
451
|
+
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
452
|
+
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
453
|
+
const formData = new FormData();
|
|
454
|
+
formData.append("file", file);
|
|
455
|
+
const response = await fetchImpl(`${baseUrl}/agent/attachments`, {
|
|
456
|
+
method: "POST",
|
|
457
|
+
headers: {
|
|
458
|
+
accept: "application/json",
|
|
459
|
+
...this.options.headers,
|
|
460
|
+
...dynamic
|
|
461
|
+
},
|
|
462
|
+
body: formData,
|
|
463
|
+
...this.options.credentials !== void 0 ? {
|
|
464
|
+
credentials: this.options.credentials
|
|
465
|
+
} : {}
|
|
466
|
+
});
|
|
467
|
+
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
468
|
+
}
|
|
419
469
|
promoteThread(id) {
|
|
420
470
|
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
421
471
|
}
|
|
@@ -455,6 +505,9 @@ var AgentClient = class {
|
|
|
455
505
|
credentials: this.options.credentials
|
|
456
506
|
} : {}
|
|
457
507
|
});
|
|
508
|
+
return this.handleResponse(response, method, path);
|
|
509
|
+
}
|
|
510
|
+
async handleResponse(response, method, path) {
|
|
458
511
|
if (!response.ok) {
|
|
459
512
|
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
460
513
|
}
|
|
@@ -465,15 +518,64 @@ var AgentClient = class {
|
|
|
465
518
|
}
|
|
466
519
|
};
|
|
467
520
|
|
|
521
|
+
// src/stored-message-to-ui-message.ts
|
|
522
|
+
function storedMessageToUiMessage(message) {
|
|
523
|
+
const parts = [];
|
|
524
|
+
if (message.content) {
|
|
525
|
+
parts.push({
|
|
526
|
+
type: "text",
|
|
527
|
+
text: message.content
|
|
528
|
+
});
|
|
529
|
+
}
|
|
530
|
+
for (const attachment of message.attachments ?? []) {
|
|
531
|
+
parts.push({
|
|
532
|
+
type: "file",
|
|
533
|
+
mediaType: attachment.contentType,
|
|
534
|
+
filename: attachment.name,
|
|
535
|
+
url: attachment.url
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
for (const call of message.toolCalls ?? []) {
|
|
539
|
+
const result = message.toolResults?.find((candidate) => candidate.id === call.id);
|
|
540
|
+
const toolKind = call.kind;
|
|
541
|
+
parts.push({
|
|
542
|
+
type: `tool-${call.name}`,
|
|
543
|
+
toolCallId: call.id,
|
|
544
|
+
...toolKind !== void 0 ? {
|
|
545
|
+
toolMetadata: {
|
|
546
|
+
toolKind
|
|
547
|
+
}
|
|
548
|
+
} : {},
|
|
549
|
+
...result !== void 0 ? {
|
|
550
|
+
state: "output-available",
|
|
551
|
+
input: call.input,
|
|
552
|
+
output: result.output
|
|
553
|
+
} : {
|
|
554
|
+
state: "input-available",
|
|
555
|
+
input: call.input
|
|
556
|
+
}
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
id: message.id,
|
|
561
|
+
role: message.role,
|
|
562
|
+
parts
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
566
|
+
|
|
468
567
|
// src/use-agent-chat.ts
|
|
469
568
|
import { useChat } from "@ai-sdk/react";
|
|
470
|
-
import { useCallback, useMemo, useRef, useState } from "react";
|
|
569
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
471
570
|
function useAgentChat(options) {
|
|
472
571
|
const latest = useRef(options);
|
|
473
572
|
latest.current = options;
|
|
474
573
|
const [runId, setRunId] = useState(options.resumeRunId);
|
|
475
574
|
const runIdRef = useRef(runId);
|
|
476
575
|
runIdRef.current = runId;
|
|
576
|
+
const autoResumeRunIdRef = useRef(void 0);
|
|
577
|
+
const [autoResume, setAutoResume] = useState(false);
|
|
578
|
+
const [activeRunId, setActiveRunId] = useState(null);
|
|
477
579
|
const regenerateNext = useRef(false);
|
|
478
580
|
const createdThreadId = useRef(void 0);
|
|
479
581
|
const client = useMemo(() => {
|
|
@@ -491,6 +593,26 @@ function useAgentChat(options) {
|
|
|
491
593
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
492
594
|
});
|
|
493
595
|
}, []);
|
|
596
|
+
useEffect(() => {
|
|
597
|
+
if (!options.resume || options.threadId === void 0) return;
|
|
598
|
+
let cancelled = false;
|
|
599
|
+
const threadId = options.threadId;
|
|
600
|
+
void client.getThread(threadId).then((thread) => {
|
|
601
|
+
if (cancelled) return;
|
|
602
|
+
setActiveRunId(thread.activeRunId ?? null);
|
|
603
|
+
if (thread.activeRunId != null) {
|
|
604
|
+
autoResumeRunIdRef.current = thread.activeRunId;
|
|
605
|
+
setAutoResume(true);
|
|
606
|
+
}
|
|
607
|
+
});
|
|
608
|
+
return () => {
|
|
609
|
+
cancelled = true;
|
|
610
|
+
};
|
|
611
|
+
}, [
|
|
612
|
+
client,
|
|
613
|
+
options.resume,
|
|
614
|
+
options.threadId
|
|
615
|
+
]);
|
|
494
616
|
const transport = useMemo(() => {
|
|
495
617
|
function onMeta(meta) {
|
|
496
618
|
setRunId(meta.runId);
|
|
@@ -532,13 +654,13 @@ function useAgentChat(options) {
|
|
|
532
654
|
} : {}
|
|
533
655
|
};
|
|
534
656
|
}, "getBody"),
|
|
535
|
-
getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId, "getResumeRunId"),
|
|
657
|
+
getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId ?? autoResumeRunIdRef.current, "getResumeRunId"),
|
|
536
658
|
onMeta
|
|
537
659
|
});
|
|
538
660
|
}, []);
|
|
539
661
|
const chat = useChat({
|
|
540
662
|
transport,
|
|
541
|
-
resume: options.resumeRunId !== void 0,
|
|
663
|
+
resume: options.resumeRunId !== void 0 || autoResume,
|
|
542
664
|
...options.threadId !== void 0 ? {
|
|
543
665
|
id: options.threadId
|
|
544
666
|
} : {},
|
|
@@ -605,10 +727,10 @@ function useAgentChat(options) {
|
|
|
605
727
|
]);
|
|
606
728
|
const cancel = useCallback(async () => {
|
|
607
729
|
chatRef.current.stop();
|
|
608
|
-
const
|
|
609
|
-
if (
|
|
730
|
+
const activeRunId2 = runIdRef.current;
|
|
731
|
+
if (activeRunId2) {
|
|
610
732
|
try {
|
|
611
|
-
await client.cancelStream(
|
|
733
|
+
await client.cancelStream(activeRunId2);
|
|
612
734
|
} catch {
|
|
613
735
|
}
|
|
614
736
|
}
|
|
@@ -640,6 +762,8 @@ function useAgentChat(options) {
|
|
|
640
762
|
...chat,
|
|
641
763
|
addToolResult,
|
|
642
764
|
runId,
|
|
765
|
+
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
766
|
+
activeRunId,
|
|
643
767
|
client,
|
|
644
768
|
threads,
|
|
645
769
|
loadThreads,
|
|
@@ -668,10 +792,10 @@ async function mergeHeaders(options) {
|
|
|
668
792
|
__name(mergeHeaders, "mergeHeaders");
|
|
669
793
|
|
|
670
794
|
// src/components/chat-input.tsx
|
|
671
|
-
import React, { useEffect as
|
|
795
|
+
import React, { useEffect as useEffect3, useState as useState3 } from "react";
|
|
672
796
|
|
|
673
797
|
// src/components/use-speech-recognition.ts
|
|
674
|
-
import { useCallback as useCallback2, useEffect, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
798
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
675
799
|
function getRecognitionCtor() {
|
|
676
800
|
if (typeof window === "undefined") return null;
|
|
677
801
|
const scope = window;
|
|
@@ -687,7 +811,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
687
811
|
const interimRef = useRef2(onInterimTranscript);
|
|
688
812
|
finalRef.current = onFinalTranscript;
|
|
689
813
|
interimRef.current = onInterimTranscript;
|
|
690
|
-
|
|
814
|
+
useEffect2(() => {
|
|
691
815
|
if (!ctor) return;
|
|
692
816
|
const recognition = new ctor();
|
|
693
817
|
recognition.lang = lang;
|
|
@@ -767,7 +891,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
767
891
|
}, "onFinalTranscript"),
|
|
768
892
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
769
893
|
});
|
|
770
|
-
|
|
894
|
+
useEffect3(() => {
|
|
771
895
|
if (!speech.isListening) setInterim("");
|
|
772
896
|
}, [
|
|
773
897
|
speech.isListening
|
|
@@ -826,7 +950,7 @@ __name(ChatInput, "ChatInput");
|
|
|
826
950
|
|
|
827
951
|
// src/components/message-item.tsx
|
|
828
952
|
import { isTextUIPart, isToolUIPart } from "ai";
|
|
829
|
-
import React2, { useEffect as
|
|
953
|
+
import React2, { useEffect as useEffect4, useRef as useRef3, useState as useState4 } from "react";
|
|
830
954
|
function MessageItem({ message, renderToolPart, renderToolGroup, renderText, classNames, isStreaming = false, editable, onEditSubmit, regeneratable, onRegenerate, onFork, usage, createdAt }) {
|
|
831
955
|
const isUser = message.role === "user";
|
|
832
956
|
const parts = message.parts ?? [];
|
|
@@ -930,7 +1054,7 @@ function EditableUserBubble({ message, onSubmit, renderText, classNames, actions
|
|
|
930
1054
|
const [isEditing, setIsEditing] = useState4(false);
|
|
931
1055
|
const [draft, setDraft] = useState4(initialText);
|
|
932
1056
|
const textareaRef = useRef3(null);
|
|
933
|
-
|
|
1057
|
+
useEffect4(() => {
|
|
934
1058
|
if (isEditing && textareaRef.current) {
|
|
935
1059
|
const el = textareaRef.current;
|
|
936
1060
|
el.focus();
|
|
@@ -1248,6 +1372,7 @@ export {
|
|
|
1248
1372
|
formatRelativeTime,
|
|
1249
1373
|
loadStoredSpeechLang,
|
|
1250
1374
|
persistSpeechLang,
|
|
1375
|
+
storedMessageToUiMessage,
|
|
1251
1376
|
useAgentChat,
|
|
1252
1377
|
useSpeechRecognition
|
|
1253
1378
|
};
|