@dudousxd/nestjs-agent-react 0.25.0 → 0.27.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 +53 -6
- package/dist/{client-D4ZSbZJ8.d.cts → client-DNM5Qm5N.d.cts} +70 -2
- package/dist/{client-D4ZSbZJ8.d.ts → client-DNM5Qm5N.d.ts} +70 -2
- package/dist/{generative-ui-BejHxxbR.d.cts → generative-ui-jT9TYfCi.d.cts} +1 -1
- package/dist/{generative-ui-BejHxxbR.d.ts → generative-ui-jT9TYfCi.d.ts} +1 -1
- package/dist/genui-json-render.d.cts +1 -1
- package/dist/genui-json-render.d.ts +1 -1
- package/dist/genui.d.cts +2 -2
- package/dist/genui.d.ts +2 -2
- package/dist/index.cjs +572 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +147 -10
- package/dist/index.d.ts +147 -10
- package/dist/index.js +569 -37
- package/dist/index.js.map +1 -1
- package/dist/media.cjs.map +1 -1
- package/dist/media.d.cts +1 -1
- package/dist/media.d.ts +1 -1
- package/dist/media.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -110,12 +110,48 @@ var AgentClient = class {
|
|
|
110
110
|
signal: request.signal
|
|
111
111
|
} : {}
|
|
112
112
|
});
|
|
113
|
+
if (response.status === 202) {
|
|
114
|
+
return {
|
|
115
|
+
body: emptyStream(),
|
|
116
|
+
queued: await response.json()
|
|
117
|
+
};
|
|
118
|
+
}
|
|
113
119
|
if (!response.ok || !response.body) {
|
|
114
120
|
throw await this.failure(response, "POST", `${this.agentPath()}/chat`);
|
|
115
121
|
}
|
|
116
122
|
return streamResponse(response);
|
|
117
123
|
}
|
|
118
124
|
/**
|
|
125
|
+
* `POST <path>/chat` for a message that should wait in the thread's queue — the body carries
|
|
126
|
+
* `mode: 'queue'` (or `'interrupt'`), which the server always answers with `202` JSON.
|
|
127
|
+
*/
|
|
128
|
+
enqueueMessage(request) {
|
|
129
|
+
return this.request("POST", "/chat", {
|
|
130
|
+
mode: "queue",
|
|
131
|
+
...request.body
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
/** `GET <path>/threads/:id/queue` — the thread's waiting messages, and whether it drains. */
|
|
135
|
+
getQueue(threadId) {
|
|
136
|
+
return this.request("GET", `/threads/${encodeURIComponent(threadId)}/queue`);
|
|
137
|
+
}
|
|
138
|
+
/** `PATCH <path>/queue/:messageId` — change a waiting message's text/attachments, or move it. */
|
|
139
|
+
updateQueuedMessage(messageId, update) {
|
|
140
|
+
return this.request("PATCH", `/queue/${encodeURIComponent(messageId)}`, update);
|
|
141
|
+
}
|
|
142
|
+
/** `DELETE <path>/queue/:messageId`. */
|
|
143
|
+
removeQueuedMessage(messageId) {
|
|
144
|
+
return this.request("DELETE", `/queue/${encodeURIComponent(messageId)}`);
|
|
145
|
+
}
|
|
146
|
+
/** `DELETE <path>/threads/:id/queue` — drop every waiting message. */
|
|
147
|
+
clearQueue(threadId) {
|
|
148
|
+
return this.request("DELETE", `/threads/${encodeURIComponent(threadId)}/queue`);
|
|
149
|
+
}
|
|
150
|
+
/** `POST <path>/threads/:id/queue/resume` — lift a pause; `runId` when the head started. */
|
|
151
|
+
resumeQueue(threadId) {
|
|
152
|
+
return this.request("POST", `/threads/${encodeURIComponent(threadId)}/queue/resume`);
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
119
155
|
* `GET <path>/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
|
|
120
156
|
* streaming under that id (404).
|
|
121
157
|
*/
|
|
@@ -381,6 +417,14 @@ var AgentClient = class {
|
|
|
381
417
|
return JSON.parse(text);
|
|
382
418
|
}
|
|
383
419
|
};
|
|
420
|
+
function emptyStream() {
|
|
421
|
+
return new ReadableStream({
|
|
422
|
+
start(controller) {
|
|
423
|
+
controller.close();
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
__name(emptyStream, "emptyStream");
|
|
384
428
|
function streamResponse(response) {
|
|
385
429
|
const body = response.body;
|
|
386
430
|
const runId = response.headers?.get(HEADER_RUN_ID) ?? void 0;
|
|
@@ -543,6 +587,10 @@ var AgentChatTransport = class {
|
|
|
543
587
|
signal: options.abortSignal
|
|
544
588
|
} : {}
|
|
545
589
|
});
|
|
590
|
+
if (response.queued !== void 0) {
|
|
591
|
+
this.attemptLive = false;
|
|
592
|
+
return queuedChunkStream(response.queued);
|
|
593
|
+
}
|
|
546
594
|
this.captureHeaderMeta(response);
|
|
547
595
|
return this.toChunkStream(response, {
|
|
548
596
|
...headers !== void 0 ? {
|
|
@@ -573,6 +621,7 @@ var AgentChatTransport = class {
|
|
|
573
621
|
});
|
|
574
622
|
if (response === null) {
|
|
575
623
|
this.attemptLive = false;
|
|
624
|
+
this.options.onResumeGone?.(runId);
|
|
576
625
|
return null;
|
|
577
626
|
}
|
|
578
627
|
this.captureHeaderMeta(response);
|
|
@@ -950,6 +999,9 @@ var AgentChatTransport = class {
|
|
|
950
999
|
break;
|
|
951
1000
|
case "title":
|
|
952
1001
|
case "cancelled":
|
|
1002
|
+
// The thread's queue changed, or this run handed the thread to the next queued message
|
|
1003
|
+
// (`started`). Thread-level, like the title.
|
|
1004
|
+
case "queue":
|
|
953
1005
|
forwardAsData(event, true);
|
|
954
1006
|
break;
|
|
955
1007
|
default:
|
|
@@ -1052,6 +1104,22 @@ var AgentChatTransport = class {
|
|
|
1052
1104
|
});
|
|
1053
1105
|
}
|
|
1054
1106
|
};
|
|
1107
|
+
function queuedChunkStream(queued) {
|
|
1108
|
+
return new ReadableStream({
|
|
1109
|
+
start(controller) {
|
|
1110
|
+
controller.enqueue({
|
|
1111
|
+
type: "data-queue",
|
|
1112
|
+
data: {
|
|
1113
|
+
queue: queued.queue,
|
|
1114
|
+
queuedSend: queued
|
|
1115
|
+
},
|
|
1116
|
+
transient: true
|
|
1117
|
+
});
|
|
1118
|
+
controller.close();
|
|
1119
|
+
}
|
|
1120
|
+
});
|
|
1121
|
+
}
|
|
1122
|
+
__name(queuedChunkStream, "queuedChunkStream");
|
|
1055
1123
|
function parseSseFrame(raw) {
|
|
1056
1124
|
let event;
|
|
1057
1125
|
let id;
|
|
@@ -3766,6 +3834,7 @@ function useChatTranscript(options) {
|
|
|
3766
3834
|
} : {}
|
|
3767
3835
|
});
|
|
3768
3836
|
const showTypingIndicator = isBusy && lastMessage?.role !== "assistant";
|
|
3837
|
+
const queued = useQueuedItems(options.queue ?? null, options.readOnly === true);
|
|
3769
3838
|
return {
|
|
3770
3839
|
items,
|
|
3771
3840
|
status,
|
|
@@ -3785,13 +3854,56 @@ function useChatTranscript(options) {
|
|
|
3785
3854
|
},
|
|
3786
3855
|
scroll,
|
|
3787
3856
|
stop: {
|
|
3788
|
-
available: isBusy && options.onStop !== void 0,
|
|
3857
|
+
available: isBusy && options.readOnly !== true && options.onStop !== void 0,
|
|
3789
3858
|
isStopping: stopRequested,
|
|
3790
3859
|
stop
|
|
3791
|
-
}
|
|
3860
|
+
},
|
|
3861
|
+
queued,
|
|
3862
|
+
queuePaused: options.queue?.paused ?? null
|
|
3792
3863
|
};
|
|
3793
3864
|
}
|
|
3794
3865
|
__name(useChatTranscript, "useChatTranscript");
|
|
3866
|
+
function useQueuedItems(queue, readOnly) {
|
|
3867
|
+
const latestQueue = useRef9(queue);
|
|
3868
|
+
latestQueue.current = queue;
|
|
3869
|
+
const removers = useRef9(/* @__PURE__ */ new Map());
|
|
3870
|
+
const removerFor = useCallback8((id) => {
|
|
3871
|
+
let remover = removers.current.get(id);
|
|
3872
|
+
if (remover === void 0) {
|
|
3873
|
+
remover = /* @__PURE__ */ __name(() => {
|
|
3874
|
+
void latestQueue.current?.remove?.(id);
|
|
3875
|
+
}, "remover");
|
|
3876
|
+
removers.current.set(id, remover);
|
|
3877
|
+
}
|
|
3878
|
+
return remover;
|
|
3879
|
+
}, []);
|
|
3880
|
+
if (queue === null || queue === void 0) {
|
|
3881
|
+
return [];
|
|
3882
|
+
}
|
|
3883
|
+
return queue.items.map((message, position) => {
|
|
3884
|
+
const state = message.state === "sending" ? "sending" : queue.paused !== null ? "paused" : "queued";
|
|
3885
|
+
return {
|
|
3886
|
+
id: message.id,
|
|
3887
|
+
role: "user",
|
|
3888
|
+
text: message.text,
|
|
3889
|
+
files: message.attachments.map((attachment) => ({
|
|
3890
|
+
url: attachment.url,
|
|
3891
|
+
mediaType: attachment.contentType,
|
|
3892
|
+
filename: attachment.name,
|
|
3893
|
+
isImage: attachment.contentType.startsWith("image/")
|
|
3894
|
+
})),
|
|
3895
|
+
state,
|
|
3896
|
+
position,
|
|
3897
|
+
isNext: position === 0,
|
|
3898
|
+
interrupt: message.interrupt,
|
|
3899
|
+
remove: {
|
|
3900
|
+
available: !readOnly && state !== "sending" && queue.remove !== void 0,
|
|
3901
|
+
run: removerFor(message.id)
|
|
3902
|
+
}
|
|
3903
|
+
};
|
|
3904
|
+
});
|
|
3905
|
+
}
|
|
3906
|
+
__name(useQueuedItems, "useQueuedItems");
|
|
3795
3907
|
function useTranscriptItem(options) {
|
|
3796
3908
|
const { message, isStreaming = false } = options;
|
|
3797
3909
|
const messagesRef = useRef9([
|
|
@@ -4055,11 +4167,12 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4055
4167
|
...options.toolCatalog !== void 0 ? {
|
|
4056
4168
|
toolCatalog: options.toolCatalog
|
|
4057
4169
|
} : {},
|
|
4058
|
-
|
|
4170
|
+
// Read-only still lifts question sets into their block, so the outcome shows.
|
|
4171
|
+
...handlers.onAnswer !== void 0 || options.readOnly === true && options.onAnswer !== null ? {
|
|
4059
4172
|
elicitation: {
|
|
4060
4173
|
picked: /* @__PURE__ */ __name((toolCallId, questionId) => picks.get(pickKey(toolCallId, questionId)), "picked"),
|
|
4061
4174
|
pick,
|
|
4062
|
-
canAnswer:
|
|
4175
|
+
canAnswer: handlers.onAnswer !== void 0,
|
|
4063
4176
|
canSkip: handlers.onSkip !== void 0,
|
|
4064
4177
|
answer,
|
|
4065
4178
|
skip,
|
|
@@ -4087,7 +4200,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4087
4200
|
copy: callbacks.copy
|
|
4088
4201
|
},
|
|
4089
4202
|
edit: {
|
|
4090
|
-
available: isUser && options.editable === true && options.onEditSubmit !== void 0,
|
|
4203
|
+
available: isUser && options.readOnly !== true && options.editable === true && options.onEditSubmit !== void 0,
|
|
4091
4204
|
isEditing,
|
|
4092
4205
|
draft: draft ?? text,
|
|
4093
4206
|
canSave: (draft ?? "").trim().length > 0,
|
|
@@ -4103,11 +4216,11 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4103
4216
|
}), "getTextareaProps")
|
|
4104
4217
|
},
|
|
4105
4218
|
fork: {
|
|
4106
|
-
available: options.onFork !== void 0,
|
|
4219
|
+
available: options.readOnly !== true && options.onFork !== void 0,
|
|
4107
4220
|
run: callbacks.fork
|
|
4108
4221
|
},
|
|
4109
4222
|
regenerate: {
|
|
4110
|
-
available: isLastAssistant && options.regeneratable === true && options.onRegenerate !== void 0,
|
|
4223
|
+
available: isLastAssistant && options.readOnly !== true && options.regeneratable === true && options.onRegenerate !== void 0,
|
|
4111
4224
|
run: callbacks.regenerate
|
|
4112
4225
|
}
|
|
4113
4226
|
});
|
|
@@ -4116,7 +4229,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4116
4229
|
}
|
|
4117
4230
|
__name(useTranscriptItems, "useTranscriptItems");
|
|
4118
4231
|
function settleHandlers(options, backend) {
|
|
4119
|
-
const pick = /* @__PURE__ */ __name((own, fallback2) => own === null ? void 0 : own ?? fallback2, "pick");
|
|
4232
|
+
const pick = /* @__PURE__ */ __name((own, fallback2) => options.readOnly === true || own === null ? void 0 : own ?? fallback2, "pick");
|
|
4120
4233
|
return {
|
|
4121
4234
|
onAnswer: pick(options.onAnswer, backend.answerToolCall ? async (input) => {
|
|
4122
4235
|
await backend.answerToolCall?.(input);
|
|
@@ -4409,10 +4522,13 @@ function joinClasses(...entries) {
|
|
|
4409
4522
|
__name(joinClasses, "joinClasses");
|
|
4410
4523
|
|
|
4411
4524
|
// src/components/message-list.tsx
|
|
4412
|
-
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 }) {
|
|
4525
|
+
function MessageList({ messages, status, renderToolPart, renderToolGroup, renderText, renderReasoning, renderFiles, renderUi, reasoningLabel, getMeta, readOnly, onFork, editable, onEditSubmit, regeneratable, onRegenerate, getUsage, getCreatedAt, emptyState, typingIndicator, followUps, onFollowUpClick, error, onRetry, queue, classNames }) {
|
|
4413
4526
|
const transcript = useChatTranscript({
|
|
4414
4527
|
messages,
|
|
4415
4528
|
status,
|
|
4529
|
+
...readOnly !== void 0 ? {
|
|
4530
|
+
readOnly
|
|
4531
|
+
} : {},
|
|
4416
4532
|
...onFork ? {
|
|
4417
4533
|
onFork
|
|
4418
4534
|
} : {},
|
|
@@ -4436,9 +4552,16 @@ function MessageList({ messages, status, renderToolPart, renderToolGroup, render
|
|
|
4436
4552
|
} : {},
|
|
4437
4553
|
...followUps !== void 0 ? {
|
|
4438
4554
|
followUps
|
|
4555
|
+
} : {},
|
|
4556
|
+
...queue !== void 0 ? {
|
|
4557
|
+
queue: {
|
|
4558
|
+
items: queue.items,
|
|
4559
|
+
paused: queue.paused,
|
|
4560
|
+
remove: queue.remove
|
|
4561
|
+
}
|
|
4439
4562
|
} : {}
|
|
4440
4563
|
});
|
|
4441
|
-
if (transcript.showEmptyState) {
|
|
4564
|
+
if (transcript.showEmptyState && transcript.queued.length === 0) {
|
|
4442
4565
|
return /* @__PURE__ */ React.createElement("div", {
|
|
4443
4566
|
className: classNames?.empty
|
|
4444
4567
|
}, emptyState);
|
|
@@ -4482,7 +4605,23 @@ function MessageList({ messages, status, renderToolPart, renderToolGroup, render
|
|
|
4482
4605
|
...getMeta ? {
|
|
4483
4606
|
meta: getMeta(item.message)
|
|
4484
4607
|
} : {}
|
|
4485
|
-
})), transcript.
|
|
4608
|
+
})), transcript.queued.length > 0 ? /* @__PURE__ */ React.createElement("div", {
|
|
4609
|
+
className: classNames?.queue
|
|
4610
|
+
}, transcript.queued.map((item) => /* @__PURE__ */ React.createElement("div", {
|
|
4611
|
+
key: item.id,
|
|
4612
|
+
className: classNames?.queuedItem,
|
|
4613
|
+
"data-state": item.state
|
|
4614
|
+
}, /* @__PURE__ */ React.createElement("span", null, item.text), item.remove.available ? /* @__PURE__ */ React.createElement("button", {
|
|
4615
|
+
type: "button",
|
|
4616
|
+
className: classNames?.queuedRemove,
|
|
4617
|
+
onClick: item.remove.run
|
|
4618
|
+
}, "Remove") : null)), transcript.queuePaused !== null && queue !== void 0 ? /* @__PURE__ */ React.createElement("div", {
|
|
4619
|
+
className: classNames?.queuePaused
|
|
4620
|
+
}, /* @__PURE__ */ React.createElement("span", null, "Queue paused"), /* @__PURE__ */ React.createElement("button", {
|
|
4621
|
+
type: "button",
|
|
4622
|
+
className: classNames?.queueResume,
|
|
4623
|
+
onClick: /* @__PURE__ */ __name(() => void queue.resume(), "onClick")
|
|
4624
|
+
}, "Resume")) : null) : null, transcript.showTypingIndicator ? /* @__PURE__ */ React.createElement("div", {
|
|
4486
4625
|
className: classNames?.typing
|
|
4487
4626
|
}, typingIndicator ?? "Thinking\u2026") : null, error && onRetry ? /* @__PURE__ */ React.createElement("div", {
|
|
4488
4627
|
className: classNames?.error
|
|
@@ -4877,6 +5016,7 @@ function isPromise(value) {
|
|
|
4877
5016
|
__name(isPromise, "isPromise");
|
|
4878
5017
|
|
|
4879
5018
|
// src/presentation/use-tool-catalog.ts
|
|
5019
|
+
import { ALL_AGENTS } from "@dudousxd/nestjs-agent-core";
|
|
4880
5020
|
import { useCallback as useCallback10, useEffect as useEffect13, useMemo as useMemo10, useState as useState13 } from "react";
|
|
4881
5021
|
var shared2 = /* @__PURE__ */ new WeakMap();
|
|
4882
5022
|
function load2(client, agent) {
|
|
@@ -5250,6 +5390,77 @@ __name(sumUsage, "sumUsage");
|
|
|
5250
5390
|
// src/use-agent-chat.ts
|
|
5251
5391
|
import { useChat } from "@ai-sdk/react";
|
|
5252
5392
|
import { useCallback as useCallback11, useEffect as useEffect15, useMemo as useMemo11, useRef as useRef12, useState as useState15 } from "react";
|
|
5393
|
+
var EMPTY_QUEUE = {
|
|
5394
|
+
items: [],
|
|
5395
|
+
paused: null
|
|
5396
|
+
};
|
|
5397
|
+
var localQueueSequence = 0;
|
|
5398
|
+
function queuedUserMessage(turn) {
|
|
5399
|
+
return {
|
|
5400
|
+
id: turn.messageId,
|
|
5401
|
+
role: "user",
|
|
5402
|
+
metadata: {
|
|
5403
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5404
|
+
},
|
|
5405
|
+
parts: [
|
|
5406
|
+
...turn.attachments.map((attachment) => ({
|
|
5407
|
+
type: "file",
|
|
5408
|
+
mediaType: attachment.contentType,
|
|
5409
|
+
filename: attachment.name,
|
|
5410
|
+
url: attachment.url,
|
|
5411
|
+
providerMetadata: {
|
|
5412
|
+
agent: {
|
|
5413
|
+
mediaId: attachment.mediaId
|
|
5414
|
+
}
|
|
5415
|
+
}
|
|
5416
|
+
})),
|
|
5417
|
+
...turn.text.length > 0 ? [
|
|
5418
|
+
{
|
|
5419
|
+
type: "text",
|
|
5420
|
+
text: turn.text
|
|
5421
|
+
}
|
|
5422
|
+
] : []
|
|
5423
|
+
]
|
|
5424
|
+
};
|
|
5425
|
+
}
|
|
5426
|
+
__name(queuedUserMessage, "queuedUserMessage");
|
|
5427
|
+
function draftOf(message) {
|
|
5428
|
+
if (typeof message !== "object" || message === null) {
|
|
5429
|
+
return {
|
|
5430
|
+
text: "",
|
|
5431
|
+
attachments: []
|
|
5432
|
+
};
|
|
5433
|
+
}
|
|
5434
|
+
const draft = message;
|
|
5435
|
+
let text = typeof draft.text === "string" ? draft.text : "";
|
|
5436
|
+
if (text === "" && Array.isArray(draft.parts)) {
|
|
5437
|
+
text = draft.parts.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("");
|
|
5438
|
+
}
|
|
5439
|
+
const files = Array.isArray(draft.files) ? draft.files : Array.isArray(draft.parts) ? draft.parts.filter((part) => part.type === "file") : [];
|
|
5440
|
+
const attachments = files.flatMap((file) => {
|
|
5441
|
+
const mediaId = file.providerMetadata?.agent?.mediaId;
|
|
5442
|
+
return typeof mediaId === "string" ? [
|
|
5443
|
+
{
|
|
5444
|
+
mediaId,
|
|
5445
|
+
url: String(file.url ?? ""),
|
|
5446
|
+
contentType: String(file.mediaType ?? "application/octet-stream"),
|
|
5447
|
+
name: String(file.filename ?? mediaId)
|
|
5448
|
+
}
|
|
5449
|
+
] : [];
|
|
5450
|
+
});
|
|
5451
|
+
return {
|
|
5452
|
+
text,
|
|
5453
|
+
attachments
|
|
5454
|
+
};
|
|
5455
|
+
}
|
|
5456
|
+
__name(draftOf, "draftOf");
|
|
5457
|
+
function lastUserIndex(messages) {
|
|
5458
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
5459
|
+
if (messages[index]?.role === "user") return index;
|
|
5460
|
+
}
|
|
5461
|
+
return -1;
|
|
5462
|
+
}
|
|
5463
|
+
__name(lastUserIndex, "lastUserIndex");
|
|
5253
5464
|
var localChatSequence = 0;
|
|
5254
5465
|
function localChatId() {
|
|
5255
5466
|
localChatSequence += 1;
|
|
@@ -5279,7 +5490,7 @@ function useAgentChat(options = {}) {
|
|
|
5279
5490
|
const settlingRunIdRef = useRef12(void 0);
|
|
5280
5491
|
const autoResumeRunIdRef = useRef12(void 0);
|
|
5281
5492
|
const [activeRunId, setActiveRunId] = useState15(null);
|
|
5282
|
-
const [
|
|
5493
|
+
const [historyFor, setHistoryFor] = useState15(void 0);
|
|
5283
5494
|
const [historyError, setHistoryError] = useState15(null);
|
|
5284
5495
|
const [threadModel, setThreadModel] = useState15(null);
|
|
5285
5496
|
const [pickedModel, setPickedModel] = useState15(void 0);
|
|
@@ -5289,6 +5500,41 @@ function useAgentChat(options = {}) {
|
|
|
5289
5500
|
const pendingPin = useRef12(void 0);
|
|
5290
5501
|
const regenerateNext = useRef12(false);
|
|
5291
5502
|
const currentThreadId = useCallback11(() => latest.current.threadId ?? createdThreadId.current, []);
|
|
5503
|
+
const [queueState, setQueueState] = useState15(EMPTY_QUEUE);
|
|
5504
|
+
const queueStateRef = useRef12(queueState);
|
|
5505
|
+
const applyQueue = useCallback11((next) => {
|
|
5506
|
+
queueStateRef.current = next;
|
|
5507
|
+
setQueueState(next);
|
|
5508
|
+
}, []);
|
|
5509
|
+
const [pendingSends, setPendingSends] = useState15([]);
|
|
5510
|
+
const [queueError, setQueueError] = useState15(null);
|
|
5511
|
+
const handoff = useRef12(void 0);
|
|
5512
|
+
const queuedSend = useRef12(void 0);
|
|
5513
|
+
const threadWaiters = useRef12([]);
|
|
5514
|
+
const turnBody = useCallback11(() => {
|
|
5515
|
+
const current = latest.current;
|
|
5516
|
+
const pageContext = current.getPageContext?.() ?? null;
|
|
5517
|
+
const threadId = currentThreadId();
|
|
5518
|
+
const model = current.model ?? (lockedRef.current ? void 0 : pickedModelRef.current);
|
|
5519
|
+
return {
|
|
5520
|
+
// Read per send: a host that switches agents before the first message (a picker on a new
|
|
5521
|
+
// chat) sends the one picked now, not the one this chat mounted with.
|
|
5522
|
+
...current.agent !== void 0 ? {
|
|
5523
|
+
agent: current.agent
|
|
5524
|
+
} : {},
|
|
5525
|
+
...threadId !== void 0 ? {
|
|
5526
|
+
threadId
|
|
5527
|
+
} : {},
|
|
5528
|
+
...model !== void 0 ? {
|
|
5529
|
+
model
|
|
5530
|
+
} : {},
|
|
5531
|
+
...pageContext ? {
|
|
5532
|
+
pageContext
|
|
5533
|
+
} : {}
|
|
5534
|
+
};
|
|
5535
|
+
}, [
|
|
5536
|
+
currentThreadId
|
|
5537
|
+
]);
|
|
5292
5538
|
const [connection, setConnection] = useState15({
|
|
5293
5539
|
status: "live"
|
|
5294
5540
|
});
|
|
@@ -5297,6 +5543,11 @@ function useAgentChat(options = {}) {
|
|
|
5297
5543
|
function onMeta(meta) {
|
|
5298
5544
|
settlingRunIdRef.current = meta.runId;
|
|
5299
5545
|
setRunId(meta.runId);
|
|
5546
|
+
if (meta.threadId) {
|
|
5547
|
+
const waiting = threadWaiters.current;
|
|
5548
|
+
threadWaiters.current = [];
|
|
5549
|
+
for (const resolve of waiting) resolve(meta.threadId);
|
|
5550
|
+
}
|
|
5300
5551
|
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
5301
5552
|
createdThreadId.current = meta.threadId;
|
|
5302
5553
|
const pin = pendingPin.current;
|
|
@@ -5328,27 +5579,10 @@ function useAgentChat(options = {}) {
|
|
|
5328
5579
|
agent: options.agent
|
|
5329
5580
|
} : {},
|
|
5330
5581
|
getBody: /* @__PURE__ */ __name(() => {
|
|
5331
|
-
const current = latest.current;
|
|
5332
|
-
const pageContext = current.getPageContext?.() ?? null;
|
|
5333
5582
|
const regenerate2 = regenerateNext.current;
|
|
5334
5583
|
regenerateNext.current = false;
|
|
5335
|
-
const threadId = currentThreadId();
|
|
5336
|
-
const model = current.model ?? (lockedRef.current ? void 0 : pickedModelRef.current);
|
|
5337
5584
|
return {
|
|
5338
|
-
|
|
5339
|
-
// new chat) sends the one picked now, not the one this chat mounted with.
|
|
5340
|
-
...current.agent !== void 0 ? {
|
|
5341
|
-
agent: current.agent
|
|
5342
|
-
} : {},
|
|
5343
|
-
...threadId !== void 0 ? {
|
|
5344
|
-
threadId
|
|
5345
|
-
} : {},
|
|
5346
|
-
...model !== void 0 ? {
|
|
5347
|
-
model
|
|
5348
|
-
} : {},
|
|
5349
|
-
...pageContext ? {
|
|
5350
|
-
pageContext
|
|
5351
|
-
} : {},
|
|
5585
|
+
...turnBody(),
|
|
5352
5586
|
...regenerate2 ? {
|
|
5353
5587
|
regenerate: true
|
|
5354
5588
|
} : {}
|
|
@@ -5358,7 +5592,11 @@ function useAgentChat(options = {}) {
|
|
|
5358
5592
|
onMeta,
|
|
5359
5593
|
onAttemptStart: /* @__PURE__ */ __name(() => {
|
|
5360
5594
|
settlingRunIdRef.current = void 0;
|
|
5361
|
-
}, "onAttemptStart")
|
|
5595
|
+
}, "onAttemptStart"),
|
|
5596
|
+
// A queued turn this chat went to attach to had already finished: read what it wrote.
|
|
5597
|
+
onResumeGone: /* @__PURE__ */ __name(() => {
|
|
5598
|
+
void resyncFromThread();
|
|
5599
|
+
}, "onResumeGone")
|
|
5362
5600
|
});
|
|
5363
5601
|
}, []);
|
|
5364
5602
|
const backgroundRef = useRef12(() => Promise.resolve());
|
|
@@ -5371,6 +5609,9 @@ function useAgentChat(options = {}) {
|
|
|
5371
5609
|
} : {},
|
|
5372
5610
|
onData: /* @__PURE__ */ __name((part) => {
|
|
5373
5611
|
latest.current.onData?.(part);
|
|
5612
|
+
if (part.type === "data-queue") {
|
|
5613
|
+
onQueueData(part.data);
|
|
5614
|
+
}
|
|
5374
5615
|
if (part.type === "data-title") {
|
|
5375
5616
|
const title = part.data?.title;
|
|
5376
5617
|
if (typeof title === "string") {
|
|
@@ -5402,6 +5643,20 @@ function useAgentChat(options = {}) {
|
|
|
5402
5643
|
setConnection({
|
|
5403
5644
|
status: "live"
|
|
5404
5645
|
});
|
|
5646
|
+
const queuedInstead = queuedSend.current;
|
|
5647
|
+
if (queuedInstead !== void 0) {
|
|
5648
|
+
queuedSend.current = void 0;
|
|
5649
|
+
chatRef.current.setMessages((current) => {
|
|
5650
|
+
const last = lastUserIndex(current);
|
|
5651
|
+
return last === -1 ? current : current.slice(0, last);
|
|
5652
|
+
});
|
|
5653
|
+
}
|
|
5654
|
+
const next = handoff.current;
|
|
5655
|
+
handoff.current = void 0;
|
|
5656
|
+
if (next !== void 0) {
|
|
5657
|
+
attachTo(next);
|
|
5658
|
+
return;
|
|
5659
|
+
}
|
|
5405
5660
|
if (resyncAfterSettle.current) {
|
|
5406
5661
|
resyncAfterSettle.current = false;
|
|
5407
5662
|
void resyncFromThread();
|
|
@@ -5410,6 +5665,46 @@ function useAgentChat(options = {}) {
|
|
|
5410
5665
|
});
|
|
5411
5666
|
const chatRef = useRef12(chat);
|
|
5412
5667
|
chatRef.current = chat;
|
|
5668
|
+
function onQueueData(data) {
|
|
5669
|
+
const frame = data ?? {};
|
|
5670
|
+
const before = queueStateRef.current;
|
|
5671
|
+
if (frame.queue !== void 0) applyQueue(frame.queue);
|
|
5672
|
+
const started = frame.started;
|
|
5673
|
+
if (started !== void 0 && started.runId !== settlingRunIdRef.current) {
|
|
5674
|
+
const waiting = before.items.find((item) => item.id === started.messageId);
|
|
5675
|
+
handoff.current = {
|
|
5676
|
+
...started,
|
|
5677
|
+
text: waiting?.content ?? "",
|
|
5678
|
+
attachments: waiting?.attachments ?? []
|
|
5679
|
+
};
|
|
5680
|
+
}
|
|
5681
|
+
if (frame.queuedSend !== void 0) {
|
|
5682
|
+
queuedSend.current = frame.queuedSend;
|
|
5683
|
+
if (frame.queuedSend.runId !== void 0) {
|
|
5684
|
+
handoff.current = {
|
|
5685
|
+
messageId: frame.queuedSend.messageId,
|
|
5686
|
+
runId: frame.queuedSend.runId,
|
|
5687
|
+
...draftOf(chatRef.current.messages[lastUserIndex(chatRef.current.messages)])
|
|
5688
|
+
};
|
|
5689
|
+
}
|
|
5690
|
+
}
|
|
5691
|
+
}
|
|
5692
|
+
__name(onQueueData, "onQueueData");
|
|
5693
|
+
function attachTo(turn) {
|
|
5694
|
+
setTimeout(() => {
|
|
5695
|
+
if (turn.text.length > 0 || turn.attachments.length > 0) {
|
|
5696
|
+
chatRef.current.setMessages((current) => current.some((message) => message.id === turn.messageId) ? current : [
|
|
5697
|
+
...current,
|
|
5698
|
+
queuedUserMessage(turn)
|
|
5699
|
+
]);
|
|
5700
|
+
} else {
|
|
5701
|
+
resyncAfterSettle.current = true;
|
|
5702
|
+
}
|
|
5703
|
+
autoResumeRunIdRef.current = turn.runId;
|
|
5704
|
+
void chatRef.current.resumeStream();
|
|
5705
|
+
}, 0);
|
|
5706
|
+
}
|
|
5707
|
+
__name(attachTo, "attachTo");
|
|
5413
5708
|
async function resyncFromThread() {
|
|
5414
5709
|
const threadId = currentThreadId();
|
|
5415
5710
|
if (threadId === void 0) return;
|
|
@@ -5432,6 +5727,11 @@ function useAgentChat(options = {}) {
|
|
|
5432
5727
|
setHistoryError(null);
|
|
5433
5728
|
autoResumeRunIdRef.current = void 0;
|
|
5434
5729
|
pendingPin.current = void 0;
|
|
5730
|
+
applyQueue(EMPTY_QUEUE);
|
|
5731
|
+
setPendingSends([]);
|
|
5732
|
+
setQueueError(null);
|
|
5733
|
+
handoff.current = void 0;
|
|
5734
|
+
queuedSend.current = void 0;
|
|
5435
5735
|
}, [
|
|
5436
5736
|
chatId
|
|
5437
5737
|
]);
|
|
@@ -5444,7 +5744,6 @@ function useAgentChat(options = {}) {
|
|
|
5444
5744
|
const wantResume = current.resume !== false && current.resumeRunId === void 0;
|
|
5445
5745
|
if (!wantHistory && !wantResume) return;
|
|
5446
5746
|
let cancelled = false;
|
|
5447
|
-
if (wantHistory) setIsLoadingHistory(true);
|
|
5448
5747
|
client.getThread(threadId).then((thread) => {
|
|
5449
5748
|
if (cancelled || thread == null) return;
|
|
5450
5749
|
const active = thread.activeRunId ?? null;
|
|
@@ -5452,6 +5751,11 @@ function useAgentChat(options = {}) {
|
|
|
5452
5751
|
setActiveRunId(active);
|
|
5453
5752
|
setThreadModel(thread.model ?? null);
|
|
5454
5753
|
setHistoryError(null);
|
|
5754
|
+
const queue2 = thread.queue ?? EMPTY_QUEUE;
|
|
5755
|
+
applyQueue(queue2);
|
|
5756
|
+
if (queue2.items.length > 0 && queue2.paused === null && active === null && typeof client.resumeQueue === "function") {
|
|
5757
|
+
void resumeQueueRef.current().catch(() => void 0);
|
|
5758
|
+
}
|
|
5455
5759
|
const resuming = wantResume && active !== null;
|
|
5456
5760
|
if (wantHistory && chatRef.current.messages.length === 0) {
|
|
5457
5761
|
const rows = resuming ? stored.filter((message) => !(message.role === "assistant" && message.runId === active)) : stored;
|
|
@@ -5465,17 +5769,17 @@ function useAgentChat(options = {}) {
|
|
|
5465
5769
|
if (cancelled) return;
|
|
5466
5770
|
setHistoryError(error instanceof Error ? error : new Error(String(error)));
|
|
5467
5771
|
}).finally(() => {
|
|
5468
|
-
if (!cancelled)
|
|
5772
|
+
if (!cancelled) setHistoryFor(threadId);
|
|
5469
5773
|
});
|
|
5470
5774
|
return () => {
|
|
5471
5775
|
cancelled = true;
|
|
5472
|
-
setIsLoadingHistory(false);
|
|
5473
5776
|
};
|
|
5474
5777
|
}, [
|
|
5475
5778
|
client,
|
|
5476
5779
|
chatId,
|
|
5477
5780
|
options.threadId
|
|
5478
5781
|
]);
|
|
5782
|
+
const isLoadingHistory = options.threadId !== void 0 && options.threadId !== createdThreadId.current && options.history !== false && options.initialMessages === void 0 && historyFor !== options.threadId;
|
|
5479
5783
|
const turnInFlight = useRef12(false);
|
|
5480
5784
|
const isTurnInFlight = useCallback11(() => turnInFlight.current || transport.isAttemptLive, [
|
|
5481
5785
|
transport
|
|
@@ -5487,10 +5791,98 @@ function useAgentChat(options = {}) {
|
|
|
5487
5791
|
const blocked = options.blocked !== void 0 ? options.blocked : quota.blocked;
|
|
5488
5792
|
const blockedRef = useRef12(blocked);
|
|
5489
5793
|
blockedRef.current = blocked;
|
|
5794
|
+
const whileRunning = typeof client.enqueueMessage === "function" ? options.whileRunning ?? "queue" : "block";
|
|
5795
|
+
const whileRunningRef = useRef12(whileRunning);
|
|
5796
|
+
whileRunningRef.current = whileRunning;
|
|
5797
|
+
const threadForQueue = useCallback11(async () => {
|
|
5798
|
+
const known = currentThreadId();
|
|
5799
|
+
if (known !== void 0) return known;
|
|
5800
|
+
if (!isTurnInFlight()) throw new Error("queue: this chat has no thread yet");
|
|
5801
|
+
return new Promise((resolve) => threadWaiters.current.push(resolve));
|
|
5802
|
+
}, [
|
|
5803
|
+
currentThreadId,
|
|
5804
|
+
isTurnInFlight
|
|
5805
|
+
]);
|
|
5806
|
+
const queueCall = useCallback11(async (run) => {
|
|
5807
|
+
try {
|
|
5808
|
+
const result = await run();
|
|
5809
|
+
setQueueError(null);
|
|
5810
|
+
return result;
|
|
5811
|
+
} catch (error) {
|
|
5812
|
+
setQueueError(error instanceof Error ? error : new Error(String(error)));
|
|
5813
|
+
throw error;
|
|
5814
|
+
}
|
|
5815
|
+
}, []);
|
|
5816
|
+
const enqueue = useCallback11(async (text2, { attachments = [], mode = "queue" } = {}) => {
|
|
5817
|
+
const block = blockedRef.current;
|
|
5818
|
+
if (block != null) throw new QuotaBlockedError(block);
|
|
5819
|
+
const send = client.enqueueMessage;
|
|
5820
|
+
if (typeof send !== "function") throw new AgentBackendUnsupportedError("enqueueMessage");
|
|
5821
|
+
localQueueSequence += 1;
|
|
5822
|
+
const localId = `local-queued-${localQueueSequence}`;
|
|
5823
|
+
setPendingSends((current) => [
|
|
5824
|
+
...current,
|
|
5825
|
+
{
|
|
5826
|
+
id: localId,
|
|
5827
|
+
text: text2,
|
|
5828
|
+
attachments,
|
|
5829
|
+
state: "sending",
|
|
5830
|
+
interrupt: mode === "interrupt",
|
|
5831
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5832
|
+
}
|
|
5833
|
+
]);
|
|
5834
|
+
try {
|
|
5835
|
+
await queueCall(async () => {
|
|
5836
|
+
const threadId = await threadForQueue();
|
|
5837
|
+
const result = await send.call(client, {
|
|
5838
|
+
body: {
|
|
5839
|
+
...turnBody(),
|
|
5840
|
+
threadId,
|
|
5841
|
+
message: text2,
|
|
5842
|
+
mode,
|
|
5843
|
+
...attachments.length > 0 ? {
|
|
5844
|
+
attachments: attachments.map(({ mediaId }) => ({
|
|
5845
|
+
mediaId
|
|
5846
|
+
}))
|
|
5847
|
+
} : {}
|
|
5848
|
+
}
|
|
5849
|
+
});
|
|
5850
|
+
applyQueue(result.queue);
|
|
5851
|
+
if (result.runId !== void 0) {
|
|
5852
|
+
const turn = {
|
|
5853
|
+
messageId: result.messageId,
|
|
5854
|
+
runId: result.runId,
|
|
5855
|
+
text: text2,
|
|
5856
|
+
attachments
|
|
5857
|
+
};
|
|
5858
|
+
if (isTurnInFlight()) handoff.current = turn;
|
|
5859
|
+
else attachTo(turn);
|
|
5860
|
+
}
|
|
5861
|
+
});
|
|
5862
|
+
} finally {
|
|
5863
|
+
setPendingSends((current) => current.filter((item) => item.id !== localId));
|
|
5864
|
+
}
|
|
5865
|
+
}, [
|
|
5866
|
+
client,
|
|
5867
|
+
queueCall,
|
|
5868
|
+
threadForQueue,
|
|
5869
|
+
turnBody,
|
|
5870
|
+
applyQueue,
|
|
5871
|
+
isTurnInFlight
|
|
5872
|
+
]);
|
|
5490
5873
|
const sendMessage = useCallback11(async (...args) => {
|
|
5491
5874
|
const block = blockedRef.current;
|
|
5492
5875
|
if (block != null) throw new QuotaBlockedError(block);
|
|
5493
|
-
if (isTurnInFlight())
|
|
5876
|
+
if (isTurnInFlight()) {
|
|
5877
|
+
const mode = whileRunningRef.current;
|
|
5878
|
+
if (mode === "block") return;
|
|
5879
|
+
const draft = draftOf(args[0]);
|
|
5880
|
+
await enqueue(draft.text, {
|
|
5881
|
+
attachments: draft.attachments,
|
|
5882
|
+
mode
|
|
5883
|
+
});
|
|
5884
|
+
return;
|
|
5885
|
+
}
|
|
5494
5886
|
turnInFlight.current = true;
|
|
5495
5887
|
try {
|
|
5496
5888
|
await chatRef.current.sendMessage(...args);
|
|
@@ -5498,8 +5890,122 @@ function useAgentChat(options = {}) {
|
|
|
5498
5890
|
turnInFlight.current = false;
|
|
5499
5891
|
}
|
|
5500
5892
|
}, [
|
|
5893
|
+
isTurnInFlight,
|
|
5894
|
+
enqueue
|
|
5895
|
+
]);
|
|
5896
|
+
const removeQueued = useCallback11(async (id) => {
|
|
5897
|
+
const previous = queueStateRef.current;
|
|
5898
|
+
applyQueue({
|
|
5899
|
+
...previous,
|
|
5900
|
+
items: previous.items.filter((item) => item.id !== id)
|
|
5901
|
+
});
|
|
5902
|
+
try {
|
|
5903
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "removeQueuedMessage")(id)));
|
|
5904
|
+
} catch (error) {
|
|
5905
|
+
applyQueue(previous);
|
|
5906
|
+
throw error;
|
|
5907
|
+
}
|
|
5908
|
+
}, [
|
|
5909
|
+
client,
|
|
5910
|
+
queueCall,
|
|
5911
|
+
applyQueue
|
|
5912
|
+
]);
|
|
5913
|
+
const editQueued = useCallback11(async (id, text2) => {
|
|
5914
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "updateQueuedMessage")(id, {
|
|
5915
|
+
message: text2
|
|
5916
|
+
})));
|
|
5917
|
+
}, [
|
|
5918
|
+
client,
|
|
5919
|
+
queueCall,
|
|
5920
|
+
applyQueue
|
|
5921
|
+
]);
|
|
5922
|
+
const moveQueued = useCallback11(async (id, index) => {
|
|
5923
|
+
const previous = queueStateRef.current;
|
|
5924
|
+
const moving = previous.items.find((item) => item.id === id);
|
|
5925
|
+
if (moving !== void 0) {
|
|
5926
|
+
const rest = previous.items.filter((item) => item.id !== id);
|
|
5927
|
+
rest.splice(Math.max(0, Math.min(rest.length, index)), 0, moving);
|
|
5928
|
+
applyQueue({
|
|
5929
|
+
...previous,
|
|
5930
|
+
items: rest
|
|
5931
|
+
});
|
|
5932
|
+
}
|
|
5933
|
+
try {
|
|
5934
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "updateQueuedMessage")(id, {
|
|
5935
|
+
position: index
|
|
5936
|
+
})));
|
|
5937
|
+
} catch (error) {
|
|
5938
|
+
applyQueue(previous);
|
|
5939
|
+
throw error;
|
|
5940
|
+
}
|
|
5941
|
+
}, [
|
|
5942
|
+
client,
|
|
5943
|
+
queueCall,
|
|
5944
|
+
applyQueue
|
|
5945
|
+
]);
|
|
5946
|
+
const clearQueue = useCallback11(async () => {
|
|
5947
|
+
const threadId = currentThreadId();
|
|
5948
|
+
if (threadId === void 0) return;
|
|
5949
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "clearQueue")(threadId)));
|
|
5950
|
+
}, [
|
|
5951
|
+
client,
|
|
5952
|
+
queueCall,
|
|
5953
|
+
currentThreadId,
|
|
5954
|
+
applyQueue
|
|
5955
|
+
]);
|
|
5956
|
+
const resumeQueue = useCallback11(async () => {
|
|
5957
|
+
const threadId = currentThreadId();
|
|
5958
|
+
if (threadId === void 0) return;
|
|
5959
|
+
const before = queueStateRef.current;
|
|
5960
|
+
const result = await queueCall(() => requireBackendMethod(client, "resumeQueue")(threadId));
|
|
5961
|
+
applyQueue({
|
|
5962
|
+
items: result.items,
|
|
5963
|
+
paused: result.paused
|
|
5964
|
+
});
|
|
5965
|
+
if (result.runId !== void 0) {
|
|
5966
|
+
const head = before.items.find((item) => item.id === result.runId) ?? before.items[0];
|
|
5967
|
+
const turn = {
|
|
5968
|
+
messageId: result.runId,
|
|
5969
|
+
runId: result.runId,
|
|
5970
|
+
text: head?.content ?? "",
|
|
5971
|
+
attachments: head?.attachments ?? []
|
|
5972
|
+
};
|
|
5973
|
+
if (isTurnInFlight()) handoff.current = turn;
|
|
5974
|
+
else attachTo(turn);
|
|
5975
|
+
}
|
|
5976
|
+
}, [
|
|
5977
|
+
client,
|
|
5978
|
+
queueCall,
|
|
5979
|
+
currentThreadId,
|
|
5980
|
+
applyQueue,
|
|
5501
5981
|
isTurnInFlight
|
|
5502
5982
|
]);
|
|
5983
|
+
const resumeQueueRef = useRef12(resumeQueue);
|
|
5984
|
+
resumeQueueRef.current = resumeQueue;
|
|
5985
|
+
const queuedItems = [
|
|
5986
|
+
...pendingSends.filter((item) => item.interrupt),
|
|
5987
|
+
...queueState.items.map((item) => ({
|
|
5988
|
+
id: item.id,
|
|
5989
|
+
text: item.content,
|
|
5990
|
+
attachments: item.attachments ?? [],
|
|
5991
|
+
state: "queued",
|
|
5992
|
+
interrupt: item.interrupt === true,
|
|
5993
|
+
createdAt: item.createdAt
|
|
5994
|
+
})),
|
|
5995
|
+
...pendingSends.filter((item) => !item.interrupt)
|
|
5996
|
+
];
|
|
5997
|
+
const queue = {
|
|
5998
|
+
items: queuedItems,
|
|
5999
|
+
paused: queueState.paused,
|
|
6000
|
+
isSupported: typeof client.enqueueMessage === "function",
|
|
6001
|
+
add: enqueue,
|
|
6002
|
+
remove: removeQueued,
|
|
6003
|
+
edit: editQueued,
|
|
6004
|
+
move: moveQueued,
|
|
6005
|
+
clear: clearQueue,
|
|
6006
|
+
resume: resumeQueue,
|
|
6007
|
+
error: queueError
|
|
6008
|
+
};
|
|
5503
6009
|
const addToolResult = useCallback11(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
5504
6010
|
tool,
|
|
5505
6011
|
toolCallId,
|
|
@@ -5740,12 +6246,14 @@ function useAgentChat(options = {}) {
|
|
|
5740
6246
|
};
|
|
5741
6247
|
const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
|
|
5742
6248
|
const isBusy = status === "submitted" || status === "streaming" || status === "reconnecting";
|
|
6249
|
+
const isBusyRef = useRef12(isBusy);
|
|
6250
|
+
isBusyRef.current = isBusy;
|
|
5743
6251
|
const files = useAttachments({
|
|
5744
6252
|
...options.composer,
|
|
5745
6253
|
backend: client
|
|
5746
6254
|
});
|
|
5747
6255
|
const [text, setText] = useState15("");
|
|
5748
|
-
const blockedBy = blocked != null ? "quota" : files.isUploading ? "uploading" : isBusy ? "busy" : text.trim().length === 0 && files.refs.length === 0 ? "empty" : null;
|
|
6256
|
+
const blockedBy = blocked != null ? "quota" : files.isUploading ? "uploading" : isBusy && whileRunning === "block" ? "busy" : text.trim().length === 0 && files.refs.length === 0 ? "empty" : null;
|
|
5749
6257
|
const composerRef = useRef12({
|
|
5750
6258
|
text,
|
|
5751
6259
|
files,
|
|
@@ -5776,6 +6284,17 @@ function useAgentChat(options = {}) {
|
|
|
5776
6284
|
const draft = current.text.trim();
|
|
5777
6285
|
setText("");
|
|
5778
6286
|
current.files.clear();
|
|
6287
|
+
const mode = whileRunningRef.current;
|
|
6288
|
+
if (mode !== "block" && (isTurnInFlight() || isBusyRef.current)) {
|
|
6289
|
+
const attachments = current.files.items.flatMap((item) => item.status === "ready" && item.attachment !== void 0 ? [
|
|
6290
|
+
item.attachment
|
|
6291
|
+
] : []);
|
|
6292
|
+
await enqueue(draft, {
|
|
6293
|
+
attachments,
|
|
6294
|
+
mode
|
|
6295
|
+
});
|
|
6296
|
+
return;
|
|
6297
|
+
}
|
|
5779
6298
|
await sendMessage(files2.length > 0 ? {
|
|
5780
6299
|
text: draft,
|
|
5781
6300
|
files: files2
|
|
@@ -5787,7 +6306,9 @@ function useAgentChat(options = {}) {
|
|
|
5787
6306
|
}
|
|
5788
6307
|
} : void 0);
|
|
5789
6308
|
}, [
|
|
5790
|
-
sendMessage
|
|
6309
|
+
sendMessage,
|
|
6310
|
+
enqueue,
|
|
6311
|
+
isTurnInFlight
|
|
5791
6312
|
]);
|
|
5792
6313
|
const composer = {
|
|
5793
6314
|
text,
|
|
@@ -5822,6 +6343,11 @@ function useAgentChat(options = {}) {
|
|
|
5822
6343
|
...ownCatalog === void 0 ? {
|
|
5823
6344
|
toolCatalog: tools.catalog
|
|
5824
6345
|
} : {},
|
|
6346
|
+
queue: {
|
|
6347
|
+
items: queue.items,
|
|
6348
|
+
paused: queue.paused,
|
|
6349
|
+
remove: removeQueued
|
|
6350
|
+
},
|
|
5825
6351
|
...options.transcript
|
|
5826
6352
|
});
|
|
5827
6353
|
const background = {
|
|
@@ -5856,6 +6382,11 @@ function useAgentChat(options = {}) {
|
|
|
5856
6382
|
transcript,
|
|
5857
6383
|
/** A headless composer: draft, files, send gate, `submit()`. */
|
|
5858
6384
|
composer,
|
|
6385
|
+
/**
|
|
6386
|
+
* Messages sent while a turn was running, waiting their turn (server-side): list, edit, move,
|
|
6387
|
+
* remove, clear, and resume a paused queue.
|
|
6388
|
+
*/
|
|
6389
|
+
queue,
|
|
5859
6390
|
/** The model picker: `list`, `selected`, `select(id)`, `pinToThread(id)`. */
|
|
5860
6391
|
models,
|
|
5861
6392
|
/** The caller's budget (`GET <base>/quota`); `quota.blocked` gates sends. */
|
|
@@ -5885,6 +6416,7 @@ var QuotaBlockedError = class extends Error {
|
|
|
5885
6416
|
}
|
|
5886
6417
|
};
|
|
5887
6418
|
export {
|
|
6419
|
+
ALL_AGENTS,
|
|
5888
6420
|
AgentBackendUnsupportedError,
|
|
5889
6421
|
AgentChatTransport,
|
|
5890
6422
|
AgentClient,
|