@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.js
CHANGED
|
@@ -1,6 +1,255 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
|
+
// src/client.ts
|
|
5
|
+
var AgentHttpError = class extends Error {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "AgentHttpError");
|
|
8
|
+
}
|
|
9
|
+
status;
|
|
10
|
+
method;
|
|
11
|
+
path;
|
|
12
|
+
constructor(status, method, path, statusText) {
|
|
13
|
+
super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
|
|
14
|
+
this.name = "AgentHttpError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var HEADER_RUN_ID = "x-agent-run-id";
|
|
18
|
+
var HEADER_THREAD_ID = "x-agent-thread-id";
|
|
19
|
+
var AgentClient = class {
|
|
20
|
+
static {
|
|
21
|
+
__name(this, "AgentClient");
|
|
22
|
+
}
|
|
23
|
+
options;
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.options = options;
|
|
26
|
+
}
|
|
27
|
+
/** `POST /agent/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
|
|
28
|
+
async openChatStream(request) {
|
|
29
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: {
|
|
32
|
+
"content-type": "application/json",
|
|
33
|
+
accept: "text/event-stream",
|
|
34
|
+
...await this.resolveHeaders(),
|
|
35
|
+
...request.headers
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify(request.body),
|
|
38
|
+
...this.credentials(),
|
|
39
|
+
...request.signal !== void 0 ? {
|
|
40
|
+
signal: request.signal
|
|
41
|
+
} : {}
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok || !response.body) {
|
|
44
|
+
throw new AgentHttpError(response.status, "POST", "/agent/chat", response.statusText);
|
|
45
|
+
}
|
|
46
|
+
return streamResponse(response);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `GET /agent/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
|
|
50
|
+
* streaming under that id (404).
|
|
51
|
+
*/
|
|
52
|
+
async resumeChatStream(request) {
|
|
53
|
+
const path = `/agent/chat/${encodeURIComponent(request.runId)}/stream`;
|
|
54
|
+
const query = request.after !== void 0 && request.after > 0 ? `?after=${request.after}` : "";
|
|
55
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}${query}`, {
|
|
56
|
+
method: "GET",
|
|
57
|
+
headers: {
|
|
58
|
+
accept: "text/event-stream",
|
|
59
|
+
...await this.resolveHeaders(),
|
|
60
|
+
...request.headers
|
|
61
|
+
},
|
|
62
|
+
...this.credentials(),
|
|
63
|
+
...request.signal !== void 0 ? {
|
|
64
|
+
signal: request.signal
|
|
65
|
+
} : {}
|
|
66
|
+
});
|
|
67
|
+
if (response.status === 404) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (!response.ok || !response.body) {
|
|
71
|
+
throw new AgentHttpError(response.status, "GET", path, response.statusText);
|
|
72
|
+
}
|
|
73
|
+
return streamResponse(response);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Rate a message (`'up'`/`'down'`, optional comment) or clear its rating (`value: null`). Answers
|
|
77
|
+
* the stored rating.
|
|
78
|
+
*/
|
|
79
|
+
setMessageFeedback(messageId, input) {
|
|
80
|
+
return this.request("POST", `/agent/messages/${encodeURIComponent(messageId)}/feedback`, input);
|
|
81
|
+
}
|
|
82
|
+
listThreads() {
|
|
83
|
+
return this.request("GET", "/agent/threads");
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
87
|
+
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
88
|
+
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
89
|
+
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
90
|
+
*/
|
|
91
|
+
listSkills(threadId) {
|
|
92
|
+
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
93
|
+
return this.request("GET", `/agent/skills${query}`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
|
|
97
|
+
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
|
|
98
|
+
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
|
|
99
|
+
*/
|
|
100
|
+
listTools(agent) {
|
|
101
|
+
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
102
|
+
return this.request("GET", `/agent/tools${query}`);
|
|
103
|
+
}
|
|
104
|
+
getThread(id) {
|
|
105
|
+
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
106
|
+
}
|
|
107
|
+
deleteThread(id) {
|
|
108
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
|
|
109
|
+
}
|
|
110
|
+
forkFromMessage(threadId, messageId) {
|
|
111
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
112
|
+
}
|
|
113
|
+
renameThread(id, title) {
|
|
114
|
+
return this.updateThread(id, {
|
|
115
|
+
title
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
119
|
+
updateThread(id, patch) {
|
|
120
|
+
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
124
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
125
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
126
|
+
*/
|
|
127
|
+
async uploadAttachment(file, options = {}) {
|
|
128
|
+
const formData = new FormData();
|
|
129
|
+
formData.append("file", file);
|
|
130
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: {
|
|
133
|
+
accept: "application/json",
|
|
134
|
+
...await this.resolveHeaders()
|
|
135
|
+
},
|
|
136
|
+
body: formData,
|
|
137
|
+
...this.credentials(),
|
|
138
|
+
...options.signal !== void 0 ? {
|
|
139
|
+
signal: options.signal
|
|
140
|
+
} : {}
|
|
141
|
+
});
|
|
142
|
+
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
143
|
+
}
|
|
144
|
+
promoteThread(id) {
|
|
145
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
146
|
+
}
|
|
147
|
+
truncateFromMessage(threadId, messageId) {
|
|
148
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
|
|
149
|
+
}
|
|
150
|
+
/** `GET /agent/models?agent=` — the models this caller may pick, grouped by provider. */
|
|
151
|
+
listModels(agent) {
|
|
152
|
+
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
153
|
+
return this.request("GET", `/agent/models${query}`);
|
|
154
|
+
}
|
|
155
|
+
/** `GET /agent/agents` — the registered agents, the default one flagged. */
|
|
156
|
+
listAgents() {
|
|
157
|
+
return this.request("GET", "/agent/agents");
|
|
158
|
+
}
|
|
159
|
+
getQuotaToday() {
|
|
160
|
+
return this.request("GET", "/agent/quota/today");
|
|
161
|
+
}
|
|
162
|
+
cancelStream(runId) {
|
|
163
|
+
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
167
|
+
* the decision came through (the server records `'web'` when omitted).
|
|
168
|
+
*/
|
|
169
|
+
approveToolCall(input) {
|
|
170
|
+
return this.request("POST", "/agent/tool-call/approve", input);
|
|
171
|
+
}
|
|
172
|
+
rejectToolCall(input) {
|
|
173
|
+
return this.request("POST", "/agent/tool-call/reject", input);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
177
|
+
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
178
|
+
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
179
|
+
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
180
|
+
*/
|
|
181
|
+
answerToolCall(input) {
|
|
182
|
+
return this.request("POST", "/agent/tool-call/answer", input);
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
186
|
+
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
187
|
+
* evidence the user chose them.
|
|
188
|
+
*/
|
|
189
|
+
skipToolCall(input) {
|
|
190
|
+
return this.request("POST", "/agent/tool-call/skip", input);
|
|
191
|
+
}
|
|
192
|
+
fetchImpl() {
|
|
193
|
+
return this.options.fetch ?? globalThis.fetch;
|
|
194
|
+
}
|
|
195
|
+
baseUrl() {
|
|
196
|
+
return (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
197
|
+
}
|
|
198
|
+
async resolveHeaders() {
|
|
199
|
+
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
200
|
+
return {
|
|
201
|
+
...this.options.headers,
|
|
202
|
+
...dynamic
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
credentials() {
|
|
206
|
+
return this.options.credentials !== void 0 ? {
|
|
207
|
+
credentials: this.options.credentials
|
|
208
|
+
} : {};
|
|
209
|
+
}
|
|
210
|
+
async request(method, path, body) {
|
|
211
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}`, {
|
|
212
|
+
method,
|
|
213
|
+
headers: {
|
|
214
|
+
accept: "application/json",
|
|
215
|
+
...body !== void 0 ? {
|
|
216
|
+
"content-type": "application/json"
|
|
217
|
+
} : {},
|
|
218
|
+
...await this.resolveHeaders()
|
|
219
|
+
},
|
|
220
|
+
...body !== void 0 ? {
|
|
221
|
+
body: JSON.stringify(body)
|
|
222
|
+
} : {},
|
|
223
|
+
...this.credentials()
|
|
224
|
+
});
|
|
225
|
+
return this.handleResponse(response, method, path);
|
|
226
|
+
}
|
|
227
|
+
async handleResponse(response, method, path) {
|
|
228
|
+
if (!response.ok) {
|
|
229
|
+
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
230
|
+
}
|
|
231
|
+
if (response.status === 204) return void 0;
|
|
232
|
+
const text = await response.text();
|
|
233
|
+
if (!text) return void 0;
|
|
234
|
+
return JSON.parse(text);
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
function streamResponse(response) {
|
|
238
|
+
const body = response.body;
|
|
239
|
+
const runId = response.headers?.get(HEADER_RUN_ID) ?? void 0;
|
|
240
|
+
const threadId = response.headers?.get(HEADER_THREAD_ID) ?? void 0;
|
|
241
|
+
return {
|
|
242
|
+
body,
|
|
243
|
+
...runId ? {
|
|
244
|
+
runId
|
|
245
|
+
} : {},
|
|
246
|
+
...threadId ? {
|
|
247
|
+
threadId
|
|
248
|
+
} : {}
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
__name(streamResponse, "streamResponse");
|
|
252
|
+
|
|
4
253
|
// src/reasoning/timing.ts
|
|
5
254
|
import { useEffect, useRef, useState } from "react";
|
|
6
255
|
var REASONING_METADATA_KEY = "agent";
|
|
@@ -61,8 +310,11 @@ function formatElapsed(ms) {
|
|
|
61
310
|
__name(formatElapsed, "formatElapsed");
|
|
62
311
|
|
|
63
312
|
// src/agent-chat-transport.ts
|
|
64
|
-
var
|
|
65
|
-
|
|
313
|
+
var DEFAULT_RECONNECT = {
|
|
314
|
+
maxAttempts: 6,
|
|
315
|
+
baseDelayMs: 500,
|
|
316
|
+
maxDelayMs: 8e3
|
|
317
|
+
};
|
|
66
318
|
var ASK_TOOL_NAME = "ask";
|
|
67
319
|
var AgentChatTransport = class {
|
|
68
320
|
static {
|
|
@@ -79,8 +331,26 @@ var AgentChatTransport = class {
|
|
|
79
331
|
* several entries under each id — which React renders as duplicate keys.
|
|
80
332
|
*/
|
|
81
333
|
attemptLive = false;
|
|
334
|
+
backend;
|
|
82
335
|
constructor(options = {}) {
|
|
83
336
|
this.options = options;
|
|
337
|
+
this.backend = options.backend ?? new AgentClient({
|
|
338
|
+
...options.baseUrl !== void 0 ? {
|
|
339
|
+
baseUrl: options.baseUrl
|
|
340
|
+
} : {},
|
|
341
|
+
...options.headers !== void 0 ? {
|
|
342
|
+
headers: options.headers
|
|
343
|
+
} : {},
|
|
344
|
+
...options.getHeaders !== void 0 ? {
|
|
345
|
+
getHeaders: options.getHeaders
|
|
346
|
+
} : {},
|
|
347
|
+
...options.credentials !== void 0 ? {
|
|
348
|
+
credentials: options.credentials
|
|
349
|
+
} : {},
|
|
350
|
+
...options.fetch !== void 0 ? {
|
|
351
|
+
fetch: options.fetch
|
|
352
|
+
} : {}
|
|
353
|
+
});
|
|
84
354
|
}
|
|
85
355
|
/** Run id of the most recent stream — HITL approve/reject target this. */
|
|
86
356
|
get runId() {
|
|
@@ -113,26 +383,25 @@ var AgentChatTransport = class {
|
|
|
113
383
|
...options.body ?? {},
|
|
114
384
|
message
|
|
115
385
|
};
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
386
|
+
const headers = headerRecord(options.headers);
|
|
387
|
+
const response = await this.backend.openChatStream({
|
|
388
|
+
body,
|
|
389
|
+
...headers !== void 0 ? {
|
|
390
|
+
headers
|
|
391
|
+
} : {},
|
|
392
|
+
...options.abortSignal ? {
|
|
393
|
+
signal: options.abortSignal
|
|
394
|
+
} : {}
|
|
395
|
+
});
|
|
396
|
+
this.captureHeaderMeta(response);
|
|
397
|
+
return this.toChunkStream(response, {
|
|
398
|
+
...headers !== void 0 ? {
|
|
399
|
+
headers
|
|
126
400
|
} : {},
|
|
127
401
|
...options.abortSignal ? {
|
|
128
402
|
signal: options.abortSignal
|
|
129
403
|
} : {}
|
|
130
404
|
});
|
|
131
|
-
if (!response.ok || !response.body) {
|
|
132
|
-
throw new Error(`Agent chat request failed: ${response.status} ${response.statusText}`);
|
|
133
|
-
}
|
|
134
|
-
this.captureHeaderMeta(response.headers);
|
|
135
|
-
return this.toChunkStream(response.body);
|
|
136
405
|
} catch (error) {
|
|
137
406
|
this.attemptLive = false;
|
|
138
407
|
throw error;
|
|
@@ -145,51 +414,41 @@ var AgentChatTransport = class {
|
|
|
145
414
|
if (runId === void 0) return null;
|
|
146
415
|
this.attemptLive = true;
|
|
147
416
|
try {
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
},
|
|
154
|
-
...this.options.credentials !== void 0 ? {
|
|
155
|
-
credentials: this.options.credentials
|
|
417
|
+
const headers = headerRecord(options.headers);
|
|
418
|
+
const response = await this.backend.resumeChatStream({
|
|
419
|
+
runId,
|
|
420
|
+
...headers !== void 0 ? {
|
|
421
|
+
headers
|
|
156
422
|
} : {}
|
|
157
423
|
});
|
|
158
|
-
if (response
|
|
424
|
+
if (response === null) {
|
|
159
425
|
this.attemptLive = false;
|
|
160
426
|
return null;
|
|
161
427
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
428
|
+
this.captureHeaderMeta(response);
|
|
429
|
+
return this.toChunkStream(response, {
|
|
430
|
+
runId,
|
|
431
|
+
...headers !== void 0 ? {
|
|
432
|
+
headers
|
|
433
|
+
} : {}
|
|
434
|
+
});
|
|
167
435
|
} catch (error) {
|
|
168
436
|
this.attemptLive = false;
|
|
169
437
|
throw error;
|
|
170
438
|
}
|
|
171
439
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
baseUrl() {
|
|
176
|
-
return (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
440
|
+
captureHeaderMeta(response) {
|
|
441
|
+
if (response.runId) this.currentRunId = response.runId;
|
|
442
|
+
if (response.threadId) this.currentThreadId = response.threadId;
|
|
177
443
|
}
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
444
|
+
reconnectPolicy() {
|
|
445
|
+
const configured = this.options.reconnect;
|
|
446
|
+
if (configured === false) return null;
|
|
181
447
|
return {
|
|
182
|
-
...
|
|
183
|
-
...
|
|
184
|
-
...extra
|
|
448
|
+
...DEFAULT_RECONNECT,
|
|
449
|
+
...configured
|
|
185
450
|
};
|
|
186
451
|
}
|
|
187
|
-
captureHeaderMeta(headers) {
|
|
188
|
-
const runId = headers.get(HEADER_RUN_ID);
|
|
189
|
-
const threadId = headers.get(HEADER_THREAD_ID);
|
|
190
|
-
if (runId) this.currentRunId = runId;
|
|
191
|
-
if (threadId) this.currentThreadId = threadId;
|
|
192
|
-
}
|
|
193
452
|
recordMeta(meta) {
|
|
194
453
|
this.currentRunId = meta.runId;
|
|
195
454
|
this.currentThreadId = meta.threadId;
|
|
@@ -208,10 +467,66 @@ var AgentChatTransport = class {
|
|
|
208
467
|
* call plus its tool execution; text and reasoning open lazily and close at the step boundary, so
|
|
209
468
|
* tool-call cards (input streaming → output) render live between the prose.
|
|
210
469
|
*/
|
|
211
|
-
toChunkStream(source) {
|
|
212
|
-
|
|
213
|
-
|
|
470
|
+
toChunkStream(source, context) {
|
|
471
|
+
let reader = source.body.getReader();
|
|
472
|
+
let decoder = new TextDecoder();
|
|
214
473
|
let buffer = "";
|
|
474
|
+
let streamRunId = source.runId ?? context.runId;
|
|
475
|
+
let lastSeq;
|
|
476
|
+
let cancelled = false;
|
|
477
|
+
const backend = this.backend;
|
|
478
|
+
const policy = this.reconnectPolicy();
|
|
479
|
+
const notify = /* @__PURE__ */ __name((state) => this.options.onConnectionChange?.(state), "notify");
|
|
480
|
+
const aborted = /* @__PURE__ */ __name(() => cancelled || context.signal?.aborted === true, "aborted");
|
|
481
|
+
const canResume = /* @__PURE__ */ __name(() => policy !== null && lastSeq !== void 0 && streamRunId !== void 0 && !aborted(), "canResume");
|
|
482
|
+
const resume = /* @__PURE__ */ __name(async () => {
|
|
483
|
+
const runId = streamRunId;
|
|
484
|
+
const after = lastSeq ?? 0;
|
|
485
|
+
const { maxAttempts, baseDelayMs, maxDelayMs } = policy;
|
|
486
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
487
|
+
notify({
|
|
488
|
+
status: "reconnecting",
|
|
489
|
+
runId,
|
|
490
|
+
attempt,
|
|
491
|
+
after
|
|
492
|
+
});
|
|
493
|
+
await delay(Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1)), context.signal);
|
|
494
|
+
if (aborted()) return "aborted";
|
|
495
|
+
try {
|
|
496
|
+
const next = await backend.resumeChatStream({
|
|
497
|
+
runId,
|
|
498
|
+
after,
|
|
499
|
+
...context.headers !== void 0 ? {
|
|
500
|
+
headers: context.headers
|
|
501
|
+
} : {},
|
|
502
|
+
...context.signal !== void 0 ? {
|
|
503
|
+
signal: context.signal
|
|
504
|
+
} : {}
|
|
505
|
+
});
|
|
506
|
+
if (next === null) {
|
|
507
|
+
notify({
|
|
508
|
+
status: "gone",
|
|
509
|
+
runId
|
|
510
|
+
});
|
|
511
|
+
return "gone";
|
|
512
|
+
}
|
|
513
|
+
reader = next.body.getReader();
|
|
514
|
+
decoder = new TextDecoder();
|
|
515
|
+
buffer = "";
|
|
516
|
+
notify({
|
|
517
|
+
status: "live"
|
|
518
|
+
});
|
|
519
|
+
return "resumed";
|
|
520
|
+
} catch {
|
|
521
|
+
if (aborted()) return "aborted";
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
notify({
|
|
525
|
+
status: "failed",
|
|
526
|
+
runId
|
|
527
|
+
});
|
|
528
|
+
return "failed";
|
|
529
|
+
}, "resume");
|
|
215
530
|
let started = false;
|
|
216
531
|
let stepOpen = false;
|
|
217
532
|
let stepIndex = 0;
|
|
@@ -222,7 +537,10 @@ var AgentChatTransport = class {
|
|
|
222
537
|
let reasoningSeq = 0;
|
|
223
538
|
const announced = /* @__PURE__ */ new Set();
|
|
224
539
|
const toolMetadata = /* @__PURE__ */ new Map();
|
|
225
|
-
const record = /* @__PURE__ */ __name((meta) =>
|
|
540
|
+
const record = /* @__PURE__ */ __name((meta) => {
|
|
541
|
+
streamRunId = meta.runId;
|
|
542
|
+
this.recordMeta(meta);
|
|
543
|
+
}, "record");
|
|
226
544
|
const endAttempt = /* @__PURE__ */ __name(() => {
|
|
227
545
|
this.attemptLive = false;
|
|
228
546
|
}, "endAttempt");
|
|
@@ -231,7 +549,12 @@ var AgentChatTransport = class {
|
|
|
231
549
|
function ensureStarted() {
|
|
232
550
|
if (started) return;
|
|
233
551
|
started = true;
|
|
234
|
-
controller.enqueue({
|
|
552
|
+
controller.enqueue(streamRunId !== void 0 ? {
|
|
553
|
+
type: "start",
|
|
554
|
+
messageMetadata: {
|
|
555
|
+
runId: streamRunId
|
|
556
|
+
}
|
|
557
|
+
} : {
|
|
235
558
|
type: "start"
|
|
236
559
|
});
|
|
237
560
|
}
|
|
@@ -487,33 +810,67 @@ var AgentChatTransport = class {
|
|
|
487
810
|
endAttempt();
|
|
488
811
|
}
|
|
489
812
|
__name(finish, "finish");
|
|
813
|
+
function fail(errorText, start = true) {
|
|
814
|
+
if (start) ensureStarted();
|
|
815
|
+
controller.enqueue({
|
|
816
|
+
type: "error",
|
|
817
|
+
errorText
|
|
818
|
+
});
|
|
819
|
+
controller.close();
|
|
820
|
+
endAttempt();
|
|
821
|
+
}
|
|
822
|
+
__name(fail, "fail");
|
|
823
|
+
async function recover(error) {
|
|
824
|
+
if (!canResume()) {
|
|
825
|
+
if (error === void 0) {
|
|
826
|
+
finish();
|
|
827
|
+
} else {
|
|
828
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
829
|
+
}
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
const outcome = await resume();
|
|
833
|
+
if (outcome === "resumed") return true;
|
|
834
|
+
if (outcome === "failed") {
|
|
835
|
+
fail("Lost the connection to the agent stream");
|
|
836
|
+
} else {
|
|
837
|
+
finish();
|
|
838
|
+
}
|
|
839
|
+
return false;
|
|
840
|
+
}
|
|
841
|
+
__name(recover, "recover");
|
|
490
842
|
try {
|
|
491
843
|
while (true) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
844
|
+
let result;
|
|
845
|
+
try {
|
|
846
|
+
result = await reader.read();
|
|
847
|
+
} catch (error) {
|
|
848
|
+
if (await recover(error)) continue;
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
if (result.done) {
|
|
852
|
+
if (await recover(void 0)) continue;
|
|
495
853
|
return;
|
|
496
854
|
}
|
|
497
|
-
buffer += decoder.decode(value, {
|
|
855
|
+
buffer += decoder.decode(result.value, {
|
|
498
856
|
stream: true
|
|
499
857
|
});
|
|
500
858
|
let separator = buffer.indexOf("\n\n");
|
|
501
859
|
while (separator !== -1) {
|
|
502
860
|
const rawEvent = buffer.slice(0, separator);
|
|
503
861
|
buffer = buffer.slice(separator + 2);
|
|
862
|
+
separator = buffer.indexOf("\n\n");
|
|
504
863
|
const frame = parseSseFrame(rawEvent);
|
|
864
|
+
if (frame.id !== void 0) {
|
|
865
|
+
if (lastSeq !== void 0 && frame.id <= lastSeq) continue;
|
|
866
|
+
lastSeq = frame.id;
|
|
867
|
+
}
|
|
505
868
|
if (frame.event === "done") {
|
|
506
869
|
finish();
|
|
507
870
|
return;
|
|
508
871
|
}
|
|
509
872
|
if (frame.event === "error") {
|
|
510
|
-
|
|
511
|
-
controller.enqueue({
|
|
512
|
-
type: "error",
|
|
513
|
-
errorText: parseErrorText(frame.data)
|
|
514
|
-
});
|
|
515
|
-
controller.close();
|
|
516
|
-
endAttempt();
|
|
873
|
+
fail(parseErrorText(frame.data));
|
|
517
874
|
return;
|
|
518
875
|
}
|
|
519
876
|
if (frame.event === "meta") {
|
|
@@ -523,19 +880,14 @@ var AgentChatTransport = class {
|
|
|
523
880
|
const event = parseEvent(frame.data);
|
|
524
881
|
if (event) emit(event);
|
|
525
882
|
}
|
|
526
|
-
separator = buffer.indexOf("\n\n");
|
|
527
883
|
}
|
|
528
884
|
}
|
|
529
885
|
} catch (error) {
|
|
530
|
-
|
|
531
|
-
type: "error",
|
|
532
|
-
errorText: error instanceof Error ? error.message : "Agent stream error"
|
|
533
|
-
});
|
|
534
|
-
controller.close();
|
|
535
|
-
endAttempt();
|
|
886
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
536
887
|
}
|
|
537
888
|
},
|
|
538
889
|
cancel() {
|
|
890
|
+
cancelled = true;
|
|
539
891
|
void reader.cancel();
|
|
540
892
|
endAttempt();
|
|
541
893
|
}
|
|
@@ -544,17 +896,22 @@ var AgentChatTransport = class {
|
|
|
544
896
|
};
|
|
545
897
|
function parseSseFrame(raw) {
|
|
546
898
|
let event;
|
|
899
|
+
let id;
|
|
547
900
|
const dataLines = [];
|
|
548
901
|
for (const line of raw.split("\n")) {
|
|
549
902
|
if (line.startsWith("event:")) {
|
|
550
903
|
event = line.slice("event:".length).trim();
|
|
904
|
+
} else if (line.startsWith("id:")) {
|
|
905
|
+
const parsed = Number(line.slice("id:".length).trim());
|
|
906
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) id = parsed;
|
|
551
907
|
} else if (line.startsWith("data:")) {
|
|
552
908
|
dataLines.push(line.slice("data:".length).trimStart());
|
|
553
909
|
}
|
|
554
910
|
}
|
|
555
911
|
return {
|
|
556
912
|
event,
|
|
557
|
-
data: dataLines.length > 0 ? dataLines.join("\n") : void 0
|
|
913
|
+
data: dataLines.length > 0 ? dataLines.join("\n") : void 0,
|
|
914
|
+
id
|
|
558
915
|
};
|
|
559
916
|
}
|
|
560
917
|
__name(parseSseFrame, "parseSseFrame");
|
|
@@ -595,15 +952,382 @@ function parseEvent(data) {
|
|
|
595
952
|
}
|
|
596
953
|
return null;
|
|
597
954
|
}
|
|
598
|
-
__name(parseEvent, "parseEvent");
|
|
599
|
-
function extractText(message) {
|
|
600
|
-
let text = "";
|
|
601
|
-
for (const part of message.parts ?? []) {
|
|
602
|
-
if (part.type === "text") text += part.text;
|
|
955
|
+
__name(parseEvent, "parseEvent");
|
|
956
|
+
function extractText(message) {
|
|
957
|
+
let text = "";
|
|
958
|
+
for (const part of message.parts ?? []) {
|
|
959
|
+
if (part.type === "text") text += part.text;
|
|
960
|
+
}
|
|
961
|
+
return text;
|
|
962
|
+
}
|
|
963
|
+
__name(extractText, "extractText");
|
|
964
|
+
function headerRecord(headers) {
|
|
965
|
+
if (headers === void 0) return void 0;
|
|
966
|
+
return headers instanceof Headers ? Object.fromEntries(headers.entries()) : headers;
|
|
967
|
+
}
|
|
968
|
+
__name(headerRecord, "headerRecord");
|
|
969
|
+
function delay(ms, signal) {
|
|
970
|
+
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
971
|
+
return new Promise((resolve) => {
|
|
972
|
+
const timer = setTimeout(done, ms);
|
|
973
|
+
function done() {
|
|
974
|
+
clearTimeout(timer);
|
|
975
|
+
signal?.removeEventListener("abort", done);
|
|
976
|
+
resolve();
|
|
977
|
+
}
|
|
978
|
+
__name(done, "done");
|
|
979
|
+
signal?.addEventListener("abort", done, {
|
|
980
|
+
once: true
|
|
981
|
+
});
|
|
982
|
+
});
|
|
983
|
+
}
|
|
984
|
+
__name(delay, "delay");
|
|
985
|
+
|
|
986
|
+
// src/backend.ts
|
|
987
|
+
var AgentBackendUnsupportedError = class extends Error {
|
|
988
|
+
static {
|
|
989
|
+
__name(this, "AgentBackendUnsupportedError");
|
|
990
|
+
}
|
|
991
|
+
method;
|
|
992
|
+
constructor(method) {
|
|
993
|
+
super(`The agent backend does not implement ${method}()`), this.method = method;
|
|
994
|
+
this.name = "AgentBackendUnsupportedError";
|
|
995
|
+
}
|
|
996
|
+
};
|
|
997
|
+
function requireBackendMethod(backend, name) {
|
|
998
|
+
const method = backend[name];
|
|
999
|
+
if (typeof method !== "function") {
|
|
1000
|
+
throw new AgentBackendUnsupportedError(name);
|
|
1001
|
+
}
|
|
1002
|
+
return method.bind(backend);
|
|
1003
|
+
}
|
|
1004
|
+
__name(requireBackendMethod, "requireBackendMethod");
|
|
1005
|
+
|
|
1006
|
+
// src/catalog/use-agents.ts
|
|
1007
|
+
import { useMemo } from "react";
|
|
1008
|
+
|
|
1009
|
+
// src/catalog/use-resource.ts
|
|
1010
|
+
import { useCallback, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
1011
|
+
function useResource(load2, key, enabled) {
|
|
1012
|
+
const [data, setData] = useState2(void 0);
|
|
1013
|
+
const [isLoading, setIsLoading] = useState2(enabled);
|
|
1014
|
+
const [error, setError] = useState2(null);
|
|
1015
|
+
const loadRef = useRef2(load2);
|
|
1016
|
+
loadRef.current = load2;
|
|
1017
|
+
const generation = useRef2(0);
|
|
1018
|
+
const refresh = useCallback(async () => {
|
|
1019
|
+
const mine = ++generation.current;
|
|
1020
|
+
setIsLoading(true);
|
|
1021
|
+
try {
|
|
1022
|
+
const value = await loadRef.current();
|
|
1023
|
+
if (mine !== generation.current) return;
|
|
1024
|
+
setData(value);
|
|
1025
|
+
setError(null);
|
|
1026
|
+
} catch (caught) {
|
|
1027
|
+
if (mine !== generation.current) return;
|
|
1028
|
+
setError(caught instanceof Error ? caught : new Error(String(caught)));
|
|
1029
|
+
} finally {
|
|
1030
|
+
if (mine === generation.current) setIsLoading(false);
|
|
1031
|
+
}
|
|
1032
|
+
}, []);
|
|
1033
|
+
useEffect2(() => {
|
|
1034
|
+
if (!enabled) return;
|
|
1035
|
+
void refresh();
|
|
1036
|
+
}, [
|
|
1037
|
+
enabled,
|
|
1038
|
+
key,
|
|
1039
|
+
refresh
|
|
1040
|
+
]);
|
|
1041
|
+
return {
|
|
1042
|
+
data,
|
|
1043
|
+
isLoading,
|
|
1044
|
+
error,
|
|
1045
|
+
refresh
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
__name(useResource, "useResource");
|
|
1049
|
+
|
|
1050
|
+
// src/catalog/use-agents.ts
|
|
1051
|
+
function useAgents(options) {
|
|
1052
|
+
const { backend, enabled = true } = options;
|
|
1053
|
+
const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listAgents")(), "", enabled);
|
|
1054
|
+
return useMemo(() => {
|
|
1055
|
+
const agents = data ?? [];
|
|
1056
|
+
return {
|
|
1057
|
+
agents,
|
|
1058
|
+
defaultAgent: agents.find((agent) => agent.isDefault === true)?.name ?? null,
|
|
1059
|
+
isLoading,
|
|
1060
|
+
error,
|
|
1061
|
+
refresh
|
|
1062
|
+
};
|
|
1063
|
+
}, [
|
|
1064
|
+
data,
|
|
1065
|
+
isLoading,
|
|
1066
|
+
error,
|
|
1067
|
+
refresh
|
|
1068
|
+
]);
|
|
1069
|
+
}
|
|
1070
|
+
__name(useAgents, "useAgents");
|
|
1071
|
+
|
|
1072
|
+
// src/catalog/use-models.ts
|
|
1073
|
+
import { useMemo as useMemo2 } from "react";
|
|
1074
|
+
function useModels(options) {
|
|
1075
|
+
const { backend, agent, enabled = true } = options;
|
|
1076
|
+
const { data, isLoading, error, refresh } = useResource(() => requireBackendMethod(backend, "listModels")(agent), `${agent ?? ""}`, enabled);
|
|
1077
|
+
return useMemo2(() => {
|
|
1078
|
+
const providers = data?.providers ?? [];
|
|
1079
|
+
const models = providers.flatMap((provider) => provider.models.map((model) => ({
|
|
1080
|
+
...model,
|
|
1081
|
+
providerId: provider.id,
|
|
1082
|
+
providerLabel: provider.label
|
|
1083
|
+
})));
|
|
1084
|
+
const byId = new Map(models.map((model) => [
|
|
1085
|
+
model.id,
|
|
1086
|
+
model
|
|
1087
|
+
]));
|
|
1088
|
+
return {
|
|
1089
|
+
providers,
|
|
1090
|
+
models,
|
|
1091
|
+
defaultModel: data?.default ?? null,
|
|
1092
|
+
find: /* @__PURE__ */ __name((id) => id == null ? void 0 : byId.get(id), "find"),
|
|
1093
|
+
isLoading,
|
|
1094
|
+
error,
|
|
1095
|
+
refresh
|
|
1096
|
+
};
|
|
1097
|
+
}, [
|
|
1098
|
+
data,
|
|
1099
|
+
isLoading,
|
|
1100
|
+
error,
|
|
1101
|
+
refresh
|
|
1102
|
+
]);
|
|
1103
|
+
}
|
|
1104
|
+
__name(useModels, "useModels");
|
|
1105
|
+
|
|
1106
|
+
// src/feedback/use-message-feedback.ts
|
|
1107
|
+
import { useCallback as useCallback2, useRef as useRef3, useState as useState3 } from "react";
|
|
1108
|
+
function useMessageFeedback(options) {
|
|
1109
|
+
const latest = useRef3(options);
|
|
1110
|
+
latest.current = options;
|
|
1111
|
+
const [local, setLocal] = useState3(/* @__PURE__ */ new Map());
|
|
1112
|
+
const [pending, setPending] = useState3(/* @__PURE__ */ new Set());
|
|
1113
|
+
const [error, setError] = useState3(null);
|
|
1114
|
+
const localRef = useRef3(local);
|
|
1115
|
+
localRef.current = local;
|
|
1116
|
+
const resolved = useRef3(/* @__PURE__ */ new Map());
|
|
1117
|
+
const feedbackOf = useCallback2((message) => {
|
|
1118
|
+
const mine = local.get(message.id);
|
|
1119
|
+
if (mine !== void 0) return mine.feedback;
|
|
1120
|
+
return metadataOf(message).feedback ?? null;
|
|
1121
|
+
}, [
|
|
1122
|
+
local
|
|
1123
|
+
]);
|
|
1124
|
+
const isPending = useCallback2((message) => pending.has(message.id), [
|
|
1125
|
+
pending
|
|
1126
|
+
]);
|
|
1127
|
+
const storedIdFor = useCallback2(async (message) => {
|
|
1128
|
+
const runId = metadataOf(message).runId;
|
|
1129
|
+
if (runId === void 0) return message.id;
|
|
1130
|
+
const known = resolved.current.get(message.id);
|
|
1131
|
+
if (known !== void 0) return known;
|
|
1132
|
+
const { backend, threadId } = latest.current;
|
|
1133
|
+
const thread = typeof threadId === "function" ? threadId() : threadId;
|
|
1134
|
+
if (thread === void 0) {
|
|
1135
|
+
throw new Error("useMessageFeedback: a streamed message needs `threadId` to be rated");
|
|
1136
|
+
}
|
|
1137
|
+
const detail = await backend.getThread(thread);
|
|
1138
|
+
const row = [
|
|
1139
|
+
...detail.messages
|
|
1140
|
+
].reverse().find((candidate) => candidate.role === "assistant" && candidate.runId === runId);
|
|
1141
|
+
if (row === void 0) {
|
|
1142
|
+
throw new Error(`useMessageFeedback: run ${runId} has no persisted answer yet`);
|
|
1143
|
+
}
|
|
1144
|
+
resolved.current.set(message.id, row.id);
|
|
1145
|
+
return row.id;
|
|
1146
|
+
}, []);
|
|
1147
|
+
const rate = useCallback2(async (message, value, comment) => {
|
|
1148
|
+
const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
|
|
1149
|
+
const had = localRef.current.get(message.id);
|
|
1150
|
+
const optimistic = value === null ? null : {
|
|
1151
|
+
value,
|
|
1152
|
+
...comment !== void 0 && comment.trim().length > 0 ? {
|
|
1153
|
+
comment: comment.trim()
|
|
1154
|
+
} : {},
|
|
1155
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1156
|
+
};
|
|
1157
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1158
|
+
feedback: optimistic
|
|
1159
|
+
}));
|
|
1160
|
+
setPending((current) => new Set(current).add(message.id));
|
|
1161
|
+
try {
|
|
1162
|
+
const storedId = await storedIdFor(message);
|
|
1163
|
+
const { feedback } = await setFeedback(storedId, {
|
|
1164
|
+
value,
|
|
1165
|
+
...comment !== void 0 ? {
|
|
1166
|
+
comment
|
|
1167
|
+
} : {}
|
|
1168
|
+
});
|
|
1169
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1170
|
+
feedback
|
|
1171
|
+
}));
|
|
1172
|
+
setError(null);
|
|
1173
|
+
} catch (caught) {
|
|
1174
|
+
setLocal((current) => {
|
|
1175
|
+
const next = new Map(current);
|
|
1176
|
+
if (had === void 0) next.delete(message.id);
|
|
1177
|
+
else next.set(message.id, had);
|
|
1178
|
+
return next;
|
|
1179
|
+
});
|
|
1180
|
+
const failure = caught instanceof Error ? caught : new Error(String(caught));
|
|
1181
|
+
setError(failure);
|
|
1182
|
+
throw failure;
|
|
1183
|
+
} finally {
|
|
1184
|
+
setPending((current) => {
|
|
1185
|
+
const next = new Set(current);
|
|
1186
|
+
next.delete(message.id);
|
|
1187
|
+
return next;
|
|
1188
|
+
});
|
|
1189
|
+
}
|
|
1190
|
+
}, [
|
|
1191
|
+
storedIdFor
|
|
1192
|
+
]);
|
|
1193
|
+
const toggle = useCallback2((message, value) => {
|
|
1194
|
+
const mine = localRef.current.get(message.id);
|
|
1195
|
+
const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
|
|
1196
|
+
return rate(message, current?.value === value ? null : value);
|
|
1197
|
+
}, [
|
|
1198
|
+
rate
|
|
1199
|
+
]);
|
|
1200
|
+
return {
|
|
1201
|
+
feedbackOf,
|
|
1202
|
+
isPending,
|
|
1203
|
+
rate,
|
|
1204
|
+
toggle,
|
|
1205
|
+
error
|
|
1206
|
+
};
|
|
1207
|
+
}
|
|
1208
|
+
__name(useMessageFeedback, "useMessageFeedback");
|
|
1209
|
+
function metadataOf(message) {
|
|
1210
|
+
const metadata = message.metadata;
|
|
1211
|
+
return metadata !== null && typeof metadata === "object" ? metadata : {};
|
|
1212
|
+
}
|
|
1213
|
+
__name(metadataOf, "metadataOf");
|
|
1214
|
+
|
|
1215
|
+
// src/threads/threads-events.ts
|
|
1216
|
+
var listeners = /* @__PURE__ */ new WeakMap();
|
|
1217
|
+
function notifyThreads(backend, event) {
|
|
1218
|
+
for (const listener of listeners.get(backend) ?? []) {
|
|
1219
|
+
listener(event);
|
|
603
1220
|
}
|
|
604
|
-
return text;
|
|
605
1221
|
}
|
|
606
|
-
__name(
|
|
1222
|
+
__name(notifyThreads, "notifyThreads");
|
|
1223
|
+
function onThreadsEvent(backend, listener) {
|
|
1224
|
+
let set = listeners.get(backend);
|
|
1225
|
+
if (set === void 0) {
|
|
1226
|
+
set = /* @__PURE__ */ new Set();
|
|
1227
|
+
listeners.set(backend, set);
|
|
1228
|
+
}
|
|
1229
|
+
set.add(listener);
|
|
1230
|
+
return () => {
|
|
1231
|
+
set.delete(listener);
|
|
1232
|
+
};
|
|
1233
|
+
}
|
|
1234
|
+
__name(onThreadsEvent, "onThreadsEvent");
|
|
1235
|
+
|
|
1236
|
+
// src/threads/use-threads.ts
|
|
1237
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef4, useState as useState4 } from "react";
|
|
1238
|
+
function useThreads(options) {
|
|
1239
|
+
const { backend, enabled = true } = options;
|
|
1240
|
+
const [threads, setThreads] = useState4([]);
|
|
1241
|
+
const [isLoading, setIsLoading] = useState4(enabled);
|
|
1242
|
+
const [error, setError] = useState4(null);
|
|
1243
|
+
const threadsRef = useRef4(threads);
|
|
1244
|
+
threadsRef.current = threads;
|
|
1245
|
+
const generation = useRef4(0);
|
|
1246
|
+
const refresh = useCallback3(async () => {
|
|
1247
|
+
const mine = ++generation.current;
|
|
1248
|
+
setIsLoading(true);
|
|
1249
|
+
try {
|
|
1250
|
+
const list = await backend.listThreads();
|
|
1251
|
+
if (mine !== generation.current) return;
|
|
1252
|
+
setThreads(list);
|
|
1253
|
+
setError(null);
|
|
1254
|
+
} catch (caught) {
|
|
1255
|
+
if (mine !== generation.current) return;
|
|
1256
|
+
setError(caught instanceof Error ? caught : new Error(String(caught)));
|
|
1257
|
+
} finally {
|
|
1258
|
+
if (mine === generation.current) setIsLoading(false);
|
|
1259
|
+
}
|
|
1260
|
+
}, [
|
|
1261
|
+
backend
|
|
1262
|
+
]);
|
|
1263
|
+
useEffect3(() => {
|
|
1264
|
+
if (!enabled) return;
|
|
1265
|
+
void refresh();
|
|
1266
|
+
return onThreadsEvent(backend, (event) => {
|
|
1267
|
+
if (event.type === "title") {
|
|
1268
|
+
setThreads((current) => current.map((thread) => thread.id === event.threadId ? {
|
|
1269
|
+
...thread,
|
|
1270
|
+
title: event.title
|
|
1271
|
+
} : thread));
|
|
1272
|
+
} else if (event.type === "removed") {
|
|
1273
|
+
setThreads((current) => current.filter((thread) => thread.id !== event.threadId));
|
|
1274
|
+
} else {
|
|
1275
|
+
void refresh();
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
}, [
|
|
1279
|
+
backend,
|
|
1280
|
+
enabled,
|
|
1281
|
+
refresh
|
|
1282
|
+
]);
|
|
1283
|
+
const rename = useCallback3(async (id, title) => {
|
|
1284
|
+
const before = threadsRef.current;
|
|
1285
|
+
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
1286
|
+
...thread,
|
|
1287
|
+
title
|
|
1288
|
+
} : thread));
|
|
1289
|
+
try {
|
|
1290
|
+
await backend.updateThread(id, {
|
|
1291
|
+
title
|
|
1292
|
+
});
|
|
1293
|
+
notifyThreads(backend, {
|
|
1294
|
+
type: "title",
|
|
1295
|
+
threadId: id,
|
|
1296
|
+
title
|
|
1297
|
+
});
|
|
1298
|
+
} catch (caught) {
|
|
1299
|
+
setThreads(before);
|
|
1300
|
+
throw caught;
|
|
1301
|
+
}
|
|
1302
|
+
}, [
|
|
1303
|
+
backend
|
|
1304
|
+
]);
|
|
1305
|
+
const remove = useCallback3(async (id) => {
|
|
1306
|
+
const before = threadsRef.current;
|
|
1307
|
+
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
1308
|
+
try {
|
|
1309
|
+
await backend.deleteThread(id);
|
|
1310
|
+
notifyThreads(backend, {
|
|
1311
|
+
type: "removed",
|
|
1312
|
+
threadId: id
|
|
1313
|
+
});
|
|
1314
|
+
} catch (caught) {
|
|
1315
|
+
setThreads(before);
|
|
1316
|
+
throw caught;
|
|
1317
|
+
}
|
|
1318
|
+
}, [
|
|
1319
|
+
backend
|
|
1320
|
+
]);
|
|
1321
|
+
return {
|
|
1322
|
+
threads,
|
|
1323
|
+
isLoading,
|
|
1324
|
+
error,
|
|
1325
|
+
refresh,
|
|
1326
|
+
rename,
|
|
1327
|
+
remove
|
|
1328
|
+
};
|
|
1329
|
+
}
|
|
1330
|
+
__name(useThreads, "useThreads");
|
|
607
1331
|
|
|
608
1332
|
// src/background-runs.ts
|
|
609
1333
|
function asReceipt(output) {
|
|
@@ -652,170 +1376,11 @@ function backgroundRunsFromThread(messages) {
|
|
|
652
1376
|
}
|
|
653
1377
|
__name(backgroundRunsFromThread, "backgroundRunsFromThread");
|
|
654
1378
|
|
|
655
|
-
// src/client.ts
|
|
656
|
-
var AgentHttpError = class extends Error {
|
|
657
|
-
static {
|
|
658
|
-
__name(this, "AgentHttpError");
|
|
659
|
-
}
|
|
660
|
-
status;
|
|
661
|
-
method;
|
|
662
|
-
path;
|
|
663
|
-
constructor(status, method, path, statusText) {
|
|
664
|
-
super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
|
|
665
|
-
this.name = "AgentHttpError";
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
var AgentClient = class {
|
|
669
|
-
static {
|
|
670
|
-
__name(this, "AgentClient");
|
|
671
|
-
}
|
|
672
|
-
options;
|
|
673
|
-
constructor(options = {}) {
|
|
674
|
-
this.options = options;
|
|
675
|
-
}
|
|
676
|
-
listThreads() {
|
|
677
|
-
return this.request("GET", "/agent/threads");
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
681
|
-
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
682
|
-
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
683
|
-
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
684
|
-
*/
|
|
685
|
-
listSkills(threadId) {
|
|
686
|
-
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
687
|
-
return this.request("GET", `/agent/skills${query}`);
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
|
|
691
|
-
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
|
|
692
|
-
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
|
|
693
|
-
*/
|
|
694
|
-
listTools(agent) {
|
|
695
|
-
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
696
|
-
return this.request("GET", `/agent/tools${query}`);
|
|
697
|
-
}
|
|
698
|
-
getThread(id) {
|
|
699
|
-
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
700
|
-
}
|
|
701
|
-
deleteThread(id) {
|
|
702
|
-
return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
|
|
703
|
-
}
|
|
704
|
-
forkFromMessage(threadId, messageId) {
|
|
705
|
-
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
706
|
-
}
|
|
707
|
-
renameThread(id, title) {
|
|
708
|
-
return this.updateThread(id, {
|
|
709
|
-
title
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
713
|
-
updateThread(id, patch) {
|
|
714
|
-
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
718
|
-
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
719
|
-
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
720
|
-
*/
|
|
721
|
-
async uploadAttachment(file) {
|
|
722
|
-
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
723
|
-
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
724
|
-
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
725
|
-
const formData = new FormData();
|
|
726
|
-
formData.append("file", file);
|
|
727
|
-
const response = await fetchImpl(`${baseUrl}/agent/attachments`, {
|
|
728
|
-
method: "POST",
|
|
729
|
-
headers: {
|
|
730
|
-
accept: "application/json",
|
|
731
|
-
...this.options.headers,
|
|
732
|
-
...dynamic
|
|
733
|
-
},
|
|
734
|
-
body: formData,
|
|
735
|
-
...this.options.credentials !== void 0 ? {
|
|
736
|
-
credentials: this.options.credentials
|
|
737
|
-
} : {}
|
|
738
|
-
});
|
|
739
|
-
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
740
|
-
}
|
|
741
|
-
promoteThread(id) {
|
|
742
|
-
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
743
|
-
}
|
|
744
|
-
truncateFromMessage(threadId, messageId) {
|
|
745
|
-
return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
|
|
746
|
-
}
|
|
747
|
-
getQuotaToday() {
|
|
748
|
-
return this.request("GET", "/agent/quota/today");
|
|
749
|
-
}
|
|
750
|
-
cancelStream(runId) {
|
|
751
|
-
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
755
|
-
* the decision came through (the server records `'web'` when omitted).
|
|
756
|
-
*/
|
|
757
|
-
approveToolCall(input) {
|
|
758
|
-
return this.request("POST", "/agent/tool-call/approve", input);
|
|
759
|
-
}
|
|
760
|
-
rejectToolCall(input) {
|
|
761
|
-
return this.request("POST", "/agent/tool-call/reject", input);
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
765
|
-
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
766
|
-
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
767
|
-
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
768
|
-
*/
|
|
769
|
-
answerToolCall(input) {
|
|
770
|
-
return this.request("POST", "/agent/tool-call/answer", input);
|
|
771
|
-
}
|
|
772
|
-
/**
|
|
773
|
-
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
774
|
-
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
775
|
-
* evidence the user chose them.
|
|
776
|
-
*/
|
|
777
|
-
skipToolCall(input) {
|
|
778
|
-
return this.request("POST", "/agent/tool-call/skip", input);
|
|
779
|
-
}
|
|
780
|
-
async request(method, path, body) {
|
|
781
|
-
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
782
|
-
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
783
|
-
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
784
|
-
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
785
|
-
method,
|
|
786
|
-
headers: {
|
|
787
|
-
accept: "application/json",
|
|
788
|
-
...body !== void 0 ? {
|
|
789
|
-
"content-type": "application/json"
|
|
790
|
-
} : {},
|
|
791
|
-
...this.options.headers,
|
|
792
|
-
...dynamic
|
|
793
|
-
},
|
|
794
|
-
...body !== void 0 ? {
|
|
795
|
-
body: JSON.stringify(body)
|
|
796
|
-
} : {},
|
|
797
|
-
...this.options.credentials !== void 0 ? {
|
|
798
|
-
credentials: this.options.credentials
|
|
799
|
-
} : {}
|
|
800
|
-
});
|
|
801
|
-
return this.handleResponse(response, method, path);
|
|
802
|
-
}
|
|
803
|
-
async handleResponse(response, method, path) {
|
|
804
|
-
if (!response.ok) {
|
|
805
|
-
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
806
|
-
}
|
|
807
|
-
if (response.status === 204) return void 0;
|
|
808
|
-
const text = await response.text();
|
|
809
|
-
if (!text) return void 0;
|
|
810
|
-
return JSON.parse(text);
|
|
811
|
-
}
|
|
812
|
-
};
|
|
813
|
-
|
|
814
1379
|
// src/components/chat-input.tsx
|
|
815
|
-
import React2, { useEffect as
|
|
1380
|
+
import React2, { useEffect as useEffect5, useState as useState6 } from "react";
|
|
816
1381
|
|
|
817
1382
|
// src/components/use-speech-recognition.ts
|
|
818
|
-
import { useCallback, useEffect as
|
|
1383
|
+
import { useCallback as useCallback4, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef5, useState as useState5 } from "react";
|
|
819
1384
|
function getRecognitionCtor() {
|
|
820
1385
|
if (typeof window === "undefined") return null;
|
|
821
1386
|
const scope = window;
|
|
@@ -823,15 +1388,15 @@ function getRecognitionCtor() {
|
|
|
823
1388
|
}
|
|
824
1389
|
__name(getRecognitionCtor, "getRecognitionCtor");
|
|
825
1390
|
function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
|
|
826
|
-
const recognitionRef =
|
|
827
|
-
const [isListening, setIsListening] =
|
|
828
|
-
const ctor =
|
|
1391
|
+
const recognitionRef = useRef5(null);
|
|
1392
|
+
const [isListening, setIsListening] = useState5(false);
|
|
1393
|
+
const ctor = useMemo3(() => getRecognitionCtor(), []);
|
|
829
1394
|
const isSupported = ctor !== null;
|
|
830
|
-
const finalRef =
|
|
831
|
-
const interimRef =
|
|
1395
|
+
const finalRef = useRef5(onFinalTranscript);
|
|
1396
|
+
const interimRef = useRef5(onInterimTranscript);
|
|
832
1397
|
finalRef.current = onFinalTranscript;
|
|
833
1398
|
interimRef.current = onInterimTranscript;
|
|
834
|
-
|
|
1399
|
+
useEffect4(() => {
|
|
835
1400
|
if (!ctor) return;
|
|
836
1401
|
const recognition = new ctor();
|
|
837
1402
|
recognition.lang = lang;
|
|
@@ -870,7 +1435,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
870
1435
|
ctor,
|
|
871
1436
|
lang
|
|
872
1437
|
]);
|
|
873
|
-
const start =
|
|
1438
|
+
const start = useCallback4(() => {
|
|
874
1439
|
const recognition = recognitionRef.current;
|
|
875
1440
|
if (!recognition) return;
|
|
876
1441
|
try {
|
|
@@ -880,7 +1445,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
880
1445
|
console.warn("SpeechRecognition start failed", error);
|
|
881
1446
|
}
|
|
882
1447
|
}, []);
|
|
883
|
-
const stop =
|
|
1448
|
+
const stop = useCallback4(() => {
|
|
884
1449
|
const recognition = recognitionRef.current;
|
|
885
1450
|
if (!recognition) return;
|
|
886
1451
|
try {
|
|
@@ -899,8 +1464,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
|
|
|
899
1464
|
|
|
900
1465
|
// src/components/chat-input.tsx
|
|
901
1466
|
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 }) {
|
|
902
|
-
const [value, setValue] =
|
|
903
|
-
const [interim, setInterim] =
|
|
1467
|
+
const [value, setValue] = useState6("");
|
|
1468
|
+
const [interim, setInterim] = useState6("");
|
|
904
1469
|
const speech = useSpeechRecognition({
|
|
905
1470
|
lang: speechLang,
|
|
906
1471
|
onFinalTranscript: /* @__PURE__ */ __name((text) => {
|
|
@@ -911,7 +1476,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
911
1476
|
}, "onFinalTranscript"),
|
|
912
1477
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
913
1478
|
});
|
|
914
|
-
|
|
1479
|
+
useEffect5(() => {
|
|
915
1480
|
if (!speech.isListening) setInterim("");
|
|
916
1481
|
}, [
|
|
917
1482
|
speech.isListening
|
|
@@ -978,7 +1543,7 @@ __name(ChatInput, "ChatInput");
|
|
|
978
1543
|
import React3 from "react";
|
|
979
1544
|
|
|
980
1545
|
// src/transcript/use-chat-transcript.ts
|
|
981
|
-
import { useCallback as
|
|
1546
|
+
import { useCallback as useCallback6, useEffect as useEffect7, useRef as useRef7, useState as useState8 } from "react";
|
|
982
1547
|
|
|
983
1548
|
// src/transcript/model.ts
|
|
984
1549
|
import { readElicitationInput, validateElicitationAnswer as validateElicitationAnswer2 } from "@dudousxd/nestjs-agent-core";
|
|
@@ -1955,11 +2520,11 @@ function formatCostUsd(cost) {
|
|
|
1955
2520
|
__name(formatCostUsd, "formatCostUsd");
|
|
1956
2521
|
|
|
1957
2522
|
// src/transcript/use-stick-to-bottom.ts
|
|
1958
|
-
import { useCallback as
|
|
2523
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef6, useState as useState7 } from "react";
|
|
1959
2524
|
function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
1960
|
-
const containerRef =
|
|
1961
|
-
const [isAtBottom, setIsAtBottom] =
|
|
1962
|
-
const measure =
|
|
2525
|
+
const containerRef = useRef6(null);
|
|
2526
|
+
const [isAtBottom, setIsAtBottom] = useState7(true);
|
|
2527
|
+
const measure = useCallback5(() => {
|
|
1963
2528
|
const element = containerRef.current;
|
|
1964
2529
|
if (!element) {
|
|
1965
2530
|
return;
|
|
@@ -1969,7 +2534,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1969
2534
|
}, [
|
|
1970
2535
|
threshold
|
|
1971
2536
|
]);
|
|
1972
|
-
const scrollToBottom =
|
|
2537
|
+
const scrollToBottom = useCallback5(() => {
|
|
1973
2538
|
const element = containerRef.current;
|
|
1974
2539
|
if (!element) {
|
|
1975
2540
|
return;
|
|
@@ -1977,7 +2542,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1977
2542
|
element.scrollTop = element.scrollHeight;
|
|
1978
2543
|
setIsAtBottom(true);
|
|
1979
2544
|
}, []);
|
|
1980
|
-
const setContainer =
|
|
2545
|
+
const setContainer = useCallback5((element) => {
|
|
1981
2546
|
containerRef.current = element;
|
|
1982
2547
|
if (element) {
|
|
1983
2548
|
measure();
|
|
@@ -1985,7 +2550,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1985
2550
|
}, [
|
|
1986
2551
|
measure
|
|
1987
2552
|
]);
|
|
1988
|
-
|
|
2553
|
+
useEffect6(() => {
|
|
1989
2554
|
if (isAtBottom) {
|
|
1990
2555
|
scrollToBottom();
|
|
1991
2556
|
}
|
|
@@ -1994,7 +2559,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1994
2559
|
isAtBottom,
|
|
1995
2560
|
scrollToBottom
|
|
1996
2561
|
]);
|
|
1997
|
-
const getContainerProps =
|
|
2562
|
+
const getContainerProps = useCallback5(() => ({
|
|
1998
2563
|
ref: setContainer,
|
|
1999
2564
|
onScroll: measure
|
|
2000
2565
|
}), [
|
|
@@ -2016,14 +2581,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
|
|
|
2016
2581
|
var DEFAULT_COPY_RESET_MS = 1500;
|
|
2017
2582
|
function useChatTranscript(options) {
|
|
2018
2583
|
const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
|
|
2019
|
-
const latest =
|
|
2584
|
+
const latest = useRef7(options);
|
|
2020
2585
|
latest.current = options;
|
|
2021
|
-
const [visibleCount, setVisibleCount] =
|
|
2022
|
-
const [stopRequested, setStopRequested] =
|
|
2023
|
-
const isStreaming = status === "streaming";
|
|
2586
|
+
const [visibleCount, setVisibleCount] = useState8(initialVisibleCount);
|
|
2587
|
+
const [stopRequested, setStopRequested] = useState8(false);
|
|
2588
|
+
const isStreaming = status === "streaming" || status === "reconnecting";
|
|
2024
2589
|
const isBusy = status === "submitted" || isStreaming;
|
|
2025
2590
|
const isEmpty = messages.length === 0;
|
|
2026
|
-
|
|
2591
|
+
useEffect7(() => {
|
|
2027
2592
|
if (!isBusy) {
|
|
2028
2593
|
setStopRequested(false);
|
|
2029
2594
|
}
|
|
@@ -2040,16 +2605,16 @@ function useChatTranscript(options) {
|
|
|
2040
2605
|
streamingMessageId,
|
|
2041
2606
|
options
|
|
2042
2607
|
});
|
|
2043
|
-
const stop =
|
|
2608
|
+
const stop = useCallback6(() => {
|
|
2044
2609
|
const current = latest.current;
|
|
2045
|
-
if (current.status !== "submitted" && current.status !== "streaming") {
|
|
2610
|
+
if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
|
|
2046
2611
|
return;
|
|
2047
2612
|
}
|
|
2048
2613
|
setStopRequested(true);
|
|
2049
2614
|
void current.onStop?.();
|
|
2050
2615
|
}, []);
|
|
2051
|
-
const loadEarlier =
|
|
2052
|
-
const showAll =
|
|
2616
|
+
const loadEarlier = useCallback6(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
|
|
2617
|
+
const showAll = useCallback6(() => setVisibleCount(latest.current.messages.length), []);
|
|
2053
2618
|
const scroll = useStickToBottom({
|
|
2054
2619
|
contentKey: contentSignature(messages),
|
|
2055
2620
|
...stickToBottomThreshold !== void 0 ? {
|
|
@@ -2085,7 +2650,7 @@ function useChatTranscript(options) {
|
|
|
2085
2650
|
__name(useChatTranscript, "useChatTranscript");
|
|
2086
2651
|
function useTranscriptItem(options) {
|
|
2087
2652
|
const { message, isStreaming = false } = options;
|
|
2088
|
-
const messagesRef =
|
|
2653
|
+
const messagesRef = useRef7([
|
|
2089
2654
|
message
|
|
2090
2655
|
]);
|
|
2091
2656
|
if (messagesRef.current[0] !== message) {
|
|
@@ -2103,13 +2668,13 @@ function useTranscriptItem(options) {
|
|
|
2103
2668
|
}
|
|
2104
2669
|
__name(useTranscriptItem, "useTranscriptItem");
|
|
2105
2670
|
function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
|
|
2106
|
-
const [drafts, setDrafts] =
|
|
2107
|
-
const [copied, setCopied] =
|
|
2108
|
-
const [openReasoning, setOpenReasoning] =
|
|
2109
|
-
const [picks, setPicks] =
|
|
2110
|
-
const [settling, setSettling] =
|
|
2111
|
-
const [settleErrors, setSettleErrors] =
|
|
2112
|
-
const latest =
|
|
2671
|
+
const [drafts, setDrafts] = useState8(() => /* @__PURE__ */ new Map());
|
|
2672
|
+
const [copied, setCopied] = useState8(() => /* @__PURE__ */ new Set());
|
|
2673
|
+
const [openReasoning, setOpenReasoning] = useState8(() => /* @__PURE__ */ new Map());
|
|
2674
|
+
const [picks, setPicks] = useState8(() => /* @__PURE__ */ new Map());
|
|
2675
|
+
const [settling, setSettling] = useState8(() => /* @__PURE__ */ new Map());
|
|
2676
|
+
const [settleErrors, setSettleErrors] = useState8(() => /* @__PURE__ */ new Map());
|
|
2677
|
+
const latest = useRef7({
|
|
2113
2678
|
options,
|
|
2114
2679
|
drafts,
|
|
2115
2680
|
picks,
|
|
@@ -2121,23 +2686,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2121
2686
|
picks,
|
|
2122
2687
|
messages
|
|
2123
2688
|
};
|
|
2124
|
-
const copyTimers =
|
|
2125
|
-
|
|
2689
|
+
const copyTimers = useRef7(/* @__PURE__ */ new Map());
|
|
2690
|
+
useEffect7(() => () => {
|
|
2126
2691
|
for (const timer of copyTimers.current.values()) {
|
|
2127
2692
|
clearTimeout(timer);
|
|
2128
2693
|
}
|
|
2129
2694
|
copyTimers.current.clear();
|
|
2130
2695
|
}, []);
|
|
2131
|
-
const callbackCache =
|
|
2132
|
-
const reasoningToggleCache =
|
|
2133
|
-
const toggleReasoning =
|
|
2696
|
+
const callbackCache = useRef7(/* @__PURE__ */ new Map());
|
|
2697
|
+
const reasoningToggleCache = useRef7(/* @__PURE__ */ new Map());
|
|
2698
|
+
const toggleReasoning = useCallback6((key, open) => {
|
|
2134
2699
|
setOpenReasoning((current) => {
|
|
2135
2700
|
const next = new Map(current);
|
|
2136
2701
|
next.set(key, open ?? !(current.get(key) ?? false));
|
|
2137
2702
|
return next;
|
|
2138
2703
|
});
|
|
2139
2704
|
}, []);
|
|
2140
|
-
const stableToggle =
|
|
2705
|
+
const stableToggle = useCallback6((key) => {
|
|
2141
2706
|
let cached = reasoningToggleCache.current.get(key);
|
|
2142
2707
|
if (!cached) {
|
|
2143
2708
|
cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
|
|
@@ -2147,10 +2712,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2147
2712
|
}, [
|
|
2148
2713
|
toggleReasoning
|
|
2149
2714
|
]);
|
|
2150
|
-
const pick =
|
|
2715
|
+
const pick = useCallback6((toolCallId, questionId, values) => {
|
|
2151
2716
|
setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
|
|
2152
2717
|
}, []);
|
|
2153
|
-
const settle =
|
|
2718
|
+
const settle = useCallback6((toolCallId, action, send) => {
|
|
2154
2719
|
setSettleErrors((current) => {
|
|
2155
2720
|
if (!current.has(toolCallId)) {
|
|
2156
2721
|
return current;
|
|
@@ -2174,7 +2739,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2174
2739
|
failed(error);
|
|
2175
2740
|
}
|
|
2176
2741
|
}, []);
|
|
2177
|
-
const answer =
|
|
2742
|
+
const answer = useCallback6((toolCallId) => {
|
|
2178
2743
|
const current = latest.current;
|
|
2179
2744
|
const onAnswer = current.options.onAnswer;
|
|
2180
2745
|
if (!onAnswer) {
|
|
@@ -2184,7 +2749,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2184
2749
|
}, [
|
|
2185
2750
|
settle
|
|
2186
2751
|
]);
|
|
2187
|
-
const skip =
|
|
2752
|
+
const skip = useCallback6((toolCallId) => {
|
|
2188
2753
|
const onSkip = latest.current.options.onSkip;
|
|
2189
2754
|
if (onSkip) {
|
|
2190
2755
|
settle(toolCallId, "skip", () => onSkip(toolCallId));
|
|
@@ -2192,7 +2757,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2192
2757
|
}, [
|
|
2193
2758
|
settle
|
|
2194
2759
|
]);
|
|
2195
|
-
const approve =
|
|
2760
|
+
const approve = useCallback6((toolCallId, options2) => {
|
|
2196
2761
|
const onApprove = latest.current.options.onApprove;
|
|
2197
2762
|
if (onApprove) {
|
|
2198
2763
|
settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
|
|
@@ -2200,7 +2765,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2200
2765
|
}, [
|
|
2201
2766
|
settle
|
|
2202
2767
|
]);
|
|
2203
|
-
const reject =
|
|
2768
|
+
const reject = useCallback6((toolCallId) => {
|
|
2204
2769
|
const onReject = latest.current.options.onReject;
|
|
2205
2770
|
if (onReject) {
|
|
2206
2771
|
settle(toolCallId, "reject", () => onReject(toolCallId));
|
|
@@ -2208,7 +2773,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2208
2773
|
}, [
|
|
2209
2774
|
settle
|
|
2210
2775
|
]);
|
|
2211
|
-
const callbacksFor =
|
|
2776
|
+
const callbacksFor = useCallback6((id) => {
|
|
2212
2777
|
const cached = callbackCache.current.get(id);
|
|
2213
2778
|
if (cached) {
|
|
2214
2779
|
return cached;
|
|
@@ -2831,7 +3396,11 @@ function createSkillsSource(options) {
|
|
|
2831
3396
|
if (cached) {
|
|
2832
3397
|
return cached;
|
|
2833
3398
|
}
|
|
2834
|
-
const
|
|
3399
|
+
const listSkills = options.client.listSkills;
|
|
3400
|
+
if (listSkills === void 0) {
|
|
3401
|
+
return Promise.reject(new AgentBackendUnsupportedError("listSkills"));
|
|
3402
|
+
}
|
|
3403
|
+
const pending = listSkills.call(options.client, threadId).then((entries) => entries.map(toItem)).catch((error) => {
|
|
2835
3404
|
byThread.delete(key);
|
|
2836
3405
|
throw error;
|
|
2837
3406
|
});
|
|
@@ -2861,35 +3430,35 @@ function toItem(entry) {
|
|
|
2861
3430
|
__name(toItem, "toItem");
|
|
2862
3431
|
|
|
2863
3432
|
// src/composer/use-composer-autocomplete.ts
|
|
2864
|
-
import { useCallback as
|
|
3433
|
+
import { useCallback as useCallback7, useEffect as useEffect8, useId, useLayoutEffect, useMemo as useMemo4, useRef as useRef8, useState as useState9 } from "react";
|
|
2865
3434
|
function useComposerAutocomplete(options) {
|
|
2866
3435
|
const { value, sources, acceptOnTab = true } = options;
|
|
2867
|
-
const latest =
|
|
3436
|
+
const latest = useRef8(options);
|
|
2868
3437
|
latest.current = options;
|
|
2869
|
-
const inputRef =
|
|
2870
|
-
const [caret, setCaret] =
|
|
2871
|
-
const [items, setItems] =
|
|
2872
|
-
const [isLoading, setLoading] =
|
|
2873
|
-
const [error, setError] =
|
|
2874
|
-
const [activeIndex, setActiveIndex] =
|
|
2875
|
-
const [dismissedIndex, setDismissedIndex] =
|
|
3438
|
+
const inputRef = useRef8(null);
|
|
3439
|
+
const [caret, setCaret] = useState9(value.length);
|
|
3440
|
+
const [items, setItems] = useState9([]);
|
|
3441
|
+
const [isLoading, setLoading] = useState9(false);
|
|
3442
|
+
const [error, setError] = useState9(null);
|
|
3443
|
+
const [activeIndex, setActiveIndex] = useState9(0);
|
|
3444
|
+
const [dismissedIndex, setDismissedIndex] = useState9(null);
|
|
2876
3445
|
const safeCaret = Math.min(caret, value.length);
|
|
2877
|
-
const match =
|
|
3446
|
+
const match = useMemo4(() => findActiveTrigger(value, safeCaret, sources), [
|
|
2878
3447
|
value,
|
|
2879
3448
|
safeCaret,
|
|
2880
3449
|
sources
|
|
2881
3450
|
]);
|
|
2882
3451
|
const isActive = match !== null && dismissedIndex !== match.index;
|
|
2883
|
-
const matchRef =
|
|
3452
|
+
const matchRef = useRef8(match);
|
|
2884
3453
|
matchRef.current = match;
|
|
2885
|
-
const caretRef =
|
|
3454
|
+
const caretRef = useRef8(safeCaret);
|
|
2886
3455
|
caretRef.current = safeCaret;
|
|
2887
|
-
const itemsRef =
|
|
3456
|
+
const itemsRef = useRef8(items);
|
|
2888
3457
|
itemsRef.current = items;
|
|
2889
|
-
const activeIndexRef =
|
|
3458
|
+
const activeIndexRef = useRef8(activeIndex);
|
|
2890
3459
|
activeIndexRef.current = activeIndex;
|
|
2891
3460
|
const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
|
|
2892
|
-
|
|
3461
|
+
useEffect8(() => {
|
|
2893
3462
|
const current = matchRef.current;
|
|
2894
3463
|
if (requestKey === null || current === null) {
|
|
2895
3464
|
setItems([]);
|
|
@@ -2939,23 +3508,23 @@ function useComposerAutocomplete(options) {
|
|
|
2939
3508
|
const activeItem = isOpen ? items[activeIndex] ?? null : null;
|
|
2940
3509
|
const reactId = useId();
|
|
2941
3510
|
const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
|
|
2942
|
-
const optionId =
|
|
3511
|
+
const optionId = useCallback7((index) => `${listboxId}-option-${index}`, [
|
|
2943
3512
|
listboxId
|
|
2944
3513
|
]);
|
|
2945
|
-
const highlight =
|
|
3514
|
+
const highlight = useCallback7((index) => {
|
|
2946
3515
|
const total = itemsRef.current.length;
|
|
2947
3516
|
if (total === 0) return;
|
|
2948
3517
|
setActiveIndex((index % total + total) % total);
|
|
2949
3518
|
}, []);
|
|
2950
|
-
const moveHighlight =
|
|
3519
|
+
const moveHighlight = useCallback7((delta) => highlight(activeIndexRef.current + delta), [
|
|
2951
3520
|
highlight
|
|
2952
3521
|
]);
|
|
2953
|
-
const dismiss =
|
|
3522
|
+
const dismiss = useCallback7(() => {
|
|
2954
3523
|
const current = matchRef.current;
|
|
2955
3524
|
if (current) setDismissedIndex(current.index);
|
|
2956
3525
|
}, []);
|
|
2957
|
-
const pendingCaret =
|
|
2958
|
-
const accept =
|
|
3526
|
+
const pendingCaret = useRef8(null);
|
|
3527
|
+
const accept = useCallback7((item) => {
|
|
2959
3528
|
const current = matchRef.current;
|
|
2960
3529
|
if (!current) return;
|
|
2961
3530
|
const chosen = item ?? itemsRef.current[activeIndexRef.current];
|
|
@@ -2972,10 +3541,10 @@ function useComposerAutocomplete(options) {
|
|
|
2972
3541
|
pendingCaret.current = null;
|
|
2973
3542
|
inputRef.current?.setSelectionRange(position, position);
|
|
2974
3543
|
});
|
|
2975
|
-
const readCaret =
|
|
3544
|
+
const readCaret = useCallback7((element) => {
|
|
2976
3545
|
setCaret(element.selectionStart ?? element.value.length);
|
|
2977
3546
|
}, []);
|
|
2978
|
-
const onKeyDown =
|
|
3547
|
+
const onKeyDown = useCallback7((event) => {
|
|
2979
3548
|
if (!isOpen) return;
|
|
2980
3549
|
const hasItems = itemsRef.current.length > 0;
|
|
2981
3550
|
switch (event.key) {
|
|
@@ -3013,23 +3582,23 @@ function useComposerAutocomplete(options) {
|
|
|
3013
3582
|
dismiss,
|
|
3014
3583
|
moveHighlight
|
|
3015
3584
|
]);
|
|
3016
|
-
const onChange =
|
|
3585
|
+
const onChange = useCallback7((event) => {
|
|
3017
3586
|
readCaret(event.target);
|
|
3018
3587
|
const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
|
|
3019
3588
|
setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
|
|
3020
3589
|
}, [
|
|
3021
3590
|
readCaret
|
|
3022
3591
|
]);
|
|
3023
|
-
const onSelect =
|
|
3592
|
+
const onSelect = useCallback7((event) => readCaret(event.currentTarget), [
|
|
3024
3593
|
readCaret
|
|
3025
3594
|
]);
|
|
3026
|
-
const onClick =
|
|
3595
|
+
const onClick = useCallback7((event) => readCaret(event.currentTarget), [
|
|
3027
3596
|
readCaret
|
|
3028
3597
|
]);
|
|
3029
|
-
const setInput =
|
|
3598
|
+
const setInput = useCallback7((element) => {
|
|
3030
3599
|
inputRef.current = element;
|
|
3031
3600
|
}, []);
|
|
3032
|
-
const getInputProps =
|
|
3601
|
+
const getInputProps = useCallback7(() => ({
|
|
3033
3602
|
ref: setInput,
|
|
3034
3603
|
onChange,
|
|
3035
3604
|
onKeyDown,
|
|
@@ -3057,7 +3626,7 @@ function useComposerAutocomplete(options) {
|
|
|
3057
3626
|
activeIndex,
|
|
3058
3627
|
optionId
|
|
3059
3628
|
]);
|
|
3060
|
-
const getListboxProps =
|
|
3629
|
+
const getListboxProps = useCallback7(() => ({
|
|
3061
3630
|
id: listboxId,
|
|
3062
3631
|
role: "listbox",
|
|
3063
3632
|
"aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
|
|
@@ -3065,7 +3634,7 @@ function useComposerAutocomplete(options) {
|
|
|
3065
3634
|
listboxId,
|
|
3066
3635
|
match
|
|
3067
3636
|
]);
|
|
3068
|
-
const getOptionProps =
|
|
3637
|
+
const getOptionProps = useCallback7((index) => ({
|
|
3069
3638
|
id: optionId(index),
|
|
3070
3639
|
role: "option",
|
|
3071
3640
|
"aria-selected": index === activeIndex
|
|
@@ -3099,7 +3668,7 @@ function isPromise(value) {
|
|
|
3099
3668
|
__name(isPromise, "isPromise");
|
|
3100
3669
|
|
|
3101
3670
|
// src/presentation/use-tool-catalog.ts
|
|
3102
|
-
import { useCallback as
|
|
3671
|
+
import { useCallback as useCallback8, useEffect as useEffect9, useMemo as useMemo5, useState as useState10 } from "react";
|
|
3103
3672
|
var shared = /* @__PURE__ */ new WeakMap();
|
|
3104
3673
|
function load(client, agent) {
|
|
3105
3674
|
let byAgent = shared.get(client);
|
|
@@ -3110,6 +3679,9 @@ function load(client, agent) {
|
|
|
3110
3679
|
const key = agent ?? "";
|
|
3111
3680
|
const cached = byAgent.get(key);
|
|
3112
3681
|
if (cached !== void 0) return cached;
|
|
3682
|
+
if (client.listTools === void 0) {
|
|
3683
|
+
return Promise.reject(new AgentBackendUnsupportedError("listTools"));
|
|
3684
|
+
}
|
|
3113
3685
|
const request = client.listTools(agent).catch((error) => {
|
|
3114
3686
|
byAgent.delete(key);
|
|
3115
3687
|
throw error;
|
|
@@ -3120,11 +3692,11 @@ function load(client, agent) {
|
|
|
3120
3692
|
__name(load, "load");
|
|
3121
3693
|
function useToolCatalog(options) {
|
|
3122
3694
|
const { client, agent, enabled = true } = options;
|
|
3123
|
-
const [entries, setEntries] =
|
|
3124
|
-
const [isLoading, setLoading] =
|
|
3125
|
-
const [error, setError] =
|
|
3126
|
-
const [generation, setGeneration] =
|
|
3127
|
-
|
|
3695
|
+
const [entries, setEntries] = useState10([]);
|
|
3696
|
+
const [isLoading, setLoading] = useState10(enabled);
|
|
3697
|
+
const [error, setError] = useState10(null);
|
|
3698
|
+
const [generation, setGeneration] = useState10(0);
|
|
3699
|
+
useEffect9(() => {
|
|
3128
3700
|
if (!enabled) {
|
|
3129
3701
|
setLoading(false);
|
|
3130
3702
|
return;
|
|
@@ -3150,14 +3722,14 @@ function useToolCatalog(options) {
|
|
|
3150
3722
|
enabled,
|
|
3151
3723
|
generation
|
|
3152
3724
|
]);
|
|
3153
|
-
const refresh =
|
|
3725
|
+
const refresh = useCallback8(() => {
|
|
3154
3726
|
shared.get(client)?.delete(agent ?? "");
|
|
3155
3727
|
setGeneration((current) => current + 1);
|
|
3156
3728
|
}, [
|
|
3157
3729
|
client,
|
|
3158
3730
|
agent
|
|
3159
3731
|
]);
|
|
3160
|
-
const catalog =
|
|
3732
|
+
const catalog = useMemo5(() => toolCatalogFrom(entries), [
|
|
3161
3733
|
entries
|
|
3162
3734
|
]);
|
|
3163
3735
|
return {
|
|
@@ -3171,7 +3743,7 @@ function useToolCatalog(options) {
|
|
|
3171
3743
|
__name(useToolCatalog, "useToolCatalog");
|
|
3172
3744
|
|
|
3173
3745
|
// src/approvals/countdown.ts
|
|
3174
|
-
import { useEffect as
|
|
3746
|
+
import { useEffect as useEffect10, useRef as useRef9, useState as useState11 } from "react";
|
|
3175
3747
|
function approvalCountdown(expiresAt, now = Date.now()) {
|
|
3176
3748
|
const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
|
|
3177
3749
|
if (!Number.isFinite(deadline)) {
|
|
@@ -3189,10 +3761,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
|
|
|
3189
3761
|
__name(approvalCountdown, "approvalCountdown");
|
|
3190
3762
|
function useApprovalCountdown(expiresAt, options = {}) {
|
|
3191
3763
|
const { intervalMs = 1e3 } = options;
|
|
3192
|
-
const nowRef =
|
|
3764
|
+
const nowRef = useRef9(options.now ?? Date.now);
|
|
3193
3765
|
nowRef.current = options.now ?? Date.now;
|
|
3194
|
-
const [state, setState] =
|
|
3195
|
-
|
|
3766
|
+
const [state, setState] = useState11(() => approvalCountdown(expiresAt, nowRef.current()));
|
|
3767
|
+
useEffect10(() => {
|
|
3196
3768
|
const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
|
|
3197
3769
|
const first = read();
|
|
3198
3770
|
setState(first);
|
|
@@ -3358,7 +3930,12 @@ function storedMessageToUiMessage(message) {
|
|
|
3358
3930
|
return {
|
|
3359
3931
|
id: message.id,
|
|
3360
3932
|
role: message.role,
|
|
3361
|
-
parts
|
|
3933
|
+
parts,
|
|
3934
|
+
...message.feedback !== void 0 ? {
|
|
3935
|
+
metadata: {
|
|
3936
|
+
feedback: message.feedback
|
|
3937
|
+
}
|
|
3938
|
+
} : {}
|
|
3362
3939
|
};
|
|
3363
3940
|
}
|
|
3364
3941
|
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
@@ -3394,14 +3971,21 @@ function mergeTurn(rows) {
|
|
|
3394
3971
|
}
|
|
3395
3972
|
const parts = rows.flatMap((row) => storedMessageToUiMessage(row).parts);
|
|
3396
3973
|
const usage = sumUsage(rows);
|
|
3974
|
+
const feedback = lastRow.feedback;
|
|
3975
|
+
const metadata = {
|
|
3976
|
+
...usage !== void 0 ? {
|
|
3977
|
+
usage
|
|
3978
|
+
} : {},
|
|
3979
|
+
...feedback !== void 0 ? {
|
|
3980
|
+
feedback
|
|
3981
|
+
} : {}
|
|
3982
|
+
};
|
|
3397
3983
|
return {
|
|
3398
3984
|
id: lastRow.id,
|
|
3399
3985
|
role: lastRow.role,
|
|
3400
3986
|
parts,
|
|
3401
|
-
...
|
|
3402
|
-
metadata
|
|
3403
|
-
usage
|
|
3404
|
-
}
|
|
3987
|
+
...Object.keys(metadata).length > 0 ? {
|
|
3988
|
+
metadata
|
|
3405
3989
|
} : {}
|
|
3406
3990
|
};
|
|
3407
3991
|
}
|
|
@@ -3422,20 +4006,21 @@ __name(sumUsage, "sumUsage");
|
|
|
3422
4006
|
|
|
3423
4007
|
// src/use-agent-chat.ts
|
|
3424
4008
|
import { useChat } from "@ai-sdk/react";
|
|
3425
|
-
import { useCallback as
|
|
4009
|
+
import { useCallback as useCallback9, useEffect as useEffect11, useMemo as useMemo6, useRef as useRef10, useState as useState12 } from "react";
|
|
3426
4010
|
function useAgentChat(options) {
|
|
3427
|
-
const latest =
|
|
4011
|
+
const latest = useRef10(options);
|
|
3428
4012
|
latest.current = options;
|
|
3429
|
-
const [runId, setRunId] =
|
|
3430
|
-
const runIdRef =
|
|
4013
|
+
const [runId, setRunId] = useState12(options.resumeRunId);
|
|
4014
|
+
const runIdRef = useRef10(runId);
|
|
3431
4015
|
runIdRef.current = runId;
|
|
3432
|
-
const settlingRunIdRef =
|
|
3433
|
-
const autoResumeRunIdRef =
|
|
3434
|
-
const [autoResume, setAutoResume] =
|
|
3435
|
-
const [activeRunId, setActiveRunId] =
|
|
3436
|
-
const regenerateNext =
|
|
3437
|
-
const createdThreadId =
|
|
3438
|
-
const client =
|
|
4016
|
+
const settlingRunIdRef = useRef10(void 0);
|
|
4017
|
+
const autoResumeRunIdRef = useRef10(void 0);
|
|
4018
|
+
const [autoResume, setAutoResume] = useState12(false);
|
|
4019
|
+
const [activeRunId, setActiveRunId] = useState12(null);
|
|
4020
|
+
const regenerateNext = useRef10(false);
|
|
4021
|
+
const createdThreadId = useRef10(void 0);
|
|
4022
|
+
const client = useMemo6(() => {
|
|
4023
|
+
if (options.backend) return options.backend;
|
|
3439
4024
|
if (options.client) return options.client;
|
|
3440
4025
|
return new AgentClient({
|
|
3441
4026
|
...options.baseUrl !== void 0 ? {
|
|
@@ -3450,7 +4035,11 @@ function useAgentChat(options) {
|
|
|
3450
4035
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
3451
4036
|
});
|
|
3452
4037
|
}, []);
|
|
3453
|
-
|
|
4038
|
+
const [connection, setConnection] = useState12({
|
|
4039
|
+
status: "live"
|
|
4040
|
+
});
|
|
4041
|
+
const resyncAfterSettle = useRef10(false);
|
|
4042
|
+
useEffect11(() => {
|
|
3454
4043
|
if (!options.resume || options.threadId === void 0) return;
|
|
3455
4044
|
let cancelled = false;
|
|
3456
4045
|
const threadId = options.threadId;
|
|
@@ -3470,30 +4059,33 @@ function useAgentChat(options) {
|
|
|
3470
4059
|
options.resume,
|
|
3471
4060
|
options.threadId
|
|
3472
4061
|
]);
|
|
3473
|
-
const transport =
|
|
4062
|
+
const transport = useMemo6(() => {
|
|
3474
4063
|
function onMeta(meta) {
|
|
3475
4064
|
settlingRunIdRef.current = meta.runId;
|
|
3476
4065
|
setRunId(meta.runId);
|
|
3477
4066
|
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
3478
4067
|
createdThreadId.current = meta.threadId;
|
|
3479
4068
|
latest.current.onThreadCreated?.(meta.threadId);
|
|
4069
|
+
notifyThreads(client, {
|
|
4070
|
+
type: "changed"
|
|
4071
|
+
});
|
|
3480
4072
|
}
|
|
3481
4073
|
}
|
|
3482
4074
|
__name(onMeta, "onMeta");
|
|
3483
4075
|
return new AgentChatTransport({
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
...options.credentials !== void 0 ? {
|
|
3488
|
-
credentials: options.credentials
|
|
3489
|
-
} : {},
|
|
3490
|
-
...options.fetch !== void 0 ? {
|
|
3491
|
-
fetch: options.fetch
|
|
4076
|
+
backend: client,
|
|
4077
|
+
...options.reconnect !== void 0 ? {
|
|
4078
|
+
reconnect: options.reconnect
|
|
3492
4079
|
} : {},
|
|
4080
|
+
onConnectionChange: /* @__PURE__ */ __name((state) => {
|
|
4081
|
+
if (state.status === "gone") resyncAfterSettle.current = true;
|
|
4082
|
+
setConnection(state.status === "gone" ? {
|
|
4083
|
+
status: "live"
|
|
4084
|
+
} : state);
|
|
4085
|
+
}, "onConnectionChange"),
|
|
3493
4086
|
...options.agent !== void 0 ? {
|
|
3494
4087
|
agent: options.agent
|
|
3495
4088
|
} : {},
|
|
3496
|
-
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders"),
|
|
3497
4089
|
getBody: /* @__PURE__ */ __name(() => {
|
|
3498
4090
|
const current = latest.current;
|
|
3499
4091
|
const pageContext = current.getPageContext?.() ?? null;
|
|
@@ -3504,6 +4096,9 @@ function useAgentChat(options) {
|
|
|
3504
4096
|
...threadId !== void 0 ? {
|
|
3505
4097
|
threadId
|
|
3506
4098
|
} : {},
|
|
4099
|
+
...current.model !== void 0 ? {
|
|
4100
|
+
model: current.model
|
|
4101
|
+
} : {},
|
|
3507
4102
|
...pageContext ? {
|
|
3508
4103
|
pageContext
|
|
3509
4104
|
} : {},
|
|
@@ -3519,7 +4114,7 @@ function useAgentChat(options) {
|
|
|
3519
4114
|
}, "onAttemptStart")
|
|
3520
4115
|
});
|
|
3521
4116
|
}, []);
|
|
3522
|
-
const backgroundRef =
|
|
4117
|
+
const backgroundRef = useRef10(() => Promise.resolve());
|
|
3523
4118
|
const chat = useChat({
|
|
3524
4119
|
transport,
|
|
3525
4120
|
resume: options.resumeRunId !== void 0 || autoResume,
|
|
@@ -3533,7 +4128,15 @@ function useAgentChat(options) {
|
|
|
3533
4128
|
latest.current.onData?.(part);
|
|
3534
4129
|
if (part.type === "data-title") {
|
|
3535
4130
|
const title = part.data?.title;
|
|
3536
|
-
if (typeof title === "string")
|
|
4131
|
+
if (typeof title === "string") {
|
|
4132
|
+
latest.current.onTitle?.(title);
|
|
4133
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4134
|
+
if (threadId !== void 0) notifyThreads(client, {
|
|
4135
|
+
type: "title",
|
|
4136
|
+
threadId,
|
|
4137
|
+
title
|
|
4138
|
+
});
|
|
4139
|
+
}
|
|
3537
4140
|
}
|
|
3538
4141
|
}, "onData"),
|
|
3539
4142
|
onFinish: /* @__PURE__ */ __name(({ isError }) => {
|
|
@@ -3547,16 +4150,36 @@ function useAgentChat(options) {
|
|
|
3547
4150
|
runId: settledRunId,
|
|
3548
4151
|
status: isError ? "failed" : "completed"
|
|
3549
4152
|
});
|
|
4153
|
+
notifyThreads(client, {
|
|
4154
|
+
type: "changed"
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
setConnection({
|
|
4158
|
+
status: "live"
|
|
4159
|
+
});
|
|
4160
|
+
if (resyncAfterSettle.current) {
|
|
4161
|
+
resyncAfterSettle.current = false;
|
|
4162
|
+
void resyncFromThread();
|
|
3550
4163
|
}
|
|
3551
4164
|
}, "onFinish")
|
|
3552
4165
|
});
|
|
3553
|
-
const chatRef =
|
|
4166
|
+
const chatRef = useRef10(chat);
|
|
3554
4167
|
chatRef.current = chat;
|
|
3555
|
-
|
|
3556
|
-
|
|
4168
|
+
async function resyncFromThread() {
|
|
4169
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4170
|
+
if (threadId === void 0) return;
|
|
4171
|
+
try {
|
|
4172
|
+
const thread = await client.getThread(threadId);
|
|
4173
|
+
chatRef.current.setMessages(storedThreadToUiMessages(thread.messages));
|
|
4174
|
+
} catch {
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
__name(resyncFromThread, "resyncFromThread");
|
|
4178
|
+
const turnInFlight = useRef10(false);
|
|
4179
|
+
const isTurnInFlight = useCallback9(() => turnInFlight.current || transport.isAttemptLive, [
|
|
3557
4180
|
transport
|
|
3558
4181
|
]);
|
|
3559
|
-
const sendMessage =
|
|
4182
|
+
const sendMessage = useCallback9(async (...args) => {
|
|
3560
4183
|
if (isTurnInFlight()) return;
|
|
3561
4184
|
turnInFlight.current = true;
|
|
3562
4185
|
try {
|
|
@@ -3567,15 +4190,15 @@ function useAgentChat(options) {
|
|
|
3567
4190
|
}, [
|
|
3568
4191
|
isTurnInFlight
|
|
3569
4192
|
]);
|
|
3570
|
-
const addToolResult =
|
|
4193
|
+
const addToolResult = useCallback9(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
3571
4194
|
tool,
|
|
3572
4195
|
toolCallId,
|
|
3573
4196
|
output
|
|
3574
4197
|
}), []);
|
|
3575
|
-
const [threads, setThreads] =
|
|
3576
|
-
const [quota, setQuota] =
|
|
3577
|
-
const [backgroundRuns, setBackgroundRuns] =
|
|
3578
|
-
const refreshBackground =
|
|
4198
|
+
const [threads, setThreads] = useState12([]);
|
|
4199
|
+
const [quota, setQuota] = useState12(null);
|
|
4200
|
+
const [backgroundRuns, setBackgroundRuns] = useState12([]);
|
|
4201
|
+
const refreshBackground = useCallback9(async () => {
|
|
3579
4202
|
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
3580
4203
|
if (threadId === void 0) {
|
|
3581
4204
|
return;
|
|
@@ -3600,7 +4223,7 @@ function useAgentChat(options) {
|
|
|
3600
4223
|
]);
|
|
3601
4224
|
backgroundRef.current = refreshBackground;
|
|
3602
4225
|
const isWorking = backgroundRuns.some((run) => run.status === "running");
|
|
3603
|
-
|
|
4226
|
+
useEffect11(() => {
|
|
3604
4227
|
if (options.background !== true || options.threadId === void 0) {
|
|
3605
4228
|
return;
|
|
3606
4229
|
}
|
|
@@ -3610,7 +4233,7 @@ function useAgentChat(options) {
|
|
|
3610
4233
|
options.threadId,
|
|
3611
4234
|
refreshBackground
|
|
3612
4235
|
]);
|
|
3613
|
-
|
|
4236
|
+
useEffect11(() => {
|
|
3614
4237
|
const every = latest.current.backgroundPollMs ?? 5e3;
|
|
3615
4238
|
if (latest.current.background !== true || !isWorking || every <= 0) {
|
|
3616
4239
|
return;
|
|
@@ -3621,27 +4244,29 @@ function useAgentChat(options) {
|
|
|
3621
4244
|
isWorking,
|
|
3622
4245
|
refreshBackground
|
|
3623
4246
|
]);
|
|
3624
|
-
const loadThreads =
|
|
4247
|
+
const loadThreads = useCallback9(async () => {
|
|
3625
4248
|
const list = await client.listThreads();
|
|
3626
4249
|
setThreads(list);
|
|
3627
4250
|
return list;
|
|
3628
4251
|
}, [
|
|
3629
4252
|
client
|
|
3630
4253
|
]);
|
|
3631
|
-
const loadThread =
|
|
4254
|
+
const loadThread = useCallback9((id) => client.getThread(id), [
|
|
3632
4255
|
client
|
|
3633
4256
|
]);
|
|
3634
|
-
const deleteThread =
|
|
4257
|
+
const deleteThread = useCallback9(async (id) => {
|
|
3635
4258
|
await client.deleteThread(id);
|
|
3636
4259
|
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
3637
4260
|
}, [
|
|
3638
4261
|
client
|
|
3639
4262
|
]);
|
|
3640
|
-
const forkThread =
|
|
4263
|
+
const forkThread = useCallback9((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
|
|
3641
4264
|
client
|
|
3642
4265
|
]);
|
|
3643
|
-
const renameThread =
|
|
3644
|
-
await client.
|
|
4266
|
+
const renameThread = useCallback9(async (id, title) => {
|
|
4267
|
+
await client.updateThread(id, {
|
|
4268
|
+
title
|
|
4269
|
+
});
|
|
3645
4270
|
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
3646
4271
|
...thread,
|
|
3647
4272
|
title
|
|
@@ -3649,24 +4274,24 @@ function useAgentChat(options) {
|
|
|
3649
4274
|
}, [
|
|
3650
4275
|
client
|
|
3651
4276
|
]);
|
|
3652
|
-
const promoteThread =
|
|
3653
|
-
await client
|
|
4277
|
+
const promoteThread = useCallback9(async (id) => {
|
|
4278
|
+
await requireBackendMethod(client, "promoteThread")(id);
|
|
3654
4279
|
await loadThreads();
|
|
3655
4280
|
}, [
|
|
3656
4281
|
client,
|
|
3657
4282
|
loadThreads
|
|
3658
4283
|
]);
|
|
3659
|
-
const truncateFromMessage =
|
|
4284
|
+
const truncateFromMessage = useCallback9((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
|
|
3660
4285
|
client
|
|
3661
4286
|
]);
|
|
3662
|
-
const loadQuota =
|
|
3663
|
-
const today = await client
|
|
4287
|
+
const loadQuota = useCallback9(async () => {
|
|
4288
|
+
const today = await requireBackendMethod(client, "getQuotaToday")();
|
|
3664
4289
|
setQuota(today);
|
|
3665
4290
|
return today;
|
|
3666
4291
|
}, [
|
|
3667
4292
|
client
|
|
3668
4293
|
]);
|
|
3669
|
-
const cancel =
|
|
4294
|
+
const cancel = useCallback9(async () => {
|
|
3670
4295
|
chatRef.current.stop();
|
|
3671
4296
|
const activeRunId2 = runIdRef.current;
|
|
3672
4297
|
if (activeRunId2) {
|
|
@@ -3678,8 +4303,8 @@ function useAgentChat(options) {
|
|
|
3678
4303
|
}, [
|
|
3679
4304
|
client
|
|
3680
4305
|
]);
|
|
3681
|
-
const approve =
|
|
3682
|
-
await client
|
|
4306
|
+
const approve = useCallback9(async ({ toolCallId, remember, via }) => {
|
|
4307
|
+
await requireBackendMethod(client, "approveToolCall")({
|
|
3683
4308
|
toolCallId,
|
|
3684
4309
|
...remember === true ? {
|
|
3685
4310
|
remember: true
|
|
@@ -3691,8 +4316,8 @@ function useAgentChat(options) {
|
|
|
3691
4316
|
}, [
|
|
3692
4317
|
client
|
|
3693
4318
|
]);
|
|
3694
|
-
const reject =
|
|
3695
|
-
await client
|
|
4319
|
+
const reject = useCallback9(async ({ toolCallId, reason, via }) => {
|
|
4320
|
+
await requireBackendMethod(client, "rejectToolCall")({
|
|
3696
4321
|
toolCallId,
|
|
3697
4322
|
...reason !== void 0 ? {
|
|
3698
4323
|
reason
|
|
@@ -3704,8 +4329,8 @@ function useAgentChat(options) {
|
|
|
3704
4329
|
}, [
|
|
3705
4330
|
client
|
|
3706
4331
|
]);
|
|
3707
|
-
const answer =
|
|
3708
|
-
await client
|
|
4332
|
+
const answer = useCallback9(async ({ toolCallId, answers }) => {
|
|
4333
|
+
await requireBackendMethod(client, "answerToolCall")({
|
|
3709
4334
|
toolCallId,
|
|
3710
4335
|
...answers !== void 0 ? {
|
|
3711
4336
|
answers
|
|
@@ -3714,14 +4339,14 @@ function useAgentChat(options) {
|
|
|
3714
4339
|
}, [
|
|
3715
4340
|
client
|
|
3716
4341
|
]);
|
|
3717
|
-
const skip =
|
|
3718
|
-
await client
|
|
4342
|
+
const skip = useCallback9(async ({ toolCallId }) => {
|
|
4343
|
+
await requireBackendMethod(client, "skipToolCall")({
|
|
3719
4344
|
toolCallId
|
|
3720
4345
|
});
|
|
3721
4346
|
}, [
|
|
3722
4347
|
client
|
|
3723
4348
|
]);
|
|
3724
|
-
const regenerate =
|
|
4349
|
+
const regenerate = useCallback9(() => {
|
|
3725
4350
|
if (isTurnInFlight()) return;
|
|
3726
4351
|
regenerateNext.current = true;
|
|
3727
4352
|
turnInFlight.current = true;
|
|
@@ -3736,8 +4361,27 @@ function useAgentChat(options) {
|
|
|
3736
4361
|
isWorking,
|
|
3737
4362
|
refresh: refreshBackground
|
|
3738
4363
|
};
|
|
4364
|
+
const setThreadModel = useCallback9(async (model) => {
|
|
4365
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4366
|
+
if (threadId === void 0) {
|
|
4367
|
+
throw new Error("setThreadModel: this chat has no thread yet");
|
|
4368
|
+
}
|
|
4369
|
+
await client.updateThread(threadId, {
|
|
4370
|
+
model
|
|
4371
|
+
});
|
|
4372
|
+
notifyThreads(client, {
|
|
4373
|
+
type: "changed"
|
|
4374
|
+
});
|
|
4375
|
+
}, [
|
|
4376
|
+
client
|
|
4377
|
+
]);
|
|
4378
|
+
const getThreadId = useCallback9(() => latest.current.threadId ?? createdThreadId.current, []);
|
|
4379
|
+
const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
|
|
3739
4380
|
return {
|
|
3740
4381
|
...chat,
|
|
4382
|
+
status,
|
|
4383
|
+
/** Where the live stream stands — see {@link StreamConnectionState}. */
|
|
4384
|
+
connection,
|
|
3741
4385
|
sendMessage,
|
|
3742
4386
|
addToolResult,
|
|
3743
4387
|
/** Sub-agents this conversation started and did not wait for. */
|
|
@@ -3745,6 +4389,15 @@ function useAgentChat(options) {
|
|
|
3745
4389
|
runId,
|
|
3746
4390
|
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
3747
4391
|
activeRunId,
|
|
4392
|
+
/**
|
|
4393
|
+
* The thread this chat is on right now: the `threadId` option, else the one the backend created
|
|
4394
|
+
* on the first send. A getter, because the created id is learned mid-stream.
|
|
4395
|
+
*/
|
|
4396
|
+
getThreadId,
|
|
4397
|
+
setThreadModel,
|
|
4398
|
+
/** The backend this chat talks to — pass it to `useThreads`, `useMessageFeedback`, … */
|
|
4399
|
+
backend: client,
|
|
4400
|
+
/** Same object as {@link backend}; kept for callers that read it under its old name. */
|
|
3748
4401
|
client,
|
|
3749
4402
|
threads,
|
|
3750
4403
|
loadThreads,
|
|
@@ -3774,6 +4427,7 @@ async function mergeHeaders(options) {
|
|
|
3774
4427
|
}
|
|
3775
4428
|
__name(mergeHeaders, "mergeHeaders");
|
|
3776
4429
|
export {
|
|
4430
|
+
AgentBackendUnsupportedError,
|
|
3777
4431
|
AgentChatTransport,
|
|
3778
4432
|
AgentClient,
|
|
3779
4433
|
AgentHttpError,
|
|
@@ -3803,22 +4457,29 @@ export {
|
|
|
3803
4457
|
inferResultView,
|
|
3804
4458
|
isActionCall,
|
|
3805
4459
|
loadStoredSpeechLang,
|
|
4460
|
+
notifyThreads,
|
|
4461
|
+
onThreadsEvent,
|
|
3806
4462
|
persistSpeechLang,
|
|
3807
4463
|
phraseFor,
|
|
3808
4464
|
readPath,
|
|
3809
4465
|
readReasoningMs,
|
|
4466
|
+
requireBackendMethod,
|
|
3810
4467
|
resolveResultView,
|
|
3811
4468
|
storedMessageToUiMessage,
|
|
3812
4469
|
storedThreadToUiMessages,
|
|
3813
4470
|
toolCallState,
|
|
3814
4471
|
toolCatalogFrom,
|
|
3815
4472
|
useAgentChat,
|
|
4473
|
+
useAgents,
|
|
3816
4474
|
useApprovalCountdown,
|
|
3817
4475
|
useChatTranscript,
|
|
3818
4476
|
useComposerAutocomplete,
|
|
3819
4477
|
useElapsed,
|
|
4478
|
+
useMessageFeedback,
|
|
4479
|
+
useModels,
|
|
3820
4480
|
useSpeechRecognition,
|
|
3821
4481
|
useStickToBottom,
|
|
4482
|
+
useThreads,
|
|
3822
4483
|
useToolCatalog,
|
|
3823
4484
|
useTranscriptItem,
|
|
3824
4485
|
validateElicitationAnswer as validateAnswer,
|