@dudousxd/nestjs-agent-react 0.7.5 → 0.9.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 +35 -2
- package/dist/index.cjs +451 -179
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +119 -12
- package/dist/index.d.ts +119 -12
- package/dist/index.js +358 -89
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -49,20 +49,82 @@ __export(src_exports, {
|
|
|
49
49
|
extractMessageText: () => extractMessageText,
|
|
50
50
|
filterAutocompleteItems: () => filterAutocompleteItems,
|
|
51
51
|
findActiveTrigger: () => findActiveTrigger,
|
|
52
|
+
formatElapsed: () => formatElapsed,
|
|
52
53
|
formatRelativeTime: () => formatRelativeTime,
|
|
53
54
|
loadStoredSpeechLang: () => loadStoredSpeechLang,
|
|
54
55
|
persistSpeechLang: () => persistSpeechLang,
|
|
56
|
+
readReasoningMs: () => readReasoningMs,
|
|
55
57
|
storedMessageToUiMessage: () => storedMessageToUiMessage,
|
|
56
58
|
storedThreadToUiMessages: () => storedThreadToUiMessages,
|
|
57
59
|
useAgentChat: () => useAgentChat,
|
|
58
60
|
useChatTranscript: () => useChatTranscript,
|
|
59
61
|
useComposerAutocomplete: () => useComposerAutocomplete,
|
|
62
|
+
useElapsed: () => useElapsed,
|
|
60
63
|
useSpeechRecognition: () => useSpeechRecognition,
|
|
61
64
|
useStickToBottom: () => useStickToBottom,
|
|
62
65
|
useTranscriptItem: () => useTranscriptItem
|
|
63
66
|
});
|
|
64
67
|
module.exports = __toCommonJS(src_exports);
|
|
65
68
|
|
|
69
|
+
// src/reasoning/timing.ts
|
|
70
|
+
var import_react = require("react");
|
|
71
|
+
var REASONING_METADATA_KEY = "agent";
|
|
72
|
+
function reasoningDurationMetadata(reasoningMs) {
|
|
73
|
+
return {
|
|
74
|
+
[REASONING_METADATA_KEY]: {
|
|
75
|
+
reasoningMs
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
__name(reasoningDurationMetadata, "reasoningDurationMetadata");
|
|
80
|
+
function readReasoningMs(part) {
|
|
81
|
+
const metadata = part.providerMetadata;
|
|
82
|
+
if (typeof metadata !== "object" || metadata === null) return null;
|
|
83
|
+
const scoped = metadata[REASONING_METADATA_KEY];
|
|
84
|
+
if (typeof scoped !== "object" || scoped === null) return null;
|
|
85
|
+
const value = scoped.reasoningMs;
|
|
86
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : null;
|
|
87
|
+
}
|
|
88
|
+
__name(readReasoningMs, "readReasoningMs");
|
|
89
|
+
function useElapsed(running, options = {}) {
|
|
90
|
+
const { intervalMs = 1e3 } = options;
|
|
91
|
+
const nowRef = (0, import_react.useRef)(options.now ?? Date.now);
|
|
92
|
+
nowRef.current = options.now ?? Date.now;
|
|
93
|
+
const startedAt = (0, import_react.useRef)(null);
|
|
94
|
+
const [elapsed, setElapsed] = (0, import_react.useState)(0);
|
|
95
|
+
(0, import_react.useEffect)(() => {
|
|
96
|
+
if (!running) {
|
|
97
|
+
if (startedAt.current !== null) {
|
|
98
|
+
setElapsed(nowRef.current() - startedAt.current);
|
|
99
|
+
startedAt.current = null;
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
startedAt.current = nowRef.current();
|
|
104
|
+
setElapsed(0);
|
|
105
|
+
const timer = setInterval(() => {
|
|
106
|
+
if (startedAt.current !== null) setElapsed(nowRef.current() - startedAt.current);
|
|
107
|
+
}, intervalMs);
|
|
108
|
+
return () => clearInterval(timer);
|
|
109
|
+
}, [
|
|
110
|
+
running,
|
|
111
|
+
intervalMs
|
|
112
|
+
]);
|
|
113
|
+
return elapsed;
|
|
114
|
+
}
|
|
115
|
+
__name(useElapsed, "useElapsed");
|
|
116
|
+
function formatElapsed(ms) {
|
|
117
|
+
if (!Number.isFinite(ms) || ms < 1e3) return "<1s";
|
|
118
|
+
const totalSeconds = Math.floor(ms / 1e3);
|
|
119
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
120
|
+
const minutes = Math.floor(totalSeconds % 3600 / 60);
|
|
121
|
+
const seconds = totalSeconds % 60;
|
|
122
|
+
if (hours > 0) return minutes > 0 ? `${hours}h ${minutes}m` : `${hours}h`;
|
|
123
|
+
if (minutes > 0) return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
|
|
124
|
+
return `${seconds}s`;
|
|
125
|
+
}
|
|
126
|
+
__name(formatElapsed, "formatElapsed");
|
|
127
|
+
|
|
66
128
|
// src/agent-chat-transport.ts
|
|
67
129
|
var HEADER_RUN_ID = "x-agent-run-id";
|
|
68
130
|
var HEADER_THREAD_ID = "x-agent-thread-id";
|
|
@@ -202,7 +264,8 @@ var AgentChatTransport = class {
|
|
|
202
264
|
* Parse the backend's SSE byte stream and re-emit it as a valid v7 UI-message chunk stream.
|
|
203
265
|
* Recognized frames:
|
|
204
266
|
* - `event: meta` `data: {"runId","threadId"}` → records identity
|
|
205
|
-
* - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool
|
|
267
|
+
* - `data: <AgentStreamEvent JSON>` → mapped to UI chunks (text / reasoning / tool /
|
|
268
|
+
* `data-*`; see {@link AgentChatTransport} docs)
|
|
206
269
|
* - `event: done` `data: {}` → terminates
|
|
207
270
|
* - `event: error` `data: {code,message}` → error chunk
|
|
208
271
|
*
|
|
@@ -219,7 +282,11 @@ var AgentChatTransport = class {
|
|
|
219
282
|
let stepIndex = 0;
|
|
220
283
|
let textId = null;
|
|
221
284
|
let reasoningId = null;
|
|
285
|
+
let reasoningStartedAt = 0;
|
|
286
|
+
let textSeq = 0;
|
|
287
|
+
let reasoningSeq = 0;
|
|
222
288
|
const announced = /* @__PURE__ */ new Set();
|
|
289
|
+
const toolMetadata = /* @__PURE__ */ new Map();
|
|
223
290
|
const record = /* @__PURE__ */ __name((meta) => this.recordMeta(meta), "record");
|
|
224
291
|
const endAttempt = /* @__PURE__ */ __name(() => {
|
|
225
292
|
this.attemptLive = false;
|
|
@@ -241,6 +308,8 @@ var AgentChatTransport = class {
|
|
|
241
308
|
stepIndex += 1;
|
|
242
309
|
textId = null;
|
|
243
310
|
reasoningId = null;
|
|
311
|
+
textSeq = 0;
|
|
312
|
+
reasoningSeq = 0;
|
|
244
313
|
controller.enqueue({
|
|
245
314
|
type: "start-step"
|
|
246
315
|
});
|
|
@@ -250,8 +319,7 @@ var AgentChatTransport = class {
|
|
|
250
319
|
if (!stepOpen) openStep();
|
|
251
320
|
}
|
|
252
321
|
__name(ensureStep, "ensureStep");
|
|
253
|
-
function
|
|
254
|
-
if (!stepOpen) return;
|
|
322
|
+
function closeRuns(reasoningMs) {
|
|
255
323
|
if (textId !== null) {
|
|
256
324
|
controller.enqueue({
|
|
257
325
|
type: "text-end",
|
|
@@ -262,23 +330,55 @@ var AgentChatTransport = class {
|
|
|
262
330
|
if (reasoningId !== null) {
|
|
263
331
|
controller.enqueue({
|
|
264
332
|
type: "reasoning-end",
|
|
265
|
-
id: reasoningId
|
|
333
|
+
id: reasoningId,
|
|
334
|
+
providerMetadata: reasoningDurationMetadata(reasoningMs ?? Math.max(0, Date.now() - reasoningStartedAt))
|
|
266
335
|
});
|
|
267
336
|
reasoningId = null;
|
|
268
337
|
}
|
|
338
|
+
}
|
|
339
|
+
__name(closeRuns, "closeRuns");
|
|
340
|
+
function closeStep(reasoningMs) {
|
|
341
|
+
if (!stepOpen) return;
|
|
342
|
+
closeRuns(reasoningMs);
|
|
269
343
|
controller.enqueue({
|
|
270
344
|
type: "finish-step"
|
|
271
345
|
});
|
|
272
346
|
stepOpen = false;
|
|
273
347
|
}
|
|
274
348
|
__name(closeStep, "closeStep");
|
|
349
|
+
function metadataFor(id, toolKind2, parentId) {
|
|
350
|
+
const merged = {
|
|
351
|
+
...toolMetadata.get(id)
|
|
352
|
+
};
|
|
353
|
+
if (toolKind2 !== void 0) merged.toolKind = toolKind2;
|
|
354
|
+
if (parentId !== void 0 && merged.parentId === void 0) merged.parentId = parentId;
|
|
355
|
+
if (Object.keys(merged).length === 0) return void 0;
|
|
356
|
+
toolMetadata.set(id, merged);
|
|
357
|
+
return merged;
|
|
358
|
+
}
|
|
359
|
+
__name(metadataFor, "metadataFor");
|
|
360
|
+
function forwardAsData(event, transient) {
|
|
361
|
+
ensureStarted();
|
|
362
|
+
const { kind, ...data } = event;
|
|
363
|
+
controller.enqueue({
|
|
364
|
+
type: `data-${kind}`,
|
|
365
|
+
...typeof data.id === "string" ? {
|
|
366
|
+
id: data.id
|
|
367
|
+
} : {},
|
|
368
|
+
data,
|
|
369
|
+
...transient ? {
|
|
370
|
+
transient: true
|
|
371
|
+
} : {}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
__name(forwardAsData, "forwardAsData");
|
|
275
375
|
function emit(event) {
|
|
276
376
|
switch (event.kind) {
|
|
277
377
|
case "step-start":
|
|
278
378
|
openStep();
|
|
279
379
|
break;
|
|
280
380
|
case "step-finish":
|
|
281
|
-
closeStep();
|
|
381
|
+
closeStep(event.reasoningMs);
|
|
282
382
|
if (event.costUsd !== void 0) {
|
|
283
383
|
controller.enqueue({
|
|
284
384
|
type: "message-metadata",
|
|
@@ -291,7 +391,8 @@ var AgentChatTransport = class {
|
|
|
291
391
|
case "text":
|
|
292
392
|
ensureStep();
|
|
293
393
|
if (textId === null) {
|
|
294
|
-
textId = `txt-${stepIndex}`;
|
|
394
|
+
textId = textSeq === 0 ? `txt-${stepIndex}` : `txt-${stepIndex}.${textSeq}`;
|
|
395
|
+
textSeq += 1;
|
|
295
396
|
controller.enqueue({
|
|
296
397
|
type: "text-start",
|
|
297
398
|
id: textId
|
|
@@ -306,7 +407,9 @@ var AgentChatTransport = class {
|
|
|
306
407
|
case "reasoning":
|
|
307
408
|
ensureStep();
|
|
308
409
|
if (reasoningId === null) {
|
|
309
|
-
reasoningId = `rsn-${stepIndex}`;
|
|
410
|
+
reasoningId = reasoningSeq === 0 ? `rsn-${stepIndex}` : `rsn-${stepIndex}.${reasoningSeq}`;
|
|
411
|
+
reasoningSeq += 1;
|
|
412
|
+
reasoningStartedAt = Date.now();
|
|
310
413
|
controller.enqueue({
|
|
311
414
|
type: "reasoning-start",
|
|
312
415
|
id: reasoningId
|
|
@@ -318,23 +421,20 @@ var AgentChatTransport = class {
|
|
|
318
421
|
delta: event.text
|
|
319
422
|
});
|
|
320
423
|
break;
|
|
321
|
-
case "tool-input-start":
|
|
424
|
+
case "tool-input-start": {
|
|
322
425
|
ensureStep();
|
|
323
426
|
announced.add(event.id);
|
|
427
|
+
const metadata = metadataFor(event.id, event.toolKind, event.parentId);
|
|
324
428
|
controller.enqueue({
|
|
325
429
|
type: "tool-input-start",
|
|
326
430
|
toolCallId: event.id,
|
|
327
431
|
toolName: event.name,
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
// UI gate approval affordances on `kind === 'action'` without hardcoding tool names.
|
|
331
|
-
...event.toolKind !== void 0 ? {
|
|
332
|
-
toolMetadata: {
|
|
333
|
-
toolKind: event.toolKind
|
|
334
|
-
}
|
|
432
|
+
...metadata !== void 0 ? {
|
|
433
|
+
toolMetadata: metadata
|
|
335
434
|
} : {}
|
|
336
435
|
});
|
|
337
436
|
break;
|
|
437
|
+
}
|
|
338
438
|
case "tool-input-delta":
|
|
339
439
|
ensureStep();
|
|
340
440
|
controller.enqueue({
|
|
@@ -343,21 +443,21 @@ var AgentChatTransport = class {
|
|
|
343
443
|
inputTextDelta: event.delta
|
|
344
444
|
});
|
|
345
445
|
break;
|
|
346
|
-
case "tool-input-available":
|
|
446
|
+
case "tool-input-available": {
|
|
347
447
|
ensureStep();
|
|
348
448
|
announced.add(event.id);
|
|
449
|
+
const metadata = metadataFor(event.id, event.toolKind, event.parentId);
|
|
349
450
|
controller.enqueue({
|
|
350
451
|
type: "tool-input-available",
|
|
351
452
|
toolCallId: event.id,
|
|
352
453
|
toolName: event.name,
|
|
353
454
|
input: event.input,
|
|
354
|
-
...
|
|
355
|
-
toolMetadata:
|
|
356
|
-
toolKind: event.toolKind
|
|
357
|
-
}
|
|
455
|
+
...metadata !== void 0 ? {
|
|
456
|
+
toolMetadata: metadata
|
|
358
457
|
} : {}
|
|
359
458
|
});
|
|
360
459
|
break;
|
|
460
|
+
}
|
|
361
461
|
case "elicitation":
|
|
362
462
|
ensureStep();
|
|
363
463
|
if (!announced.has(event.id)) {
|
|
@@ -398,7 +498,39 @@ var AgentChatTransport = class {
|
|
|
398
498
|
toolCallId: event.id
|
|
399
499
|
});
|
|
400
500
|
break;
|
|
501
|
+
case "approval-requested":
|
|
502
|
+
ensureStep();
|
|
503
|
+
forwardAsData(event, false);
|
|
504
|
+
if (announced.has(event.id)) {
|
|
505
|
+
controller.enqueue({
|
|
506
|
+
type: "tool-approval-request",
|
|
507
|
+
approvalId: event.id,
|
|
508
|
+
toolCallId: event.id
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
break;
|
|
512
|
+
case "ui":
|
|
513
|
+
ensureStep();
|
|
514
|
+
closeRuns();
|
|
515
|
+
controller.enqueue({
|
|
516
|
+
type: "data-ui",
|
|
517
|
+
id: event.id,
|
|
518
|
+
data: {
|
|
519
|
+
id: event.id,
|
|
520
|
+
component: event.component,
|
|
521
|
+
props: event.props,
|
|
522
|
+
...event.version !== void 0 ? {
|
|
523
|
+
version: event.version
|
|
524
|
+
} : {}
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
break;
|
|
528
|
+
case "title":
|
|
529
|
+
case "cancelled":
|
|
530
|
+
forwardAsData(event, true);
|
|
531
|
+
break;
|
|
401
532
|
default:
|
|
533
|
+
forwardAsData(event, false);
|
|
402
534
|
break;
|
|
403
535
|
}
|
|
404
536
|
}
|
|
@@ -725,10 +857,10 @@ var AgentClient = class {
|
|
|
725
857
|
};
|
|
726
858
|
|
|
727
859
|
// src/components/chat-input.tsx
|
|
728
|
-
var
|
|
860
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
729
861
|
|
|
730
862
|
// src/components/use-speech-recognition.ts
|
|
731
|
-
var
|
|
863
|
+
var import_react2 = require("react");
|
|
732
864
|
function getRecognitionCtor() {
|
|
733
865
|
if (typeof window === "undefined") return null;
|
|
734
866
|
const scope = window;
|
|
@@ -736,15 +868,15 @@ function getRecognitionCtor() {
|
|
|
736
868
|
}
|
|
737
869
|
__name(getRecognitionCtor, "getRecognitionCtor");
|
|
738
870
|
function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTranscript }) {
|
|
739
|
-
const recognitionRef = (0,
|
|
740
|
-
const [isListening, setIsListening] = (0,
|
|
741
|
-
const ctor = (0,
|
|
871
|
+
const recognitionRef = (0, import_react2.useRef)(null);
|
|
872
|
+
const [isListening, setIsListening] = (0, import_react2.useState)(false);
|
|
873
|
+
const ctor = (0, import_react2.useMemo)(() => getRecognitionCtor(), []);
|
|
742
874
|
const isSupported = ctor !== null;
|
|
743
|
-
const finalRef = (0,
|
|
744
|
-
const interimRef = (0,
|
|
875
|
+
const finalRef = (0, import_react2.useRef)(onFinalTranscript);
|
|
876
|
+
const interimRef = (0, import_react2.useRef)(onInterimTranscript);
|
|
745
877
|
finalRef.current = onFinalTranscript;
|
|
746
878
|
interimRef.current = onInterimTranscript;
|
|
747
|
-
(0,
|
|
879
|
+
(0, import_react2.useEffect)(() => {
|
|
748
880
|
if (!ctor) return;
|
|
749
881
|
const recognition = new ctor();
|
|
750
882
|
recognition.lang = lang;
|
|
@@ -783,7 +915,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
783
915
|
ctor,
|
|
784
916
|
lang
|
|
785
917
|
]);
|
|
786
|
-
const start = (0,
|
|
918
|
+
const start = (0, import_react2.useCallback)(() => {
|
|
787
919
|
const recognition = recognitionRef.current;
|
|
788
920
|
if (!recognition) return;
|
|
789
921
|
try {
|
|
@@ -793,7 +925,7 @@ function useSpeechRecognition({ lang = "en-US", onFinalTranscript, onInterimTran
|
|
|
793
925
|
console.warn("SpeechRecognition start failed", error);
|
|
794
926
|
}
|
|
795
927
|
}, []);
|
|
796
|
-
const stop = (0,
|
|
928
|
+
const stop = (0, import_react2.useCallback)(() => {
|
|
797
929
|
const recognition = recognitionRef.current;
|
|
798
930
|
if (!recognition) return;
|
|
799
931
|
try {
|
|
@@ -812,8 +944,8 @@ __name(useSpeechRecognition, "useSpeechRecognition");
|
|
|
812
944
|
|
|
813
945
|
// src/components/chat-input.tsx
|
|
814
946
|
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 }) {
|
|
815
|
-
const [value, setValue] = (0,
|
|
816
|
-
const [interim, setInterim] = (0,
|
|
947
|
+
const [value, setValue] = (0, import_react3.useState)("");
|
|
948
|
+
const [interim, setInterim] = (0, import_react3.useState)("");
|
|
817
949
|
const speech = useSpeechRecognition({
|
|
818
950
|
lang: speechLang,
|
|
819
951
|
onFinalTranscript: /* @__PURE__ */ __name((text) => {
|
|
@@ -824,7 +956,7 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
824
956
|
}, "onFinalTranscript"),
|
|
825
957
|
onInterimTranscript: /* @__PURE__ */ __name((text) => setInterim(text), "onInterimTranscript")
|
|
826
958
|
});
|
|
827
|
-
(0,
|
|
959
|
+
(0, import_react3.useEffect)(() => {
|
|
828
960
|
if (!speech.isListening) setInterim("");
|
|
829
961
|
}, [
|
|
830
962
|
speech.isListening
|
|
@@ -852,9 +984,9 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
852
984
|
const displayValue = speech.isListening && interim ? `${value}${value && !value.endsWith(" ") ? " " : ""}${interim}` : value;
|
|
853
985
|
const showMic = enableSpeech && speech.isSupported;
|
|
854
986
|
const showStop = isStreaming && onStop !== void 0;
|
|
855
|
-
return /* @__PURE__ */
|
|
987
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", {
|
|
856
988
|
className
|
|
857
|
-
}, /* @__PURE__ */
|
|
989
|
+
}, /* @__PURE__ */ import_react3.default.createElement("textarea", {
|
|
858
990
|
className: textareaClassName,
|
|
859
991
|
rows,
|
|
860
992
|
value: displayValue,
|
|
@@ -865,17 +997,17 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
865
997
|
}, "onChange"),
|
|
866
998
|
onKeyDown: handleKeyDown,
|
|
867
999
|
placeholder: placeholder ?? "Ask something (Enter to send, Shift+Enter for newline)"
|
|
868
|
-
}), /* @__PURE__ */
|
|
1000
|
+
}), /* @__PURE__ */ import_react3.default.createElement("button", {
|
|
869
1001
|
type: "button",
|
|
870
1002
|
className: sendButtonClassName,
|
|
871
1003
|
onClick: submit,
|
|
872
1004
|
disabled: disabled || !value.trim()
|
|
873
|
-
}, sendLabel), showStop ? /* @__PURE__ */
|
|
1005
|
+
}, sendLabel), showStop ? /* @__PURE__ */ import_react3.default.createElement("button", {
|
|
874
1006
|
type: "button",
|
|
875
1007
|
className: stopButtonClassName,
|
|
876
1008
|
onClick: /* @__PURE__ */ __name(() => void onStop(), "onClick"),
|
|
877
1009
|
"aria-label": "Stop generating"
|
|
878
|
-
}, stopLabel) : null, showMic ? /* @__PURE__ */
|
|
1010
|
+
}, stopLabel) : null, showMic ? /* @__PURE__ */ import_react3.default.createElement("button", {
|
|
879
1011
|
type: "button",
|
|
880
1012
|
className: micButtonClassName,
|
|
881
1013
|
onClick: toggleMic,
|
|
@@ -888,10 +1020,10 @@ function ChatInput({ onSubmit, disabled, placeholder, rows = 2, enableSpeech = t
|
|
|
888
1020
|
__name(ChatInput, "ChatInput");
|
|
889
1021
|
|
|
890
1022
|
// src/components/message-item.tsx
|
|
891
|
-
var
|
|
1023
|
+
var import_react6 = __toESM(require("react"), 1);
|
|
892
1024
|
|
|
893
1025
|
// src/transcript/use-chat-transcript.ts
|
|
894
|
-
var
|
|
1026
|
+
var import_react5 = require("react");
|
|
895
1027
|
|
|
896
1028
|
// src/transcript/model.ts
|
|
897
1029
|
var import_ai = require("ai");
|
|
@@ -906,15 +1038,18 @@ function buildTranscriptBlocks(message, options) {
|
|
|
906
1038
|
let reasoningCounter = 0;
|
|
907
1039
|
let sourcesCounter = 0;
|
|
908
1040
|
let elicitationCounter = 0;
|
|
1041
|
+
const approvals = readApprovals(message.parts ?? []);
|
|
909
1042
|
function flushTools() {
|
|
910
1043
|
if (toolBuffer.length === 0) {
|
|
911
1044
|
return;
|
|
912
1045
|
}
|
|
1046
|
+
const calls = toolBuffer.map((part) => buildToolCall(part, options.approval, approvals.get(part.toolCallId) ?? null));
|
|
913
1047
|
blocks.push({
|
|
914
1048
|
kind: "tools",
|
|
915
1049
|
key: `${message.id}-tools-${toolCounter++}`,
|
|
916
1050
|
parts: toolBuffer,
|
|
917
|
-
calls
|
|
1051
|
+
calls,
|
|
1052
|
+
roots: nestToolCalls(calls)
|
|
918
1053
|
});
|
|
919
1054
|
toolBuffer = [];
|
|
920
1055
|
}
|
|
@@ -977,7 +1112,21 @@ function buildTranscriptBlocks(message, options) {
|
|
|
977
1112
|
fileBuffer.push(part);
|
|
978
1113
|
continue;
|
|
979
1114
|
}
|
|
1115
|
+
if (part.type === APPROVAL_PART) {
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
980
1118
|
flushAll();
|
|
1119
|
+
if ((0, import_ai.isDataUIPart)(part)) {
|
|
1120
|
+
const ui = part.type === UI_PART ? readUiComponent(part) : null;
|
|
1121
|
+
if (ui !== null) {
|
|
1122
|
+
blocks.push({
|
|
1123
|
+
kind: "ui",
|
|
1124
|
+
key: `${message.id}-ui-${ui.id}`,
|
|
1125
|
+
...ui
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
continue;
|
|
1129
|
+
}
|
|
981
1130
|
if ((0, import_ai.isTextUIPart)(part)) {
|
|
982
1131
|
blocks.push({
|
|
983
1132
|
kind: "text",
|
|
@@ -995,6 +1144,7 @@ function buildTranscriptBlocks(message, options) {
|
|
|
995
1144
|
key,
|
|
996
1145
|
text: part.text,
|
|
997
1146
|
isStreaming,
|
|
1147
|
+
durationMs: readReasoningMs(part),
|
|
998
1148
|
isOpen: options.isReasoningOpen(key, isStreaming),
|
|
999
1149
|
toggle: /* @__PURE__ */ __name((open) => options.toggleReasoning(key, open), "toggle")
|
|
1000
1150
|
});
|
|
@@ -1061,7 +1211,42 @@ function isAwaitingApproval(part) {
|
|
|
1061
1211
|
return toolKind(part) === "action" && readElicitationRequest(part) === null;
|
|
1062
1212
|
}
|
|
1063
1213
|
__name(isAwaitingApproval, "isAwaitingApproval");
|
|
1064
|
-
function
|
|
1214
|
+
function toolParentId(part) {
|
|
1215
|
+
const metadata = part.toolMetadata;
|
|
1216
|
+
return isRecord(metadata) && typeof metadata.parentId === "string" ? metadata.parentId : null;
|
|
1217
|
+
}
|
|
1218
|
+
__name(toolParentId, "toolParentId");
|
|
1219
|
+
function nestToolCalls(calls) {
|
|
1220
|
+
const byId = new Map(calls.map((call) => [
|
|
1221
|
+
call.toolCallId,
|
|
1222
|
+
call
|
|
1223
|
+
]));
|
|
1224
|
+
const roots = [];
|
|
1225
|
+
for (const call of calls) {
|
|
1226
|
+
const parent = call.parentId !== null ? byId.get(call.parentId) : void 0;
|
|
1227
|
+
if (parent === void 0 || isAncestor(call, parent, byId)) {
|
|
1228
|
+
roots.push(call);
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
parent.children.push(call);
|
|
1232
|
+
}
|
|
1233
|
+
return roots;
|
|
1234
|
+
}
|
|
1235
|
+
__name(nestToolCalls, "nestToolCalls");
|
|
1236
|
+
function isAncestor(call, candidate, byId) {
|
|
1237
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1238
|
+
let current = candidate;
|
|
1239
|
+
while (current !== void 0 && !seen.has(current.toolCallId)) {
|
|
1240
|
+
if (current.toolCallId === call.toolCallId) {
|
|
1241
|
+
return true;
|
|
1242
|
+
}
|
|
1243
|
+
seen.add(current.toolCallId);
|
|
1244
|
+
current = current.parentId !== null ? byId.get(current.parentId) : void 0;
|
|
1245
|
+
}
|
|
1246
|
+
return false;
|
|
1247
|
+
}
|
|
1248
|
+
__name(isAncestor, "isAncestor");
|
|
1249
|
+
function buildToolCall(part, options, approval) {
|
|
1065
1250
|
const toolCallId = part.toolCallId;
|
|
1066
1251
|
const awaiting = isAwaitingApproval(part);
|
|
1067
1252
|
const sending = awaiting ? options?.submitting(toolCallId) ?? null : null;
|
|
@@ -1069,6 +1254,10 @@ function buildToolCall(part, options) {
|
|
|
1069
1254
|
part,
|
|
1070
1255
|
toolCallId,
|
|
1071
1256
|
name: (0, import_ai.getToolName)(part),
|
|
1257
|
+
toolKind: toolKind(part) ?? null,
|
|
1258
|
+
parentId: toolParentId(part),
|
|
1259
|
+
children: [],
|
|
1260
|
+
approval,
|
|
1072
1261
|
isAwaitingApproval: awaiting,
|
|
1073
1262
|
approve: {
|
|
1074
1263
|
available: awaiting && options?.canApprove === true,
|
|
@@ -1199,6 +1388,44 @@ function buildElicitationBlock(key, part, request, options) {
|
|
|
1199
1388
|
};
|
|
1200
1389
|
}
|
|
1201
1390
|
__name(buildElicitationBlock, "buildElicitationBlock");
|
|
1391
|
+
var UI_PART = "data-ui";
|
|
1392
|
+
var APPROVAL_PART = "data-approval-requested";
|
|
1393
|
+
function readApprovals(parts) {
|
|
1394
|
+
const out = /* @__PURE__ */ new Map();
|
|
1395
|
+
for (const part of parts) {
|
|
1396
|
+
if (part.type !== APPROVAL_PART) {
|
|
1397
|
+
continue;
|
|
1398
|
+
}
|
|
1399
|
+
const data = part.data;
|
|
1400
|
+
if (!isRecord(data) || typeof data.id !== "string" || typeof data.approver !== "string") {
|
|
1401
|
+
continue;
|
|
1402
|
+
}
|
|
1403
|
+
out.set(data.id, {
|
|
1404
|
+
approver: data.approver,
|
|
1405
|
+
expiresAt: typeof data.expiresAt === "string" ? data.expiresAt : null,
|
|
1406
|
+
reason: typeof data.reason === "string" ? data.reason : null
|
|
1407
|
+
});
|
|
1408
|
+
}
|
|
1409
|
+
return out;
|
|
1410
|
+
}
|
|
1411
|
+
__name(readApprovals, "readApprovals");
|
|
1412
|
+
function readUiComponent(part) {
|
|
1413
|
+
const data = part.data;
|
|
1414
|
+
if (!isRecord(data) || typeof data.component !== "string") {
|
|
1415
|
+
return null;
|
|
1416
|
+
}
|
|
1417
|
+
const id = typeof data.id === "string" ? data.id : part.id;
|
|
1418
|
+
if (id === void 0) {
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1421
|
+
return {
|
|
1422
|
+
id,
|
|
1423
|
+
component: data.component,
|
|
1424
|
+
props: isRecord(data.props) ? data.props : {},
|
|
1425
|
+
version: typeof data.version === "number" ? data.version : null
|
|
1426
|
+
};
|
|
1427
|
+
}
|
|
1428
|
+
__name(readUiComponent, "readUiComponent");
|
|
1202
1429
|
function isRecord(value) {
|
|
1203
1430
|
return typeof value === "object" && value !== null;
|
|
1204
1431
|
}
|
|
@@ -1330,11 +1557,11 @@ function formatCostUsd(cost) {
|
|
|
1330
1557
|
__name(formatCostUsd, "formatCostUsd");
|
|
1331
1558
|
|
|
1332
1559
|
// src/transcript/use-stick-to-bottom.ts
|
|
1333
|
-
var
|
|
1560
|
+
var import_react4 = require("react");
|
|
1334
1561
|
function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
1335
|
-
const containerRef = (0,
|
|
1336
|
-
const [isAtBottom, setIsAtBottom] = (0,
|
|
1337
|
-
const measure = (0,
|
|
1562
|
+
const containerRef = (0, import_react4.useRef)(null);
|
|
1563
|
+
const [isAtBottom, setIsAtBottom] = (0, import_react4.useState)(true);
|
|
1564
|
+
const measure = (0, import_react4.useCallback)(() => {
|
|
1338
1565
|
const element = containerRef.current;
|
|
1339
1566
|
if (!element) {
|
|
1340
1567
|
return;
|
|
@@ -1344,7 +1571,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1344
1571
|
}, [
|
|
1345
1572
|
threshold
|
|
1346
1573
|
]);
|
|
1347
|
-
const scrollToBottom = (0,
|
|
1574
|
+
const scrollToBottom = (0, import_react4.useCallback)(() => {
|
|
1348
1575
|
const element = containerRef.current;
|
|
1349
1576
|
if (!element) {
|
|
1350
1577
|
return;
|
|
@@ -1352,7 +1579,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1352
1579
|
element.scrollTop = element.scrollHeight;
|
|
1353
1580
|
setIsAtBottom(true);
|
|
1354
1581
|
}, []);
|
|
1355
|
-
const setContainer = (0,
|
|
1582
|
+
const setContainer = (0, import_react4.useCallback)((element) => {
|
|
1356
1583
|
containerRef.current = element;
|
|
1357
1584
|
if (element) {
|
|
1358
1585
|
measure();
|
|
@@ -1360,7 +1587,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1360
1587
|
}, [
|
|
1361
1588
|
measure
|
|
1362
1589
|
]);
|
|
1363
|
-
(0,
|
|
1590
|
+
(0, import_react4.useEffect)(() => {
|
|
1364
1591
|
if (isAtBottom) {
|
|
1365
1592
|
scrollToBottom();
|
|
1366
1593
|
}
|
|
@@ -1369,7 +1596,7 @@ function useStickToBottom({ contentKey, threshold = 32 }) {
|
|
|
1369
1596
|
isAtBottom,
|
|
1370
1597
|
scrollToBottom
|
|
1371
1598
|
]);
|
|
1372
|
-
const getContainerProps = (0,
|
|
1599
|
+
const getContainerProps = (0, import_react4.useCallback)(() => ({
|
|
1373
1600
|
ref: setContainer,
|
|
1374
1601
|
onScroll: measure
|
|
1375
1602
|
}), [
|
|
@@ -1391,14 +1618,14 @@ var DEFAULT_LOAD_EARLIER_STEP = 50;
|
|
|
1391
1618
|
var DEFAULT_COPY_RESET_MS = 1500;
|
|
1392
1619
|
function useChatTranscript(options) {
|
|
1393
1620
|
const { messages, status, initialVisibleCount = DEFAULT_VISIBLE_COUNT, loadEarlierStep = DEFAULT_LOAD_EARLIER_STEP, followUps, stickToBottomThreshold } = options;
|
|
1394
|
-
const latest = (0,
|
|
1621
|
+
const latest = (0, import_react5.useRef)(options);
|
|
1395
1622
|
latest.current = options;
|
|
1396
|
-
const [visibleCount, setVisibleCount] = (0,
|
|
1397
|
-
const [stopRequested, setStopRequested] = (0,
|
|
1623
|
+
const [visibleCount, setVisibleCount] = (0, import_react5.useState)(initialVisibleCount);
|
|
1624
|
+
const [stopRequested, setStopRequested] = (0, import_react5.useState)(false);
|
|
1398
1625
|
const isStreaming = status === "streaming";
|
|
1399
1626
|
const isBusy = status === "submitted" || isStreaming;
|
|
1400
1627
|
const isEmpty = messages.length === 0;
|
|
1401
|
-
(0,
|
|
1628
|
+
(0, import_react5.useEffect)(() => {
|
|
1402
1629
|
if (!isBusy) {
|
|
1403
1630
|
setStopRequested(false);
|
|
1404
1631
|
}
|
|
@@ -1415,7 +1642,7 @@ function useChatTranscript(options) {
|
|
|
1415
1642
|
streamingMessageId,
|
|
1416
1643
|
options
|
|
1417
1644
|
});
|
|
1418
|
-
const stop = (0,
|
|
1645
|
+
const stop = (0, import_react5.useCallback)(() => {
|
|
1419
1646
|
const current = latest.current;
|
|
1420
1647
|
if (current.status !== "submitted" && current.status !== "streaming") {
|
|
1421
1648
|
return;
|
|
@@ -1423,8 +1650,8 @@ function useChatTranscript(options) {
|
|
|
1423
1650
|
setStopRequested(true);
|
|
1424
1651
|
void current.onStop?.();
|
|
1425
1652
|
}, []);
|
|
1426
|
-
const loadEarlier = (0,
|
|
1427
|
-
const showAll = (0,
|
|
1653
|
+
const loadEarlier = (0, import_react5.useCallback)(() => setVisibleCount((count) => count + (latest.current.loadEarlierStep ?? DEFAULT_LOAD_EARLIER_STEP)), []);
|
|
1654
|
+
const showAll = (0, import_react5.useCallback)(() => setVisibleCount(latest.current.messages.length), []);
|
|
1428
1655
|
const scroll = useStickToBottom({
|
|
1429
1656
|
contentKey: contentSignature(messages),
|
|
1430
1657
|
...stickToBottomThreshold !== void 0 ? {
|
|
@@ -1460,7 +1687,7 @@ function useChatTranscript(options) {
|
|
|
1460
1687
|
__name(useChatTranscript, "useChatTranscript");
|
|
1461
1688
|
function useTranscriptItem(options) {
|
|
1462
1689
|
const { message, isStreaming = false } = options;
|
|
1463
|
-
const messagesRef = (0,
|
|
1690
|
+
const messagesRef = (0, import_react5.useRef)([
|
|
1464
1691
|
message
|
|
1465
1692
|
]);
|
|
1466
1693
|
if (messagesRef.current[0] !== message) {
|
|
@@ -1478,13 +1705,13 @@ function useTranscriptItem(options) {
|
|
|
1478
1705
|
}
|
|
1479
1706
|
__name(useTranscriptItem, "useTranscriptItem");
|
|
1480
1707
|
function useTranscriptItems({ messages, visibleStart, streamingMessageId, options }) {
|
|
1481
|
-
const [drafts, setDrafts] = (0,
|
|
1482
|
-
const [copied, setCopied] = (0,
|
|
1483
|
-
const [openReasoning, setOpenReasoning] = (0,
|
|
1484
|
-
const [picks, setPicks] = (0,
|
|
1485
|
-
const [settling, setSettling] = (0,
|
|
1486
|
-
const [settleErrors, setSettleErrors] = (0,
|
|
1487
|
-
const latest = (0,
|
|
1708
|
+
const [drafts, setDrafts] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
|
|
1709
|
+
const [copied, setCopied] = (0, import_react5.useState)(() => /* @__PURE__ */ new Set());
|
|
1710
|
+
const [openReasoning, setOpenReasoning] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
|
|
1711
|
+
const [picks, setPicks] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
|
|
1712
|
+
const [settling, setSettling] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
|
|
1713
|
+
const [settleErrors, setSettleErrors] = (0, import_react5.useState)(() => /* @__PURE__ */ new Map());
|
|
1714
|
+
const latest = (0, import_react5.useRef)({
|
|
1488
1715
|
options,
|
|
1489
1716
|
drafts,
|
|
1490
1717
|
picks,
|
|
@@ -1496,23 +1723,23 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
1496
1723
|
picks,
|
|
1497
1724
|
messages
|
|
1498
1725
|
};
|
|
1499
|
-
const copyTimers = (0,
|
|
1500
|
-
(0,
|
|
1726
|
+
const copyTimers = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1727
|
+
(0, import_react5.useEffect)(() => () => {
|
|
1501
1728
|
for (const timer of copyTimers.current.values()) {
|
|
1502
1729
|
clearTimeout(timer);
|
|
1503
1730
|
}
|
|
1504
1731
|
copyTimers.current.clear();
|
|
1505
1732
|
}, []);
|
|
1506
|
-
const callbackCache = (0,
|
|
1507
|
-
const reasoningToggleCache = (0,
|
|
1508
|
-
const toggleReasoning = (0,
|
|
1733
|
+
const callbackCache = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1734
|
+
const reasoningToggleCache = (0, import_react5.useRef)(/* @__PURE__ */ new Map());
|
|
1735
|
+
const toggleReasoning = (0, import_react5.useCallback)((key, open) => {
|
|
1509
1736
|
setOpenReasoning((current) => {
|
|
1510
1737
|
const next = new Map(current);
|
|
1511
1738
|
next.set(key, open ?? !(current.get(key) ?? false));
|
|
1512
1739
|
return next;
|
|
1513
1740
|
});
|
|
1514
1741
|
}, []);
|
|
1515
|
-
const stableToggle = (0,
|
|
1742
|
+
const stableToggle = (0, import_react5.useCallback)((key) => {
|
|
1516
1743
|
let cached = reasoningToggleCache.current.get(key);
|
|
1517
1744
|
if (!cached) {
|
|
1518
1745
|
cached = /* @__PURE__ */ __name((open) => toggleReasoning(key, open), "cached");
|
|
@@ -1522,10 +1749,10 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
1522
1749
|
}, [
|
|
1523
1750
|
toggleReasoning
|
|
1524
1751
|
]);
|
|
1525
|
-
const pick = (0,
|
|
1752
|
+
const pick = (0, import_react5.useCallback)((toolCallId, questionId, values) => {
|
|
1526
1753
|
setPicks((current) => new Map(current).set(pickKey(toolCallId, questionId), values));
|
|
1527
1754
|
}, []);
|
|
1528
|
-
const settle = (0,
|
|
1755
|
+
const settle = (0, import_react5.useCallback)((toolCallId, action, send) => {
|
|
1529
1756
|
setSettleErrors((current) => {
|
|
1530
1757
|
if (!current.has(toolCallId)) {
|
|
1531
1758
|
return current;
|
|
@@ -1549,7 +1776,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
1549
1776
|
failed(error);
|
|
1550
1777
|
}
|
|
1551
1778
|
}, []);
|
|
1552
|
-
const answer = (0,
|
|
1779
|
+
const answer = (0, import_react5.useCallback)((toolCallId) => {
|
|
1553
1780
|
const current = latest.current;
|
|
1554
1781
|
const onAnswer = current.options.onAnswer;
|
|
1555
1782
|
if (!onAnswer) {
|
|
@@ -1559,7 +1786,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
1559
1786
|
}, [
|
|
1560
1787
|
settle
|
|
1561
1788
|
]);
|
|
1562
|
-
const skip = (0,
|
|
1789
|
+
const skip = (0, import_react5.useCallback)((toolCallId) => {
|
|
1563
1790
|
const onSkip = latest.current.options.onSkip;
|
|
1564
1791
|
if (onSkip) {
|
|
1565
1792
|
settle(toolCallId, "skip", () => onSkip(toolCallId));
|
|
@@ -1567,7 +1794,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
1567
1794
|
}, [
|
|
1568
1795
|
settle
|
|
1569
1796
|
]);
|
|
1570
|
-
const approve = (0,
|
|
1797
|
+
const approve = (0, import_react5.useCallback)((toolCallId) => {
|
|
1571
1798
|
const onApprove = latest.current.options.onApprove;
|
|
1572
1799
|
if (onApprove) {
|
|
1573
1800
|
settle(toolCallId, "approve", () => onApprove(toolCallId));
|
|
@@ -1575,7 +1802,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
1575
1802
|
}, [
|
|
1576
1803
|
settle
|
|
1577
1804
|
]);
|
|
1578
|
-
const reject = (0,
|
|
1805
|
+
const reject = (0, import_react5.useCallback)((toolCallId) => {
|
|
1579
1806
|
const onReject = latest.current.options.onReject;
|
|
1580
1807
|
if (onReject) {
|
|
1581
1808
|
settle(toolCallId, "reject", () => onReject(toolCallId));
|
|
@@ -1583,7 +1810,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
1583
1810
|
}, [
|
|
1584
1811
|
settle
|
|
1585
1812
|
]);
|
|
1586
|
-
const callbacksFor = (0,
|
|
1813
|
+
const callbacksFor = (0, import_react5.useCallback)((id) => {
|
|
1587
1814
|
const cached = callbackCache.current.get(id);
|
|
1588
1815
|
if (cached) {
|
|
1589
1816
|
return cached;
|
|
@@ -1822,7 +2049,7 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
|
|
|
1822
2049
|
getCreatedAt: /* @__PURE__ */ __name(() => createdAt, "getCreatedAt")
|
|
1823
2050
|
} : {}
|
|
1824
2051
|
});
|
|
1825
|
-
return /* @__PURE__ */
|
|
2052
|
+
return /* @__PURE__ */ import_react6.default.createElement(MessageItemView, {
|
|
1826
2053
|
item,
|
|
1827
2054
|
...slots
|
|
1828
2055
|
});
|
|
@@ -1830,51 +2057,51 @@ function MessageItem({ message, isStreaming = false, editable, onEditSubmit, reg
|
|
|
1830
2057
|
__name(MessageItem, "MessageItem");
|
|
1831
2058
|
function MessageItemView({ item, ...slots }) {
|
|
1832
2059
|
const { classNames } = slots;
|
|
1833
|
-
const actionRow = /* @__PURE__ */
|
|
2060
|
+
const actionRow = /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1834
2061
|
className: classNames?.actions,
|
|
1835
2062
|
"data-actions-for": item.role
|
|
1836
|
-
}, item.copy.available ? /* @__PURE__ */
|
|
2063
|
+
}, item.copy.available ? /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1837
2064
|
type: "button",
|
|
1838
2065
|
onClick: item.copy.copy,
|
|
1839
2066
|
className: classNames?.actionButton,
|
|
1840
2067
|
title: item.copy.copied ? "Copied!" : "Copy message text"
|
|
1841
|
-
}, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */
|
|
2068
|
+
}, item.copy.copied ? "Copied" : "Copy") : null, item.fork.available ? /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1842
2069
|
type: "button",
|
|
1843
2070
|
onClick: item.fork.run,
|
|
1844
2071
|
className: classNames?.actionButton,
|
|
1845
2072
|
title: "Fork thread from this message"
|
|
1846
|
-
}, "Fork") : null, item.regenerate.available ? /* @__PURE__ */
|
|
2073
|
+
}, "Fork") : null, item.regenerate.available ? /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1847
2074
|
type: "button",
|
|
1848
2075
|
onClick: item.regenerate.run,
|
|
1849
2076
|
className: classNames?.actionButton,
|
|
1850
2077
|
title: "Regenerate response"
|
|
1851
|
-
}, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */
|
|
2078
|
+
}, "Regenerate") : null, slots.meta, !item.isUser && item.usage ? /* @__PURE__ */ import_react6.default.createElement("span", {
|
|
1852
2079
|
className: classNames?.usage,
|
|
1853
2080
|
title: `Input ${item.usage.inputTokens.toLocaleString()} \xB7 Output ${item.usage.outputTokens.toLocaleString()} tokens`
|
|
1854
|
-
}, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */
|
|
2081
|
+
}, item.usage.costLabel, " \xB7 ", item.usage.tokensLabel) : null, item.timestamp ? /* @__PURE__ */ import_react6.default.createElement("span", {
|
|
1855
2082
|
className: classNames?.timestamp,
|
|
1856
2083
|
title: item.timestamp.absolute
|
|
1857
2084
|
}, item.timestamp.relative) : null);
|
|
1858
2085
|
if (item.edit.available) {
|
|
1859
|
-
return /* @__PURE__ */
|
|
2086
|
+
return /* @__PURE__ */ import_react6.default.createElement(EditableUserBubble, {
|
|
1860
2087
|
item,
|
|
1861
2088
|
slots,
|
|
1862
2089
|
actions: actionRow
|
|
1863
2090
|
});
|
|
1864
2091
|
}
|
|
1865
2092
|
const className = joinClasses(classNames?.root, classNames?.byRole?.[item.role]);
|
|
1866
|
-
return /* @__PURE__ */
|
|
2093
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1867
2094
|
className,
|
|
1868
2095
|
"data-role": item.role,
|
|
1869
2096
|
"data-streaming": item.isStreaming ? "true" : void 0
|
|
1870
|
-
}, /* @__PURE__ */
|
|
2097
|
+
}, /* @__PURE__ */ import_react6.default.createElement("div", null, renderBlocks(item, slots)), actionRow);
|
|
1871
2098
|
}
|
|
1872
2099
|
__name(MessageItemView, "MessageItemView");
|
|
1873
2100
|
function renderBlocks(item, slots) {
|
|
1874
2101
|
const nodes = [];
|
|
1875
2102
|
for (const block of item.blocks) {
|
|
1876
2103
|
if (block.kind === "text") {
|
|
1877
|
-
nodes.push(/* @__PURE__ */
|
|
2104
|
+
nodes.push(/* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1878
2105
|
key: block.key,
|
|
1879
2106
|
className: slots.classNames?.text
|
|
1880
2107
|
}, slots.renderText ? slots.renderText(block.text, {
|
|
@@ -1892,24 +2119,32 @@ function renderBlocks(item, slots) {
|
|
|
1892
2119
|
}
|
|
1893
2120
|
if (block.kind === "tools") {
|
|
1894
2121
|
nodes.push(...renderToolBlock(item, block, slots));
|
|
2122
|
+
continue;
|
|
2123
|
+
}
|
|
2124
|
+
if (block.kind === "ui" && slots.renderUi) {
|
|
2125
|
+
nodes.push(/* @__PURE__ */ import_react6.default.createElement("div", {
|
|
2126
|
+
key: block.key,
|
|
2127
|
+
"data-slot": "ui",
|
|
2128
|
+
"data-component": block.component
|
|
2129
|
+
}, slots.renderUi(block)));
|
|
1895
2130
|
}
|
|
1896
2131
|
}
|
|
1897
2132
|
return nodes;
|
|
1898
2133
|
}
|
|
1899
2134
|
__name(renderBlocks, "renderBlocks");
|
|
1900
2135
|
function renderFilesBlock(block, slots) {
|
|
1901
|
-
return /* @__PURE__ */
|
|
2136
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1902
2137
|
key: block.key,
|
|
1903
2138
|
className: slots.classNames?.files,
|
|
1904
2139
|
"data-files": "true"
|
|
1905
|
-
}, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */
|
|
2140
|
+
}, slots.renderFiles ? slots.renderFiles(block.files) : block.files.map((file) => /* @__PURE__ */ import_react6.default.createElement("a", {
|
|
1906
2141
|
key: file.url,
|
|
1907
2142
|
href: file.url,
|
|
1908
2143
|
target: "_blank",
|
|
1909
2144
|
rel: "noreferrer",
|
|
1910
2145
|
className: slots.classNames?.file,
|
|
1911
2146
|
title: file.filename ?? file.mediaType
|
|
1912
|
-
}, file.isImage ? /* @__PURE__ */
|
|
2147
|
+
}, file.isImage ? /* @__PURE__ */ import_react6.default.createElement("img", {
|
|
1913
2148
|
src: file.url,
|
|
1914
2149
|
alt: file.filename ?? "image",
|
|
1915
2150
|
className: slots.classNames?.fileImage
|
|
@@ -1917,17 +2152,17 @@ function renderFilesBlock(block, slots) {
|
|
|
1917
2152
|
}
|
|
1918
2153
|
__name(renderFilesBlock, "renderFilesBlock");
|
|
1919
2154
|
function renderReasoningBlock(block, slots) {
|
|
1920
|
-
return /* @__PURE__ */
|
|
2155
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1921
2156
|
key: block.key,
|
|
1922
2157
|
className: slots.classNames?.reasoning,
|
|
1923
2158
|
"data-reasoning": "true",
|
|
1924
2159
|
"data-streaming": block.isStreaming ? "true" : void 0
|
|
1925
|
-
}, /* @__PURE__ */
|
|
2160
|
+
}, /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1926
2161
|
type: "button",
|
|
1927
2162
|
className: slots.classNames?.reasoningToggle,
|
|
1928
2163
|
onClick: /* @__PURE__ */ __name(() => block.toggle(), "onClick"),
|
|
1929
2164
|
"aria-expanded": block.isOpen
|
|
1930
|
-
}, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */
|
|
2165
|
+
}, slots.reasoningLabel ?? "Reasoning"), block.isOpen ? /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1931
2166
|
className: slots.classNames?.reasoningText
|
|
1932
2167
|
}, slots.renderReasoning ? slots.renderReasoning(block.text, {
|
|
1933
2168
|
isStreaming: block.isStreaming,
|
|
@@ -1938,7 +2173,7 @@ __name(renderReasoningBlock, "renderReasoningBlock");
|
|
|
1938
2173
|
function renderToolBlock(item, block, slots) {
|
|
1939
2174
|
if (slots.renderToolGroup) {
|
|
1940
2175
|
return [
|
|
1941
|
-
/* @__PURE__ */
|
|
2176
|
+
/* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, {
|
|
1942
2177
|
key: block.key
|
|
1943
2178
|
}, slots.renderToolGroup(block.parts, block.key))
|
|
1944
2179
|
];
|
|
@@ -1949,7 +2184,7 @@ function renderToolBlock(item, block, slots) {
|
|
|
1949
2184
|
const renderToolPart = slots.renderToolPart;
|
|
1950
2185
|
return block.parts.map((part) => {
|
|
1951
2186
|
const partKey = `${item.id}-${part.toolCallId}`;
|
|
1952
|
-
return /* @__PURE__ */
|
|
2187
|
+
return /* @__PURE__ */ import_react6.default.createElement(import_react6.default.Fragment, {
|
|
1953
2188
|
key: partKey
|
|
1954
2189
|
}, renderToolPart(part, partKey));
|
|
1955
2190
|
});
|
|
@@ -1959,14 +2194,14 @@ function EditableUserBubble({ item, slots, actions }) {
|
|
|
1959
2194
|
const { classNames } = slots;
|
|
1960
2195
|
const { edit } = item;
|
|
1961
2196
|
if (!edit.isEditing) {
|
|
1962
|
-
return /* @__PURE__ */
|
|
2197
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1963
2198
|
className: joinClasses(classNames?.root, classNames?.byRole?.user),
|
|
1964
2199
|
"data-role": "user"
|
|
1965
|
-
}, /* @__PURE__ */
|
|
2200
|
+
}, /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1966
2201
|
className: classNames?.text
|
|
1967
2202
|
}, slots.renderText ? slots.renderText(item.text, {
|
|
1968
2203
|
isStreaming: false
|
|
1969
|
-
}) : item.text), /* @__PURE__ */
|
|
2204
|
+
}) : item.text), /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1970
2205
|
type: "button",
|
|
1971
2206
|
onClick: edit.start,
|
|
1972
2207
|
className: classNames?.actionButton,
|
|
@@ -1974,18 +2209,18 @@ function EditableUserBubble({ item, slots, actions }) {
|
|
|
1974
2209
|
}, "Edit"), actions);
|
|
1975
2210
|
}
|
|
1976
2211
|
const textareaProps = edit.getTextareaProps();
|
|
1977
|
-
return /* @__PURE__ */
|
|
2212
|
+
return /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1978
2213
|
"data-editing": "true"
|
|
1979
|
-
}, /* @__PURE__ */
|
|
2214
|
+
}, /* @__PURE__ */ import_react6.default.createElement("textarea", {
|
|
1980
2215
|
...textareaProps,
|
|
1981
2216
|
rows: Math.min(8, edit.draft.split("\n").length + 1),
|
|
1982
2217
|
className: classNames?.editTextarea
|
|
1983
|
-
}), /* @__PURE__ */
|
|
2218
|
+
}), /* @__PURE__ */ import_react6.default.createElement("div", {
|
|
1984
2219
|
className: classNames?.editActions
|
|
1985
|
-
}, /* @__PURE__ */
|
|
2220
|
+
}, /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1986
2221
|
type: "button",
|
|
1987
2222
|
onClick: edit.cancel
|
|
1988
|
-
}, "Cancel"), /* @__PURE__ */
|
|
2223
|
+
}, "Cancel"), /* @__PURE__ */ import_react6.default.createElement("button", {
|
|
1989
2224
|
type: "button",
|
|
1990
2225
|
onClick: edit.save,
|
|
1991
2226
|
disabled: !edit.canSave
|
|
@@ -1999,7 +2234,7 @@ function joinClasses(...entries) {
|
|
|
1999
2234
|
__name(joinClasses, "joinClasses");
|
|
2000
2235
|
|
|
2001
2236
|
// src/components/message-list.tsx
|
|
2002
|
-
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 }) {
|
|
2237
|
+
function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, renderUi, reasoningLabel, getMeta, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, classNames }) {
|
|
2003
2238
|
const transcript = useChatTranscript({
|
|
2004
2239
|
messages,
|
|
2005
2240
|
status,
|
|
@@ -2049,6 +2284,9 @@ function MessageList({ messages, status, renderToolPart, renderToolGroup, render
|
|
|
2049
2284
|
...renderFiles ? {
|
|
2050
2285
|
renderFiles
|
|
2051
2286
|
} : {},
|
|
2287
|
+
...renderUi ? {
|
|
2288
|
+
renderUi
|
|
2289
|
+
} : {},
|
|
2052
2290
|
...reasoningLabel !== void 0 ? {
|
|
2053
2291
|
reasoningLabel
|
|
2054
2292
|
} : {},
|
|
@@ -2222,35 +2460,35 @@ function toItem(entry) {
|
|
|
2222
2460
|
__name(toItem, "toItem");
|
|
2223
2461
|
|
|
2224
2462
|
// src/composer/use-composer-autocomplete.ts
|
|
2225
|
-
var
|
|
2463
|
+
var import_react7 = require("react");
|
|
2226
2464
|
function useComposerAutocomplete(options) {
|
|
2227
2465
|
const { value, sources, acceptOnTab = true } = options;
|
|
2228
|
-
const latest = (0,
|
|
2466
|
+
const latest = (0, import_react7.useRef)(options);
|
|
2229
2467
|
latest.current = options;
|
|
2230
|
-
const inputRef = (0,
|
|
2231
|
-
const [caret, setCaret] = (0,
|
|
2232
|
-
const [items, setItems] = (0,
|
|
2233
|
-
const [isLoading, setLoading] = (0,
|
|
2234
|
-
const [error, setError] = (0,
|
|
2235
|
-
const [activeIndex, setActiveIndex] = (0,
|
|
2236
|
-
const [dismissedIndex, setDismissedIndex] = (0,
|
|
2468
|
+
const inputRef = (0, import_react7.useRef)(null);
|
|
2469
|
+
const [caret, setCaret] = (0, import_react7.useState)(value.length);
|
|
2470
|
+
const [items, setItems] = (0, import_react7.useState)([]);
|
|
2471
|
+
const [isLoading, setLoading] = (0, import_react7.useState)(false);
|
|
2472
|
+
const [error, setError] = (0, import_react7.useState)(null);
|
|
2473
|
+
const [activeIndex, setActiveIndex] = (0, import_react7.useState)(0);
|
|
2474
|
+
const [dismissedIndex, setDismissedIndex] = (0, import_react7.useState)(null);
|
|
2237
2475
|
const safeCaret = Math.min(caret, value.length);
|
|
2238
|
-
const match = (0,
|
|
2476
|
+
const match = (0, import_react7.useMemo)(() => findActiveTrigger(value, safeCaret, sources), [
|
|
2239
2477
|
value,
|
|
2240
2478
|
safeCaret,
|
|
2241
2479
|
sources
|
|
2242
2480
|
]);
|
|
2243
2481
|
const isActive = match !== null && dismissedIndex !== match.index;
|
|
2244
|
-
const matchRef = (0,
|
|
2482
|
+
const matchRef = (0, import_react7.useRef)(match);
|
|
2245
2483
|
matchRef.current = match;
|
|
2246
|
-
const caretRef = (0,
|
|
2484
|
+
const caretRef = (0, import_react7.useRef)(safeCaret);
|
|
2247
2485
|
caretRef.current = safeCaret;
|
|
2248
|
-
const itemsRef = (0,
|
|
2486
|
+
const itemsRef = (0, import_react7.useRef)(items);
|
|
2249
2487
|
itemsRef.current = items;
|
|
2250
|
-
const activeIndexRef = (0,
|
|
2488
|
+
const activeIndexRef = (0, import_react7.useRef)(activeIndex);
|
|
2251
2489
|
activeIndexRef.current = activeIndex;
|
|
2252
2490
|
const requestKey = isActive && match ? `${match.source.id}\0${match.query}` : null;
|
|
2253
|
-
(0,
|
|
2491
|
+
(0, import_react7.useEffect)(() => {
|
|
2254
2492
|
const current = matchRef.current;
|
|
2255
2493
|
if (requestKey === null || current === null) {
|
|
2256
2494
|
setItems([]);
|
|
@@ -2298,25 +2536,25 @@ function useComposerAutocomplete(options) {
|
|
|
2298
2536
|
]);
|
|
2299
2537
|
const isOpen = isActive;
|
|
2300
2538
|
const activeItem = isOpen ? items[activeIndex] ?? null : null;
|
|
2301
|
-
const reactId = (0,
|
|
2539
|
+
const reactId = (0, import_react7.useId)();
|
|
2302
2540
|
const listboxId = options.id ?? `agent-autocomplete-${reactId}`;
|
|
2303
|
-
const optionId = (0,
|
|
2541
|
+
const optionId = (0, import_react7.useCallback)((index) => `${listboxId}-option-${index}`, [
|
|
2304
2542
|
listboxId
|
|
2305
2543
|
]);
|
|
2306
|
-
const highlight = (0,
|
|
2544
|
+
const highlight = (0, import_react7.useCallback)((index) => {
|
|
2307
2545
|
const total = itemsRef.current.length;
|
|
2308
2546
|
if (total === 0) return;
|
|
2309
2547
|
setActiveIndex((index % total + total) % total);
|
|
2310
2548
|
}, []);
|
|
2311
|
-
const moveHighlight = (0,
|
|
2549
|
+
const moveHighlight = (0, import_react7.useCallback)((delta) => highlight(activeIndexRef.current + delta), [
|
|
2312
2550
|
highlight
|
|
2313
2551
|
]);
|
|
2314
|
-
const dismiss = (0,
|
|
2552
|
+
const dismiss = (0, import_react7.useCallback)(() => {
|
|
2315
2553
|
const current = matchRef.current;
|
|
2316
2554
|
if (current) setDismissedIndex(current.index);
|
|
2317
2555
|
}, []);
|
|
2318
|
-
const pendingCaret = (0,
|
|
2319
|
-
const accept = (0,
|
|
2556
|
+
const pendingCaret = (0, import_react7.useRef)(null);
|
|
2557
|
+
const accept = (0, import_react7.useCallback)((item) => {
|
|
2320
2558
|
const current = matchRef.current;
|
|
2321
2559
|
if (!current) return;
|
|
2322
2560
|
const chosen = item ?? itemsRef.current[activeIndexRef.current];
|
|
@@ -2327,16 +2565,16 @@ function useComposerAutocomplete(options) {
|
|
|
2327
2565
|
setDismissedIndex(null);
|
|
2328
2566
|
pendingCaret.current = edit.caret;
|
|
2329
2567
|
}, []);
|
|
2330
|
-
(0,
|
|
2568
|
+
(0, import_react7.useLayoutEffect)(() => {
|
|
2331
2569
|
const position = pendingCaret.current;
|
|
2332
2570
|
if (position === null) return;
|
|
2333
2571
|
pendingCaret.current = null;
|
|
2334
2572
|
inputRef.current?.setSelectionRange(position, position);
|
|
2335
2573
|
});
|
|
2336
|
-
const readCaret = (0,
|
|
2574
|
+
const readCaret = (0, import_react7.useCallback)((element) => {
|
|
2337
2575
|
setCaret(element.selectionStart ?? element.value.length);
|
|
2338
2576
|
}, []);
|
|
2339
|
-
const onKeyDown = (0,
|
|
2577
|
+
const onKeyDown = (0, import_react7.useCallback)((event) => {
|
|
2340
2578
|
if (!isOpen) return;
|
|
2341
2579
|
const hasItems = itemsRef.current.length > 0;
|
|
2342
2580
|
switch (event.key) {
|
|
@@ -2374,23 +2612,23 @@ function useComposerAutocomplete(options) {
|
|
|
2374
2612
|
dismiss,
|
|
2375
2613
|
moveHighlight
|
|
2376
2614
|
]);
|
|
2377
|
-
const onChange = (0,
|
|
2615
|
+
const onChange = (0, import_react7.useCallback)((event) => {
|
|
2378
2616
|
readCaret(event.target);
|
|
2379
2617
|
const next = findActiveTrigger(event.target.value, event.target.selectionStart ?? event.target.value.length, latest.current.sources);
|
|
2380
2618
|
setDismissedIndex((current) => current !== null && next?.index === current ? current : null);
|
|
2381
2619
|
}, [
|
|
2382
2620
|
readCaret
|
|
2383
2621
|
]);
|
|
2384
|
-
const onSelect = (0,
|
|
2622
|
+
const onSelect = (0, import_react7.useCallback)((event) => readCaret(event.currentTarget), [
|
|
2385
2623
|
readCaret
|
|
2386
2624
|
]);
|
|
2387
|
-
const onClick = (0,
|
|
2625
|
+
const onClick = (0, import_react7.useCallback)((event) => readCaret(event.currentTarget), [
|
|
2388
2626
|
readCaret
|
|
2389
2627
|
]);
|
|
2390
|
-
const setInput = (0,
|
|
2628
|
+
const setInput = (0, import_react7.useCallback)((element) => {
|
|
2391
2629
|
inputRef.current = element;
|
|
2392
2630
|
}, []);
|
|
2393
|
-
const getInputProps = (0,
|
|
2631
|
+
const getInputProps = (0, import_react7.useCallback)(() => ({
|
|
2394
2632
|
ref: setInput,
|
|
2395
2633
|
onChange,
|
|
2396
2634
|
onKeyDown,
|
|
@@ -2418,7 +2656,7 @@ function useComposerAutocomplete(options) {
|
|
|
2418
2656
|
activeIndex,
|
|
2419
2657
|
optionId
|
|
2420
2658
|
]);
|
|
2421
|
-
const getListboxProps = (0,
|
|
2659
|
+
const getListboxProps = (0, import_react7.useCallback)(() => ({
|
|
2422
2660
|
id: listboxId,
|
|
2423
2661
|
role: "listbox",
|
|
2424
2662
|
"aria-label": match?.source.label ?? match?.source.id ?? "Suggestions"
|
|
@@ -2426,7 +2664,7 @@ function useComposerAutocomplete(options) {
|
|
|
2426
2664
|
listboxId,
|
|
2427
2665
|
match
|
|
2428
2666
|
]);
|
|
2429
|
-
const getOptionProps = (0,
|
|
2667
|
+
const getOptionProps = (0, import_react7.useCallback)((index) => ({
|
|
2430
2668
|
id: optionId(index),
|
|
2431
2669
|
role: "option",
|
|
2432
2670
|
"aria-selected": index === activeIndex
|
|
@@ -2480,6 +2718,16 @@ function refusalReason(result) {
|
|
|
2480
2718
|
__name(refusalReason, "refusalReason");
|
|
2481
2719
|
function storedMessageToUiMessage(message) {
|
|
2482
2720
|
const parts = [];
|
|
2721
|
+
if (message.reasoning) {
|
|
2722
|
+
parts.push({
|
|
2723
|
+
type: "reasoning",
|
|
2724
|
+
text: message.reasoning,
|
|
2725
|
+
state: "done",
|
|
2726
|
+
...message.reasoningMs !== void 0 ? {
|
|
2727
|
+
providerMetadata: reasoningDurationMetadata(message.reasoningMs)
|
|
2728
|
+
} : {}
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2483
2731
|
if (message.content) {
|
|
2484
2732
|
parts.push({
|
|
2485
2733
|
type: "text",
|
|
@@ -2494,6 +2742,20 @@ function storedMessageToUiMessage(message) {
|
|
|
2494
2742
|
url: attachment.url
|
|
2495
2743
|
});
|
|
2496
2744
|
}
|
|
2745
|
+
for (const component of message.ui ?? []) {
|
|
2746
|
+
parts.push({
|
|
2747
|
+
type: "data-ui",
|
|
2748
|
+
id: component.id,
|
|
2749
|
+
data: {
|
|
2750
|
+
id: component.id,
|
|
2751
|
+
component: component.component,
|
|
2752
|
+
props: component.props,
|
|
2753
|
+
...component.version !== void 0 ? {
|
|
2754
|
+
version: component.version
|
|
2755
|
+
} : {}
|
|
2756
|
+
}
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2497
2759
|
for (const call of message.toolCalls ?? []) {
|
|
2498
2760
|
const result = message.toolResults?.find((candidate) => candidate.id === call.id);
|
|
2499
2761
|
const toolKind2 = call.kind;
|
|
@@ -2595,21 +2857,21 @@ function sumUsage(rows) {
|
|
|
2595
2857
|
__name(sumUsage, "sumUsage");
|
|
2596
2858
|
|
|
2597
2859
|
// src/use-agent-chat.ts
|
|
2598
|
-
var
|
|
2599
|
-
var
|
|
2860
|
+
var import_react8 = require("@ai-sdk/react");
|
|
2861
|
+
var import_react9 = require("react");
|
|
2600
2862
|
function useAgentChat(options) {
|
|
2601
|
-
const latest = (0,
|
|
2863
|
+
const latest = (0, import_react9.useRef)(options);
|
|
2602
2864
|
latest.current = options;
|
|
2603
|
-
const [runId, setRunId] = (0,
|
|
2604
|
-
const runIdRef = (0,
|
|
2865
|
+
const [runId, setRunId] = (0, import_react9.useState)(options.resumeRunId);
|
|
2866
|
+
const runIdRef = (0, import_react9.useRef)(runId);
|
|
2605
2867
|
runIdRef.current = runId;
|
|
2606
|
-
const settlingRunIdRef = (0,
|
|
2607
|
-
const autoResumeRunIdRef = (0,
|
|
2608
|
-
const [autoResume, setAutoResume] = (0,
|
|
2609
|
-
const [activeRunId, setActiveRunId] = (0,
|
|
2610
|
-
const regenerateNext = (0,
|
|
2611
|
-
const createdThreadId = (0,
|
|
2612
|
-
const client = (0,
|
|
2868
|
+
const settlingRunIdRef = (0, import_react9.useRef)(void 0);
|
|
2869
|
+
const autoResumeRunIdRef = (0, import_react9.useRef)(void 0);
|
|
2870
|
+
const [autoResume, setAutoResume] = (0, import_react9.useState)(false);
|
|
2871
|
+
const [activeRunId, setActiveRunId] = (0, import_react9.useState)(null);
|
|
2872
|
+
const regenerateNext = (0, import_react9.useRef)(false);
|
|
2873
|
+
const createdThreadId = (0, import_react9.useRef)(void 0);
|
|
2874
|
+
const client = (0, import_react9.useMemo)(() => {
|
|
2613
2875
|
if (options.client) return options.client;
|
|
2614
2876
|
return new AgentClient({
|
|
2615
2877
|
...options.baseUrl !== void 0 ? {
|
|
@@ -2624,7 +2886,7 @@ function useAgentChat(options) {
|
|
|
2624
2886
|
getHeaders: /* @__PURE__ */ __name(async () => mergeHeaders(latest.current), "getHeaders")
|
|
2625
2887
|
});
|
|
2626
2888
|
}, []);
|
|
2627
|
-
(0,
|
|
2889
|
+
(0, import_react9.useEffect)(() => {
|
|
2628
2890
|
if (!options.resume || options.threadId === void 0) return;
|
|
2629
2891
|
let cancelled = false;
|
|
2630
2892
|
const threadId = options.threadId;
|
|
@@ -2644,7 +2906,7 @@ function useAgentChat(options) {
|
|
|
2644
2906
|
options.resume,
|
|
2645
2907
|
options.threadId
|
|
2646
2908
|
]);
|
|
2647
|
-
const transport = (0,
|
|
2909
|
+
const transport = (0, import_react9.useMemo)(() => {
|
|
2648
2910
|
function onMeta(meta) {
|
|
2649
2911
|
settlingRunIdRef.current = meta.runId;
|
|
2650
2912
|
setRunId(meta.runId);
|
|
@@ -2693,8 +2955,8 @@ function useAgentChat(options) {
|
|
|
2693
2955
|
}, "onAttemptStart")
|
|
2694
2956
|
});
|
|
2695
2957
|
}, []);
|
|
2696
|
-
const backgroundRef = (0,
|
|
2697
|
-
const chat = (0,
|
|
2958
|
+
const backgroundRef = (0, import_react9.useRef)(() => Promise.resolve());
|
|
2959
|
+
const chat = (0, import_react8.useChat)({
|
|
2698
2960
|
transport,
|
|
2699
2961
|
resume: options.resumeRunId !== void 0 || autoResume,
|
|
2700
2962
|
...options.threadId !== void 0 ? {
|
|
@@ -2703,6 +2965,13 @@ function useAgentChat(options) {
|
|
|
2703
2965
|
...options.initialMessages !== void 0 ? {
|
|
2704
2966
|
messages: options.initialMessages
|
|
2705
2967
|
} : {},
|
|
2968
|
+
onData: /* @__PURE__ */ __name((part) => {
|
|
2969
|
+
latest.current.onData?.(part);
|
|
2970
|
+
if (part.type === "data-title") {
|
|
2971
|
+
const title = part.data?.title;
|
|
2972
|
+
if (typeof title === "string") latest.current.onTitle?.(title);
|
|
2973
|
+
}
|
|
2974
|
+
}, "onData"),
|
|
2706
2975
|
onFinish: /* @__PURE__ */ __name(({ isError }) => {
|
|
2707
2976
|
latest.current.onFinish?.();
|
|
2708
2977
|
if (latest.current.background === true) {
|
|
@@ -2717,13 +2986,13 @@ function useAgentChat(options) {
|
|
|
2717
2986
|
}
|
|
2718
2987
|
}, "onFinish")
|
|
2719
2988
|
});
|
|
2720
|
-
const chatRef = (0,
|
|
2989
|
+
const chatRef = (0, import_react9.useRef)(chat);
|
|
2721
2990
|
chatRef.current = chat;
|
|
2722
|
-
const turnInFlight = (0,
|
|
2723
|
-
const isTurnInFlight = (0,
|
|
2991
|
+
const turnInFlight = (0, import_react9.useRef)(false);
|
|
2992
|
+
const isTurnInFlight = (0, import_react9.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
|
|
2724
2993
|
transport
|
|
2725
2994
|
]);
|
|
2726
|
-
const sendMessage = (0,
|
|
2995
|
+
const sendMessage = (0, import_react9.useCallback)(async (...args) => {
|
|
2727
2996
|
if (isTurnInFlight()) return;
|
|
2728
2997
|
turnInFlight.current = true;
|
|
2729
2998
|
try {
|
|
@@ -2734,15 +3003,15 @@ function useAgentChat(options) {
|
|
|
2734
3003
|
}, [
|
|
2735
3004
|
isTurnInFlight
|
|
2736
3005
|
]);
|
|
2737
|
-
const addToolResult = (0,
|
|
3006
|
+
const addToolResult = (0, import_react9.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
2738
3007
|
tool,
|
|
2739
3008
|
toolCallId,
|
|
2740
3009
|
output
|
|
2741
3010
|
}), []);
|
|
2742
|
-
const [threads, setThreads] = (0,
|
|
2743
|
-
const [quota, setQuota] = (0,
|
|
2744
|
-
const [backgroundRuns, setBackgroundRuns] = (0,
|
|
2745
|
-
const refreshBackground = (0,
|
|
3011
|
+
const [threads, setThreads] = (0, import_react9.useState)([]);
|
|
3012
|
+
const [quota, setQuota] = (0, import_react9.useState)(null);
|
|
3013
|
+
const [backgroundRuns, setBackgroundRuns] = (0, import_react9.useState)([]);
|
|
3014
|
+
const refreshBackground = (0, import_react9.useCallback)(async () => {
|
|
2746
3015
|
const threadId = latest.current.threadId ?? createdThreadId.current;
|
|
2747
3016
|
if (threadId === void 0) {
|
|
2748
3017
|
return;
|
|
@@ -2767,7 +3036,7 @@ function useAgentChat(options) {
|
|
|
2767
3036
|
]);
|
|
2768
3037
|
backgroundRef.current = refreshBackground;
|
|
2769
3038
|
const isWorking = backgroundRuns.some((run) => run.status === "running");
|
|
2770
|
-
(0,
|
|
3039
|
+
(0, import_react9.useEffect)(() => {
|
|
2771
3040
|
if (options.background !== true || options.threadId === void 0) {
|
|
2772
3041
|
return;
|
|
2773
3042
|
}
|
|
@@ -2777,7 +3046,7 @@ function useAgentChat(options) {
|
|
|
2777
3046
|
options.threadId,
|
|
2778
3047
|
refreshBackground
|
|
2779
3048
|
]);
|
|
2780
|
-
(0,
|
|
3049
|
+
(0, import_react9.useEffect)(() => {
|
|
2781
3050
|
const every = latest.current.backgroundPollMs ?? 5e3;
|
|
2782
3051
|
if (latest.current.background !== true || !isWorking || every <= 0) {
|
|
2783
3052
|
return;
|
|
@@ -2788,26 +3057,26 @@ function useAgentChat(options) {
|
|
|
2788
3057
|
isWorking,
|
|
2789
3058
|
refreshBackground
|
|
2790
3059
|
]);
|
|
2791
|
-
const loadThreads = (0,
|
|
3060
|
+
const loadThreads = (0, import_react9.useCallback)(async () => {
|
|
2792
3061
|
const list = await client.listThreads();
|
|
2793
3062
|
setThreads(list);
|
|
2794
3063
|
return list;
|
|
2795
3064
|
}, [
|
|
2796
3065
|
client
|
|
2797
3066
|
]);
|
|
2798
|
-
const loadThread = (0,
|
|
3067
|
+
const loadThread = (0, import_react9.useCallback)((id) => client.getThread(id), [
|
|
2799
3068
|
client
|
|
2800
3069
|
]);
|
|
2801
|
-
const deleteThread = (0,
|
|
3070
|
+
const deleteThread = (0, import_react9.useCallback)(async (id) => {
|
|
2802
3071
|
await client.deleteThread(id);
|
|
2803
3072
|
setThreads((current) => current.filter((thread) => thread.id !== id));
|
|
2804
3073
|
}, [
|
|
2805
3074
|
client
|
|
2806
3075
|
]);
|
|
2807
|
-
const forkThread = (0,
|
|
3076
|
+
const forkThread = (0, import_react9.useCallback)((threadId, messageId) => client.forkFromMessage(threadId, messageId), [
|
|
2808
3077
|
client
|
|
2809
3078
|
]);
|
|
2810
|
-
const renameThread = (0,
|
|
3079
|
+
const renameThread = (0, import_react9.useCallback)(async (id, title) => {
|
|
2811
3080
|
await client.renameThread(id, title);
|
|
2812
3081
|
setThreads((current) => current.map((thread) => thread.id === id ? {
|
|
2813
3082
|
...thread,
|
|
@@ -2816,24 +3085,24 @@ function useAgentChat(options) {
|
|
|
2816
3085
|
}, [
|
|
2817
3086
|
client
|
|
2818
3087
|
]);
|
|
2819
|
-
const promoteThread = (0,
|
|
3088
|
+
const promoteThread = (0, import_react9.useCallback)(async (id) => {
|
|
2820
3089
|
await client.promoteThread(id);
|
|
2821
3090
|
await loadThreads();
|
|
2822
3091
|
}, [
|
|
2823
3092
|
client,
|
|
2824
3093
|
loadThreads
|
|
2825
3094
|
]);
|
|
2826
|
-
const truncateFromMessage = (0,
|
|
3095
|
+
const truncateFromMessage = (0, import_react9.useCallback)((threadId, messageId) => client.truncateFromMessage(threadId, messageId).then(() => void 0), [
|
|
2827
3096
|
client
|
|
2828
3097
|
]);
|
|
2829
|
-
const loadQuota = (0,
|
|
3098
|
+
const loadQuota = (0, import_react9.useCallback)(async () => {
|
|
2830
3099
|
const today = await client.getQuotaToday();
|
|
2831
3100
|
setQuota(today);
|
|
2832
3101
|
return today;
|
|
2833
3102
|
}, [
|
|
2834
3103
|
client
|
|
2835
3104
|
]);
|
|
2836
|
-
const cancel = (0,
|
|
3105
|
+
const cancel = (0, import_react9.useCallback)(async () => {
|
|
2837
3106
|
chatRef.current.stop();
|
|
2838
3107
|
const activeRunId2 = runIdRef.current;
|
|
2839
3108
|
if (activeRunId2) {
|
|
@@ -2845,14 +3114,14 @@ function useAgentChat(options) {
|
|
|
2845
3114
|
}, [
|
|
2846
3115
|
client
|
|
2847
3116
|
]);
|
|
2848
|
-
const approve = (0,
|
|
3117
|
+
const approve = (0, import_react9.useCallback)(async ({ toolCallId }) => {
|
|
2849
3118
|
await client.approveToolCall({
|
|
2850
3119
|
toolCallId
|
|
2851
3120
|
});
|
|
2852
3121
|
}, [
|
|
2853
3122
|
client
|
|
2854
3123
|
]);
|
|
2855
|
-
const reject = (0,
|
|
3124
|
+
const reject = (0, import_react9.useCallback)(async ({ toolCallId, reason }) => {
|
|
2856
3125
|
await client.rejectToolCall({
|
|
2857
3126
|
toolCallId,
|
|
2858
3127
|
...reason !== void 0 ? {
|
|
@@ -2862,7 +3131,7 @@ function useAgentChat(options) {
|
|
|
2862
3131
|
}, [
|
|
2863
3132
|
client
|
|
2864
3133
|
]);
|
|
2865
|
-
const answer = (0,
|
|
3134
|
+
const answer = (0, import_react9.useCallback)(async ({ toolCallId, answers }) => {
|
|
2866
3135
|
await client.answerToolCall({
|
|
2867
3136
|
toolCallId,
|
|
2868
3137
|
...answers !== void 0 ? {
|
|
@@ -2872,14 +3141,14 @@ function useAgentChat(options) {
|
|
|
2872
3141
|
}, [
|
|
2873
3142
|
client
|
|
2874
3143
|
]);
|
|
2875
|
-
const skip = (0,
|
|
3144
|
+
const skip = (0, import_react9.useCallback)(async ({ toolCallId }) => {
|
|
2876
3145
|
await client.skipToolCall({
|
|
2877
3146
|
toolCallId
|
|
2878
3147
|
});
|
|
2879
3148
|
}, [
|
|
2880
3149
|
client
|
|
2881
3150
|
]);
|
|
2882
|
-
const regenerate = (0,
|
|
3151
|
+
const regenerate = (0, import_react9.useCallback)(() => {
|
|
2883
3152
|
if (isTurnInFlight()) return;
|
|
2884
3153
|
regenerateNext.current = true;
|
|
2885
3154
|
turnInFlight.current = true;
|
|
@@ -2951,14 +3220,17 @@ __name(mergeHeaders, "mergeHeaders");
|
|
|
2951
3220
|
extractMessageText,
|
|
2952
3221
|
filterAutocompleteItems,
|
|
2953
3222
|
findActiveTrigger,
|
|
3223
|
+
formatElapsed,
|
|
2954
3224
|
formatRelativeTime,
|
|
2955
3225
|
loadStoredSpeechLang,
|
|
2956
3226
|
persistSpeechLang,
|
|
3227
|
+
readReasoningMs,
|
|
2957
3228
|
storedMessageToUiMessage,
|
|
2958
3229
|
storedThreadToUiMessages,
|
|
2959
3230
|
useAgentChat,
|
|
2960
3231
|
useChatTranscript,
|
|
2961
3232
|
useComposerAutocomplete,
|
|
3233
|
+
useElapsed,
|
|
2962
3234
|
useSpeechRecognition,
|
|
2963
3235
|
useStickToBottom,
|
|
2964
3236
|
useTranscriptItem
|