@dudousxd/nestjs-agent-react 0.14.0 → 0.16.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/README.md +119 -1
- package/dist/genui.d.cts +1 -1
- package/dist/genui.d.ts +1 -1
- package/dist/index.cjs +1101 -432
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +388 -31
- package/dist/index.d.ts +388 -31
- package/dist/index.js +1055 -394
- package/dist/index.js.map +1 -1
- package/dist/{model-CMNhyau6.d.cts → model-gHqjNjLJ.d.cts} +5 -1
- package/dist/{model-CMNhyau6.d.ts → model-gHqjNjLJ.d.ts} +5 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
var src_exports = {};
|
|
33
33
|
__export(src_exports, {
|
|
34
|
+
AgentBackendUnsupportedError: () => AgentBackendUnsupportedError,
|
|
34
35
|
AgentChatTransport: () => AgentChatTransport,
|
|
35
36
|
AgentClient: () => AgentClient,
|
|
36
37
|
AgentHttpError: () => AgentHttpError,
|
|
@@ -60,22 +61,29 @@ __export(src_exports, {
|
|
|
60
61
|
inferResultView: () => inferResultView,
|
|
61
62
|
isActionCall: () => isActionCall,
|
|
62
63
|
loadStoredSpeechLang: () => loadStoredSpeechLang,
|
|
64
|
+
notifyThreads: () => notifyThreads,
|
|
65
|
+
onThreadsEvent: () => onThreadsEvent,
|
|
63
66
|
persistSpeechLang: () => persistSpeechLang,
|
|
64
67
|
phraseFor: () => phraseFor,
|
|
65
68
|
readPath: () => readPath,
|
|
66
69
|
readReasoningMs: () => readReasoningMs,
|
|
70
|
+
requireBackendMethod: () => requireBackendMethod,
|
|
67
71
|
resolveResultView: () => resolveResultView,
|
|
68
72
|
storedMessageToUiMessage: () => storedMessageToUiMessage,
|
|
69
73
|
storedThreadToUiMessages: () => storedThreadToUiMessages,
|
|
70
74
|
toolCallState: () => toolCallState,
|
|
71
75
|
toolCatalogFrom: () => toolCatalogFrom,
|
|
72
76
|
useAgentChat: () => useAgentChat,
|
|
77
|
+
useAgents: () => useAgents,
|
|
73
78
|
useApprovalCountdown: () => useApprovalCountdown,
|
|
74
79
|
useChatTranscript: () => useChatTranscript,
|
|
75
80
|
useComposerAutocomplete: () => useComposerAutocomplete,
|
|
76
81
|
useElapsed: () => useElapsed,
|
|
82
|
+
useMessageFeedback: () => useMessageFeedback,
|
|
83
|
+
useModels: () => useModels,
|
|
77
84
|
useSpeechRecognition: () => useSpeechRecognition,
|
|
78
85
|
useStickToBottom: () => useStickToBottom,
|
|
86
|
+
useThreads: () => useThreads,
|
|
79
87
|
useToolCatalog: () => useToolCatalog,
|
|
80
88
|
useTranscriptItem: () => useTranscriptItem,
|
|
81
89
|
validateAnswer: () => import_nestjs_agent_core.validateElicitationAnswer,
|
|
@@ -83,6 +91,255 @@ __export(src_exports, {
|
|
|
83
91
|
});
|
|
84
92
|
module.exports = __toCommonJS(src_exports);
|
|
85
93
|
|
|
94
|
+
// src/client.ts
|
|
95
|
+
var AgentHttpError = class extends Error {
|
|
96
|
+
static {
|
|
97
|
+
__name(this, "AgentHttpError");
|
|
98
|
+
}
|
|
99
|
+
status;
|
|
100
|
+
method;
|
|
101
|
+
path;
|
|
102
|
+
constructor(status, method, path, statusText) {
|
|
103
|
+
super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
|
|
104
|
+
this.name = "AgentHttpError";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var HEADER_RUN_ID = "x-agent-run-id";
|
|
108
|
+
var HEADER_THREAD_ID = "x-agent-thread-id";
|
|
109
|
+
var AgentClient = class {
|
|
110
|
+
static {
|
|
111
|
+
__name(this, "AgentClient");
|
|
112
|
+
}
|
|
113
|
+
options;
|
|
114
|
+
constructor(options = {}) {
|
|
115
|
+
this.options = options;
|
|
116
|
+
}
|
|
117
|
+
/** `POST /agent/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
|
|
118
|
+
async openChatStream(request) {
|
|
119
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: {
|
|
122
|
+
"content-type": "application/json",
|
|
123
|
+
accept: "text/event-stream",
|
|
124
|
+
...await this.resolveHeaders(),
|
|
125
|
+
...request.headers
|
|
126
|
+
},
|
|
127
|
+
body: JSON.stringify(request.body),
|
|
128
|
+
...this.credentials(),
|
|
129
|
+
...request.signal !== void 0 ? {
|
|
130
|
+
signal: request.signal
|
|
131
|
+
} : {}
|
|
132
|
+
});
|
|
133
|
+
if (!response.ok || !response.body) {
|
|
134
|
+
throw new AgentHttpError(response.status, "POST", "/agent/chat", response.statusText);
|
|
135
|
+
}
|
|
136
|
+
return streamResponse(response);
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* `GET /agent/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
|
|
140
|
+
* streaming under that id (404).
|
|
141
|
+
*/
|
|
142
|
+
async resumeChatStream(request) {
|
|
143
|
+
const path = `/agent/chat/${encodeURIComponent(request.runId)}/stream`;
|
|
144
|
+
const query = request.after !== void 0 && request.after > 0 ? `?after=${request.after}` : "";
|
|
145
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}${query}`, {
|
|
146
|
+
method: "GET",
|
|
147
|
+
headers: {
|
|
148
|
+
accept: "text/event-stream",
|
|
149
|
+
...await this.resolveHeaders(),
|
|
150
|
+
...request.headers
|
|
151
|
+
},
|
|
152
|
+
...this.credentials(),
|
|
153
|
+
...request.signal !== void 0 ? {
|
|
154
|
+
signal: request.signal
|
|
155
|
+
} : {}
|
|
156
|
+
});
|
|
157
|
+
if (response.status === 404) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
if (!response.ok || !response.body) {
|
|
161
|
+
throw new AgentHttpError(response.status, "GET", path, response.statusText);
|
|
162
|
+
}
|
|
163
|
+
return streamResponse(response);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Rate a message (`'up'`/`'down'`, optional comment) or clear its rating (`value: null`). Answers
|
|
167
|
+
* the stored rating.
|
|
168
|
+
*/
|
|
169
|
+
setMessageFeedback(messageId, input) {
|
|
170
|
+
return this.request("POST", `/agent/messages/${encodeURIComponent(messageId)}/feedback`, input);
|
|
171
|
+
}
|
|
172
|
+
listThreads() {
|
|
173
|
+
return this.request("GET", "/agent/threads");
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
177
|
+
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
178
|
+
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
179
|
+
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
180
|
+
*/
|
|
181
|
+
listSkills(threadId) {
|
|
182
|
+
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
183
|
+
return this.request("GET", `/agent/skills${query}`);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
|
|
187
|
+
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
|
|
188
|
+
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
|
|
189
|
+
*/
|
|
190
|
+
listTools(agent) {
|
|
191
|
+
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
192
|
+
return this.request("GET", `/agent/tools${query}`);
|
|
193
|
+
}
|
|
194
|
+
getThread(id) {
|
|
195
|
+
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
196
|
+
}
|
|
197
|
+
deleteThread(id) {
|
|
198
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
|
|
199
|
+
}
|
|
200
|
+
forkFromMessage(threadId, messageId) {
|
|
201
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
202
|
+
}
|
|
203
|
+
renameThread(id, title) {
|
|
204
|
+
return this.updateThread(id, {
|
|
205
|
+
title
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
209
|
+
updateThread(id, patch) {
|
|
210
|
+
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
214
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
215
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
216
|
+
*/
|
|
217
|
+
async uploadAttachment(file, options = {}) {
|
|
218
|
+
const formData = new FormData();
|
|
219
|
+
formData.append("file", file);
|
|
220
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
|
|
221
|
+
method: "POST",
|
|
222
|
+
headers: {
|
|
223
|
+
accept: "application/json",
|
|
224
|
+
...await this.resolveHeaders()
|
|
225
|
+
},
|
|
226
|
+
body: formData,
|
|
227
|
+
...this.credentials(),
|
|
228
|
+
...options.signal !== void 0 ? {
|
|
229
|
+
signal: options.signal
|
|
230
|
+
} : {}
|
|
231
|
+
});
|
|
232
|
+
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
233
|
+
}
|
|
234
|
+
promoteThread(id) {
|
|
235
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
236
|
+
}
|
|
237
|
+
truncateFromMessage(threadId, messageId) {
|
|
238
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
|
|
239
|
+
}
|
|
240
|
+
/** `GET /agent/models?agent=` — the models this caller may pick, grouped by provider. */
|
|
241
|
+
listModels(agent) {
|
|
242
|
+
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
243
|
+
return this.request("GET", `/agent/models${query}`);
|
|
244
|
+
}
|
|
245
|
+
/** `GET /agent/agents` — the registered agents, the default one flagged. */
|
|
246
|
+
listAgents() {
|
|
247
|
+
return this.request("GET", "/agent/agents");
|
|
248
|
+
}
|
|
249
|
+
getQuotaToday() {
|
|
250
|
+
return this.request("GET", "/agent/quota/today");
|
|
251
|
+
}
|
|
252
|
+
cancelStream(runId) {
|
|
253
|
+
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
257
|
+
* the decision came through (the server records `'web'` when omitted).
|
|
258
|
+
*/
|
|
259
|
+
approveToolCall(input) {
|
|
260
|
+
return this.request("POST", "/agent/tool-call/approve", input);
|
|
261
|
+
}
|
|
262
|
+
rejectToolCall(input) {
|
|
263
|
+
return this.request("POST", "/agent/tool-call/reject", input);
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
267
|
+
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
268
|
+
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
269
|
+
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
270
|
+
*/
|
|
271
|
+
answerToolCall(input) {
|
|
272
|
+
return this.request("POST", "/agent/tool-call/answer", input);
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
276
|
+
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
277
|
+
* evidence the user chose them.
|
|
278
|
+
*/
|
|
279
|
+
skipToolCall(input) {
|
|
280
|
+
return this.request("POST", "/agent/tool-call/skip", input);
|
|
281
|
+
}
|
|
282
|
+
fetchImpl() {
|
|
283
|
+
return this.options.fetch ?? globalThis.fetch;
|
|
284
|
+
}
|
|
285
|
+
baseUrl() {
|
|
286
|
+
return (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
287
|
+
}
|
|
288
|
+
async resolveHeaders() {
|
|
289
|
+
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
290
|
+
return {
|
|
291
|
+
...this.options.headers,
|
|
292
|
+
...dynamic
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
credentials() {
|
|
296
|
+
return this.options.credentials !== void 0 ? {
|
|
297
|
+
credentials: this.options.credentials
|
|
298
|
+
} : {};
|
|
299
|
+
}
|
|
300
|
+
async request(method, path, body) {
|
|
301
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}`, {
|
|
302
|
+
method,
|
|
303
|
+
headers: {
|
|
304
|
+
accept: "application/json",
|
|
305
|
+
...body !== void 0 ? {
|
|
306
|
+
"content-type": "application/json"
|
|
307
|
+
} : {},
|
|
308
|
+
...await this.resolveHeaders()
|
|
309
|
+
},
|
|
310
|
+
...body !== void 0 ? {
|
|
311
|
+
body: JSON.stringify(body)
|
|
312
|
+
} : {},
|
|
313
|
+
...this.credentials()
|
|
314
|
+
});
|
|
315
|
+
return this.handleResponse(response, method, path);
|
|
316
|
+
}
|
|
317
|
+
async handleResponse(response, method, path) {
|
|
318
|
+
if (!response.ok) {
|
|
319
|
+
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
320
|
+
}
|
|
321
|
+
if (response.status === 204) return void 0;
|
|
322
|
+
const text = await response.text();
|
|
323
|
+
if (!text) return void 0;
|
|
324
|
+
return JSON.parse(text);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
function streamResponse(response) {
|
|
328
|
+
const body = response.body;
|
|
329
|
+
const runId = response.headers?.get(HEADER_RUN_ID) ?? void 0;
|
|
330
|
+
const threadId = response.headers?.get(HEADER_THREAD_ID) ?? void 0;
|
|
331
|
+
return {
|
|
332
|
+
body,
|
|
333
|
+
...runId ? {
|
|
334
|
+
runId
|
|
335
|
+
} : {},
|
|
336
|
+
...threadId ? {
|
|
337
|
+
threadId
|
|
338
|
+
} : {}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
__name(streamResponse, "streamResponse");
|
|
342
|
+
|
|
86
343
|
// src/reasoning/timing.ts
|
|
87
344
|
var import_react = require("react");
|
|
88
345
|
var REASONING_METADATA_KEY = "agent";
|
|
@@ -143,8 +400,11 @@ function formatElapsed(ms) {
|
|
|
143
400
|
__name(formatElapsed, "formatElapsed");
|
|
144
401
|
|
|
145
402
|
// src/agent-chat-transport.ts
|
|
146
|
-
var
|
|
147
|
-
|
|
403
|
+
var DEFAULT_RECONNECT = {
|
|
404
|
+
maxAttempts: 6,
|
|
405
|
+
baseDelayMs: 500,
|
|
406
|
+
maxDelayMs: 8e3
|
|
407
|
+
};
|
|
148
408
|
var ASK_TOOL_NAME = "ask";
|
|
149
409
|
var AgentChatTransport = class {
|
|
150
410
|
static {
|
|
@@ -161,8 +421,26 @@ var AgentChatTransport = class {
|
|
|
161
421
|
* several entries under each id — which React renders as duplicate keys.
|
|
162
422
|
*/
|
|
163
423
|
attemptLive = false;
|
|
424
|
+
backend;
|
|
164
425
|
constructor(options = {}) {
|
|
165
426
|
this.options = options;
|
|
427
|
+
this.backend = options.backend ?? new AgentClient({
|
|
428
|
+
...options.baseUrl !== void 0 ? {
|
|
429
|
+
baseUrl: options.baseUrl
|
|
430
|
+
} : {},
|
|
431
|
+
...options.headers !== void 0 ? {
|
|
432
|
+
headers: options.headers
|
|
433
|
+
} : {},
|
|
434
|
+
...options.getHeaders !== void 0 ? {
|
|
435
|
+
getHeaders: options.getHeaders
|
|
436
|
+
} : {},
|
|
437
|
+
...options.credentials !== void 0 ? {
|
|
438
|
+
credentials: options.credentials
|
|
439
|
+
} : {},
|
|
440
|
+
...options.fetch !== void 0 ? {
|
|
441
|
+
fetch: options.fetch
|
|
442
|
+
} : {}
|
|
443
|
+
});
|
|
166
444
|
}
|
|
167
445
|
/** Run id of the most recent stream — HITL approve/reject target this. */
|
|
168
446
|
get runId() {
|
|
@@ -195,26 +473,25 @@ var AgentChatTransport = class {
|
|
|
195
473
|
...options.body ?? {},
|
|
196
474
|
message
|
|
197
475
|
};
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
476
|
+
const headers = headerRecord(options.headers);
|
|
477
|
+
const response = await this.backend.openChatStream({
|
|
478
|
+
body,
|
|
479
|
+
...headers !== void 0 ? {
|
|
480
|
+
headers
|
|
481
|
+
} : {},
|
|
482
|
+
...options.abortSignal ? {
|
|
483
|
+
signal: options.abortSignal
|
|
484
|
+
} : {}
|
|
485
|
+
});
|
|
486
|
+
this.captureHeaderMeta(response);
|
|
487
|
+
return this.toChunkStream(response, {
|
|
488
|
+
...headers !== void 0 ? {
|
|
489
|
+
headers
|
|
208
490
|
} : {},
|
|
209
491
|
...options.abortSignal ? {
|
|
210
492
|
signal: options.abortSignal
|
|
211
493
|
} : {}
|
|
212
494
|
});
|
|
213
|
-
if (!response.ok || !response.body) {
|
|
214
|
-
throw new Error(`Agent chat request failed: ${response.status} ${response.statusText}`);
|
|
215
|
-
}
|
|
216
|
-
this.captureHeaderMeta(response.headers);
|
|
217
|
-
return this.toChunkStream(response.body);
|
|
218
495
|
} catch (error) {
|
|
219
496
|
this.attemptLive = false;
|
|
220
497
|
throw error;
|
|
@@ -227,51 +504,41 @@ var AgentChatTransport = class {
|
|
|
227
504
|
if (runId === void 0) return null;
|
|
228
505
|
this.attemptLive = true;
|
|
229
506
|
try {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
},
|
|
236
|
-
...this.options.credentials !== void 0 ? {
|
|
237
|
-
credentials: this.options.credentials
|
|
507
|
+
const headers = headerRecord(options.headers);
|
|
508
|
+
const response = await this.backend.resumeChatStream({
|
|
509
|
+
runId,
|
|
510
|
+
...headers !== void 0 ? {
|
|
511
|
+
headers
|
|
238
512
|
} : {}
|
|
239
513
|
});
|
|
240
|
-
if (response
|
|
514
|
+
if (response === null) {
|
|
241
515
|
this.attemptLive = false;
|
|
242
516
|
return null;
|
|
243
517
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
518
|
+
this.captureHeaderMeta(response);
|
|
519
|
+
return this.toChunkStream(response, {
|
|
520
|
+
runId,
|
|
521
|
+
...headers !== void 0 ? {
|
|
522
|
+
headers
|
|
523
|
+
} : {}
|
|
524
|
+
});
|
|
249
525
|
} catch (error) {
|
|
250
526
|
this.attemptLive = false;
|
|
251
527
|
throw error;
|
|
252
528
|
}
|
|
253
529
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
baseUrl() {
|
|
258
|
-
return (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
530
|
+
captureHeaderMeta(response) {
|
|
531
|
+
if (response.runId) this.currentRunId = response.runId;
|
|
532
|
+
if (response.threadId) this.currentThreadId = response.threadId;
|
|
259
533
|
}
|
|
260
|
-
|
|
261
|
-
const
|
|
262
|
-
|
|
534
|
+
reconnectPolicy() {
|
|
535
|
+
const configured = this.options.reconnect;
|
|
536
|
+
if (configured === false) return null;
|
|
263
537
|
return {
|
|
264
|
-
...
|
|
265
|
-
...
|
|
266
|
-
...extra
|
|
538
|
+
...DEFAULT_RECONNECT,
|
|
539
|
+
...configured
|
|
267
540
|
};
|
|
268
541
|
}
|
|
269
|
-
captureHeaderMeta(headers) {
|
|
270
|
-
const runId = headers.get(HEADER_RUN_ID);
|
|
271
|
-
const threadId = headers.get(HEADER_THREAD_ID);
|
|
272
|
-
if (runId) this.currentRunId = runId;
|
|
273
|
-
if (threadId) this.currentThreadId = threadId;
|
|
274
|
-
}
|
|
275
542
|
recordMeta(meta) {
|
|
276
543
|
this.currentRunId = meta.runId;
|
|
277
544
|
this.currentThreadId = meta.threadId;
|
|
@@ -290,10 +557,66 @@ var AgentChatTransport = class {
|
|
|
290
557
|
* call plus its tool execution; text and reasoning open lazily and close at the step boundary, so
|
|
291
558
|
* tool-call cards (input streaming → output) render live between the prose.
|
|
292
559
|
*/
|
|
293
|
-
toChunkStream(source) {
|
|
294
|
-
|
|
295
|
-
|
|
560
|
+
toChunkStream(source, context) {
|
|
561
|
+
let reader = source.body.getReader();
|
|
562
|
+
let decoder = new TextDecoder();
|
|
296
563
|
let buffer = "";
|
|
564
|
+
let streamRunId = source.runId ?? context.runId;
|
|
565
|
+
let lastSeq;
|
|
566
|
+
let cancelled = false;
|
|
567
|
+
const backend = this.backend;
|
|
568
|
+
const policy = this.reconnectPolicy();
|
|
569
|
+
const notify = /* @__PURE__ */ __name((state) => this.options.onConnectionChange?.(state), "notify");
|
|
570
|
+
const aborted = /* @__PURE__ */ __name(() => cancelled || context.signal?.aborted === true, "aborted");
|
|
571
|
+
const canResume = /* @__PURE__ */ __name(() => policy !== null && lastSeq !== void 0 && streamRunId !== void 0 && !aborted(), "canResume");
|
|
572
|
+
const resume = /* @__PURE__ */ __name(async () => {
|
|
573
|
+
const runId = streamRunId;
|
|
574
|
+
const after = lastSeq ?? 0;
|
|
575
|
+
const { maxAttempts, baseDelayMs, maxDelayMs } = policy;
|
|
576
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
577
|
+
notify({
|
|
578
|
+
status: "reconnecting",
|
|
579
|
+
runId,
|
|
580
|
+
attempt,
|
|
581
|
+
after
|
|
582
|
+
});
|
|
583
|
+
await delay(Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1)), context.signal);
|
|
584
|
+
if (aborted()) return "aborted";
|
|
585
|
+
try {
|
|
586
|
+
const next = await backend.resumeChatStream({
|
|
587
|
+
runId,
|
|
588
|
+
after,
|
|
589
|
+
...context.headers !== void 0 ? {
|
|
590
|
+
headers: context.headers
|
|
591
|
+
} : {},
|
|
592
|
+
...context.signal !== void 0 ? {
|
|
593
|
+
signal: context.signal
|
|
594
|
+
} : {}
|
|
595
|
+
});
|
|
596
|
+
if (next === null) {
|
|
597
|
+
notify({
|
|
598
|
+
status: "gone",
|
|
599
|
+
runId
|
|
600
|
+
});
|
|
601
|
+
return "gone";
|
|
602
|
+
}
|
|
603
|
+
reader = next.body.getReader();
|
|
604
|
+
decoder = new TextDecoder();
|
|
605
|
+
buffer = "";
|
|
606
|
+
notify({
|
|
607
|
+
status: "live"
|
|
608
|
+
});
|
|
609
|
+
return "resumed";
|
|
610
|
+
} catch {
|
|
611
|
+
if (aborted()) return "aborted";
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
notify({
|
|
615
|
+
status: "failed",
|
|
616
|
+
runId
|
|
617
|
+
});
|
|
618
|
+
return "failed";
|
|
619
|
+
}, "resume");
|
|
297
620
|
let started = false;
|
|
298
621
|
let stepOpen = false;
|
|
299
622
|
let stepIndex = 0;
|
|
@@ -304,7 +627,10 @@ var AgentChatTransport = class {
|
|
|
304
627
|
let reasoningSeq = 0;
|
|
305
628
|
const announced = /* @__PURE__ */ new Set();
|
|
306
629
|
const toolMetadata = /* @__PURE__ */ new Map();
|
|
307
|
-
const record = /* @__PURE__ */ __name((meta) =>
|
|
630
|
+
const record = /* @__PURE__ */ __name((meta) => {
|
|
631
|
+
streamRunId = meta.runId;
|
|
632
|
+
this.recordMeta(meta);
|
|
633
|
+
}, "record");
|
|
308
634
|
const endAttempt = /* @__PURE__ */ __name(() => {
|
|
309
635
|
this.attemptLive = false;
|
|
310
636
|
}, "endAttempt");
|
|
@@ -313,7 +639,12 @@ var AgentChatTransport = class {
|
|
|
313
639
|
function ensureStarted() {
|
|
314
640
|
if (started) return;
|
|
315
641
|
started = true;
|
|
316
|
-
controller.enqueue({
|
|
642
|
+
controller.enqueue(streamRunId !== void 0 ? {
|
|
643
|
+
type: "start",
|
|
644
|
+
messageMetadata: {
|
|
645
|
+
runId: streamRunId
|
|
646
|
+
}
|
|
647
|
+
} : {
|
|
317
648
|
type: "start"
|
|
318
649
|
});
|
|
319
650
|
}
|
|
@@ -569,33 +900,67 @@ var AgentChatTransport = class {
|
|
|
569
900
|
endAttempt();
|
|
570
901
|
}
|
|
571
902
|
__name(finish, "finish");
|
|
903
|
+
function fail(errorText, start = true) {
|
|
904
|
+
if (start) ensureStarted();
|
|
905
|
+
controller.enqueue({
|
|
906
|
+
type: "error",
|
|
907
|
+
errorText
|
|
908
|
+
});
|
|
909
|
+
controller.close();
|
|
910
|
+
endAttempt();
|
|
911
|
+
}
|
|
912
|
+
__name(fail, "fail");
|
|
913
|
+
async function recover(error) {
|
|
914
|
+
if (!canResume()) {
|
|
915
|
+
if (error === void 0) {
|
|
916
|
+
finish();
|
|
917
|
+
} else {
|
|
918
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
919
|
+
}
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
const outcome = await resume();
|
|
923
|
+
if (outcome === "resumed") return true;
|
|
924
|
+
if (outcome === "failed") {
|
|
925
|
+
fail("Lost the connection to the agent stream");
|
|
926
|
+
} else {
|
|
927
|
+
finish();
|
|
928
|
+
}
|
|
929
|
+
return false;
|
|
930
|
+
}
|
|
931
|
+
__name(recover, "recover");
|
|
572
932
|
try {
|
|
573
933
|
while (true) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
934
|
+
let result;
|
|
935
|
+
try {
|
|
936
|
+
result = await reader.read();
|
|
937
|
+
} catch (error) {
|
|
938
|
+
if (await recover(error)) continue;
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (result.done) {
|
|
942
|
+
if (await recover(void 0)) continue;
|
|
577
943
|
return;
|
|
578
944
|
}
|
|
579
|
-
buffer += decoder.decode(value, {
|
|
945
|
+
buffer += decoder.decode(result.value, {
|
|
580
946
|
stream: true
|
|
581
947
|
});
|
|
582
948
|
let separator = buffer.indexOf("\n\n");
|
|
583
949
|
while (separator !== -1) {
|
|
584
950
|
const rawEvent = buffer.slice(0, separator);
|
|
585
951
|
buffer = buffer.slice(separator + 2);
|
|
952
|
+
separator = buffer.indexOf("\n\n");
|
|
586
953
|
const frame = parseSseFrame(rawEvent);
|
|
954
|
+
if (frame.id !== void 0) {
|
|
955
|
+
if (lastSeq !== void 0 && frame.id <= lastSeq) continue;
|
|
956
|
+
lastSeq = frame.id;
|
|
957
|
+
}
|
|
587
958
|
if (frame.event === "done") {
|
|
588
959
|
finish();
|
|
589
960
|
return;
|
|
590
961
|
}
|
|
591
962
|
if (frame.event === "error") {
|
|
592
|
-
|
|
593
|
-
controller.enqueue({
|
|
594
|
-
type: "error",
|
|
595
|
-
errorText: parseErrorText(frame.data)
|
|
596
|
-
});
|
|
597
|
-
controller.close();
|
|
598
|
-
endAttempt();
|
|
963
|
+
fail(parseErrorText(frame.data));
|
|
599
964
|
return;
|
|
600
965
|
}
|
|
601
966
|
if (frame.event === "meta") {
|
|
@@ -605,19 +970,14 @@ var AgentChatTransport = class {
|
|
|
605
970
|
const event = parseEvent(frame.data);
|
|
606
971
|
if (event) emit(event);
|
|
607
972
|
}
|
|
608
|
-
separator = buffer.indexOf("\n\n");
|
|
609
973
|
}
|
|
610
974
|
}
|
|
611
975
|
} catch (error) {
|
|
612
|
-
|
|
613
|
-
type: "error",
|
|
614
|
-
errorText: error instanceof Error ? error.message : "Agent stream error"
|
|
615
|
-
});
|
|
616
|
-
controller.close();
|
|
617
|
-
endAttempt();
|
|
976
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
618
977
|
}
|
|
619
978
|
},
|
|
620
979
|
cancel() {
|
|
980
|
+
cancelled = true;
|
|
621
981
|
void reader.cancel();
|
|
622
982
|
endAttempt();
|
|
623
983
|
}
|
|
@@ -626,17 +986,22 @@ var AgentChatTransport = class {
|
|
|
626
986
|
};
|
|
627
987
|
function parseSseFrame(raw) {
|
|
628
988
|
let event;
|
|
989
|
+
let id;
|
|
629
990
|
const dataLines = [];
|
|
630
991
|
for (const line of raw.split("\n")) {
|
|
631
992
|
if (line.startsWith("event:")) {
|
|
632
993
|
event = line.slice("event:".length).trim();
|
|
994
|
+
} else if (line.startsWith("id:")) {
|
|
995
|
+
const parsed = Number(line.slice("id:".length).trim());
|
|
996
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) id = parsed;
|
|
633
997
|
} else if (line.startsWith("data:")) {
|
|
634
998
|
dataLines.push(line.slice("data:".length).trimStart());
|
|
635
999
|
}
|
|
636
1000
|
}
|
|
637
1001
|
return {
|
|
638
1002
|
event,
|
|
639
|
-
data: dataLines.length > 0 ? dataLines.join("\n") : void 0
|
|
1003
|
+
data: dataLines.length > 0 ? dataLines.join("\n") : void 0,
|
|
1004
|
+
id
|
|
640
1005
|
};
|
|
641
1006
|
}
|
|
642
1007
|
__name(parseSseFrame, "parseSseFrame");
|
|
@@ -677,15 +1042,382 @@ function parseEvent(data) {
|
|
|
677
1042
|
}
|
|
678
1043
|
return null;
|
|
679
1044
|
}
|
|
680
|
-
__name(parseEvent, "parseEvent");
|
|
681
|
-
function extractText(message) {
|
|
682
|
-
let text = "";
|
|
683
|
-
for (const part of message.parts ?? []) {
|
|
684
|
-
if (part.type === "text") text += part.text;
|
|
1045
|
+
__name(parseEvent, "parseEvent");
|
|
1046
|
+
function extractText(message) {
|
|
1047
|
+
let text = "";
|
|
1048
|
+
for (const part of message.parts ?? []) {
|
|
1049
|
+
if (part.type === "text") text += part.text;
|
|
1050
|
+
}
|
|
1051
|
+
return text;
|
|
1052
|
+
}
|
|
1053
|
+
__name(extractText, "extractText");
|
|
1054
|
+
function headerRecord(headers) {
|
|
1055
|
+
if (headers === void 0) return void 0;
|
|
1056
|
+
return headers instanceof Headers ? Object.fromEntries(headers.entries()) : headers;
|
|
1057
|
+
}
|
|
1058
|
+
__name(headerRecord, "headerRecord");
|
|
1059
|
+
function delay(ms, signal) {
|
|
1060
|
+
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
1061
|
+
return new Promise((resolve) => {
|
|
1062
|
+
const timer = setTimeout(done, ms);
|
|
1063
|
+
function done() {
|
|
1064
|
+
clearTimeout(timer);
|
|
1065
|
+
signal?.removeEventListener("abort", done);
|
|
1066
|
+
resolve();
|
|
1067
|
+
}
|
|
1068
|
+
__name(done, "done");
|
|
1069
|
+
signal?.addEventListener("abort", done, {
|
|
1070
|
+
once: true
|
|
1071
|
+
});
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
__name(delay, "delay");
|
|
1075
|
+
|
|
1076
|
+
// src/backend.ts
|
|
1077
|
+
var AgentBackendUnsupportedError = class extends Error {
|
|
1078
|
+
static {
|
|
1079
|
+
__name(this, "AgentBackendUnsupportedError");
|
|
1080
|
+
}
|
|
1081
|
+
method;
|
|
1082
|
+
constructor(method) {
|
|
1083
|
+
super(`The agent backend does not implement ${method}()`), this.method = method;
|
|
1084
|
+
this.name = "AgentBackendUnsupportedError";
|
|
1085
|
+
}
|
|
1086
|
+
};
|
|
1087
|
+
function requireBackendMethod(backend, name) {
|
|
1088
|
+
const method = backend[name];
|
|
1089
|
+
if (typeof method !== "function") {
|
|
1090
|
+
throw new AgentBackendUnsupportedError(name);
|
|
1091
|
+
}
|
|
1092
|
+
return method.bind(backend);
|
|
1093
|
+
}
|
|
1094
|
+
__name(requireBackendMethod, "requireBackendMethod");
|
|
1095
|
+
|
|
1096
|
+
// src/catalog/use-agents.ts
|
|
1097
|
+
var import_react3 = require("react");
|
|
1098
|
+
|
|
1099
|
+
// src/catalog/use-resource.ts
|
|
1100
|
+
var import_react2 = require("react");
|
|
1101
|
+
function useResource(load2, key, enabled) {
|
|
1102
|
+
const [data, setData] = (0, import_react2.useState)(void 0);
|
|
1103
|
+
const [isLoading, setIsLoading] = (0, import_react2.useState)(enabled);
|
|
1104
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
1105
|
+
const loadRef = (0, import_react2.useRef)(load2);
|
|
1106
|
+
loadRef.current = load2;
|
|
1107
|
+
const generation = (0, import_react2.useRef)(0);
|
|
1108
|
+
const refresh = (0, import_react2.useCallback)(async () => {
|
|
1109
|
+
const mine = ++generation.current;
|
|
1110
|
+
setIsLoading(true);
|
|
1111
|
+
try {
|
|
1112
|
+
const value = await loadRef.current();
|
|
1113
|
+
if (mine !== generation.current) return;
|
|
1114
|
+
setData(value);
|
|
1115
|
+
setError(null);
|
|
1116
|
+
} catch (caught) {
|
|
1117
|
+
if (mine !== generation.current) return;
|
|
1118
|
+
setError(caught instanceof Error ? caught : new Error(String(caught)));
|
|
1119
|
+
} finally {
|
|
1120
|
+
if (mine === generation.current) setIsLoading(false);
|
|
1121
|
+
}
|
|
1122
|
+
}, []);
|
|
1123
|
+
(0, import_react2.useEffect)(() => {
|
|
1124
|
+
if (!enabled) return;
|
|
1125
|
+
void refresh();
|
|
1126
|
+
}, [
|
|
1127
|
+
enabled,
|
|
1128
|
+
key,
|
|
1129
|
+
refresh
|
|
1130
|
+
]);
|
|
1131
|
+
return {
|
|
1132
|
+
data,
|
|
1133
|
+
isLoading,
|
|
1134
|
+
error,
|
|
1135
|
+
refresh
|
|
1136
|
+
};
|
|
1137
|
+
}
|
|
1138
|
+
__name(useResource, "useResource");
|
|
1139
|
+
|
|
1140
|
+
// src/catalog/use-agents.ts
|
|
1141
|
+
function useAgents(options) {
|
|
1142
|
+
const { backend, enabled = true } = options;
|
|
1143
|
+
const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
|
|
1144
|
+
return (0, import_react3.useMemo)(() => {
|
|
1145
|
+
const agents = data ?? [];
|
|
1146
|
+
return {
|
|
1147
|
+
agents,
|
|
1148
|
+
defaultAgent: agents.find((agent) => agent.isDefault === true)?.name ?? null,
|
|
1149
|
+
isLoading,
|
|
1150
|
+
error,
|
|
1151
|
+
refresh
|
|
1152
|
+
};
|
|
1153
|
+
}, [
|
|
1154
|
+
data,
|
|
1155
|
+
isLoading,
|
|
1156
|
+
error,
|
|
1157
|
+
refresh
|
|
1158
|
+
]);
|
|
1159
|
+
}
|
|
1160
|
+
__name(useAgents, "useAgents");
|
|
1161
|
+
|
|
1162
|
+
// src/catalog/use-models.ts
|
|
1163
|
+
var import_react4 = require("react");
|
|
1164
|
+
function useModels(options) {
|
|
1165
|
+
const { backend, agent, enabled = true } = options;
|
|
1166
|
+
const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
|
|
1167
|
+
return (0, import_react4.useMemo)(() => {
|
|
1168
|
+
const providers = data?.providers ?? [];
|
|
1169
|
+
const models = providers.flatMap((provider) => provider.models.map((model) => ({
|
|
1170
|
+
...model,
|
|
1171
|
+
providerId: provider.id,
|
|
1172
|
+
providerLabel: provider.label
|
|
1173
|
+
})));
|
|
1174
|
+
const byId = new Map(models.map((model) => [
|
|
1175
|
+
model.id,
|
|
1176
|
+
model
|
|
1177
|
+
]));
|
|
1178
|
+
return {
|
|
1179
|
+
providers,
|
|
1180
|
+
models,
|
|
1181
|
+
defaultModel: data?.default ?? null,
|
|
1182
|
+
find: /* @__PURE__ */ __name((id) => id == null ? void 0 : byId.get(id), "find"),
|
|
1183
|
+
isLoading,
|
|
1184
|
+
error,
|
|
1185
|
+
refresh
|
|
1186
|
+
};
|
|
1187
|
+
}, [
|
|
1188
|
+
data,
|
|
1189
|
+
isLoading,
|
|
1190
|
+
error,
|
|
1191
|
+
refresh
|
|
1192
|
+
]);
|
|
1193
|
+
}
|
|
1194
|
+
__name(useModels, "useModels");
|
|
1195
|
+
|
|
1196
|
+
// src/feedback/use-message-feedback.ts
|
|
1197
|
+
var import_react5 = require("react");
|
|
1198
|
+
function useMessageFeedback(options) {
|
|
1199
|
+
const latest = (0, import_react5.useRef)(options);
|
|
1200
|
+
latest.current = options;
|
|
1201
|
+
const [local, setLocal] = (0, import_react5.useState)(/* @__PURE__ */ new Map());
|
|
1202
|
+
const [pending, setPending] = (0, import_react5.useState)(/* @__PURE__ */ new Set());
|
|
1203
|
+
const [error, setError] = (0, import_react5.useState)(null);
|
|
1204
|
+
const localRef = (0, import_react5.useRef)(local);
|
|
1205
|
+
localRef.current = local;
|
|
1206
|
+
const resolved = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1207
|
+
const feedbackOf = (0, import_react5.useCallback)((message) => {
|
|
1208
|
+
const mine = local.get(message.id);
|
|
1209
|
+
if (mine !== void 0) return mine.feedback;
|
|
1210
|
+
return metadataOf(message).feedback ?? null;
|
|
1211
|
+
}, [
|
|
1212
|
+
local
|
|
1213
|
+
]);
|
|
1214
|
+
const isPending = (0, import_react5.useCallback)((message) => pending.has(message.id), [
|
|
1215
|
+
pending
|
|
1216
|
+
]);
|
|
1217
|
+
const storedIdFor = (0, import_react5.useCallback)(async (message) => {
|
|
1218
|
+
const runId = metadataOf(message).runId;
|
|
1219
|
+
if (runId === void 0) return message.id;
|
|
1220
|
+
const known = resolved.current.get(message.id);
|
|
1221
|
+
if (known !== void 0) return known;
|
|
1222
|
+
const { backend, threadId } = latest.current;
|
|
1223
|
+
const thread = typeof threadId === "function" ? threadId() : threadId;
|
|
1224
|
+
if (thread === void 0) {
|
|
1225
|
+
throw new Error("useMessageFeedback: a streamed message needs `threadId` to be rated");
|
|
1226
|
+
}
|
|
1227
|
+
const detail = await backend.getThread(thread);
|
|
1228
|
+
const row = [
|
|
1229
|
+
...detail.messages
|
|
1230
|
+
].reverse().find((candidate) => candidate.role === "assistant" && candidate.runId === runId);
|
|
1231
|
+
if (row === void 0) {
|
|
1232
|
+
throw new Error(`useMessageFeedback: run ${runId} has no persisted answer yet`);
|
|
1233
|
+
}
|
|
1234
|
+
resolved.current.set(message.id, row.id);
|
|
1235
|
+
return row.id;
|
|
1236
|
+
}, []);
|
|
1237
|
+
const rate = (0, import_react5.useCallback)(async (message, value, comment) => {
|
|
1238
|
+
const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
|
|
1239
|
+
const had = localRef.current.get(message.id);
|
|
1240
|
+
const optimistic = value === null ? null : {
|
|
1241
|
+
value,
|
|
1242
|
+
...comment !== void 0 && comment.trim().length > 0 ? {
|
|
1243
|
+
comment: comment.trim()
|
|
1244
|
+
} : {},
|
|
1245
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1246
|
+
};
|
|
1247
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1248
|
+
feedback: optimistic
|
|
1249
|
+
}));
|
|
1250
|
+
setPending((current) => new Set(current).add(message.id));
|
|
1251
|
+
try {
|
|
1252
|
+
const storedId = await storedIdFor(message);
|
|
1253
|
+
const { feedback } = await setFeedback(storedId, {
|
|
1254
|
+
value,
|
|
1255
|
+
...comment !== void 0 ? {
|
|
1256
|
+
comment
|
|
1257
|
+
} : {}
|
|
1258
|
+
});
|
|
1259
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1260
|
+
feedback
|
|
1261
|
+
}));
|
|
1262
|
+
setError(null);
|
|
1263
|
+
} catch (caught) {
|
|
1264
|
+
setLocal((current) => {
|
|
1265
|
+
const next = new Map(current);
|
|
1266
|
+
if (had === void 0) next.delete(message.id);
|
|
1267
|
+
else next.set(message.id, had);
|
|
1268
|
+
return next;
|
|
1269
|
+
});
|
|
1270
|
+
const failure = caught instanceof Error ? caught : new Error(String(caught));
|
|
1271
|
+
setError(failure);
|
|
1272
|
+
throw failure;
|
|
1273
|
+
} finally {
|
|
1274
|
+
setPending((current) => {
|
|
1275
|
+
const next = new Set(current);
|
|
1276
|
+
next.delete(message.id);
|
|
1277
|
+
return next;
|
|
1278
|
+
});
|
|
1279
|
+
}
|
|
1280
|
+
}, [
|
|
1281
|
+
storedIdFor
|
|
1282
|
+
]);
|
|
1283
|
+
const toggle = (0, import_react5.useCallback)((message, value) => {
|
|
1284
|
+
const mine = localRef.current.get(message.id);
|
|
1285
|
+
const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
|
|
1286
|
+
return rate(message, current?.value === value ? null : value);
|
|
1287
|
+
}, [
|
|
1288
|
+
rate
|
|
1289
|
+
]);
|
|
1290
|
+
return {
|
|
1291
|
+
feedbackOf,
|
|
1292
|
+
isPending,
|
|
1293
|
+
rate,
|
|
1294
|
+
toggle,
|
|
1295
|
+
error
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
__name(useMessageFeedback, "useMessageFeedback");
|
|
1299
|
+
function metadataOf(message) {
|
|
1300
|
+
const metadata = message.metadata;
|
|
1301
|
+
return metadata !== null && typeof metadata === "object" ? metadata : {};
|
|
1302
|
+
}
|
|
1303
|
+
__name(metadataOf, "metadataOf");
|
|
1304
|
+
|
|
1305
|
+
// src/threads/threads-events.ts
|
|
1306
|
+
var listeners = /* @__PURE__ */ new WeakMap();
|
|
1307
|
+
function notifyThreads(backend, event) {
|
|
1308
|
+
for (const listener of listeners.get(backend) ?? []) {
|
|
1309
|
+
listener(event);
|
|
685
1310
|
}
|
|
686
|
-
return text;
|
|
687
1311
|
}
|
|
688
|
-
__name(
|
|
1312
|
+
__name(notifyThreads, "notifyThreads");
|
|
1313
|
+
function onThreadsEvent(backend, listener) {
|
|
1314
|
+
let set = listeners.get(backend);
|
|
1315
|
+
if (set === void 0) {
|
|
1316
|
+
set = /* @__PURE__ */ new Set();
|
|
1317
|
+
listeners.set(backend, set);
|
|
1318
|
+
}
|
|
1319
|
+
set.add(listener);
|
|
1320
|
+
return () => {
|
|
1321
|
+
set.delete(listener);
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
__name(onThreadsEvent, "onThreadsEvent");
|
|
1325
|
+
|
|
1326
|
+
// src/threads/use-threads.ts
|
|
1327
|
+
var import_react6 = require("react");
|
|
1328
|
+
function useThreads(options) {
|
|
1329
|
+
const { backend, enabled = true } = options;
|
|
1330
|
+
const [threads, setThreads] = (0, import_react6.useState)([]);
|
|
1331
|
+
const [isLoading, setIsLoading] = (0, import_react6.useState)(enabled);
|
|
1332
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
1333
|
+
const threadsRef = (0, import_react6.useRef)(threads);
|
|
1334
|
+
threadsRef.current = threads;
|
|
1335
|
+
const generation = (0, import_react6.useRef)(0);
|
|
1336
|
+
const refresh = (0, import_react6.useCallback)(async () => {
|
|
1337
|
+
const mine = ++generation.current;
|
|
1338
|
+
setIsLoading(true);
|
|
1339
|
+
try {
|
|
1340
|
+
const list = await backend.listThreads();
|
|
1341
|
+
if (mine !== generation.current) return;
|
|
1342
|
+
setThreads(list);
|
|
1343
|
+
setError(null);
|
|
1344
|
+
} catch (caught) {
|
|
1345
|
+
if (mine !== generation.current) return;
|
|
1346
|
+
setError(caught instanceof Error ? caught : new Error(String(caught)));
|
|
1347
|
+
} finally {
|
|
1348
|
+
if (mine === generation.current) setIsLoading(false);
|
|
1349
|
+
}
|
|
1350
|
+
}, [
|
|
1351
|
+
backend
|
|
1352
|
+
]);
|
|
1353
|
+
(0, import_react6.useEffect)(() => {
|
|
1354
|
+
if (!enabled) return;
|
|
1355
|
+
void refresh();
|
|
1356
|
+
return onThreadsEvent(backend, (event) => {
|
|
1357
|
+
if (event.type === "title") {
|
|
1358
|
+
setThreads((current) => current.map((thread) => thread.id === event.threadId ? {
|
|
1359
|
+
...thread,
|
|
1360
|
+
title: event.title
|
|
1361
|
+
} : thread));
|
|
1362
|
+
} else if (event.type === "removed") {
|
|
1363
|
+
setThreads((current) => current.filter((thread) => thread.id !== event.threadId));
|
|
1364
|
+
} else {
|
|
1365
|
+
void refresh();
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
}, [
|
|
1369
|
+
backend,
|
|
1370
|
+
enabled,
|
|
1371
|
+
refresh
|
|
1372
|
+
]);
|
|
1373
|
+
const rename = (0, import_react6.useCallback)(async (id, title) => {
|
|
1374
|
+
const before = threadsRef.current;
|
|
1375
|
+
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
1376
|
+
...thread,
|
|
1377
|
+
title
|
|
1378
|
+
} : thread));
|
|
1379
|
+
try {
|
|
1380
|
+
await backend.updateThread(id, {
|
|
1381
|
+
title
|
|
1382
|
+
});
|
|
1383
|
+
notifyThreads(backend, {
|
|
1384
|
+
type: "title",
|
|
1385
|
+
threadId: id,
|
|
1386
|
+
title
|
|
1387
|
+
});
|
|
1388
|
+
} catch (caught) {
|
|
1389
|
+
setThreads(before);
|
|
1390
|
+
throw caught;
|
|
1391
|
+
}
|
|
1392
|
+
}, [
|
|
1393
|
+
backend
|
|
1394
|
+
]);
|
|
1395
|
+
const remove = (0, import_react6.useCallback)(async (id) => {
|
|
1396
|
+
const before = threadsRef.current;
|
|
1397
|
+
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
1398
|
+
try {
|
|
1399
|
+
await backend.deleteThread(id);
|
|
1400
|
+
notifyThreads(backend, {
|
|
1401
|
+
type: "removed",
|
|
1402
|
+
threadId: id
|
|
1403
|
+
});
|
|
1404
|
+
} catch (caught) {
|
|
1405
|
+
setThreads(before);
|
|
1406
|
+
throw caught;
|
|
1407
|
+
}
|
|
1408
|
+
}, [
|
|
1409
|
+
backend
|
|
1410
|
+
]);
|
|
1411
|
+
return {
|
|
1412
|
+
threads,
|
|
1413
|
+
isLoading,
|
|
1414
|
+
error,
|
|
1415
|
+
refresh,
|
|
1416
|
+
rename,
|
|
1417
|
+
remove
|
|
1418
|
+
};
|
|
1419
|
+
}
|
|
1420
|
+
__name(useThreads, "useThreads");
|
|
689
1421
|
|
|
690
1422
|
// src/background-runs.ts
|
|
691
1423
|
function asReceipt(output) {
|
|
@@ -734,170 +1466,11 @@ function backgroundRunsFromThread(messages) {
|
|
|
734
1466
|
}
|
|
735
1467
|
__name(backgroundRunsFromThread, "backgroundRunsFromThread");
|
|
736
1468
|
|
|
737
|
-
// src/client.ts
|
|
738
|
-
var AgentHttpError = class extends Error {
|
|
739
|
-
static {
|
|
740
|
-
__name(this, "AgentHttpError");
|
|
741
|
-
}
|
|
742
|
-
status;
|
|
743
|
-
method;
|
|
744
|
-
path;
|
|
745
|
-
constructor(status, method, path, statusText) {
|
|
746
|
-
super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
|
|
747
|
-
this.name = "AgentHttpError";
|
|
748
|
-
}
|
|
749
|
-
};
|
|
750
|
-
var AgentClient = class {
|
|
751
|
-
static {
|
|
752
|
-
__name(this, "AgentClient");
|
|
753
|
-
}
|
|
754
|
-
options;
|
|
755
|
-
constructor(options = {}) {
|
|
756
|
-
this.options = options;
|
|
757
|
-
}
|
|
758
|
-
listThreads() {
|
|
759
|
-
return this.request("GET", "/agent/threads");
|
|
760
|
-
}
|
|
761
|
-
/**
|
|
762
|
-
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
763
|
-
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
764
|
-
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
765
|
-
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
766
|
-
*/
|
|
767
|
-
listSkills(threadId) {
|
|
768
|
-
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
769
|
-
return this.request("GET", `/agent/skills${query}`);
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
|
|
773
|
-
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
|
|
774
|
-
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
|
|
775
|
-
*/
|
|
776
|
-
listTools(agent) {
|
|
777
|
-
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
778
|
-
return this.request("GET", `/agent/tools${query}`);
|
|
779
|
-
}
|
|
780
|
-
getThread(id) {
|
|
781
|
-
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
782
|
-
}
|
|
783
|
-
deleteThread(id) {
|
|
784
|
-
return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
|
|
785
|
-
}
|
|
786
|
-
forkFromMessage(threadId, messageId) {
|
|
787
|
-
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
788
|
-
}
|
|
789
|
-
renameThread(id, title) {
|
|
790
|
-
return this.updateThread(id, {
|
|
791
|
-
title
|
|
792
|
-
});
|
|
793
|
-
}
|
|
794
|
-
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
795
|
-
updateThread(id, patch) {
|
|
796
|
-
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
797
|
-
}
|
|
798
|
-
/**
|
|
799
|
-
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
800
|
-
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
801
|
-
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
802
|
-
*/
|
|
803
|
-
async uploadAttachment(file) {
|
|
804
|
-
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
805
|
-
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
806
|
-
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
807
|
-
const formData = new FormData();
|
|
808
|
-
formData.append("file", file);
|
|
809
|
-
const response = await fetchImpl(`${baseUrl}/agent/attachments`, {
|
|
810
|
-
method: "POST",
|
|
811
|
-
headers: {
|
|
812
|
-
accept: "application/json",
|
|
813
|
-
...this.options.headers,
|
|
814
|
-
...dynamic
|
|
815
|
-
},
|
|
816
|
-
body: formData,
|
|
817
|
-
...this.options.credentials !== void 0 ? {
|
|
818
|
-
credentials: this.options.credentials
|
|
819
|
-
} : {}
|
|
820
|
-
});
|
|
821
|
-
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
822
|
-
}
|
|
823
|
-
promoteThread(id) {
|
|
824
|
-
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
825
|
-
}
|
|
826
|
-
truncateFromMessage(threadId, messageId) {
|
|
827
|
-
return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
|
|
828
|
-
}
|
|
829
|
-
getQuotaToday() {
|
|
830
|
-
return this.request("GET", "/agent/quota/today");
|
|
831
|
-
}
|
|
832
|
-
cancelStream(runId) {
|
|
833
|
-
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
834
|
-
}
|
|
835
|
-
/**
|
|
836
|
-
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
837
|
-
* the decision came through (the server records `'web'` when omitted).
|
|
838
|
-
*/
|
|
839
|
-
approveToolCall(input) {
|
|
840
|
-
return this.request("POST", "/agent/tool-call/approve", input);
|
|
841
|
-
}
|
|
842
|
-
rejectToolCall(input) {
|
|
843
|
-
return this.request("POST", "/agent/tool-call/reject", input);
|
|
844
|
-
}
|
|
845
|
-
/**
|
|
846
|
-
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
847
|
-
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
848
|
-
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
849
|
-
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
850
|
-
*/
|
|
851
|
-
answerToolCall(input) {
|
|
852
|
-
return this.request("POST", "/agent/tool-call/answer", input);
|
|
853
|
-
}
|
|
854
|
-
/**
|
|
855
|
-
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
856
|
-
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
857
|
-
* evidence the user chose them.
|
|
858
|
-
*/
|
|
859
|
-
skipToolCall(input) {
|
|
860
|
-
return this.request("POST", "/agent/tool-call/skip", input);
|
|
861
|
-
}
|
|
862
|
-
async request(method, path, body) {
|
|
863
|
-
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
864
|
-
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
865
|
-
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
866
|
-
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
867
|
-
method,
|
|
868
|
-
headers: {
|
|
869
|
-
accept: "application/json",
|
|
870
|
-
...body !== void 0 ? {
|
|
871
|
-
"content-type": "application/json"
|
|
872
|
-
} : {},
|
|
873
|
-
...this.options.headers,
|
|
874
|
-
...dynamic
|
|
875
|
-
},
|
|
876
|
-
...body !== void 0 ? {
|
|
877
|
-
body: JSON.stringify(body)
|
|
878
|
-
} : {},
|
|
879
|
-
...this.options.credentials !== void 0 ? {
|
|
880
|
-
credentials: this.options.credentials
|
|
881
|
-
} : {}
|
|
882
|
-
});
|
|
883
|
-
return this.handleResponse(response, method, path);
|
|
884
|
-
}
|
|
885
|
-
async handleResponse(response, method, path) {
|
|
886
|
-
if (!response.ok) {
|
|
887
|
-
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
888
|
-
}
|
|
889
|
-
if (response.status === 204) return void 0;
|
|
890
|
-
const text = await response.text();
|
|
891
|
-
if (!text) return void 0;
|
|
892
|
-
return JSON.parse(text);
|
|
893
|
-
}
|
|
894
|
-
};
|
|
895
|
-
|
|
896
1469
|
// src/components/chat-input.tsx
|
|
897
|
-
var
|
|
1470
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
898
1471
|
|
|
899
1472
|
// src/components/use-speech-recognition.ts
|
|
900
|
-
var
|
|
1473
|
+
var import_react7 = require("react");
|
|
901
1474
|
function getRecognitionCtor() {
|
|
902
1475
|
if (typeof window === "undefined") return null;
|
|
903
1476
|
const scope = window;
|
|
@@ -905,15 +1478,15 @@ function getRecognitionCtor() {
|
|
|
905
1478
|
}
|
|
906
1479
|
__name(getRecognitionCtor, "getRecognitionCtor");
|
|
907
1480
|
function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
|
|
908
|
-
const recognitionRef = (0,
|
|
909
|
-
const [isListening, setIsListening] = (0,
|
|
910
|
-
const ctor = (0,
|
|
1481
|
+
const recognitionRef = (0, import_react7.useRef)(null);
|
|
1482
|
+
const [isListening, setIsListening] = (0, import_react7.useState)(false);
|
|
1483
|
+
const ctor = (0, import_react7.useMemo)(() => getRecognitionCtor(), []);
|
|
911
1484
|
const isSupported = ctor !== null;
|
|
912
|
-
const finalRef = (0,
|
|
913
|
-
const interimRef = (0,
|
|
1485
|
+
const finalRef = (0, import_react7.useRef)(onFinalTranscript);
|
|
1486
|
+
const interimRef = (0, import_react7.useRef)(onInterimTranscript);
|
|
914
1487
|
finalRef.current = onFinalTranscript;
|
|
915
1488
|
interimRef.current = onInterimTranscript;
|
|
916
|
-
(0,
|
|
1489
|
+
(0, import_react7.useEffect)(() => {
|
|
917
1490
|
if (!ctor) return;
|
|
918
1491
|
const recognition = new ctor();
|
|
919
1492
|
recognition.lang = lang;
|
|
@@ -952,7 +1525,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
952
1525
|
ctor,
|
|
953
1526
|
lang
|
|
954
1527
|
]);
|
|
955
|
-
const start = (0,
|
|
1528
|
+
const start = (0, import_react7.useCallback)(() => {
|
|
956
1529
|
const recognition = recognitionRef.current;
|
|
957
1530
|
if (!recognition) return;
|
|
958
1531
|
try {
|
|
@@ -962,7 +1535,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
962
1535
|
console.warn("SpeechRecognition start failed", error);
|
|
963
1536
|
}
|
|
964
1537
|
}, []);
|
|
965
|
-
const stop = (0,
|
|
1538
|
+
const stop = (0, import_react7.useCallback)(() => {
|
|
966
1539
|
const recognition = recognitionRef.current;
|
|
967
1540
|
if (!recognition) return;
|
|
968
1541
|
try {
|
|
@@ -981,8 +1554,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
|
|
|
981
1554
|
|
|
982
1555
|
// src/components/chat-input.tsx
|
|
983
1556
|
function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = "en-US", sendLabel = "Send", onStop, isStreaming = false, stopLabel = "Stop generating", className, textareaClassName, sendButtonClassName, micButtonClassName, stopButtonClassName, renderMic }) {
|
|
984
|
-
const [value, setValue] = (0,
|
|
985
|
-
const [interim, setInterim] = (0,
|
|
1557
|
+
const [value, setValue] = (0, import_react8.useState)("");
|
|
1558
|
+
const [interim, setInterim] = (0, import_react8.useState)("");
|
|
986
1559
|
const speech = useSpeechRecognition({
|
|
987
1560
|
lang: speechLang,
|
|
988
1561
|
onFinalTranscript: /* @__PURE__ */ __name((text) => {
|
|
@@ -993,7 +1566,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
993
1566
|
}, "onFinalTranscript"),
|
|
994
1567
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
995
1568
|
});
|
|
996
|
-
(0,
|
|
1569
|
+
(0, import_react8.useEffect)(() => {
|
|
997
1570
|
if (!speech.isListening) setInterim("");
|
|
998
1571
|
}, [
|
|
999
1572
|
speech.isListening
|
|
@@ -1021,9 +1594,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1021
1594
|
const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
|
|
1022
1595
|
const showMic = enableSpeech && speech.isSupported;
|
|
1023
1596
|
const showStop = isStreaming && onStop !== void 0;
|
|
1024
|
-
return /* @__PURE__ */
|
|
1597
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
1025
1598
|
className
|
|
1026
|
-
}, /* @__PURE__ */
|
|
1599
|
+
}, /* @__PURE__ */ import_react8.default.createElement("textarea", {
|
|
1027
1600
|
className: textareaClassName,
|
|
1028
1601
|
rows,
|
|
1029
1602
|
value: displayValue,
|
|
@@ -1034,17 +1607,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1034
1607
|
}, "onChange"),
|
|
1035
1608
|
onKeyDown: handleKeyDown,
|
|
1036
1609
|
placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
|
|
1037
|
-
}), /* @__PURE__ */
|
|
1610
|
+
}), /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
1038
1611
|
type: "button",
|
|
1039
1612
|
className: sendButtonClassName,
|
|
1040
1613
|
onClick: submit,
|
|
1041
1614
|
disabled: disabled || !value.trim()
|
|
1042
|
-
}, sendLabel), showStop ? /* @__PURE__ */
|
|
1615
|
+
}, sendLabel), showStop ? /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
1043
1616
|
type: "button",
|
|
1044
1617
|
className: stopButtonClassName,
|
|
1045
1618
|
onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
|
|
1046
1619
|
"aria-label": "Stop generating"
|
|
1047
|
-
}, stopLabel) : null, showMic ? /* @__PURE__ */
|
|
1620
|
+
}, stopLabel) : null, showMic ? /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
1048
1621
|
type: "button",
|
|
1049
1622
|
className: micButtonClassName,
|
|
1050
1623
|
onClick: toggleMic,
|
|
@@ -1057,10 +1630,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1057
1630
|
__name(ChatInput, "ChatInput");
|
|
1058
1631
|
|
|
1059
1632
|
// src/components/message-item.tsx
|
|
1060
|
-
var
|
|
1633
|
+
var import_react11 = __toESM(require("react"), 1);
|
|
1061
1634
|
|
|
1062
1635
|
// src/transcript/use-chat-transcript.ts
|
|
1063
|
-
var
|
|
1636
|
+
var import_react10 = require("react");
|
|
1064
1637
|
|
|
1065
1638
|
// src/transcript/model.ts
|
|
1066
1639
|
var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
|
|
@@ -2037,11 +2610,11 @@ function formatCostUsd(cost) {
|
|
|
2037
2610
|
__name(formatCostUsd, "formatCostUsd");
|
|
2038
2611
|
|
|
2039
2612
|
// src/transcript/use-stick-to-bottom.ts
|
|
2040
|
-
var
|
|
2613
|
+
var import_react9 = require("react");
|
|
2041
2614
|
function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
2042
|
-
const containerRef = (0,
|
|
2043
|
-
const [isAtBottom, setIsAtBottom] = (0,
|
|
2044
|
-
const measure = (0,
|
|
2615
|
+
const containerRef = (0, import_react9.useRef)(null);
|
|
2616
|
+
const [isAtBottom, setIsAtBottom] = (0, import_react9.useState)(true);
|
|
2617
|
+
const measure = (0, import_react9.useCallback)(() => {
|
|
2045
2618
|
const element = containerRef.current;
|
|
2046
2619
|
if (!element) {
|
|
2047
2620
|
return;
|
|
@@ -2051,7 +2624,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2051
2624
|
}, [
|
|
2052
2625
|
threshold
|
|
2053
2626
|
]);
|
|
2054
|
-
const scrollToBottom = (0,
|
|
2627
|
+
const scrollToBottom = (0, import_react9.useCallback)(() => {
|
|
2055
2628
|
const element = containerRef.current;
|
|
2056
2629
|
if (!element) {
|
|
2057
2630
|
return;
|
|
@@ -2059,7 +2632,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2059
2632
|
element.scrollTop = element.scrollHeight;
|
|
2060
2633
|
setIsAtBottom(true);
|
|
2061
2634
|
}, []);
|
|
2062
|
-
const setContainer = (0,
|
|
2635
|
+
const setContainer = (0, import_react9.useCallback)((element) => {
|
|
2063
2636
|
containerRef.current = element;
|
|
2064
2637
|
if (element) {
|
|
2065
2638
|
measure();
|
|
@@ -2067,7 +2640,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2067
2640
|
}, [
|
|
2068
2641
|
measure
|
|
2069
2642
|
]);
|
|
2070
|
-
(0,
|
|
2643
|
+
(0, import_react9.useEffect)(() => {
|
|
2071
2644
|
if (isAtBottom) {
|
|
2072
2645
|
scrollToBottom();
|
|
2073
2646
|
}
|
|
@@ -2076,7 +2649,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2076
2649
|
isAtBottom,
|
|
2077
2650
|
scrollToBottom
|
|
2078
2651
|
]);
|
|
2079
|
-
const getContainerProps = (0,
|
|
2652
|
+
const getContainerProps = (0, import_react9.useCallback)(() => ({
|
|
2080
2653
|
ref: setContainer,
|
|
2081
2654
|
onScroll: measure
|
|
2082
2655
|
}), [
|
|
@@ -2098,14 +2671,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
|
|
|
2098
2671
|
var DEFAULT_COPY_RESET_MS = 1500;
|
|
2099
2672
|
function useChatTranscript(options) {
|
|
2100
2673
|
const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
|
|
2101
|
-
const latest = (0,
|
|
2674
|
+
const latest = (0, import_react10.useRef)(options);
|
|
2102
2675
|
latest.current = options;
|
|
2103
|
-
const [visibleCount, setVisibleCount] = (0,
|
|
2104
|
-
const [stopRequested, setStopRequested] = (0,
|
|
2105
|
-
const isStreaming = status === "streaming";
|
|
2676
|
+
const [visibleCount, setVisibleCount] = (0, import_react10.useState)(initialVisibleCount);
|
|
2677
|
+
const [stopRequested, setStopRequested] = (0, import_react10.useState)(false);
|
|
2678
|
+
const isStreaming = status === "streaming" || status === "reconnecting";
|
|
2106
2679
|
const isBusy = status === "submitted" || isStreaming;
|
|
2107
2680
|
const isEmpty = messages.length === 0;
|
|
2108
|
-
(0,
|
|
2681
|
+
(0, import_react10.useEffect)(() => {
|
|
2109
2682
|
if (!isBusy) {
|
|
2110
2683
|
setStopRequested(false);
|
|
2111
2684
|
}
|
|
@@ -2122,16 +2695,16 @@ function useChatTranscript(options) {
|
|
|
2122
2695
|
streamingMessageId,
|
|
2123
2696
|
options
|
|
2124
2697
|
});
|
|
2125
|
-
const stop = (0,
|
|
2698
|
+
const stop = (0, import_react10.useCallback)(() => {
|
|
2126
2699
|
const current = latest.current;
|
|
2127
|
-
if (current.status !== "submitted" && current.status !== "streaming") {
|
|
2700
|
+
if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
|
|
2128
2701
|
return;
|
|
2129
2702
|
}
|
|
2130
2703
|
setStopRequested(true);
|
|
2131
2704
|
void current.onStop?.();
|
|
2132
2705
|
}, []);
|
|
2133
|
-
const loadEarlier = (0,
|
|
2134
|
-
const showAll = (0,
|
|
2706
|
+
const loadEarlier = (0, import_react10.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
|
|
2707
|
+
const showAll = (0, import_react10.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
|
|
2135
2708
|
const scroll = useStickToBottom({
|
|
2136
2709
|
contentKey: contentSignature(messages),
|
|
2137
2710
|
...stickToBottomThreshold !== void 0 ? {
|
|
@@ -2167,7 +2740,7 @@ function useChatTranscript(options) {
|
|
|
2167
2740
|
__name(useChatTranscript, "useChatTranscript");
|
|
2168
2741
|
function useTranscriptItem(options) {
|
|
2169
2742
|
const { message, isStreaming = false } = options;
|
|
2170
|
-
const messagesRef = (0,
|
|
2743
|
+
const messagesRef = (0, import_react10.useRef)([
|
|
2171
2744
|
message
|
|
2172
2745
|
]);
|
|
2173
2746
|
if (messagesRef.current[0] !== message) {
|
|
@@ -2185,13 +2758,13 @@ function useTranscriptItem(options) {
|
|
|
2185
2758
|
}
|
|
2186
2759
|
__name(useTranscriptItem, "useTranscriptItem");
|
|
2187
2760
|
function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
|
|
2188
|
-
const [drafts, setDrafts] = (0,
|
|
2189
|
-
const [copied, setCopied] = (0,
|
|
2190
|
-
const [openReasoning, setOpenReasoning] = (0,
|
|
2191
|
-
const [picks, setPicks] = (0,
|
|
2192
|
-
const [settling, setSettling] = (0,
|
|
2193
|
-
const [settleErrors, setSettleErrors] = (0,
|
|
2194
|
-
const latest = (0,
|
|
2761
|
+
const [drafts, setDrafts] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
|
|
2762
|
+
const [copied, setCopied] = (0, import_react10.useState)(() => /* @__PURE__ */ new Set());
|
|
2763
|
+
const [openReasoning, setOpenReasoning] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
|
|
2764
|
+
const [picks, setPicks] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
|
|
2765
|
+
const [settling, setSettling] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
|
|
2766
|
+
const [settleErrors, setSettleErrors] = (0, import_react10.useState)(() => /* @__PURE__ */ new Map());
|
|
2767
|
+
const latest = (0, import_react10.useRef)({
|
|
2195
2768
|
options,
|
|
2196
2769
|
drafts,
|
|
2197
2770
|
picks,
|
|
@@ -2203,23 +2776,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2203
2776
|
picks,
|
|
2204
2777
|
messages
|
|
2205
2778
|
};
|
|
2206
|
-
const copyTimers = (0,
|
|
2207
|
-
(0,
|
|
2779
|
+
const copyTimers = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
|
|
2780
|
+
(0, import_react10.useEffect)(() => () => {
|
|
2208
2781
|
for (const timer of copyTimers.current.values()) {
|
|
2209
2782
|
clearTimeout(timer);
|
|
2210
2783
|
}
|
|
2211
2784
|
copyTimers.current.clear();
|
|
2212
2785
|
}, []);
|
|
2213
|
-
const callbackCache = (0,
|
|
2214
|
-
const reasoningToggleCache = (0,
|
|
2215
|
-
const toggleReasoning = (0,
|
|
2786
|
+
const callbackCache = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
|
|
2787
|
+
const reasoningToggleCache = (0, import_react10.useRef)(/* @__PURE__ */ new Map());
|
|
2788
|
+
const toggleReasoning = (0, import_react10.useCallback)((key, open) => {
|
|
2216
2789
|
setOpenReasoning((current) => {
|
|
2217
2790
|
const next = new Map(current);
|
|
2218
2791
|
next.set(key, open ?? !(current.get(key) ?? false));
|
|
2219
2792
|
return next;
|
|
2220
2793
|
});
|
|
2221
2794
|
}, []);
|
|
2222
|
-
const stableToggle = (0,
|
|
2795
|
+
const stableToggle = (0, import_react10.useCallback)((key) => {
|
|
2223
2796
|
let cached = reasoningToggleCache.current.get(key);
|
|
2224
2797
|
if (!cached) {
|
|
2225
2798
|
cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
|
|
@@ -2229,10 +2802,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2229
2802
|
}, [
|
|
2230
2803
|
toggleReasoning
|
|
2231
2804
|
]);
|
|
2232
|
-
const pick = (0,
|
|
2805
|
+
const pick = (0, import_react10.useCallback)((toolCallId, questionId, values) => {
|
|
2233
2806
|
setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
|
|
2234
2807
|
}, []);
|
|
2235
|
-
const settle = (0,
|
|
2808
|
+
const settle = (0, import_react10.useCallback)((toolCallId, action, send) => {
|
|
2236
2809
|
setSettleErrors((current) => {
|
|
2237
2810
|
if (!current.has(toolCallId)) {
|
|
2238
2811
|
return current;
|
|
@@ -2256,7 +2829,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2256
2829
|
failed(error);
|
|
2257
2830
|
}
|
|
2258
2831
|
}, []);
|
|
2259
|
-
const answer = (0,
|
|
2832
|
+
const answer = (0, import_react10.useCallback)((toolCallId) => {
|
|
2260
2833
|
const current = latest.current;
|
|
2261
2834
|
const onAnswer = current.options.onAnswer;
|
|
2262
2835
|
if (!onAnswer) {
|
|
@@ -2266,7 +2839,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2266
2839
|
}, [
|
|
2267
2840
|
settle
|
|
2268
2841
|
]);
|
|
2269
|
-
const skip = (0,
|
|
2842
|
+
const skip = (0, import_react10.useCallback)((toolCallId) => {
|
|
2270
2843
|
const onSkip = latest.current.options.onSkip;
|
|
2271
2844
|
if (onSkip) {
|
|
2272
2845
|
settle(toolCallId, "skip", () => onSkip(toolCallId));
|
|
@@ -2274,7 +2847,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2274
2847
|
}, [
|
|
2275
2848
|
settle
|
|
2276
2849
|
]);
|
|
2277
|
-
const approve = (0,
|
|
2850
|
+
const approve = (0, import_react10.useCallback)((toolCallId, options2) => {
|
|
2278
2851
|
const onApprove = latest.current.options.onApprove;
|
|
2279
2852
|
if (onApprove) {
|
|
2280
2853
|
settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
|
|
@@ -2282,7 +2855,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2282
2855
|
}, [
|
|
2283
2856
|
settle
|
|
2284
2857
|
]);
|
|
2285
|
-
const reject = (0,
|
|
2858
|
+
const reject = (0, import_react10.useCallback)((toolCallId) => {
|
|
2286
2859
|
const onReject = latest.current.options.onReject;
|
|
2287
2860
|
if (onReject) {
|
|
2288
2861
|
settle(toolCallId, "reject", () => onReject(toolCallId));
|
|
@@ -2290,7 +2863,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2290
2863
|
}, [
|
|
2291
2864
|
settle
|
|
2292
2865
|
]);
|
|
2293
|
-
const callbacksFor = (0,
|
|
2866
|
+
const callbacksFor = (0, import_react10.useCallback)((id) => {
|
|
2294
2867
|
const cached = callbackCache.current.get(id);
|
|
2295
2868
|
if (cached) {
|
|
2296
2869
|
return cached;
|
|
@@ -2532,7 +3105,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
|
|
|
2532
3105
|
getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
|
|
2533
3106
|
} : {}
|
|
2534
3107
|
});
|
|
2535
|
-
return /* @__PURE__ */
|
|
3108
|
+
return /* @__PURE__ */ import_react11.default.createElement(MessageItemView, {
|
|
2536
3109
|
item,
|
|
2537
3110
|
...slots
|
|
2538
3111
|
});
|
|
@@ -2540,51 +3113,51 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
|
|
|
2540
3113
|
__name(MessageItem, "MessageItem");
|
|
2541
3114
|
function MessageItemView({ item, ...slots }) {
|
|
2542
3115
|
const { classNames } = slots;
|
|
2543
|
-
const actionRow = /* @__PURE__ */
|
|
3116
|
+
const actionRow = /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2544
3117
|
className: classNames?.actions,
|
|
2545
3118
|
"data-actions-for": item.role
|
|
2546
|
-
}, item.copy.available ? /* @__PURE__ */
|
|
3119
|
+
}, item.copy.available ? /* @__PURE__ */ import_react11.default.createElement("button", {
|
|
2547
3120
|
type: "button",
|
|
2548
3121
|
onClick: item.copy.copy,
|
|
2549
3122
|
className: classNames?.actionButton,
|
|
2550
3123
|
title: item.copy.copied ? "Copied!" : "Copy message text"
|
|
2551
|
-
}, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */
|
|
3124
|
+
}, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react11.default.createElement("button", {
|
|
2552
3125
|
type: "button",
|
|
2553
3126
|
onClick: item.fork.run,
|
|
2554
3127
|
className: classNames?.actionButton,
|
|
2555
3128
|
title: "Fork thread from this message"
|
|
2556
|
-
}, "Fork") : null, item.regenerate.available ? /* @__PURE__ */
|
|
3129
|
+
}, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react11.default.createElement("button", {
|
|
2557
3130
|
type: "button",
|
|
2558
3131
|
onClick: item.regenerate.run,
|
|
2559
3132
|
className: classNames?.actionButton,
|
|
2560
3133
|
title: "Regenerate response"
|
|
2561
|
-
}, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */
|
|
3134
|
+
}, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react11.default.createElement("span", {
|
|
2562
3135
|
className: classNames?.usage,
|
|
2563
3136
|
title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
|
|
2564
|
-
}, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */
|
|
3137
|
+
}, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react11.default.createElement("span", {
|
|
2565
3138
|
className: classNames?.timestamp,
|
|
2566
3139
|
title: item.timestamp.absolute
|
|
2567
3140
|
}, item.timestamp.relative) : null);
|
|
2568
3141
|
if (item.edit.available) {
|
|
2569
|
-
return /* @__PURE__ */
|
|
3142
|
+
return /* @__PURE__ */ import_react11.default.createElement(EditableUserBubble, {
|
|
2570
3143
|
item,
|
|
2571
3144
|
slots,
|
|
2572
3145
|
actions: actionRow
|
|
2573
3146
|
});
|
|
2574
3147
|
}
|
|
2575
3148
|
const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
|
|
2576
|
-
return /* @__PURE__ */
|
|
3149
|
+
return /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2577
3150
|
className,
|
|
2578
3151
|
"data-role": item.role,
|
|
2579
3152
|
"data-streaming": item.isStreaming ? "true" : void 0
|
|
2580
|
-
}, /* @__PURE__ */
|
|
3153
|
+
}, /* @__PURE__ */ import_react11.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
|
|
2581
3154
|
}
|
|
2582
3155
|
__name(MessageItemView, "MessageItemView");
|
|
2583
3156
|
function renderBlocks(item, slots) {
|
|
2584
3157
|
const nodes = [];
|
|
2585
3158
|
for (const block of item.blocks) {
|
|
2586
3159
|
if (block.kind === "text") {
|
|
2587
|
-
nodes.push(/* @__PURE__ */
|
|
3160
|
+
nodes.push(/* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2588
3161
|
key: block.key,
|
|
2589
3162
|
className: slots.classNames?.text
|
|
2590
3163
|
}, slots.renderText ? slots.renderText(block.text, {
|
|
@@ -2605,7 +3178,7 @@ function renderBlocks(item, slots) {
|
|
|
2605
3178
|
continue;
|
|
2606
3179
|
}
|
|
2607
3180
|
if (block.kind === "ui" && slots.renderUi) {
|
|
2608
|
-
nodes.push(/* @__PURE__ */
|
|
3181
|
+
nodes.push(/* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2609
3182
|
key: block.key,
|
|
2610
3183
|
"data-slot": "ui",
|
|
2611
3184
|
"data-component": block.component
|
|
@@ -2616,18 +3189,18 @@ function renderBlocks(item, slots) {
|
|
|
2616
3189
|
}
|
|
2617
3190
|
__name(renderBlocks, "renderBlocks");
|
|
2618
3191
|
function renderFilesBlock(block, slots) {
|
|
2619
|
-
return /* @__PURE__ */
|
|
3192
|
+
return /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2620
3193
|
key: block.key,
|
|
2621
3194
|
className: slots.classNames?.files,
|
|
2622
3195
|
"data-files": "true"
|
|
2623
|
-
}, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */
|
|
3196
|
+
}, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react11.default.createElement("a", {
|
|
2624
3197
|
key: file.url,
|
|
2625
3198
|
href: file.url,
|
|
2626
3199
|
target: "_blank",
|
|
2627
3200
|
rel: "noreferrer",
|
|
2628
3201
|
className: slots.classNames?.file,
|
|
2629
3202
|
title: file.filename ?? file.mediaType
|
|
2630
|
-
}, file.isImage ? /* @__PURE__ */
|
|
3203
|
+
}, file.isImage ? /* @__PURE__ */ import_react11.default.createElement("img", {
|
|
2631
3204
|
src: file.url,
|
|
2632
3205
|
alt: file.filename ?? "image",
|
|
2633
3206
|
className: slots.classNames?.fileImage
|
|
@@ -2635,17 +3208,17 @@ function renderFilesBlock(block, slots) {
|
|
|
2635
3208
|
}
|
|
2636
3209
|
__name(renderFilesBlock, "renderFilesBlock");
|
|
2637
3210
|
function renderReasoningBlock(block, slots) {
|
|
2638
|
-
return /* @__PURE__ */
|
|
3211
|
+
return /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2639
3212
|
key: block.key,
|
|
2640
3213
|
className: slots.classNames?.reasoning,
|
|
2641
3214
|
"data-reasoning": "true",
|
|
2642
3215
|
"data-streaming": block.isStreaming ? "true" : void 0
|
|
2643
|
-
}, /* @__PURE__ */
|
|
3216
|
+
}, /* @__PURE__ */ import_react11.default.createElement("button", {
|
|
2644
3217
|
type: "button",
|
|
2645
3218
|
className: slots.classNames?.reasoningToggle,
|
|
2646
3219
|
onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
|
|
2647
3220
|
"aria-expanded": block.isOpen
|
|
2648
|
-
}, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */
|
|
3221
|
+
}, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2649
3222
|
className: slots.classNames?.reasoningText
|
|
2650
3223
|
}, slots.renderReasoning ? slots.renderReasoning(block.text, {
|
|
2651
3224
|
isStreaming: block.isStreaming,
|
|
@@ -2656,7 +3229,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
|
|
|
2656
3229
|
function renderToolBlock(item, block, slots) {
|
|
2657
3230
|
if (slots.renderToolGroup) {
|
|
2658
3231
|
return [
|
|
2659
|
-
/* @__PURE__ */
|
|
3232
|
+
/* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, {
|
|
2660
3233
|
key: block.key
|
|
2661
3234
|
}, slots.renderToolGroup(block.parts, block.key))
|
|
2662
3235
|
];
|
|
@@ -2667,7 +3240,7 @@ function renderToolBlock(item, block, slots) {
|
|
|
2667
3240
|
const renderToolPart = slots.renderToolPart;
|
|
2668
3241
|
return block.parts.map((part) => {
|
|
2669
3242
|
const partKey = `${item.id}-${part.toolCallId}`;
|
|
2670
|
-
return /* @__PURE__ */
|
|
3243
|
+
return /* @__PURE__ */ import_react11.default.createElement(import_react11.default.Fragment, {
|
|
2671
3244
|
key: partKey
|
|
2672
3245
|
}, renderToolPart(part, partKey));
|
|
2673
3246
|
});
|
|
@@ -2677,14 +3250,14 @@ function EditableUserBubble({ item, slots, actions }) {
|
|
|
2677
3250
|
const { classNames } = slots;
|
|
2678
3251
|
const { edit } = item;
|
|
2679
3252
|
if (!edit.isEditing) {
|
|
2680
|
-
return /* @__PURE__ */
|
|
3253
|
+
return /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2681
3254
|
className: joinClasses(classNames?.root, classNames?.byRole?.user),
|
|
2682
3255
|
"data-role": "user"
|
|
2683
|
-
}, /* @__PURE__ */
|
|
3256
|
+
}, /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2684
3257
|
className: classNames?.text
|
|
2685
3258
|
}, slots.renderText ? slots.renderText(item.text, {
|
|
2686
3259
|
isStreaming: false
|
|
2687
|
-
}) : item.text), /* @__PURE__ */
|
|
3260
|
+
}) : item.text), /* @__PURE__ */ import_react11.default.createElement("button", {
|
|
2688
3261
|
type: "button",
|
|
2689
3262
|
onClick: edit.start,
|
|
2690
3263
|
className: classNames?.actionButton,
|
|
@@ -2692,18 +3265,18 @@ function EditableUserBubble({ item, slots, actions }) {
|
|
|
2692
3265
|
}, "Edit"), actions);
|
|
2693
3266
|
}
|
|
2694
3267
|
const textareaProps = edit.getTextareaProps();
|
|
2695
|
-
return /* @__PURE__ */
|
|
3268
|
+
return /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2696
3269
|
"data-editing": "true"
|
|
2697
|
-
}, /* @__PURE__ */
|
|
3270
|
+
}, /* @__PURE__ */ import_react11.default.createElement("textarea", {
|
|
2698
3271
|
...textareaProps,
|
|
2699
3272
|
rows: Math.min(8, edit.draft.split("\n").length + 1),
|
|
2700
3273
|
className: classNames?.editTextarea
|
|
2701
|
-
}), /* @__PURE__ */
|
|
3274
|
+
}), /* @__PURE__ */ import_react11.default.createElement("div", {
|
|
2702
3275
|
className: classNames?.editActions
|
|
2703
|
-
}, /* @__PURE__ */
|
|
3276
|
+
}, /* @__PURE__ */ import_react11.default.createElement("button", {
|
|
2704
3277
|
type: "button",
|
|
2705
3278
|
onClick: edit.cancel
|
|
2706
|
-
}, "Cancel"), /* @__PURE__ */
|
|
3279
|
+
}, "Cancel"), /* @__PURE__ */ import_react11.default.createElement("button", {
|
|
2707
3280
|
type: "button",
|
|
2708
3281
|
onClick: edit.save,
|
|
2709
3282
|
disabled: !edit.canSave
|
|
@@ -2913,7 +3486,11 @@ function createSkillsSource(options) {
|
|
|
2913
3486
|
if (cached) {
|
|
2914
3487
|
return cached;
|
|
2915
3488
|
}
|
|
2916
|
-
const
|
|
3489
|
+
const listSkills = options.client.listSkills;
|
|
3490
|
+
if (listSkills === void 0) {
|
|
3491
|
+
return Promise.reject(new AgentBackendUnsupportedError("listSkills"));
|
|
3492
|
+
}
|
|
3493
|
+
const pending = listSkills.call(options.client, threadId).then((entries) => entries.map(toItem)).catch((error) => {
|
|
2917
3494
|
byThread.delete(key);
|
|
2918
3495
|
throw error;
|
|
2919
3496
|
});
|
|
@@ -2943,35 +3520,35 @@ function toItem(entry) {
|
|
|
2943
3520
|
__name(toItem, "toItem");
|
|
2944
3521
|
|
|
2945
3522
|
// src/composer/use-composer-autocomplete.ts
|
|
2946
|
-
var
|
|
3523
|
+
var import_react12 = require("react");
|
|
2947
3524
|
function useComposerAutocomplete(options) {
|
|
2948
3525
|
const { value, sources, acceptOnTab = true } = options;
|
|
2949
|
-
const latest = (0,
|
|
3526
|
+
const latest = (0, import_react12.useRef)(options);
|
|
2950
3527
|
latest.current = options;
|
|
2951
|
-
const inputRef = (0,
|
|
2952
|
-
const [caret, setCaret] = (0,
|
|
2953
|
-
const [items, setItems] = (0,
|
|
2954
|
-
const [isLoading, setLoading] = (0,
|
|
2955
|
-
const [error, setError] = (0,
|
|
2956
|
-
const [activeIndex, setActiveIndex] = (0,
|
|
2957
|
-
const [dismissedIndex, setDismissedIndex] = (0,
|
|
3528
|
+
const inputRef = (0, import_react12.useRef)(null);
|
|
3529
|
+
const [caret, setCaret] = (0, import_react12.useState)(value.length);
|
|
3530
|
+
const [items, setItems] = (0, import_react12.useState)([]);
|
|
3531
|
+
const [isLoading, setLoading] = (0, import_react12.useState)(false);
|
|
3532
|
+
const [error, setError] = (0, import_react12.useState)(null);
|
|
3533
|
+
const [activeIndex, setActiveIndex] = (0, import_react12.useState)(0);
|
|
3534
|
+
const [dismissedIndex, setDismissedIndex] = (0, import_react12.useState)(null);
|
|
2958
3535
|
const safeCaret = Math.min(caret, value.length);
|
|
2959
|
-
const match = (0,
|
|
3536
|
+
const match = (0, import_react12.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
|
|
2960
3537
|
value,
|
|
2961
3538
|
safeCaret,
|
|
2962
3539
|
sources
|
|
2963
3540
|
]);
|
|
2964
3541
|
const isActive = match !== null && dismissedIndex !== match.index;
|
|
2965
|
-
const matchRef = (0,
|
|
3542
|
+
const matchRef = (0, import_react12.useRef)(match);
|
|
2966
3543
|
matchRef.current = match;
|
|
2967
|
-
const caretRef = (0,
|
|
3544
|
+
const caretRef = (0, import_react12.useRef)(safeCaret);
|
|
2968
3545
|
caretRef.current = safeCaret;
|
|
2969
|
-
const itemsRef = (0,
|
|
3546
|
+
const itemsRef = (0, import_react12.useRef)(items);
|
|
2970
3547
|
itemsRef.current = items;
|
|
2971
|
-
const activeIndexRef = (0,
|
|
3548
|
+
const activeIndexRef = (0, import_react12.useRef)(activeIndex);
|
|
2972
3549
|
activeIndexRef.current = activeIndex;
|
|
2973
3550
|
const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
|
|
2974
|
-
(0,
|
|
3551
|
+
(0, import_react12.useEffect)(() => {
|
|
2975
3552
|
const current = matchRef.current;
|
|
2976
3553
|
if (requestKey === null || current === null) {
|
|
2977
3554
|
setItems([]);
|
|
@@ -3019,25 +3596,25 @@ function useComposerAutocomplete(options) {
|
|
|
3019
3596
|
]);
|
|
3020
3597
|
const isOpen = isActive;
|
|
3021
3598
|
const activeItem = isOpen ? items[activeIndex] ?? null : null;
|
|
3022
|
-
const reactId = (0,
|
|
3599
|
+
const reactId = (0, import_react12.useId)();
|
|
3023
3600
|
const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
|
|
3024
|
-
const optionId = (0,
|
|
3601
|
+
const optionId = (0, import_react12.useCallback)((index) => `${listboxId}-option-${index}`, [
|
|
3025
3602
|
listboxId
|
|
3026
3603
|
]);
|
|
3027
|
-
const highlight = (0,
|
|
3604
|
+
const highlight = (0, import_react12.useCallback)((index) => {
|
|
3028
3605
|
const total = itemsRef.current.length;
|
|
3029
3606
|
if (total === 0) return;
|
|
3030
3607
|
setActiveIndex((index % total + total) % total);
|
|
3031
3608
|
}, []);
|
|
3032
|
-
const moveHighlight = (0,
|
|
3609
|
+
const moveHighlight = (0, import_react12.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
|
|
3033
3610
|
highlight
|
|
3034
3611
|
]);
|
|
3035
|
-
const dismiss = (0,
|
|
3612
|
+
const dismiss = (0, import_react12.useCallback)(() => {
|
|
3036
3613
|
const current = matchRef.current;
|
|
3037
3614
|
if (current) setDismissedIndex(current.index);
|
|
3038
3615
|
}, []);
|
|
3039
|
-
const pendingCaret = (0,
|
|
3040
|
-
const accept = (0,
|
|
3616
|
+
const pendingCaret = (0, import_react12.useRef)(null);
|
|
3617
|
+
const accept = (0, import_react12.useCallback)((item) => {
|
|
3041
3618
|
const current = matchRef.current;
|
|
3042
3619
|
if (!current) return;
|
|
3043
3620
|
const chosen = item ?? itemsRef.current[activeIndexRef.current];
|
|
@@ -3048,16 +3625,16 @@ function useComposerAutocomplete(options) {
|
|
|
3048
3625
|
setDismissedIndex(null);
|
|
3049
3626
|
pendingCaret.current = edit.caret;
|
|
3050
3627
|
}, []);
|
|
3051
|
-
(0,
|
|
3628
|
+
(0, import_react12.useLayoutEffect)(() => {
|
|
3052
3629
|
const position = pendingCaret.current;
|
|
3053
3630
|
if (position === null) return;
|
|
3054
3631
|
pendingCaret.current = null;
|
|
3055
3632
|
inputRef.current?.setSelectionRange(position, position);
|
|
3056
3633
|
});
|
|
3057
|
-
const readCaret = (0,
|
|
3634
|
+
const readCaret = (0, import_react12.useCallback)((element) => {
|
|
3058
3635
|
setCaret(element.selectionStart ?? element.value.length);
|
|
3059
3636
|
}, []);
|
|
3060
|
-
const onKeyDown = (0,
|
|
3637
|
+
const onKeyDown = (0, import_react12.useCallback)((event) => {
|
|
3061
3638
|
if (!isOpen) return;
|
|
3062
3639
|
const hasItems = itemsRef.current.length > 0;
|
|
3063
3640
|
switch (event.key) {
|
|
@@ -3095,23 +3672,23 @@ function useComposerAutocomplete(options) {
|
|
|
3095
3672
|
dismiss,
|
|
3096
3673
|
moveHighlight
|
|
3097
3674
|
]);
|
|
3098
|
-
const onChange = (0,
|
|
3675
|
+
const onChange = (0, import_react12.useCallback)((event) => {
|
|
3099
3676
|
readCaret(event.target);
|
|
3100
3677
|
const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
|
|
3101
3678
|
setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
|
|
3102
3679
|
}, [
|
|
3103
3680
|
readCaret
|
|
3104
3681
|
]);
|
|
3105
|
-
const onSelect = (0,
|
|
3682
|
+
const onSelect = (0, import_react12.useCallback)((event) => readCaret(event.currentTarget), [
|
|
3106
3683
|
readCaret
|
|
3107
3684
|
]);
|
|
3108
|
-
const onClick = (0,
|
|
3685
|
+
const onClick = (0, import_react12.useCallback)((event) => readCaret(event.currentTarget), [
|
|
3109
3686
|
readCaret
|
|
3110
3687
|
]);
|
|
3111
|
-
const setInput = (0,
|
|
3688
|
+
const setInput = (0, import_react12.useCallback)((element) => {
|
|
3112
3689
|
inputRef.current = element;
|
|
3113
3690
|
}, []);
|
|
3114
|
-
const getInputProps = (0,
|
|
3691
|
+
const getInputProps = (0, import_react12.useCallback)(() => ({
|
|
3115
3692
|
ref: setInput,
|
|
3116
3693
|
onChange,
|
|
3117
3694
|
onKeyDown,
|
|
@@ -3139,7 +3716,7 @@ function useComposerAutocomplete(options) {
|
|
|
3139
3716
|
activeIndex,
|
|
3140
3717
|
optionId
|
|
3141
3718
|
]);
|
|
3142
|
-
const getListboxProps = (0,
|
|
3719
|
+
const getListboxProps = (0, import_react12.useCallback)(() => ({
|
|
3143
3720
|
id: listboxId,
|
|
3144
3721
|
role: "listbox",
|
|
3145
3722
|
"aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
|
|
@@ -3147,7 +3724,7 @@ function useComposerAutocomplete(options) {
|
|
|
3147
3724
|
listboxId,
|
|
3148
3725
|
match
|
|
3149
3726
|
]);
|
|
3150
|
-
const getOptionProps = (0,
|
|
3727
|
+
const getOptionProps = (0, import_react12.useCallback)((index) => ({
|
|
3151
3728
|
id: optionId(index),
|
|
3152
3729
|
role: "option",
|
|
3153
3730
|
"aria-selected": index === activeIndex
|
|
@@ -3181,7 +3758,7 @@ function isPromise(value) {
|
|
|
3181
3758
|
__name(isPromise, "isPromise");
|
|
3182
3759
|
|
|
3183
3760
|
// src/presentation/use-tool-catalog.ts
|
|
3184
|
-
var
|
|
3761
|
+
var import_react13 = require("react");
|
|
3185
3762
|
var shared = /* @__PURE__ */ new WeakMap();
|
|
3186
3763
|
function load(client, agent) {
|
|
3187
3764
|
let byAgent = shared.get(client);
|
|
@@ -3192,6 +3769,9 @@ function load(client, agent) {
|
|
|
3192
3769
|
const key = agent ?? "";
|
|
3193
3770
|
const cached = byAgent.get(key);
|
|
3194
3771
|
if (cached !== void 0) return cached;
|
|
3772
|
+
if (client.listTools === void 0) {
|
|
3773
|
+
return Promise.reject(new AgentBackendUnsupportedError("listTools"));
|
|
3774
|
+
}
|
|
3195
3775
|
const request = client.listTools(agent).catch((error) => {
|
|
3196
3776
|
byAgent.delete(key);
|
|
3197
3777
|
throw error;
|
|
@@ -3202,11 +3782,11 @@ function load(client, agent) {
|
|
|
3202
3782
|
__name(load, "load");
|
|
3203
3783
|
function useToolCatalog(options) {
|
|
3204
3784
|
const { client, agent, enabled = true } = options;
|
|
3205
|
-
const [entries, setEntries] = (0,
|
|
3206
|
-
const [isLoading, setLoading] = (0,
|
|
3207
|
-
const [error, setError] = (0,
|
|
3208
|
-
const [generation, setGeneration] = (0,
|
|
3209
|
-
(0,
|
|
3785
|
+
const [entries, setEntries] = (0, import_react13.useState)([]);
|
|
3786
|
+
const [isLoading, setLoading] = (0, import_react13.useState)(enabled);
|
|
3787
|
+
const [error, setError] = (0, import_react13.useState)(null);
|
|
3788
|
+
const [generation, setGeneration] = (0, import_react13.useState)(0);
|
|
3789
|
+
(0, import_react13.useEffect)(() => {
|
|
3210
3790
|
if (!enabled) {
|
|
3211
3791
|
setLoading(false);
|
|
3212
3792
|
return;
|
|
@@ -3232,14 +3812,14 @@ function useToolCatalog(options) {
|
|
|
3232
3812
|
enabled,
|
|
3233
3813
|
generation
|
|
3234
3814
|
]);
|
|
3235
|
-
const refresh = (0,
|
|
3815
|
+
const refresh = (0, import_react13.useCallback)(() => {
|
|
3236
3816
|
shared.get(client)?.delete(agent ?? "");
|
|
3237
3817
|
setGeneration((current) => current + 1);
|
|
3238
3818
|
}, [
|
|
3239
3819
|
client,
|
|
3240
3820
|
agent
|
|
3241
3821
|
]);
|
|
3242
|
-
const catalog = (0,
|
|
3822
|
+
const catalog = (0, import_react13.useMemo)(() => toolCatalogFrom(entries), [
|
|
3243
3823
|
entries
|
|
3244
3824
|
]);
|
|
3245
3825
|
return {
|
|
@@ -3253,7 +3833,7 @@ function useToolCatalog(options) {
|
|
|
3253
3833
|
__name(useToolCatalog, "useToolCatalog");
|
|
3254
3834
|
|
|
3255
3835
|
// src/approvals/countdown.ts
|
|
3256
|
-
var
|
|
3836
|
+
var import_react14 = require("react");
|
|
3257
3837
|
function approvalCountdown(expiresAt, now = Date.now()) {
|
|
3258
3838
|
const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
|
|
3259
3839
|
if (!Number.isFinite(deadline)) {
|
|
@@ -3271,10 +3851,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
|
|
|
3271
3851
|
__name(approvalCountdown, "approvalCountdown");
|
|
3272
3852
|
function useApprovalCountdown(expiresAt, options = {}) {
|
|
3273
3853
|
const { intervalMs = 1e3 } = options;
|
|
3274
|
-
const nowRef = (0,
|
|
3854
|
+
const nowRef = (0, import_react14.useRef)(options.now ?? Date.now);
|
|
3275
3855
|
nowRef.current = options.now ?? Date.now;
|
|
3276
|
-
const [state, setState] = (0,
|
|
3277
|
-
(0,
|
|
3856
|
+
const [state, setState] = (0, import_react14.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
|
|
3857
|
+
(0, import_react14.useEffect)(() => {
|
|
3278
3858
|
const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
|
|
3279
3859
|
const first = read();
|
|
3280
3860
|
setState(first);
|
|
@@ -3440,7 +4020,12 @@ function storedMessageToUiMessage(message) {
|
|
|
3440
4020
|
return {
|
|
3441
4021
|
id: message.id,
|
|
3442
4022
|
role: message.role,
|
|
3443
|
-
parts
|
|
4023
|
+
parts,
|
|
4024
|
+
...message.feedback !== void 0 ? {
|
|
4025
|
+
metadata: {
|
|
4026
|
+
feedback: message.feedback
|
|
4027
|
+
}
|
|
4028
|
+
} : {}
|
|
3444
4029
|
};
|
|
3445
4030
|
}
|
|
3446
4031
|
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
@@ -3476,14 +4061,21 @@ function mergeTurn(rows) {
|
|
|
3476
4061
|
}
|
|
3477
4062
|
const parts = rows.flatMap((row) => storedMessageToUiMessage(row).parts);
|
|
3478
4063
|
const usage = sumUsage(rows);
|
|
4064
|
+
const feedback = lastRow.feedback;
|
|
4065
|
+
const metadata = {
|
|
4066
|
+
...usage !== void 0 ? {
|
|
4067
|
+
usage
|
|
4068
|
+
} : {},
|
|
4069
|
+
...feedback !== void 0 ? {
|
|
4070
|
+
feedback
|
|
4071
|
+
} : {}
|
|
4072
|
+
};
|
|
3479
4073
|
return {
|
|
3480
4074
|
id: lastRow.id,
|
|
3481
4075
|
role: lastRow.role,
|
|
3482
4076
|
parts,
|
|
3483
|
-
...
|
|
3484
|
-
metadata
|
|
3485
|
-
usage
|
|
3486
|
-
}
|
|
4077
|
+
...Object.keys(metadata).length > 0 ? {
|
|
4078
|
+
metadata
|
|
3487
4079
|
} : {}
|
|
3488
4080
|
};
|
|
3489
4081
|
}
|
|
@@ -3503,21 +4095,22 @@ function sumUsage(rows) {
|
|
|
3503
4095
|
__name(sumUsage, "sumUsage");
|
|
3504
4096
|
|
|
3505
4097
|
// src/use-agent-chat.ts
|
|
3506
|
-
var
|
|
3507
|
-
var
|
|
4098
|
+
var import_react15 = require("@ai-sdk/react");
|
|
4099
|
+
var import_react16 = require("react");
|
|
3508
4100
|
function useAgentChat(options) {
|
|
3509
|
-
const latest = (0,
|
|
4101
|
+
const latest = (0, import_react16.useRef)(options);
|
|
3510
4102
|
latest.current = options;
|
|
3511
|
-
const [runId, setRunId] = (0,
|
|
3512
|
-
const runIdRef = (0,
|
|
4103
|
+
const [runId, setRunId] = (0, import_react16.useState)(options.resumeRunId);
|
|
4104
|
+
const runIdRef = (0, import_react16.useRef)(runId);
|
|
3513
4105
|
runIdRef.current = runId;
|
|
3514
|
-
const settlingRunIdRef = (0,
|
|
3515
|
-
const autoResumeRunIdRef = (0,
|
|
3516
|
-
const [autoResume, setAutoResume] = (0,
|
|
3517
|
-
const [activeRunId, setActiveRunId] = (0,
|
|
3518
|
-
const regenerateNext = (0,
|
|
3519
|
-
const createdThreadId = (0,
|
|
3520
|
-
const client = (0,
|
|
4106
|
+
const settlingRunIdRef = (0, import_react16.useRef)(void 0);
|
|
4107
|
+
const autoResumeRunIdRef = (0, import_react16.useRef)(void 0);
|
|
4108
|
+
const [autoResume, setAutoResume] = (0, import_react16.useState)(false);
|
|
4109
|
+
const [activeRunId, setActiveRunId] = (0, import_react16.useState)(null);
|
|
4110
|
+
const regenerateNext = (0, import_react16.useRef)(false);
|
|
4111
|
+
const createdThreadId = (0, import_react16.useRef)(void 0);
|
|
4112
|
+
const client = (0, import_react16.useMemo)(() => {
|
|
4113
|
+
if (options.backend) return options.backend;
|
|
3521
4114
|
if (options.client) return options.client;
|
|
3522
4115
|
return new AgentClient({
|
|
3523
4116
|
...options.baseUrl !== void 0 ? {
|
|
@@ -3532,7 +4125,11 @@ function useAgentChat(options) {
|
|
|
3532
4125
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
3533
4126
|
});
|
|
3534
4127
|
}, []);
|
|
3535
|
-
(0,
|
|
4128
|
+
const [connection, setConnection] = (0, import_react16.useState)({
|
|
4129
|
+
status: "live"
|
|
4130
|
+
});
|
|
4131
|
+
const resyncAfterSettle = (0, import_react16.useRef)(false);
|
|
4132
|
+
(0, import_react16.useEffect)(() => {
|
|
3536
4133
|
if (!options.resume || options.threadId === void 0) return;
|
|
3537
4134
|
let cancelled = false;
|
|
3538
4135
|
const threadId = options.threadId;
|
|
@@ -3552,30 +4149,33 @@ function useAgentChat(options) {
|
|
|
3552
4149
|
options.resume,
|
|
3553
4150
|
options.threadId
|
|
3554
4151
|
]);
|
|
3555
|
-
const transport = (0,
|
|
4152
|
+
const transport = (0, import_react16.useMemo)(() => {
|
|
3556
4153
|
function onMeta(meta) {
|
|
3557
4154
|
settlingRunIdRef.current = meta.runId;
|
|
3558
4155
|
setRunId(meta.runId);
|
|
3559
4156
|
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
3560
4157
|
createdThreadId.current = meta.threadId;
|
|
3561
4158
|
latest.current.onThreadCreated?.(meta.threadId);
|
|
4159
|
+
notifyThreads(client, {
|
|
4160
|
+
type: "changed"
|
|
4161
|
+
});
|
|
3562
4162
|
}
|
|
3563
4163
|
}
|
|
3564
4164
|
__name(onMeta, "onMeta");
|
|
3565
4165
|
return new AgentChatTransport({
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
...options.credentials !== void 0 ? {
|
|
3570
|
-
credentials: options.credentials
|
|
3571
|
-
} : {},
|
|
3572
|
-
...options.fetch !== void 0 ? {
|
|
3573
|
-
fetch: options.fetch
|
|
4166
|
+
backend: client,
|
|
4167
|
+
...options.reconnect !== void 0 ? {
|
|
4168
|
+
reconnect: options.reconnect
|
|
3574
4169
|
} : {},
|
|
4170
|
+
onConnectionChange: /* @__PURE__ */ __name((state) => {
|
|
4171
|
+
if (state.status === "gone") resyncAfterSettle.current = true;
|
|
4172
|
+
setConnection(state.status === "gone" ? {
|
|
4173
|
+
status: "live"
|
|
4174
|
+
} : state);
|
|
4175
|
+
}, "onConnectionChange"),
|
|
3575
4176
|
...options.agent !== void 0 ? {
|
|
3576
4177
|
agent: options.agent
|
|
3577
4178
|
} : {},
|
|
3578
|
-
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders"),
|
|
3579
4179
|
getBody: /* @__PURE__ */ __name(() => {
|
|
3580
4180
|
const current = latest.current;
|
|
3581
4181
|
const pageContext = current.getPageContext?.() ?? null;
|
|
@@ -3586,6 +4186,9 @@ function useAgentChat(options) {
|
|
|
3586
4186
|
...threadId !== void 0 ? {
|
|
3587
4187
|
threadId
|
|
3588
4188
|
} : {},
|
|
4189
|
+
...current.model !== void 0 ? {
|
|
4190
|
+
model: current.model
|
|
4191
|
+
} : {},
|
|
3589
4192
|
...pageContext ? {
|
|
3590
4193
|
pageContext
|
|
3591
4194
|
} : {},
|
|
@@ -3601,8 +4204,8 @@ function useAgentChat(options) {
|
|
|
3601
4204
|
}, "onAttemptStart")
|
|
3602
4205
|
});
|
|
3603
4206
|
}, []);
|
|
3604
|
-
const backgroundRef = (0,
|
|
3605
|
-
const chat = (0,
|
|
4207
|
+
const backgroundRef = (0, import_react16.useRef)(() => Promise.resolve());
|
|
4208
|
+
const chat = (0, import_react15.useChat)({
|
|
3606
4209
|
transport,
|
|
3607
4210
|
resume: options.resumeRunId !== void 0 || autoResume,
|
|
3608
4211
|
...options.threadId !== void 0 ? {
|
|
@@ -3615,7 +4218,15 @@ function useAgentChat(options) {
|
|
|
3615
4218
|
latest.current.onData?.(part);
|
|
3616
4219
|
if (part.type === "data-title") {
|
|
3617
4220
|
const title = part.data?.title;
|
|
3618
|
-
if (typeof title === "string")
|
|
4221
|
+
if (typeof title === "string") {
|
|
4222
|
+
latest.current.onTitle?.(title);
|
|
4223
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4224
|
+
if (threadId !== void 0) notifyThreads(client, {
|
|
4225
|
+
type: "title",
|
|
4226
|
+
threadId,
|
|
4227
|
+
title
|
|
4228
|
+
});
|
|
4229
|
+
}
|
|
3619
4230
|
}
|
|
3620
4231
|
}, "onData"),
|
|
3621
4232
|
onFinish: /* @__PURE__ */ __name(({ isError }) => {
|
|
@@ -3629,16 +4240,36 @@ function useAgentChat(options) {
|
|
|
3629
4240
|
runId: settledRunId,
|
|
3630
4241
|
status: isError ? "failed" : "completed"
|
|
3631
4242
|
});
|
|
4243
|
+
notifyThreads(client, {
|
|
4244
|
+
type: "changed"
|
|
4245
|
+
});
|
|
4246
|
+
}
|
|
4247
|
+
setConnection({
|
|
4248
|
+
status: "live"
|
|
4249
|
+
});
|
|
4250
|
+
if (resyncAfterSettle.current) {
|
|
4251
|
+
resyncAfterSettle.current = false;
|
|
4252
|
+
void resyncFromThread();
|
|
3632
4253
|
}
|
|
3633
4254
|
}, "onFinish")
|
|
3634
4255
|
});
|
|
3635
|
-
const chatRef = (0,
|
|
4256
|
+
const chatRef = (0, import_react16.useRef)(chat);
|
|
3636
4257
|
chatRef.current = chat;
|
|
3637
|
-
|
|
3638
|
-
|
|
4258
|
+
async function resyncFromThread() {
|
|
4259
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4260
|
+
if (threadId === void 0) return;
|
|
4261
|
+
try {
|
|
4262
|
+
const thread = await client.getThread(threadId);
|
|
4263
|
+
chatRef.current.setMessages(storedThreadToUiMessages(thread.messages));
|
|
4264
|
+
} catch {
|
|
4265
|
+
}
|
|
4266
|
+
}
|
|
4267
|
+
__name(resyncFromThread, "resyncFromThread");
|
|
4268
|
+
const turnInFlight = (0, import_react16.useRef)(false);
|
|
4269
|
+
const isTurnInFlight = (0, import_react16.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
|
|
3639
4270
|
transport
|
|
3640
4271
|
]);
|
|
3641
|
-
const sendMessage = (0,
|
|
4272
|
+
const sendMessage = (0, import_react16.useCallback)(async (...args) => {
|
|
3642
4273
|
if (isTurnInFlight()) return;
|
|
3643
4274
|
turnInFlight.current = true;
|
|
3644
4275
|
try {
|
|
@@ -3649,15 +4280,15 @@ function useAgentChat(options) {
|
|
|
3649
4280
|
}, [
|
|
3650
4281
|
isTurnInFlight
|
|
3651
4282
|
]);
|
|
3652
|
-
const addToolResult = (0,
|
|
4283
|
+
const addToolResult = (0, import_react16.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
3653
4284
|
tool,
|
|
3654
4285
|
toolCallId,
|
|
3655
4286
|
output
|
|
3656
4287
|
}), []);
|
|
3657
|
-
const [threads, setThreads] = (0,
|
|
3658
|
-
const [quota, setQuota] = (0,
|
|
3659
|
-
const [backgroundRuns, setBackgroundRuns] = (0,
|
|
3660
|
-
const refreshBackground = (0,
|
|
4288
|
+
const [threads, setThreads] = (0, import_react16.useState)([]);
|
|
4289
|
+
const [quota, setQuota] = (0, import_react16.useState)(null);
|
|
4290
|
+
const [backgroundRuns, setBackgroundRuns] = (0, import_react16.useState)([]);
|
|
4291
|
+
const refreshBackground = (0, import_react16.useCallback)(async () => {
|
|
3661
4292
|
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
3662
4293
|
if (threadId === void 0) {
|
|
3663
4294
|
return;
|
|
@@ -3682,7 +4313,7 @@ function useAgentChat(options) {
|
|
|
3682
4313
|
]);
|
|
3683
4314
|
backgroundRef.current = refreshBackground;
|
|
3684
4315
|
const isWorking = backgroundRuns.some((run) => run.status === "running");
|
|
3685
|
-
(0,
|
|
4316
|
+
(0, import_react16.useEffect)(() => {
|
|
3686
4317
|
if (options.background !== true || options.threadId === void 0) {
|
|
3687
4318
|
return;
|
|
3688
4319
|
}
|
|
@@ -3692,7 +4323,7 @@ function useAgentChat(options) {
|
|
|
3692
4323
|
options.threadId,
|
|
3693
4324
|
refreshBackground
|
|
3694
4325
|
]);
|
|
3695
|
-
(0,
|
|
4326
|
+
(0, import_react16.useEffect)(() => {
|
|
3696
4327
|
const every = latest.current.backgroundPollMs ?? 5e3;
|
|
3697
4328
|
if (latest.current.background !== true || !isWorking || every <= 0) {
|
|
3698
4329
|
return;
|
|
@@ -3703,27 +4334,29 @@ function useAgentChat(options) {
|
|
|
3703
4334
|
isWorking,
|
|
3704
4335
|
refreshBackground
|
|
3705
4336
|
]);
|
|
3706
|
-
const loadThreads = (0,
|
|
4337
|
+
const loadThreads = (0, import_react16.useCallback)(async () => {
|
|
3707
4338
|
const list = await client.listThreads();
|
|
3708
4339
|
setThreads(list);
|
|
3709
4340
|
return list;
|
|
3710
4341
|
}, [
|
|
3711
4342
|
client
|
|
3712
4343
|
]);
|
|
3713
|
-
const loadThread = (0,
|
|
4344
|
+
const loadThread = (0, import_react16.useCallback)((id) => client.getThread(id), [
|
|
3714
4345
|
client
|
|
3715
4346
|
]);
|
|
3716
|
-
const deleteThread = (0,
|
|
4347
|
+
const deleteThread = (0, import_react16.useCallback)(async (id) => {
|
|
3717
4348
|
await client.deleteThread(id);
|
|
3718
4349
|
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
3719
4350
|
}, [
|
|
3720
4351
|
client
|
|
3721
4352
|
]);
|
|
3722
|
-
const forkThread = (0,
|
|
4353
|
+
const forkThread = (0, import_react16.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
|
|
3723
4354
|
client
|
|
3724
4355
|
]);
|
|
3725
|
-
const renameThread = (0,
|
|
3726
|
-
await client.
|
|
4356
|
+
const renameThread = (0, import_react16.useCallback)(async (id, title) => {
|
|
4357
|
+
await client.updateThread(id, {
|
|
4358
|
+
title
|
|
4359
|
+
});
|
|
3727
4360
|
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
3728
4361
|
...thread,
|
|
3729
4362
|
title
|
|
@@ -3731,24 +4364,24 @@ function useAgentChat(options) {
|
|
|
3731
4364
|
}, [
|
|
3732
4365
|
client
|
|
3733
4366
|
]);
|
|
3734
|
-
const promoteThread = (0,
|
|
3735
|
-
await client
|
|
4367
|
+
const promoteThread = (0, import_react16.useCallback)(async (id) => {
|
|
4368
|
+
await requireBackendMethod(client, "promoteThread")(id);
|
|
3736
4369
|
await loadThreads();
|
|
3737
4370
|
}, [
|
|
3738
4371
|
client,
|
|
3739
4372
|
loadThreads
|
|
3740
4373
|
]);
|
|
3741
|
-
const truncateFromMessage = (0,
|
|
4374
|
+
const truncateFromMessage = (0, import_react16.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
|
|
3742
4375
|
client
|
|
3743
4376
|
]);
|
|
3744
|
-
const loadQuota = (0,
|
|
3745
|
-
const today = await client
|
|
4377
|
+
const loadQuota = (0, import_react16.useCallback)(async () => {
|
|
4378
|
+
const today = await requireBackendMethod(client, "getQuotaToday")();
|
|
3746
4379
|
setQuota(today);
|
|
3747
4380
|
return today;
|
|
3748
4381
|
}, [
|
|
3749
4382
|
client
|
|
3750
4383
|
]);
|
|
3751
|
-
const cancel = (0,
|
|
4384
|
+
const cancel = (0, import_react16.useCallback)(async () => {
|
|
3752
4385
|
chatRef.current.stop();
|
|
3753
4386
|
const activeRunId2 = runIdRef.current;
|
|
3754
4387
|
if (activeRunId2) {
|
|
@@ -3760,8 +4393,8 @@ function useAgentChat(options) {
|
|
|
3760
4393
|
}, [
|
|
3761
4394
|
client
|
|
3762
4395
|
]);
|
|
3763
|
-
const approve = (0,
|
|
3764
|
-
await client
|
|
4396
|
+
const approve = (0, import_react16.useCallback)(async ({ toolCallId, remember, via }) => {
|
|
4397
|
+
await requireBackendMethod(client, "approveToolCall")({
|
|
3765
4398
|
toolCallId,
|
|
3766
4399
|
...remember === true ? {
|
|
3767
4400
|
remember: true
|
|
@@ -3773,8 +4406,8 @@ function useAgentChat(options) {
|
|
|
3773
4406
|
}, [
|
|
3774
4407
|
client
|
|
3775
4408
|
]);
|
|
3776
|
-
const reject = (0,
|
|
3777
|
-
await client
|
|
4409
|
+
const reject = (0, import_react16.useCallback)(async ({ toolCallId, reason, via }) => {
|
|
4410
|
+
await requireBackendMethod(client, "rejectToolCall")({
|
|
3778
4411
|
toolCallId,
|
|
3779
4412
|
...reason !== void 0 ? {
|
|
3780
4413
|
reason
|
|
@@ -3786,8 +4419,8 @@ function useAgentChat(options) {
|
|
|
3786
4419
|
}, [
|
|
3787
4420
|
client
|
|
3788
4421
|
]);
|
|
3789
|
-
const answer = (0,
|
|
3790
|
-
await client
|
|
4422
|
+
const answer = (0, import_react16.useCallback)(async ({ toolCallId, answers }) => {
|
|
4423
|
+
await requireBackendMethod(client, "answerToolCall")({
|
|
3791
4424
|
toolCallId,
|
|
3792
4425
|
...answers !== void 0 ? {
|
|
3793
4426
|
answers
|
|
@@ -3796,14 +4429,14 @@ function useAgentChat(options) {
|
|
|
3796
4429
|
}, [
|
|
3797
4430
|
client
|
|
3798
4431
|
]);
|
|
3799
|
-
const skip = (0,
|
|
3800
|
-
await client
|
|
4432
|
+
const skip = (0, import_react16.useCallback)(async ({ toolCallId }) => {
|
|
4433
|
+
await requireBackendMethod(client, "skipToolCall")({
|
|
3801
4434
|
toolCallId
|
|
3802
4435
|
});
|
|
3803
4436
|
}, [
|
|
3804
4437
|
client
|
|
3805
4438
|
]);
|
|
3806
|
-
const regenerate = (0,
|
|
4439
|
+
const regenerate = (0, import_react16.useCallback)(() => {
|
|
3807
4440
|
if (isTurnInFlight()) return;
|
|
3808
4441
|
regenerateNext.current = true;
|
|
3809
4442
|
turnInFlight.current = true;
|
|
@@ -3818,8 +4451,27 @@ function useAgentChat(options) {
|
|
|
3818
4451
|
isWorking,
|
|
3819
4452
|
refresh: refreshBackground
|
|
3820
4453
|
};
|
|
4454
|
+
const setThreadModel = (0, import_react16.useCallback)(async (model) => {
|
|
4455
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4456
|
+
if (threadId === void 0) {
|
|
4457
|
+
throw new Error("setThreadModel: this chat has no thread yet");
|
|
4458
|
+
}
|
|
4459
|
+
await client.updateThread(threadId, {
|
|
4460
|
+
model
|
|
4461
|
+
});
|
|
4462
|
+
notifyThreads(client, {
|
|
4463
|
+
type: "changed"
|
|
4464
|
+
});
|
|
4465
|
+
}, [
|
|
4466
|
+
client
|
|
4467
|
+
]);
|
|
4468
|
+
const getThreadId = (0, import_react16.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
|
|
4469
|
+
const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
|
|
3821
4470
|
return {
|
|
3822
4471
|
...chat,
|
|
4472
|
+
status,
|
|
4473
|
+
/** Where the live stream stands — see {@link StreamConnectionState}. */
|
|
4474
|
+
connection,
|
|
3823
4475
|
sendMessage,
|
|
3824
4476
|
addToolResult,
|
|
3825
4477
|
/** Sub-agents this conversation started and did not wait for. */
|
|
@@ -3827,6 +4479,15 @@ function useAgentChat(options) {
|
|
|
3827
4479
|
runId,
|
|
3828
4480
|
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
3829
4481
|
activeRunId,
|
|
4482
|
+
/**
|
|
4483
|
+
* The thread this chat is on right now: the `threadId` option, else the one the backend created
|
|
4484
|
+
* on the first send. A getter, because the created id is learned mid-stream.
|
|
4485
|
+
*/
|
|
4486
|
+
getThreadId,
|
|
4487
|
+
setThreadModel,
|
|
4488
|
+
/** The backend this chat talks to — pass it to `useThreads`, `useMessageFeedback`, … */
|
|
4489
|
+
backend: client,
|
|
4490
|
+
/** Same object as {@link backend}; kept for callers that read it under its old name. */
|
|
3830
4491
|
client,
|
|
3831
4492
|
threads,
|
|
3832
4493
|
loadThreads,
|
|
@@ -3857,6 +4518,7 @@ async function mergeHeaders(options) {
|
|
|
3857
4518
|
__name(mergeHeaders, "mergeHeaders");
|
|
3858
4519
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3859
4520
|
0 && (module.exports = {
|
|
4521
|
+
AgentBackendUnsupportedError,
|
|
3860
4522
|
AgentChatTransport,
|
|
3861
4523
|
AgentClient,
|
|
3862
4524
|
AgentHttpError,
|
|
@@ -3886,22 +4548,29 @@ __name(mergeHeaders, "mergeHeaders");
|
|
|
3886
4548
|
inferResultView,
|
|
3887
4549
|
isActionCall,
|
|
3888
4550
|
loadStoredSpeechLang,
|
|
4551
|
+
notifyThreads,
|
|
4552
|
+
onThreadsEvent,
|
|
3889
4553
|
persistSpeechLang,
|
|
3890
4554
|
phraseFor,
|
|
3891
4555
|
readPath,
|
|
3892
4556
|
readReasoningMs,
|
|
4557
|
+
requireBackendMethod,
|
|
3893
4558
|
resolveResultView,
|
|
3894
4559
|
storedMessageToUiMessage,
|
|
3895
4560
|
storedThreadToUiMessages,
|
|
3896
4561
|
toolCallState,
|
|
3897
4562
|
toolCatalogFrom,
|
|
3898
4563
|
useAgentChat,
|
|
4564
|
+
useAgents,
|
|
3899
4565
|
useApprovalCountdown,
|
|
3900
4566
|
useChatTranscript,
|
|
3901
4567
|
useComposerAutocomplete,
|
|
3902
4568
|
useElapsed,
|
|
4569
|
+
useMessageFeedback,
|
|
4570
|
+
useModels,
|
|
3903
4571
|
useSpeechRecognition,
|
|
3904
4572
|
useStickToBottom,
|
|
4573
|
+
useThreads,
|
|
3905
4574
|
useToolCatalog,
|
|
3906
4575
|
useTranscriptItem,
|
|
3907
4576
|
validateAnswer,
|