@dudousxd/nestjs-agent-react 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -1
- package/dist/genui.d.cts +1 -1
- package/dist/genui.d.ts +1 -1
- package/dist/index.cjs +970 -432
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +316 -31
- package/dist/index.d.ts +316 -31
- package/dist/index.js +931 -399
- 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,10 +61,13 @@ __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,
|
|
@@ -74,8 +78,10 @@ __export(src_exports, {
|
|
|
74
78
|
useChatTranscript: () => useChatTranscript,
|
|
75
79
|
useComposerAutocomplete: () => useComposerAutocomplete,
|
|
76
80
|
useElapsed: () => useElapsed,
|
|
81
|
+
useMessageFeedback: () => useMessageFeedback,
|
|
77
82
|
useSpeechRecognition: () => useSpeechRecognition,
|
|
78
83
|
useStickToBottom: () => useStickToBottom,
|
|
84
|
+
useThreads: () => useThreads,
|
|
79
85
|
useToolCatalog: () => useToolCatalog,
|
|
80
86
|
useTranscriptItem: () => useTranscriptItem,
|
|
81
87
|
validateAnswer: () => import_nestjs_agent_core.validateElicitationAnswer,
|
|
@@ -83,6 +89,246 @@ __export(src_exports, {
|
|
|
83
89
|
});
|
|
84
90
|
module.exports = __toCommonJS(src_exports);
|
|
85
91
|
|
|
92
|
+
// src/client.ts
|
|
93
|
+
var AgentHttpError = class extends Error {
|
|
94
|
+
static {
|
|
95
|
+
__name(this, "AgentHttpError");
|
|
96
|
+
}
|
|
97
|
+
status;
|
|
98
|
+
method;
|
|
99
|
+
path;
|
|
100
|
+
constructor(status, method, path, statusText) {
|
|
101
|
+
super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
|
|
102
|
+
this.name = "AgentHttpError";
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
var HEADER_RUN_ID = "x-agent-run-id";
|
|
106
|
+
var HEADER_THREAD_ID = "x-agent-thread-id";
|
|
107
|
+
var AgentClient = class {
|
|
108
|
+
static {
|
|
109
|
+
__name(this, "AgentClient");
|
|
110
|
+
}
|
|
111
|
+
options;
|
|
112
|
+
constructor(options = {}) {
|
|
113
|
+
this.options = options;
|
|
114
|
+
}
|
|
115
|
+
/** `POST /agent/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
|
|
116
|
+
async openChatStream(request) {
|
|
117
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {
|
|
118
|
+
method: "POST",
|
|
119
|
+
headers: {
|
|
120
|
+
"content-type": "application/json",
|
|
121
|
+
accept: "text/event-stream",
|
|
122
|
+
...await this.resolveHeaders(),
|
|
123
|
+
...request.headers
|
|
124
|
+
},
|
|
125
|
+
body: JSON.stringify(request.body),
|
|
126
|
+
...this.credentials(),
|
|
127
|
+
...request.signal !== void 0 ? {
|
|
128
|
+
signal: request.signal
|
|
129
|
+
} : {}
|
|
130
|
+
});
|
|
131
|
+
if (!response.ok || !response.body) {
|
|
132
|
+
throw new AgentHttpError(response.status, "POST", "/agent/chat", response.statusText);
|
|
133
|
+
}
|
|
134
|
+
return streamResponse(response);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* `GET /agent/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
|
|
138
|
+
* streaming under that id (404).
|
|
139
|
+
*/
|
|
140
|
+
async resumeChatStream(request) {
|
|
141
|
+
const path = `/agent/chat/${encodeURIComponent(request.runId)}/stream`;
|
|
142
|
+
const query = request.after !== void 0 && request.after > 0 ? `?after=${request.after}` : "";
|
|
143
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}${query}`, {
|
|
144
|
+
method: "GET",
|
|
145
|
+
headers: {
|
|
146
|
+
accept: "text/event-stream",
|
|
147
|
+
...await this.resolveHeaders(),
|
|
148
|
+
...request.headers
|
|
149
|
+
},
|
|
150
|
+
...this.credentials(),
|
|
151
|
+
...request.signal !== void 0 ? {
|
|
152
|
+
signal: request.signal
|
|
153
|
+
} : {}
|
|
154
|
+
});
|
|
155
|
+
if (response.status === 404) {
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
if (!response.ok || !response.body) {
|
|
159
|
+
throw new AgentHttpError(response.status, "GET", path, response.statusText);
|
|
160
|
+
}
|
|
161
|
+
return streamResponse(response);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Rate a message (`'up'`/`'down'`, optional comment) or clear its rating (`value: null`). Answers
|
|
165
|
+
* the stored rating.
|
|
166
|
+
*/
|
|
167
|
+
setMessageFeedback(messageId, input) {
|
|
168
|
+
return this.request("POST", `/agent/messages/${encodeURIComponent(messageId)}/feedback`, input);
|
|
169
|
+
}
|
|
170
|
+
listThreads() {
|
|
171
|
+
return this.request("GET", "/agent/threads");
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
175
|
+
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
176
|
+
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
177
|
+
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
178
|
+
*/
|
|
179
|
+
listSkills(threadId) {
|
|
180
|
+
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
181
|
+
return this.request("GET", `/agent/skills${query}`);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
|
|
185
|
+
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
|
|
186
|
+
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
|
|
187
|
+
*/
|
|
188
|
+
listTools(agent) {
|
|
189
|
+
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
190
|
+
return this.request("GET", `/agent/tools${query}`);
|
|
191
|
+
}
|
|
192
|
+
getThread(id) {
|
|
193
|
+
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
194
|
+
}
|
|
195
|
+
deleteThread(id) {
|
|
196
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
|
|
197
|
+
}
|
|
198
|
+
forkFromMessage(threadId, messageId) {
|
|
199
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
200
|
+
}
|
|
201
|
+
renameThread(id, title) {
|
|
202
|
+
return this.updateThread(id, {
|
|
203
|
+
title
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
207
|
+
updateThread(id, patch) {
|
|
208
|
+
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
212
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
213
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
214
|
+
*/
|
|
215
|
+
async uploadAttachment(file, options = {}) {
|
|
216
|
+
const formData = new FormData();
|
|
217
|
+
formData.append("file", file);
|
|
218
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
|
|
219
|
+
method: "POST",
|
|
220
|
+
headers: {
|
|
221
|
+
accept: "application/json",
|
|
222
|
+
...await this.resolveHeaders()
|
|
223
|
+
},
|
|
224
|
+
body: formData,
|
|
225
|
+
...this.credentials(),
|
|
226
|
+
...options.signal !== void 0 ? {
|
|
227
|
+
signal: options.signal
|
|
228
|
+
} : {}
|
|
229
|
+
});
|
|
230
|
+
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
231
|
+
}
|
|
232
|
+
promoteThread(id) {
|
|
233
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
234
|
+
}
|
|
235
|
+
truncateFromMessage(threadId, messageId) {
|
|
236
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
|
|
237
|
+
}
|
|
238
|
+
getQuotaToday() {
|
|
239
|
+
return this.request("GET", "/agent/quota/today");
|
|
240
|
+
}
|
|
241
|
+
cancelStream(runId) {
|
|
242
|
+
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
246
|
+
* the decision came through (the server records `'web'` when omitted).
|
|
247
|
+
*/
|
|
248
|
+
approveToolCall(input) {
|
|
249
|
+
return this.request("POST", "/agent/tool-call/approve", input);
|
|
250
|
+
}
|
|
251
|
+
rejectToolCall(input) {
|
|
252
|
+
return this.request("POST", "/agent/tool-call/reject", input);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
256
|
+
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
257
|
+
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
258
|
+
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
259
|
+
*/
|
|
260
|
+
answerToolCall(input) {
|
|
261
|
+
return this.request("POST", "/agent/tool-call/answer", input);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
265
|
+
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
266
|
+
* evidence the user chose them.
|
|
267
|
+
*/
|
|
268
|
+
skipToolCall(input) {
|
|
269
|
+
return this.request("POST", "/agent/tool-call/skip", input);
|
|
270
|
+
}
|
|
271
|
+
fetchImpl() {
|
|
272
|
+
return this.options.fetch ?? globalThis.fetch;
|
|
273
|
+
}
|
|
274
|
+
baseUrl() {
|
|
275
|
+
return (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
276
|
+
}
|
|
277
|
+
async resolveHeaders() {
|
|
278
|
+
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
279
|
+
return {
|
|
280
|
+
...this.options.headers,
|
|
281
|
+
...dynamic
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
credentials() {
|
|
285
|
+
return this.options.credentials !== void 0 ? {
|
|
286
|
+
credentials: this.options.credentials
|
|
287
|
+
} : {};
|
|
288
|
+
}
|
|
289
|
+
async request(method, path, body) {
|
|
290
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}`, {
|
|
291
|
+
method,
|
|
292
|
+
headers: {
|
|
293
|
+
accept: "application/json",
|
|
294
|
+
...body !== void 0 ? {
|
|
295
|
+
"content-type": "application/json"
|
|
296
|
+
} : {},
|
|
297
|
+
...await this.resolveHeaders()
|
|
298
|
+
},
|
|
299
|
+
...body !== void 0 ? {
|
|
300
|
+
body: JSON.stringify(body)
|
|
301
|
+
} : {},
|
|
302
|
+
...this.credentials()
|
|
303
|
+
});
|
|
304
|
+
return this.handleResponse(response, method, path);
|
|
305
|
+
}
|
|
306
|
+
async handleResponse(response, method, path) {
|
|
307
|
+
if (!response.ok) {
|
|
308
|
+
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
309
|
+
}
|
|
310
|
+
if (response.status === 204) return void 0;
|
|
311
|
+
const text = await response.text();
|
|
312
|
+
if (!text) return void 0;
|
|
313
|
+
return JSON.parse(text);
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
function streamResponse(response) {
|
|
317
|
+
const body = response.body;
|
|
318
|
+
const runId = response.headers?.get(HEADER_RUN_ID) ?? void 0;
|
|
319
|
+
const threadId = response.headers?.get(HEADER_THREAD_ID) ?? void 0;
|
|
320
|
+
return {
|
|
321
|
+
body,
|
|
322
|
+
...runId ? {
|
|
323
|
+
runId
|
|
324
|
+
} : {},
|
|
325
|
+
...threadId ? {
|
|
326
|
+
threadId
|
|
327
|
+
} : {}
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
__name(streamResponse, "streamResponse");
|
|
331
|
+
|
|
86
332
|
// src/reasoning/timing.ts
|
|
87
333
|
var import_react = require("react");
|
|
88
334
|
var REASONING_METADATA_KEY = "agent";
|
|
@@ -143,8 +389,11 @@ function formatElapsed(ms) {
|
|
|
143
389
|
__name(formatElapsed, "formatElapsed");
|
|
144
390
|
|
|
145
391
|
// src/agent-chat-transport.ts
|
|
146
|
-
var
|
|
147
|
-
|
|
392
|
+
var DEFAULT_RECONNECT = {
|
|
393
|
+
maxAttempts: 6,
|
|
394
|
+
baseDelayMs: 500,
|
|
395
|
+
maxDelayMs: 8e3
|
|
396
|
+
};
|
|
148
397
|
var ASK_TOOL_NAME = "ask";
|
|
149
398
|
var AgentChatTransport = class {
|
|
150
399
|
static {
|
|
@@ -161,8 +410,26 @@ var AgentChatTransport = class {
|
|
|
161
410
|
* several entries under each id — which React renders as duplicate keys.
|
|
162
411
|
*/
|
|
163
412
|
attemptLive = false;
|
|
413
|
+
backend;
|
|
164
414
|
constructor(options = {}) {
|
|
165
415
|
this.options = options;
|
|
416
|
+
this.backend = options.backend ?? new AgentClient({
|
|
417
|
+
...options.baseUrl !== void 0 ? {
|
|
418
|
+
baseUrl: options.baseUrl
|
|
419
|
+
} : {},
|
|
420
|
+
...options.headers !== void 0 ? {
|
|
421
|
+
headers: options.headers
|
|
422
|
+
} : {},
|
|
423
|
+
...options.getHeaders !== void 0 ? {
|
|
424
|
+
getHeaders: options.getHeaders
|
|
425
|
+
} : {},
|
|
426
|
+
...options.credentials !== void 0 ? {
|
|
427
|
+
credentials: options.credentials
|
|
428
|
+
} : {},
|
|
429
|
+
...options.fetch !== void 0 ? {
|
|
430
|
+
fetch: options.fetch
|
|
431
|
+
} : {}
|
|
432
|
+
});
|
|
166
433
|
}
|
|
167
434
|
/** Run id of the most recent stream — HITL approve/reject target this. */
|
|
168
435
|
get runId() {
|
|
@@ -195,26 +462,25 @@ var AgentChatTransport = class {
|
|
|
195
462
|
...options.body ?? {},
|
|
196
463
|
message
|
|
197
464
|
};
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
465
|
+
const headers = headerRecord(options.headers);
|
|
466
|
+
const response = await this.backend.openChatStream({
|
|
467
|
+
body,
|
|
468
|
+
...headers !== void 0 ? {
|
|
469
|
+
headers
|
|
470
|
+
} : {},
|
|
471
|
+
...options.abortSignal ? {
|
|
472
|
+
signal: options.abortSignal
|
|
473
|
+
} : {}
|
|
474
|
+
});
|
|
475
|
+
this.captureHeaderMeta(response);
|
|
476
|
+
return this.toChunkStream(response, {
|
|
477
|
+
...headers !== void 0 ? {
|
|
478
|
+
headers
|
|
208
479
|
} : {},
|
|
209
480
|
...options.abortSignal ? {
|
|
210
481
|
signal: options.abortSignal
|
|
211
482
|
} : {}
|
|
212
483
|
});
|
|
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
484
|
} catch (error) {
|
|
219
485
|
this.attemptLive = false;
|
|
220
486
|
throw error;
|
|
@@ -227,51 +493,41 @@ var AgentChatTransport = class {
|
|
|
227
493
|
if (runId === void 0) return null;
|
|
228
494
|
this.attemptLive = true;
|
|
229
495
|
try {
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
},
|
|
236
|
-
...this.options.credentials !== void 0 ? {
|
|
237
|
-
credentials: this.options.credentials
|
|
496
|
+
const headers = headerRecord(options.headers);
|
|
497
|
+
const response = await this.backend.resumeChatStream({
|
|
498
|
+
runId,
|
|
499
|
+
...headers !== void 0 ? {
|
|
500
|
+
headers
|
|
238
501
|
} : {}
|
|
239
502
|
});
|
|
240
|
-
if (response
|
|
503
|
+
if (response === null) {
|
|
241
504
|
this.attemptLive = false;
|
|
242
505
|
return null;
|
|
243
506
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
507
|
+
this.captureHeaderMeta(response);
|
|
508
|
+
return this.toChunkStream(response, {
|
|
509
|
+
runId,
|
|
510
|
+
...headers !== void 0 ? {
|
|
511
|
+
headers
|
|
512
|
+
} : {}
|
|
513
|
+
});
|
|
249
514
|
} catch (error) {
|
|
250
515
|
this.attemptLive = false;
|
|
251
516
|
throw error;
|
|
252
517
|
}
|
|
253
518
|
}
|
|
254
|
-
|
|
255
|
-
|
|
519
|
+
captureHeaderMeta(response) {
|
|
520
|
+
if (response.runId) this.currentRunId = response.runId;
|
|
521
|
+
if (response.threadId) this.currentThreadId = response.threadId;
|
|
256
522
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
async resolveHeaders(perRequest) {
|
|
261
|
-
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
262
|
-
const extra = perRequest instanceof Headers ? Object.fromEntries(perRequest.entries()) : perRequest ?? {};
|
|
523
|
+
reconnectPolicy() {
|
|
524
|
+
const configured = this.options.reconnect;
|
|
525
|
+
if (configured === false) return null;
|
|
263
526
|
return {
|
|
264
|
-
...
|
|
265
|
-
...
|
|
266
|
-
...extra
|
|
527
|
+
...DEFAULT_RECONNECT,
|
|
528
|
+
...configured
|
|
267
529
|
};
|
|
268
530
|
}
|
|
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
531
|
recordMeta(meta) {
|
|
276
532
|
this.currentRunId = meta.runId;
|
|
277
533
|
this.currentThreadId = meta.threadId;
|
|
@@ -290,10 +546,66 @@ var AgentChatTransport = class {
|
|
|
290
546
|
* call plus its tool execution; text and reasoning open lazily and close at the step boundary, so
|
|
291
547
|
* tool-call cards (input streaming → output) render live between the prose.
|
|
292
548
|
*/
|
|
293
|
-
toChunkStream(source) {
|
|
294
|
-
|
|
295
|
-
|
|
549
|
+
toChunkStream(source, context) {
|
|
550
|
+
let reader = source.body.getReader();
|
|
551
|
+
let decoder = new TextDecoder();
|
|
296
552
|
let buffer = "";
|
|
553
|
+
let streamRunId = source.runId ?? context.runId;
|
|
554
|
+
let lastSeq;
|
|
555
|
+
let cancelled = false;
|
|
556
|
+
const backend = this.backend;
|
|
557
|
+
const policy = this.reconnectPolicy();
|
|
558
|
+
const notify = /* @__PURE__ */ __name((state) => this.options.onConnectionChange?.(state), "notify");
|
|
559
|
+
const aborted = /* @__PURE__ */ __name(() => cancelled || context.signal?.aborted === true, "aborted");
|
|
560
|
+
const canResume = /* @__PURE__ */ __name(() => policy !== null && lastSeq !== void 0 && streamRunId !== void 0 && !aborted(), "canResume");
|
|
561
|
+
const resume = /* @__PURE__ */ __name(async () => {
|
|
562
|
+
const runId = streamRunId;
|
|
563
|
+
const after = lastSeq ?? 0;
|
|
564
|
+
const { maxAttempts, baseDelayMs, maxDelayMs } = policy;
|
|
565
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
566
|
+
notify({
|
|
567
|
+
status: "reconnecting",
|
|
568
|
+
runId,
|
|
569
|
+
attempt,
|
|
570
|
+
after
|
|
571
|
+
});
|
|
572
|
+
await delay(Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1)), context.signal);
|
|
573
|
+
if (aborted()) return "aborted";
|
|
574
|
+
try {
|
|
575
|
+
const next = await backend.resumeChatStream({
|
|
576
|
+
runId,
|
|
577
|
+
after,
|
|
578
|
+
...context.headers !== void 0 ? {
|
|
579
|
+
headers: context.headers
|
|
580
|
+
} : {},
|
|
581
|
+
...context.signal !== void 0 ? {
|
|
582
|
+
signal: context.signal
|
|
583
|
+
} : {}
|
|
584
|
+
});
|
|
585
|
+
if (next === null) {
|
|
586
|
+
notify({
|
|
587
|
+
status: "gone",
|
|
588
|
+
runId
|
|
589
|
+
});
|
|
590
|
+
return "gone";
|
|
591
|
+
}
|
|
592
|
+
reader = next.body.getReader();
|
|
593
|
+
decoder = new TextDecoder();
|
|
594
|
+
buffer = "";
|
|
595
|
+
notify({
|
|
596
|
+
status: "live"
|
|
597
|
+
});
|
|
598
|
+
return "resumed";
|
|
599
|
+
} catch {
|
|
600
|
+
if (aborted()) return "aborted";
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
notify({
|
|
604
|
+
status: "failed",
|
|
605
|
+
runId
|
|
606
|
+
});
|
|
607
|
+
return "failed";
|
|
608
|
+
}, "resume");
|
|
297
609
|
let started = false;
|
|
298
610
|
let stepOpen = false;
|
|
299
611
|
let stepIndex = 0;
|
|
@@ -304,7 +616,10 @@ var AgentChatTransport = class {
|
|
|
304
616
|
let reasoningSeq = 0;
|
|
305
617
|
const announced = /* @__PURE__ */ new Set();
|
|
306
618
|
const toolMetadata = /* @__PURE__ */ new Map();
|
|
307
|
-
const record = /* @__PURE__ */ __name((meta) =>
|
|
619
|
+
const record = /* @__PURE__ */ __name((meta) => {
|
|
620
|
+
streamRunId = meta.runId;
|
|
621
|
+
this.recordMeta(meta);
|
|
622
|
+
}, "record");
|
|
308
623
|
const endAttempt = /* @__PURE__ */ __name(() => {
|
|
309
624
|
this.attemptLive = false;
|
|
310
625
|
}, "endAttempt");
|
|
@@ -313,7 +628,12 @@ var AgentChatTransport = class {
|
|
|
313
628
|
function ensureStarted() {
|
|
314
629
|
if (started) return;
|
|
315
630
|
started = true;
|
|
316
|
-
controller.enqueue({
|
|
631
|
+
controller.enqueue(streamRunId !== void 0 ? {
|
|
632
|
+
type: "start",
|
|
633
|
+
messageMetadata: {
|
|
634
|
+
runId: streamRunId
|
|
635
|
+
}
|
|
636
|
+
} : {
|
|
317
637
|
type: "start"
|
|
318
638
|
});
|
|
319
639
|
}
|
|
@@ -569,33 +889,67 @@ var AgentChatTransport = class {
|
|
|
569
889
|
endAttempt();
|
|
570
890
|
}
|
|
571
891
|
__name(finish, "finish");
|
|
892
|
+
function fail(errorText, start = true) {
|
|
893
|
+
if (start) ensureStarted();
|
|
894
|
+
controller.enqueue({
|
|
895
|
+
type: "error",
|
|
896
|
+
errorText
|
|
897
|
+
});
|
|
898
|
+
controller.close();
|
|
899
|
+
endAttempt();
|
|
900
|
+
}
|
|
901
|
+
__name(fail, "fail");
|
|
902
|
+
async function recover(error) {
|
|
903
|
+
if (!canResume()) {
|
|
904
|
+
if (error === void 0) {
|
|
905
|
+
finish();
|
|
906
|
+
} else {
|
|
907
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
908
|
+
}
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
const outcome = await resume();
|
|
912
|
+
if (outcome === "resumed") return true;
|
|
913
|
+
if (outcome === "failed") {
|
|
914
|
+
fail("Lost the connection to the agent stream");
|
|
915
|
+
} else {
|
|
916
|
+
finish();
|
|
917
|
+
}
|
|
918
|
+
return false;
|
|
919
|
+
}
|
|
920
|
+
__name(recover, "recover");
|
|
572
921
|
try {
|
|
573
922
|
while (true) {
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
923
|
+
let result;
|
|
924
|
+
try {
|
|
925
|
+
result = await reader.read();
|
|
926
|
+
} catch (error) {
|
|
927
|
+
if (await recover(error)) continue;
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
if (result.done) {
|
|
931
|
+
if (await recover(void 0)) continue;
|
|
577
932
|
return;
|
|
578
933
|
}
|
|
579
|
-
buffer += decoder.decode(value, {
|
|
934
|
+
buffer += decoder.decode(result.value, {
|
|
580
935
|
stream: true
|
|
581
936
|
});
|
|
582
937
|
let separator = buffer.indexOf("\n\n");
|
|
583
938
|
while (separator !== -1) {
|
|
584
939
|
const rawEvent = buffer.slice(0, separator);
|
|
585
940
|
buffer = buffer.slice(separator + 2);
|
|
941
|
+
separator = buffer.indexOf("\n\n");
|
|
586
942
|
const frame = parseSseFrame(rawEvent);
|
|
943
|
+
if (frame.id !== void 0) {
|
|
944
|
+
if (lastSeq !== void 0 && frame.id <= lastSeq) continue;
|
|
945
|
+
lastSeq = frame.id;
|
|
946
|
+
}
|
|
587
947
|
if (frame.event === "done") {
|
|
588
948
|
finish();
|
|
589
949
|
return;
|
|
590
950
|
}
|
|
591
951
|
if (frame.event === "error") {
|
|
592
|
-
|
|
593
|
-
controller.enqueue({
|
|
594
|
-
type: "error",
|
|
595
|
-
errorText: parseErrorText(frame.data)
|
|
596
|
-
});
|
|
597
|
-
controller.close();
|
|
598
|
-
endAttempt();
|
|
952
|
+
fail(parseErrorText(frame.data));
|
|
599
953
|
return;
|
|
600
954
|
}
|
|
601
955
|
if (frame.event === "meta") {
|
|
@@ -605,19 +959,14 @@ var AgentChatTransport = class {
|
|
|
605
959
|
const event = parseEvent(frame.data);
|
|
606
960
|
if (event) emit(event);
|
|
607
961
|
}
|
|
608
|
-
separator = buffer.indexOf("\n\n");
|
|
609
962
|
}
|
|
610
963
|
}
|
|
611
964
|
} catch (error) {
|
|
612
|
-
|
|
613
|
-
type: "error",
|
|
614
|
-
errorText: error instanceof Error ? error.message : "Agent stream error"
|
|
615
|
-
});
|
|
616
|
-
controller.close();
|
|
617
|
-
endAttempt();
|
|
965
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
618
966
|
}
|
|
619
967
|
},
|
|
620
968
|
cancel() {
|
|
969
|
+
cancelled = true;
|
|
621
970
|
void reader.cancel();
|
|
622
971
|
endAttempt();
|
|
623
972
|
}
|
|
@@ -626,17 +975,22 @@ var AgentChatTransport = class {
|
|
|
626
975
|
};
|
|
627
976
|
function parseSseFrame(raw) {
|
|
628
977
|
let event;
|
|
978
|
+
let id;
|
|
629
979
|
const dataLines = [];
|
|
630
980
|
for (const line of raw.split("\n")) {
|
|
631
981
|
if (line.startsWith("event:")) {
|
|
632
982
|
event = line.slice("event:".length).trim();
|
|
983
|
+
} else if (line.startsWith("id:")) {
|
|
984
|
+
const parsed = Number(line.slice("id:".length).trim());
|
|
985
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) id = parsed;
|
|
633
986
|
} else if (line.startsWith("data:")) {
|
|
634
987
|
dataLines.push(line.slice("data:".length).trimStart());
|
|
635
988
|
}
|
|
636
989
|
}
|
|
637
990
|
return {
|
|
638
991
|
event,
|
|
639
|
-
data: dataLines.length > 0 ? dataLines.join("\n") : void 0
|
|
992
|
+
data: dataLines.length > 0 ? dataLines.join("\n") : void 0,
|
|
993
|
+
id
|
|
640
994
|
};
|
|
641
995
|
}
|
|
642
996
|
__name(parseSseFrame, "parseSseFrame");
|
|
@@ -677,15 +1031,282 @@ function parseEvent(data) {
|
|
|
677
1031
|
}
|
|
678
1032
|
return null;
|
|
679
1033
|
}
|
|
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;
|
|
1034
|
+
__name(parseEvent, "parseEvent");
|
|
1035
|
+
function extractText(message) {
|
|
1036
|
+
let text = "";
|
|
1037
|
+
for (const part of message.parts ?? []) {
|
|
1038
|
+
if (part.type === "text") text += part.text;
|
|
1039
|
+
}
|
|
1040
|
+
return text;
|
|
1041
|
+
}
|
|
1042
|
+
__name(extractText, "extractText");
|
|
1043
|
+
function headerRecord(headers) {
|
|
1044
|
+
if (headers === void 0) return void 0;
|
|
1045
|
+
return headers instanceof Headers ? Object.fromEntries(headers.entries()) : headers;
|
|
1046
|
+
}
|
|
1047
|
+
__name(headerRecord, "headerRecord");
|
|
1048
|
+
function delay(ms, signal) {
|
|
1049
|
+
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
1050
|
+
return new Promise((resolve) => {
|
|
1051
|
+
const timer = setTimeout(done, ms);
|
|
1052
|
+
function done() {
|
|
1053
|
+
clearTimeout(timer);
|
|
1054
|
+
signal?.removeEventListener("abort", done);
|
|
1055
|
+
resolve();
|
|
1056
|
+
}
|
|
1057
|
+
__name(done, "done");
|
|
1058
|
+
signal?.addEventListener("abort", done, {
|
|
1059
|
+
once: true
|
|
1060
|
+
});
|
|
1061
|
+
});
|
|
1062
|
+
}
|
|
1063
|
+
__name(delay, "delay");
|
|
1064
|
+
|
|
1065
|
+
// src/backend.ts
|
|
1066
|
+
var AgentBackendUnsupportedError = class extends Error {
|
|
1067
|
+
static {
|
|
1068
|
+
__name(this, "AgentBackendUnsupportedError");
|
|
1069
|
+
}
|
|
1070
|
+
method;
|
|
1071
|
+
constructor(method) {
|
|
1072
|
+
super(`The agent backend does not implement ${method}()`), this.method = method;
|
|
1073
|
+
this.name = "AgentBackendUnsupportedError";
|
|
1074
|
+
}
|
|
1075
|
+
};
|
|
1076
|
+
function requireBackendMethod(backend, name) {
|
|
1077
|
+
const method = backend[name];
|
|
1078
|
+
if (typeof method !== "function") {
|
|
1079
|
+
throw new AgentBackendUnsupportedError(name);
|
|
1080
|
+
}
|
|
1081
|
+
return method.bind(backend);
|
|
1082
|
+
}
|
|
1083
|
+
__name(requireBackendMethod, "requireBackendMethod");
|
|
1084
|
+
|
|
1085
|
+
// src/feedback/use-message-feedback.ts
|
|
1086
|
+
var import_react2 = require("react");
|
|
1087
|
+
function useMessageFeedback(options) {
|
|
1088
|
+
const latest = (0, import_react2.useRef)(options);
|
|
1089
|
+
latest.current = options;
|
|
1090
|
+
const [local, setLocal] = (0, import_react2.useState)(/* @__PURE__ */ new Map());
|
|
1091
|
+
const [pending, setPending] = (0, import_react2.useState)(/* @__PURE__ */ new Set());
|
|
1092
|
+
const [error, setError] = (0, import_react2.useState)(null);
|
|
1093
|
+
const localRef = (0, import_react2.useRef)(local);
|
|
1094
|
+
localRef.current = local;
|
|
1095
|
+
const resolved = (0, import_react2.useRef)(/* @__PURE__ */ new Map());
|
|
1096
|
+
const feedbackOf = (0, import_react2.useCallback)((message) => {
|
|
1097
|
+
const mine = local.get(message.id);
|
|
1098
|
+
if (mine !== void 0) return mine.feedback;
|
|
1099
|
+
return metadataOf(message).feedback ?? null;
|
|
1100
|
+
}, [
|
|
1101
|
+
local
|
|
1102
|
+
]);
|
|
1103
|
+
const isPending = (0, import_react2.useCallback)((message) => pending.has(message.id), [
|
|
1104
|
+
pending
|
|
1105
|
+
]);
|
|
1106
|
+
const storedIdFor = (0, import_react2.useCallback)(async (message) => {
|
|
1107
|
+
const runId = metadataOf(message).runId;
|
|
1108
|
+
if (runId === void 0) return message.id;
|
|
1109
|
+
const known = resolved.current.get(message.id);
|
|
1110
|
+
if (known !== void 0) return known;
|
|
1111
|
+
const { backend, threadId } = latest.current;
|
|
1112
|
+
const thread = typeof threadId === "function" ? threadId() : threadId;
|
|
1113
|
+
if (thread === void 0) {
|
|
1114
|
+
throw new Error("useMessageFeedback: a streamed message needs `threadId` to be rated");
|
|
1115
|
+
}
|
|
1116
|
+
const detail = await backend.getThread(thread);
|
|
1117
|
+
const row = [
|
|
1118
|
+
...detail.messages
|
|
1119
|
+
].reverse().find((candidate) => candidate.role === "assistant" && candidate.runId === runId);
|
|
1120
|
+
if (row === void 0) {
|
|
1121
|
+
throw new Error(`useMessageFeedback: run ${runId} has no persisted answer yet`);
|
|
1122
|
+
}
|
|
1123
|
+
resolved.current.set(message.id, row.id);
|
|
1124
|
+
return row.id;
|
|
1125
|
+
}, []);
|
|
1126
|
+
const rate = (0, import_react2.useCallback)(async (message, value, comment) => {
|
|
1127
|
+
const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
|
|
1128
|
+
const had = localRef.current.get(message.id);
|
|
1129
|
+
const optimistic = value === null ? null : {
|
|
1130
|
+
value,
|
|
1131
|
+
...comment !== void 0 && comment.trim().length > 0 ? {
|
|
1132
|
+
comment: comment.trim()
|
|
1133
|
+
} : {},
|
|
1134
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1135
|
+
};
|
|
1136
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1137
|
+
feedback: optimistic
|
|
1138
|
+
}));
|
|
1139
|
+
setPending((current) => new Set(current).add(message.id));
|
|
1140
|
+
try {
|
|
1141
|
+
const storedId = await storedIdFor(message);
|
|
1142
|
+
const { feedback } = await setFeedback(storedId, {
|
|
1143
|
+
value,
|
|
1144
|
+
...comment !== void 0 ? {
|
|
1145
|
+
comment
|
|
1146
|
+
} : {}
|
|
1147
|
+
});
|
|
1148
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1149
|
+
feedback
|
|
1150
|
+
}));
|
|
1151
|
+
setError(null);
|
|
1152
|
+
} catch (caught) {
|
|
1153
|
+
setLocal((current) => {
|
|
1154
|
+
const next = new Map(current);
|
|
1155
|
+
if (had === void 0) next.delete(message.id);
|
|
1156
|
+
else next.set(message.id, had);
|
|
1157
|
+
return next;
|
|
1158
|
+
});
|
|
1159
|
+
const failure = caught instanceof Error ? caught : new Error(String(caught));
|
|
1160
|
+
setError(failure);
|
|
1161
|
+
throw failure;
|
|
1162
|
+
} finally {
|
|
1163
|
+
setPending((current) => {
|
|
1164
|
+
const next = new Set(current);
|
|
1165
|
+
next.delete(message.id);
|
|
1166
|
+
return next;
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
}, [
|
|
1170
|
+
storedIdFor
|
|
1171
|
+
]);
|
|
1172
|
+
const toggle = (0, import_react2.useCallback)((message, value) => {
|
|
1173
|
+
const mine = localRef.current.get(message.id);
|
|
1174
|
+
const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
|
|
1175
|
+
return rate(message, current?.value === value ? null : value);
|
|
1176
|
+
}, [
|
|
1177
|
+
rate
|
|
1178
|
+
]);
|
|
1179
|
+
return {
|
|
1180
|
+
feedbackOf,
|
|
1181
|
+
isPending,
|
|
1182
|
+
rate,
|
|
1183
|
+
toggle,
|
|
1184
|
+
error
|
|
1185
|
+
};
|
|
1186
|
+
}
|
|
1187
|
+
__name(useMessageFeedback, "useMessageFeedback");
|
|
1188
|
+
function metadataOf(message) {
|
|
1189
|
+
const metadata = message.metadata;
|
|
1190
|
+
return metadata !== null && typeof metadata === "object" ? metadata : {};
|
|
1191
|
+
}
|
|
1192
|
+
__name(metadataOf, "metadataOf");
|
|
1193
|
+
|
|
1194
|
+
// src/threads/threads-events.ts
|
|
1195
|
+
var listeners = /* @__PURE__ */ new WeakMap();
|
|
1196
|
+
function notifyThreads(backend, event) {
|
|
1197
|
+
for (const listener of listeners.get(backend) ?? []) {
|
|
1198
|
+
listener(event);
|
|
685
1199
|
}
|
|
686
|
-
return text;
|
|
687
1200
|
}
|
|
688
|
-
__name(
|
|
1201
|
+
__name(notifyThreads, "notifyThreads");
|
|
1202
|
+
function onThreadsEvent(backend, listener) {
|
|
1203
|
+
let set = listeners.get(backend);
|
|
1204
|
+
if (set === void 0) {
|
|
1205
|
+
set = /* @__PURE__ */ new Set();
|
|
1206
|
+
listeners.set(backend, set);
|
|
1207
|
+
}
|
|
1208
|
+
set.add(listener);
|
|
1209
|
+
return () => {
|
|
1210
|
+
set.delete(listener);
|
|
1211
|
+
};
|
|
1212
|
+
}
|
|
1213
|
+
__name(onThreadsEvent, "onThreadsEvent");
|
|
1214
|
+
|
|
1215
|
+
// src/threads/use-threads.ts
|
|
1216
|
+
var import_react3 = require("react");
|
|
1217
|
+
function useThreads(options) {
|
|
1218
|
+
const { backend, enabled = true } = options;
|
|
1219
|
+
const [threads, setThreads] = (0, import_react3.useState)([]);
|
|
1220
|
+
const [isLoading, setIsLoading] = (0, import_react3.useState)(enabled);
|
|
1221
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
1222
|
+
const threadsRef = (0, import_react3.useRef)(threads);
|
|
1223
|
+
threadsRef.current = threads;
|
|
1224
|
+
const generation = (0, import_react3.useRef)(0);
|
|
1225
|
+
const refresh = (0, import_react3.useCallback)(async () => {
|
|
1226
|
+
const mine = ++generation.current;
|
|
1227
|
+
setIsLoading(true);
|
|
1228
|
+
try {
|
|
1229
|
+
const list = await backend.listThreads();
|
|
1230
|
+
if (mine !== generation.current) return;
|
|
1231
|
+
setThreads(list);
|
|
1232
|
+
setError(null);
|
|
1233
|
+
} catch (caught) {
|
|
1234
|
+
if (mine !== generation.current) return;
|
|
1235
|
+
setError(caught instanceof Error ? caught : new Error(String(caught)));
|
|
1236
|
+
} finally {
|
|
1237
|
+
if (mine === generation.current) setIsLoading(false);
|
|
1238
|
+
}
|
|
1239
|
+
}, [
|
|
1240
|
+
backend
|
|
1241
|
+
]);
|
|
1242
|
+
(0, import_react3.useEffect)(() => {
|
|
1243
|
+
if (!enabled) return;
|
|
1244
|
+
void refresh();
|
|
1245
|
+
return onThreadsEvent(backend, (event) => {
|
|
1246
|
+
if (event.type === "title") {
|
|
1247
|
+
setThreads((current) => current.map((thread) => thread.id === event.threadId ? {
|
|
1248
|
+
...thread,
|
|
1249
|
+
title: event.title
|
|
1250
|
+
} : thread));
|
|
1251
|
+
} else if (event.type === "removed") {
|
|
1252
|
+
setThreads((current) => current.filter((thread) => thread.id !== event.threadId));
|
|
1253
|
+
} else {
|
|
1254
|
+
void refresh();
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
}, [
|
|
1258
|
+
backend,
|
|
1259
|
+
enabled,
|
|
1260
|
+
refresh
|
|
1261
|
+
]);
|
|
1262
|
+
const rename = (0, import_react3.useCallback)(async (id, title) => {
|
|
1263
|
+
const before = threadsRef.current;
|
|
1264
|
+
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
1265
|
+
...thread,
|
|
1266
|
+
title
|
|
1267
|
+
} : thread));
|
|
1268
|
+
try {
|
|
1269
|
+
await backend.updateThread(id, {
|
|
1270
|
+
title
|
|
1271
|
+
});
|
|
1272
|
+
notifyThreads(backend, {
|
|
1273
|
+
type: "title",
|
|
1274
|
+
threadId: id,
|
|
1275
|
+
title
|
|
1276
|
+
});
|
|
1277
|
+
} catch (caught) {
|
|
1278
|
+
setThreads(before);
|
|
1279
|
+
throw caught;
|
|
1280
|
+
}
|
|
1281
|
+
}, [
|
|
1282
|
+
backend
|
|
1283
|
+
]);
|
|
1284
|
+
const remove = (0, import_react3.useCallback)(async (id) => {
|
|
1285
|
+
const before = threadsRef.current;
|
|
1286
|
+
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
1287
|
+
try {
|
|
1288
|
+
await backend.deleteThread(id);
|
|
1289
|
+
notifyThreads(backend, {
|
|
1290
|
+
type: "removed",
|
|
1291
|
+
threadId: id
|
|
1292
|
+
});
|
|
1293
|
+
} catch (caught) {
|
|
1294
|
+
setThreads(before);
|
|
1295
|
+
throw caught;
|
|
1296
|
+
}
|
|
1297
|
+
}, [
|
|
1298
|
+
backend
|
|
1299
|
+
]);
|
|
1300
|
+
return {
|
|
1301
|
+
threads,
|
|
1302
|
+
isLoading,
|
|
1303
|
+
error,
|
|
1304
|
+
refresh,
|
|
1305
|
+
rename,
|
|
1306
|
+
remove
|
|
1307
|
+
};
|
|
1308
|
+
}
|
|
1309
|
+
__name(useThreads, "useThreads");
|
|
689
1310
|
|
|
690
1311
|
// src/background-runs.ts
|
|
691
1312
|
function asReceipt(output) {
|
|
@@ -734,170 +1355,11 @@ function backgroundRunsFromThread(messages) {
|
|
|
734
1355
|
}
|
|
735
1356
|
__name(backgroundRunsFromThread, "backgroundRunsFromThread");
|
|
736
1357
|
|
|
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
1358
|
// src/components/chat-input.tsx
|
|
897
|
-
var
|
|
1359
|
+
var import_react5 = __toESM(require("react"), 1);
|
|
898
1360
|
|
|
899
1361
|
// src/components/use-speech-recognition.ts
|
|
900
|
-
var
|
|
1362
|
+
var import_react4 = require("react");
|
|
901
1363
|
function getRecognitionCtor() {
|
|
902
1364
|
if (typeof window === "undefined") return null;
|
|
903
1365
|
const scope = window;
|
|
@@ -905,15 +1367,15 @@ function getRecognitionCtor() {
|
|
|
905
1367
|
}
|
|
906
1368
|
__name(getRecognitionCtor, "getRecognitionCtor");
|
|
907
1369
|
function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
|
|
908
|
-
const recognitionRef = (0,
|
|
909
|
-
const [isListening, setIsListening] = (0,
|
|
910
|
-
const ctor = (0,
|
|
1370
|
+
const recognitionRef = (0, import_react4.useRef)(null);
|
|
1371
|
+
const [isListening, setIsListening] = (0, import_react4.useState)(false);
|
|
1372
|
+
const ctor = (0, import_react4.useMemo)(() => getRecognitionCtor(), []);
|
|
911
1373
|
const isSupported = ctor !== null;
|
|
912
|
-
const finalRef = (0,
|
|
913
|
-
const interimRef = (0,
|
|
1374
|
+
const finalRef = (0, import_react4.useRef)(onFinalTranscript);
|
|
1375
|
+
const interimRef = (0, import_react4.useRef)(onInterimTranscript);
|
|
914
1376
|
finalRef.current = onFinalTranscript;
|
|
915
1377
|
interimRef.current = onInterimTranscript;
|
|
916
|
-
(0,
|
|
1378
|
+
(0, import_react4.useEffect)(() => {
|
|
917
1379
|
if (!ctor) return;
|
|
918
1380
|
const recognition = new ctor();
|
|
919
1381
|
recognition.lang = lang;
|
|
@@ -952,7 +1414,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
952
1414
|
ctor,
|
|
953
1415
|
lang
|
|
954
1416
|
]);
|
|
955
|
-
const start = (0,
|
|
1417
|
+
const start = (0, import_react4.useCallback)(() => {
|
|
956
1418
|
const recognition = recognitionRef.current;
|
|
957
1419
|
if (!recognition) return;
|
|
958
1420
|
try {
|
|
@@ -962,7 +1424,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
962
1424
|
console.warn("SpeechRecognition start failed", error);
|
|
963
1425
|
}
|
|
964
1426
|
}, []);
|
|
965
|
-
const stop = (0,
|
|
1427
|
+
const stop = (0, import_react4.useCallback)(() => {
|
|
966
1428
|
const recognition = recognitionRef.current;
|
|
967
1429
|
if (!recognition) return;
|
|
968
1430
|
try {
|
|
@@ -981,8 +1443,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
|
|
|
981
1443
|
|
|
982
1444
|
// src/components/chat-input.tsx
|
|
983
1445
|
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,
|
|
1446
|
+
const [value, setValue] = (0, import_react5.useState)("");
|
|
1447
|
+
const [interim, setInterim] = (0, import_react5.useState)("");
|
|
986
1448
|
const speech = useSpeechRecognition({
|
|
987
1449
|
lang: speechLang,
|
|
988
1450
|
onFinalTranscript: /* @__PURE__ */ __name((text) => {
|
|
@@ -993,7 +1455,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
993
1455
|
}, "onFinalTranscript"),
|
|
994
1456
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
995
1457
|
});
|
|
996
|
-
(0,
|
|
1458
|
+
(0, import_react5.useEffect)(() => {
|
|
997
1459
|
if (!speech.isListening) setInterim("");
|
|
998
1460
|
}, [
|
|
999
1461
|
speech.isListening
|
|
@@ -1021,9 +1483,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1021
1483
|
const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
|
|
1022
1484
|
const showMic = enableSpeech && speech.isSupported;
|
|
1023
1485
|
const showStop = isStreaming && onStop !== void 0;
|
|
1024
|
-
return /* @__PURE__ */
|
|
1486
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1025
1487
|
className
|
|
1026
|
-
}, /* @__PURE__ */
|
|
1488
|
+
}, /* @__PURE__ */ import_react5.default.createElement("textarea", {
|
|
1027
1489
|
className: textareaClassName,
|
|
1028
1490
|
rows,
|
|
1029
1491
|
value: displayValue,
|
|
@@ -1034,17 +1496,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1034
1496
|
}, "onChange"),
|
|
1035
1497
|
onKeyDown: handleKeyDown,
|
|
1036
1498
|
placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
|
|
1037
|
-
}), /* @__PURE__ */
|
|
1499
|
+
}), /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1038
1500
|
type: "button",
|
|
1039
1501
|
className: sendButtonClassName,
|
|
1040
1502
|
onClick: submit,
|
|
1041
1503
|
disabled: disabled || !value.trim()
|
|
1042
|
-
}, sendLabel), showStop ? /* @__PURE__ */
|
|
1504
|
+
}, sendLabel), showStop ? /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1043
1505
|
type: "button",
|
|
1044
1506
|
className: stopButtonClassName,
|
|
1045
1507
|
onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
|
|
1046
1508
|
"aria-label": "Stop generating"
|
|
1047
|
-
}, stopLabel) : null, showMic ? /* @__PURE__ */
|
|
1509
|
+
}, stopLabel) : null, showMic ? /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1048
1510
|
type: "button",
|
|
1049
1511
|
className: micButtonClassName,
|
|
1050
1512
|
onClick: toggleMic,
|
|
@@ -1057,10 +1519,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1057
1519
|
__name(ChatInput, "ChatInput");
|
|
1058
1520
|
|
|
1059
1521
|
// src/components/message-item.tsx
|
|
1060
|
-
var
|
|
1522
|
+
var import_react8 = __toESM(require("react"), 1);
|
|
1061
1523
|
|
|
1062
1524
|
// src/transcript/use-chat-transcript.ts
|
|
1063
|
-
var
|
|
1525
|
+
var import_react7 = require("react");
|
|
1064
1526
|
|
|
1065
1527
|
// src/transcript/model.ts
|
|
1066
1528
|
var import_nestjs_agent_core2 = require("@dudousxd/nestjs-agent-core");
|
|
@@ -2037,11 +2499,11 @@ function formatCostUsd(cost) {
|
|
|
2037
2499
|
__name(formatCostUsd, "formatCostUsd");
|
|
2038
2500
|
|
|
2039
2501
|
// src/transcript/use-stick-to-bottom.ts
|
|
2040
|
-
var
|
|
2502
|
+
var import_react6 = require("react");
|
|
2041
2503
|
function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
2042
|
-
const containerRef = (0,
|
|
2043
|
-
const [isAtBottom, setIsAtBottom] = (0,
|
|
2044
|
-
const measure = (0,
|
|
2504
|
+
const containerRef = (0, import_react6.useRef)(null);
|
|
2505
|
+
const [isAtBottom, setIsAtBottom] = (0, import_react6.useState)(true);
|
|
2506
|
+
const measure = (0, import_react6.useCallback)(() => {
|
|
2045
2507
|
const element = containerRef.current;
|
|
2046
2508
|
if (!element) {
|
|
2047
2509
|
return;
|
|
@@ -2051,7 +2513,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2051
2513
|
}, [
|
|
2052
2514
|
threshold
|
|
2053
2515
|
]);
|
|
2054
|
-
const scrollToBottom = (0,
|
|
2516
|
+
const scrollToBottom = (0, import_react6.useCallback)(() => {
|
|
2055
2517
|
const element = containerRef.current;
|
|
2056
2518
|
if (!element) {
|
|
2057
2519
|
return;
|
|
@@ -2059,7 +2521,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2059
2521
|
element.scrollTop = element.scrollHeight;
|
|
2060
2522
|
setIsAtBottom(true);
|
|
2061
2523
|
}, []);
|
|
2062
|
-
const setContainer = (0,
|
|
2524
|
+
const setContainer = (0, import_react6.useCallback)((element) => {
|
|
2063
2525
|
containerRef.current = element;
|
|
2064
2526
|
if (element) {
|
|
2065
2527
|
measure();
|
|
@@ -2067,7 +2529,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2067
2529
|
}, [
|
|
2068
2530
|
measure
|
|
2069
2531
|
]);
|
|
2070
|
-
(0,
|
|
2532
|
+
(0, import_react6.useEffect)(() => {
|
|
2071
2533
|
if (isAtBottom) {
|
|
2072
2534
|
scrollToBottom();
|
|
2073
2535
|
}
|
|
@@ -2076,7 +2538,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
2076
2538
|
isAtBottom,
|
|
2077
2539
|
scrollToBottom
|
|
2078
2540
|
]);
|
|
2079
|
-
const getContainerProps = (0,
|
|
2541
|
+
const getContainerProps = (0, import_react6.useCallback)(() => ({
|
|
2080
2542
|
ref: setContainer,
|
|
2081
2543
|
onScroll: measure
|
|
2082
2544
|
}), [
|
|
@@ -2098,14 +2560,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
|
|
|
2098
2560
|
var DEFAULT_COPY_RESET_MS = 1500;
|
|
2099
2561
|
function useChatTranscript(options) {
|
|
2100
2562
|
const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
|
|
2101
|
-
const latest = (0,
|
|
2563
|
+
const latest = (0, import_react7.useRef)(options);
|
|
2102
2564
|
latest.current = options;
|
|
2103
|
-
const [visibleCount, setVisibleCount] = (0,
|
|
2104
|
-
const [stopRequested, setStopRequested] = (0,
|
|
2105
|
-
const isStreaming = status === "streaming";
|
|
2565
|
+
const [visibleCount, setVisibleCount] = (0, import_react7.useState)(initialVisibleCount);
|
|
2566
|
+
const [stopRequested, setStopRequested] = (0, import_react7.useState)(false);
|
|
2567
|
+
const isStreaming = status === "streaming" || status === "reconnecting";
|
|
2106
2568
|
const isBusy = status === "submitted" || isStreaming;
|
|
2107
2569
|
const isEmpty = messages.length === 0;
|
|
2108
|
-
(0,
|
|
2570
|
+
(0, import_react7.useEffect)(() => {
|
|
2109
2571
|
if (!isBusy) {
|
|
2110
2572
|
setStopRequested(false);
|
|
2111
2573
|
}
|
|
@@ -2122,16 +2584,16 @@ function useChatTranscript(options) {
|
|
|
2122
2584
|
streamingMessageId,
|
|
2123
2585
|
options
|
|
2124
2586
|
});
|
|
2125
|
-
const stop = (0,
|
|
2587
|
+
const stop = (0, import_react7.useCallback)(() => {
|
|
2126
2588
|
const current = latest.current;
|
|
2127
|
-
if (current.status !== "submitted" && current.status !== "streaming") {
|
|
2589
|
+
if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
|
|
2128
2590
|
return;
|
|
2129
2591
|
}
|
|
2130
2592
|
setStopRequested(true);
|
|
2131
2593
|
void current.onStop?.();
|
|
2132
2594
|
}, []);
|
|
2133
|
-
const loadEarlier = (0,
|
|
2134
|
-
const showAll = (0,
|
|
2595
|
+
const loadEarlier = (0, import_react7.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
|
|
2596
|
+
const showAll = (0, import_react7.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
|
|
2135
2597
|
const scroll = useStickToBottom({
|
|
2136
2598
|
contentKey: contentSignature(messages),
|
|
2137
2599
|
...stickToBottomThreshold !== void 0 ? {
|
|
@@ -2167,7 +2629,7 @@ function useChatTranscript(options) {
|
|
|
2167
2629
|
__name(useChatTranscript, "useChatTranscript");
|
|
2168
2630
|
function useTranscriptItem(options) {
|
|
2169
2631
|
const { message, isStreaming = false } = options;
|
|
2170
|
-
const messagesRef = (0,
|
|
2632
|
+
const messagesRef = (0, import_react7.useRef)([
|
|
2171
2633
|
message
|
|
2172
2634
|
]);
|
|
2173
2635
|
if (messagesRef.current[0] !== message) {
|
|
@@ -2185,13 +2647,13 @@ function useTranscriptItem(options) {
|
|
|
2185
2647
|
}
|
|
2186
2648
|
__name(useTranscriptItem, "useTranscriptItem");
|
|
2187
2649
|
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,
|
|
2650
|
+
const [drafts, setDrafts] = (0, import_react7.useState)(() => /* @__PURE__ */ new Map());
|
|
2651
|
+
const [copied, setCopied] = (0, import_react7.useState)(() => /* @__PURE__ */ new Set());
|
|
2652
|
+
const [openReasoning, setOpenReasoning] = (0, import_react7.useState)(() => /* @__PURE__ */ new Map());
|
|
2653
|
+
const [picks, setPicks] = (0, import_react7.useState)(() => /* @__PURE__ */ new Map());
|
|
2654
|
+
const [settling, setSettling] = (0, import_react7.useState)(() => /* @__PURE__ */ new Map());
|
|
2655
|
+
const [settleErrors, setSettleErrors] = (0, import_react7.useState)(() => /* @__PURE__ */ new Map());
|
|
2656
|
+
const latest = (0, import_react7.useRef)({
|
|
2195
2657
|
options,
|
|
2196
2658
|
drafts,
|
|
2197
2659
|
picks,
|
|
@@ -2203,23 +2665,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2203
2665
|
picks,
|
|
2204
2666
|
messages
|
|
2205
2667
|
};
|
|
2206
|
-
const copyTimers = (0,
|
|
2207
|
-
(0,
|
|
2668
|
+
const copyTimers = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
2669
|
+
(0, import_react7.useEffect)(() => () => {
|
|
2208
2670
|
for (const timer of copyTimers.current.values()) {
|
|
2209
2671
|
clearTimeout(timer);
|
|
2210
2672
|
}
|
|
2211
2673
|
copyTimers.current.clear();
|
|
2212
2674
|
}, []);
|
|
2213
|
-
const callbackCache = (0,
|
|
2214
|
-
const reasoningToggleCache = (0,
|
|
2215
|
-
const toggleReasoning = (0,
|
|
2675
|
+
const callbackCache = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
2676
|
+
const reasoningToggleCache = (0, import_react7.useRef)(/* @__PURE__ */ new Map());
|
|
2677
|
+
const toggleReasoning = (0, import_react7.useCallback)((key, open) => {
|
|
2216
2678
|
setOpenReasoning((current) => {
|
|
2217
2679
|
const next = new Map(current);
|
|
2218
2680
|
next.set(key, open ?? !(current.get(key) ?? false));
|
|
2219
2681
|
return next;
|
|
2220
2682
|
});
|
|
2221
2683
|
}, []);
|
|
2222
|
-
const stableToggle = (0,
|
|
2684
|
+
const stableToggle = (0, import_react7.useCallback)((key) => {
|
|
2223
2685
|
let cached = reasoningToggleCache.current.get(key);
|
|
2224
2686
|
if (!cached) {
|
|
2225
2687
|
cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
|
|
@@ -2229,10 +2691,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2229
2691
|
}, [
|
|
2230
2692
|
toggleReasoning
|
|
2231
2693
|
]);
|
|
2232
|
-
const pick = (0,
|
|
2694
|
+
const pick = (0, import_react7.useCallback)((toolCallId, questionId, values) => {
|
|
2233
2695
|
setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
|
|
2234
2696
|
}, []);
|
|
2235
|
-
const settle = (0,
|
|
2697
|
+
const settle = (0, import_react7.useCallback)((toolCallId, action, send) => {
|
|
2236
2698
|
setSettleErrors((current) => {
|
|
2237
2699
|
if (!current.has(toolCallId)) {
|
|
2238
2700
|
return current;
|
|
@@ -2256,7 +2718,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2256
2718
|
failed(error);
|
|
2257
2719
|
}
|
|
2258
2720
|
}, []);
|
|
2259
|
-
const answer = (0,
|
|
2721
|
+
const answer = (0, import_react7.useCallback)((toolCallId) => {
|
|
2260
2722
|
const current = latest.current;
|
|
2261
2723
|
const onAnswer = current.options.onAnswer;
|
|
2262
2724
|
if (!onAnswer) {
|
|
@@ -2266,7 +2728,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2266
2728
|
}, [
|
|
2267
2729
|
settle
|
|
2268
2730
|
]);
|
|
2269
|
-
const skip = (0,
|
|
2731
|
+
const skip = (0, import_react7.useCallback)((toolCallId) => {
|
|
2270
2732
|
const onSkip = latest.current.options.onSkip;
|
|
2271
2733
|
if (onSkip) {
|
|
2272
2734
|
settle(toolCallId, "skip", () => onSkip(toolCallId));
|
|
@@ -2274,7 +2736,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2274
2736
|
}, [
|
|
2275
2737
|
settle
|
|
2276
2738
|
]);
|
|
2277
|
-
const approve = (0,
|
|
2739
|
+
const approve = (0, import_react7.useCallback)((toolCallId, options2) => {
|
|
2278
2740
|
const onApprove = latest.current.options.onApprove;
|
|
2279
2741
|
if (onApprove) {
|
|
2280
2742
|
settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
|
|
@@ -2282,7 +2744,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2282
2744
|
}, [
|
|
2283
2745
|
settle
|
|
2284
2746
|
]);
|
|
2285
|
-
const reject = (0,
|
|
2747
|
+
const reject = (0, import_react7.useCallback)((toolCallId) => {
|
|
2286
2748
|
const onReject = latest.current.options.onReject;
|
|
2287
2749
|
if (onReject) {
|
|
2288
2750
|
settle(toolCallId, "reject", () => onReject(toolCallId));
|
|
@@ -2290,7 +2752,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2290
2752
|
}, [
|
|
2291
2753
|
settle
|
|
2292
2754
|
]);
|
|
2293
|
-
const callbacksFor = (0,
|
|
2755
|
+
const callbacksFor = (0, import_react7.useCallback)((id) => {
|
|
2294
2756
|
const cached = callbackCache.current.get(id);
|
|
2295
2757
|
if (cached) {
|
|
2296
2758
|
return cached;
|
|
@@ -2532,7 +2994,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
|
|
|
2532
2994
|
getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
|
|
2533
2995
|
} : {}
|
|
2534
2996
|
});
|
|
2535
|
-
return /* @__PURE__ */
|
|
2997
|
+
return /* @__PURE__ */ import_react8.default.createElement(MessageItemView, {
|
|
2536
2998
|
item,
|
|
2537
2999
|
...slots
|
|
2538
3000
|
});
|
|
@@ -2540,51 +3002,51 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
|
|
|
2540
3002
|
__name(MessageItem, "MessageItem");
|
|
2541
3003
|
function MessageItemView({ item, ...slots }) {
|
|
2542
3004
|
const { classNames } = slots;
|
|
2543
|
-
const actionRow = /* @__PURE__ */
|
|
3005
|
+
const actionRow = /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2544
3006
|
className: classNames?.actions,
|
|
2545
3007
|
"data-actions-for": item.role
|
|
2546
|
-
}, item.copy.available ? /* @__PURE__ */
|
|
3008
|
+
}, item.copy.available ? /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
2547
3009
|
type: "button",
|
|
2548
3010
|
onClick: item.copy.copy,
|
|
2549
3011
|
className: classNames?.actionButton,
|
|
2550
3012
|
title: item.copy.copied ? "Copied!" : "Copy message text"
|
|
2551
|
-
}, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */
|
|
3013
|
+
}, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
2552
3014
|
type: "button",
|
|
2553
3015
|
onClick: item.fork.run,
|
|
2554
3016
|
className: classNames?.actionButton,
|
|
2555
3017
|
title: "Fork thread from this message"
|
|
2556
|
-
}, "Fork") : null, item.regenerate.available ? /* @__PURE__ */
|
|
3018
|
+
}, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
2557
3019
|
type: "button",
|
|
2558
3020
|
onClick: item.regenerate.run,
|
|
2559
3021
|
className: classNames?.actionButton,
|
|
2560
3022
|
title: "Regenerate response"
|
|
2561
|
-
}, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */
|
|
3023
|
+
}, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react8.default.createElement("span", {
|
|
2562
3024
|
className: classNames?.usage,
|
|
2563
3025
|
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__ */
|
|
3026
|
+
}, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react8.default.createElement("span", {
|
|
2565
3027
|
className: classNames?.timestamp,
|
|
2566
3028
|
title: item.timestamp.absolute
|
|
2567
3029
|
}, item.timestamp.relative) : null);
|
|
2568
3030
|
if (item.edit.available) {
|
|
2569
|
-
return /* @__PURE__ */
|
|
3031
|
+
return /* @__PURE__ */ import_react8.default.createElement(EditableUserBubble, {
|
|
2570
3032
|
item,
|
|
2571
3033
|
slots,
|
|
2572
3034
|
actions: actionRow
|
|
2573
3035
|
});
|
|
2574
3036
|
}
|
|
2575
3037
|
const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
|
|
2576
|
-
return /* @__PURE__ */
|
|
3038
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2577
3039
|
className,
|
|
2578
3040
|
"data-role": item.role,
|
|
2579
3041
|
"data-streaming": item.isStreaming ? "true" : void 0
|
|
2580
|
-
}, /* @__PURE__ */
|
|
3042
|
+
}, /* @__PURE__ */ import_react8.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
|
|
2581
3043
|
}
|
|
2582
3044
|
__name(MessageItemView, "MessageItemView");
|
|
2583
3045
|
function renderBlocks(item, slots) {
|
|
2584
3046
|
const nodes = [];
|
|
2585
3047
|
for (const block of item.blocks) {
|
|
2586
3048
|
if (block.kind === "text") {
|
|
2587
|
-
nodes.push(/* @__PURE__ */
|
|
3049
|
+
nodes.push(/* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2588
3050
|
key: block.key,
|
|
2589
3051
|
className: slots.classNames?.text
|
|
2590
3052
|
}, slots.renderText ? slots.renderText(block.text, {
|
|
@@ -2605,7 +3067,7 @@ function renderBlocks(item, slots) {
|
|
|
2605
3067
|
continue;
|
|
2606
3068
|
}
|
|
2607
3069
|
if (block.kind === "ui" && slots.renderUi) {
|
|
2608
|
-
nodes.push(/* @__PURE__ */
|
|
3070
|
+
nodes.push(/* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2609
3071
|
key: block.key,
|
|
2610
3072
|
"data-slot": "ui",
|
|
2611
3073
|
"data-component": block.component
|
|
@@ -2616,18 +3078,18 @@ function renderBlocks(item, slots) {
|
|
|
2616
3078
|
}
|
|
2617
3079
|
__name(renderBlocks, "renderBlocks");
|
|
2618
3080
|
function renderFilesBlock(block, slots) {
|
|
2619
|
-
return /* @__PURE__ */
|
|
3081
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2620
3082
|
key: block.key,
|
|
2621
3083
|
className: slots.classNames?.files,
|
|
2622
3084
|
"data-files": "true"
|
|
2623
|
-
}, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */
|
|
3085
|
+
}, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react8.default.createElement("a", {
|
|
2624
3086
|
key: file.url,
|
|
2625
3087
|
href: file.url,
|
|
2626
3088
|
target: "_blank",
|
|
2627
3089
|
rel: "noreferrer",
|
|
2628
3090
|
className: slots.classNames?.file,
|
|
2629
3091
|
title: file.filename ?? file.mediaType
|
|
2630
|
-
}, file.isImage ? /* @__PURE__ */
|
|
3092
|
+
}, file.isImage ? /* @__PURE__ */ import_react8.default.createElement("img", {
|
|
2631
3093
|
src: file.url,
|
|
2632
3094
|
alt: file.filename ?? "image",
|
|
2633
3095
|
className: slots.classNames?.fileImage
|
|
@@ -2635,17 +3097,17 @@ function renderFilesBlock(block, slots) {
|
|
|
2635
3097
|
}
|
|
2636
3098
|
__name(renderFilesBlock, "renderFilesBlock");
|
|
2637
3099
|
function renderReasoningBlock(block, slots) {
|
|
2638
|
-
return /* @__PURE__ */
|
|
3100
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2639
3101
|
key: block.key,
|
|
2640
3102
|
className: slots.classNames?.reasoning,
|
|
2641
3103
|
"data-reasoning": "true",
|
|
2642
3104
|
"data-streaming": block.isStreaming ? "true" : void 0
|
|
2643
|
-
}, /* @__PURE__ */
|
|
3105
|
+
}, /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
2644
3106
|
type: "button",
|
|
2645
3107
|
className: slots.classNames?.reasoningToggle,
|
|
2646
3108
|
onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
|
|
2647
3109
|
"aria-expanded": block.isOpen
|
|
2648
|
-
}, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */
|
|
3110
|
+
}, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2649
3111
|
className: slots.classNames?.reasoningText
|
|
2650
3112
|
}, slots.renderReasoning ? slots.renderReasoning(block.text, {
|
|
2651
3113
|
isStreaming: block.isStreaming,
|
|
@@ -2656,7 +3118,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
|
|
|
2656
3118
|
function renderToolBlock(item, block, slots) {
|
|
2657
3119
|
if (slots.renderToolGroup) {
|
|
2658
3120
|
return [
|
|
2659
|
-
/* @__PURE__ */
|
|
3121
|
+
/* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, {
|
|
2660
3122
|
key: block.key
|
|
2661
3123
|
}, slots.renderToolGroup(block.parts, block.key))
|
|
2662
3124
|
];
|
|
@@ -2667,7 +3129,7 @@ function renderToolBlock(item, block, slots) {
|
|
|
2667
3129
|
const renderToolPart = slots.renderToolPart;
|
|
2668
3130
|
return block.parts.map((part) => {
|
|
2669
3131
|
const partKey = `${item.id}-${part.toolCallId}`;
|
|
2670
|
-
return /* @__PURE__ */
|
|
3132
|
+
return /* @__PURE__ */ import_react8.default.createElement(import_react8.default.Fragment, {
|
|
2671
3133
|
key: partKey
|
|
2672
3134
|
}, renderToolPart(part, partKey));
|
|
2673
3135
|
});
|
|
@@ -2677,14 +3139,14 @@ function EditableUserBubble({ item, slots, actions }) {
|
|
|
2677
3139
|
const { classNames } = slots;
|
|
2678
3140
|
const { edit } = item;
|
|
2679
3141
|
if (!edit.isEditing) {
|
|
2680
|
-
return /* @__PURE__ */
|
|
3142
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2681
3143
|
className: joinClasses(classNames?.root, classNames?.byRole?.user),
|
|
2682
3144
|
"data-role": "user"
|
|
2683
|
-
}, /* @__PURE__ */
|
|
3145
|
+
}, /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2684
3146
|
className: classNames?.text
|
|
2685
3147
|
}, slots.renderText ? slots.renderText(item.text, {
|
|
2686
3148
|
isStreaming: false
|
|
2687
|
-
}) : item.text), /* @__PURE__ */
|
|
3149
|
+
}) : item.text), /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
2688
3150
|
type: "button",
|
|
2689
3151
|
onClick: edit.start,
|
|
2690
3152
|
className: classNames?.actionButton,
|
|
@@ -2692,18 +3154,18 @@ function EditableUserBubble({ item, slots, actions }) {
|
|
|
2692
3154
|
}, "Edit"), actions);
|
|
2693
3155
|
}
|
|
2694
3156
|
const textareaProps = edit.getTextareaProps();
|
|
2695
|
-
return /* @__PURE__ */
|
|
3157
|
+
return /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2696
3158
|
"data-editing": "true"
|
|
2697
|
-
}, /* @__PURE__ */
|
|
3159
|
+
}, /* @__PURE__ */ import_react8.default.createElement("textarea", {
|
|
2698
3160
|
...textareaProps,
|
|
2699
3161
|
rows: Math.min(8, edit.draft.split("\n").length + 1),
|
|
2700
3162
|
className: classNames?.editTextarea
|
|
2701
|
-
}), /* @__PURE__ */
|
|
3163
|
+
}), /* @__PURE__ */ import_react8.default.createElement("div", {
|
|
2702
3164
|
className: classNames?.editActions
|
|
2703
|
-
}, /* @__PURE__ */
|
|
3165
|
+
}, /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
2704
3166
|
type: "button",
|
|
2705
3167
|
onClick: edit.cancel
|
|
2706
|
-
}, "Cancel"), /* @__PURE__ */
|
|
3168
|
+
}, "Cancel"), /* @__PURE__ */ import_react8.default.createElement("button", {
|
|
2707
3169
|
type: "button",
|
|
2708
3170
|
onClick: edit.save,
|
|
2709
3171
|
disabled: !edit.canSave
|
|
@@ -2913,7 +3375,11 @@ function createSkillsSource(options) {
|
|
|
2913
3375
|
if (cached) {
|
|
2914
3376
|
return cached;
|
|
2915
3377
|
}
|
|
2916
|
-
const
|
|
3378
|
+
const listSkills = options.client.listSkills;
|
|
3379
|
+
if (listSkills === void 0) {
|
|
3380
|
+
return Promise.reject(new AgentBackendUnsupportedError("listSkills"));
|
|
3381
|
+
}
|
|
3382
|
+
const pending = listSkills.call(options.client, threadId).then((entries) => entries.map(toItem)).catch((error) => {
|
|
2917
3383
|
byThread.delete(key);
|
|
2918
3384
|
throw error;
|
|
2919
3385
|
});
|
|
@@ -2943,35 +3409,35 @@ function toItem(entry) {
|
|
|
2943
3409
|
__name(toItem, "toItem");
|
|
2944
3410
|
|
|
2945
3411
|
// src/composer/use-composer-autocomplete.ts
|
|
2946
|
-
var
|
|
3412
|
+
var import_react9 = require("react");
|
|
2947
3413
|
function useComposerAutocomplete(options) {
|
|
2948
3414
|
const { value, sources, acceptOnTab = true } = options;
|
|
2949
|
-
const latest = (0,
|
|
3415
|
+
const latest = (0, import_react9.useRef)(options);
|
|
2950
3416
|
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,
|
|
3417
|
+
const inputRef = (0, import_react9.useRef)(null);
|
|
3418
|
+
const [caret, setCaret] = (0, import_react9.useState)(value.length);
|
|
3419
|
+
const [items, setItems] = (0, import_react9.useState)([]);
|
|
3420
|
+
const [isLoading, setLoading] = (0, import_react9.useState)(false);
|
|
3421
|
+
const [error, setError] = (0, import_react9.useState)(null);
|
|
3422
|
+
const [activeIndex, setActiveIndex] = (0, import_react9.useState)(0);
|
|
3423
|
+
const [dismissedIndex, setDismissedIndex] = (0, import_react9.useState)(null);
|
|
2958
3424
|
const safeCaret = Math.min(caret, value.length);
|
|
2959
|
-
const match = (0,
|
|
3425
|
+
const match = (0, import_react9.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
|
|
2960
3426
|
value,
|
|
2961
3427
|
safeCaret,
|
|
2962
3428
|
sources
|
|
2963
3429
|
]);
|
|
2964
3430
|
const isActive = match !== null && dismissedIndex !== match.index;
|
|
2965
|
-
const matchRef = (0,
|
|
3431
|
+
const matchRef = (0, import_react9.useRef)(match);
|
|
2966
3432
|
matchRef.current = match;
|
|
2967
|
-
const caretRef = (0,
|
|
3433
|
+
const caretRef = (0, import_react9.useRef)(safeCaret);
|
|
2968
3434
|
caretRef.current = safeCaret;
|
|
2969
|
-
const itemsRef = (0,
|
|
3435
|
+
const itemsRef = (0, import_react9.useRef)(items);
|
|
2970
3436
|
itemsRef.current = items;
|
|
2971
|
-
const activeIndexRef = (0,
|
|
3437
|
+
const activeIndexRef = (0, import_react9.useRef)(activeIndex);
|
|
2972
3438
|
activeIndexRef.current = activeIndex;
|
|
2973
3439
|
const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
|
|
2974
|
-
(0,
|
|
3440
|
+
(0, import_react9.useEffect)(() => {
|
|
2975
3441
|
const current = matchRef.current;
|
|
2976
3442
|
if (requestKey === null || current === null) {
|
|
2977
3443
|
setItems([]);
|
|
@@ -3019,25 +3485,25 @@ function useComposerAutocomplete(options) {
|
|
|
3019
3485
|
]);
|
|
3020
3486
|
const isOpen = isActive;
|
|
3021
3487
|
const activeItem = isOpen ? items[activeIndex] ?? null : null;
|
|
3022
|
-
const reactId = (0,
|
|
3488
|
+
const reactId = (0, import_react9.useId)();
|
|
3023
3489
|
const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
|
|
3024
|
-
const optionId = (0,
|
|
3490
|
+
const optionId = (0, import_react9.useCallback)((index) => `${listboxId}-option-${index}`, [
|
|
3025
3491
|
listboxId
|
|
3026
3492
|
]);
|
|
3027
|
-
const highlight = (0,
|
|
3493
|
+
const highlight = (0, import_react9.useCallback)((index) => {
|
|
3028
3494
|
const total = itemsRef.current.length;
|
|
3029
3495
|
if (total === 0) return;
|
|
3030
3496
|
setActiveIndex((index % total + total) % total);
|
|
3031
3497
|
}, []);
|
|
3032
|
-
const moveHighlight = (0,
|
|
3498
|
+
const moveHighlight = (0, import_react9.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
|
|
3033
3499
|
highlight
|
|
3034
3500
|
]);
|
|
3035
|
-
const dismiss = (0,
|
|
3501
|
+
const dismiss = (0, import_react9.useCallback)(() => {
|
|
3036
3502
|
const current = matchRef.current;
|
|
3037
3503
|
if (current) setDismissedIndex(current.index);
|
|
3038
3504
|
}, []);
|
|
3039
|
-
const pendingCaret = (0,
|
|
3040
|
-
const accept = (0,
|
|
3505
|
+
const pendingCaret = (0, import_react9.useRef)(null);
|
|
3506
|
+
const accept = (0, import_react9.useCallback)((item) => {
|
|
3041
3507
|
const current = matchRef.current;
|
|
3042
3508
|
if (!current) return;
|
|
3043
3509
|
const chosen = item ?? itemsRef.current[activeIndexRef.current];
|
|
@@ -3048,16 +3514,16 @@ function useComposerAutocomplete(options) {
|
|
|
3048
3514
|
setDismissedIndex(null);
|
|
3049
3515
|
pendingCaret.current = edit.caret;
|
|
3050
3516
|
}, []);
|
|
3051
|
-
(0,
|
|
3517
|
+
(0, import_react9.useLayoutEffect)(() => {
|
|
3052
3518
|
const position = pendingCaret.current;
|
|
3053
3519
|
if (position === null) return;
|
|
3054
3520
|
pendingCaret.current = null;
|
|
3055
3521
|
inputRef.current?.setSelectionRange(position, position);
|
|
3056
3522
|
});
|
|
3057
|
-
const readCaret = (0,
|
|
3523
|
+
const readCaret = (0, import_react9.useCallback)((element) => {
|
|
3058
3524
|
setCaret(element.selectionStart ?? element.value.length);
|
|
3059
3525
|
}, []);
|
|
3060
|
-
const onKeyDown = (0,
|
|
3526
|
+
const onKeyDown = (0, import_react9.useCallback)((event) => {
|
|
3061
3527
|
if (!isOpen) return;
|
|
3062
3528
|
const hasItems = itemsRef.current.length > 0;
|
|
3063
3529
|
switch (event.key) {
|
|
@@ -3095,23 +3561,23 @@ function useComposerAutocomplete(options) {
|
|
|
3095
3561
|
dismiss,
|
|
3096
3562
|
moveHighlight
|
|
3097
3563
|
]);
|
|
3098
|
-
const onChange = (0,
|
|
3564
|
+
const onChange = (0, import_react9.useCallback)((event) => {
|
|
3099
3565
|
readCaret(event.target);
|
|
3100
3566
|
const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
|
|
3101
3567
|
setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
|
|
3102
3568
|
}, [
|
|
3103
3569
|
readCaret
|
|
3104
3570
|
]);
|
|
3105
|
-
const onSelect = (0,
|
|
3571
|
+
const onSelect = (0, import_react9.useCallback)((event) => readCaret(event.currentTarget), [
|
|
3106
3572
|
readCaret
|
|
3107
3573
|
]);
|
|
3108
|
-
const onClick = (0,
|
|
3574
|
+
const onClick = (0, import_react9.useCallback)((event) => readCaret(event.currentTarget), [
|
|
3109
3575
|
readCaret
|
|
3110
3576
|
]);
|
|
3111
|
-
const setInput = (0,
|
|
3577
|
+
const setInput = (0, import_react9.useCallback)((element) => {
|
|
3112
3578
|
inputRef.current = element;
|
|
3113
3579
|
}, []);
|
|
3114
|
-
const getInputProps = (0,
|
|
3580
|
+
const getInputProps = (0, import_react9.useCallback)(() => ({
|
|
3115
3581
|
ref: setInput,
|
|
3116
3582
|
onChange,
|
|
3117
3583
|
onKeyDown,
|
|
@@ -3139,7 +3605,7 @@ function useComposerAutocomplete(options) {
|
|
|
3139
3605
|
activeIndex,
|
|
3140
3606
|
optionId
|
|
3141
3607
|
]);
|
|
3142
|
-
const getListboxProps = (0,
|
|
3608
|
+
const getListboxProps = (0, import_react9.useCallback)(() => ({
|
|
3143
3609
|
id: listboxId,
|
|
3144
3610
|
role: "listbox",
|
|
3145
3611
|
"aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
|
|
@@ -3147,7 +3613,7 @@ function useComposerAutocomplete(options) {
|
|
|
3147
3613
|
listboxId,
|
|
3148
3614
|
match
|
|
3149
3615
|
]);
|
|
3150
|
-
const getOptionProps = (0,
|
|
3616
|
+
const getOptionProps = (0, import_react9.useCallback)((index) => ({
|
|
3151
3617
|
id: optionId(index),
|
|
3152
3618
|
role: "option",
|
|
3153
3619
|
"aria-selected": index === activeIndex
|
|
@@ -3181,7 +3647,7 @@ function isPromise(value) {
|
|
|
3181
3647
|
__name(isPromise, "isPromise");
|
|
3182
3648
|
|
|
3183
3649
|
// src/presentation/use-tool-catalog.ts
|
|
3184
|
-
var
|
|
3650
|
+
var import_react10 = require("react");
|
|
3185
3651
|
var shared = /* @__PURE__ */ new WeakMap();
|
|
3186
3652
|
function load(client, agent) {
|
|
3187
3653
|
let byAgent = shared.get(client);
|
|
@@ -3192,6 +3658,9 @@ function load(client, agent) {
|
|
|
3192
3658
|
const key = agent ?? "";
|
|
3193
3659
|
const cached = byAgent.get(key);
|
|
3194
3660
|
if (cached !== void 0) return cached;
|
|
3661
|
+
if (client.listTools === void 0) {
|
|
3662
|
+
return Promise.reject(new AgentBackendUnsupportedError("listTools"));
|
|
3663
|
+
}
|
|
3195
3664
|
const request = client.listTools(agent).catch((error) => {
|
|
3196
3665
|
byAgent.delete(key);
|
|
3197
3666
|
throw error;
|
|
@@ -3202,11 +3671,11 @@ function load(client, agent) {
|
|
|
3202
3671
|
__name(load, "load");
|
|
3203
3672
|
function useToolCatalog(options) {
|
|
3204
3673
|
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,
|
|
3674
|
+
const [entries, setEntries] = (0, import_react10.useState)([]);
|
|
3675
|
+
const [isLoading, setLoading] = (0, import_react10.useState)(enabled);
|
|
3676
|
+
const [error, setError] = (0, import_react10.useState)(null);
|
|
3677
|
+
const [generation, setGeneration] = (0, import_react10.useState)(0);
|
|
3678
|
+
(0, import_react10.useEffect)(() => {
|
|
3210
3679
|
if (!enabled) {
|
|
3211
3680
|
setLoading(false);
|
|
3212
3681
|
return;
|
|
@@ -3232,14 +3701,14 @@ function useToolCatalog(options) {
|
|
|
3232
3701
|
enabled,
|
|
3233
3702
|
generation
|
|
3234
3703
|
]);
|
|
3235
|
-
const refresh = (0,
|
|
3704
|
+
const refresh = (0, import_react10.useCallback)(() => {
|
|
3236
3705
|
shared.get(client)?.delete(agent ?? "");
|
|
3237
3706
|
setGeneration((current) => current + 1);
|
|
3238
3707
|
}, [
|
|
3239
3708
|
client,
|
|
3240
3709
|
agent
|
|
3241
3710
|
]);
|
|
3242
|
-
const catalog = (0,
|
|
3711
|
+
const catalog = (0, import_react10.useMemo)(() => toolCatalogFrom(entries), [
|
|
3243
3712
|
entries
|
|
3244
3713
|
]);
|
|
3245
3714
|
return {
|
|
@@ -3253,7 +3722,7 @@ function useToolCatalog(options) {
|
|
|
3253
3722
|
__name(useToolCatalog, "useToolCatalog");
|
|
3254
3723
|
|
|
3255
3724
|
// src/approvals/countdown.ts
|
|
3256
|
-
var
|
|
3725
|
+
var import_react11 = require("react");
|
|
3257
3726
|
function approvalCountdown(expiresAt, now = Date.now()) {
|
|
3258
3727
|
const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
|
|
3259
3728
|
if (!Number.isFinite(deadline)) {
|
|
@@ -3271,10 +3740,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
|
|
|
3271
3740
|
__name(approvalCountdown, "approvalCountdown");
|
|
3272
3741
|
function useApprovalCountdown(expiresAt, options = {}) {
|
|
3273
3742
|
const { intervalMs = 1e3 } = options;
|
|
3274
|
-
const nowRef = (0,
|
|
3743
|
+
const nowRef = (0, import_react11.useRef)(options.now ?? Date.now);
|
|
3275
3744
|
nowRef.current = options.now ?? Date.now;
|
|
3276
|
-
const [state, setState] = (0,
|
|
3277
|
-
(0,
|
|
3745
|
+
const [state, setState] = (0, import_react11.useState)(() => approvalCountdown(expiresAt, nowRef.current()));
|
|
3746
|
+
(0, import_react11.useEffect)(() => {
|
|
3278
3747
|
const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
|
|
3279
3748
|
const first = read();
|
|
3280
3749
|
setState(first);
|
|
@@ -3440,7 +3909,12 @@ function storedMessageToUiMessage(message) {
|
|
|
3440
3909
|
return {
|
|
3441
3910
|
id: message.id,
|
|
3442
3911
|
role: message.role,
|
|
3443
|
-
parts
|
|
3912
|
+
parts,
|
|
3913
|
+
...message.feedback !== void 0 ? {
|
|
3914
|
+
metadata: {
|
|
3915
|
+
feedback: message.feedback
|
|
3916
|
+
}
|
|
3917
|
+
} : {}
|
|
3444
3918
|
};
|
|
3445
3919
|
}
|
|
3446
3920
|
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
@@ -3476,14 +3950,21 @@ function mergeTurn(rows) {
|
|
|
3476
3950
|
}
|
|
3477
3951
|
const parts = rows.flatMap((row) => storedMessageToUiMessage(row).parts);
|
|
3478
3952
|
const usage = sumUsage(rows);
|
|
3953
|
+
const feedback = lastRow.feedback;
|
|
3954
|
+
const metadata = {
|
|
3955
|
+
...usage !== void 0 ? {
|
|
3956
|
+
usage
|
|
3957
|
+
} : {},
|
|
3958
|
+
...feedback !== void 0 ? {
|
|
3959
|
+
feedback
|
|
3960
|
+
} : {}
|
|
3961
|
+
};
|
|
3479
3962
|
return {
|
|
3480
3963
|
id: lastRow.id,
|
|
3481
3964
|
role: lastRow.role,
|
|
3482
3965
|
parts,
|
|
3483
|
-
...
|
|
3484
|
-
metadata
|
|
3485
|
-
usage
|
|
3486
|
-
}
|
|
3966
|
+
...Object.keys(metadata).length > 0 ? {
|
|
3967
|
+
metadata
|
|
3487
3968
|
} : {}
|
|
3488
3969
|
};
|
|
3489
3970
|
}
|
|
@@ -3503,21 +3984,22 @@ function sumUsage(rows) {
|
|
|
3503
3984
|
__name(sumUsage, "sumUsage");
|
|
3504
3985
|
|
|
3505
3986
|
// src/use-agent-chat.ts
|
|
3506
|
-
var
|
|
3507
|
-
var
|
|
3987
|
+
var import_react12 = require("@ai-sdk/react");
|
|
3988
|
+
var import_react13 = require("react");
|
|
3508
3989
|
function useAgentChat(options) {
|
|
3509
|
-
const latest = (0,
|
|
3990
|
+
const latest = (0, import_react13.useRef)(options);
|
|
3510
3991
|
latest.current = options;
|
|
3511
|
-
const [runId, setRunId] = (0,
|
|
3512
|
-
const runIdRef = (0,
|
|
3992
|
+
const [runId, setRunId] = (0, import_react13.useState)(options.resumeRunId);
|
|
3993
|
+
const runIdRef = (0, import_react13.useRef)(runId);
|
|
3513
3994
|
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,
|
|
3995
|
+
const settlingRunIdRef = (0, import_react13.useRef)(void 0);
|
|
3996
|
+
const autoResumeRunIdRef = (0, import_react13.useRef)(void 0);
|
|
3997
|
+
const [autoResume, setAutoResume] = (0, import_react13.useState)(false);
|
|
3998
|
+
const [activeRunId, setActiveRunId] = (0, import_react13.useState)(null);
|
|
3999
|
+
const regenerateNext = (0, import_react13.useRef)(false);
|
|
4000
|
+
const createdThreadId = (0, import_react13.useRef)(void 0);
|
|
4001
|
+
const client = (0, import_react13.useMemo)(() => {
|
|
4002
|
+
if (options.backend) return options.backend;
|
|
3521
4003
|
if (options.client) return options.client;
|
|
3522
4004
|
return new AgentClient({
|
|
3523
4005
|
...options.baseUrl !== void 0 ? {
|
|
@@ -3532,7 +4014,11 @@ function useAgentChat(options) {
|
|
|
3532
4014
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
3533
4015
|
});
|
|
3534
4016
|
}, []);
|
|
3535
|
-
(0,
|
|
4017
|
+
const [connection, setConnection] = (0, import_react13.useState)({
|
|
4018
|
+
status: "live"
|
|
4019
|
+
});
|
|
4020
|
+
const resyncAfterSettle = (0, import_react13.useRef)(false);
|
|
4021
|
+
(0, import_react13.useEffect)(() => {
|
|
3536
4022
|
if (!options.resume || options.threadId === void 0) return;
|
|
3537
4023
|
let cancelled = false;
|
|
3538
4024
|
const threadId = options.threadId;
|
|
@@ -3552,30 +4038,33 @@ function useAgentChat(options) {
|
|
|
3552
4038
|
options.resume,
|
|
3553
4039
|
options.threadId
|
|
3554
4040
|
]);
|
|
3555
|
-
const transport = (0,
|
|
4041
|
+
const transport = (0, import_react13.useMemo)(() => {
|
|
3556
4042
|
function onMeta(meta) {
|
|
3557
4043
|
settlingRunIdRef.current = meta.runId;
|
|
3558
4044
|
setRunId(meta.runId);
|
|
3559
4045
|
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
3560
4046
|
createdThreadId.current = meta.threadId;
|
|
3561
4047
|
latest.current.onThreadCreated?.(meta.threadId);
|
|
4048
|
+
notifyThreads(client, {
|
|
4049
|
+
type: "changed"
|
|
4050
|
+
});
|
|
3562
4051
|
}
|
|
3563
4052
|
}
|
|
3564
4053
|
__name(onMeta, "onMeta");
|
|
3565
4054
|
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
|
|
4055
|
+
backend: client,
|
|
4056
|
+
...options.reconnect !== void 0 ? {
|
|
4057
|
+
reconnect: options.reconnect
|
|
3574
4058
|
} : {},
|
|
4059
|
+
onConnectionChange: /* @__PURE__ */ __name((state) => {
|
|
4060
|
+
if (state.status === "gone") resyncAfterSettle.current = true;
|
|
4061
|
+
setConnection(state.status === "gone" ? {
|
|
4062
|
+
status: "live"
|
|
4063
|
+
} : state);
|
|
4064
|
+
}, "onConnectionChange"),
|
|
3575
4065
|
...options.agent !== void 0 ? {
|
|
3576
4066
|
agent: options.agent
|
|
3577
4067
|
} : {},
|
|
3578
|
-
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders"),
|
|
3579
4068
|
getBody: /* @__PURE__ */ __name(() => {
|
|
3580
4069
|
const current = latest.current;
|
|
3581
4070
|
const pageContext = current.getPageContext?.() ?? null;
|
|
@@ -3601,8 +4090,8 @@ function useAgentChat(options) {
|
|
|
3601
4090
|
}, "onAttemptStart")
|
|
3602
4091
|
});
|
|
3603
4092
|
}, []);
|
|
3604
|
-
const backgroundRef = (0,
|
|
3605
|
-
const chat = (0,
|
|
4093
|
+
const backgroundRef = (0, import_react13.useRef)(() => Promise.resolve());
|
|
4094
|
+
const chat = (0, import_react12.useChat)({
|
|
3606
4095
|
transport,
|
|
3607
4096
|
resume: options.resumeRunId !== void 0 || autoResume,
|
|
3608
4097
|
...options.threadId !== void 0 ? {
|
|
@@ -3615,7 +4104,15 @@ function useAgentChat(options) {
|
|
|
3615
4104
|
latest.current.onData?.(part);
|
|
3616
4105
|
if (part.type === "data-title") {
|
|
3617
4106
|
const title = part.data?.title;
|
|
3618
|
-
if (typeof title === "string")
|
|
4107
|
+
if (typeof title === "string") {
|
|
4108
|
+
latest.current.onTitle?.(title);
|
|
4109
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4110
|
+
if (threadId !== void 0) notifyThreads(client, {
|
|
4111
|
+
type: "title",
|
|
4112
|
+
threadId,
|
|
4113
|
+
title
|
|
4114
|
+
});
|
|
4115
|
+
}
|
|
3619
4116
|
}
|
|
3620
4117
|
}, "onData"),
|
|
3621
4118
|
onFinish: /* @__PURE__ */ __name(({ isError }) => {
|
|
@@ -3629,16 +4126,36 @@ function useAgentChat(options) {
|
|
|
3629
4126
|
runId: settledRunId,
|
|
3630
4127
|
status: isError ? "failed" : "completed"
|
|
3631
4128
|
});
|
|
4129
|
+
notifyThreads(client, {
|
|
4130
|
+
type: "changed"
|
|
4131
|
+
});
|
|
4132
|
+
}
|
|
4133
|
+
setConnection({
|
|
4134
|
+
status: "live"
|
|
4135
|
+
});
|
|
4136
|
+
if (resyncAfterSettle.current) {
|
|
4137
|
+
resyncAfterSettle.current = false;
|
|
4138
|
+
void resyncFromThread();
|
|
3632
4139
|
}
|
|
3633
4140
|
}, "onFinish")
|
|
3634
4141
|
});
|
|
3635
|
-
const chatRef = (0,
|
|
4142
|
+
const chatRef = (0, import_react13.useRef)(chat);
|
|
3636
4143
|
chatRef.current = chat;
|
|
3637
|
-
|
|
3638
|
-
|
|
4144
|
+
async function resyncFromThread() {
|
|
4145
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4146
|
+
if (threadId === void 0) return;
|
|
4147
|
+
try {
|
|
4148
|
+
const thread = await client.getThread(threadId);
|
|
4149
|
+
chatRef.current.setMessages(storedThreadToUiMessages(thread.messages));
|
|
4150
|
+
} catch {
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
__name(resyncFromThread, "resyncFromThread");
|
|
4154
|
+
const turnInFlight = (0, import_react13.useRef)(false);
|
|
4155
|
+
const isTurnInFlight = (0, import_react13.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
|
|
3639
4156
|
transport
|
|
3640
4157
|
]);
|
|
3641
|
-
const sendMessage = (0,
|
|
4158
|
+
const sendMessage = (0, import_react13.useCallback)(async (...args) => {
|
|
3642
4159
|
if (isTurnInFlight()) return;
|
|
3643
4160
|
turnInFlight.current = true;
|
|
3644
4161
|
try {
|
|
@@ -3649,15 +4166,15 @@ function useAgentChat(options) {
|
|
|
3649
4166
|
}, [
|
|
3650
4167
|
isTurnInFlight
|
|
3651
4168
|
]);
|
|
3652
|
-
const addToolResult = (0,
|
|
4169
|
+
const addToolResult = (0, import_react13.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
3653
4170
|
tool,
|
|
3654
4171
|
toolCallId,
|
|
3655
4172
|
output
|
|
3656
4173
|
}), []);
|
|
3657
|
-
const [threads, setThreads] = (0,
|
|
3658
|
-
const [quota, setQuota] = (0,
|
|
3659
|
-
const [backgroundRuns, setBackgroundRuns] = (0,
|
|
3660
|
-
const refreshBackground = (0,
|
|
4174
|
+
const [threads, setThreads] = (0, import_react13.useState)([]);
|
|
4175
|
+
const [quota, setQuota] = (0, import_react13.useState)(null);
|
|
4176
|
+
const [backgroundRuns, setBackgroundRuns] = (0, import_react13.useState)([]);
|
|
4177
|
+
const refreshBackground = (0, import_react13.useCallback)(async () => {
|
|
3661
4178
|
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
3662
4179
|
if (threadId === void 0) {
|
|
3663
4180
|
return;
|
|
@@ -3682,7 +4199,7 @@ function useAgentChat(options) {
|
|
|
3682
4199
|
]);
|
|
3683
4200
|
backgroundRef.current = refreshBackground;
|
|
3684
4201
|
const isWorking = backgroundRuns.some((run) => run.status === "running");
|
|
3685
|
-
(0,
|
|
4202
|
+
(0, import_react13.useEffect)(() => {
|
|
3686
4203
|
if (options.background !== true || options.threadId === void 0) {
|
|
3687
4204
|
return;
|
|
3688
4205
|
}
|
|
@@ -3692,7 +4209,7 @@ function useAgentChat(options) {
|
|
|
3692
4209
|
options.threadId,
|
|
3693
4210
|
refreshBackground
|
|
3694
4211
|
]);
|
|
3695
|
-
(0,
|
|
4212
|
+
(0, import_react13.useEffect)(() => {
|
|
3696
4213
|
const every = latest.current.backgroundPollMs ?? 5e3;
|
|
3697
4214
|
if (latest.current.background !== true || !isWorking || every <= 0) {
|
|
3698
4215
|
return;
|
|
@@ -3703,27 +4220,29 @@ function useAgentChat(options) {
|
|
|
3703
4220
|
isWorking,
|
|
3704
4221
|
refreshBackground
|
|
3705
4222
|
]);
|
|
3706
|
-
const loadThreads = (0,
|
|
4223
|
+
const loadThreads = (0, import_react13.useCallback)(async () => {
|
|
3707
4224
|
const list = await client.listThreads();
|
|
3708
4225
|
setThreads(list);
|
|
3709
4226
|
return list;
|
|
3710
4227
|
}, [
|
|
3711
4228
|
client
|
|
3712
4229
|
]);
|
|
3713
|
-
const loadThread = (0,
|
|
4230
|
+
const loadThread = (0, import_react13.useCallback)((id) => client.getThread(id), [
|
|
3714
4231
|
client
|
|
3715
4232
|
]);
|
|
3716
|
-
const deleteThread = (0,
|
|
4233
|
+
const deleteThread = (0, import_react13.useCallback)(async (id) => {
|
|
3717
4234
|
await client.deleteThread(id);
|
|
3718
4235
|
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
3719
4236
|
}, [
|
|
3720
4237
|
client
|
|
3721
4238
|
]);
|
|
3722
|
-
const forkThread = (0,
|
|
4239
|
+
const forkThread = (0, import_react13.useCallback)((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
|
|
3723
4240
|
client
|
|
3724
4241
|
]);
|
|
3725
|
-
const renameThread = (0,
|
|
3726
|
-
await client.
|
|
4242
|
+
const renameThread = (0, import_react13.useCallback)(async (id, title) => {
|
|
4243
|
+
await client.updateThread(id, {
|
|
4244
|
+
title
|
|
4245
|
+
});
|
|
3727
4246
|
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
3728
4247
|
...thread,
|
|
3729
4248
|
title
|
|
@@ -3731,24 +4250,24 @@ function useAgentChat(options) {
|
|
|
3731
4250
|
}, [
|
|
3732
4251
|
client
|
|
3733
4252
|
]);
|
|
3734
|
-
const promoteThread = (0,
|
|
3735
|
-
await client
|
|
4253
|
+
const promoteThread = (0, import_react13.useCallback)(async (id) => {
|
|
4254
|
+
await requireBackendMethod(client, "promoteThread")(id);
|
|
3736
4255
|
await loadThreads();
|
|
3737
4256
|
}, [
|
|
3738
4257
|
client,
|
|
3739
4258
|
loadThreads
|
|
3740
4259
|
]);
|
|
3741
|
-
const truncateFromMessage = (0,
|
|
4260
|
+
const truncateFromMessage = (0, import_react13.useCallback)((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
|
|
3742
4261
|
client
|
|
3743
4262
|
]);
|
|
3744
|
-
const loadQuota = (0,
|
|
3745
|
-
const today = await client
|
|
4263
|
+
const loadQuota = (0, import_react13.useCallback)(async () => {
|
|
4264
|
+
const today = await requireBackendMethod(client, "getQuotaToday")();
|
|
3746
4265
|
setQuota(today);
|
|
3747
4266
|
return today;
|
|
3748
4267
|
}, [
|
|
3749
4268
|
client
|
|
3750
4269
|
]);
|
|
3751
|
-
const cancel = (0,
|
|
4270
|
+
const cancel = (0, import_react13.useCallback)(async () => {
|
|
3752
4271
|
chatRef.current.stop();
|
|
3753
4272
|
const activeRunId2 = runIdRef.current;
|
|
3754
4273
|
if (activeRunId2) {
|
|
@@ -3760,8 +4279,8 @@ function useAgentChat(options) {
|
|
|
3760
4279
|
}, [
|
|
3761
4280
|
client
|
|
3762
4281
|
]);
|
|
3763
|
-
const approve = (0,
|
|
3764
|
-
await client
|
|
4282
|
+
const approve = (0, import_react13.useCallback)(async ({ toolCallId, remember, via }) => {
|
|
4283
|
+
await requireBackendMethod(client, "approveToolCall")({
|
|
3765
4284
|
toolCallId,
|
|
3766
4285
|
...remember === true ? {
|
|
3767
4286
|
remember: true
|
|
@@ -3773,8 +4292,8 @@ function useAgentChat(options) {
|
|
|
3773
4292
|
}, [
|
|
3774
4293
|
client
|
|
3775
4294
|
]);
|
|
3776
|
-
const reject = (0,
|
|
3777
|
-
await client
|
|
4295
|
+
const reject = (0, import_react13.useCallback)(async ({ toolCallId, reason, via }) => {
|
|
4296
|
+
await requireBackendMethod(client, "rejectToolCall")({
|
|
3778
4297
|
toolCallId,
|
|
3779
4298
|
...reason !== void 0 ? {
|
|
3780
4299
|
reason
|
|
@@ -3786,8 +4305,8 @@ function useAgentChat(options) {
|
|
|
3786
4305
|
}, [
|
|
3787
4306
|
client
|
|
3788
4307
|
]);
|
|
3789
|
-
const answer = (0,
|
|
3790
|
-
await client
|
|
4308
|
+
const answer = (0, import_react13.useCallback)(async ({ toolCallId, answers }) => {
|
|
4309
|
+
await requireBackendMethod(client, "answerToolCall")({
|
|
3791
4310
|
toolCallId,
|
|
3792
4311
|
...answers !== void 0 ? {
|
|
3793
4312
|
answers
|
|
@@ -3796,14 +4315,14 @@ function useAgentChat(options) {
|
|
|
3796
4315
|
}, [
|
|
3797
4316
|
client
|
|
3798
4317
|
]);
|
|
3799
|
-
const skip = (0,
|
|
3800
|
-
await client
|
|
4318
|
+
const skip = (0, import_react13.useCallback)(async ({ toolCallId }) => {
|
|
4319
|
+
await requireBackendMethod(client, "skipToolCall")({
|
|
3801
4320
|
toolCallId
|
|
3802
4321
|
});
|
|
3803
4322
|
}, [
|
|
3804
4323
|
client
|
|
3805
4324
|
]);
|
|
3806
|
-
const regenerate = (0,
|
|
4325
|
+
const regenerate = (0, import_react13.useCallback)(() => {
|
|
3807
4326
|
if (isTurnInFlight()) return;
|
|
3808
4327
|
regenerateNext.current = true;
|
|
3809
4328
|
turnInFlight.current = true;
|
|
@@ -3818,8 +4337,13 @@ function useAgentChat(options) {
|
|
|
3818
4337
|
isWorking,
|
|
3819
4338
|
refresh: refreshBackground
|
|
3820
4339
|
};
|
|
4340
|
+
const getThreadId = (0, import_react13.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
|
|
4341
|
+
const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
|
|
3821
4342
|
return {
|
|
3822
4343
|
...chat,
|
|
4344
|
+
status,
|
|
4345
|
+
/** Where the live stream stands — see {@link StreamConnectionState}. */
|
|
4346
|
+
connection,
|
|
3823
4347
|
sendMessage,
|
|
3824
4348
|
addToolResult,
|
|
3825
4349
|
/** Sub-agents this conversation started and did not wait for. */
|
|
@@ -3827,6 +4351,14 @@ function useAgentChat(options) {
|
|
|
3827
4351
|
runId,
|
|
3828
4352
|
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
3829
4353
|
activeRunId,
|
|
4354
|
+
/**
|
|
4355
|
+
* The thread this chat is on right now: the `threadId` option, else the one the backend created
|
|
4356
|
+
* on the first send. A getter, because the created id is learned mid-stream.
|
|
4357
|
+
*/
|
|
4358
|
+
getThreadId,
|
|
4359
|
+
/** The backend this chat talks to — pass it to `useThreads`, `useMessageFeedback`, … */
|
|
4360
|
+
backend: client,
|
|
4361
|
+
/** Same object as {@link backend}; kept for callers that read it under its old name. */
|
|
3830
4362
|
client,
|
|
3831
4363
|
threads,
|
|
3832
4364
|
loadThreads,
|
|
@@ -3857,6 +4389,7 @@ async function mergeHeaders(options) {
|
|
|
3857
4389
|
__name(mergeHeaders, "mergeHeaders");
|
|
3858
4390
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3859
4391
|
0 && (module.exports = {
|
|
4392
|
+
AgentBackendUnsupportedError,
|
|
3860
4393
|
AgentChatTransport,
|
|
3861
4394
|
AgentClient,
|
|
3862
4395
|
AgentHttpError,
|
|
@@ -3886,10 +4419,13 @@ __name(mergeHeaders, "mergeHeaders");
|
|
|
3886
4419
|
inferResultView,
|
|
3887
4420
|
isActionCall,
|
|
3888
4421
|
loadStoredSpeechLang,
|
|
4422
|
+
notifyThreads,
|
|
4423
|
+
onThreadsEvent,
|
|
3889
4424
|
persistSpeechLang,
|
|
3890
4425
|
phraseFor,
|
|
3891
4426
|
readPath,
|
|
3892
4427
|
readReasoningMs,
|
|
4428
|
+
requireBackendMethod,
|
|
3893
4429
|
resolveResultView,
|
|
3894
4430
|
storedMessageToUiMessage,
|
|
3895
4431
|
storedThreadToUiMessages,
|
|
@@ -3900,8 +4436,10 @@ __name(mergeHeaders, "mergeHeaders");
|
|
|
3900
4436
|
useChatTranscript,
|
|
3901
4437
|
useComposerAutocomplete,
|
|
3902
4438
|
useElapsed,
|
|
4439
|
+
useMessageFeedback,
|
|
3903
4440
|
useSpeechRecognition,
|
|
3904
4441
|
useStickToBottom,
|
|
4442
|
+
useThreads,
|
|
3905
4443
|
useToolCatalog,
|
|
3906
4444
|
useTranscriptItem,
|
|
3907
4445
|
validateAnswer,
|