@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.js
CHANGED
|
@@ -1,6 +1,246 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
|
+
// src/client.ts
|
|
5
|
+
var AgentHttpError = class extends Error {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "AgentHttpError");
|
|
8
|
+
}
|
|
9
|
+
status;
|
|
10
|
+
method;
|
|
11
|
+
path;
|
|
12
|
+
constructor(status, method, path, statusText) {
|
|
13
|
+
super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
|
|
14
|
+
this.name = "AgentHttpError";
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
var HEADER_RUN_ID = "x-agent-run-id";
|
|
18
|
+
var HEADER_THREAD_ID = "x-agent-thread-id";
|
|
19
|
+
var AgentClient = class {
|
|
20
|
+
static {
|
|
21
|
+
__name(this, "AgentClient");
|
|
22
|
+
}
|
|
23
|
+
options;
|
|
24
|
+
constructor(options = {}) {
|
|
25
|
+
this.options = options;
|
|
26
|
+
}
|
|
27
|
+
/** `POST /agent/chat` → the turn's SSE stream. Throws {@link AgentHttpError} on a non-2xx. */
|
|
28
|
+
async openChatStream(request) {
|
|
29
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: {
|
|
32
|
+
"content-type": "application/json",
|
|
33
|
+
accept: "text/event-stream",
|
|
34
|
+
...await this.resolveHeaders(),
|
|
35
|
+
...request.headers
|
|
36
|
+
},
|
|
37
|
+
body: JSON.stringify(request.body),
|
|
38
|
+
...this.credentials(),
|
|
39
|
+
...request.signal !== void 0 ? {
|
|
40
|
+
signal: request.signal
|
|
41
|
+
} : {}
|
|
42
|
+
});
|
|
43
|
+
if (!response.ok || !response.body) {
|
|
44
|
+
throw new AgentHttpError(response.status, "POST", "/agent/chat", response.statusText);
|
|
45
|
+
}
|
|
46
|
+
return streamResponse(response);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `GET /agent/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
|
|
50
|
+
* streaming under that id (404).
|
|
51
|
+
*/
|
|
52
|
+
async resumeChatStream(request) {
|
|
53
|
+
const path = `/agent/chat/${encodeURIComponent(request.runId)}/stream`;
|
|
54
|
+
const query = request.after !== void 0 && request.after > 0 ? `?after=${request.after}` : "";
|
|
55
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}${query}`, {
|
|
56
|
+
method: "GET",
|
|
57
|
+
headers: {
|
|
58
|
+
accept: "text/event-stream",
|
|
59
|
+
...await this.resolveHeaders(),
|
|
60
|
+
...request.headers
|
|
61
|
+
},
|
|
62
|
+
...this.credentials(),
|
|
63
|
+
...request.signal !== void 0 ? {
|
|
64
|
+
signal: request.signal
|
|
65
|
+
} : {}
|
|
66
|
+
});
|
|
67
|
+
if (response.status === 404) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
if (!response.ok || !response.body) {
|
|
71
|
+
throw new AgentHttpError(response.status, "GET", path, response.statusText);
|
|
72
|
+
}
|
|
73
|
+
return streamResponse(response);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Rate a message (`'up'`/`'down'`, optional comment) or clear its rating (`value: null`). Answers
|
|
77
|
+
* the stored rating.
|
|
78
|
+
*/
|
|
79
|
+
setMessageFeedback(messageId, input) {
|
|
80
|
+
return this.request("POST", `/agent/messages/${encodeURIComponent(messageId)}/feedback`, input);
|
|
81
|
+
}
|
|
82
|
+
listThreads() {
|
|
83
|
+
return this.request("GET", "/agent/threads");
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
87
|
+
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
88
|
+
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
89
|
+
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
90
|
+
*/
|
|
91
|
+
listSkills(threadId) {
|
|
92
|
+
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
93
|
+
return this.request("GET", `/agent/skills${query}`);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
|
|
97
|
+
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
|
|
98
|
+
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
|
|
99
|
+
*/
|
|
100
|
+
listTools(agent) {
|
|
101
|
+
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
102
|
+
return this.request("GET", `/agent/tools${query}`);
|
|
103
|
+
}
|
|
104
|
+
getThread(id) {
|
|
105
|
+
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
106
|
+
}
|
|
107
|
+
deleteThread(id) {
|
|
108
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
|
|
109
|
+
}
|
|
110
|
+
forkFromMessage(threadId, messageId) {
|
|
111
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
112
|
+
}
|
|
113
|
+
renameThread(id, title) {
|
|
114
|
+
return this.updateThread(id, {
|
|
115
|
+
title
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
119
|
+
updateThread(id, patch) {
|
|
120
|
+
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
124
|
+
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
125
|
+
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
126
|
+
*/
|
|
127
|
+
async uploadAttachment(file, options = {}) {
|
|
128
|
+
const formData = new FormData();
|
|
129
|
+
formData.append("file", file);
|
|
130
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/attachments`, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers: {
|
|
133
|
+
accept: "application/json",
|
|
134
|
+
...await this.resolveHeaders()
|
|
135
|
+
},
|
|
136
|
+
body: formData,
|
|
137
|
+
...this.credentials(),
|
|
138
|
+
...options.signal !== void 0 ? {
|
|
139
|
+
signal: options.signal
|
|
140
|
+
} : {}
|
|
141
|
+
});
|
|
142
|
+
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
143
|
+
}
|
|
144
|
+
promoteThread(id) {
|
|
145
|
+
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
146
|
+
}
|
|
147
|
+
truncateFromMessage(threadId, messageId) {
|
|
148
|
+
return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
|
|
149
|
+
}
|
|
150
|
+
getQuotaToday() {
|
|
151
|
+
return this.request("GET", "/agent/quota/today");
|
|
152
|
+
}
|
|
153
|
+
cancelStream(runId) {
|
|
154
|
+
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
158
|
+
* the decision came through (the server records `'web'` when omitted).
|
|
159
|
+
*/
|
|
160
|
+
approveToolCall(input) {
|
|
161
|
+
return this.request("POST", "/agent/tool-call/approve", input);
|
|
162
|
+
}
|
|
163
|
+
rejectToolCall(input) {
|
|
164
|
+
return this.request("POST", "/agent/tool-call/reject", input);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
168
|
+
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
169
|
+
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
170
|
+
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
171
|
+
*/
|
|
172
|
+
answerToolCall(input) {
|
|
173
|
+
return this.request("POST", "/agent/tool-call/answer", input);
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
177
|
+
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
178
|
+
* evidence the user chose them.
|
|
179
|
+
*/
|
|
180
|
+
skipToolCall(input) {
|
|
181
|
+
return this.request("POST", "/agent/tool-call/skip", input);
|
|
182
|
+
}
|
|
183
|
+
fetchImpl() {
|
|
184
|
+
return this.options.fetch ?? globalThis.fetch;
|
|
185
|
+
}
|
|
186
|
+
baseUrl() {
|
|
187
|
+
return (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
188
|
+
}
|
|
189
|
+
async resolveHeaders() {
|
|
190
|
+
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
191
|
+
return {
|
|
192
|
+
...this.options.headers,
|
|
193
|
+
...dynamic
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
credentials() {
|
|
197
|
+
return this.options.credentials !== void 0 ? {
|
|
198
|
+
credentials: this.options.credentials
|
|
199
|
+
} : {};
|
|
200
|
+
}
|
|
201
|
+
async request(method, path, body) {
|
|
202
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}${path}`, {
|
|
203
|
+
method,
|
|
204
|
+
headers: {
|
|
205
|
+
accept: "application/json",
|
|
206
|
+
...body !== void 0 ? {
|
|
207
|
+
"content-type": "application/json"
|
|
208
|
+
} : {},
|
|
209
|
+
...await this.resolveHeaders()
|
|
210
|
+
},
|
|
211
|
+
...body !== void 0 ? {
|
|
212
|
+
body: JSON.stringify(body)
|
|
213
|
+
} : {},
|
|
214
|
+
...this.credentials()
|
|
215
|
+
});
|
|
216
|
+
return this.handleResponse(response, method, path);
|
|
217
|
+
}
|
|
218
|
+
async handleResponse(response, method, path) {
|
|
219
|
+
if (!response.ok) {
|
|
220
|
+
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
221
|
+
}
|
|
222
|
+
if (response.status === 204) return void 0;
|
|
223
|
+
const text = await response.text();
|
|
224
|
+
if (!text) return void 0;
|
|
225
|
+
return JSON.parse(text);
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
function streamResponse(response) {
|
|
229
|
+
const body = response.body;
|
|
230
|
+
const runId = response.headers?.get(HEADER_RUN_ID) ?? void 0;
|
|
231
|
+
const threadId = response.headers?.get(HEADER_THREAD_ID) ?? void 0;
|
|
232
|
+
return {
|
|
233
|
+
body,
|
|
234
|
+
...runId ? {
|
|
235
|
+
runId
|
|
236
|
+
} : {},
|
|
237
|
+
...threadId ? {
|
|
238
|
+
threadId
|
|
239
|
+
} : {}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
__name(streamResponse, "streamResponse");
|
|
243
|
+
|
|
4
244
|
// src/reasoning/timing.ts
|
|
5
245
|
import { useEffect, useRef, useState } from "react";
|
|
6
246
|
var REASONING_METADATA_KEY = "agent";
|
|
@@ -61,8 +301,11 @@ function formatElapsed(ms) {
|
|
|
61
301
|
__name(formatElapsed, "formatElapsed");
|
|
62
302
|
|
|
63
303
|
// src/agent-chat-transport.ts
|
|
64
|
-
var
|
|
65
|
-
|
|
304
|
+
var DEFAULT_RECONNECT = {
|
|
305
|
+
maxAttempts: 6,
|
|
306
|
+
baseDelayMs: 500,
|
|
307
|
+
maxDelayMs: 8e3
|
|
308
|
+
};
|
|
66
309
|
var ASK_TOOL_NAME = "ask";
|
|
67
310
|
var AgentChatTransport = class {
|
|
68
311
|
static {
|
|
@@ -79,8 +322,26 @@ var AgentChatTransport = class {
|
|
|
79
322
|
* several entries under each id — which React renders as duplicate keys.
|
|
80
323
|
*/
|
|
81
324
|
attemptLive = false;
|
|
325
|
+
backend;
|
|
82
326
|
constructor(options = {}) {
|
|
83
327
|
this.options = options;
|
|
328
|
+
this.backend = options.backend ?? new AgentClient({
|
|
329
|
+
...options.baseUrl !== void 0 ? {
|
|
330
|
+
baseUrl: options.baseUrl
|
|
331
|
+
} : {},
|
|
332
|
+
...options.headers !== void 0 ? {
|
|
333
|
+
headers: options.headers
|
|
334
|
+
} : {},
|
|
335
|
+
...options.getHeaders !== void 0 ? {
|
|
336
|
+
getHeaders: options.getHeaders
|
|
337
|
+
} : {},
|
|
338
|
+
...options.credentials !== void 0 ? {
|
|
339
|
+
credentials: options.credentials
|
|
340
|
+
} : {},
|
|
341
|
+
...options.fetch !== void 0 ? {
|
|
342
|
+
fetch: options.fetch
|
|
343
|
+
} : {}
|
|
344
|
+
});
|
|
84
345
|
}
|
|
85
346
|
/** Run id of the most recent stream — HITL approve/reject target this. */
|
|
86
347
|
get runId() {
|
|
@@ -113,26 +374,25 @@ var AgentChatTransport = class {
|
|
|
113
374
|
...options.body ?? {},
|
|
114
375
|
message
|
|
115
376
|
};
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
377
|
+
const headers = headerRecord(options.headers);
|
|
378
|
+
const response = await this.backend.openChatStream({
|
|
379
|
+
body,
|
|
380
|
+
...headers !== void 0 ? {
|
|
381
|
+
headers
|
|
382
|
+
} : {},
|
|
383
|
+
...options.abortSignal ? {
|
|
384
|
+
signal: options.abortSignal
|
|
385
|
+
} : {}
|
|
386
|
+
});
|
|
387
|
+
this.captureHeaderMeta(response);
|
|
388
|
+
return this.toChunkStream(response, {
|
|
389
|
+
...headers !== void 0 ? {
|
|
390
|
+
headers
|
|
126
391
|
} : {},
|
|
127
392
|
...options.abortSignal ? {
|
|
128
393
|
signal: options.abortSignal
|
|
129
394
|
} : {}
|
|
130
395
|
});
|
|
131
|
-
if (!response.ok || !response.body) {
|
|
132
|
-
throw new Error(`Agent chat request failed: ${response.status} ${response.statusText}`);
|
|
133
|
-
}
|
|
134
|
-
this.captureHeaderMeta(response.headers);
|
|
135
|
-
return this.toChunkStream(response.body);
|
|
136
396
|
} catch (error) {
|
|
137
397
|
this.attemptLive = false;
|
|
138
398
|
throw error;
|
|
@@ -145,51 +405,41 @@ var AgentChatTransport = class {
|
|
|
145
405
|
if (runId === void 0) return null;
|
|
146
406
|
this.attemptLive = true;
|
|
147
407
|
try {
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
},
|
|
154
|
-
...this.options.credentials !== void 0 ? {
|
|
155
|
-
credentials: this.options.credentials
|
|
408
|
+
const headers = headerRecord(options.headers);
|
|
409
|
+
const response = await this.backend.resumeChatStream({
|
|
410
|
+
runId,
|
|
411
|
+
...headers !== void 0 ? {
|
|
412
|
+
headers
|
|
156
413
|
} : {}
|
|
157
414
|
});
|
|
158
|
-
if (response
|
|
415
|
+
if (response === null) {
|
|
159
416
|
this.attemptLive = false;
|
|
160
417
|
return null;
|
|
161
418
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
419
|
+
this.captureHeaderMeta(response);
|
|
420
|
+
return this.toChunkStream(response, {
|
|
421
|
+
runId,
|
|
422
|
+
...headers !== void 0 ? {
|
|
423
|
+
headers
|
|
424
|
+
} : {}
|
|
425
|
+
});
|
|
167
426
|
} catch (error) {
|
|
168
427
|
this.attemptLive = false;
|
|
169
428
|
throw error;
|
|
170
429
|
}
|
|
171
430
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
baseUrl() {
|
|
176
|
-
return (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
431
|
+
captureHeaderMeta(response) {
|
|
432
|
+
if (response.runId) this.currentRunId = response.runId;
|
|
433
|
+
if (response.threadId) this.currentThreadId = response.threadId;
|
|
177
434
|
}
|
|
178
|
-
|
|
179
|
-
const
|
|
180
|
-
|
|
435
|
+
reconnectPolicy() {
|
|
436
|
+
const configured = this.options.reconnect;
|
|
437
|
+
if (configured === false) return null;
|
|
181
438
|
return {
|
|
182
|
-
...
|
|
183
|
-
...
|
|
184
|
-
...extra
|
|
439
|
+
...DEFAULT_RECONNECT,
|
|
440
|
+
...configured
|
|
185
441
|
};
|
|
186
442
|
}
|
|
187
|
-
captureHeaderMeta(headers) {
|
|
188
|
-
const runId = headers.get(HEADER_RUN_ID);
|
|
189
|
-
const threadId = headers.get(HEADER_THREAD_ID);
|
|
190
|
-
if (runId) this.currentRunId = runId;
|
|
191
|
-
if (threadId) this.currentThreadId = threadId;
|
|
192
|
-
}
|
|
193
443
|
recordMeta(meta) {
|
|
194
444
|
this.currentRunId = meta.runId;
|
|
195
445
|
this.currentThreadId = meta.threadId;
|
|
@@ -208,10 +458,66 @@ var AgentChatTransport = class {
|
|
|
208
458
|
* call plus its tool execution; text and reasoning open lazily and close at the step boundary, so
|
|
209
459
|
* tool-call cards (input streaming → output) render live between the prose.
|
|
210
460
|
*/
|
|
211
|
-
toChunkStream(source) {
|
|
212
|
-
|
|
213
|
-
|
|
461
|
+
toChunkStream(source, context) {
|
|
462
|
+
let reader = source.body.getReader();
|
|
463
|
+
let decoder = new TextDecoder();
|
|
214
464
|
let buffer = "";
|
|
465
|
+
let streamRunId = source.runId ?? context.runId;
|
|
466
|
+
let lastSeq;
|
|
467
|
+
let cancelled = false;
|
|
468
|
+
const backend = this.backend;
|
|
469
|
+
const policy = this.reconnectPolicy();
|
|
470
|
+
const notify = /* @__PURE__ */ __name((state) => this.options.onConnectionChange?.(state), "notify");
|
|
471
|
+
const aborted = /* @__PURE__ */ __name(() => cancelled || context.signal?.aborted === true, "aborted");
|
|
472
|
+
const canResume = /* @__PURE__ */ __name(() => policy !== null && lastSeq !== void 0 && streamRunId !== void 0 && !aborted(), "canResume");
|
|
473
|
+
const resume = /* @__PURE__ */ __name(async () => {
|
|
474
|
+
const runId = streamRunId;
|
|
475
|
+
const after = lastSeq ?? 0;
|
|
476
|
+
const { maxAttempts, baseDelayMs, maxDelayMs } = policy;
|
|
477
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
478
|
+
notify({
|
|
479
|
+
status: "reconnecting",
|
|
480
|
+
runId,
|
|
481
|
+
attempt,
|
|
482
|
+
after
|
|
483
|
+
});
|
|
484
|
+
await delay(Math.min(maxDelayMs, baseDelayMs * 2 ** (attempt - 1)), context.signal);
|
|
485
|
+
if (aborted()) return "aborted";
|
|
486
|
+
try {
|
|
487
|
+
const next = await backend.resumeChatStream({
|
|
488
|
+
runId,
|
|
489
|
+
after,
|
|
490
|
+
...context.headers !== void 0 ? {
|
|
491
|
+
headers: context.headers
|
|
492
|
+
} : {},
|
|
493
|
+
...context.signal !== void 0 ? {
|
|
494
|
+
signal: context.signal
|
|
495
|
+
} : {}
|
|
496
|
+
});
|
|
497
|
+
if (next === null) {
|
|
498
|
+
notify({
|
|
499
|
+
status: "gone",
|
|
500
|
+
runId
|
|
501
|
+
});
|
|
502
|
+
return "gone";
|
|
503
|
+
}
|
|
504
|
+
reader = next.body.getReader();
|
|
505
|
+
decoder = new TextDecoder();
|
|
506
|
+
buffer = "";
|
|
507
|
+
notify({
|
|
508
|
+
status: "live"
|
|
509
|
+
});
|
|
510
|
+
return "resumed";
|
|
511
|
+
} catch {
|
|
512
|
+
if (aborted()) return "aborted";
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
notify({
|
|
516
|
+
status: "failed",
|
|
517
|
+
runId
|
|
518
|
+
});
|
|
519
|
+
return "failed";
|
|
520
|
+
}, "resume");
|
|
215
521
|
let started = false;
|
|
216
522
|
let stepOpen = false;
|
|
217
523
|
let stepIndex = 0;
|
|
@@ -222,7 +528,10 @@ var AgentChatTransport = class {
|
|
|
222
528
|
let reasoningSeq = 0;
|
|
223
529
|
const announced = /* @__PURE__ */ new Set();
|
|
224
530
|
const toolMetadata = /* @__PURE__ */ new Map();
|
|
225
|
-
const record = /* @__PURE__ */ __name((meta) =>
|
|
531
|
+
const record = /* @__PURE__ */ __name((meta) => {
|
|
532
|
+
streamRunId = meta.runId;
|
|
533
|
+
this.recordMeta(meta);
|
|
534
|
+
}, "record");
|
|
226
535
|
const endAttempt = /* @__PURE__ */ __name(() => {
|
|
227
536
|
this.attemptLive = false;
|
|
228
537
|
}, "endAttempt");
|
|
@@ -231,7 +540,12 @@ var AgentChatTransport = class {
|
|
|
231
540
|
function ensureStarted() {
|
|
232
541
|
if (started) return;
|
|
233
542
|
started = true;
|
|
234
|
-
controller.enqueue({
|
|
543
|
+
controller.enqueue(streamRunId !== void 0 ? {
|
|
544
|
+
type: "start",
|
|
545
|
+
messageMetadata: {
|
|
546
|
+
runId: streamRunId
|
|
547
|
+
}
|
|
548
|
+
} : {
|
|
235
549
|
type: "start"
|
|
236
550
|
});
|
|
237
551
|
}
|
|
@@ -487,33 +801,67 @@ var AgentChatTransport = class {
|
|
|
487
801
|
endAttempt();
|
|
488
802
|
}
|
|
489
803
|
__name(finish, "finish");
|
|
804
|
+
function fail(errorText, start = true) {
|
|
805
|
+
if (start) ensureStarted();
|
|
806
|
+
controller.enqueue({
|
|
807
|
+
type: "error",
|
|
808
|
+
errorText
|
|
809
|
+
});
|
|
810
|
+
controller.close();
|
|
811
|
+
endAttempt();
|
|
812
|
+
}
|
|
813
|
+
__name(fail, "fail");
|
|
814
|
+
async function recover(error) {
|
|
815
|
+
if (!canResume()) {
|
|
816
|
+
if (error === void 0) {
|
|
817
|
+
finish();
|
|
818
|
+
} else {
|
|
819
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
820
|
+
}
|
|
821
|
+
return false;
|
|
822
|
+
}
|
|
823
|
+
const outcome = await resume();
|
|
824
|
+
if (outcome === "resumed") return true;
|
|
825
|
+
if (outcome === "failed") {
|
|
826
|
+
fail("Lost the connection to the agent stream");
|
|
827
|
+
} else {
|
|
828
|
+
finish();
|
|
829
|
+
}
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
__name(recover, "recover");
|
|
490
833
|
try {
|
|
491
834
|
while (true) {
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
835
|
+
let result;
|
|
836
|
+
try {
|
|
837
|
+
result = await reader.read();
|
|
838
|
+
} catch (error) {
|
|
839
|
+
if (await recover(error)) continue;
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
if (result.done) {
|
|
843
|
+
if (await recover(void 0)) continue;
|
|
495
844
|
return;
|
|
496
845
|
}
|
|
497
|
-
buffer += decoder.decode(value, {
|
|
846
|
+
buffer += decoder.decode(result.value, {
|
|
498
847
|
stream: true
|
|
499
848
|
});
|
|
500
849
|
let separator = buffer.indexOf("\n\n");
|
|
501
850
|
while (separator !== -1) {
|
|
502
851
|
const rawEvent = buffer.slice(0, separator);
|
|
503
852
|
buffer = buffer.slice(separator + 2);
|
|
853
|
+
separator = buffer.indexOf("\n\n");
|
|
504
854
|
const frame = parseSseFrame(rawEvent);
|
|
855
|
+
if (frame.id !== void 0) {
|
|
856
|
+
if (lastSeq !== void 0 && frame.id <= lastSeq) continue;
|
|
857
|
+
lastSeq = frame.id;
|
|
858
|
+
}
|
|
505
859
|
if (frame.event === "done") {
|
|
506
860
|
finish();
|
|
507
861
|
return;
|
|
508
862
|
}
|
|
509
863
|
if (frame.event === "error") {
|
|
510
|
-
|
|
511
|
-
controller.enqueue({
|
|
512
|
-
type: "error",
|
|
513
|
-
errorText: parseErrorText(frame.data)
|
|
514
|
-
});
|
|
515
|
-
controller.close();
|
|
516
|
-
endAttempt();
|
|
864
|
+
fail(parseErrorText(frame.data));
|
|
517
865
|
return;
|
|
518
866
|
}
|
|
519
867
|
if (frame.event === "meta") {
|
|
@@ -523,19 +871,14 @@ var AgentChatTransport = class {
|
|
|
523
871
|
const event = parseEvent(frame.data);
|
|
524
872
|
if (event) emit(event);
|
|
525
873
|
}
|
|
526
|
-
separator = buffer.indexOf("\n\n");
|
|
527
874
|
}
|
|
528
875
|
}
|
|
529
876
|
} catch (error) {
|
|
530
|
-
|
|
531
|
-
type: "error",
|
|
532
|
-
errorText: error instanceof Error ? error.message : "Agent stream error"
|
|
533
|
-
});
|
|
534
|
-
controller.close();
|
|
535
|
-
endAttempt();
|
|
877
|
+
fail(error instanceof Error ? error.message : "Agent stream error", false);
|
|
536
878
|
}
|
|
537
879
|
},
|
|
538
880
|
cancel() {
|
|
881
|
+
cancelled = true;
|
|
539
882
|
void reader.cancel();
|
|
540
883
|
endAttempt();
|
|
541
884
|
}
|
|
@@ -544,17 +887,22 @@ var AgentChatTransport = class {
|
|
|
544
887
|
};
|
|
545
888
|
function parseSseFrame(raw) {
|
|
546
889
|
let event;
|
|
890
|
+
let id;
|
|
547
891
|
const dataLines = [];
|
|
548
892
|
for (const line of raw.split("\n")) {
|
|
549
893
|
if (line.startsWith("event:")) {
|
|
550
894
|
event = line.slice("event:".length).trim();
|
|
895
|
+
} else if (line.startsWith("id:")) {
|
|
896
|
+
const parsed = Number(line.slice("id:".length).trim());
|
|
897
|
+
if (Number.isSafeInteger(parsed) && parsed > 0) id = parsed;
|
|
551
898
|
} else if (line.startsWith("data:")) {
|
|
552
899
|
dataLines.push(line.slice("data:".length).trimStart());
|
|
553
900
|
}
|
|
554
901
|
}
|
|
555
902
|
return {
|
|
556
903
|
event,
|
|
557
|
-
data: dataLines.length > 0 ? dataLines.join("\n") : void 0
|
|
904
|
+
data: dataLines.length > 0 ? dataLines.join("\n") : void 0,
|
|
905
|
+
id
|
|
558
906
|
};
|
|
559
907
|
}
|
|
560
908
|
__name(parseSseFrame, "parseSseFrame");
|
|
@@ -584,26 +932,293 @@ function parseErrorText(data) {
|
|
|
584
932
|
}
|
|
585
933
|
return "Agent run failed";
|
|
586
934
|
}
|
|
587
|
-
__name(parseErrorText, "parseErrorText");
|
|
588
|
-
function parseEvent(data) {
|
|
589
|
-
try {
|
|
590
|
-
const parsed = JSON.parse(data);
|
|
591
|
-
if (parsed !== null && typeof parsed === "object" && "kind" in parsed) {
|
|
592
|
-
return parsed;
|
|
593
|
-
}
|
|
594
|
-
} catch {
|
|
935
|
+
__name(parseErrorText, "parseErrorText");
|
|
936
|
+
function parseEvent(data) {
|
|
937
|
+
try {
|
|
938
|
+
const parsed = JSON.parse(data);
|
|
939
|
+
if (parsed !== null && typeof parsed === "object" && "kind" in parsed) {
|
|
940
|
+
return parsed;
|
|
941
|
+
}
|
|
942
|
+
} catch {
|
|
943
|
+
}
|
|
944
|
+
return null;
|
|
945
|
+
}
|
|
946
|
+
__name(parseEvent, "parseEvent");
|
|
947
|
+
function extractText(message) {
|
|
948
|
+
let text = "";
|
|
949
|
+
for (const part of message.parts ?? []) {
|
|
950
|
+
if (part.type === "text") text += part.text;
|
|
951
|
+
}
|
|
952
|
+
return text;
|
|
953
|
+
}
|
|
954
|
+
__name(extractText, "extractText");
|
|
955
|
+
function headerRecord(headers) {
|
|
956
|
+
if (headers === void 0) return void 0;
|
|
957
|
+
return headers instanceof Headers ? Object.fromEntries(headers.entries()) : headers;
|
|
958
|
+
}
|
|
959
|
+
__name(headerRecord, "headerRecord");
|
|
960
|
+
function delay(ms, signal) {
|
|
961
|
+
if (ms <= 0 || signal?.aborted) return Promise.resolve();
|
|
962
|
+
return new Promise((resolve) => {
|
|
963
|
+
const timer = setTimeout(done, ms);
|
|
964
|
+
function done() {
|
|
965
|
+
clearTimeout(timer);
|
|
966
|
+
signal?.removeEventListener("abort", done);
|
|
967
|
+
resolve();
|
|
968
|
+
}
|
|
969
|
+
__name(done, "done");
|
|
970
|
+
signal?.addEventListener("abort", done, {
|
|
971
|
+
once: true
|
|
972
|
+
});
|
|
973
|
+
});
|
|
974
|
+
}
|
|
975
|
+
__name(delay, "delay");
|
|
976
|
+
|
|
977
|
+
// src/backend.ts
|
|
978
|
+
var AgentBackendUnsupportedError = class extends Error {
|
|
979
|
+
static {
|
|
980
|
+
__name(this, "AgentBackendUnsupportedError");
|
|
981
|
+
}
|
|
982
|
+
method;
|
|
983
|
+
constructor(method) {
|
|
984
|
+
super(`The agent backend does not implement ${method}()`), this.method = method;
|
|
985
|
+
this.name = "AgentBackendUnsupportedError";
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
function requireBackendMethod(backend, name) {
|
|
989
|
+
const method = backend[name];
|
|
990
|
+
if (typeof method !== "function") {
|
|
991
|
+
throw new AgentBackendUnsupportedError(name);
|
|
992
|
+
}
|
|
993
|
+
return method.bind(backend);
|
|
994
|
+
}
|
|
995
|
+
__name(requireBackendMethod, "requireBackendMethod");
|
|
996
|
+
|
|
997
|
+
// src/feedback/use-message-feedback.ts
|
|
998
|
+
import { useCallback, useRef as useRef2, useState as useState2 } from "react";
|
|
999
|
+
function useMessageFeedback(options) {
|
|
1000
|
+
const latest = useRef2(options);
|
|
1001
|
+
latest.current = options;
|
|
1002
|
+
const [local, setLocal] = useState2(/* @__PURE__ */ new Map());
|
|
1003
|
+
const [pending, setPending] = useState2(/* @__PURE__ */ new Set());
|
|
1004
|
+
const [error, setError] = useState2(null);
|
|
1005
|
+
const localRef = useRef2(local);
|
|
1006
|
+
localRef.current = local;
|
|
1007
|
+
const resolved = useRef2(/* @__PURE__ */ new Map());
|
|
1008
|
+
const feedbackOf = useCallback((message) => {
|
|
1009
|
+
const mine = local.get(message.id);
|
|
1010
|
+
if (mine !== void 0) return mine.feedback;
|
|
1011
|
+
return metadataOf(message).feedback ?? null;
|
|
1012
|
+
}, [
|
|
1013
|
+
local
|
|
1014
|
+
]);
|
|
1015
|
+
const isPending = useCallback((message) => pending.has(message.id), [
|
|
1016
|
+
pending
|
|
1017
|
+
]);
|
|
1018
|
+
const storedIdFor = useCallback(async (message) => {
|
|
1019
|
+
const runId = metadataOf(message).runId;
|
|
1020
|
+
if (runId === void 0) return message.id;
|
|
1021
|
+
const known = resolved.current.get(message.id);
|
|
1022
|
+
if (known !== void 0) return known;
|
|
1023
|
+
const { backend, threadId } = latest.current;
|
|
1024
|
+
const thread = typeof threadId === "function" ? threadId() : threadId;
|
|
1025
|
+
if (thread === void 0) {
|
|
1026
|
+
throw new Error("useMessageFeedback: a streamed message needs `threadId` to be rated");
|
|
1027
|
+
}
|
|
1028
|
+
const detail = await backend.getThread(thread);
|
|
1029
|
+
const row = [
|
|
1030
|
+
...detail.messages
|
|
1031
|
+
].reverse().find((candidate) => candidate.role === "assistant" && candidate.runId === runId);
|
|
1032
|
+
if (row === void 0) {
|
|
1033
|
+
throw new Error(`useMessageFeedback: run ${runId} has no persisted answer yet`);
|
|
1034
|
+
}
|
|
1035
|
+
resolved.current.set(message.id, row.id);
|
|
1036
|
+
return row.id;
|
|
1037
|
+
}, []);
|
|
1038
|
+
const rate = useCallback(async (message, value, comment) => {
|
|
1039
|
+
const setFeedback = requireBackendMethod(latest.current.backend, "setMessageFeedback");
|
|
1040
|
+
const had = localRef.current.get(message.id);
|
|
1041
|
+
const optimistic = value === null ? null : {
|
|
1042
|
+
value,
|
|
1043
|
+
...comment !== void 0 && comment.trim().length > 0 ? {
|
|
1044
|
+
comment: comment.trim()
|
|
1045
|
+
} : {},
|
|
1046
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1047
|
+
};
|
|
1048
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1049
|
+
feedback: optimistic
|
|
1050
|
+
}));
|
|
1051
|
+
setPending((current) => new Set(current).add(message.id));
|
|
1052
|
+
try {
|
|
1053
|
+
const storedId = await storedIdFor(message);
|
|
1054
|
+
const { feedback } = await setFeedback(storedId, {
|
|
1055
|
+
value,
|
|
1056
|
+
...comment !== void 0 ? {
|
|
1057
|
+
comment
|
|
1058
|
+
} : {}
|
|
1059
|
+
});
|
|
1060
|
+
setLocal((current) => new Map(current).set(message.id, {
|
|
1061
|
+
feedback
|
|
1062
|
+
}));
|
|
1063
|
+
setError(null);
|
|
1064
|
+
} catch (caught) {
|
|
1065
|
+
setLocal((current) => {
|
|
1066
|
+
const next = new Map(current);
|
|
1067
|
+
if (had === void 0) next.delete(message.id);
|
|
1068
|
+
else next.set(message.id, had);
|
|
1069
|
+
return next;
|
|
1070
|
+
});
|
|
1071
|
+
const failure = caught instanceof Error ? caught : new Error(String(caught));
|
|
1072
|
+
setError(failure);
|
|
1073
|
+
throw failure;
|
|
1074
|
+
} finally {
|
|
1075
|
+
setPending((current) => {
|
|
1076
|
+
const next = new Set(current);
|
|
1077
|
+
next.delete(message.id);
|
|
1078
|
+
return next;
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
}, [
|
|
1082
|
+
storedIdFor
|
|
1083
|
+
]);
|
|
1084
|
+
const toggle = useCallback((message, value) => {
|
|
1085
|
+
const mine = localRef.current.get(message.id);
|
|
1086
|
+
const current = mine !== void 0 ? mine.feedback : metadataOf(message).feedback ?? null;
|
|
1087
|
+
return rate(message, current?.value === value ? null : value);
|
|
1088
|
+
}, [
|
|
1089
|
+
rate
|
|
1090
|
+
]);
|
|
1091
|
+
return {
|
|
1092
|
+
feedbackOf,
|
|
1093
|
+
isPending,
|
|
1094
|
+
rate,
|
|
1095
|
+
toggle,
|
|
1096
|
+
error
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
__name(useMessageFeedback, "useMessageFeedback");
|
|
1100
|
+
function metadataOf(message) {
|
|
1101
|
+
const metadata = message.metadata;
|
|
1102
|
+
return metadata !== null && typeof metadata === "object" ? metadata : {};
|
|
1103
|
+
}
|
|
1104
|
+
__name(metadataOf, "metadataOf");
|
|
1105
|
+
|
|
1106
|
+
// src/threads/threads-events.ts
|
|
1107
|
+
var listeners = /* @__PURE__ */ new WeakMap();
|
|
1108
|
+
function notifyThreads(backend, event) {
|
|
1109
|
+
for (const listener of listeners.get(backend) ?? []) {
|
|
1110
|
+
listener(event);
|
|
595
1111
|
}
|
|
596
|
-
return null;
|
|
597
1112
|
}
|
|
598
|
-
__name(
|
|
599
|
-
function
|
|
600
|
-
let
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
1113
|
+
__name(notifyThreads, "notifyThreads");
|
|
1114
|
+
function onThreadsEvent(backend, listener) {
|
|
1115
|
+
let set = listeners.get(backend);
|
|
1116
|
+
if (set === void 0) {
|
|
1117
|
+
set = /* @__PURE__ */ new Set();
|
|
1118
|
+
listeners.set(backend, set);
|
|
1119
|
+
}
|
|
1120
|
+
set.add(listener);
|
|
1121
|
+
return () => {
|
|
1122
|
+
set.delete(listener);
|
|
1123
|
+
};
|
|
605
1124
|
}
|
|
606
|
-
__name(
|
|
1125
|
+
__name(onThreadsEvent, "onThreadsEvent");
|
|
1126
|
+
|
|
1127
|
+
// src/threads/use-threads.ts
|
|
1128
|
+
import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef3, useState as useState3 } from "react";
|
|
1129
|
+
function useThreads(options) {
|
|
1130
|
+
const { backend, enabled = true } = options;
|
|
1131
|
+
const [threads, setThreads] = useState3([]);
|
|
1132
|
+
const [isLoading, setIsLoading] = useState3(enabled);
|
|
1133
|
+
const [error, setError] = useState3(null);
|
|
1134
|
+
const threadsRef = useRef3(threads);
|
|
1135
|
+
threadsRef.current = threads;
|
|
1136
|
+
const generation = useRef3(0);
|
|
1137
|
+
const refresh = useCallback2(async () => {
|
|
1138
|
+
const mine = ++generation.current;
|
|
1139
|
+
setIsLoading(true);
|
|
1140
|
+
try {
|
|
1141
|
+
const list = await backend.listThreads();
|
|
1142
|
+
if (mine !== generation.current) return;
|
|
1143
|
+
setThreads(list);
|
|
1144
|
+
setError(null);
|
|
1145
|
+
} catch (caught) {
|
|
1146
|
+
if (mine !== generation.current) return;
|
|
1147
|
+
setError(caught instanceof Error ? caught : new Error(String(caught)));
|
|
1148
|
+
} finally {
|
|
1149
|
+
if (mine === generation.current) setIsLoading(false);
|
|
1150
|
+
}
|
|
1151
|
+
}, [
|
|
1152
|
+
backend
|
|
1153
|
+
]);
|
|
1154
|
+
useEffect2(() => {
|
|
1155
|
+
if (!enabled) return;
|
|
1156
|
+
void refresh();
|
|
1157
|
+
return onThreadsEvent(backend, (event) => {
|
|
1158
|
+
if (event.type === "title") {
|
|
1159
|
+
setThreads((current) => current.map((thread) => thread.id === event.threadId ? {
|
|
1160
|
+
...thread,
|
|
1161
|
+
title: event.title
|
|
1162
|
+
} : thread));
|
|
1163
|
+
} else if (event.type === "removed") {
|
|
1164
|
+
setThreads((current) => current.filter((thread) => thread.id !== event.threadId));
|
|
1165
|
+
} else {
|
|
1166
|
+
void refresh();
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
}, [
|
|
1170
|
+
backend,
|
|
1171
|
+
enabled,
|
|
1172
|
+
refresh
|
|
1173
|
+
]);
|
|
1174
|
+
const rename = useCallback2(async (id, title) => {
|
|
1175
|
+
const before = threadsRef.current;
|
|
1176
|
+
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
1177
|
+
...thread,
|
|
1178
|
+
title
|
|
1179
|
+
} : thread));
|
|
1180
|
+
try {
|
|
1181
|
+
await backend.updateThread(id, {
|
|
1182
|
+
title
|
|
1183
|
+
});
|
|
1184
|
+
notifyThreads(backend, {
|
|
1185
|
+
type: "title",
|
|
1186
|
+
threadId: id,
|
|
1187
|
+
title
|
|
1188
|
+
});
|
|
1189
|
+
} catch (caught) {
|
|
1190
|
+
setThreads(before);
|
|
1191
|
+
throw caught;
|
|
1192
|
+
}
|
|
1193
|
+
}, [
|
|
1194
|
+
backend
|
|
1195
|
+
]);
|
|
1196
|
+
const remove = useCallback2(async (id) => {
|
|
1197
|
+
const before = threadsRef.current;
|
|
1198
|
+
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
1199
|
+
try {
|
|
1200
|
+
await backend.deleteThread(id);
|
|
1201
|
+
notifyThreads(backend, {
|
|
1202
|
+
type: "removed",
|
|
1203
|
+
threadId: id
|
|
1204
|
+
});
|
|
1205
|
+
} catch (caught) {
|
|
1206
|
+
setThreads(before);
|
|
1207
|
+
throw caught;
|
|
1208
|
+
}
|
|
1209
|
+
}, [
|
|
1210
|
+
backend
|
|
1211
|
+
]);
|
|
1212
|
+
return {
|
|
1213
|
+
threads,
|
|
1214
|
+
isLoading,
|
|
1215
|
+
error,
|
|
1216
|
+
refresh,
|
|
1217
|
+
rename,
|
|
1218
|
+
remove
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
__name(useThreads, "useThreads");
|
|
607
1222
|
|
|
608
1223
|
// src/background-runs.ts
|
|
609
1224
|
function asReceipt(output) {
|
|
@@ -652,170 +1267,11 @@ function backgroundRunsFromThread(messages) {
|
|
|
652
1267
|
}
|
|
653
1268
|
__name(backgroundRunsFromThread, "backgroundRunsFromThread");
|
|
654
1269
|
|
|
655
|
-
// src/client.ts
|
|
656
|
-
var AgentHttpError = class extends Error {
|
|
657
|
-
static {
|
|
658
|
-
__name(this, "AgentHttpError");
|
|
659
|
-
}
|
|
660
|
-
status;
|
|
661
|
-
method;
|
|
662
|
-
path;
|
|
663
|
-
constructor(status, method, path, statusText) {
|
|
664
|
-
super(`Agent request failed: ${method} ${path} \u2192 ${status} ${statusText}`), this.status = status, this.method = method, this.path = path;
|
|
665
|
-
this.name = "AgentHttpError";
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
var AgentClient = class {
|
|
669
|
-
static {
|
|
670
|
-
__name(this, "AgentClient");
|
|
671
|
-
}
|
|
672
|
-
options;
|
|
673
|
-
constructor(options = {}) {
|
|
674
|
-
this.options = options;
|
|
675
|
-
}
|
|
676
|
-
listThreads() {
|
|
677
|
-
return this.request("GET", "/agent/threads");
|
|
678
|
-
}
|
|
679
|
-
/**
|
|
680
|
-
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
681
|
-
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
682
|
-
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
683
|
-
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
684
|
-
*/
|
|
685
|
-
listSkills(threadId) {
|
|
686
|
-
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
687
|
-
return this.request("GET", `/agent/skills${query}`);
|
|
688
|
-
}
|
|
689
|
-
/**
|
|
690
|
-
* The tools this caller can reach through `agent` (the default agent when omitted), each with the
|
|
691
|
-
* server-declared `presentation` a chat narrates it by — the same list the model is offered.
|
|
692
|
-
* Prefer {@link useToolCatalog}, which fetches it once and shares it.
|
|
693
|
-
*/
|
|
694
|
-
listTools(agent) {
|
|
695
|
-
const query = agent === void 0 ? "" : `?agent=${encodeURIComponent(agent)}`;
|
|
696
|
-
return this.request("GET", `/agent/tools${query}`);
|
|
697
|
-
}
|
|
698
|
-
getThread(id) {
|
|
699
|
-
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
700
|
-
}
|
|
701
|
-
deleteThread(id) {
|
|
702
|
-
return this.request("DELETE", `/agent/threads/${encodeURIComponent(id)}`);
|
|
703
|
-
}
|
|
704
|
-
forkFromMessage(threadId, messageId) {
|
|
705
|
-
return this.request("POST", `/agent/threads/${encodeURIComponent(threadId)}/fork-from/${encodeURIComponent(messageId)}`);
|
|
706
|
-
}
|
|
707
|
-
renameThread(id, title) {
|
|
708
|
-
return this.updateThread(id, {
|
|
709
|
-
title
|
|
710
|
-
});
|
|
711
|
-
}
|
|
712
|
-
/** General `PATCH /agent/threads/:threadId` — title and/or the thread's pinned default agent. */
|
|
713
|
-
updateThread(id, patch) {
|
|
714
|
-
return this.request("PATCH", `/agent/threads/${encodeURIComponent(id)}`, patch);
|
|
715
|
-
}
|
|
716
|
-
/**
|
|
717
|
-
* Uploads a file (image/PDF) for a vision-capable model turn. Multipart, field name `file` —
|
|
718
|
-
* mirrors the backend's `POST /agent/attachments`. The returned {@link MessageAttachment} is
|
|
719
|
-
* what a caller then rides on `sendMessage({ text }, { body: { attachments: [...] } })`.
|
|
720
|
-
*/
|
|
721
|
-
async uploadAttachment(file) {
|
|
722
|
-
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
723
|
-
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
724
|
-
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
725
|
-
const formData = new FormData();
|
|
726
|
-
formData.append("file", file);
|
|
727
|
-
const response = await fetchImpl(`${baseUrl}/agent/attachments`, {
|
|
728
|
-
method: "POST",
|
|
729
|
-
headers: {
|
|
730
|
-
accept: "application/json",
|
|
731
|
-
...this.options.headers,
|
|
732
|
-
...dynamic
|
|
733
|
-
},
|
|
734
|
-
body: formData,
|
|
735
|
-
...this.options.credentials !== void 0 ? {
|
|
736
|
-
credentials: this.options.credentials
|
|
737
|
-
} : {}
|
|
738
|
-
});
|
|
739
|
-
return this.handleResponse(response, "POST", "/agent/attachments");
|
|
740
|
-
}
|
|
741
|
-
promoteThread(id) {
|
|
742
|
-
return this.request("POST", `/agent/threads/${encodeURIComponent(id)}/promote`);
|
|
743
|
-
}
|
|
744
|
-
truncateFromMessage(threadId, messageId) {
|
|
745
|
-
return this.request("DELETE", `/agent/threads/${encodeURIComponent(threadId)}/from/${encodeURIComponent(messageId)}`);
|
|
746
|
-
}
|
|
747
|
-
getQuotaToday() {
|
|
748
|
-
return this.request("GET", "/agent/quota/today");
|
|
749
|
-
}
|
|
750
|
-
cancelStream(runId) {
|
|
751
|
-
return this.request("POST", `/agent/chat/${encodeURIComponent(runId)}/cancel`);
|
|
752
|
-
}
|
|
753
|
-
/**
|
|
754
|
-
* `remember` approves later calls of the same tool in the same thread; `via` names the surface
|
|
755
|
-
* the decision came through (the server records `'web'` when omitted).
|
|
756
|
-
*/
|
|
757
|
-
approveToolCall(input) {
|
|
758
|
-
return this.request("POST", "/agent/tool-call/approve", input);
|
|
759
|
-
}
|
|
760
|
-
rejectToolCall(input) {
|
|
761
|
-
return this.request("POST", "/agent/tool-call/reject", input);
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
765
|
-
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
766
|
-
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
767
|
-
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
768
|
-
*/
|
|
769
|
-
answerToolCall(input) {
|
|
770
|
-
return this.request("POST", "/agent/tool-call/answer", input);
|
|
771
|
-
}
|
|
772
|
-
/**
|
|
773
|
-
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
774
|
-
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
775
|
-
* evidence the user chose them.
|
|
776
|
-
*/
|
|
777
|
-
skipToolCall(input) {
|
|
778
|
-
return this.request("POST", "/agent/tool-call/skip", input);
|
|
779
|
-
}
|
|
780
|
-
async request(method, path, body) {
|
|
781
|
-
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
782
|
-
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
783
|
-
const dynamic = await this.options.getHeaders?.() ?? {};
|
|
784
|
-
const response = await fetchImpl(`${baseUrl}${path}`, {
|
|
785
|
-
method,
|
|
786
|
-
headers: {
|
|
787
|
-
accept: "application/json",
|
|
788
|
-
...body !== void 0 ? {
|
|
789
|
-
"content-type": "application/json"
|
|
790
|
-
} : {},
|
|
791
|
-
...this.options.headers,
|
|
792
|
-
...dynamic
|
|
793
|
-
},
|
|
794
|
-
...body !== void 0 ? {
|
|
795
|
-
body: JSON.stringify(body)
|
|
796
|
-
} : {},
|
|
797
|
-
...this.options.credentials !== void 0 ? {
|
|
798
|
-
credentials: this.options.credentials
|
|
799
|
-
} : {}
|
|
800
|
-
});
|
|
801
|
-
return this.handleResponse(response, method, path);
|
|
802
|
-
}
|
|
803
|
-
async handleResponse(response, method, path) {
|
|
804
|
-
if (!response.ok) {
|
|
805
|
-
throw new AgentHttpError(response.status, method, path, response.statusText);
|
|
806
|
-
}
|
|
807
|
-
if (response.status === 204) return void 0;
|
|
808
|
-
const text = await response.text();
|
|
809
|
-
if (!text) return void 0;
|
|
810
|
-
return JSON.parse(text);
|
|
811
|
-
}
|
|
812
|
-
};
|
|
813
|
-
|
|
814
1270
|
// src/components/chat-input.tsx
|
|
815
|
-
import React2, { useEffect as
|
|
1271
|
+
import React2, { useEffect as useEffect4, useState as useState5 } from "react";
|
|
816
1272
|
|
|
817
1273
|
// src/components/use-speech-recognition.ts
|
|
818
|
-
import { useCallback, useEffect as
|
|
1274
|
+
import { useCallback as useCallback3, useEffect as useEffect3, useMemo, useRef as useRef4, useState as useState4 } from "react";
|
|
819
1275
|
function getRecognitionCtor() {
|
|
820
1276
|
if (typeof window === "undefined") return null;
|
|
821
1277
|
const scope = window;
|
|
@@ -823,15 +1279,15 @@ function getRecognitionCtor() {
|
|
|
823
1279
|
}
|
|
824
1280
|
__name(getRecognitionCtor, "getRecognitionCtor");
|
|
825
1281
|
function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
|
|
826
|
-
const recognitionRef =
|
|
827
|
-
const [isListening, setIsListening] =
|
|
1282
|
+
const recognitionRef = useRef4(null);
|
|
1283
|
+
const [isListening, setIsListening] = useState4(false);
|
|
828
1284
|
const ctor = useMemo(() => getRecognitionCtor(), []);
|
|
829
1285
|
const isSupported = ctor !== null;
|
|
830
|
-
const finalRef =
|
|
831
|
-
const interimRef =
|
|
1286
|
+
const finalRef = useRef4(onFinalTranscript);
|
|
1287
|
+
const interimRef = useRef4(onInterimTranscript);
|
|
832
1288
|
finalRef.current = onFinalTranscript;
|
|
833
1289
|
interimRef.current = onInterimTranscript;
|
|
834
|
-
|
|
1290
|
+
useEffect3(() => {
|
|
835
1291
|
if (!ctor) return;
|
|
836
1292
|
const recognition = new ctor();
|
|
837
1293
|
recognition.lang = lang;
|
|
@@ -870,7 +1326,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
870
1326
|
ctor,
|
|
871
1327
|
lang
|
|
872
1328
|
]);
|
|
873
|
-
const start =
|
|
1329
|
+
const start = useCallback3(() => {
|
|
874
1330
|
const recognition = recognitionRef.current;
|
|
875
1331
|
if (!recognition) return;
|
|
876
1332
|
try {
|
|
@@ -880,7 +1336,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
880
1336
|
console.warn("SpeechRecognition start failed", error);
|
|
881
1337
|
}
|
|
882
1338
|
}, []);
|
|
883
|
-
const stop =
|
|
1339
|
+
const stop = useCallback3(() => {
|
|
884
1340
|
const recognition = recognitionRef.current;
|
|
885
1341
|
if (!recognition) return;
|
|
886
1342
|
try {
|
|
@@ -899,8 +1355,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
|
|
|
899
1355
|
|
|
900
1356
|
// src/components/chat-input.tsx
|
|
901
1357
|
function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = "en-US", sendLabel = "Send", onStop, isStreaming = false, stopLabel = "Stop generating", className, textareaClassName, sendButtonClassName, micButtonClassName, stopButtonClassName, renderMic }) {
|
|
902
|
-
const [value, setValue] =
|
|
903
|
-
const [interim, setInterim] =
|
|
1358
|
+
const [value, setValue] = useState5("");
|
|
1359
|
+
const [interim, setInterim] = useState5("");
|
|
904
1360
|
const speech = useSpeechRecognition({
|
|
905
1361
|
lang: speechLang,
|
|
906
1362
|
onFinalTranscript: /* @__PURE__ */ __name((text) => {
|
|
@@ -911,7 +1367,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
911
1367
|
}, "onFinalTranscript"),
|
|
912
1368
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
913
1369
|
});
|
|
914
|
-
|
|
1370
|
+
useEffect4(() => {
|
|
915
1371
|
if (!speech.isListening) setInterim("");
|
|
916
1372
|
}, [
|
|
917
1373
|
speech.isListening
|
|
@@ -978,7 +1434,7 @@ __name(ChatInput, "ChatInput");
|
|
|
978
1434
|
import React3 from "react";
|
|
979
1435
|
|
|
980
1436
|
// src/transcript/use-chat-transcript.ts
|
|
981
|
-
import { useCallback as
|
|
1437
|
+
import { useCallback as useCallback5, useEffect as useEffect6, useRef as useRef6, useState as useState7 } from "react";
|
|
982
1438
|
|
|
983
1439
|
// src/transcript/model.ts
|
|
984
1440
|
import { readElicitationInput, validateElicitationAnswer as validateElicitationAnswer2 } from "@dudousxd/nestjs-agent-core";
|
|
@@ -1955,11 +2411,11 @@ function formatCostUsd(cost) {
|
|
|
1955
2411
|
__name(formatCostUsd, "formatCostUsd");
|
|
1956
2412
|
|
|
1957
2413
|
// src/transcript/use-stick-to-bottom.ts
|
|
1958
|
-
import { useCallback as
|
|
2414
|
+
import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef5, useState as useState6 } from "react";
|
|
1959
2415
|
function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
1960
|
-
const containerRef =
|
|
1961
|
-
const [isAtBottom, setIsAtBottom] =
|
|
1962
|
-
const measure =
|
|
2416
|
+
const containerRef = useRef5(null);
|
|
2417
|
+
const [isAtBottom, setIsAtBottom] = useState6(true);
|
|
2418
|
+
const measure = useCallback4(() => {
|
|
1963
2419
|
const element = containerRef.current;
|
|
1964
2420
|
if (!element) {
|
|
1965
2421
|
return;
|
|
@@ -1969,7 +2425,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1969
2425
|
}, [
|
|
1970
2426
|
threshold
|
|
1971
2427
|
]);
|
|
1972
|
-
const scrollToBottom =
|
|
2428
|
+
const scrollToBottom = useCallback4(() => {
|
|
1973
2429
|
const element = containerRef.current;
|
|
1974
2430
|
if (!element) {
|
|
1975
2431
|
return;
|
|
@@ -1977,7 +2433,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1977
2433
|
element.scrollTop = element.scrollHeight;
|
|
1978
2434
|
setIsAtBottom(true);
|
|
1979
2435
|
}, []);
|
|
1980
|
-
const setContainer =
|
|
2436
|
+
const setContainer = useCallback4((element) => {
|
|
1981
2437
|
containerRef.current = element;
|
|
1982
2438
|
if (element) {
|
|
1983
2439
|
measure();
|
|
@@ -1985,7 +2441,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1985
2441
|
}, [
|
|
1986
2442
|
measure
|
|
1987
2443
|
]);
|
|
1988
|
-
|
|
2444
|
+
useEffect5(() => {
|
|
1989
2445
|
if (isAtBottom) {
|
|
1990
2446
|
scrollToBottom();
|
|
1991
2447
|
}
|
|
@@ -1994,7 +2450,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1994
2450
|
isAtBottom,
|
|
1995
2451
|
scrollToBottom
|
|
1996
2452
|
]);
|
|
1997
|
-
const getContainerProps =
|
|
2453
|
+
const getContainerProps = useCallback4(() => ({
|
|
1998
2454
|
ref: setContainer,
|
|
1999
2455
|
onScroll: measure
|
|
2000
2456
|
}), [
|
|
@@ -2016,14 +2472,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
|
|
|
2016
2472
|
var DEFAULT_COPY_RESET_MS = 1500;
|
|
2017
2473
|
function useChatTranscript(options) {
|
|
2018
2474
|
const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
|
|
2019
|
-
const latest =
|
|
2475
|
+
const latest = useRef6(options);
|
|
2020
2476
|
latest.current = options;
|
|
2021
|
-
const [visibleCount, setVisibleCount] =
|
|
2022
|
-
const [stopRequested, setStopRequested] =
|
|
2023
|
-
const isStreaming = status === "streaming";
|
|
2477
|
+
const [visibleCount, setVisibleCount] = useState7(initialVisibleCount);
|
|
2478
|
+
const [stopRequested, setStopRequested] = useState7(false);
|
|
2479
|
+
const isStreaming = status === "streaming" || status === "reconnecting";
|
|
2024
2480
|
const isBusy = status === "submitted" || isStreaming;
|
|
2025
2481
|
const isEmpty = messages.length === 0;
|
|
2026
|
-
|
|
2482
|
+
useEffect6(() => {
|
|
2027
2483
|
if (!isBusy) {
|
|
2028
2484
|
setStopRequested(false);
|
|
2029
2485
|
}
|
|
@@ -2040,16 +2496,16 @@ function useChatTranscript(options) {
|
|
|
2040
2496
|
streamingMessageId,
|
|
2041
2497
|
options
|
|
2042
2498
|
});
|
|
2043
|
-
const stop =
|
|
2499
|
+
const stop = useCallback5(() => {
|
|
2044
2500
|
const current = latest.current;
|
|
2045
|
-
if (current.status !== "submitted" && current.status !== "streaming") {
|
|
2501
|
+
if (current.status !== "submitted" && current.status !== "streaming" && current.status !== "reconnecting") {
|
|
2046
2502
|
return;
|
|
2047
2503
|
}
|
|
2048
2504
|
setStopRequested(true);
|
|
2049
2505
|
void current.onStop?.();
|
|
2050
2506
|
}, []);
|
|
2051
|
-
const loadEarlier =
|
|
2052
|
-
const showAll =
|
|
2507
|
+
const loadEarlier = useCallback5(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
|
|
2508
|
+
const showAll = useCallback5(() => setVisibleCount(latest.current.messages.length), []);
|
|
2053
2509
|
const scroll = useStickToBottom({
|
|
2054
2510
|
contentKey: contentSignature(messages),
|
|
2055
2511
|
...stickToBottomThreshold !== void 0 ? {
|
|
@@ -2085,7 +2541,7 @@ function useChatTranscript(options) {
|
|
|
2085
2541
|
__name(useChatTranscript, "useChatTranscript");
|
|
2086
2542
|
function useTranscriptItem(options) {
|
|
2087
2543
|
const { message, isStreaming = false } = options;
|
|
2088
|
-
const messagesRef =
|
|
2544
|
+
const messagesRef = useRef6([
|
|
2089
2545
|
message
|
|
2090
2546
|
]);
|
|
2091
2547
|
if (messagesRef.current[0] !== message) {
|
|
@@ -2103,13 +2559,13 @@ function useTranscriptItem(options) {
|
|
|
2103
2559
|
}
|
|
2104
2560
|
__name(useTranscriptItem, "useTranscriptItem");
|
|
2105
2561
|
function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
|
|
2106
|
-
const [drafts, setDrafts] =
|
|
2107
|
-
const [copied, setCopied] =
|
|
2108
|
-
const [openReasoning, setOpenReasoning] =
|
|
2109
|
-
const [picks, setPicks] =
|
|
2110
|
-
const [settling, setSettling] =
|
|
2111
|
-
const [settleErrors, setSettleErrors] =
|
|
2112
|
-
const latest =
|
|
2562
|
+
const [drafts, setDrafts] = useState7(() => /* @__PURE__ */ new Map());
|
|
2563
|
+
const [copied, setCopied] = useState7(() => /* @__PURE__ */ new Set());
|
|
2564
|
+
const [openReasoning, setOpenReasoning] = useState7(() => /* @__PURE__ */ new Map());
|
|
2565
|
+
const [picks, setPicks] = useState7(() => /* @__PURE__ */ new Map());
|
|
2566
|
+
const [settling, setSettling] = useState7(() => /* @__PURE__ */ new Map());
|
|
2567
|
+
const [settleErrors, setSettleErrors] = useState7(() => /* @__PURE__ */ new Map());
|
|
2568
|
+
const latest = useRef6({
|
|
2113
2569
|
options,
|
|
2114
2570
|
drafts,
|
|
2115
2571
|
picks,
|
|
@@ -2121,23 +2577,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2121
2577
|
picks,
|
|
2122
2578
|
messages
|
|
2123
2579
|
};
|
|
2124
|
-
const copyTimers =
|
|
2125
|
-
|
|
2580
|
+
const copyTimers = useRef6(/* @__PURE__ */ new Map());
|
|
2581
|
+
useEffect6(() => () => {
|
|
2126
2582
|
for (const timer of copyTimers.current.values()) {
|
|
2127
2583
|
clearTimeout(timer);
|
|
2128
2584
|
}
|
|
2129
2585
|
copyTimers.current.clear();
|
|
2130
2586
|
}, []);
|
|
2131
|
-
const callbackCache =
|
|
2132
|
-
const reasoningToggleCache =
|
|
2133
|
-
const toggleReasoning =
|
|
2587
|
+
const callbackCache = useRef6(/* @__PURE__ */ new Map());
|
|
2588
|
+
const reasoningToggleCache = useRef6(/* @__PURE__ */ new Map());
|
|
2589
|
+
const toggleReasoning = useCallback5((key, open) => {
|
|
2134
2590
|
setOpenReasoning((current) => {
|
|
2135
2591
|
const next = new Map(current);
|
|
2136
2592
|
next.set(key, open ?? !(current.get(key) ?? false));
|
|
2137
2593
|
return next;
|
|
2138
2594
|
});
|
|
2139
2595
|
}, []);
|
|
2140
|
-
const stableToggle =
|
|
2596
|
+
const stableToggle = useCallback5((key) => {
|
|
2141
2597
|
let cached = reasoningToggleCache.current.get(key);
|
|
2142
2598
|
if (!cached) {
|
|
2143
2599
|
cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
|
|
@@ -2147,10 +2603,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2147
2603
|
}, [
|
|
2148
2604
|
toggleReasoning
|
|
2149
2605
|
]);
|
|
2150
|
-
const pick =
|
|
2606
|
+
const pick = useCallback5((toolCallId, questionId, values) => {
|
|
2151
2607
|
setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
|
|
2152
2608
|
}, []);
|
|
2153
|
-
const settle =
|
|
2609
|
+
const settle = useCallback5((toolCallId, action, send) => {
|
|
2154
2610
|
setSettleErrors((current) => {
|
|
2155
2611
|
if (!current.has(toolCallId)) {
|
|
2156
2612
|
return current;
|
|
@@ -2174,7 +2630,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2174
2630
|
failed(error);
|
|
2175
2631
|
}
|
|
2176
2632
|
}, []);
|
|
2177
|
-
const answer =
|
|
2633
|
+
const answer = useCallback5((toolCallId) => {
|
|
2178
2634
|
const current = latest.current;
|
|
2179
2635
|
const onAnswer = current.options.onAnswer;
|
|
2180
2636
|
if (!onAnswer) {
|
|
@@ -2184,7 +2640,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2184
2640
|
}, [
|
|
2185
2641
|
settle
|
|
2186
2642
|
]);
|
|
2187
|
-
const skip =
|
|
2643
|
+
const skip = useCallback5((toolCallId) => {
|
|
2188
2644
|
const onSkip = latest.current.options.onSkip;
|
|
2189
2645
|
if (onSkip) {
|
|
2190
2646
|
settle(toolCallId, "skip", () => onSkip(toolCallId));
|
|
@@ -2192,7 +2648,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2192
2648
|
}, [
|
|
2193
2649
|
settle
|
|
2194
2650
|
]);
|
|
2195
|
-
const approve =
|
|
2651
|
+
const approve = useCallback5((toolCallId, options2) => {
|
|
2196
2652
|
const onApprove = latest.current.options.onApprove;
|
|
2197
2653
|
if (onApprove) {
|
|
2198
2654
|
settle(toolCallId, "approve", () => options2 === void 0 ? onApprove(toolCallId) : onApprove(toolCallId, options2));
|
|
@@ -2200,7 +2656,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2200
2656
|
}, [
|
|
2201
2657
|
settle
|
|
2202
2658
|
]);
|
|
2203
|
-
const reject =
|
|
2659
|
+
const reject = useCallback5((toolCallId) => {
|
|
2204
2660
|
const onReject = latest.current.options.onReject;
|
|
2205
2661
|
if (onReject) {
|
|
2206
2662
|
settle(toolCallId, "reject", () => onReject(toolCallId));
|
|
@@ -2208,7 +2664,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
2208
2664
|
}, [
|
|
2209
2665
|
settle
|
|
2210
2666
|
]);
|
|
2211
|
-
const callbacksFor =
|
|
2667
|
+
const callbacksFor = useCallback5((id) => {
|
|
2212
2668
|
const cached = callbackCache.current.get(id);
|
|
2213
2669
|
if (cached) {
|
|
2214
2670
|
return cached;
|
|
@@ -2831,7 +3287,11 @@ function createSkillsSource(options) {
|
|
|
2831
3287
|
if (cached) {
|
|
2832
3288
|
return cached;
|
|
2833
3289
|
}
|
|
2834
|
-
const
|
|
3290
|
+
const listSkills = options.client.listSkills;
|
|
3291
|
+
if (listSkills === void 0) {
|
|
3292
|
+
return Promise.reject(new AgentBackendUnsupportedError("listSkills"));
|
|
3293
|
+
}
|
|
3294
|
+
const pending = listSkills.call(options.client, threadId).then((entries) => entries.map(toItem)).catch((error) => {
|
|
2835
3295
|
byThread.delete(key);
|
|
2836
3296
|
throw error;
|
|
2837
3297
|
});
|
|
@@ -2861,18 +3321,18 @@ function toItem(entry) {
|
|
|
2861
3321
|
__name(toItem, "toItem");
|
|
2862
3322
|
|
|
2863
3323
|
// src/composer/use-composer-autocomplete.ts
|
|
2864
|
-
import { useCallback as
|
|
3324
|
+
import { useCallback as useCallback6, useEffect as useEffect7, useId, useLayoutEffect, useMemo as useMemo2, useRef as useRef7, useState as useState8 } from "react";
|
|
2865
3325
|
function useComposerAutocomplete(options) {
|
|
2866
3326
|
const { value, sources, acceptOnTab = true } = options;
|
|
2867
|
-
const latest =
|
|
3327
|
+
const latest = useRef7(options);
|
|
2868
3328
|
latest.current = options;
|
|
2869
|
-
const inputRef =
|
|
2870
|
-
const [caret, setCaret] =
|
|
2871
|
-
const [items, setItems] =
|
|
2872
|
-
const [isLoading, setLoading] =
|
|
2873
|
-
const [error, setError] =
|
|
2874
|
-
const [activeIndex, setActiveIndex] =
|
|
2875
|
-
const [dismissedIndex, setDismissedIndex] =
|
|
3329
|
+
const inputRef = useRef7(null);
|
|
3330
|
+
const [caret, setCaret] = useState8(value.length);
|
|
3331
|
+
const [items, setItems] = useState8([]);
|
|
3332
|
+
const [isLoading, setLoading] = useState8(false);
|
|
3333
|
+
const [error, setError] = useState8(null);
|
|
3334
|
+
const [activeIndex, setActiveIndex] = useState8(0);
|
|
3335
|
+
const [dismissedIndex, setDismissedIndex] = useState8(null);
|
|
2876
3336
|
const safeCaret = Math.min(caret, value.length);
|
|
2877
3337
|
const match = useMemo2(() => findActiveTrigger(value, safeCaret, sources), [
|
|
2878
3338
|
value,
|
|
@@ -2880,16 +3340,16 @@ function useComposerAutocomplete(options) {
|
|
|
2880
3340
|
sources
|
|
2881
3341
|
]);
|
|
2882
3342
|
const isActive = match !== null && dismissedIndex !== match.index;
|
|
2883
|
-
const matchRef =
|
|
3343
|
+
const matchRef = useRef7(match);
|
|
2884
3344
|
matchRef.current = match;
|
|
2885
|
-
const caretRef =
|
|
3345
|
+
const caretRef = useRef7(safeCaret);
|
|
2886
3346
|
caretRef.current = safeCaret;
|
|
2887
|
-
const itemsRef =
|
|
3347
|
+
const itemsRef = useRef7(items);
|
|
2888
3348
|
itemsRef.current = items;
|
|
2889
|
-
const activeIndexRef =
|
|
3349
|
+
const activeIndexRef = useRef7(activeIndex);
|
|
2890
3350
|
activeIndexRef.current = activeIndex;
|
|
2891
3351
|
const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
|
|
2892
|
-
|
|
3352
|
+
useEffect7(() => {
|
|
2893
3353
|
const current = matchRef.current;
|
|
2894
3354
|
if (requestKey === null || current === null) {
|
|
2895
3355
|
setItems([]);
|
|
@@ -2939,23 +3399,23 @@ function useComposerAutocomplete(options) {
|
|
|
2939
3399
|
const activeItem = isOpen ? items[activeIndex] ?? null : null;
|
|
2940
3400
|
const reactId = useId();
|
|
2941
3401
|
const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
|
|
2942
|
-
const optionId =
|
|
3402
|
+
const optionId = useCallback6((index) => `${listboxId}-option-${index}`, [
|
|
2943
3403
|
listboxId
|
|
2944
3404
|
]);
|
|
2945
|
-
const highlight =
|
|
3405
|
+
const highlight = useCallback6((index) => {
|
|
2946
3406
|
const total = itemsRef.current.length;
|
|
2947
3407
|
if (total === 0) return;
|
|
2948
3408
|
setActiveIndex((index % total + total) % total);
|
|
2949
3409
|
}, []);
|
|
2950
|
-
const moveHighlight =
|
|
3410
|
+
const moveHighlight = useCallback6((delta) => highlight(activeIndexRef.current + delta), [
|
|
2951
3411
|
highlight
|
|
2952
3412
|
]);
|
|
2953
|
-
const dismiss =
|
|
3413
|
+
const dismiss = useCallback6(() => {
|
|
2954
3414
|
const current = matchRef.current;
|
|
2955
3415
|
if (current) setDismissedIndex(current.index);
|
|
2956
3416
|
}, []);
|
|
2957
|
-
const pendingCaret =
|
|
2958
|
-
const accept =
|
|
3417
|
+
const pendingCaret = useRef7(null);
|
|
3418
|
+
const accept = useCallback6((item) => {
|
|
2959
3419
|
const current = matchRef.current;
|
|
2960
3420
|
if (!current) return;
|
|
2961
3421
|
const chosen = item ?? itemsRef.current[activeIndexRef.current];
|
|
@@ -2972,10 +3432,10 @@ function useComposerAutocomplete(options) {
|
|
|
2972
3432
|
pendingCaret.current = null;
|
|
2973
3433
|
inputRef.current?.setSelectionRange(position, position);
|
|
2974
3434
|
});
|
|
2975
|
-
const readCaret =
|
|
3435
|
+
const readCaret = useCallback6((element) => {
|
|
2976
3436
|
setCaret(element.selectionStart ?? element.value.length);
|
|
2977
3437
|
}, []);
|
|
2978
|
-
const onKeyDown =
|
|
3438
|
+
const onKeyDown = useCallback6((event) => {
|
|
2979
3439
|
if (!isOpen) return;
|
|
2980
3440
|
const hasItems = itemsRef.current.length > 0;
|
|
2981
3441
|
switch (event.key) {
|
|
@@ -3013,23 +3473,23 @@ function useComposerAutocomplete(options) {
|
|
|
3013
3473
|
dismiss,
|
|
3014
3474
|
moveHighlight
|
|
3015
3475
|
]);
|
|
3016
|
-
const onChange =
|
|
3476
|
+
const onChange = useCallback6((event) => {
|
|
3017
3477
|
readCaret(event.target);
|
|
3018
3478
|
const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
|
|
3019
3479
|
setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
|
|
3020
3480
|
}, [
|
|
3021
3481
|
readCaret
|
|
3022
3482
|
]);
|
|
3023
|
-
const onSelect =
|
|
3483
|
+
const onSelect = useCallback6((event) => readCaret(event.currentTarget), [
|
|
3024
3484
|
readCaret
|
|
3025
3485
|
]);
|
|
3026
|
-
const onClick =
|
|
3486
|
+
const onClick = useCallback6((event) => readCaret(event.currentTarget), [
|
|
3027
3487
|
readCaret
|
|
3028
3488
|
]);
|
|
3029
|
-
const setInput =
|
|
3489
|
+
const setInput = useCallback6((element) => {
|
|
3030
3490
|
inputRef.current = element;
|
|
3031
3491
|
}, []);
|
|
3032
|
-
const getInputProps =
|
|
3492
|
+
const getInputProps = useCallback6(() => ({
|
|
3033
3493
|
ref: setInput,
|
|
3034
3494
|
onChange,
|
|
3035
3495
|
onKeyDown,
|
|
@@ -3057,7 +3517,7 @@ function useComposerAutocomplete(options) {
|
|
|
3057
3517
|
activeIndex,
|
|
3058
3518
|
optionId
|
|
3059
3519
|
]);
|
|
3060
|
-
const getListboxProps =
|
|
3520
|
+
const getListboxProps = useCallback6(() => ({
|
|
3061
3521
|
id: listboxId,
|
|
3062
3522
|
role: "listbox",
|
|
3063
3523
|
"aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
|
|
@@ -3065,7 +3525,7 @@ function useComposerAutocomplete(options) {
|
|
|
3065
3525
|
listboxId,
|
|
3066
3526
|
match
|
|
3067
3527
|
]);
|
|
3068
|
-
const getOptionProps =
|
|
3528
|
+
const getOptionProps = useCallback6((index) => ({
|
|
3069
3529
|
id: optionId(index),
|
|
3070
3530
|
role: "option",
|
|
3071
3531
|
"aria-selected": index === activeIndex
|
|
@@ -3099,7 +3559,7 @@ function isPromise(value) {
|
|
|
3099
3559
|
__name(isPromise, "isPromise");
|
|
3100
3560
|
|
|
3101
3561
|
// src/presentation/use-tool-catalog.ts
|
|
3102
|
-
import { useCallback as
|
|
3562
|
+
import { useCallback as useCallback7, useEffect as useEffect8, useMemo as useMemo3, useState as useState9 } from "react";
|
|
3103
3563
|
var shared = /* @__PURE__ */ new WeakMap();
|
|
3104
3564
|
function load(client, agent) {
|
|
3105
3565
|
let byAgent = shared.get(client);
|
|
@@ -3110,6 +3570,9 @@ function load(client, agent) {
|
|
|
3110
3570
|
const key = agent ?? "";
|
|
3111
3571
|
const cached = byAgent.get(key);
|
|
3112
3572
|
if (cached !== void 0) return cached;
|
|
3573
|
+
if (client.listTools === void 0) {
|
|
3574
|
+
return Promise.reject(new AgentBackendUnsupportedError("listTools"));
|
|
3575
|
+
}
|
|
3113
3576
|
const request = client.listTools(agent).catch((error) => {
|
|
3114
3577
|
byAgent.delete(key);
|
|
3115
3578
|
throw error;
|
|
@@ -3120,11 +3583,11 @@ function load(client, agent) {
|
|
|
3120
3583
|
__name(load, "load");
|
|
3121
3584
|
function useToolCatalog(options) {
|
|
3122
3585
|
const { client, agent, enabled = true } = options;
|
|
3123
|
-
const [entries, setEntries] =
|
|
3124
|
-
const [isLoading, setLoading] =
|
|
3125
|
-
const [error, setError] =
|
|
3126
|
-
const [generation, setGeneration] =
|
|
3127
|
-
|
|
3586
|
+
const [entries, setEntries] = useState9([]);
|
|
3587
|
+
const [isLoading, setLoading] = useState9(enabled);
|
|
3588
|
+
const [error, setError] = useState9(null);
|
|
3589
|
+
const [generation, setGeneration] = useState9(0);
|
|
3590
|
+
useEffect8(() => {
|
|
3128
3591
|
if (!enabled) {
|
|
3129
3592
|
setLoading(false);
|
|
3130
3593
|
return;
|
|
@@ -3150,7 +3613,7 @@ function useToolCatalog(options) {
|
|
|
3150
3613
|
enabled,
|
|
3151
3614
|
generation
|
|
3152
3615
|
]);
|
|
3153
|
-
const refresh =
|
|
3616
|
+
const refresh = useCallback7(() => {
|
|
3154
3617
|
shared.get(client)?.delete(agent ?? "");
|
|
3155
3618
|
setGeneration((current) => current + 1);
|
|
3156
3619
|
}, [
|
|
@@ -3171,7 +3634,7 @@ function useToolCatalog(options) {
|
|
|
3171
3634
|
__name(useToolCatalog, "useToolCatalog");
|
|
3172
3635
|
|
|
3173
3636
|
// src/approvals/countdown.ts
|
|
3174
|
-
import { useEffect as
|
|
3637
|
+
import { useEffect as useEffect9, useRef as useRef8, useState as useState10 } from "react";
|
|
3175
3638
|
function approvalCountdown(expiresAt, now = Date.now()) {
|
|
3176
3639
|
const deadline = expiresAt == null ? Number.NaN : Date.parse(expiresAt);
|
|
3177
3640
|
if (!Number.isFinite(deadline)) {
|
|
@@ -3189,10 +3652,10 @@ function approvalCountdown(expiresAt, now = Date.now()) {
|
|
|
3189
3652
|
__name(approvalCountdown, "approvalCountdown");
|
|
3190
3653
|
function useApprovalCountdown(expiresAt, options = {}) {
|
|
3191
3654
|
const { intervalMs = 1e3 } = options;
|
|
3192
|
-
const nowRef =
|
|
3655
|
+
const nowRef = useRef8(options.now ?? Date.now);
|
|
3193
3656
|
nowRef.current = options.now ?? Date.now;
|
|
3194
|
-
const [state, setState] =
|
|
3195
|
-
|
|
3657
|
+
const [state, setState] = useState10(() => approvalCountdown(expiresAt, nowRef.current()));
|
|
3658
|
+
useEffect9(() => {
|
|
3196
3659
|
const read = /* @__PURE__ */ __name(() => approvalCountdown(expiresAt, nowRef.current()), "read");
|
|
3197
3660
|
const first = read();
|
|
3198
3661
|
setState(first);
|
|
@@ -3358,7 +3821,12 @@ function storedMessageToUiMessage(message) {
|
|
|
3358
3821
|
return {
|
|
3359
3822
|
id: message.id,
|
|
3360
3823
|
role: message.role,
|
|
3361
|
-
parts
|
|
3824
|
+
parts,
|
|
3825
|
+
...message.feedback !== void 0 ? {
|
|
3826
|
+
metadata: {
|
|
3827
|
+
feedback: message.feedback
|
|
3828
|
+
}
|
|
3829
|
+
} : {}
|
|
3362
3830
|
};
|
|
3363
3831
|
}
|
|
3364
3832
|
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
@@ -3394,14 +3862,21 @@ function mergeTurn(rows) {
|
|
|
3394
3862
|
}
|
|
3395
3863
|
const parts = rows.flatMap((row) => storedMessageToUiMessage(row).parts);
|
|
3396
3864
|
const usage = sumUsage(rows);
|
|
3865
|
+
const feedback = lastRow.feedback;
|
|
3866
|
+
const metadata = {
|
|
3867
|
+
...usage !== void 0 ? {
|
|
3868
|
+
usage
|
|
3869
|
+
} : {},
|
|
3870
|
+
...feedback !== void 0 ? {
|
|
3871
|
+
feedback
|
|
3872
|
+
} : {}
|
|
3873
|
+
};
|
|
3397
3874
|
return {
|
|
3398
3875
|
id: lastRow.id,
|
|
3399
3876
|
role: lastRow.role,
|
|
3400
3877
|
parts,
|
|
3401
|
-
...
|
|
3402
|
-
metadata
|
|
3403
|
-
usage
|
|
3404
|
-
}
|
|
3878
|
+
...Object.keys(metadata).length > 0 ? {
|
|
3879
|
+
metadata
|
|
3405
3880
|
} : {}
|
|
3406
3881
|
};
|
|
3407
3882
|
}
|
|
@@ -3422,20 +3897,21 @@ __name(sumUsage, "sumUsage");
|
|
|
3422
3897
|
|
|
3423
3898
|
// src/use-agent-chat.ts
|
|
3424
3899
|
import { useChat } from "@ai-sdk/react";
|
|
3425
|
-
import { useCallback as
|
|
3900
|
+
import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo4, useRef as useRef9, useState as useState11 } from "react";
|
|
3426
3901
|
function useAgentChat(options) {
|
|
3427
|
-
const latest =
|
|
3902
|
+
const latest = useRef9(options);
|
|
3428
3903
|
latest.current = options;
|
|
3429
|
-
const [runId, setRunId] =
|
|
3430
|
-
const runIdRef =
|
|
3904
|
+
const [runId, setRunId] = useState11(options.resumeRunId);
|
|
3905
|
+
const runIdRef = useRef9(runId);
|
|
3431
3906
|
runIdRef.current = runId;
|
|
3432
|
-
const settlingRunIdRef =
|
|
3433
|
-
const autoResumeRunIdRef =
|
|
3434
|
-
const [autoResume, setAutoResume] =
|
|
3435
|
-
const [activeRunId, setActiveRunId] =
|
|
3436
|
-
const regenerateNext =
|
|
3437
|
-
const createdThreadId =
|
|
3907
|
+
const settlingRunIdRef = useRef9(void 0);
|
|
3908
|
+
const autoResumeRunIdRef = useRef9(void 0);
|
|
3909
|
+
const [autoResume, setAutoResume] = useState11(false);
|
|
3910
|
+
const [activeRunId, setActiveRunId] = useState11(null);
|
|
3911
|
+
const regenerateNext = useRef9(false);
|
|
3912
|
+
const createdThreadId = useRef9(void 0);
|
|
3438
3913
|
const client = useMemo4(() => {
|
|
3914
|
+
if (options.backend) return options.backend;
|
|
3439
3915
|
if (options.client) return options.client;
|
|
3440
3916
|
return new AgentClient({
|
|
3441
3917
|
...options.baseUrl !== void 0 ? {
|
|
@@ -3450,7 +3926,11 @@ function useAgentChat(options) {
|
|
|
3450
3926
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
3451
3927
|
});
|
|
3452
3928
|
}, []);
|
|
3453
|
-
|
|
3929
|
+
const [connection, setConnection] = useState11({
|
|
3930
|
+
status: "live"
|
|
3931
|
+
});
|
|
3932
|
+
const resyncAfterSettle = useRef9(false);
|
|
3933
|
+
useEffect10(() => {
|
|
3454
3934
|
if (!options.resume || options.threadId === void 0) return;
|
|
3455
3935
|
let cancelled = false;
|
|
3456
3936
|
const threadId = options.threadId;
|
|
@@ -3477,23 +3957,26 @@ function useAgentChat(options) {
|
|
|
3477
3957
|
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
3478
3958
|
createdThreadId.current = meta.threadId;
|
|
3479
3959
|
latest.current.onThreadCreated?.(meta.threadId);
|
|
3960
|
+
notifyThreads(client, {
|
|
3961
|
+
type: "changed"
|
|
3962
|
+
});
|
|
3480
3963
|
}
|
|
3481
3964
|
}
|
|
3482
3965
|
__name(onMeta, "onMeta");
|
|
3483
3966
|
return new AgentChatTransport({
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
...options.credentials !== void 0 ? {
|
|
3488
|
-
credentials: options.credentials
|
|
3489
|
-
} : {},
|
|
3490
|
-
...options.fetch !== void 0 ? {
|
|
3491
|
-
fetch: options.fetch
|
|
3967
|
+
backend: client,
|
|
3968
|
+
...options.reconnect !== void 0 ? {
|
|
3969
|
+
reconnect: options.reconnect
|
|
3492
3970
|
} : {},
|
|
3971
|
+
onConnectionChange: /* @__PURE__ */ __name((state) => {
|
|
3972
|
+
if (state.status === "gone") resyncAfterSettle.current = true;
|
|
3973
|
+
setConnection(state.status === "gone" ? {
|
|
3974
|
+
status: "live"
|
|
3975
|
+
} : state);
|
|
3976
|
+
}, "onConnectionChange"),
|
|
3493
3977
|
...options.agent !== void 0 ? {
|
|
3494
3978
|
agent: options.agent
|
|
3495
3979
|
} : {},
|
|
3496
|
-
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders"),
|
|
3497
3980
|
getBody: /* @__PURE__ */ __name(() => {
|
|
3498
3981
|
const current = latest.current;
|
|
3499
3982
|
const pageContext = current.getPageContext?.() ?? null;
|
|
@@ -3519,7 +4002,7 @@ function useAgentChat(options) {
|
|
|
3519
4002
|
}, "onAttemptStart")
|
|
3520
4003
|
});
|
|
3521
4004
|
}, []);
|
|
3522
|
-
const backgroundRef =
|
|
4005
|
+
const backgroundRef = useRef9(() => Promise.resolve());
|
|
3523
4006
|
const chat = useChat({
|
|
3524
4007
|
transport,
|
|
3525
4008
|
resume: options.resumeRunId !== void 0 || autoResume,
|
|
@@ -3533,7 +4016,15 @@ function useAgentChat(options) {
|
|
|
3533
4016
|
latest.current.onData?.(part);
|
|
3534
4017
|
if (part.type === "data-title") {
|
|
3535
4018
|
const title = part.data?.title;
|
|
3536
|
-
if (typeof title === "string")
|
|
4019
|
+
if (typeof title === "string") {
|
|
4020
|
+
latest.current.onTitle?.(title);
|
|
4021
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4022
|
+
if (threadId !== void 0) notifyThreads(client, {
|
|
4023
|
+
type: "title",
|
|
4024
|
+
threadId,
|
|
4025
|
+
title
|
|
4026
|
+
});
|
|
4027
|
+
}
|
|
3537
4028
|
}
|
|
3538
4029
|
}, "onData"),
|
|
3539
4030
|
onFinish: /* @__PURE__ */ __name(({ isError }) => {
|
|
@@ -3547,16 +4038,36 @@ function useAgentChat(options) {
|
|
|
3547
4038
|
runId: settledRunId,
|
|
3548
4039
|
status: isError ? "failed" : "completed"
|
|
3549
4040
|
});
|
|
4041
|
+
notifyThreads(client, {
|
|
4042
|
+
type: "changed"
|
|
4043
|
+
});
|
|
4044
|
+
}
|
|
4045
|
+
setConnection({
|
|
4046
|
+
status: "live"
|
|
4047
|
+
});
|
|
4048
|
+
if (resyncAfterSettle.current) {
|
|
4049
|
+
resyncAfterSettle.current = false;
|
|
4050
|
+
void resyncFromThread();
|
|
3550
4051
|
}
|
|
3551
4052
|
}, "onFinish")
|
|
3552
4053
|
});
|
|
3553
|
-
const chatRef =
|
|
4054
|
+
const chatRef = useRef9(chat);
|
|
3554
4055
|
chatRef.current = chat;
|
|
3555
|
-
|
|
3556
|
-
|
|
4056
|
+
async function resyncFromThread() {
|
|
4057
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
4058
|
+
if (threadId === void 0) return;
|
|
4059
|
+
try {
|
|
4060
|
+
const thread = await client.getThread(threadId);
|
|
4061
|
+
chatRef.current.setMessages(storedThreadToUiMessages(thread.messages));
|
|
4062
|
+
} catch {
|
|
4063
|
+
}
|
|
4064
|
+
}
|
|
4065
|
+
__name(resyncFromThread, "resyncFromThread");
|
|
4066
|
+
const turnInFlight = useRef9(false);
|
|
4067
|
+
const isTurnInFlight = useCallback8(() => turnInFlight.current || transport.isAttemptLive, [
|
|
3557
4068
|
transport
|
|
3558
4069
|
]);
|
|
3559
|
-
const sendMessage =
|
|
4070
|
+
const sendMessage = useCallback8(async (...args) => {
|
|
3560
4071
|
if (isTurnInFlight()) return;
|
|
3561
4072
|
turnInFlight.current = true;
|
|
3562
4073
|
try {
|
|
@@ -3567,15 +4078,15 @@ function useAgentChat(options) {
|
|
|
3567
4078
|
}, [
|
|
3568
4079
|
isTurnInFlight
|
|
3569
4080
|
]);
|
|
3570
|
-
const addToolResult =
|
|
4081
|
+
const addToolResult = useCallback8(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
3571
4082
|
tool,
|
|
3572
4083
|
toolCallId,
|
|
3573
4084
|
output
|
|
3574
4085
|
}), []);
|
|
3575
|
-
const [threads, setThreads] =
|
|
3576
|
-
const [quota, setQuota] =
|
|
3577
|
-
const [backgroundRuns, setBackgroundRuns] =
|
|
3578
|
-
const refreshBackground =
|
|
4086
|
+
const [threads, setThreads] = useState11([]);
|
|
4087
|
+
const [quota, setQuota] = useState11(null);
|
|
4088
|
+
const [backgroundRuns, setBackgroundRuns] = useState11([]);
|
|
4089
|
+
const refreshBackground = useCallback8(async () => {
|
|
3579
4090
|
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
3580
4091
|
if (threadId === void 0) {
|
|
3581
4092
|
return;
|
|
@@ -3600,7 +4111,7 @@ function useAgentChat(options) {
|
|
|
3600
4111
|
]);
|
|
3601
4112
|
backgroundRef.current = refreshBackground;
|
|
3602
4113
|
const isWorking = backgroundRuns.some((run) => run.status === "running");
|
|
3603
|
-
|
|
4114
|
+
useEffect10(() => {
|
|
3604
4115
|
if (options.background !== true || options.threadId === void 0) {
|
|
3605
4116
|
return;
|
|
3606
4117
|
}
|
|
@@ -3610,7 +4121,7 @@ function useAgentChat(options) {
|
|
|
3610
4121
|
options.threadId,
|
|
3611
4122
|
refreshBackground
|
|
3612
4123
|
]);
|
|
3613
|
-
|
|
4124
|
+
useEffect10(() => {
|
|
3614
4125
|
const every = latest.current.backgroundPollMs ?? 5e3;
|
|
3615
4126
|
if (latest.current.background !== true || !isWorking || every <= 0) {
|
|
3616
4127
|
return;
|
|
@@ -3621,27 +4132,29 @@ function useAgentChat(options) {
|
|
|
3621
4132
|
isWorking,
|
|
3622
4133
|
refreshBackground
|
|
3623
4134
|
]);
|
|
3624
|
-
const loadThreads =
|
|
4135
|
+
const loadThreads = useCallback8(async () => {
|
|
3625
4136
|
const list = await client.listThreads();
|
|
3626
4137
|
setThreads(list);
|
|
3627
4138
|
return list;
|
|
3628
4139
|
}, [
|
|
3629
4140
|
client
|
|
3630
4141
|
]);
|
|
3631
|
-
const loadThread =
|
|
4142
|
+
const loadThread = useCallback8((id) => client.getThread(id), [
|
|
3632
4143
|
client
|
|
3633
4144
|
]);
|
|
3634
|
-
const deleteThread =
|
|
4145
|
+
const deleteThread = useCallback8(async (id) => {
|
|
3635
4146
|
await client.deleteThread(id);
|
|
3636
4147
|
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
3637
4148
|
}, [
|
|
3638
4149
|
client
|
|
3639
4150
|
]);
|
|
3640
|
-
const forkThread =
|
|
4151
|
+
const forkThread = useCallback8((threadId, messageId) => requireBackendMethod(client, "forkFromMessage")(threadId, messageId), [
|
|
3641
4152
|
client
|
|
3642
4153
|
]);
|
|
3643
|
-
const renameThread =
|
|
3644
|
-
await client.
|
|
4154
|
+
const renameThread = useCallback8(async (id, title) => {
|
|
4155
|
+
await client.updateThread(id, {
|
|
4156
|
+
title
|
|
4157
|
+
});
|
|
3645
4158
|
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
3646
4159
|
...thread,
|
|
3647
4160
|
title
|
|
@@ -3649,24 +4162,24 @@ function useAgentChat(options) {
|
|
|
3649
4162
|
}, [
|
|
3650
4163
|
client
|
|
3651
4164
|
]);
|
|
3652
|
-
const promoteThread =
|
|
3653
|
-
await client
|
|
4165
|
+
const promoteThread = useCallback8(async (id) => {
|
|
4166
|
+
await requireBackendMethod(client, "promoteThread")(id);
|
|
3654
4167
|
await loadThreads();
|
|
3655
4168
|
}, [
|
|
3656
4169
|
client,
|
|
3657
4170
|
loadThreads
|
|
3658
4171
|
]);
|
|
3659
|
-
const truncateFromMessage =
|
|
4172
|
+
const truncateFromMessage = useCallback8((threadId, messageId) => requireBackendMethod(client, "truncateFromMessage")(threadId, messageId).then(() => void 0), [
|
|
3660
4173
|
client
|
|
3661
4174
|
]);
|
|
3662
|
-
const loadQuota =
|
|
3663
|
-
const today = await client
|
|
4175
|
+
const loadQuota = useCallback8(async () => {
|
|
4176
|
+
const today = await requireBackendMethod(client, "getQuotaToday")();
|
|
3664
4177
|
setQuota(today);
|
|
3665
4178
|
return today;
|
|
3666
4179
|
}, [
|
|
3667
4180
|
client
|
|
3668
4181
|
]);
|
|
3669
|
-
const cancel =
|
|
4182
|
+
const cancel = useCallback8(async () => {
|
|
3670
4183
|
chatRef.current.stop();
|
|
3671
4184
|
const activeRunId2 = runIdRef.current;
|
|
3672
4185
|
if (activeRunId2) {
|
|
@@ -3678,8 +4191,8 @@ function useAgentChat(options) {
|
|
|
3678
4191
|
}, [
|
|
3679
4192
|
client
|
|
3680
4193
|
]);
|
|
3681
|
-
const approve =
|
|
3682
|
-
await client
|
|
4194
|
+
const approve = useCallback8(async ({ toolCallId, remember, via }) => {
|
|
4195
|
+
await requireBackendMethod(client, "approveToolCall")({
|
|
3683
4196
|
toolCallId,
|
|
3684
4197
|
...remember === true ? {
|
|
3685
4198
|
remember: true
|
|
@@ -3691,8 +4204,8 @@ function useAgentChat(options) {
|
|
|
3691
4204
|
}, [
|
|
3692
4205
|
client
|
|
3693
4206
|
]);
|
|
3694
|
-
const reject =
|
|
3695
|
-
await client
|
|
4207
|
+
const reject = useCallback8(async ({ toolCallId, reason, via }) => {
|
|
4208
|
+
await requireBackendMethod(client, "rejectToolCall")({
|
|
3696
4209
|
toolCallId,
|
|
3697
4210
|
...reason !== void 0 ? {
|
|
3698
4211
|
reason
|
|
@@ -3704,8 +4217,8 @@ function useAgentChat(options) {
|
|
|
3704
4217
|
}, [
|
|
3705
4218
|
client
|
|
3706
4219
|
]);
|
|
3707
|
-
const answer =
|
|
3708
|
-
await client
|
|
4220
|
+
const answer = useCallback8(async ({ toolCallId, answers }) => {
|
|
4221
|
+
await requireBackendMethod(client, "answerToolCall")({
|
|
3709
4222
|
toolCallId,
|
|
3710
4223
|
...answers !== void 0 ? {
|
|
3711
4224
|
answers
|
|
@@ -3714,14 +4227,14 @@ function useAgentChat(options) {
|
|
|
3714
4227
|
}, [
|
|
3715
4228
|
client
|
|
3716
4229
|
]);
|
|
3717
|
-
const skip =
|
|
3718
|
-
await client
|
|
4230
|
+
const skip = useCallback8(async ({ toolCallId }) => {
|
|
4231
|
+
await requireBackendMethod(client, "skipToolCall")({
|
|
3719
4232
|
toolCallId
|
|
3720
4233
|
});
|
|
3721
4234
|
}, [
|
|
3722
4235
|
client
|
|
3723
4236
|
]);
|
|
3724
|
-
const regenerate =
|
|
4237
|
+
const regenerate = useCallback8(() => {
|
|
3725
4238
|
if (isTurnInFlight()) return;
|
|
3726
4239
|
regenerateNext.current = true;
|
|
3727
4240
|
turnInFlight.current = true;
|
|
@@ -3736,8 +4249,13 @@ function useAgentChat(options) {
|
|
|
3736
4249
|
isWorking,
|
|
3737
4250
|
refresh: refreshBackground
|
|
3738
4251
|
};
|
|
4252
|
+
const getThreadId = useCallback8(() => latest.current.threadId ?? createdThreadId.current, []);
|
|
4253
|
+
const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
|
|
3739
4254
|
return {
|
|
3740
4255
|
...chat,
|
|
4256
|
+
status,
|
|
4257
|
+
/** Where the live stream stands — see {@link StreamConnectionState}. */
|
|
4258
|
+
connection,
|
|
3741
4259
|
sendMessage,
|
|
3742
4260
|
addToolResult,
|
|
3743
4261
|
/** Sub-agents this conversation started and did not wait for. */
|
|
@@ -3745,6 +4263,14 @@ function useAgentChat(options) {
|
|
|
3745
4263
|
runId,
|
|
3746
4264
|
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
3747
4265
|
activeRunId,
|
|
4266
|
+
/**
|
|
4267
|
+
* The thread this chat is on right now: the `threadId` option, else the one the backend created
|
|
4268
|
+
* on the first send. A getter, because the created id is learned mid-stream.
|
|
4269
|
+
*/
|
|
4270
|
+
getThreadId,
|
|
4271
|
+
/** The backend this chat talks to — pass it to `useThreads`, `useMessageFeedback`, … */
|
|
4272
|
+
backend: client,
|
|
4273
|
+
/** Same object as {@link backend}; kept for callers that read it under its old name. */
|
|
3748
4274
|
client,
|
|
3749
4275
|
threads,
|
|
3750
4276
|
loadThreads,
|
|
@@ -3774,6 +4300,7 @@ async function mergeHeaders(options) {
|
|
|
3774
4300
|
}
|
|
3775
4301
|
__name(mergeHeaders, "mergeHeaders");
|
|
3776
4302
|
export {
|
|
4303
|
+
AgentBackendUnsupportedError,
|
|
3777
4304
|
AgentChatTransport,
|
|
3778
4305
|
AgentClient,
|
|
3779
4306
|
AgentHttpError,
|
|
@@ -3803,10 +4330,13 @@ export {
|
|
|
3803
4330
|
inferResultView,
|
|
3804
4331
|
isActionCall,
|
|
3805
4332
|
loadStoredSpeechLang,
|
|
4333
|
+
notifyThreads,
|
|
4334
|
+
onThreadsEvent,
|
|
3806
4335
|
persistSpeechLang,
|
|
3807
4336
|
phraseFor,
|
|
3808
4337
|
readPath,
|
|
3809
4338
|
readReasoningMs,
|
|
4339
|
+
requireBackendMethod,
|
|
3810
4340
|
resolveResultView,
|
|
3811
4341
|
storedMessageToUiMessage,
|
|
3812
4342
|
storedThreadToUiMessages,
|
|
@@ -3817,8 +4347,10 @@ export {
|
|
|
3817
4347
|
useChatTranscript,
|
|
3818
4348
|
useComposerAutocomplete,
|
|
3819
4349
|
useElapsed,
|
|
4350
|
+
useMessageFeedback,
|
|
3820
4351
|
useSpeechRecognition,
|
|
3821
4352
|
useStickToBottom,
|
|
4353
|
+
useThreads,
|
|
3822
4354
|
useToolCatalog,
|
|
3823
4355
|
useTranscriptItem,
|
|
3824
4356
|
validateElicitationAnswer as validateAnswer,
|