@dudousxd/nestjs-agent-react 0.5.5 → 0.6.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 +296 -8
- package/dist/index.cjs +2189 -758
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +980 -224
- package/dist/index.d.ts +980 -224
- package/dist/index.js +2175 -758
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -37,21 +37,36 @@ __export(src_exports, {
|
|
|
37
37
|
ChatInput: () => ChatInput,
|
|
38
38
|
DEFAULT_SPEECH_LANG: () => DEFAULT_SPEECH_LANG,
|
|
39
39
|
MessageItem: () => MessageItem,
|
|
40
|
+
MessageItemView: () => MessageItemView,
|
|
40
41
|
MessageList: () => MessageList,
|
|
41
42
|
SPEECH_LANGUAGES: () => SPEECH_LANGUAGES,
|
|
43
|
+
applyCompletion: () => applyCompletion,
|
|
44
|
+
backgroundRunsFromThread: () => backgroundRunsFromThread,
|
|
45
|
+
buildTranscriptBlocks: () => buildTranscriptBlocks,
|
|
46
|
+
createSkillsSource: () => createSkillsSource,
|
|
47
|
+
describeTimestamp: () => describeTimestamp,
|
|
48
|
+
describeUsage: () => describeUsage,
|
|
49
|
+
extractMessageText: () => extractMessageText,
|
|
50
|
+
filterAutocompleteItems: () => filterAutocompleteItems,
|
|
51
|
+
findActiveTrigger: () => findActiveTrigger,
|
|
42
52
|
formatRelativeTime: () => formatRelativeTime,
|
|
43
53
|
loadStoredSpeechLang: () => loadStoredSpeechLang,
|
|
44
54
|
persistSpeechLang: () => persistSpeechLang,
|
|
45
55
|
storedMessageToUiMessage: () => storedMessageToUiMessage,
|
|
46
56
|
storedThreadToUiMessages: () => storedThreadToUiMessages,
|
|
47
57
|
useAgentChat: () => useAgentChat,
|
|
48
|
-
|
|
58
|
+
useChatTranscript: () => useChatTranscript,
|
|
59
|
+
useComposerAutocomplete: () => useComposerAutocomplete,
|
|
60
|
+
useSpeechRecognition: () => useSpeechRecognition,
|
|
61
|
+
useStickToBottom: () => useStickToBottom,
|
|
62
|
+
useTranscriptItem: () => useTranscriptItem
|
|
49
63
|
});
|
|
50
64
|
module.exports = __toCommonJS(src_exports);
|
|
51
65
|
|
|
52
66
|
// src/agent-chat-transport.ts
|
|
53
67
|
var HEADER_RUN_ID = "x-agent-run-id";
|
|
54
68
|
var HEADER_THREAD_ID = "x-agent-thread-id";
|
|
69
|
+
var ASK_TOOL_NAME = "ask";
|
|
55
70
|
var AgentChatTransport = class {
|
|
56
71
|
static {
|
|
57
72
|
__name(this, "AgentChatTransport");
|
|
@@ -59,6 +74,14 @@ var AgentChatTransport = class {
|
|
|
59
74
|
options;
|
|
60
75
|
currentRunId;
|
|
61
76
|
currentThreadId;
|
|
77
|
+
/**
|
|
78
|
+
* One attempt at a time. The AI SDK keeps a single in-flight response per chat and decides
|
|
79
|
+
* whether its streamed message REPLACES the list's last entry or is appended by comparing that
|
|
80
|
+
* response's message id against the last entry alone. Two attempts writing into one chat
|
|
81
|
+
* therefore append alternating copies of each other's message, and the list ends up holding
|
|
82
|
+
* several entries under each id — which React renders as duplicate keys.
|
|
83
|
+
*/
|
|
84
|
+
attemptLive = false;
|
|
62
85
|
constructor(options = {}) {
|
|
63
86
|
this.options = options;
|
|
64
87
|
}
|
|
@@ -70,59 +93,84 @@ var AgentChatTransport = class {
|
|
|
70
93
|
get threadId() {
|
|
71
94
|
return this.currentThreadId;
|
|
72
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* True from the moment a send or resume attempt starts until its chunk stream terminates. A
|
|
98
|
+
* caller that can refuse a turn BEFORE the SDK commits to one — `useAgentChat` wrapping
|
|
99
|
+
* `sendMessage` — reads this; the transport itself can only refuse a resume, which is the one
|
|
100
|
+
* attempt the SDK lets a transport decline.
|
|
101
|
+
*/
|
|
102
|
+
get isAttemptLive() {
|
|
103
|
+
return this.attemptLive;
|
|
104
|
+
}
|
|
73
105
|
async sendMessages(options) {
|
|
74
106
|
this.options.onAttemptStart?.();
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
107
|
+
this.attemptLive = true;
|
|
108
|
+
try {
|
|
109
|
+
const lastMessage = options.messages.at(-1);
|
|
110
|
+
const message = lastMessage ? extractText(lastMessage) : "";
|
|
111
|
+
const body = {
|
|
112
|
+
...this.options.agent !== void 0 ? {
|
|
113
|
+
agent: this.options.agent
|
|
114
|
+
} : {},
|
|
115
|
+
...this.options.getBody?.() ?? {},
|
|
116
|
+
...options.body ?? {},
|
|
117
|
+
message
|
|
118
|
+
};
|
|
119
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat`, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: {
|
|
122
|
+
"content-type": "application/json",
|
|
123
|
+
accept: "text/event-stream",
|
|
124
|
+
...await this.resolveHeaders(options.headers)
|
|
125
|
+
},
|
|
126
|
+
body: JSON.stringify(body),
|
|
127
|
+
...this.options.credentials !== void 0 ? {
|
|
128
|
+
credentials: this.options.credentials
|
|
129
|
+
} : {},
|
|
130
|
+
...options.abortSignal ? {
|
|
131
|
+
signal: options.abortSignal
|
|
132
|
+
} : {}
|
|
133
|
+
});
|
|
134
|
+
if (!response.ok || !response.body) {
|
|
135
|
+
throw new Error(`Agent chat request failed: ${response.status} ${response.statusText}`);
|
|
136
|
+
}
|
|
137
|
+
this.captureHeaderMeta(response.headers);
|
|
138
|
+
return this.toChunkStream(response.body);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
this.attemptLive = false;
|
|
141
|
+
throw error;
|
|
102
142
|
}
|
|
103
|
-
this.captureHeaderMeta(response.headers);
|
|
104
|
-
return this.toChunkStream(response.body);
|
|
105
143
|
}
|
|
106
144
|
async reconnectToStream(options) {
|
|
145
|
+
if (this.attemptLive) return null;
|
|
107
146
|
this.options.onAttemptStart?.();
|
|
108
147
|
const runId = this.options.getResumeRunId?.();
|
|
109
148
|
if (runId === void 0) return null;
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
149
|
+
this.attemptLive = true;
|
|
150
|
+
try {
|
|
151
|
+
const response = await this.fetchImpl()(`${this.baseUrl()}/agent/chat/${encodeURIComponent(runId)}/stream`, {
|
|
152
|
+
method: "GET",
|
|
153
|
+
headers: {
|
|
154
|
+
accept: "text/event-stream",
|
|
155
|
+
...await this.resolveHeaders(options.headers)
|
|
156
|
+
},
|
|
157
|
+
...this.options.credentials !== void 0 ? {
|
|
158
|
+
credentials: this.options.credentials
|
|
159
|
+
} : {}
|
|
160
|
+
});
|
|
161
|
+
if (response.status === 404) {
|
|
162
|
+
this.attemptLive = false;
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
if (!response.ok || !response.body) {
|
|
166
|
+
throw new Error(`Agent stream reconnect failed: ${response.status} ${response.statusText}`);
|
|
167
|
+
}
|
|
168
|
+
this.captureHeaderMeta(response.headers);
|
|
169
|
+
return this.toChunkStream(response.body);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
this.attemptLive = false;
|
|
172
|
+
throw error;
|
|
123
173
|
}
|
|
124
|
-
this.captureHeaderMeta(response.headers);
|
|
125
|
-
return this.toChunkStream(response.body);
|
|
126
174
|
}
|
|
127
175
|
fetchImpl() {
|
|
128
176
|
return this.options.fetch ?? globalThis.fetch;
|
|
@@ -171,7 +219,11 @@ var AgentChatTransport = class {
|
|
|
171
219
|
let stepIndex = 0;
|
|
172
220
|
let textId = null;
|
|
173
221
|
let reasoningId = null;
|
|
222
|
+
const announced = /* @__PURE__ */ new Set();
|
|
174
223
|
const record = /* @__PURE__ */ __name((meta) => this.recordMeta(meta), "record");
|
|
224
|
+
const endAttempt = /* @__PURE__ */ __name(() => {
|
|
225
|
+
this.attemptLive = false;
|
|
226
|
+
}, "endAttempt");
|
|
175
227
|
return new ReadableStream({
|
|
176
228
|
async pull(controller) {
|
|
177
229
|
function ensureStarted() {
|
|
@@ -268,6 +320,7 @@ var AgentChatTransport = class {
|
|
|
268
320
|
break;
|
|
269
321
|
case "tool-input-start":
|
|
270
322
|
ensureStep();
|
|
323
|
+
announced.add(event.id);
|
|
271
324
|
controller.enqueue({
|
|
272
325
|
type: "tool-input-start",
|
|
273
326
|
toolCallId: event.id,
|
|
@@ -292,6 +345,7 @@ var AgentChatTransport = class {
|
|
|
292
345
|
break;
|
|
293
346
|
case "tool-input-available":
|
|
294
347
|
ensureStep();
|
|
348
|
+
announced.add(event.id);
|
|
295
349
|
controller.enqueue({
|
|
296
350
|
type: "tool-input-available",
|
|
297
351
|
toolCallId: event.id,
|
|
@@ -304,6 +358,23 @@ var AgentChatTransport = class {
|
|
|
304
358
|
} : {}
|
|
305
359
|
});
|
|
306
360
|
break;
|
|
361
|
+
case "elicitation":
|
|
362
|
+
ensureStep();
|
|
363
|
+
if (!announced.has(event.id)) {
|
|
364
|
+
announced.add(event.id);
|
|
365
|
+
controller.enqueue({
|
|
366
|
+
type: "tool-input-available",
|
|
367
|
+
toolCallId: event.id,
|
|
368
|
+
toolName: ASK_TOOL_NAME,
|
|
369
|
+
input: {
|
|
370
|
+
...event.request.preamble !== void 0 ? {
|
|
371
|
+
preamble: event.request.preamble
|
|
372
|
+
} : {},
|
|
373
|
+
questions: event.request.questions
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
break;
|
|
307
378
|
case "tool-output":
|
|
308
379
|
ensureStep();
|
|
309
380
|
controller.enqueue({
|
|
@@ -332,6 +403,7 @@ var AgentChatTransport = class {
|
|
|
332
403
|
type: "finish"
|
|
333
404
|
});
|
|
334
405
|
controller.close();
|
|
406
|
+
endAttempt();
|
|
335
407
|
}
|
|
336
408
|
__name(finish, "finish");
|
|
337
409
|
try {
|
|
@@ -360,6 +432,7 @@ var AgentChatTransport = class {
|
|
|
360
432
|
errorText: parseErrorText(frame.data)
|
|
361
433
|
});
|
|
362
434
|
controller.close();
|
|
435
|
+
endAttempt();
|
|
363
436
|
return;
|
|
364
437
|
}
|
|
365
438
|
if (frame.event === "meta") {
|
|
@@ -378,10 +451,12 @@ var AgentChatTransport = class {
|
|
|
378
451
|
errorText: error instanceof Error ? error.message : "Agent stream error"
|
|
379
452
|
});
|
|
380
453
|
controller.close();
|
|
454
|
+
endAttempt();
|
|
381
455
|
}
|
|
382
456
|
},
|
|
383
457
|
cancel() {
|
|
384
458
|
void reader.cancel();
|
|
459
|
+
endAttempt();
|
|
385
460
|
}
|
|
386
461
|
});
|
|
387
462
|
}
|
|
@@ -449,6 +524,53 @@ function extractText(message) {
|
|
|
449
524
|
}
|
|
450
525
|
__name(extractText, "extractText");
|
|
451
526
|
|
|
527
|
+
// src/background-runs.ts
|
|
528
|
+
function asReceipt(output) {
|
|
529
|
+
if (typeof output !== "object" || output === null) {
|
|
530
|
+
return null;
|
|
531
|
+
}
|
|
532
|
+
const candidate = output;
|
|
533
|
+
if (candidate.detached !== true || candidate.status !== "started" || typeof candidate.agent !== "string" || typeof candidate.runId !== "string") {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
detached: true,
|
|
538
|
+
status: "started",
|
|
539
|
+
agent: candidate.agent,
|
|
540
|
+
runId: candidate.runId
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
__name(asReceipt, "asReceipt");
|
|
544
|
+
function backgroundRunsFromThread(messages) {
|
|
545
|
+
const byRun = /* @__PURE__ */ new Map();
|
|
546
|
+
for (const message of messages) {
|
|
547
|
+
for (const result of message.toolResults ?? []) {
|
|
548
|
+
const receipt = asReceipt(result.output);
|
|
549
|
+
if (receipt === null || byRun.has(receipt.runId)) {
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
byRun.set(receipt.runId, {
|
|
553
|
+
runId: receipt.runId,
|
|
554
|
+
agent: receipt.agent,
|
|
555
|
+
toolCallId: result.id,
|
|
556
|
+
toolName: result.name,
|
|
557
|
+
status: "running"
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
for (const message of messages) {
|
|
562
|
+
const run = message.runId === void 0 ? void 0 : byRun.get(message.runId);
|
|
563
|
+
if (run !== void 0) {
|
|
564
|
+
run.status = "delivered";
|
|
565
|
+
run.message = message;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
return [
|
|
569
|
+
...byRun.values()
|
|
570
|
+
];
|
|
571
|
+
}
|
|
572
|
+
__name(backgroundRunsFromThread, "backgroundRunsFromThread");
|
|
573
|
+
|
|
452
574
|
// src/client.ts
|
|
453
575
|
var AgentHttpError = class extends Error {
|
|
454
576
|
static {
|
|
@@ -473,6 +595,16 @@ var AgentClient = class {
|
|
|
473
595
|
listThreads() {
|
|
474
596
|
return this.request("GET", "/agent/threads");
|
|
475
597
|
}
|
|
598
|
+
/**
|
|
599
|
+
* The skills this caller can invoke right now, scope-resolved — the same list, built by the same
|
|
600
|
+
* call, that the model is offered, so what a user can type after a `/` and what the agent can
|
|
601
|
+
* reach cannot drift apart. `threadId` reaches the host's own resolver, which may scope a skill to
|
|
602
|
+
* one conversation; omitted, the server reads it as a brand-new thread.
|
|
603
|
+
*/
|
|
604
|
+
listSkills(threadId) {
|
|
605
|
+
const query = threadId === void 0 ? "" : `?threadId=${encodeURIComponent(threadId)}`;
|
|
606
|
+
return this.request("GET", `/agent/skills${query}`);
|
|
607
|
+
}
|
|
476
608
|
getThread(id) {
|
|
477
609
|
return this.request("GET", `/agent/threads/${encodeURIComponent(id)}`);
|
|
478
610
|
}
|
|
@@ -534,6 +666,23 @@ var AgentClient = class {
|
|
|
534
666
|
rejectToolCall(input) {
|
|
535
667
|
return this.request("POST", "/agent/tool-call/reject", input);
|
|
536
668
|
}
|
|
669
|
+
/**
|
|
670
|
+
* Settle a parked question set. `answers` is questionId → chosen option values; a question left
|
|
671
|
+
* out takes the pre-picked default the request carried, resolved server-side against the request
|
|
672
|
+
* the run already holds. Omit the whole object and the user has confirmed every pre-picked
|
|
673
|
+
* answer — which is the point of the surface, so it is a valid submission rather than a blank.
|
|
674
|
+
*/
|
|
675
|
+
answerToolCall(input) {
|
|
676
|
+
return this.request("POST", "/agent/tool-call/answer", input);
|
|
677
|
+
}
|
|
678
|
+
/**
|
|
679
|
+
* Decline to answer and let the agent proceed on its own pre-picked values. Lands on the same
|
|
680
|
+
* values a confirmation would, and persists differently on purpose — only one of them is
|
|
681
|
+
* evidence the user chose them.
|
|
682
|
+
*/
|
|
683
|
+
skipToolCall(input) {
|
|
684
|
+
return this.request("POST", "/agent/tool-call/skip", input);
|
|
685
|
+
}
|
|
537
686
|
async request(method, path, body) {
|
|
538
687
|
const fetchImpl = this.options.fetch ?? globalThis.fetch;
|
|
539
688
|
const baseUrl = (this.options.baseUrl ?? "").replace(/\/$/, "");
|
|
@@ -568,414 +717,76 @@ var AgentClient = class {
|
|
|
568
717
|
}
|
|
569
718
|
};
|
|
570
719
|
|
|
571
|
-
// src/
|
|
572
|
-
|
|
573
|
-
const parts = [];
|
|
574
|
-
if (message.content) {
|
|
575
|
-
parts.push({
|
|
576
|
-
type: "text",
|
|
577
|
-
text: message.content
|
|
578
|
-
});
|
|
579
|
-
}
|
|
580
|
-
for (const attachment of message.attachments ?? []) {
|
|
581
|
-
parts.push({
|
|
582
|
-
type: "file",
|
|
583
|
-
mediaType: attachment.contentType,
|
|
584
|
-
filename: attachment.name,
|
|
585
|
-
url: attachment.url
|
|
586
|
-
});
|
|
587
|
-
}
|
|
588
|
-
for (const call of message.toolCalls ?? []) {
|
|
589
|
-
const result = message.toolResults?.find((candidate) => candidate.id === call.id);
|
|
590
|
-
const toolKind = call.kind;
|
|
591
|
-
parts.push({
|
|
592
|
-
type: `tool-${call.name}`,
|
|
593
|
-
toolCallId: call.id,
|
|
594
|
-
...toolKind !== void 0 ? {
|
|
595
|
-
toolMetadata: {
|
|
596
|
-
toolKind
|
|
597
|
-
}
|
|
598
|
-
} : {},
|
|
599
|
-
...result !== void 0 ? {
|
|
600
|
-
state: "output-available",
|
|
601
|
-
input: call.input,
|
|
602
|
-
output: result.output
|
|
603
|
-
} : {
|
|
604
|
-
state: "input-available",
|
|
605
|
-
input: call.input
|
|
606
|
-
}
|
|
607
|
-
});
|
|
608
|
-
}
|
|
609
|
-
return {
|
|
610
|
-
id: message.id,
|
|
611
|
-
role: message.role,
|
|
612
|
-
parts
|
|
613
|
-
};
|
|
614
|
-
}
|
|
615
|
-
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
720
|
+
// src/components/chat-input.tsx
|
|
721
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
616
722
|
|
|
617
|
-
// src/
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
if (message.role === "assistant" && previousRow?.role === "assistant" && currentTurn) {
|
|
624
|
-
currentTurn.push(message);
|
|
625
|
-
} else {
|
|
626
|
-
turns.push([
|
|
627
|
-
message
|
|
628
|
-
]);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
return turns.map(mergeTurn);
|
|
723
|
+
// src/components/use-speech-recognition.ts
|
|
724
|
+
var import_react = require("react");
|
|
725
|
+
function getRecognitionCtor() {
|
|
726
|
+
if (typeof window === "undefined") return null;
|
|
727
|
+
const scope = window;
|
|
728
|
+
return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;
|
|
632
729
|
}
|
|
633
|
-
__name(
|
|
634
|
-
function
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
730
|
+
__name(getRecognitionCtor, "getRecognitionCtor");
|
|
731
|
+
function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
|
|
732
|
+
const recognitionRef = (0, import_react.useRef)(null);
|
|
733
|
+
const [isListening, setIsListening] = (0, import_react.useState)(false);
|
|
734
|
+
const ctor = (0, import_react.useMemo)(() => getRecognitionCtor(), []);
|
|
735
|
+
const isSupported = ctor !== null;
|
|
736
|
+
const finalRef = (0, import_react.useRef)(onFinalTranscript);
|
|
737
|
+
const interimRef = (0, import_react.useRef)(onInterimTranscript);
|
|
738
|
+
finalRef.current = onFinalTranscript;
|
|
739
|
+
interimRef.current = onInterimTranscript;
|
|
740
|
+
(0, import_react.useEffect)(() => {
|
|
741
|
+
if (!ctor) return;
|
|
742
|
+
const recognition = new ctor();
|
|
743
|
+
recognition.lang = lang;
|
|
744
|
+
recognition.continuous = false;
|
|
745
|
+
recognition.interimResults = true;
|
|
746
|
+
recognition.onresult = (event) => {
|
|
747
|
+
let interim = "";
|
|
748
|
+
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
749
|
+
const result = event.results[i];
|
|
750
|
+
if (!result) continue;
|
|
751
|
+
const text = result[0]?.transcript ?? "";
|
|
752
|
+
if (result.isFinal) {
|
|
753
|
+
finalRef.current(text);
|
|
754
|
+
} else {
|
|
755
|
+
interim += text;
|
|
756
|
+
}
|
|
651
757
|
}
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
const usages = rows.map((row) => row.usage).filter((usage) => usage !== void 0);
|
|
658
|
-
if (usages.length === 0) {
|
|
659
|
-
return void 0;
|
|
660
|
-
}
|
|
661
|
-
const allCostsUnknown = usages.every((usage) => usage.costUsd === null || usage.costUsd === void 0);
|
|
662
|
-
return {
|
|
663
|
-
inputTokens: usages.reduce((sum, usage) => sum + usage.inputTokens, 0),
|
|
664
|
-
outputTokens: usages.reduce((sum, usage) => sum + usage.outputTokens, 0),
|
|
665
|
-
costUsd: allCostsUnknown ? null : usages.reduce((sum, usage) => sum + (usage.costUsd ?? 0), 0)
|
|
666
|
-
};
|
|
667
|
-
}
|
|
668
|
-
__name(sumUsage, "sumUsage");
|
|
669
|
-
|
|
670
|
-
// src/use-agent-chat.ts
|
|
671
|
-
var import_react = require("@ai-sdk/react");
|
|
672
|
-
var import_react2 = require("react");
|
|
673
|
-
function useAgentChat(options) {
|
|
674
|
-
const latest = (0, import_react2.useRef)(options);
|
|
675
|
-
latest.current = options;
|
|
676
|
-
const [runId, setRunId] = (0, import_react2.useState)(options.resumeRunId);
|
|
677
|
-
const runIdRef = (0, import_react2.useRef)(runId);
|
|
678
|
-
runIdRef.current = runId;
|
|
679
|
-
const settlingRunIdRef = (0, import_react2.useRef)(void 0);
|
|
680
|
-
const autoResumeRunIdRef = (0, import_react2.useRef)(void 0);
|
|
681
|
-
const [autoResume, setAutoResume] = (0, import_react2.useState)(false);
|
|
682
|
-
const [activeRunId, setActiveRunId] = (0, import_react2.useState)(null);
|
|
683
|
-
const regenerateNext = (0, import_react2.useRef)(false);
|
|
684
|
-
const createdThreadId = (0, import_react2.useRef)(void 0);
|
|
685
|
-
const client = (0, import_react2.useMemo)(() => {
|
|
686
|
-
if (options.client) return options.client;
|
|
687
|
-
return new AgentClient({
|
|
688
|
-
...options.baseUrl !== void 0 ? {
|
|
689
|
-
baseUrl: options.baseUrl
|
|
690
|
-
} : {},
|
|
691
|
-
...options.credentials !== void 0 ? {
|
|
692
|
-
credentials: options.credentials
|
|
693
|
-
} : {},
|
|
694
|
-
...options.fetch !== void 0 ? {
|
|
695
|
-
fetch: options.fetch
|
|
696
|
-
} : {},
|
|
697
|
-
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
698
|
-
});
|
|
699
|
-
}, []);
|
|
700
|
-
(0, import_react2.useEffect)(() => {
|
|
701
|
-
if (!options.resume || options.threadId === void 0) return;
|
|
702
|
-
let cancelled = false;
|
|
703
|
-
const threadId = options.threadId;
|
|
704
|
-
void client.getThread(threadId).then((thread) => {
|
|
705
|
-
if (cancelled) return;
|
|
706
|
-
setActiveRunId(thread.activeRunId ?? null);
|
|
707
|
-
if (thread.activeRunId != null) {
|
|
708
|
-
autoResumeRunIdRef.current = thread.activeRunId;
|
|
709
|
-
setAutoResume(true);
|
|
758
|
+
if (interim && interimRef.current) interimRef.current(interim);
|
|
759
|
+
};
|
|
760
|
+
recognition.onerror = (event) => {
|
|
761
|
+
if (event.error !== "no-speech" && event.error !== "aborted") {
|
|
762
|
+
console.warn("SpeechRecognition error", event.error);
|
|
710
763
|
}
|
|
711
|
-
|
|
764
|
+
setIsListening(false);
|
|
765
|
+
};
|
|
766
|
+
recognition.onend = () => setIsListening(false);
|
|
767
|
+
recognitionRef.current = recognition;
|
|
712
768
|
return () => {
|
|
713
|
-
|
|
769
|
+
try {
|
|
770
|
+
recognition.stop();
|
|
771
|
+
} catch {
|
|
772
|
+
}
|
|
773
|
+
recognitionRef.current = null;
|
|
714
774
|
};
|
|
715
775
|
}, [
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
options.threadId
|
|
776
|
+
ctor,
|
|
777
|
+
lang
|
|
719
778
|
]);
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
}
|
|
729
|
-
__name(onMeta, "onMeta");
|
|
730
|
-
return new AgentChatTransport({
|
|
731
|
-
...options.baseUrl !== void 0 ? {
|
|
732
|
-
baseUrl: options.baseUrl
|
|
733
|
-
} : {},
|
|
734
|
-
...options.credentials !== void 0 ? {
|
|
735
|
-
credentials: options.credentials
|
|
736
|
-
} : {},
|
|
737
|
-
...options.fetch !== void 0 ? {
|
|
738
|
-
fetch: options.fetch
|
|
739
|
-
} : {},
|
|
740
|
-
...options.agent !== void 0 ? {
|
|
741
|
-
agent: options.agent
|
|
742
|
-
} : {},
|
|
743
|
-
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders"),
|
|
744
|
-
getBody: /* @__PURE__ */ __name(() => {
|
|
745
|
-
const current = latest.current;
|
|
746
|
-
const pageContext = current.getPageContext?.() ?? null;
|
|
747
|
-
const regenerate2 = regenerateNext.current;
|
|
748
|
-
regenerateNext.current = false;
|
|
749
|
-
const threadId = current.threadId ?? createdThreadId.current;
|
|
750
|
-
return {
|
|
751
|
-
...threadId !== void 0 ? {
|
|
752
|
-
threadId
|
|
753
|
-
} : {},
|
|
754
|
-
...pageContext ? {
|
|
755
|
-
pageContext
|
|
756
|
-
} : {},
|
|
757
|
-
...regenerate2 ? {
|
|
758
|
-
regenerate: true
|
|
759
|
-
} : {}
|
|
760
|
-
};
|
|
761
|
-
}, "getBody"),
|
|
762
|
-
getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId ?? autoResumeRunIdRef.current, "getResumeRunId"),
|
|
763
|
-
onMeta,
|
|
764
|
-
onAttemptStart: /* @__PURE__ */ __name(() => {
|
|
765
|
-
settlingRunIdRef.current = void 0;
|
|
766
|
-
}, "onAttemptStart")
|
|
767
|
-
});
|
|
768
|
-
}, []);
|
|
769
|
-
const chat = (0, import_react.useChat)({
|
|
770
|
-
transport,
|
|
771
|
-
resume: options.resumeRunId !== void 0 || autoResume,
|
|
772
|
-
...options.threadId !== void 0 ? {
|
|
773
|
-
id: options.threadId
|
|
774
|
-
} : {},
|
|
775
|
-
...options.initialMessages !== void 0 ? {
|
|
776
|
-
messages: options.initialMessages
|
|
777
|
-
} : {},
|
|
778
|
-
onFinish: /* @__PURE__ */ __name(({ isError }) => {
|
|
779
|
-
latest.current.onFinish?.();
|
|
780
|
-
const settledRunId = settlingRunIdRef.current;
|
|
781
|
-
if (settledRunId !== void 0) {
|
|
782
|
-
latest.current.onRunSettled?.({
|
|
783
|
-
runId: settledRunId,
|
|
784
|
-
status: isError ? "failed" : "completed"
|
|
785
|
-
});
|
|
786
|
-
}
|
|
787
|
-
}, "onFinish")
|
|
788
|
-
});
|
|
789
|
-
const chatRef = (0, import_react2.useRef)(chat);
|
|
790
|
-
chatRef.current = chat;
|
|
791
|
-
const addToolResult = (0, import_react2.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
792
|
-
tool,
|
|
793
|
-
toolCallId,
|
|
794
|
-
output
|
|
795
|
-
}), []);
|
|
796
|
-
const [threads, setThreads] = (0, import_react2.useState)([]);
|
|
797
|
-
const [quota, setQuota] = (0, import_react2.useState)(null);
|
|
798
|
-
const loadThreads = (0, import_react2.useCallback)(async () => {
|
|
799
|
-
const list = await client.listThreads();
|
|
800
|
-
setThreads(list);
|
|
801
|
-
return list;
|
|
802
|
-
}, [
|
|
803
|
-
client
|
|
804
|
-
]);
|
|
805
|
-
const loadThread = (0, import_react2.useCallback)((id) => client.getThread(id), [
|
|
806
|
-
client
|
|
807
|
-
]);
|
|
808
|
-
const deleteThread = (0, import_react2.useCallback)(async (id) => {
|
|
809
|
-
await client.deleteThread(id);
|
|
810
|
-
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
811
|
-
}, [
|
|
812
|
-
client
|
|
813
|
-
]);
|
|
814
|
-
const forkThread = (0, import_react2.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
|
|
815
|
-
client
|
|
816
|
-
]);
|
|
817
|
-
const renameThread = (0, import_react2.useCallback)(async (id, title) => {
|
|
818
|
-
await client.renameThread(id, title);
|
|
819
|
-
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
820
|
-
...thread,
|
|
821
|
-
title
|
|
822
|
-
} : thread));
|
|
823
|
-
}, [
|
|
824
|
-
client
|
|
825
|
-
]);
|
|
826
|
-
const promoteThread = (0, import_react2.useCallback)(async (id) => {
|
|
827
|
-
await client.promoteThread(id);
|
|
828
|
-
await loadThreads();
|
|
829
|
-
}, [
|
|
830
|
-
client,
|
|
831
|
-
loadThreads
|
|
832
|
-
]);
|
|
833
|
-
const truncateFromMessage = (0, import_react2.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
|
|
834
|
-
client
|
|
835
|
-
]);
|
|
836
|
-
const loadQuota = (0, import_react2.useCallback)(async () => {
|
|
837
|
-
const today = await client.getQuotaToday();
|
|
838
|
-
setQuota(today);
|
|
839
|
-
return today;
|
|
840
|
-
}, [
|
|
841
|
-
client
|
|
842
|
-
]);
|
|
843
|
-
const cancel = (0, import_react2.useCallback)(async () => {
|
|
844
|
-
chatRef.current.stop();
|
|
845
|
-
const activeRunId2 = runIdRef.current;
|
|
846
|
-
if (activeRunId2) {
|
|
847
|
-
try {
|
|
848
|
-
await client.cancelStream(activeRunId2);
|
|
849
|
-
} catch {
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
}, [
|
|
853
|
-
client
|
|
854
|
-
]);
|
|
855
|
-
const approve = (0, import_react2.useCallback)(async ({ toolCallId }) => {
|
|
856
|
-
await client.approveToolCall({
|
|
857
|
-
toolCallId
|
|
858
|
-
});
|
|
859
|
-
}, [
|
|
860
|
-
client
|
|
861
|
-
]);
|
|
862
|
-
const reject = (0, import_react2.useCallback)(async ({ toolCallId, reason }) => {
|
|
863
|
-
await client.rejectToolCall({
|
|
864
|
-
toolCallId,
|
|
865
|
-
...reason !== void 0 ? {
|
|
866
|
-
reason
|
|
867
|
-
} : {}
|
|
868
|
-
});
|
|
869
|
-
}, [
|
|
870
|
-
client
|
|
871
|
-
]);
|
|
872
|
-
const regenerate = (0, import_react2.useCallback)(() => {
|
|
873
|
-
regenerateNext.current = true;
|
|
874
|
-
void chatRef.current.regenerate();
|
|
875
|
-
}, []);
|
|
876
|
-
return {
|
|
877
|
-
...chat,
|
|
878
|
-
addToolResult,
|
|
879
|
-
runId,
|
|
880
|
-
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
881
|
-
activeRunId,
|
|
882
|
-
client,
|
|
883
|
-
threads,
|
|
884
|
-
loadThreads,
|
|
885
|
-
loadThread,
|
|
886
|
-
deleteThread,
|
|
887
|
-
forkThread,
|
|
888
|
-
renameThread,
|
|
889
|
-
promoteThread,
|
|
890
|
-
truncateFromMessage,
|
|
891
|
-
quota,
|
|
892
|
-
loadQuota,
|
|
893
|
-
cancel,
|
|
894
|
-
approve,
|
|
895
|
-
reject,
|
|
896
|
-
regenerate
|
|
897
|
-
};
|
|
898
|
-
}
|
|
899
|
-
__name(useAgentChat, "useAgentChat");
|
|
900
|
-
async function mergeHeaders(options) {
|
|
901
|
-
const dynamic = await options.getHeaders?.() ?? {};
|
|
902
|
-
return {
|
|
903
|
-
...options.headers,
|
|
904
|
-
...dynamic
|
|
905
|
-
};
|
|
906
|
-
}
|
|
907
|
-
__name(mergeHeaders, "mergeHeaders");
|
|
908
|
-
|
|
909
|
-
// src/components/chat-input.tsx
|
|
910
|
-
var import_react4 = __toESM(require("react"), 1);
|
|
911
|
-
|
|
912
|
-
// src/components/use-speech-recognition.ts
|
|
913
|
-
var import_react3 = require("react");
|
|
914
|
-
function getRecognitionCtor() {
|
|
915
|
-
if (typeof window === "undefined") return null;
|
|
916
|
-
const scope = window;
|
|
917
|
-
return scope.SpeechRecognition ?? scope.webkitSpeechRecognition ?? null;
|
|
918
|
-
}
|
|
919
|
-
__name(getRecognitionCtor, "getRecognitionCtor");
|
|
920
|
-
function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
|
|
921
|
-
const recognitionRef = (0, import_react3.useRef)(null);
|
|
922
|
-
const [isListening, setIsListening] = (0, import_react3.useState)(false);
|
|
923
|
-
const ctor = (0, import_react3.useMemo)(() => getRecognitionCtor(), []);
|
|
924
|
-
const isSupported = ctor !== null;
|
|
925
|
-
const finalRef = (0, import_react3.useRef)(onFinalTranscript);
|
|
926
|
-
const interimRef = (0, import_react3.useRef)(onInterimTranscript);
|
|
927
|
-
finalRef.current = onFinalTranscript;
|
|
928
|
-
interimRef.current = onInterimTranscript;
|
|
929
|
-
(0, import_react3.useEffect)(() => {
|
|
930
|
-
if (!ctor) return;
|
|
931
|
-
const recognition = new ctor();
|
|
932
|
-
recognition.lang = lang;
|
|
933
|
-
recognition.continuous = false;
|
|
934
|
-
recognition.interimResults = true;
|
|
935
|
-
recognition.onresult = (event) => {
|
|
936
|
-
let interim = "";
|
|
937
|
-
for (let i = event.resultIndex; i < event.results.length; i++) {
|
|
938
|
-
const result = event.results[i];
|
|
939
|
-
if (!result) continue;
|
|
940
|
-
const text = result[0]?.transcript ?? "";
|
|
941
|
-
if (result.isFinal) {
|
|
942
|
-
finalRef.current(text);
|
|
943
|
-
} else {
|
|
944
|
-
interim += text;
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
if (interim && interimRef.current) interimRef.current(interim);
|
|
948
|
-
};
|
|
949
|
-
recognition.onerror = (event) => {
|
|
950
|
-
if (event.error !== "no-speech" && event.error !== "aborted") {
|
|
951
|
-
console.warn("SpeechRecognition error", event.error);
|
|
952
|
-
}
|
|
953
|
-
setIsListening(false);
|
|
954
|
-
};
|
|
955
|
-
recognition.onend = () => setIsListening(false);
|
|
956
|
-
recognitionRef.current = recognition;
|
|
957
|
-
return () => {
|
|
958
|
-
try {
|
|
959
|
-
recognition.stop();
|
|
960
|
-
} catch {
|
|
961
|
-
}
|
|
962
|
-
recognitionRef.current = null;
|
|
963
|
-
};
|
|
964
|
-
}, [
|
|
965
|
-
ctor,
|
|
966
|
-
lang
|
|
967
|
-
]);
|
|
968
|
-
const start = (0, import_react3.useCallback)(() => {
|
|
969
|
-
const recognition = recognitionRef.current;
|
|
970
|
-
if (!recognition) return;
|
|
971
|
-
try {
|
|
972
|
-
recognition.start();
|
|
973
|
-
setIsListening(true);
|
|
974
|
-
} catch (error) {
|
|
975
|
-
console.warn("SpeechRecognition start failed", error);
|
|
779
|
+
const start = (0, import_react.useCallback)(() => {
|
|
780
|
+
const recognition = recognitionRef.current;
|
|
781
|
+
if (!recognition) return;
|
|
782
|
+
try {
|
|
783
|
+
recognition.start();
|
|
784
|
+
setIsListening(true);
|
|
785
|
+
} catch (error) {
|
|
786
|
+
console.warn("SpeechRecognition start failed", error);
|
|
976
787
|
}
|
|
977
788
|
}, []);
|
|
978
|
-
const stop = (0,
|
|
789
|
+
const stop = (0, import_react.useCallback)(() => {
|
|
979
790
|
const recognition = recognitionRef.current;
|
|
980
791
|
if (!recognition) return;
|
|
981
792
|
try {
|
|
@@ -993,9 +804,9 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
993
804
|
__name(useSpeechRecognition, "useSpeechRecognition");
|
|
994
805
|
|
|
995
806
|
// src/components/chat-input.tsx
|
|
996
|
-
function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = true, speechLang = "en-US", sendLabel = "Send", className, textareaClassName, sendButtonClassName, micButtonClassName, renderMic }) {
|
|
997
|
-
const [value, setValue] = (0,
|
|
998
|
-
const [interim, setInterim] = (0,
|
|
807
|
+
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 }) {
|
|
808
|
+
const [value, setValue] = (0, import_react2.useState)("");
|
|
809
|
+
const [interim, setInterim] = (0, import_react2.useState)("");
|
|
999
810
|
const speech = useSpeechRecognition({
|
|
1000
811
|
lang: speechLang,
|
|
1001
812
|
onFinalTranscript: /* @__PURE__ */ __name((text) => {
|
|
@@ -1006,7 +817,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1006
817
|
}, "onFinalTranscript"),
|
|
1007
818
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
1008
819
|
});
|
|
1009
|
-
(0,
|
|
820
|
+
(0, import_react2.useEffect)(() => {
|
|
1010
821
|
if (!speech.isListening) setInterim("");
|
|
1011
822
|
}, [
|
|
1012
823
|
speech.isListening
|
|
@@ -1033,9 +844,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1033
844
|
__name(toggleMic, "toggleMic");
|
|
1034
845
|
const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
|
|
1035
846
|
const showMic = enableSpeech && speech.isSupported;
|
|
1036
|
-
|
|
847
|
+
const showStop = isStreaming && onStop !== void 0;
|
|
848
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", {
|
|
1037
849
|
className
|
|
1038
|
-
}, /* @__PURE__ */
|
|
850
|
+
}, /* @__PURE__ */ import_react2.default.createElement("textarea", {
|
|
1039
851
|
className: textareaClassName,
|
|
1040
852
|
rows,
|
|
1041
853
|
value: displayValue,
|
|
@@ -1046,12 +858,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1046
858
|
}, "onChange"),
|
|
1047
859
|
onKeyDown: handleKeyDown,
|
|
1048
860
|
placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
|
|
1049
|
-
}), /* @__PURE__ */
|
|
861
|
+
}), /* @__PURE__ */ import_react2.default.createElement("button", {
|
|
1050
862
|
type: "button",
|
|
1051
863
|
className: sendButtonClassName,
|
|
1052
864
|
onClick: submit,
|
|
1053
865
|
disabled: disabled || !value.trim()
|
|
1054
|
-
}, sendLabel),
|
|
866
|
+
}, sendLabel), showStop ? /* @__PURE__ */ import_react2.default.createElement("button", {
|
|
867
|
+
type: "button",
|
|
868
|
+
className: stopButtonClassName,
|
|
869
|
+
onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
|
|
870
|
+
"aria-label": "Stop generating"
|
|
871
|
+
}, stopLabel) : null, showMic ? /* @__PURE__ */ import_react2.default.createElement("button", {
|
|
1055
872
|
type: "button",
|
|
1056
873
|
className: micButtonClassName,
|
|
1057
874
|
onClick: toggleMic,
|
|
@@ -1064,223 +881,393 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
1064
881
|
__name(ChatInput, "ChatInput");
|
|
1065
882
|
|
|
1066
883
|
// src/components/message-item.tsx
|
|
1067
|
-
var import_ai = require("ai");
|
|
1068
884
|
var import_react5 = __toESM(require("react"), 1);
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
}, copyableText ? /* @__PURE__ */ import_react5.default.createElement(CopyButton, {
|
|
1077
|
-
text: copyableText,
|
|
1078
|
-
className: classNames?.actionButton
|
|
1079
|
-
}) : null, onFork ? /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1080
|
-
type: "button",
|
|
1081
|
-
onClick: /* @__PURE__ */ __name(() => void onFork(message.id), "onClick"),
|
|
1082
|
-
className: classNames?.actionButton,
|
|
1083
|
-
title: "Fork thread from this message"
|
|
1084
|
-
}, "Fork") : null, !isUser && regeneratable && onRegenerate ? /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1085
|
-
type: "button",
|
|
1086
|
-
onClick: /* @__PURE__ */ __name(() => void onRegenerate(), "onClick"),
|
|
1087
|
-
className: classNames?.actionButton,
|
|
1088
|
-
title: "Regenerate response"
|
|
1089
|
-
}, "Regenerate") : null, !isUser && usage ? /* @__PURE__ */ import_react5.default.createElement(UsageInline, {
|
|
1090
|
-
usage,
|
|
1091
|
-
className: classNames?.usage
|
|
1092
|
-
}) : null, createdAt ? /* @__PURE__ */ import_react5.default.createElement(TimestampInline, {
|
|
1093
|
-
createdAt,
|
|
1094
|
-
className: classNames?.timestamp
|
|
1095
|
-
}) : null);
|
|
1096
|
-
if (isUser && editable && onEditSubmit) {
|
|
1097
|
-
return /* @__PURE__ */ import_react5.default.createElement(EditableUserBubble, {
|
|
1098
|
-
message,
|
|
1099
|
-
onSubmit: onEditSubmit,
|
|
1100
|
-
renderText,
|
|
1101
|
-
classNames,
|
|
1102
|
-
actions: actionRow
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
const className = [
|
|
1106
|
-
classNames?.root,
|
|
1107
|
-
classNames?.byRole?.[message.role]
|
|
1108
|
-
].filter((entry) => entry).join(" ");
|
|
1109
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1110
|
-
className: className === "" ? void 0 : className,
|
|
1111
|
-
"data-role": message.role,
|
|
1112
|
-
"data-streaming": isStreaming ? "true" : void 0
|
|
1113
|
-
}, /* @__PURE__ */ import_react5.default.createElement("div", null, renderParts(message, parts, {
|
|
1114
|
-
renderText,
|
|
1115
|
-
renderToolPart,
|
|
1116
|
-
renderToolGroup,
|
|
1117
|
-
classNames,
|
|
1118
|
-
isStreaming
|
|
1119
|
-
})), actionRow);
|
|
1120
|
-
}
|
|
1121
|
-
__name(MessageItem, "MessageItem");
|
|
1122
|
-
function renderParts(message, parts, ctx) {
|
|
885
|
+
|
|
886
|
+
// src/transcript/use-chat-transcript.ts
|
|
887
|
+
var import_react4 = require("react");
|
|
888
|
+
|
|
889
|
+
// src/transcript/model.ts
|
|
890
|
+
var import_ai = require("ai");
|
|
891
|
+
function buildTranscriptBlocks(message, options) {
|
|
1123
892
|
const blocks = [];
|
|
1124
893
|
let toolBuffer = [];
|
|
1125
|
-
let
|
|
894
|
+
let retrievalBuffer = [];
|
|
895
|
+
let fileBuffer = [];
|
|
896
|
+
let toolCounter = 0;
|
|
1126
897
|
let textCounter = 0;
|
|
898
|
+
let fileCounter = 0;
|
|
899
|
+
let reasoningCounter = 0;
|
|
900
|
+
let sourcesCounter = 0;
|
|
901
|
+
let elicitationCounter = 0;
|
|
1127
902
|
function flushTools() {
|
|
1128
903
|
if (toolBuffer.length === 0) {
|
|
1129
904
|
return;
|
|
1130
905
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
}
|
|
1137
|
-
for (const part of toolBuffer) {
|
|
1138
|
-
const partKey = `${message.id}-${part.toolCallId}`;
|
|
1139
|
-
blocks.push(/* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, {
|
|
1140
|
-
key: partKey
|
|
1141
|
-
}, ctx.renderToolPart(part, partKey)));
|
|
1142
|
-
}
|
|
1143
|
-
}
|
|
906
|
+
blocks.push({
|
|
907
|
+
kind: "tools",
|
|
908
|
+
key: `${message.id}-tools-${toolCounter++}`,
|
|
909
|
+
parts: toolBuffer,
|
|
910
|
+
calls: toolBuffer.map((part) => buildToolCall(part, options.approval))
|
|
911
|
+
});
|
|
1144
912
|
toolBuffer = [];
|
|
1145
913
|
}
|
|
1146
914
|
__name(flushTools, "flushTools");
|
|
1147
|
-
|
|
915
|
+
function flushSources() {
|
|
916
|
+
if (retrievalBuffer.length === 0) {
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
blocks.push(buildSourcesBlock(`${message.id}-sources-${sourcesCounter++}`, retrievalBuffer));
|
|
920
|
+
retrievalBuffer = [];
|
|
921
|
+
}
|
|
922
|
+
__name(flushSources, "flushSources");
|
|
923
|
+
function flushFiles() {
|
|
924
|
+
if (fileBuffer.length === 0) {
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
927
|
+
blocks.push({
|
|
928
|
+
kind: "files",
|
|
929
|
+
key: `${message.id}-files-${fileCounter++}`,
|
|
930
|
+
files: fileBuffer.map((part) => ({
|
|
931
|
+
url: part.url,
|
|
932
|
+
mediaType: part.mediaType,
|
|
933
|
+
filename: part.filename ?? null,
|
|
934
|
+
isImage: part.mediaType.startsWith("image/")
|
|
935
|
+
}))
|
|
936
|
+
});
|
|
937
|
+
fileBuffer = [];
|
|
938
|
+
}
|
|
939
|
+
__name(flushFiles, "flushFiles");
|
|
940
|
+
function flushAll() {
|
|
941
|
+
flushTools();
|
|
942
|
+
flushSources();
|
|
943
|
+
flushFiles();
|
|
944
|
+
}
|
|
945
|
+
__name(flushAll, "flushAll");
|
|
946
|
+
for (const part of message.parts ?? []) {
|
|
1148
947
|
if ((0, import_ai.isToolUIPart)(part)) {
|
|
948
|
+
const elicitation = options.elicitation;
|
|
949
|
+
if (elicitation !== void 0) {
|
|
950
|
+
const request = readElicitationRequest(part);
|
|
951
|
+
if (request !== null) {
|
|
952
|
+
flushAll();
|
|
953
|
+
blocks.push(buildElicitationBlock(`${message.id}-elicitation-${elicitationCounter++}`, part, request, elicitation));
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
if (options.sources === true && readPassages(part) !== null) {
|
|
958
|
+
flushTools();
|
|
959
|
+
retrievalBuffer.push(part);
|
|
960
|
+
continue;
|
|
961
|
+
}
|
|
962
|
+
flushSources();
|
|
963
|
+
flushFiles();
|
|
1149
964
|
toolBuffer.push(part);
|
|
1150
965
|
continue;
|
|
1151
966
|
}
|
|
1152
|
-
|
|
967
|
+
if ((0, import_ai.isFileUIPart)(part)) {
|
|
968
|
+
flushTools();
|
|
969
|
+
flushSources();
|
|
970
|
+
fileBuffer.push(part);
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
flushAll();
|
|
1153
974
|
if ((0, import_ai.isTextUIPart)(part)) {
|
|
1154
|
-
|
|
1155
|
-
|
|
975
|
+
blocks.push({
|
|
976
|
+
kind: "text",
|
|
977
|
+
key: `${message.id}-text-${textCounter++}`,
|
|
978
|
+
text: part.text,
|
|
979
|
+
isStreaming: part.state === "streaming"
|
|
980
|
+
});
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
983
|
+
if ((0, import_ai.isReasoningUIPart)(part)) {
|
|
984
|
+
const key = `${message.id}-reasoning-${reasoningCounter++}`;
|
|
985
|
+
const isStreaming = part.state === "streaming";
|
|
986
|
+
blocks.push({
|
|
987
|
+
kind: "reasoning",
|
|
1156
988
|
key,
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
989
|
+
text: part.text,
|
|
990
|
+
isStreaming,
|
|
991
|
+
isOpen: options.isReasoningOpen(key, isStreaming),
|
|
992
|
+
toggle: /* @__PURE__ */ __name((open) => options.toggleReasoning(key, open), "toggle")
|
|
993
|
+
});
|
|
1161
994
|
}
|
|
1162
995
|
}
|
|
1163
|
-
|
|
996
|
+
flushAll();
|
|
1164
997
|
return blocks;
|
|
1165
998
|
}
|
|
1166
|
-
__name(
|
|
1167
|
-
function
|
|
1168
|
-
const
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
const
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
999
|
+
__name(buildTranscriptBlocks, "buildTranscriptBlocks");
|
|
1000
|
+
function buildSourcesBlock(key, parts) {
|
|
1001
|
+
const byId = /* @__PURE__ */ new Map();
|
|
1002
|
+
let query = null;
|
|
1003
|
+
let passageCount = 0;
|
|
1004
|
+
for (const part of parts) {
|
|
1005
|
+
query ??= readQuery(part);
|
|
1006
|
+
for (const passage of readPassages(part) ?? []) {
|
|
1007
|
+
passageCount++;
|
|
1008
|
+
const id = passage.source ?? passage.id;
|
|
1009
|
+
const existing = byId.get(id);
|
|
1010
|
+
if (existing) {
|
|
1011
|
+
existing.passageCount++;
|
|
1012
|
+
existing.topScore = Math.max(existing.topScore, passage.score);
|
|
1013
|
+
existing.passages.push(passage);
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
byId.set(id, {
|
|
1017
|
+
id,
|
|
1018
|
+
label: passage.source ?? passage.id,
|
|
1019
|
+
passageCount: 1,
|
|
1020
|
+
topScore: passage.score,
|
|
1021
|
+
passages: [
|
|
1022
|
+
passage
|
|
1023
|
+
]
|
|
1024
|
+
});
|
|
1185
1025
|
}
|
|
1186
|
-
setIsEditing(false);
|
|
1187
|
-
await onSubmit(next);
|
|
1188
|
-
}
|
|
1189
|
-
__name(handleSave, "handleSave");
|
|
1190
|
-
if (!isEditing) {
|
|
1191
|
-
const rootClass = [
|
|
1192
|
-
classNames?.root,
|
|
1193
|
-
classNames?.byRole?.user
|
|
1194
|
-
].filter((entry) => entry).join(" ");
|
|
1195
|
-
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1196
|
-
className: rootClass === "" ? void 0 : rootClass,
|
|
1197
|
-
"data-role": "user"
|
|
1198
|
-
}, /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1199
|
-
className: classNames?.text
|
|
1200
|
-
}, renderText ? renderText(initialText, {
|
|
1201
|
-
isStreaming: false
|
|
1202
|
-
}) : initialText), /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1203
|
-
type: "button",
|
|
1204
|
-
onClick: /* @__PURE__ */ __name(() => {
|
|
1205
|
-
setDraft(initialText);
|
|
1206
|
-
setIsEditing(true);
|
|
1207
|
-
}, "onClick"),
|
|
1208
|
-
className: classNames?.actionButton,
|
|
1209
|
-
title: "Edit & resubmit"
|
|
1210
|
-
}, "Edit"), actions);
|
|
1211
1026
|
}
|
|
1212
|
-
return
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1027
|
+
return {
|
|
1028
|
+
kind: "sources",
|
|
1029
|
+
key,
|
|
1030
|
+
query,
|
|
1031
|
+
sources: [
|
|
1032
|
+
...byId.values()
|
|
1033
|
+
],
|
|
1034
|
+
passageCount
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
__name(buildSourcesBlock, "buildSourcesBlock");
|
|
1038
|
+
function isSettled(state) {
|
|
1039
|
+
return state === "output-available" || state === "output-error" || state === "output-denied";
|
|
1040
|
+
}
|
|
1041
|
+
__name(isSettled, "isSettled");
|
|
1042
|
+
function toolKind(part) {
|
|
1043
|
+
const metadata = part.toolMetadata;
|
|
1044
|
+
return isRecord(metadata) && typeof metadata.toolKind === "string" ? metadata.toolKind : void 0;
|
|
1045
|
+
}
|
|
1046
|
+
__name(toolKind, "toolKind");
|
|
1047
|
+
function isAwaitingApproval(part) {
|
|
1048
|
+
if (part.state === "approval-requested") {
|
|
1049
|
+
return true;
|
|
1050
|
+
}
|
|
1051
|
+
if (part.state !== "input-available") {
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
return toolKind(part) === "action" && readElicitationRequest(part) === null;
|
|
1055
|
+
}
|
|
1056
|
+
__name(isAwaitingApproval, "isAwaitingApproval");
|
|
1057
|
+
function buildToolCall(part, options) {
|
|
1058
|
+
const toolCallId = part.toolCallId;
|
|
1059
|
+
const awaiting = isAwaitingApproval(part);
|
|
1060
|
+
const isSubmitting = awaiting && options?.isSubmitting(toolCallId) === true;
|
|
1061
|
+
return {
|
|
1062
|
+
part,
|
|
1063
|
+
toolCallId,
|
|
1064
|
+
name: (0, import_ai.getToolName)(part),
|
|
1065
|
+
isAwaitingApproval: awaiting,
|
|
1066
|
+
approve: {
|
|
1067
|
+
available: awaiting && options?.canApprove === true,
|
|
1068
|
+
isSubmitting,
|
|
1069
|
+
run: /* @__PURE__ */ __name(() => options?.approve(toolCallId), "run")
|
|
1070
|
+
},
|
|
1071
|
+
reject: {
|
|
1072
|
+
available: awaiting && options?.canReject === true,
|
|
1073
|
+
isSubmitting,
|
|
1074
|
+
run: /* @__PURE__ */ __name(() => options?.reject(toolCallId), "run")
|
|
1075
|
+
},
|
|
1076
|
+
error: options?.errorOf(toolCallId) ?? null
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
__name(buildToolCall, "buildToolCall");
|
|
1080
|
+
function readElicitationRequest(part) {
|
|
1081
|
+
const input = part.input;
|
|
1082
|
+
if (!isRecord(input) || !Array.isArray(input.questions) || input.questions.length === 0) {
|
|
1083
|
+
return null;
|
|
1084
|
+
}
|
|
1085
|
+
const questions = [];
|
|
1086
|
+
for (const candidate of input.questions) {
|
|
1087
|
+
if (!isRecord(candidate) || typeof candidate.id !== "string" || typeof candidate.prompt !== "string" || !Array.isArray(candidate.options) || candidate.options.length === 0) {
|
|
1088
|
+
return null;
|
|
1089
|
+
}
|
|
1090
|
+
const options = [];
|
|
1091
|
+
for (const option of candidate.options) {
|
|
1092
|
+
if (!isRecord(option) || typeof option.value !== "string" || typeof option.label !== "string") {
|
|
1093
|
+
return null;
|
|
1226
1094
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1095
|
+
options.push({
|
|
1096
|
+
value: option.value,
|
|
1097
|
+
label: option.label,
|
|
1098
|
+
hotkey: typeof option.hotkey === "string" ? option.hotkey : null
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
questions.push({
|
|
1102
|
+
id: candidate.id,
|
|
1103
|
+
prompt: candidate.prompt,
|
|
1104
|
+
multiple: candidate.multiple === true,
|
|
1105
|
+
defaults: Array.isArray(candidate.defaults) ? candidate.defaults.filter((value) => typeof value === "string") : [],
|
|
1106
|
+
options
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
return {
|
|
1110
|
+
preamble: typeof input.preamble === "string" ? input.preamble : null,
|
|
1111
|
+
questions
|
|
1112
|
+
};
|
|
1240
1113
|
}
|
|
1241
|
-
__name(
|
|
1242
|
-
function
|
|
1243
|
-
const
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1114
|
+
__name(readElicitationRequest, "readElicitationRequest");
|
|
1115
|
+
function readElicitationOutcome(part) {
|
|
1116
|
+
const output = part.output;
|
|
1117
|
+
if (!isRecord(output) || !isRecord(output.answers) || typeof output.skipped !== "boolean") {
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
const answers = {};
|
|
1121
|
+
for (const [id, values] of Object.entries(output.answers)) {
|
|
1122
|
+
answers[id] = Array.isArray(values) ? values.filter((value) => typeof value === "string") : [];
|
|
1123
|
+
}
|
|
1124
|
+
return {
|
|
1125
|
+
answers,
|
|
1126
|
+
skipped: output.skipped,
|
|
1127
|
+
defaulted: Array.isArray(output.defaulted) ? output.defaulted.filter((id) => typeof id === "string") : [],
|
|
1128
|
+
summary: typeof output.summary === "string" ? output.summary : null
|
|
1129
|
+
};
|
|
1130
|
+
}
|
|
1131
|
+
__name(readElicitationOutcome, "readElicitationOutcome");
|
|
1132
|
+
function buildElicitationBlock(key, part, request, options) {
|
|
1133
|
+
const toolCallId = part.toolCallId;
|
|
1134
|
+
const isPending = !isSettled(part.state);
|
|
1135
|
+
const outcome = isPending ? null : readElicitationOutcome(part);
|
|
1136
|
+
const questions = request.questions.map((question, index) => {
|
|
1137
|
+
const picked = isPending ? options.picked(toolCallId, question.id) : void 0;
|
|
1138
|
+
const selected = picked ?? outcome?.answers[question.id] ?? question.defaults;
|
|
1139
|
+
const pick = /* @__PURE__ */ __name((values) => options.pick(toolCallId, question.id, values), "pick");
|
|
1140
|
+
return {
|
|
1141
|
+
id: question.id,
|
|
1142
|
+
prompt: question.prompt,
|
|
1143
|
+
multiple: question.multiple,
|
|
1144
|
+
position: index + 1,
|
|
1145
|
+
selected,
|
|
1146
|
+
isPristine: isPending ? picked === void 0 : outcome?.defaulted.includes(question.id) ?? true,
|
|
1147
|
+
options: question.options.map((option) => ({
|
|
1148
|
+
value: option.value,
|
|
1149
|
+
label: option.label,
|
|
1150
|
+
hotkey: option.hotkey,
|
|
1151
|
+
isSelected: selected.includes(option.value),
|
|
1152
|
+
isDefault: question.defaults.includes(option.value),
|
|
1153
|
+
select: /* @__PURE__ */ __name(() => {
|
|
1154
|
+
if (!isPending) {
|
|
1155
|
+
return;
|
|
1156
|
+
}
|
|
1157
|
+
if (!question.multiple) {
|
|
1158
|
+
pick([
|
|
1159
|
+
option.value
|
|
1160
|
+
]);
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
pick(selected.includes(option.value) ? selected.filter((value) => value !== option.value) : [
|
|
1164
|
+
...selected,
|
|
1165
|
+
option.value
|
|
1166
|
+
]);
|
|
1167
|
+
}, "select")
|
|
1168
|
+
}))
|
|
1169
|
+
};
|
|
1170
|
+
});
|
|
1171
|
+
const isSubmitting = isPending && options.isSubmitting(toolCallId);
|
|
1172
|
+
return {
|
|
1173
|
+
kind: "elicitation",
|
|
1174
|
+
key,
|
|
1175
|
+
toolCallId,
|
|
1176
|
+
preamble: request.preamble,
|
|
1177
|
+
questions,
|
|
1178
|
+
questionCount: questions.length,
|
|
1179
|
+
isPending,
|
|
1180
|
+
outcome,
|
|
1181
|
+
error: options.errorOf(toolCallId),
|
|
1182
|
+
answer: {
|
|
1183
|
+
available: isPending && options.canAnswer,
|
|
1184
|
+
isSubmitting,
|
|
1185
|
+
run: /* @__PURE__ */ __name(() => options.answer(toolCallId), "run")
|
|
1186
|
+
},
|
|
1187
|
+
skip: {
|
|
1188
|
+
available: isPending && options.canSkip,
|
|
1189
|
+
isSubmitting,
|
|
1190
|
+
run: /* @__PURE__ */ __name(() => options.skip(toolCallId), "run")
|
|
1250
1191
|
}
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
__name(buildElicitationBlock, "buildElicitationBlock");
|
|
1195
|
+
function isRecord(value) {
|
|
1196
|
+
return typeof value === "object" && value !== null;
|
|
1197
|
+
}
|
|
1198
|
+
__name(isRecord, "isRecord");
|
|
1199
|
+
function readQuery(part) {
|
|
1200
|
+
const input = part.input;
|
|
1201
|
+
if (!isRecord(input) || typeof input.query !== "string") {
|
|
1202
|
+
return null;
|
|
1251
1203
|
}
|
|
1252
|
-
|
|
1253
|
-
return /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1254
|
-
type: "button",
|
|
1255
|
-
onClick: /* @__PURE__ */ __name(() => void handleCopy(), "onClick"),
|
|
1256
|
-
className,
|
|
1257
|
-
title: copied ? "Copied!" : "Copy message text"
|
|
1258
|
-
}, copied ? "Copied" : "Copy");
|
|
1204
|
+
return input.query;
|
|
1259
1205
|
}
|
|
1260
|
-
__name(
|
|
1261
|
-
function
|
|
1262
|
-
const
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1206
|
+
__name(readQuery, "readQuery");
|
|
1207
|
+
function readPassages(part) {
|
|
1208
|
+
const output = part.output;
|
|
1209
|
+
if (!isRecord(output) || !Array.isArray(output.passages) || output.passages.length === 0) {
|
|
1210
|
+
return null;
|
|
1211
|
+
}
|
|
1212
|
+
const passages = [];
|
|
1213
|
+
for (const candidate of output.passages) {
|
|
1214
|
+
if (!isRecord(candidate) || typeof candidate.id !== "string" || typeof candidate.text !== "string") {
|
|
1215
|
+
return null;
|
|
1216
|
+
}
|
|
1217
|
+
passages.push({
|
|
1218
|
+
id: candidate.id,
|
|
1219
|
+
text: candidate.text,
|
|
1220
|
+
score: typeof candidate.score === "number" ? candidate.score : 0,
|
|
1221
|
+
...typeof candidate.source === "string" ? {
|
|
1222
|
+
source: candidate.source
|
|
1223
|
+
} : {},
|
|
1224
|
+
...isRecord(candidate.metadata) ? {
|
|
1225
|
+
metadata: candidate.metadata
|
|
1226
|
+
} : {}
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
return passages;
|
|
1230
|
+
}
|
|
1231
|
+
__name(readPassages, "readPassages");
|
|
1232
|
+
function extractMessageText(parts) {
|
|
1233
|
+
const out = [];
|
|
1234
|
+
for (const part of parts ?? []) {
|
|
1235
|
+
if ((0, import_ai.isTextUIPart)(part)) {
|
|
1236
|
+
out.push(part.text);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
return out.join("\n\n").trim();
|
|
1267
1240
|
}
|
|
1268
|
-
__name(
|
|
1269
|
-
function
|
|
1270
|
-
const
|
|
1271
|
-
|
|
1241
|
+
__name(extractMessageText, "extractMessageText");
|
|
1242
|
+
function describeUsage(usage) {
|
|
1243
|
+
const totalTokens = usage.inputTokens + usage.outputTokens;
|
|
1244
|
+
return {
|
|
1245
|
+
...usage,
|
|
1246
|
+
totalTokens,
|
|
1247
|
+
costLabel: formatCostUsd(usage.costUsd),
|
|
1248
|
+
tokensLabel: formatTokensShort(totalTokens)
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
__name(describeUsage, "describeUsage");
|
|
1252
|
+
function describeTimestamp(createdAt) {
|
|
1253
|
+
if (createdAt == null) {
|
|
1272
1254
|
return null;
|
|
1273
1255
|
}
|
|
1274
|
-
const
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
}
|
|
1278
|
-
return
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1256
|
+
const date = new Date(createdAt);
|
|
1257
|
+
if (Number.isNaN(date.getTime())) {
|
|
1258
|
+
return null;
|
|
1259
|
+
}
|
|
1260
|
+
return {
|
|
1261
|
+
iso: createdAt,
|
|
1262
|
+
date,
|
|
1263
|
+
relative: formatRelativeTime(date),
|
|
1264
|
+
absolute: date.toLocaleString(void 0, {
|
|
1265
|
+
dateStyle: "medium",
|
|
1266
|
+
timeStyle: "short"
|
|
1267
|
+
})
|
|
1268
|
+
};
|
|
1282
1269
|
}
|
|
1283
|
-
__name(
|
|
1270
|
+
__name(describeTimestamp, "describeTimestamp");
|
|
1284
1271
|
var RELATIVE_TIME = new Intl.RelativeTimeFormat(void 0, {
|
|
1285
1272
|
numeric: "auto",
|
|
1286
1273
|
style: "narrow"
|
|
@@ -1319,6 +1306,9 @@ function formatTokensShort(n) {
|
|
|
1319
1306
|
}
|
|
1320
1307
|
__name(formatTokensShort, "formatTokensShort");
|
|
1321
1308
|
function formatCostUsd(cost) {
|
|
1309
|
+
if (cost === null) {
|
|
1310
|
+
return "\u2014";
|
|
1311
|
+
}
|
|
1322
1312
|
if (cost === 0) {
|
|
1323
1313
|
return "$0";
|
|
1324
1314
|
}
|
|
@@ -1331,142 +1321,799 @@ function formatCostUsd(cost) {
|
|
|
1331
1321
|
return `$${cost.toFixed(2)}`;
|
|
1332
1322
|
}
|
|
1333
1323
|
__name(formatCostUsd, "formatCostUsd");
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1324
|
+
|
|
1325
|
+
// src/transcript/use-stick-to-bottom.ts
|
|
1326
|
+
var import_react3 = require("react");
|
|
1327
|
+
function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
1328
|
+
const containerRef = (0, import_react3.useRef)(null);
|
|
1329
|
+
const [isAtBottom, setIsAtBottom] = (0, import_react3.useState)(true);
|
|
1330
|
+
const measure = (0, import_react3.useCallback)(() => {
|
|
1331
|
+
const element = containerRef.current;
|
|
1332
|
+
if (!element) {
|
|
1333
|
+
return;
|
|
1339
1334
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1335
|
+
const distance = element.scrollHeight - element.scrollTop - element.clientHeight;
|
|
1336
|
+
setIsAtBottom(distance <= threshold);
|
|
1337
|
+
}, [
|
|
1338
|
+
threshold
|
|
1339
|
+
]);
|
|
1340
|
+
const scrollToBottom = (0, import_react3.useCallback)(() => {
|
|
1341
|
+
const element = containerRef.current;
|
|
1342
|
+
if (!element) {
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1345
|
+
element.scrollTop = element.scrollHeight;
|
|
1346
|
+
setIsAtBottom(true);
|
|
1347
|
+
}, []);
|
|
1348
|
+
const setContainer = (0, import_react3.useCallback)((element) => {
|
|
1349
|
+
containerRef.current = element;
|
|
1350
|
+
if (element) {
|
|
1351
|
+
measure();
|
|
1352
|
+
}
|
|
1353
|
+
}, [
|
|
1354
|
+
measure
|
|
1355
|
+
]);
|
|
1356
|
+
(0, import_react3.useEffect)(() => {
|
|
1357
|
+
if (isAtBottom) {
|
|
1358
|
+
scrollToBottom();
|
|
1359
|
+
}
|
|
1360
|
+
}, [
|
|
1361
|
+
contentKey,
|
|
1362
|
+
isAtBottom,
|
|
1363
|
+
scrollToBottom
|
|
1364
|
+
]);
|
|
1365
|
+
const getContainerProps = (0, import_react3.useCallback)(() => ({
|
|
1366
|
+
ref: setContainer,
|
|
1367
|
+
onScroll: measure
|
|
1368
|
+
}), [
|
|
1369
|
+
setContainer,
|
|
1370
|
+
measure
|
|
1371
|
+
]);
|
|
1372
|
+
return {
|
|
1373
|
+
isAtBottom,
|
|
1374
|
+
showJumpToLatest: !isAtBottom,
|
|
1375
|
+
scrollToBottom,
|
|
1376
|
+
getContainerProps
|
|
1377
|
+
};
|
|
1342
1378
|
}
|
|
1343
|
-
__name(
|
|
1379
|
+
__name(useStickToBottom, "useStickToBottom");
|
|
1344
1380
|
|
|
1345
|
-
// src/
|
|
1346
|
-
var import_react6 = __toESM(require("react"), 1);
|
|
1381
|
+
// src/transcript/use-chat-transcript.ts
|
|
1347
1382
|
var DEFAULT_VISIBLE_COUNT = 50;
|
|
1348
|
-
var
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1383
|
+
var DEFAULT_LOAD_EARLIER_STEP = 50;
|
|
1384
|
+
var DEFAULT_COPY_RESET_MS = 1500;
|
|
1385
|
+
function useChatTranscript(options) {
|
|
1386
|
+
const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
|
|
1387
|
+
const latest = (0, import_react4.useRef)(options);
|
|
1388
|
+
latest.current = options;
|
|
1389
|
+
const [visibleCount, setVisibleCount] = (0, import_react4.useState)(initialVisibleCount);
|
|
1390
|
+
const [stopRequested, setStopRequested] = (0, import_react4.useState)(false);
|
|
1391
|
+
const isStreaming = status === "streaming";
|
|
1392
|
+
const isBusy = status === "submitted" || isStreaming;
|
|
1393
|
+
const isEmpty = messages.length === 0;
|
|
1394
|
+
(0, import_react4.useEffect)(() => {
|
|
1395
|
+
if (!isBusy) {
|
|
1396
|
+
setStopRequested(false);
|
|
1397
|
+
}
|
|
1398
|
+
}, [
|
|
1399
|
+
isBusy
|
|
1400
|
+
]);
|
|
1356
1401
|
const lastMessage = messages.at(-1);
|
|
1357
|
-
const
|
|
1358
|
-
const
|
|
1402
|
+
const lastAssistant = findLastAssistant(messages);
|
|
1403
|
+
const streamingMessageId = isStreaming ? lastAssistant?.id ?? null : null;
|
|
1359
1404
|
const visibleStart = Math.max(0, messages.length - visibleCount);
|
|
1360
|
-
const
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1405
|
+
const items = useTranscriptItems({
|
|
1406
|
+
messages,
|
|
1407
|
+
visibleStart,
|
|
1408
|
+
streamingMessageId,
|
|
1409
|
+
options
|
|
1410
|
+
});
|
|
1411
|
+
const stop = (0, import_react4.useCallback)(() => {
|
|
1412
|
+
const current = latest.current;
|
|
1413
|
+
if (current.status !== "submitted" && current.status !== "streaming") {
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
setStopRequested(true);
|
|
1417
|
+
void current.onStop?.();
|
|
1418
|
+
}, []);
|
|
1419
|
+
const loadEarlier = (0, import_react4.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
|
|
1420
|
+
const showAll = (0, import_react4.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
|
|
1421
|
+
const scroll = useStickToBottom({
|
|
1422
|
+
contentKey: contentSignature(messages),
|
|
1423
|
+
...stickToBottomThreshold !== void 0 ? {
|
|
1424
|
+
threshold: stickToBottomThreshold
|
|
1425
|
+
} : {}
|
|
1426
|
+
});
|
|
1427
|
+
const showTypingIndicator = isBusy && lastMessage?.role !== "assistant";
|
|
1428
|
+
return {
|
|
1429
|
+
items,
|
|
1430
|
+
status,
|
|
1431
|
+
isEmpty,
|
|
1432
|
+
isBusy,
|
|
1433
|
+
isStreaming,
|
|
1434
|
+
showEmptyState: isEmpty && status === "ready",
|
|
1435
|
+
showTypingIndicator,
|
|
1436
|
+
showFollowUps: status === "ready" && !showTypingIndicator && lastMessage?.role === "assistant" && !!followUps && followUps.length > 0,
|
|
1437
|
+
lastAssistantId: lastAssistant?.id ?? null,
|
|
1438
|
+
window: {
|
|
1439
|
+
visibleCount,
|
|
1440
|
+
hiddenCount: visibleStart,
|
|
1441
|
+
canLoadEarlier: visibleStart > 0,
|
|
1442
|
+
loadEarlier,
|
|
1443
|
+
showAll
|
|
1444
|
+
},
|
|
1445
|
+
scroll,
|
|
1446
|
+
stop: {
|
|
1447
|
+
available: isBusy && options.onStop !== void 0,
|
|
1448
|
+
isStopping: stopRequested,
|
|
1449
|
+
stop
|
|
1367
1450
|
}
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
__name(useChatTranscript, "useChatTranscript");
|
|
1454
|
+
function useTranscriptItem(options) {
|
|
1455
|
+
const { message, isStreaming = false } = options;
|
|
1456
|
+
const messagesRef = (0, import_react4.useRef)([
|
|
1457
|
+
message
|
|
1458
|
+
]);
|
|
1459
|
+
if (messagesRef.current[0] !== message) {
|
|
1460
|
+
messagesRef.current = [
|
|
1461
|
+
message
|
|
1462
|
+
];
|
|
1368
1463
|
}
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1464
|
+
const items = useTranscriptItems({
|
|
1465
|
+
messages: messagesRef.current,
|
|
1466
|
+
visibleStart: 0,
|
|
1467
|
+
streamingMessageId: isStreaming ? message.id : null,
|
|
1468
|
+
options
|
|
1469
|
+
});
|
|
1470
|
+
return items[0];
|
|
1471
|
+
}
|
|
1472
|
+
__name(useTranscriptItem, "useTranscriptItem");
|
|
1473
|
+
function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
|
|
1474
|
+
const [drafts, setDrafts] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
|
|
1475
|
+
const [copied, setCopied] = (0, import_react4.useState)(() => /* @__PURE__ */ new Set());
|
|
1476
|
+
const [openReasoning, setOpenReasoning] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
|
|
1477
|
+
const [picks, setPicks] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
|
|
1478
|
+
const [settling, setSettling] = (0, import_react4.useState)(() => /* @__PURE__ */ new Set());
|
|
1479
|
+
const [settleErrors, setSettleErrors] = (0, import_react4.useState)(() => /* @__PURE__ */ new Map());
|
|
1480
|
+
const latest = (0, import_react4.useRef)({
|
|
1481
|
+
options,
|
|
1482
|
+
drafts,
|
|
1483
|
+
picks,
|
|
1484
|
+
messages
|
|
1485
|
+
});
|
|
1486
|
+
latest.current = {
|
|
1487
|
+
options,
|
|
1488
|
+
drafts,
|
|
1489
|
+
picks,
|
|
1490
|
+
messages
|
|
1491
|
+
};
|
|
1492
|
+
const copyTimers = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
1493
|
+
(0, import_react4.useEffect)(() => () => {
|
|
1494
|
+
for (const timer of copyTimers.current.values()) {
|
|
1495
|
+
clearTimeout(timer);
|
|
1496
|
+
}
|
|
1497
|
+
copyTimers.current.clear();
|
|
1498
|
+
}, []);
|
|
1499
|
+
const callbackCache = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
1500
|
+
const reasoningToggleCache = (0, import_react4.useRef)(/* @__PURE__ */ new Map());
|
|
1501
|
+
const toggleReasoning = (0, import_react4.useCallback)((key, open) => {
|
|
1502
|
+
setOpenReasoning((current) => {
|
|
1503
|
+
const next = new Map(current);
|
|
1504
|
+
next.set(key, open ?? !(current.get(key) ?? false));
|
|
1505
|
+
return next;
|
|
1506
|
+
});
|
|
1507
|
+
}, []);
|
|
1508
|
+
const stableToggle = (0, import_react4.useCallback)((key) => {
|
|
1509
|
+
let cached = reasoningToggleCache.current.get(key);
|
|
1510
|
+
if (!cached) {
|
|
1511
|
+
cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
|
|
1512
|
+
reasoningToggleCache.current.set(key, cached);
|
|
1513
|
+
}
|
|
1514
|
+
return cached;
|
|
1515
|
+
}, [
|
|
1516
|
+
toggleReasoning
|
|
1517
|
+
]);
|
|
1518
|
+
const pick = (0, import_react4.useCallback)((toolCallId, questionId, values) => {
|
|
1519
|
+
setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
|
|
1520
|
+
}, []);
|
|
1521
|
+
const settle = (0, import_react4.useCallback)((toolCallId, send) => {
|
|
1522
|
+
setSettleErrors((current) => {
|
|
1523
|
+
if (!current.has(toolCallId)) {
|
|
1524
|
+
return current;
|
|
1525
|
+
}
|
|
1526
|
+
const next = new Map(current);
|
|
1527
|
+
next.delete(toolCallId);
|
|
1528
|
+
return next;
|
|
1529
|
+
});
|
|
1530
|
+
setSettling((current) => new Set(current).add(toolCallId));
|
|
1531
|
+
const failed = /* @__PURE__ */ __name((error) => {
|
|
1532
|
+
setSettling((current) => {
|
|
1533
|
+
const next = new Set(current);
|
|
1534
|
+
next.delete(toolCallId);
|
|
1535
|
+
return next;
|
|
1536
|
+
});
|
|
1537
|
+
setSettleErrors((current) => new Map(current).set(toolCallId, error instanceof Error ? error.message : "Could not settle this"));
|
|
1538
|
+
}, "failed");
|
|
1539
|
+
try {
|
|
1540
|
+
void Promise.resolve(send()).catch(failed);
|
|
1541
|
+
} catch (error) {
|
|
1542
|
+
failed(error);
|
|
1543
|
+
}
|
|
1544
|
+
}, []);
|
|
1545
|
+
const answer = (0, import_react4.useCallback)((toolCallId) => {
|
|
1546
|
+
const current = latest.current;
|
|
1547
|
+
const onAnswer = current.options.onAnswer;
|
|
1548
|
+
if (!onAnswer) {
|
|
1549
|
+
return;
|
|
1550
|
+
}
|
|
1551
|
+
settle(toolCallId, () => onAnswer(toolCallId, answersFor(current.picks, toolCallId)));
|
|
1552
|
+
}, [
|
|
1553
|
+
settle
|
|
1554
|
+
]);
|
|
1555
|
+
const skip = (0, import_react4.useCallback)((toolCallId) => {
|
|
1556
|
+
const onSkip = latest.current.options.onSkip;
|
|
1557
|
+
if (onSkip) {
|
|
1558
|
+
settle(toolCallId, () => onSkip(toolCallId));
|
|
1559
|
+
}
|
|
1560
|
+
}, [
|
|
1561
|
+
settle
|
|
1562
|
+
]);
|
|
1563
|
+
const approve = (0, import_react4.useCallback)((toolCallId) => {
|
|
1564
|
+
const onApprove = latest.current.options.onApprove;
|
|
1565
|
+
if (onApprove) {
|
|
1566
|
+
settle(toolCallId, () => onApprove(toolCallId));
|
|
1567
|
+
}
|
|
1568
|
+
}, [
|
|
1569
|
+
settle
|
|
1570
|
+
]);
|
|
1571
|
+
const reject = (0, import_react4.useCallback)((toolCallId) => {
|
|
1572
|
+
const onReject = latest.current.options.onReject;
|
|
1573
|
+
if (onReject) {
|
|
1574
|
+
settle(toolCallId, () => onReject(toolCallId));
|
|
1575
|
+
}
|
|
1576
|
+
}, [
|
|
1577
|
+
settle
|
|
1578
|
+
]);
|
|
1579
|
+
const callbacksFor = (0, import_react4.useCallback)((id) => {
|
|
1580
|
+
const cached = callbackCache.current.get(id);
|
|
1581
|
+
if (cached) {
|
|
1582
|
+
return cached;
|
|
1583
|
+
}
|
|
1584
|
+
const textFor = /* @__PURE__ */ __name(() => extractMessageText(latest.current.messages.find((message) => message.id === id)?.parts), "textFor");
|
|
1585
|
+
const created = {
|
|
1586
|
+
copy: /* @__PURE__ */ __name(() => {
|
|
1587
|
+
const text = textFor();
|
|
1588
|
+
if (!text) {
|
|
1589
|
+
return;
|
|
1590
|
+
}
|
|
1591
|
+
const write = latest.current.options.writeClipboard ?? ((value) => navigator.clipboard.writeText(value));
|
|
1592
|
+
void Promise.resolve().then(() => write(text)).then(() => {
|
|
1593
|
+
setCopied((current) => new Set(current).add(id));
|
|
1594
|
+
const existing = copyTimers.current.get(id);
|
|
1595
|
+
if (existing) {
|
|
1596
|
+
clearTimeout(existing);
|
|
1597
|
+
}
|
|
1598
|
+
copyTimers.current.set(id, setTimeout(() => {
|
|
1599
|
+
copyTimers.current.delete(id);
|
|
1600
|
+
setCopied((current) => {
|
|
1601
|
+
const next = new Set(current);
|
|
1602
|
+
next.delete(id);
|
|
1603
|
+
return next;
|
|
1604
|
+
});
|
|
1605
|
+
}, latest.current.options.copyResetMs ?? DEFAULT_COPY_RESET_MS));
|
|
1606
|
+
}).catch(() => {
|
|
1607
|
+
});
|
|
1608
|
+
}, "copy"),
|
|
1609
|
+
startEdit: /* @__PURE__ */ __name(() => {
|
|
1610
|
+
const text = textFor();
|
|
1611
|
+
setDrafts((current) => new Map(current).set(id, text));
|
|
1612
|
+
}, "startEdit"),
|
|
1613
|
+
cancelEdit: /* @__PURE__ */ __name(() => {
|
|
1614
|
+
setDrafts((current) => {
|
|
1615
|
+
const next = new Map(current);
|
|
1616
|
+
next.delete(id);
|
|
1617
|
+
return next;
|
|
1618
|
+
});
|
|
1619
|
+
}, "cancelEdit"),
|
|
1620
|
+
setDraft: /* @__PURE__ */ __name((value) => {
|
|
1621
|
+
setDrafts((current) => new Map(current).set(id, value));
|
|
1622
|
+
}, "setDraft"),
|
|
1623
|
+
save: /* @__PURE__ */ __name(() => {
|
|
1624
|
+
const next = (latest.current.drafts.get(id) ?? "").trim();
|
|
1625
|
+
if (!next) {
|
|
1626
|
+
return;
|
|
1627
|
+
}
|
|
1628
|
+
created.cancelEdit();
|
|
1629
|
+
void latest.current.options.onEditSubmit?.(id, next);
|
|
1630
|
+
}, "save"),
|
|
1631
|
+
fork: /* @__PURE__ */ __name(() => {
|
|
1632
|
+
void latest.current.options.onFork?.(id);
|
|
1633
|
+
}, "fork"),
|
|
1634
|
+
regenerate: /* @__PURE__ */ __name(() => {
|
|
1635
|
+
void latest.current.options.onRegenerate?.(id);
|
|
1636
|
+
}, "regenerate"),
|
|
1637
|
+
textareaRef: /* @__PURE__ */ __name((element) => {
|
|
1638
|
+
if (!element) {
|
|
1639
|
+
return;
|
|
1640
|
+
}
|
|
1641
|
+
element.focus();
|
|
1642
|
+
element.setSelectionRange(element.value.length, element.value.length);
|
|
1643
|
+
}, "textareaRef"),
|
|
1644
|
+
onTextareaChange: /* @__PURE__ */ __name((event) => {
|
|
1645
|
+
created.setDraft(event.target.value);
|
|
1646
|
+
}, "onTextareaChange"),
|
|
1647
|
+
onTextareaKeyDown: /* @__PURE__ */ __name((event) => {
|
|
1648
|
+
if (event.key === "Enter" && !event.shiftKey) {
|
|
1649
|
+
event.preventDefault();
|
|
1650
|
+
created.save();
|
|
1651
|
+
}
|
|
1652
|
+
if (event.key === "Escape") {
|
|
1653
|
+
event.preventDefault();
|
|
1654
|
+
created.cancelEdit();
|
|
1655
|
+
}
|
|
1656
|
+
}, "onTextareaKeyDown")
|
|
1657
|
+
};
|
|
1658
|
+
callbackCache.current.set(id, created);
|
|
1659
|
+
return created;
|
|
1660
|
+
}, []);
|
|
1661
|
+
const lastAssistantId = findLastAssistant(messages)?.id ?? null;
|
|
1662
|
+
const items = [];
|
|
1663
|
+
for (let index = visibleStart; index < messages.length; index++) {
|
|
1664
|
+
const message = messages[index];
|
|
1665
|
+
if (!message) {
|
|
1666
|
+
continue;
|
|
1667
|
+
}
|
|
1668
|
+
const callbacks = callbacksFor(message.id);
|
|
1669
|
+
const draft = drafts.get(message.id);
|
|
1670
|
+
const isEditing = draft !== void 0;
|
|
1671
|
+
const text = extractMessageText(message.parts);
|
|
1672
|
+
const usage = options.getUsage?.(message) ?? null;
|
|
1673
|
+
const isUser = message.role === "user";
|
|
1674
|
+
const isAssistant = message.role === "assistant";
|
|
1675
|
+
const isLastAssistant = isAssistant && message.id === lastAssistantId;
|
|
1676
|
+
items.push({
|
|
1677
|
+
id: message.id,
|
|
1382
1678
|
message,
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1679
|
+
role: message.role,
|
|
1680
|
+
index,
|
|
1681
|
+
isUser,
|
|
1682
|
+
isAssistant,
|
|
1683
|
+
isLastAssistant,
|
|
1684
|
+
isStreaming: message.id === streamingMessageId,
|
|
1685
|
+
blocks: buildTranscriptBlocks(message, {
|
|
1686
|
+
isReasoningOpen: /* @__PURE__ */ __name((key, isStreamingRun) => openReasoning.get(key) ?? isStreamingRun, "isReasoningOpen"),
|
|
1687
|
+
toggleReasoning: /* @__PURE__ */ __name((key, open) => stableToggle(key)(open), "toggleReasoning"),
|
|
1688
|
+
...options.sources !== void 0 ? {
|
|
1689
|
+
sources: options.sources
|
|
1690
|
+
} : {},
|
|
1691
|
+
...options.onAnswer !== void 0 ? {
|
|
1692
|
+
elicitation: {
|
|
1693
|
+
picked: /* @__PURE__ */ __name((toolCallId, questionId) => picks.get(pickKey(toolCallId, questionId)), "picked"),
|
|
1694
|
+
pick,
|
|
1695
|
+
canAnswer: true,
|
|
1696
|
+
canSkip: options.onSkip !== void 0,
|
|
1697
|
+
answer,
|
|
1698
|
+
skip,
|
|
1699
|
+
isSubmitting: /* @__PURE__ */ __name((toolCallId) => settling.has(toolCallId), "isSubmitting"),
|
|
1700
|
+
errorOf: /* @__PURE__ */ __name((toolCallId) => settleErrors.get(toolCallId) ?? null, "errorOf")
|
|
1701
|
+
}
|
|
1702
|
+
} : {},
|
|
1703
|
+
...options.onApprove !== void 0 || options.onReject !== void 0 ? {
|
|
1704
|
+
approval: {
|
|
1705
|
+
canApprove: options.onApprove !== void 0,
|
|
1706
|
+
canReject: options.onReject !== void 0,
|
|
1707
|
+
approve,
|
|
1708
|
+
reject,
|
|
1709
|
+
isSubmitting: /* @__PURE__ */ __name((toolCallId) => settling.has(toolCallId), "isSubmitting"),
|
|
1710
|
+
errorOf: /* @__PURE__ */ __name((toolCallId) => settleErrors.get(toolCallId) ?? null, "errorOf")
|
|
1711
|
+
}
|
|
1712
|
+
} : {}
|
|
1713
|
+
}),
|
|
1714
|
+
text,
|
|
1715
|
+
usage: usage ? describeUsage(usage) : null,
|
|
1716
|
+
timestamp: describeTimestamp(options.getCreatedAt?.(message)),
|
|
1717
|
+
copy: {
|
|
1718
|
+
available: text.length > 0,
|
|
1719
|
+
copied: copied.has(message.id),
|
|
1720
|
+
copy: callbacks.copy
|
|
1721
|
+
},
|
|
1722
|
+
edit: {
|
|
1723
|
+
available: isUser && options.editable === true && options.onEditSubmit !== void 0,
|
|
1724
|
+
isEditing,
|
|
1725
|
+
draft: draft ?? text,
|
|
1726
|
+
canSave: (draft ?? "").trim().length > 0,
|
|
1727
|
+
start: callbacks.startEdit,
|
|
1728
|
+
cancel: callbacks.cancelEdit,
|
|
1729
|
+
setDraft: callbacks.setDraft,
|
|
1730
|
+
save: callbacks.save,
|
|
1731
|
+
getTextareaProps: /* @__PURE__ */ __name(() => ({
|
|
1732
|
+
ref: callbacks.textareaRef,
|
|
1733
|
+
value: draft ?? text,
|
|
1734
|
+
onChange: callbacks.onTextareaChange,
|
|
1735
|
+
onKeyDown: callbacks.onTextareaKeyDown
|
|
1736
|
+
}), "getTextareaProps")
|
|
1737
|
+
},
|
|
1738
|
+
fork: {
|
|
1739
|
+
available: options.onFork !== void 0,
|
|
1740
|
+
run: callbacks.fork
|
|
1741
|
+
},
|
|
1742
|
+
regenerate: {
|
|
1743
|
+
available: isLastAssistant && options.regeneratable === true && options.onRegenerate !== void 0,
|
|
1744
|
+
run: callbacks.regenerate
|
|
1745
|
+
}
|
|
1417
1746
|
});
|
|
1418
|
-
}
|
|
1419
|
-
|
|
1420
|
-
}, typingIndicator ?? "Thinking\u2026") : null, error && onRetry ? /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1421
|
-
className: classNames?.error
|
|
1422
|
-
}, /* @__PURE__ */ import_react6.default.createElement("span", null, error.message || "The assistant didn't respond."), /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1423
|
-
type: "button",
|
|
1424
|
-
className: classNames?.errorRetry,
|
|
1425
|
-
onClick: /* @__PURE__ */ __name(() => void onRetry(), "onClick")
|
|
1426
|
-
}, "Retry")) : null, showFollowUps ? /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1427
|
-
className: classNames?.followUps
|
|
1428
|
-
}, followUps.map((text) => /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1429
|
-
key: text,
|
|
1430
|
-
type: "button",
|
|
1431
|
-
className: classNames?.followUpChip,
|
|
1432
|
-
onClick: /* @__PURE__ */ __name(() => onFollowUpClick(text), "onClick")
|
|
1433
|
-
}, text))) : null);
|
|
1747
|
+
}
|
|
1748
|
+
return items;
|
|
1434
1749
|
}
|
|
1435
|
-
__name(
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
},
|
|
1448
|
-
{
|
|
1449
|
-
tag: "pt-BR",
|
|
1450
|
-
label: "Portuguese (Brazil)"
|
|
1451
|
-
},
|
|
1452
|
-
{
|
|
1453
|
-
tag: "es-ES",
|
|
1454
|
-
label: "Spanish (Spain)"
|
|
1750
|
+
__name(useTranscriptItems, "useTranscriptItems");
|
|
1751
|
+
function pickKey(toolCallId, questionId) {
|
|
1752
|
+
return `${toolCallId}|${questionId}`;
|
|
1753
|
+
}
|
|
1754
|
+
__name(pickKey, "pickKey");
|
|
1755
|
+
function answersFor(picks, toolCallId) {
|
|
1756
|
+
const prefix = `${toolCallId}|`;
|
|
1757
|
+
const answers = {};
|
|
1758
|
+
for (const [key, values] of picks) {
|
|
1759
|
+
if (key.startsWith(prefix)) {
|
|
1760
|
+
answers[key.slice(prefix.length)] = values;
|
|
1761
|
+
}
|
|
1455
1762
|
}
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
const
|
|
1462
|
-
if (
|
|
1463
|
-
return
|
|
1763
|
+
return answers;
|
|
1764
|
+
}
|
|
1765
|
+
__name(answersFor, "answersFor");
|
|
1766
|
+
function findLastAssistant(messages) {
|
|
1767
|
+
for (let index = messages.length - 1; index >= 0; index--) {
|
|
1768
|
+
const message = messages[index];
|
|
1769
|
+
if (message?.role === "assistant") {
|
|
1770
|
+
return message;
|
|
1464
1771
|
}
|
|
1465
|
-
} catch {
|
|
1466
1772
|
}
|
|
1467
|
-
return
|
|
1773
|
+
return void 0;
|
|
1468
1774
|
}
|
|
1469
|
-
__name(
|
|
1775
|
+
__name(findLastAssistant, "findLastAssistant");
|
|
1776
|
+
function contentSignature(messages) {
|
|
1777
|
+
const last = messages.at(-1);
|
|
1778
|
+
if (!last) {
|
|
1779
|
+
return "0";
|
|
1780
|
+
}
|
|
1781
|
+
let length = 0;
|
|
1782
|
+
for (const part of last.parts ?? []) {
|
|
1783
|
+
if ("text" in part && typeof part.text === "string") {
|
|
1784
|
+
length += part.text.length;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
return `${messages.length}|${last.id}|${last.parts?.length ?? 0}|${length}`;
|
|
1788
|
+
}
|
|
1789
|
+
__name(contentSignature, "contentSignature");
|
|
1790
|
+
|
|
1791
|
+
// src/components/message-item.tsx
|
|
1792
|
+
function MessageItem({ message, isStreaming = false, editable, onEditSubmit, regeneratable, onRegenerate, onFork, usage, createdAt, ...slots }) {
|
|
1793
|
+
const item = useTranscriptItem({
|
|
1794
|
+
message,
|
|
1795
|
+
isStreaming,
|
|
1796
|
+
...editable !== void 0 ? {
|
|
1797
|
+
editable
|
|
1798
|
+
} : {},
|
|
1799
|
+
...onEditSubmit ? {
|
|
1800
|
+
onEditSubmit: /* @__PURE__ */ __name((_id, next) => onEditSubmit(next), "onEditSubmit")
|
|
1801
|
+
} : {},
|
|
1802
|
+
...onFork ? {
|
|
1803
|
+
onFork
|
|
1804
|
+
} : {},
|
|
1805
|
+
...regeneratable !== void 0 ? {
|
|
1806
|
+
regeneratable
|
|
1807
|
+
} : {},
|
|
1808
|
+
...onRegenerate ? {
|
|
1809
|
+
onRegenerate: /* @__PURE__ */ __name(() => onRegenerate(), "onRegenerate")
|
|
1810
|
+
} : {},
|
|
1811
|
+
...usage != null ? {
|
|
1812
|
+
getUsage: /* @__PURE__ */ __name(() => usage, "getUsage")
|
|
1813
|
+
} : {},
|
|
1814
|
+
...createdAt != null ? {
|
|
1815
|
+
getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
|
|
1816
|
+
} : {}
|
|
1817
|
+
});
|
|
1818
|
+
return /* @__PURE__ */ import_react5.default.createElement(MessageItemView, {
|
|
1819
|
+
item,
|
|
1820
|
+
...slots
|
|
1821
|
+
});
|
|
1822
|
+
}
|
|
1823
|
+
__name(MessageItem, "MessageItem");
|
|
1824
|
+
function MessageItemView({ item, ...slots }) {
|
|
1825
|
+
const { classNames } = slots;
|
|
1826
|
+
const actionRow = /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1827
|
+
className: classNames?.actions,
|
|
1828
|
+
"data-actions-for": item.role
|
|
1829
|
+
}, item.copy.available ? /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1830
|
+
type: "button",
|
|
1831
|
+
onClick: item.copy.copy,
|
|
1832
|
+
className: classNames?.actionButton,
|
|
1833
|
+
title: item.copy.copied ? "Copied!" : "Copy message text"
|
|
1834
|
+
}, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1835
|
+
type: "button",
|
|
1836
|
+
onClick: item.fork.run,
|
|
1837
|
+
className: classNames?.actionButton,
|
|
1838
|
+
title: "Fork thread from this message"
|
|
1839
|
+
}, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1840
|
+
type: "button",
|
|
1841
|
+
onClick: item.regenerate.run,
|
|
1842
|
+
className: classNames?.actionButton,
|
|
1843
|
+
title: "Regenerate response"
|
|
1844
|
+
}, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react5.default.createElement("span", {
|
|
1845
|
+
className: classNames?.usage,
|
|
1846
|
+
title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
|
|
1847
|
+
}, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react5.default.createElement("span", {
|
|
1848
|
+
className: classNames?.timestamp,
|
|
1849
|
+
title: item.timestamp.absolute
|
|
1850
|
+
}, item.timestamp.relative) : null);
|
|
1851
|
+
if (item.edit.available) {
|
|
1852
|
+
return /* @__PURE__ */ import_react5.default.createElement(EditableUserBubble, {
|
|
1853
|
+
item,
|
|
1854
|
+
slots,
|
|
1855
|
+
actions: actionRow
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
|
|
1859
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1860
|
+
className,
|
|
1861
|
+
"data-role": item.role,
|
|
1862
|
+
"data-streaming": item.isStreaming ? "true" : void 0
|
|
1863
|
+
}, /* @__PURE__ */ import_react5.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
|
|
1864
|
+
}
|
|
1865
|
+
__name(MessageItemView, "MessageItemView");
|
|
1866
|
+
function renderBlocks(item, slots) {
|
|
1867
|
+
const nodes = [];
|
|
1868
|
+
for (const block of item.blocks) {
|
|
1869
|
+
if (block.kind === "text") {
|
|
1870
|
+
nodes.push(/* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1871
|
+
key: block.key,
|
|
1872
|
+
className: slots.classNames?.text
|
|
1873
|
+
}, slots.renderText ? slots.renderText(block.text, {
|
|
1874
|
+
isStreaming: item.isStreaming
|
|
1875
|
+
}) : block.text));
|
|
1876
|
+
continue;
|
|
1877
|
+
}
|
|
1878
|
+
if (block.kind === "reasoning") {
|
|
1879
|
+
nodes.push(renderReasoningBlock(block, slots));
|
|
1880
|
+
continue;
|
|
1881
|
+
}
|
|
1882
|
+
if (block.kind === "files") {
|
|
1883
|
+
nodes.push(renderFilesBlock(block, slots));
|
|
1884
|
+
continue;
|
|
1885
|
+
}
|
|
1886
|
+
if (block.kind === "tools") {
|
|
1887
|
+
nodes.push(...renderToolBlock(item, block, slots));
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
return nodes;
|
|
1891
|
+
}
|
|
1892
|
+
__name(renderBlocks, "renderBlocks");
|
|
1893
|
+
function renderFilesBlock(block, slots) {
|
|
1894
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1895
|
+
key: block.key,
|
|
1896
|
+
className: slots.classNames?.files,
|
|
1897
|
+
"data-files": "true"
|
|
1898
|
+
}, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react5.default.createElement("a", {
|
|
1899
|
+
key: file.url,
|
|
1900
|
+
href: file.url,
|
|
1901
|
+
target: "_blank",
|
|
1902
|
+
rel: "noreferrer",
|
|
1903
|
+
className: slots.classNames?.file,
|
|
1904
|
+
title: file.filename ?? file.mediaType
|
|
1905
|
+
}, file.isImage ? /* @__PURE__ */ import_react5.default.createElement("img", {
|
|
1906
|
+
src: file.url,
|
|
1907
|
+
alt: file.filename ?? "image",
|
|
1908
|
+
className: slots.classNames?.fileImage
|
|
1909
|
+
}) : file.filename ?? file.mediaType)));
|
|
1910
|
+
}
|
|
1911
|
+
__name(renderFilesBlock, "renderFilesBlock");
|
|
1912
|
+
function renderReasoningBlock(block, slots) {
|
|
1913
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1914
|
+
key: block.key,
|
|
1915
|
+
className: slots.classNames?.reasoning,
|
|
1916
|
+
"data-reasoning": "true",
|
|
1917
|
+
"data-streaming": block.isStreaming ? "true" : void 0
|
|
1918
|
+
}, /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1919
|
+
type: "button",
|
|
1920
|
+
className: slots.classNames?.reasoningToggle,
|
|
1921
|
+
onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
|
|
1922
|
+
"aria-expanded": block.isOpen
|
|
1923
|
+
}, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1924
|
+
className: slots.classNames?.reasoningText
|
|
1925
|
+
}, slots.renderReasoning ? slots.renderReasoning(block.text, {
|
|
1926
|
+
isStreaming: block.isStreaming,
|
|
1927
|
+
isOpen: block.isOpen
|
|
1928
|
+
}) : block.text) : null);
|
|
1929
|
+
}
|
|
1930
|
+
__name(renderReasoningBlock, "renderReasoningBlock");
|
|
1931
|
+
function renderToolBlock(item, block, slots) {
|
|
1932
|
+
if (slots.renderToolGroup) {
|
|
1933
|
+
return [
|
|
1934
|
+
/* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, {
|
|
1935
|
+
key: block.key
|
|
1936
|
+
}, slots.renderToolGroup(block.parts, block.key))
|
|
1937
|
+
];
|
|
1938
|
+
}
|
|
1939
|
+
if (!slots.renderToolPart) {
|
|
1940
|
+
return [];
|
|
1941
|
+
}
|
|
1942
|
+
const renderToolPart = slots.renderToolPart;
|
|
1943
|
+
return block.parts.map((part) => {
|
|
1944
|
+
const partKey = `${item.id}-${part.toolCallId}`;
|
|
1945
|
+
return /* @__PURE__ */ import_react5.default.createElement(import_react5.default.Fragment, {
|
|
1946
|
+
key: partKey
|
|
1947
|
+
}, renderToolPart(part, partKey));
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
__name(renderToolBlock, "renderToolBlock");
|
|
1951
|
+
function EditableUserBubble({ item, slots, actions }) {
|
|
1952
|
+
const { classNames } = slots;
|
|
1953
|
+
const { edit } = item;
|
|
1954
|
+
if (!edit.isEditing) {
|
|
1955
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1956
|
+
className: joinClasses(classNames?.root, classNames?.byRole?.user),
|
|
1957
|
+
"data-role": "user"
|
|
1958
|
+
}, /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1959
|
+
className: classNames?.text
|
|
1960
|
+
}, slots.renderText ? slots.renderText(item.text, {
|
|
1961
|
+
isStreaming: false
|
|
1962
|
+
}) : item.text), /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1963
|
+
type: "button",
|
|
1964
|
+
onClick: edit.start,
|
|
1965
|
+
className: classNames?.actionButton,
|
|
1966
|
+
title: "Edit & resubmit"
|
|
1967
|
+
}, "Edit"), actions);
|
|
1968
|
+
}
|
|
1969
|
+
const textareaProps = edit.getTextareaProps();
|
|
1970
|
+
return /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1971
|
+
"data-editing": "true"
|
|
1972
|
+
}, /* @__PURE__ */ import_react5.default.createElement("textarea", {
|
|
1973
|
+
...textareaProps,
|
|
1974
|
+
rows: Math.min(8, edit.draft.split("\n").length + 1),
|
|
1975
|
+
className: classNames?.editTextarea
|
|
1976
|
+
}), /* @__PURE__ */ import_react5.default.createElement("div", {
|
|
1977
|
+
className: classNames?.editActions
|
|
1978
|
+
}, /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1979
|
+
type: "button",
|
|
1980
|
+
onClick: edit.cancel
|
|
1981
|
+
}, "Cancel"), /* @__PURE__ */ import_react5.default.createElement("button", {
|
|
1982
|
+
type: "button",
|
|
1983
|
+
onClick: edit.save,
|
|
1984
|
+
disabled: !edit.canSave
|
|
1985
|
+
}, "Resend")));
|
|
1986
|
+
}
|
|
1987
|
+
__name(EditableUserBubble, "EditableUserBubble");
|
|
1988
|
+
function joinClasses(...entries) {
|
|
1989
|
+
const joined = entries.filter((entry) => entry).join(" ");
|
|
1990
|
+
return joined === "" ? void 0 : joined;
|
|
1991
|
+
}
|
|
1992
|
+
__name(joinClasses, "joinClasses");
|
|
1993
|
+
|
|
1994
|
+
// src/components/message-list.tsx
|
|
1995
|
+
function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, reasoningLabel, getMeta, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames }) {
|
|
1996
|
+
const transcript = useChatTranscript({
|
|
1997
|
+
messages,
|
|
1998
|
+
status,
|
|
1999
|
+
...onFork ? {
|
|
2000
|
+
onFork
|
|
2001
|
+
} : {},
|
|
2002
|
+
...editable !== void 0 ? {
|
|
2003
|
+
editable
|
|
2004
|
+
} : {},
|
|
2005
|
+
...onEditSubmit ? {
|
|
2006
|
+
onEditSubmit
|
|
2007
|
+
} : {},
|
|
2008
|
+
...regeneratable !== void 0 ? {
|
|
2009
|
+
regeneratable
|
|
2010
|
+
} : {},
|
|
2011
|
+
...onRegenerate ? {
|
|
2012
|
+
onRegenerate
|
|
2013
|
+
} : {},
|
|
2014
|
+
...getUsage ? {
|
|
2015
|
+
getUsage
|
|
2016
|
+
} : {},
|
|
2017
|
+
...getCreatedAt ? {
|
|
2018
|
+
getCreatedAt
|
|
2019
|
+
} : {},
|
|
2020
|
+
...followUps !== void 0 ? {
|
|
2021
|
+
followUps
|
|
2022
|
+
} : {}
|
|
2023
|
+
});
|
|
2024
|
+
if (transcript.showEmptyState) {
|
|
2025
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
2026
|
+
className: classNames?.empty
|
|
2027
|
+
}, emptyState);
|
|
2028
|
+
}
|
|
2029
|
+
const slots = {
|
|
2030
|
+
...renderToolPart ? {
|
|
2031
|
+
renderToolPart
|
|
2032
|
+
} : {},
|
|
2033
|
+
...renderToolGroup ? {
|
|
2034
|
+
renderToolGroup
|
|
2035
|
+
} : {},
|
|
2036
|
+
...renderText ? {
|
|
2037
|
+
renderText
|
|
2038
|
+
} : {},
|
|
2039
|
+
...renderReasoning ? {
|
|
2040
|
+
renderReasoning
|
|
2041
|
+
} : {},
|
|
2042
|
+
...renderFiles ? {
|
|
2043
|
+
renderFiles
|
|
2044
|
+
} : {},
|
|
2045
|
+
...reasoningLabel !== void 0 ? {
|
|
2046
|
+
reasoningLabel
|
|
2047
|
+
} : {},
|
|
2048
|
+
...classNames?.message ? {
|
|
2049
|
+
classNames: classNames.message
|
|
2050
|
+
} : {}
|
|
2051
|
+
};
|
|
2052
|
+
return /* @__PURE__ */ React.createElement("div", {
|
|
2053
|
+
className: classNames?.root
|
|
2054
|
+
}, transcript.window.canLoadEarlier ? /* @__PURE__ */ React.createElement("button", {
|
|
2055
|
+
type: "button",
|
|
2056
|
+
className: classNames?.loadEarlier,
|
|
2057
|
+
onClick: transcript.window.loadEarlier
|
|
2058
|
+
}, "Load earlier (", transcript.window.hiddenCount, " more)") : null, transcript.items.map((item) => /* @__PURE__ */ React.createElement(MessageItemView, {
|
|
2059
|
+
key: item.id,
|
|
2060
|
+
item,
|
|
2061
|
+
...slots,
|
|
2062
|
+
...getMeta ? {
|
|
2063
|
+
meta: getMeta(item.message)
|
|
2064
|
+
} : {}
|
|
2065
|
+
})), transcript.showTypingIndicator ? /* @__PURE__ */ React.createElement("div", {
|
|
2066
|
+
className: classNames?.typing
|
|
2067
|
+
}, typingIndicator ?? "Thinking\u2026") : null, error && onRetry ? /* @__PURE__ */ React.createElement("div", {
|
|
2068
|
+
className: classNames?.error
|
|
2069
|
+
}, /* @__PURE__ */ React.createElement("span", null, error.message || "The assistant didn't respond."), /* @__PURE__ */ React.createElement("button", {
|
|
2070
|
+
type: "button",
|
|
2071
|
+
className: classNames?.errorRetry,
|
|
2072
|
+
onClick: /* @__PURE__ */ __name(() => void onRetry(), "onClick")
|
|
2073
|
+
}, "Retry")) : null, transcript.showFollowUps && onFollowUpClick && followUps ? /* @__PURE__ */ React.createElement("div", {
|
|
2074
|
+
className: classNames?.followUps
|
|
2075
|
+
}, followUps.map((text) => /* @__PURE__ */ React.createElement("button", {
|
|
2076
|
+
key: text,
|
|
2077
|
+
type: "button",
|
|
2078
|
+
className: classNames?.followUpChip,
|
|
2079
|
+
onClick: /* @__PURE__ */ __name(() => onFollowUpClick(text), "onClick")
|
|
2080
|
+
}, text))) : null);
|
|
2081
|
+
}
|
|
2082
|
+
__name(MessageList, "MessageList");
|
|
2083
|
+
|
|
2084
|
+
// src/components/speech-languages.ts
|
|
2085
|
+
var DEFAULT_SPEECH_LANG = "en-US";
|
|
2086
|
+
var SPEECH_LANGUAGES = [
|
|
2087
|
+
{
|
|
2088
|
+
tag: "en-US",
|
|
2089
|
+
label: "English (US)"
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
tag: "en-GB",
|
|
2093
|
+
label: "English (UK)"
|
|
2094
|
+
},
|
|
2095
|
+
{
|
|
2096
|
+
tag: "pt-BR",
|
|
2097
|
+
label: "Portuguese (Brazil)"
|
|
2098
|
+
},
|
|
2099
|
+
{
|
|
2100
|
+
tag: "es-ES",
|
|
2101
|
+
label: "Spanish (Spain)"
|
|
2102
|
+
}
|
|
2103
|
+
];
|
|
2104
|
+
var STORAGE_KEY = "nestjs-agent-chat-speech-lang";
|
|
2105
|
+
function loadStoredSpeechLang() {
|
|
2106
|
+
if (typeof window === "undefined") return DEFAULT_SPEECH_LANG;
|
|
2107
|
+
try {
|
|
2108
|
+
const raw = window.localStorage.getItem(STORAGE_KEY);
|
|
2109
|
+
if (raw && SPEECH_LANGUAGES.some((language) => language.tag === raw)) {
|
|
2110
|
+
return raw;
|
|
2111
|
+
}
|
|
2112
|
+
} catch {
|
|
2113
|
+
}
|
|
2114
|
+
return DEFAULT_SPEECH_LANG;
|
|
2115
|
+
}
|
|
2116
|
+
__name(loadStoredSpeechLang, "loadStoredSpeechLang");
|
|
1470
2117
|
function persistSpeechLang(tag) {
|
|
1471
2118
|
if (typeof window === "undefined") return;
|
|
1472
2119
|
try {
|
|
@@ -1475,6 +2122,776 @@ function persistSpeechLang(tag) {
|
|
|
1475
2122
|
}
|
|
1476
2123
|
}
|
|
1477
2124
|
__name(persistSpeechLang, "persistSpeechLang");
|
|
2125
|
+
|
|
2126
|
+
// src/composer/model.ts
|
|
2127
|
+
var WHITESPACE = /\s/;
|
|
2128
|
+
function findActiveTrigger(text, caret, sources) {
|
|
2129
|
+
const end = Math.max(0, Math.min(caret, text.length));
|
|
2130
|
+
for (let index = end - 1; index >= 0; index--) {
|
|
2131
|
+
const char = text[index];
|
|
2132
|
+
if (char === void 0 || WHITESPACE.test(char)) {
|
|
2133
|
+
return null;
|
|
2134
|
+
}
|
|
2135
|
+
const source = sources.find((candidate) => candidate.trigger === char);
|
|
2136
|
+
if (source && isAllowedAt(text, index, source.position)) {
|
|
2137
|
+
return {
|
|
2138
|
+
source,
|
|
2139
|
+
index,
|
|
2140
|
+
query: text.slice(index + 1, end)
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
__name(findActiveTrigger, "findActiveTrigger");
|
|
2147
|
+
function isAllowedAt(text, index, position) {
|
|
2148
|
+
if (index === 0) {
|
|
2149
|
+
return true;
|
|
2150
|
+
}
|
|
2151
|
+
if (position === "start") {
|
|
2152
|
+
return false;
|
|
2153
|
+
}
|
|
2154
|
+
const previous = text[index - 1];
|
|
2155
|
+
return previous !== void 0 && WHITESPACE.test(previous);
|
|
2156
|
+
}
|
|
2157
|
+
__name(isAllowedAt, "isAllowedAt");
|
|
2158
|
+
function filterAutocompleteItems(items, query) {
|
|
2159
|
+
const needle = query.trim().toLowerCase();
|
|
2160
|
+
return items.filter((item) => item.label.toLowerCase().includes(needle) || (item.description?.toLowerCase().includes(needle) ?? false));
|
|
2161
|
+
}
|
|
2162
|
+
__name(filterAutocompleteItems, "filterAutocompleteItems");
|
|
2163
|
+
function applyCompletion(text, caret, match, item) {
|
|
2164
|
+
const end = Math.max(match.index, Math.min(caret, text.length));
|
|
2165
|
+
const inserted = `${match.source.trigger}${item.value ?? item.label}${match.source.insertSuffix ?? " "}`;
|
|
2166
|
+
return {
|
|
2167
|
+
text: text.slice(0, match.index) + inserted + text.slice(end),
|
|
2168
|
+
caret: match.index + inserted.length
|
|
2169
|
+
};
|
|
2170
|
+
}
|
|
2171
|
+
__name(applyCompletion, "applyCompletion");
|
|
2172
|
+
|
|
2173
|
+
// src/composer/skills-source.ts
|
|
2174
|
+
function createSkillsSource(options) {
|
|
2175
|
+
const byThread = /* @__PURE__ */ new Map();
|
|
2176
|
+
return {
|
|
2177
|
+
id: "skills",
|
|
2178
|
+
label: options.label ?? "Skills",
|
|
2179
|
+
trigger: options.trigger ?? "/",
|
|
2180
|
+
position: "start",
|
|
2181
|
+
getItems: /* @__PURE__ */ __name(() => {
|
|
2182
|
+
const threadId = options.getThreadId?.();
|
|
2183
|
+
const key = threadId ?? "";
|
|
2184
|
+
const cached = byThread.get(key);
|
|
2185
|
+
if (cached) {
|
|
2186
|
+
return cached;
|
|
2187
|
+
}
|
|
2188
|
+
const pending = options.client.listSkills(threadId).then((entries) => entries.map(toItem)).catch((error) => {
|
|
2189
|
+
byThread.delete(key);
|
|
2190
|
+
throw error;
|
|
2191
|
+
});
|
|
2192
|
+
byThread.set(key, pending);
|
|
2193
|
+
return pending;
|
|
2194
|
+
}, "getItems")
|
|
2195
|
+
};
|
|
2196
|
+
}
|
|
2197
|
+
__name(createSkillsSource, "createSkillsSource");
|
|
2198
|
+
function toItem(entry) {
|
|
2199
|
+
const data = {
|
|
2200
|
+
scope: entry.scope,
|
|
2201
|
+
...entry.shadows !== void 0 ? {
|
|
2202
|
+
shadows: entry.shadows
|
|
2203
|
+
} : {}
|
|
2204
|
+
};
|
|
2205
|
+
return {
|
|
2206
|
+
id: entry.name,
|
|
2207
|
+
label: entry.name,
|
|
2208
|
+
description: entry.description,
|
|
2209
|
+
// `shadows` is present only on a conflict, so saying "overrides" is never noise: without it a
|
|
2210
|
+
// user cannot tell "there is no org default" from "there is one and mine wins".
|
|
2211
|
+
hint: entry.shadows === void 0 ? entry.scope : `${entry.scope} \xB7 overrides ${entry.shadows.join(", ")}`,
|
|
2212
|
+
data
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
__name(toItem, "toItem");
|
|
2216
|
+
|
|
2217
|
+
// src/composer/use-composer-autocomplete.ts
|
|
2218
|
+
var import_react6 = require("react");
|
|
2219
|
+
function useComposerAutocomplete(options) {
|
|
2220
|
+
const { value, sources, acceptOnTab = true } = options;
|
|
2221
|
+
const latest = (0, import_react6.useRef)(options);
|
|
2222
|
+
latest.current = options;
|
|
2223
|
+
const inputRef = (0, import_react6.useRef)(null);
|
|
2224
|
+
const [caret, setCaret] = (0, import_react6.useState)(value.length);
|
|
2225
|
+
const [items, setItems] = (0, import_react6.useState)([]);
|
|
2226
|
+
const [isLoading, setLoading] = (0, import_react6.useState)(false);
|
|
2227
|
+
const [error, setError] = (0, import_react6.useState)(null);
|
|
2228
|
+
const [activeIndex, setActiveIndex] = (0, import_react6.useState)(0);
|
|
2229
|
+
const [dismissedIndex, setDismissedIndex] = (0, import_react6.useState)(null);
|
|
2230
|
+
const safeCaret = Math.min(caret, value.length);
|
|
2231
|
+
const match = (0, import_react6.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
|
|
2232
|
+
value,
|
|
2233
|
+
safeCaret,
|
|
2234
|
+
sources
|
|
2235
|
+
]);
|
|
2236
|
+
const isActive = match !== null && dismissedIndex !== match.index;
|
|
2237
|
+
const matchRef = (0, import_react6.useRef)(match);
|
|
2238
|
+
matchRef.current = match;
|
|
2239
|
+
const caretRef = (0, import_react6.useRef)(safeCaret);
|
|
2240
|
+
caretRef.current = safeCaret;
|
|
2241
|
+
const itemsRef = (0, import_react6.useRef)(items);
|
|
2242
|
+
itemsRef.current = items;
|
|
2243
|
+
const activeIndexRef = (0, import_react6.useRef)(activeIndex);
|
|
2244
|
+
activeIndexRef.current = activeIndex;
|
|
2245
|
+
const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
|
|
2246
|
+
(0, import_react6.useEffect)(() => {
|
|
2247
|
+
const current = matchRef.current;
|
|
2248
|
+
if (requestKey === null || current === null) {
|
|
2249
|
+
setItems([]);
|
|
2250
|
+
setLoading(false);
|
|
2251
|
+
setError(null);
|
|
2252
|
+
return;
|
|
2253
|
+
}
|
|
2254
|
+
const { source, query } = current;
|
|
2255
|
+
const controller = new AbortController();
|
|
2256
|
+
let superseded = false;
|
|
2257
|
+
const settle = /* @__PURE__ */ __name((next) => {
|
|
2258
|
+
if (superseded) return;
|
|
2259
|
+
setItems((source.filter ?? filterAutocompleteItems)(next, query));
|
|
2260
|
+
setActiveIndex(0);
|
|
2261
|
+
setLoading(false);
|
|
2262
|
+
setError(null);
|
|
2263
|
+
}, "settle");
|
|
2264
|
+
const failed = /* @__PURE__ */ __name((cause) => {
|
|
2265
|
+
if (superseded) return;
|
|
2266
|
+
setItems([]);
|
|
2267
|
+
setLoading(false);
|
|
2268
|
+
setError(cause instanceof Error ? cause.message : "Could not load suggestions");
|
|
2269
|
+
latest.current.onError?.(cause, source);
|
|
2270
|
+
}, "failed");
|
|
2271
|
+
let result;
|
|
2272
|
+
try {
|
|
2273
|
+
result = source.getItems(query, controller.signal);
|
|
2274
|
+
} catch (cause) {
|
|
2275
|
+
failed(cause);
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
if (!isPromise(result)) {
|
|
2279
|
+
settle(result);
|
|
2280
|
+
return;
|
|
2281
|
+
}
|
|
2282
|
+
setLoading(true);
|
|
2283
|
+
setError(null);
|
|
2284
|
+
result.then(settle, failed);
|
|
2285
|
+
return () => {
|
|
2286
|
+
superseded = true;
|
|
2287
|
+
controller.abort();
|
|
2288
|
+
};
|
|
2289
|
+
}, [
|
|
2290
|
+
requestKey
|
|
2291
|
+
]);
|
|
2292
|
+
const isOpen = isActive;
|
|
2293
|
+
const activeItem = isOpen ? items[activeIndex] ?? null : null;
|
|
2294
|
+
const reactId = (0, import_react6.useId)();
|
|
2295
|
+
const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
|
|
2296
|
+
const optionId = (0, import_react6.useCallback)((index) => `${listboxId}-option-${index}`, [
|
|
2297
|
+
listboxId
|
|
2298
|
+
]);
|
|
2299
|
+
const highlight = (0, import_react6.useCallback)((index) => {
|
|
2300
|
+
const total = itemsRef.current.length;
|
|
2301
|
+
if (total === 0) return;
|
|
2302
|
+
setActiveIndex((index % total + total) % total);
|
|
2303
|
+
}, []);
|
|
2304
|
+
const moveHighlight = (0, import_react6.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
|
|
2305
|
+
highlight
|
|
2306
|
+
]);
|
|
2307
|
+
const dismiss = (0, import_react6.useCallback)(() => {
|
|
2308
|
+
const current = matchRef.current;
|
|
2309
|
+
if (current) setDismissedIndex(current.index);
|
|
2310
|
+
}, []);
|
|
2311
|
+
const pendingCaret = (0, import_react6.useRef)(null);
|
|
2312
|
+
const accept = (0, import_react6.useCallback)((item) => {
|
|
2313
|
+
const current = matchRef.current;
|
|
2314
|
+
if (!current) return;
|
|
2315
|
+
const chosen = item ?? itemsRef.current[activeIndexRef.current];
|
|
2316
|
+
if (!chosen) return;
|
|
2317
|
+
const edit = applyCompletion(latest.current.value, caretRef.current, current, chosen);
|
|
2318
|
+
latest.current.onValueChange(edit.text);
|
|
2319
|
+
setCaret(edit.caret);
|
|
2320
|
+
setDismissedIndex(null);
|
|
2321
|
+
pendingCaret.current = edit.caret;
|
|
2322
|
+
}, []);
|
|
2323
|
+
(0, import_react6.useLayoutEffect)(() => {
|
|
2324
|
+
const position = pendingCaret.current;
|
|
2325
|
+
if (position === null) return;
|
|
2326
|
+
pendingCaret.current = null;
|
|
2327
|
+
inputRef.current?.setSelectionRange(position, position);
|
|
2328
|
+
});
|
|
2329
|
+
const readCaret = (0, import_react6.useCallback)((element) => {
|
|
2330
|
+
setCaret(element.selectionStart ?? element.value.length);
|
|
2331
|
+
}, []);
|
|
2332
|
+
const onKeyDown = (0, import_react6.useCallback)((event) => {
|
|
2333
|
+
if (!isOpen) return;
|
|
2334
|
+
const hasItems = itemsRef.current.length > 0;
|
|
2335
|
+
switch (event.key) {
|
|
2336
|
+
case "ArrowDown":
|
|
2337
|
+
if (!hasItems) return;
|
|
2338
|
+
event.preventDefault();
|
|
2339
|
+
moveHighlight(1);
|
|
2340
|
+
return;
|
|
2341
|
+
case "ArrowUp":
|
|
2342
|
+
if (!hasItems) return;
|
|
2343
|
+
event.preventDefault();
|
|
2344
|
+
moveHighlight(-1);
|
|
2345
|
+
return;
|
|
2346
|
+
case "Enter":
|
|
2347
|
+
if (!hasItems) return;
|
|
2348
|
+
event.preventDefault();
|
|
2349
|
+
accept();
|
|
2350
|
+
return;
|
|
2351
|
+
case "Tab":
|
|
2352
|
+
if (!acceptOnTab || event.shiftKey || !hasItems) return;
|
|
2353
|
+
event.preventDefault();
|
|
2354
|
+
accept();
|
|
2355
|
+
return;
|
|
2356
|
+
case "Escape":
|
|
2357
|
+
event.preventDefault();
|
|
2358
|
+
dismiss();
|
|
2359
|
+
return;
|
|
2360
|
+
default:
|
|
2361
|
+
return;
|
|
2362
|
+
}
|
|
2363
|
+
}, [
|
|
2364
|
+
isOpen,
|
|
2365
|
+
acceptOnTab,
|
|
2366
|
+
accept,
|
|
2367
|
+
dismiss,
|
|
2368
|
+
moveHighlight
|
|
2369
|
+
]);
|
|
2370
|
+
const onChange = (0, import_react6.useCallback)((event) => {
|
|
2371
|
+
readCaret(event.target);
|
|
2372
|
+
const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
|
|
2373
|
+
setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
|
|
2374
|
+
}, [
|
|
2375
|
+
readCaret
|
|
2376
|
+
]);
|
|
2377
|
+
const onSelect = (0, import_react6.useCallback)((event) => readCaret(event.currentTarget), [
|
|
2378
|
+
readCaret
|
|
2379
|
+
]);
|
|
2380
|
+
const onClick = (0, import_react6.useCallback)((event) => readCaret(event.currentTarget), [
|
|
2381
|
+
readCaret
|
|
2382
|
+
]);
|
|
2383
|
+
const setInput = (0, import_react6.useCallback)((element) => {
|
|
2384
|
+
inputRef.current = element;
|
|
2385
|
+
}, []);
|
|
2386
|
+
const getInputProps = (0, import_react6.useCallback)(() => ({
|
|
2387
|
+
ref: setInput,
|
|
2388
|
+
onChange,
|
|
2389
|
+
onKeyDown,
|
|
2390
|
+
onSelect,
|
|
2391
|
+
onClick,
|
|
2392
|
+
role: "combobox",
|
|
2393
|
+
"aria-expanded": isOpen,
|
|
2394
|
+
"aria-autocomplete": "list",
|
|
2395
|
+
"aria-haspopup": "listbox",
|
|
2396
|
+
...isOpen ? {
|
|
2397
|
+
"aria-controls": listboxId
|
|
2398
|
+
} : {},
|
|
2399
|
+
...activeItem ? {
|
|
2400
|
+
"aria-activedescendant": optionId(activeIndex)
|
|
2401
|
+
} : {}
|
|
2402
|
+
}), [
|
|
2403
|
+
setInput,
|
|
2404
|
+
onChange,
|
|
2405
|
+
onKeyDown,
|
|
2406
|
+
onSelect,
|
|
2407
|
+
onClick,
|
|
2408
|
+
isOpen,
|
|
2409
|
+
listboxId,
|
|
2410
|
+
activeItem,
|
|
2411
|
+
activeIndex,
|
|
2412
|
+
optionId
|
|
2413
|
+
]);
|
|
2414
|
+
const getListboxProps = (0, import_react6.useCallback)(() => ({
|
|
2415
|
+
id: listboxId,
|
|
2416
|
+
role: "listbox",
|
|
2417
|
+
"aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
|
|
2418
|
+
}), [
|
|
2419
|
+
listboxId,
|
|
2420
|
+
match
|
|
2421
|
+
]);
|
|
2422
|
+
const getOptionProps = (0, import_react6.useCallback)((index) => ({
|
|
2423
|
+
id: optionId(index),
|
|
2424
|
+
role: "option",
|
|
2425
|
+
"aria-selected": index === activeIndex
|
|
2426
|
+
}), [
|
|
2427
|
+
optionId,
|
|
2428
|
+
activeIndex
|
|
2429
|
+
]);
|
|
2430
|
+
return {
|
|
2431
|
+
isOpen,
|
|
2432
|
+
source: isOpen && match ? match.source : null,
|
|
2433
|
+
trigger: isOpen && match ? match.source.trigger : null,
|
|
2434
|
+
query: match?.query ?? "",
|
|
2435
|
+
items,
|
|
2436
|
+
activeIndex,
|
|
2437
|
+
activeItem,
|
|
2438
|
+
isLoading,
|
|
2439
|
+
error,
|
|
2440
|
+
highlight,
|
|
2441
|
+
moveHighlight,
|
|
2442
|
+
accept,
|
|
2443
|
+
dismiss,
|
|
2444
|
+
getInputProps,
|
|
2445
|
+
getListboxProps,
|
|
2446
|
+
getOptionProps
|
|
2447
|
+
};
|
|
2448
|
+
}
|
|
2449
|
+
__name(useComposerAutocomplete, "useComposerAutocomplete");
|
|
2450
|
+
function isPromise(value) {
|
|
2451
|
+
return typeof value.then === "function";
|
|
2452
|
+
}
|
|
2453
|
+
__name(isPromise, "isPromise");
|
|
2454
|
+
|
|
2455
|
+
// src/stored-message-to-ui-message.ts
|
|
2456
|
+
function storedMessageToUiMessage(message) {
|
|
2457
|
+
const parts = [];
|
|
2458
|
+
if (message.content) {
|
|
2459
|
+
parts.push({
|
|
2460
|
+
type: "text",
|
|
2461
|
+
text: message.content
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
for (const attachment of message.attachments ?? []) {
|
|
2465
|
+
parts.push({
|
|
2466
|
+
type: "file",
|
|
2467
|
+
mediaType: attachment.contentType,
|
|
2468
|
+
filename: attachment.name,
|
|
2469
|
+
url: attachment.url
|
|
2470
|
+
});
|
|
2471
|
+
}
|
|
2472
|
+
for (const call of message.toolCalls ?? []) {
|
|
2473
|
+
const result = message.toolResults?.find((candidate) => candidate.id === call.id);
|
|
2474
|
+
const toolKind2 = call.kind;
|
|
2475
|
+
parts.push({
|
|
2476
|
+
type: `tool-${call.name}`,
|
|
2477
|
+
toolCallId: call.id,
|
|
2478
|
+
...toolKind2 !== void 0 ? {
|
|
2479
|
+
toolMetadata: {
|
|
2480
|
+
toolKind: toolKind2
|
|
2481
|
+
}
|
|
2482
|
+
} : {},
|
|
2483
|
+
...result !== void 0 ? {
|
|
2484
|
+
state: "output-available",
|
|
2485
|
+
input: call.input,
|
|
2486
|
+
output: result.output
|
|
2487
|
+
} : {
|
|
2488
|
+
state: "input-available",
|
|
2489
|
+
input: call.input
|
|
2490
|
+
}
|
|
2491
|
+
});
|
|
2492
|
+
}
|
|
2493
|
+
return {
|
|
2494
|
+
id: message.id,
|
|
2495
|
+
role: message.role,
|
|
2496
|
+
parts
|
|
2497
|
+
};
|
|
2498
|
+
}
|
|
2499
|
+
__name(storedMessageToUiMessage, "storedMessageToUiMessage");
|
|
2500
|
+
|
|
2501
|
+
// src/stored-thread-to-ui-messages.ts
|
|
2502
|
+
function storedThreadToUiMessages(messages) {
|
|
2503
|
+
const turns = [];
|
|
2504
|
+
for (const message of messages) {
|
|
2505
|
+
const currentTurn = turns.at(-1);
|
|
2506
|
+
const previousRow = currentTurn?.at(-1);
|
|
2507
|
+
if (message.role === "assistant" && previousRow?.role === "assistant" && currentTurn && sameRun(previousRow, message)) {
|
|
2508
|
+
currentTurn.push(message);
|
|
2509
|
+
} else {
|
|
2510
|
+
turns.push([
|
|
2511
|
+
message
|
|
2512
|
+
]);
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
return turns.map(mergeTurn);
|
|
2516
|
+
}
|
|
2517
|
+
__name(storedThreadToUiMessages, "storedThreadToUiMessages");
|
|
2518
|
+
function sameRun(previous, next) {
|
|
2519
|
+
return previous.runId === void 0 || next.runId === void 0 || previous.runId === next.runId;
|
|
2520
|
+
}
|
|
2521
|
+
__name(sameRun, "sameRun");
|
|
2522
|
+
function mergeTurn(rows) {
|
|
2523
|
+
const lastRow = rows.at(-1);
|
|
2524
|
+
if (lastRow === void 0) {
|
|
2525
|
+
throw new Error("storedThreadToUiMessages: encountered an empty turn");
|
|
2526
|
+
}
|
|
2527
|
+
if (rows.length === 1) {
|
|
2528
|
+
return storedMessageToUiMessage(lastRow);
|
|
2529
|
+
}
|
|
2530
|
+
const parts = rows.flatMap((row) => storedMessageToUiMessage(row).parts);
|
|
2531
|
+
const usage = sumUsage(rows);
|
|
2532
|
+
return {
|
|
2533
|
+
id: lastRow.id,
|
|
2534
|
+
role: lastRow.role,
|
|
2535
|
+
parts,
|
|
2536
|
+
...usage !== void 0 ? {
|
|
2537
|
+
metadata: {
|
|
2538
|
+
usage
|
|
2539
|
+
}
|
|
2540
|
+
} : {}
|
|
2541
|
+
};
|
|
2542
|
+
}
|
|
2543
|
+
__name(mergeTurn, "mergeTurn");
|
|
2544
|
+
function sumUsage(rows) {
|
|
2545
|
+
const usages = rows.map((row) => row.usage).filter((usage) => usage !== void 0);
|
|
2546
|
+
if (usages.length === 0) {
|
|
2547
|
+
return void 0;
|
|
2548
|
+
}
|
|
2549
|
+
const allCostsUnknown = usages.every((usage) => usage.costUsd === null || usage.costUsd === void 0);
|
|
2550
|
+
return {
|
|
2551
|
+
inputTokens: usages.reduce((sum, usage) => sum + usage.inputTokens, 0),
|
|
2552
|
+
outputTokens: usages.reduce((sum, usage) => sum + usage.outputTokens, 0),
|
|
2553
|
+
costUsd: allCostsUnknown ? null : usages.reduce((sum, usage) => sum + (usage.costUsd ?? 0), 0)
|
|
2554
|
+
};
|
|
2555
|
+
}
|
|
2556
|
+
__name(sumUsage, "sumUsage");
|
|
2557
|
+
|
|
2558
|
+
// src/use-agent-chat.ts
|
|
2559
|
+
var import_react7 = require("@ai-sdk/react");
|
|
2560
|
+
var import_react8 = require("react");
|
|
2561
|
+
function useAgentChat(options) {
|
|
2562
|
+
const latest = (0, import_react8.useRef)(options);
|
|
2563
|
+
latest.current = options;
|
|
2564
|
+
const [runId, setRunId] = (0, import_react8.useState)(options.resumeRunId);
|
|
2565
|
+
const runIdRef = (0, import_react8.useRef)(runId);
|
|
2566
|
+
runIdRef.current = runId;
|
|
2567
|
+
const settlingRunIdRef = (0, import_react8.useRef)(void 0);
|
|
2568
|
+
const autoResumeRunIdRef = (0, import_react8.useRef)(void 0);
|
|
2569
|
+
const [autoResume, setAutoResume] = (0, import_react8.useState)(false);
|
|
2570
|
+
const [activeRunId, setActiveRunId] = (0, import_react8.useState)(null);
|
|
2571
|
+
const regenerateNext = (0, import_react8.useRef)(false);
|
|
2572
|
+
const createdThreadId = (0, import_react8.useRef)(void 0);
|
|
2573
|
+
const client = (0, import_react8.useMemo)(() => {
|
|
2574
|
+
if (options.client) return options.client;
|
|
2575
|
+
return new AgentClient({
|
|
2576
|
+
...options.baseUrl !== void 0 ? {
|
|
2577
|
+
baseUrl: options.baseUrl
|
|
2578
|
+
} : {},
|
|
2579
|
+
...options.credentials !== void 0 ? {
|
|
2580
|
+
credentials: options.credentials
|
|
2581
|
+
} : {},
|
|
2582
|
+
...options.fetch !== void 0 ? {
|
|
2583
|
+
fetch: options.fetch
|
|
2584
|
+
} : {},
|
|
2585
|
+
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
2586
|
+
});
|
|
2587
|
+
}, []);
|
|
2588
|
+
(0, import_react8.useEffect)(() => {
|
|
2589
|
+
if (!options.resume || options.threadId === void 0) return;
|
|
2590
|
+
let cancelled = false;
|
|
2591
|
+
const threadId = options.threadId;
|
|
2592
|
+
void client.getThread(threadId).then((thread) => {
|
|
2593
|
+
if (cancelled) return;
|
|
2594
|
+
setActiveRunId(thread.activeRunId ?? null);
|
|
2595
|
+
if (thread.activeRunId != null) {
|
|
2596
|
+
autoResumeRunIdRef.current = thread.activeRunId;
|
|
2597
|
+
setAutoResume(true);
|
|
2598
|
+
}
|
|
2599
|
+
});
|
|
2600
|
+
return () => {
|
|
2601
|
+
cancelled = true;
|
|
2602
|
+
};
|
|
2603
|
+
}, [
|
|
2604
|
+
client,
|
|
2605
|
+
options.resume,
|
|
2606
|
+
options.threadId
|
|
2607
|
+
]);
|
|
2608
|
+
const transport = (0, import_react8.useMemo)(() => {
|
|
2609
|
+
function onMeta(meta) {
|
|
2610
|
+
settlingRunIdRef.current = meta.runId;
|
|
2611
|
+
setRunId(meta.runId);
|
|
2612
|
+
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
2613
|
+
createdThreadId.current = meta.threadId;
|
|
2614
|
+
latest.current.onThreadCreated?.(meta.threadId);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
__name(onMeta, "onMeta");
|
|
2618
|
+
return new AgentChatTransport({
|
|
2619
|
+
...options.baseUrl !== void 0 ? {
|
|
2620
|
+
baseUrl: options.baseUrl
|
|
2621
|
+
} : {},
|
|
2622
|
+
...options.credentials !== void 0 ? {
|
|
2623
|
+
credentials: options.credentials
|
|
2624
|
+
} : {},
|
|
2625
|
+
...options.fetch !== void 0 ? {
|
|
2626
|
+
fetch: options.fetch
|
|
2627
|
+
} : {},
|
|
2628
|
+
...options.agent !== void 0 ? {
|
|
2629
|
+
agent: options.agent
|
|
2630
|
+
} : {},
|
|
2631
|
+
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders"),
|
|
2632
|
+
getBody: /* @__PURE__ */ __name(() => {
|
|
2633
|
+
const current = latest.current;
|
|
2634
|
+
const pageContext = current.getPageContext?.() ?? null;
|
|
2635
|
+
const regenerate2 = regenerateNext.current;
|
|
2636
|
+
regenerateNext.current = false;
|
|
2637
|
+
const threadId = current.threadId ?? createdThreadId.current;
|
|
2638
|
+
return {
|
|
2639
|
+
...threadId !== void 0 ? {
|
|
2640
|
+
threadId
|
|
2641
|
+
} : {},
|
|
2642
|
+
...pageContext ? {
|
|
2643
|
+
pageContext
|
|
2644
|
+
} : {},
|
|
2645
|
+
...regenerate2 ? {
|
|
2646
|
+
regenerate: true
|
|
2647
|
+
} : {}
|
|
2648
|
+
};
|
|
2649
|
+
}, "getBody"),
|
|
2650
|
+
getResumeRunId: /* @__PURE__ */ __name(() => latest.current.resumeRunId ?? autoResumeRunIdRef.current, "getResumeRunId"),
|
|
2651
|
+
onMeta,
|
|
2652
|
+
onAttemptStart: /* @__PURE__ */ __name(() => {
|
|
2653
|
+
settlingRunIdRef.current = void 0;
|
|
2654
|
+
}, "onAttemptStart")
|
|
2655
|
+
});
|
|
2656
|
+
}, []);
|
|
2657
|
+
const backgroundRef = (0, import_react8.useRef)(() => Promise.resolve());
|
|
2658
|
+
const chat = (0, import_react7.useChat)({
|
|
2659
|
+
transport,
|
|
2660
|
+
resume: options.resumeRunId !== void 0 || autoResume,
|
|
2661
|
+
...options.threadId !== void 0 ? {
|
|
2662
|
+
id: options.threadId
|
|
2663
|
+
} : {},
|
|
2664
|
+
...options.initialMessages !== void 0 ? {
|
|
2665
|
+
messages: options.initialMessages
|
|
2666
|
+
} : {},
|
|
2667
|
+
onFinish: /* @__PURE__ */ __name(({ isError }) => {
|
|
2668
|
+
latest.current.onFinish?.();
|
|
2669
|
+
if (latest.current.background === true) {
|
|
2670
|
+
void backgroundRef.current();
|
|
2671
|
+
}
|
|
2672
|
+
const settledRunId = settlingRunIdRef.current;
|
|
2673
|
+
if (settledRunId !== void 0) {
|
|
2674
|
+
latest.current.onRunSettled?.({
|
|
2675
|
+
runId: settledRunId,
|
|
2676
|
+
status: isError ? "failed" : "completed"
|
|
2677
|
+
});
|
|
2678
|
+
}
|
|
2679
|
+
}, "onFinish")
|
|
2680
|
+
});
|
|
2681
|
+
const chatRef = (0, import_react8.useRef)(chat);
|
|
2682
|
+
chatRef.current = chat;
|
|
2683
|
+
const turnInFlight = (0, import_react8.useRef)(false);
|
|
2684
|
+
const isTurnInFlight = (0, import_react8.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
|
|
2685
|
+
transport
|
|
2686
|
+
]);
|
|
2687
|
+
const sendMessage = (0, import_react8.useCallback)(async (...args) => {
|
|
2688
|
+
if (isTurnInFlight()) return;
|
|
2689
|
+
turnInFlight.current = true;
|
|
2690
|
+
try {
|
|
2691
|
+
await chatRef.current.sendMessage(...args);
|
|
2692
|
+
} finally {
|
|
2693
|
+
turnInFlight.current = false;
|
|
2694
|
+
}
|
|
2695
|
+
}, [
|
|
2696
|
+
isTurnInFlight
|
|
2697
|
+
]);
|
|
2698
|
+
const addToolResult = (0, import_react8.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
2699
|
+
tool,
|
|
2700
|
+
toolCallId,
|
|
2701
|
+
output
|
|
2702
|
+
}), []);
|
|
2703
|
+
const [threads, setThreads] = (0, import_react8.useState)([]);
|
|
2704
|
+
const [quota, setQuota] = (0, import_react8.useState)(null);
|
|
2705
|
+
const [backgroundRuns, setBackgroundRuns] = (0, import_react8.useState)([]);
|
|
2706
|
+
const refreshBackground = (0, import_react8.useCallback)(async () => {
|
|
2707
|
+
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
2708
|
+
if (threadId === void 0) {
|
|
2709
|
+
return;
|
|
2710
|
+
}
|
|
2711
|
+
const thread = await client.getThread(threadId);
|
|
2712
|
+
const runs = backgroundRunsFromThread(thread.messages);
|
|
2713
|
+
setBackgroundRuns(runs);
|
|
2714
|
+
const landed = runs.map((run) => run.message).filter((message) => message !== void 0);
|
|
2715
|
+
if (landed.length === 0) {
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
chatRef.current.setMessages((current) => {
|
|
2719
|
+
const known = new Set(current.map((message) => message.id));
|
|
2720
|
+
const missing = landed.filter((message) => !known.has(message.id));
|
|
2721
|
+
return missing.length === 0 ? current : [
|
|
2722
|
+
...current,
|
|
2723
|
+
...missing.map((message) => storedMessageToUiMessage(message))
|
|
2724
|
+
];
|
|
2725
|
+
});
|
|
2726
|
+
}, [
|
|
2727
|
+
client
|
|
2728
|
+
]);
|
|
2729
|
+
backgroundRef.current = refreshBackground;
|
|
2730
|
+
const isWorking = backgroundRuns.some((run) => run.status === "running");
|
|
2731
|
+
(0, import_react8.useEffect)(() => {
|
|
2732
|
+
if (options.background !== true || options.threadId === void 0) {
|
|
2733
|
+
return;
|
|
2734
|
+
}
|
|
2735
|
+
void refreshBackground();
|
|
2736
|
+
}, [
|
|
2737
|
+
options.background,
|
|
2738
|
+
options.threadId,
|
|
2739
|
+
refreshBackground
|
|
2740
|
+
]);
|
|
2741
|
+
(0, import_react8.useEffect)(() => {
|
|
2742
|
+
const every = latest.current.backgroundPollMs ?? 5e3;
|
|
2743
|
+
if (latest.current.background !== true || !isWorking || every <= 0) {
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
const timer = setInterval(() => void refreshBackground(), every);
|
|
2747
|
+
return () => clearInterval(timer);
|
|
2748
|
+
}, [
|
|
2749
|
+
isWorking,
|
|
2750
|
+
refreshBackground
|
|
2751
|
+
]);
|
|
2752
|
+
const loadThreads = (0, import_react8.useCallback)(async () => {
|
|
2753
|
+
const list = await client.listThreads();
|
|
2754
|
+
setThreads(list);
|
|
2755
|
+
return list;
|
|
2756
|
+
}, [
|
|
2757
|
+
client
|
|
2758
|
+
]);
|
|
2759
|
+
const loadThread = (0, import_react8.useCallback)((id) => client.getThread(id), [
|
|
2760
|
+
client
|
|
2761
|
+
]);
|
|
2762
|
+
const deleteThread = (0, import_react8.useCallback)(async (id) => {
|
|
2763
|
+
await client.deleteThread(id);
|
|
2764
|
+
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
2765
|
+
}, [
|
|
2766
|
+
client
|
|
2767
|
+
]);
|
|
2768
|
+
const forkThread = (0, import_react8.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
|
|
2769
|
+
client
|
|
2770
|
+
]);
|
|
2771
|
+
const renameThread = (0, import_react8.useCallback)(async (id, title) => {
|
|
2772
|
+
await client.renameThread(id, title);
|
|
2773
|
+
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
2774
|
+
...thread,
|
|
2775
|
+
title
|
|
2776
|
+
} : thread));
|
|
2777
|
+
}, [
|
|
2778
|
+
client
|
|
2779
|
+
]);
|
|
2780
|
+
const promoteThread = (0, import_react8.useCallback)(async (id) => {
|
|
2781
|
+
await client.promoteThread(id);
|
|
2782
|
+
await loadThreads();
|
|
2783
|
+
}, [
|
|
2784
|
+
client,
|
|
2785
|
+
loadThreads
|
|
2786
|
+
]);
|
|
2787
|
+
const truncateFromMessage = (0, import_react8.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
|
|
2788
|
+
client
|
|
2789
|
+
]);
|
|
2790
|
+
const loadQuota = (0, import_react8.useCallback)(async () => {
|
|
2791
|
+
const today = await client.getQuotaToday();
|
|
2792
|
+
setQuota(today);
|
|
2793
|
+
return today;
|
|
2794
|
+
}, [
|
|
2795
|
+
client
|
|
2796
|
+
]);
|
|
2797
|
+
const cancel = (0, import_react8.useCallback)(async () => {
|
|
2798
|
+
chatRef.current.stop();
|
|
2799
|
+
const activeRunId2 = runIdRef.current;
|
|
2800
|
+
if (activeRunId2) {
|
|
2801
|
+
try {
|
|
2802
|
+
await client.cancelStream(activeRunId2);
|
|
2803
|
+
} catch {
|
|
2804
|
+
}
|
|
2805
|
+
}
|
|
2806
|
+
}, [
|
|
2807
|
+
client
|
|
2808
|
+
]);
|
|
2809
|
+
const approve = (0, import_react8.useCallback)(async ({ toolCallId }) => {
|
|
2810
|
+
await client.approveToolCall({
|
|
2811
|
+
toolCallId
|
|
2812
|
+
});
|
|
2813
|
+
}, [
|
|
2814
|
+
client
|
|
2815
|
+
]);
|
|
2816
|
+
const reject = (0, import_react8.useCallback)(async ({ toolCallId, reason }) => {
|
|
2817
|
+
await client.rejectToolCall({
|
|
2818
|
+
toolCallId,
|
|
2819
|
+
...reason !== void 0 ? {
|
|
2820
|
+
reason
|
|
2821
|
+
} : {}
|
|
2822
|
+
});
|
|
2823
|
+
}, [
|
|
2824
|
+
client
|
|
2825
|
+
]);
|
|
2826
|
+
const answer = (0, import_react8.useCallback)(async ({ toolCallId, answers }) => {
|
|
2827
|
+
await client.answerToolCall({
|
|
2828
|
+
toolCallId,
|
|
2829
|
+
...answers !== void 0 ? {
|
|
2830
|
+
answers
|
|
2831
|
+
} : {}
|
|
2832
|
+
});
|
|
2833
|
+
}, [
|
|
2834
|
+
client
|
|
2835
|
+
]);
|
|
2836
|
+
const skip = (0, import_react8.useCallback)(async ({ toolCallId }) => {
|
|
2837
|
+
await client.skipToolCall({
|
|
2838
|
+
toolCallId
|
|
2839
|
+
});
|
|
2840
|
+
}, [
|
|
2841
|
+
client
|
|
2842
|
+
]);
|
|
2843
|
+
const regenerate = (0, import_react8.useCallback)(() => {
|
|
2844
|
+
if (isTurnInFlight()) return;
|
|
2845
|
+
regenerateNext.current = true;
|
|
2846
|
+
turnInFlight.current = true;
|
|
2847
|
+
void chatRef.current.regenerate().finally(() => {
|
|
2848
|
+
turnInFlight.current = false;
|
|
2849
|
+
});
|
|
2850
|
+
}, [
|
|
2851
|
+
isTurnInFlight
|
|
2852
|
+
]);
|
|
2853
|
+
const background = {
|
|
2854
|
+
runs: backgroundRuns,
|
|
2855
|
+
isWorking,
|
|
2856
|
+
refresh: refreshBackground
|
|
2857
|
+
};
|
|
2858
|
+
return {
|
|
2859
|
+
...chat,
|
|
2860
|
+
sendMessage,
|
|
2861
|
+
addToolResult,
|
|
2862
|
+
/** Sub-agents this conversation started and did not wait for. */
|
|
2863
|
+
background,
|
|
2864
|
+
runId,
|
|
2865
|
+
/** The `resume`-fetched thread's active run id, or `null` once resolved with none. */
|
|
2866
|
+
activeRunId,
|
|
2867
|
+
client,
|
|
2868
|
+
threads,
|
|
2869
|
+
loadThreads,
|
|
2870
|
+
loadThread,
|
|
2871
|
+
deleteThread,
|
|
2872
|
+
forkThread,
|
|
2873
|
+
renameThread,
|
|
2874
|
+
promoteThread,
|
|
2875
|
+
truncateFromMessage,
|
|
2876
|
+
quota,
|
|
2877
|
+
loadQuota,
|
|
2878
|
+
cancel,
|
|
2879
|
+
approve,
|
|
2880
|
+
reject,
|
|
2881
|
+
answer,
|
|
2882
|
+
skip,
|
|
2883
|
+
regenerate
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
__name(useAgentChat, "useAgentChat");
|
|
2887
|
+
async function mergeHeaders(options) {
|
|
2888
|
+
const dynamic = await options.getHeaders?.() ?? {};
|
|
2889
|
+
return {
|
|
2890
|
+
...options.headers,
|
|
2891
|
+
...dynamic
|
|
2892
|
+
};
|
|
2893
|
+
}
|
|
2894
|
+
__name(mergeHeaders, "mergeHeaders");
|
|
1478
2895
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1479
2896
|
0 && (module.exports = {
|
|
1480
2897
|
AgentChatTransport,
|
|
@@ -1483,14 +2900,28 @@ __name(persistSpeechLang, "persistSpeechLang");
|
|
|
1483
2900
|
ChatInput,
|
|
1484
2901
|
DEFAULT_SPEECH_LANG,
|
|
1485
2902
|
MessageItem,
|
|
2903
|
+
MessageItemView,
|
|
1486
2904
|
MessageList,
|
|
1487
2905
|
SPEECH_LANGUAGES,
|
|
2906
|
+
applyCompletion,
|
|
2907
|
+
backgroundRunsFromThread,
|
|
2908
|
+
buildTranscriptBlocks,
|
|
2909
|
+
createSkillsSource,
|
|
2910
|
+
describeTimestamp,
|
|
2911
|
+
describeUsage,
|
|
2912
|
+
extractMessageText,
|
|
2913
|
+
filterAutocompleteItems,
|
|
2914
|
+
findActiveTrigger,
|
|
1488
2915
|
formatRelativeTime,
|
|
1489
2916
|
loadStoredSpeechLang,
|
|
1490
2917
|
persistSpeechLang,
|
|
1491
2918
|
storedMessageToUiMessage,
|
|
1492
2919
|
storedThreadToUiMessages,
|
|
1493
2920
|
useAgentChat,
|
|
1494
|
-
|
|
2921
|
+
useChatTranscript,
|
|
2922
|
+
useComposerAutocomplete,
|
|
2923
|
+
useSpeechRecognition,
|
|
2924
|
+
useStickToBottom,
|
|
2925
|
+
useTranscriptItem
|
|
1495
2926
|
});
|
|
1496
2927
|
//# sourceMappingURL=index.cjs.map
|