@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.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
// src/index.ts
|
|
32
32
|
var src_exports = {};
|
|
33
33
|
__export(src_exports, {
|
|
34
|
+
ALL_AGENTS: () => import_nestjs_agent_core4.ALL_AGENTS,
|
|
34
35
|
AgentBackendUnsupportedError: () => AgentBackendUnsupportedError,
|
|
35
36
|
AgentChatTransport: () => AgentChatTransport,
|
|
36
37
|
AgentClient: () => AgentClient,
|
|
@@ -213,12 +214,48 @@ var AgentClient = class {
|
|
|
213
214
|
signal: request.signal
|
|
214
215
|
} : {}
|
|
215
216
|
});
|
|
217
|
+
if (response.status === 202) {
|
|
218
|
+
return {
|
|
219
|
+
body: emptyStream(),
|
|
220
|
+
queued: await response.json()
|
|
221
|
+
};
|
|
222
|
+
}
|
|
216
223
|
if (!response.ok || !response.body) {
|
|
217
224
|
throw await this.failure(response, "POST", `${this.agentPath()}/chat`);
|
|
218
225
|
}
|
|
219
226
|
return streamResponse(response);
|
|
220
227
|
}
|
|
221
228
|
/**
|
|
229
|
+
* `POST <path>/chat` for a message that should wait in the thread's queue — the body carries
|
|
230
|
+
* `mode: 'queue'` (or `'interrupt'`), which the server always answers with `202` JSON.
|
|
231
|
+
*/
|
|
232
|
+
enqueueMessage(request) {
|
|
233
|
+
return this.request("POST", "/chat", {
|
|
234
|
+
mode: "queue",
|
|
235
|
+
...request.body
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/** `GET <path>/threads/:id/queue` — the thread's waiting messages, and whether it drains. */
|
|
239
|
+
getQueue(threadId) {
|
|
240
|
+
return this.request("GET", `/threads/${encodeURIComponent(threadId)}/queue`);
|
|
241
|
+
}
|
|
242
|
+
/** `PATCH <path>/queue/:messageId` — change a waiting message's text/attachments, or move it. */
|
|
243
|
+
updateQueuedMessage(messageId, update) {
|
|
244
|
+
return this.request("PATCH", `/queue/${encodeURIComponent(messageId)}`, update);
|
|
245
|
+
}
|
|
246
|
+
/** `DELETE <path>/queue/:messageId`. */
|
|
247
|
+
removeQueuedMessage(messageId) {
|
|
248
|
+
return this.request("DELETE", `/queue/${encodeURIComponent(messageId)}`);
|
|
249
|
+
}
|
|
250
|
+
/** `DELETE <path>/threads/:id/queue` — drop every waiting message. */
|
|
251
|
+
clearQueue(threadId) {
|
|
252
|
+
return this.request("DELETE", `/threads/${encodeURIComponent(threadId)}/queue`);
|
|
253
|
+
}
|
|
254
|
+
/** `POST <path>/threads/:id/queue/resume` — lift a pause; `runId` when the head started. */
|
|
255
|
+
resumeQueue(threadId) {
|
|
256
|
+
return this.request("POST", `/threads/${encodeURIComponent(threadId)}/queue/resume`);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
222
259
|
* `GET <path>/chat/:runId/stream[?after=<seq>]` → the run's SSE stream, or `null` when nothing is
|
|
223
260
|
* streaming under that id (404).
|
|
224
261
|
*/
|
|
@@ -484,6 +521,14 @@ var AgentClient = class {
|
|
|
484
521
|
return JSON.parse(text);
|
|
485
522
|
}
|
|
486
523
|
};
|
|
524
|
+
function emptyStream() {
|
|
525
|
+
return new ReadableStream({
|
|
526
|
+
start(controller) {
|
|
527
|
+
controller.close();
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
__name(emptyStream, "emptyStream");
|
|
487
532
|
function streamResponse(response) {
|
|
488
533
|
const body = response.body;
|
|
489
534
|
const runId = response.headers?.get(HEADER_RUN_ID) ?? void 0;
|
|
@@ -646,6 +691,10 @@ var AgentChatTransport = class {
|
|
|
646
691
|
signal: options.abortSignal
|
|
647
692
|
} : {}
|
|
648
693
|
});
|
|
694
|
+
if (response.queued !== void 0) {
|
|
695
|
+
this.attemptLive = false;
|
|
696
|
+
return queuedChunkStream(response.queued);
|
|
697
|
+
}
|
|
649
698
|
this.captureHeaderMeta(response);
|
|
650
699
|
return this.toChunkStream(response, {
|
|
651
700
|
...headers !== void 0 ? {
|
|
@@ -676,6 +725,7 @@ var AgentChatTransport = class {
|
|
|
676
725
|
});
|
|
677
726
|
if (response === null) {
|
|
678
727
|
this.attemptLive = false;
|
|
728
|
+
this.options.onResumeGone?.(runId);
|
|
679
729
|
return null;
|
|
680
730
|
}
|
|
681
731
|
this.captureHeaderMeta(response);
|
|
@@ -1053,6 +1103,9 @@ var AgentChatTransport = class {
|
|
|
1053
1103
|
break;
|
|
1054
1104
|
case "title":
|
|
1055
1105
|
case "cancelled":
|
|
1106
|
+
// The thread's queue changed, or this run handed the thread to the next queued message
|
|
1107
|
+
// (`started`). Thread-level, like the title.
|
|
1108
|
+
case "queue":
|
|
1056
1109
|
forwardAsData(event, true);
|
|
1057
1110
|
break;
|
|
1058
1111
|
default:
|
|
@@ -1155,6 +1208,22 @@ var AgentChatTransport = class {
|
|
|
1155
1208
|
});
|
|
1156
1209
|
}
|
|
1157
1210
|
};
|
|
1211
|
+
function queuedChunkStream(queued) {
|
|
1212
|
+
return new ReadableStream({
|
|
1213
|
+
start(controller) {
|
|
1214
|
+
controller.enqueue({
|
|
1215
|
+
type: "data-queue",
|
|
1216
|
+
data: {
|
|
1217
|
+
queue: queued.queue,
|
|
1218
|
+
queuedSend: queued
|
|
1219
|
+
},
|
|
1220
|
+
transient: true
|
|
1221
|
+
});
|
|
1222
|
+
controller.close();
|
|
1223
|
+
}
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
__name(queuedChunkStream, "queuedChunkStream");
|
|
1158
1227
|
function parseSseFrame(raw) {
|
|
1159
1228
|
let event;
|
|
1160
1229
|
let id;
|
|
@@ -3869,6 +3938,7 @@ function useChatTranscript(options) {
|
|
|
3869
3938
|
} : {}
|
|
3870
3939
|
});
|
|
3871
3940
|
const showTypingIndicator = isBusy && lastMessage?.role !== "assistant";
|
|
3941
|
+
const queued = useQueuedItems(options.queue ?? null, options.readOnly === true);
|
|
3872
3942
|
return {
|
|
3873
3943
|
items,
|
|
3874
3944
|
status,
|
|
@@ -3888,13 +3958,56 @@ function useChatTranscript(options) {
|
|
|
3888
3958
|
},
|
|
3889
3959
|
scroll,
|
|
3890
3960
|
stop: {
|
|
3891
|
-
available: isBusy && options.onStop !== void 0,
|
|
3961
|
+
available: isBusy && options.readOnly !== true && options.onStop !== void 0,
|
|
3892
3962
|
isStopping: stopRequested,
|
|
3893
3963
|
stop
|
|
3894
|
-
}
|
|
3964
|
+
},
|
|
3965
|
+
queued,
|
|
3966
|
+
queuePaused: options.queue?.paused ?? null
|
|
3895
3967
|
};
|
|
3896
3968
|
}
|
|
3897
3969
|
__name(useChatTranscript, "useChatTranscript");
|
|
3970
|
+
function useQueuedItems(queue, readOnly) {
|
|
3971
|
+
const latestQueue = (0, import_react17.useRef)(queue);
|
|
3972
|
+
latestQueue.current = queue;
|
|
3973
|
+
const removers = (0, import_react17.useRef)(/* @__PURE__ */ new Map());
|
|
3974
|
+
const removerFor = (0, import_react17.useCallback)((id) => {
|
|
3975
|
+
let remover = removers.current.get(id);
|
|
3976
|
+
if (remover === void 0) {
|
|
3977
|
+
remover = /* @__PURE__ */ __name(() => {
|
|
3978
|
+
void latestQueue.current?.remove?.(id);
|
|
3979
|
+
}, "remover");
|
|
3980
|
+
removers.current.set(id, remover);
|
|
3981
|
+
}
|
|
3982
|
+
return remover;
|
|
3983
|
+
}, []);
|
|
3984
|
+
if (queue === null || queue === void 0) {
|
|
3985
|
+
return [];
|
|
3986
|
+
}
|
|
3987
|
+
return queue.items.map((message, position) => {
|
|
3988
|
+
const state = message.state === "sending" ? "sending" : queue.paused !== null ? "paused" : "queued";
|
|
3989
|
+
return {
|
|
3990
|
+
id: message.id,
|
|
3991
|
+
role: "user",
|
|
3992
|
+
text: message.text,
|
|
3993
|
+
files: message.attachments.map((attachment) => ({
|
|
3994
|
+
url: attachment.url,
|
|
3995
|
+
mediaType: attachment.contentType,
|
|
3996
|
+
filename: attachment.name,
|
|
3997
|
+
isImage: attachment.contentType.startsWith("image/")
|
|
3998
|
+
})),
|
|
3999
|
+
state,
|
|
4000
|
+
position,
|
|
4001
|
+
isNext: position === 0,
|
|
4002
|
+
interrupt: message.interrupt,
|
|
4003
|
+
remove: {
|
|
4004
|
+
available: !readOnly && state !== "sending" && queue.remove !== void 0,
|
|
4005
|
+
run: removerFor(message.id)
|
|
4006
|
+
}
|
|
4007
|
+
};
|
|
4008
|
+
});
|
|
4009
|
+
}
|
|
4010
|
+
__name(useQueuedItems, "useQueuedItems");
|
|
3898
4011
|
function useTranscriptItem(options) {
|
|
3899
4012
|
const { message, isStreaming = false } = options;
|
|
3900
4013
|
const messagesRef = (0, import_react17.useRef)([
|
|
@@ -4158,11 +4271,12 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4158
4271
|
...options.toolCatalog !== void 0 ? {
|
|
4159
4272
|
toolCatalog: options.toolCatalog
|
|
4160
4273
|
} : {},
|
|
4161
|
-
|
|
4274
|
+
// Read-only still lifts question sets into their block, so the outcome shows.
|
|
4275
|
+
...handlers.onAnswer !== void 0 || options.readOnly === true && options.onAnswer !== null ? {
|
|
4162
4276
|
elicitation: {
|
|
4163
4277
|
picked: /* @__PURE__ */ __name((toolCallId, questionId) => picks.get(pickKey(toolCallId, questionId)), "picked"),
|
|
4164
4278
|
pick,
|
|
4165
|
-
canAnswer:
|
|
4279
|
+
canAnswer: handlers.onAnswer !== void 0,
|
|
4166
4280
|
canSkip: handlers.onSkip !== void 0,
|
|
4167
4281
|
answer,
|
|
4168
4282
|
skip,
|
|
@@ -4190,7 +4304,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4190
4304
|
copy: callbacks.copy
|
|
4191
4305
|
},
|
|
4192
4306
|
edit: {
|
|
4193
|
-
available: isUser && options.editable === true && options.onEditSubmit !== void 0,
|
|
4307
|
+
available: isUser && options.readOnly !== true && options.editable === true && options.onEditSubmit !== void 0,
|
|
4194
4308
|
isEditing,
|
|
4195
4309
|
draft: draft ?? text,
|
|
4196
4310
|
canSave: (draft ?? "").trim().length > 0,
|
|
@@ -4206,11 +4320,11 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4206
4320
|
}), "getTextareaProps")
|
|
4207
4321
|
},
|
|
4208
4322
|
fork: {
|
|
4209
|
-
available: options.onFork !== void 0,
|
|
4323
|
+
available: options.readOnly !== true && options.onFork !== void 0,
|
|
4210
4324
|
run: callbacks.fork
|
|
4211
4325
|
},
|
|
4212
4326
|
regenerate: {
|
|
4213
|
-
available: isLastAssistant && options.regeneratable === true && options.onRegenerate !== void 0,
|
|
4327
|
+
available: isLastAssistant && options.readOnly !== true && options.regeneratable === true && options.onRegenerate !== void 0,
|
|
4214
4328
|
run: callbacks.regenerate
|
|
4215
4329
|
}
|
|
4216
4330
|
});
|
|
@@ -4219,7 +4333,7 @@ function useTranscriptItems({ messages, visibleStart, streamingMessageId, option
|
|
|
4219
4333
|
}
|
|
4220
4334
|
__name(useTranscriptItems, "useTranscriptItems");
|
|
4221
4335
|
function settleHandlers(options, backend) {
|
|
4222
|
-
const pick = /* @__PURE__ */ __name((own, fallback2) => own === null ? void 0 : own ?? fallback2, "pick");
|
|
4336
|
+
const pick = /* @__PURE__ */ __name((own, fallback2) => options.readOnly === true || own === null ? void 0 : own ?? fallback2, "pick");
|
|
4223
4337
|
return {
|
|
4224
4338
|
onAnswer: pick(options.onAnswer, backend.answerToolCall ? async (input) => {
|
|
4225
4339
|
await backend.answerToolCall?.(input);
|
|
@@ -4512,10 +4626,13 @@ function joinClasses(...entries) {
|
|
|
4512
4626
|
__name(joinClasses, "joinClasses");
|
|
4513
4627
|
|
|
4514
4628
|
// src/components/message-list.tsx
|
|
4515
|
-
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 }) {
|
|
4629
|
+
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 }) {
|
|
4516
4630
|
const transcript = useChatTranscript({
|
|
4517
4631
|
messages,
|
|
4518
4632
|
status,
|
|
4633
|
+
...readOnly !== void 0 ? {
|
|
4634
|
+
readOnly
|
|
4635
|
+
} : {},
|
|
4519
4636
|
...onFork ? {
|
|
4520
4637
|
onFork
|
|
4521
4638
|
} : {},
|
|
@@ -4539,9 +4656,16 @@ function MessageList({ messages, status, renderToolPart, renderToolGroup, render
|
|
|
4539
4656
|
} : {},
|
|
4540
4657
|
...followUps !== void 0 ? {
|
|
4541
4658
|
followUps
|
|
4659
|
+
} : {},
|
|
4660
|
+
...queue !== void 0 ? {
|
|
4661
|
+
queue: {
|
|
4662
|
+
items: queue.items,
|
|
4663
|
+
paused: queue.paused,
|
|
4664
|
+
remove: queue.remove
|
|
4665
|
+
}
|
|
4542
4666
|
} : {}
|
|
4543
4667
|
});
|
|
4544
|
-
if (transcript.showEmptyState) {
|
|
4668
|
+
if (transcript.showEmptyState && transcript.queued.length === 0) {
|
|
4545
4669
|
return /* @__PURE__ */ React.createElement("div", {
|
|
4546
4670
|
className: classNames?.empty
|
|
4547
4671
|
}, emptyState);
|
|
@@ -4585,7 +4709,23 @@ function MessageList({ messages, status, renderToolPart, renderToolGroup, render
|
|
|
4585
4709
|
...getMeta ? {
|
|
4586
4710
|
meta: getMeta(item.message)
|
|
4587
4711
|
} : {}
|
|
4588
|
-
})), transcript.
|
|
4712
|
+
})), transcript.queued.length > 0 ? /* @__PURE__ */ React.createElement("div", {
|
|
4713
|
+
className: classNames?.queue
|
|
4714
|
+
}, transcript.queued.map((item) => /* @__PURE__ */ React.createElement("div", {
|
|
4715
|
+
key: item.id,
|
|
4716
|
+
className: classNames?.queuedItem,
|
|
4717
|
+
"data-state": item.state
|
|
4718
|
+
}, /* @__PURE__ */ React.createElement("span", null, item.text), item.remove.available ? /* @__PURE__ */ React.createElement("button", {
|
|
4719
|
+
type: "button",
|
|
4720
|
+
className: classNames?.queuedRemove,
|
|
4721
|
+
onClick: item.remove.run
|
|
4722
|
+
}, "Remove") : null)), transcript.queuePaused !== null && queue !== void 0 ? /* @__PURE__ */ React.createElement("div", {
|
|
4723
|
+
className: classNames?.queuePaused
|
|
4724
|
+
}, /* @__PURE__ */ React.createElement("span", null, "Queue paused"), /* @__PURE__ */ React.createElement("button", {
|
|
4725
|
+
type: "button",
|
|
4726
|
+
className: classNames?.queueResume,
|
|
4727
|
+
onClick: /* @__PURE__ */ __name(() => void queue.resume(), "onClick")
|
|
4728
|
+
}, "Resume")) : null) : null, transcript.showTypingIndicator ? /* @__PURE__ */ React.createElement("div", {
|
|
4589
4729
|
className: classNames?.typing
|
|
4590
4730
|
}, typingIndicator ?? "Thinking\u2026") : null, error && onRetry ? /* @__PURE__ */ React.createElement("div", {
|
|
4591
4731
|
className: classNames?.error
|
|
@@ -4980,6 +5120,7 @@ function isPromise(value) {
|
|
|
4980
5120
|
__name(isPromise, "isPromise");
|
|
4981
5121
|
|
|
4982
5122
|
// src/presentation/use-tool-catalog.ts
|
|
5123
|
+
var import_nestjs_agent_core4 = require("@dudousxd/nestjs-agent-core");
|
|
4983
5124
|
var import_react20 = require("react");
|
|
4984
5125
|
var shared2 = /* @__PURE__ */ new WeakMap();
|
|
4985
5126
|
function load2(client, agent) {
|
|
@@ -5099,7 +5240,7 @@ function useApprovalCountdown(expiresAt, options = {}) {
|
|
|
5099
5240
|
__name(useApprovalCountdown, "useApprovalCountdown");
|
|
5100
5241
|
|
|
5101
5242
|
// src/stored-message-to-ui-message.ts
|
|
5102
|
-
var
|
|
5243
|
+
var import_nestjs_agent_core5 = require("@dudousxd/nestjs-agent-core");
|
|
5103
5244
|
function isRefusal(result) {
|
|
5104
5245
|
if (result.denied === true) {
|
|
5105
5246
|
return true;
|
|
@@ -5112,7 +5253,7 @@ function refusalReason(result) {
|
|
|
5112
5253
|
const { output } = result;
|
|
5113
5254
|
if (output !== null && typeof output === "object" && "reason" in output) {
|
|
5114
5255
|
const reason = output.reason;
|
|
5115
|
-
return typeof reason === "string" && reason !==
|
|
5256
|
+
return typeof reason === "string" && reason !== import_nestjs_agent_core5.DEFAULT_REFUSAL_REASON ? reason : void 0;
|
|
5116
5257
|
}
|
|
5117
5258
|
return void 0;
|
|
5118
5259
|
}
|
|
@@ -5353,6 +5494,77 @@ __name(sumUsage, "sumUsage");
|
|
|
5353
5494
|
// src/use-agent-chat.ts
|
|
5354
5495
|
var import_react22 = require("@ai-sdk/react");
|
|
5355
5496
|
var import_react23 = require("react");
|
|
5497
|
+
var EMPTY_QUEUE = {
|
|
5498
|
+
items: [],
|
|
5499
|
+
paused: null
|
|
5500
|
+
};
|
|
5501
|
+
var localQueueSequence = 0;
|
|
5502
|
+
function queuedUserMessage(turn) {
|
|
5503
|
+
return {
|
|
5504
|
+
id: turn.messageId,
|
|
5505
|
+
role: "user",
|
|
5506
|
+
metadata: {
|
|
5507
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5508
|
+
},
|
|
5509
|
+
parts: [
|
|
5510
|
+
...turn.attachments.map((attachment) => ({
|
|
5511
|
+
type: "file",
|
|
5512
|
+
mediaType: attachment.contentType,
|
|
5513
|
+
filename: attachment.name,
|
|
5514
|
+
url: attachment.url,
|
|
5515
|
+
providerMetadata: {
|
|
5516
|
+
agent: {
|
|
5517
|
+
mediaId: attachment.mediaId
|
|
5518
|
+
}
|
|
5519
|
+
}
|
|
5520
|
+
})),
|
|
5521
|
+
...turn.text.length > 0 ? [
|
|
5522
|
+
{
|
|
5523
|
+
type: "text",
|
|
5524
|
+
text: turn.text
|
|
5525
|
+
}
|
|
5526
|
+
] : []
|
|
5527
|
+
]
|
|
5528
|
+
};
|
|
5529
|
+
}
|
|
5530
|
+
__name(queuedUserMessage, "queuedUserMessage");
|
|
5531
|
+
function draftOf(message) {
|
|
5532
|
+
if (typeof message !== "object" || message === null) {
|
|
5533
|
+
return {
|
|
5534
|
+
text: "",
|
|
5535
|
+
attachments: []
|
|
5536
|
+
};
|
|
5537
|
+
}
|
|
5538
|
+
const draft = message;
|
|
5539
|
+
let text = typeof draft.text === "string" ? draft.text : "";
|
|
5540
|
+
if (text === "" && Array.isArray(draft.parts)) {
|
|
5541
|
+
text = draft.parts.filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text).join("");
|
|
5542
|
+
}
|
|
5543
|
+
const files = Array.isArray(draft.files) ? draft.files : Array.isArray(draft.parts) ? draft.parts.filter((part) => part.type === "file") : [];
|
|
5544
|
+
const attachments = files.flatMap((file) => {
|
|
5545
|
+
const mediaId = file.providerMetadata?.agent?.mediaId;
|
|
5546
|
+
return typeof mediaId === "string" ? [
|
|
5547
|
+
{
|
|
5548
|
+
mediaId,
|
|
5549
|
+
url: String(file.url ?? ""),
|
|
5550
|
+
contentType: String(file.mediaType ?? "application/octet-stream"),
|
|
5551
|
+
name: String(file.filename ?? mediaId)
|
|
5552
|
+
}
|
|
5553
|
+
] : [];
|
|
5554
|
+
});
|
|
5555
|
+
return {
|
|
5556
|
+
text,
|
|
5557
|
+
attachments
|
|
5558
|
+
};
|
|
5559
|
+
}
|
|
5560
|
+
__name(draftOf, "draftOf");
|
|
5561
|
+
function lastUserIndex(messages) {
|
|
5562
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
5563
|
+
if (messages[index]?.role === "user") return index;
|
|
5564
|
+
}
|
|
5565
|
+
return -1;
|
|
5566
|
+
}
|
|
5567
|
+
__name(lastUserIndex, "lastUserIndex");
|
|
5356
5568
|
var localChatSequence = 0;
|
|
5357
5569
|
function localChatId() {
|
|
5358
5570
|
localChatSequence += 1;
|
|
@@ -5382,7 +5594,7 @@ function useAgentChat(options = {}) {
|
|
|
5382
5594
|
const settlingRunIdRef = (0, import_react23.useRef)(void 0);
|
|
5383
5595
|
const autoResumeRunIdRef = (0, import_react23.useRef)(void 0);
|
|
5384
5596
|
const [activeRunId, setActiveRunId] = (0, import_react23.useState)(null);
|
|
5385
|
-
const [
|
|
5597
|
+
const [historyFor, setHistoryFor] = (0, import_react23.useState)(void 0);
|
|
5386
5598
|
const [historyError, setHistoryError] = (0, import_react23.useState)(null);
|
|
5387
5599
|
const [threadModel, setThreadModel] = (0, import_react23.useState)(null);
|
|
5388
5600
|
const [pickedModel, setPickedModel] = (0, import_react23.useState)(void 0);
|
|
@@ -5392,6 +5604,41 @@ function useAgentChat(options = {}) {
|
|
|
5392
5604
|
const pendingPin = (0, import_react23.useRef)(void 0);
|
|
5393
5605
|
const regenerateNext = (0, import_react23.useRef)(false);
|
|
5394
5606
|
const currentThreadId = (0, import_react23.useCallback)(() => latest.current.threadId ?? createdThreadId.current, []);
|
|
5607
|
+
const [queueState, setQueueState] = (0, import_react23.useState)(EMPTY_QUEUE);
|
|
5608
|
+
const queueStateRef = (0, import_react23.useRef)(queueState);
|
|
5609
|
+
const applyQueue = (0, import_react23.useCallback)((next) => {
|
|
5610
|
+
queueStateRef.current = next;
|
|
5611
|
+
setQueueState(next);
|
|
5612
|
+
}, []);
|
|
5613
|
+
const [pendingSends, setPendingSends] = (0, import_react23.useState)([]);
|
|
5614
|
+
const [queueError, setQueueError] = (0, import_react23.useState)(null);
|
|
5615
|
+
const handoff = (0, import_react23.useRef)(void 0);
|
|
5616
|
+
const queuedSend = (0, import_react23.useRef)(void 0);
|
|
5617
|
+
const threadWaiters = (0, import_react23.useRef)([]);
|
|
5618
|
+
const turnBody = (0, import_react23.useCallback)(() => {
|
|
5619
|
+
const current = latest.current;
|
|
5620
|
+
const pageContext = current.getPageContext?.() ?? null;
|
|
5621
|
+
const threadId = currentThreadId();
|
|
5622
|
+
const model = current.model ?? (lockedRef.current ? void 0 : pickedModelRef.current);
|
|
5623
|
+
return {
|
|
5624
|
+
// Read per send: a host that switches agents before the first message (a picker on a new
|
|
5625
|
+
// chat) sends the one picked now, not the one this chat mounted with.
|
|
5626
|
+
...current.agent !== void 0 ? {
|
|
5627
|
+
agent: current.agent
|
|
5628
|
+
} : {},
|
|
5629
|
+
...threadId !== void 0 ? {
|
|
5630
|
+
threadId
|
|
5631
|
+
} : {},
|
|
5632
|
+
...model !== void 0 ? {
|
|
5633
|
+
model
|
|
5634
|
+
} : {},
|
|
5635
|
+
...pageContext ? {
|
|
5636
|
+
pageContext
|
|
5637
|
+
} : {}
|
|
5638
|
+
};
|
|
5639
|
+
}, [
|
|
5640
|
+
currentThreadId
|
|
5641
|
+
]);
|
|
5395
5642
|
const [connection, setConnection] = (0, import_react23.useState)({
|
|
5396
5643
|
status: "live"
|
|
5397
5644
|
});
|
|
@@ -5400,6 +5647,11 @@ function useAgentChat(options = {}) {
|
|
|
5400
5647
|
function onMeta(meta) {
|
|
5401
5648
|
settlingRunIdRef.current = meta.runId;
|
|
5402
5649
|
setRunId(meta.runId);
|
|
5650
|
+
if (meta.threadId) {
|
|
5651
|
+
const waiting = threadWaiters.current;
|
|
5652
|
+
threadWaiters.current = [];
|
|
5653
|
+
for (const resolve of waiting) resolve(meta.threadId);
|
|
5654
|
+
}
|
|
5403
5655
|
if (latest.current.threadId === void 0 && meta.threadId && createdThreadId.current !== meta.threadId) {
|
|
5404
5656
|
createdThreadId.current = meta.threadId;
|
|
5405
5657
|
const pin = pendingPin.current;
|
|
@@ -5431,27 +5683,10 @@ function useAgentChat(options = {}) {
|
|
|
5431
5683
|
agent: options.agent
|
|
5432
5684
|
} : {},
|
|
5433
5685
|
getBody: /* @__PURE__ */ __name(() => {
|
|
5434
|
-
const current = latest.current;
|
|
5435
|
-
const pageContext = current.getPageContext?.() ?? null;
|
|
5436
5686
|
const regenerate2 = regenerateNext.current;
|
|
5437
5687
|
regenerateNext.current = false;
|
|
5438
|
-
const threadId = currentThreadId();
|
|
5439
|
-
const model = current.model ?? (lockedRef.current ? void 0 : pickedModelRef.current);
|
|
5440
5688
|
return {
|
|
5441
|
-
|
|
5442
|
-
// new chat) sends the one picked now, not the one this chat mounted with.
|
|
5443
|
-
...current.agent !== void 0 ? {
|
|
5444
|
-
agent: current.agent
|
|
5445
|
-
} : {},
|
|
5446
|
-
...threadId !== void 0 ? {
|
|
5447
|
-
threadId
|
|
5448
|
-
} : {},
|
|
5449
|
-
...model !== void 0 ? {
|
|
5450
|
-
model
|
|
5451
|
-
} : {},
|
|
5452
|
-
...pageContext ? {
|
|
5453
|
-
pageContext
|
|
5454
|
-
} : {},
|
|
5689
|
+
...turnBody(),
|
|
5455
5690
|
...regenerate2 ? {
|
|
5456
5691
|
regenerate: true
|
|
5457
5692
|
} : {}
|
|
@@ -5461,7 +5696,11 @@ function useAgentChat(options = {}) {
|
|
|
5461
5696
|
onMeta,
|
|
5462
5697
|
onAttemptStart: /* @__PURE__ */ __name(() => {
|
|
5463
5698
|
settlingRunIdRef.current = void 0;
|
|
5464
|
-
}, "onAttemptStart")
|
|
5699
|
+
}, "onAttemptStart"),
|
|
5700
|
+
// A queued turn this chat went to attach to had already finished: read what it wrote.
|
|
5701
|
+
onResumeGone: /* @__PURE__ */ __name(() => {
|
|
5702
|
+
void resyncFromThread();
|
|
5703
|
+
}, "onResumeGone")
|
|
5465
5704
|
});
|
|
5466
5705
|
}, []);
|
|
5467
5706
|
const backgroundRef = (0, import_react23.useRef)(() => Promise.resolve());
|
|
@@ -5474,6 +5713,9 @@ function useAgentChat(options = {}) {
|
|
|
5474
5713
|
} : {},
|
|
5475
5714
|
onData: /* @__PURE__ */ __name((part) => {
|
|
5476
5715
|
latest.current.onData?.(part);
|
|
5716
|
+
if (part.type === "data-queue") {
|
|
5717
|
+
onQueueData(part.data);
|
|
5718
|
+
}
|
|
5477
5719
|
if (part.type === "data-title") {
|
|
5478
5720
|
const title = part.data?.title;
|
|
5479
5721
|
if (typeof title === "string") {
|
|
@@ -5505,6 +5747,20 @@ function useAgentChat(options = {}) {
|
|
|
5505
5747
|
setConnection({
|
|
5506
5748
|
status: "live"
|
|
5507
5749
|
});
|
|
5750
|
+
const queuedInstead = queuedSend.current;
|
|
5751
|
+
if (queuedInstead !== void 0) {
|
|
5752
|
+
queuedSend.current = void 0;
|
|
5753
|
+
chatRef.current.setMessages((current) => {
|
|
5754
|
+
const last = lastUserIndex(current);
|
|
5755
|
+
return last === -1 ? current : current.slice(0, last);
|
|
5756
|
+
});
|
|
5757
|
+
}
|
|
5758
|
+
const next = handoff.current;
|
|
5759
|
+
handoff.current = void 0;
|
|
5760
|
+
if (next !== void 0) {
|
|
5761
|
+
attachTo(next);
|
|
5762
|
+
return;
|
|
5763
|
+
}
|
|
5508
5764
|
if (resyncAfterSettle.current) {
|
|
5509
5765
|
resyncAfterSettle.current = false;
|
|
5510
5766
|
void resyncFromThread();
|
|
@@ -5513,6 +5769,46 @@ function useAgentChat(options = {}) {
|
|
|
5513
5769
|
});
|
|
5514
5770
|
const chatRef = (0, import_react23.useRef)(chat);
|
|
5515
5771
|
chatRef.current = chat;
|
|
5772
|
+
function onQueueData(data) {
|
|
5773
|
+
const frame = data ?? {};
|
|
5774
|
+
const before = queueStateRef.current;
|
|
5775
|
+
if (frame.queue !== void 0) applyQueue(frame.queue);
|
|
5776
|
+
const started = frame.started;
|
|
5777
|
+
if (started !== void 0 && started.runId !== settlingRunIdRef.current) {
|
|
5778
|
+
const waiting = before.items.find((item) => item.id === started.messageId);
|
|
5779
|
+
handoff.current = {
|
|
5780
|
+
...started,
|
|
5781
|
+
text: waiting?.content ?? "",
|
|
5782
|
+
attachments: waiting?.attachments ?? []
|
|
5783
|
+
};
|
|
5784
|
+
}
|
|
5785
|
+
if (frame.queuedSend !== void 0) {
|
|
5786
|
+
queuedSend.current = frame.queuedSend;
|
|
5787
|
+
if (frame.queuedSend.runId !== void 0) {
|
|
5788
|
+
handoff.current = {
|
|
5789
|
+
messageId: frame.queuedSend.messageId,
|
|
5790
|
+
runId: frame.queuedSend.runId,
|
|
5791
|
+
...draftOf(chatRef.current.messages[lastUserIndex(chatRef.current.messages)])
|
|
5792
|
+
};
|
|
5793
|
+
}
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
__name(onQueueData, "onQueueData");
|
|
5797
|
+
function attachTo(turn) {
|
|
5798
|
+
setTimeout(() => {
|
|
5799
|
+
if (turn.text.length > 0 || turn.attachments.length > 0) {
|
|
5800
|
+
chatRef.current.setMessages((current) => current.some((message) => message.id === turn.messageId) ? current : [
|
|
5801
|
+
...current,
|
|
5802
|
+
queuedUserMessage(turn)
|
|
5803
|
+
]);
|
|
5804
|
+
} else {
|
|
5805
|
+
resyncAfterSettle.current = true;
|
|
5806
|
+
}
|
|
5807
|
+
autoResumeRunIdRef.current = turn.runId;
|
|
5808
|
+
void chatRef.current.resumeStream();
|
|
5809
|
+
}, 0);
|
|
5810
|
+
}
|
|
5811
|
+
__name(attachTo, "attachTo");
|
|
5516
5812
|
async function resyncFromThread() {
|
|
5517
5813
|
const threadId = currentThreadId();
|
|
5518
5814
|
if (threadId === void 0) return;
|
|
@@ -5535,6 +5831,11 @@ function useAgentChat(options = {}) {
|
|
|
5535
5831
|
setHistoryError(null);
|
|
5536
5832
|
autoResumeRunIdRef.current = void 0;
|
|
5537
5833
|
pendingPin.current = void 0;
|
|
5834
|
+
applyQueue(EMPTY_QUEUE);
|
|
5835
|
+
setPendingSends([]);
|
|
5836
|
+
setQueueError(null);
|
|
5837
|
+
handoff.current = void 0;
|
|
5838
|
+
queuedSend.current = void 0;
|
|
5538
5839
|
}, [
|
|
5539
5840
|
chatId
|
|
5540
5841
|
]);
|
|
@@ -5547,7 +5848,6 @@ function useAgentChat(options = {}) {
|
|
|
5547
5848
|
const wantResume = current.resume !== false && current.resumeRunId === void 0;
|
|
5548
5849
|
if (!wantHistory && !wantResume) return;
|
|
5549
5850
|
let cancelled = false;
|
|
5550
|
-
if (wantHistory) setIsLoadingHistory(true);
|
|
5551
5851
|
client.getThread(threadId).then((thread) => {
|
|
5552
5852
|
if (cancelled || thread == null) return;
|
|
5553
5853
|
const active = thread.activeRunId ?? null;
|
|
@@ -5555,6 +5855,11 @@ function useAgentChat(options = {}) {
|
|
|
5555
5855
|
setActiveRunId(active);
|
|
5556
5856
|
setThreadModel(thread.model ?? null);
|
|
5557
5857
|
setHistoryError(null);
|
|
5858
|
+
const queue2 = thread.queue ?? EMPTY_QUEUE;
|
|
5859
|
+
applyQueue(queue2);
|
|
5860
|
+
if (queue2.items.length > 0 && queue2.paused === null && active === null && typeof client.resumeQueue === "function") {
|
|
5861
|
+
void resumeQueueRef.current().catch(() => void 0);
|
|
5862
|
+
}
|
|
5558
5863
|
const resuming = wantResume && active !== null;
|
|
5559
5864
|
if (wantHistory && chatRef.current.messages.length === 0) {
|
|
5560
5865
|
const rows = resuming ? stored.filter((message) => !(message.role === "assistant" && message.runId === active)) : stored;
|
|
@@ -5568,17 +5873,17 @@ function useAgentChat(options = {}) {
|
|
|
5568
5873
|
if (cancelled) return;
|
|
5569
5874
|
setHistoryError(error instanceof Error ? error : new Error(String(error)));
|
|
5570
5875
|
}).finally(() => {
|
|
5571
|
-
if (!cancelled)
|
|
5876
|
+
if (!cancelled) setHistoryFor(threadId);
|
|
5572
5877
|
});
|
|
5573
5878
|
return () => {
|
|
5574
5879
|
cancelled = true;
|
|
5575
|
-
setIsLoadingHistory(false);
|
|
5576
5880
|
};
|
|
5577
5881
|
}, [
|
|
5578
5882
|
client,
|
|
5579
5883
|
chatId,
|
|
5580
5884
|
options.threadId
|
|
5581
5885
|
]);
|
|
5886
|
+
const isLoadingHistory = options.threadId !== void 0 && options.threadId !== createdThreadId.current && options.history !== false && options.initialMessages === void 0 && historyFor !== options.threadId;
|
|
5582
5887
|
const turnInFlight = (0, import_react23.useRef)(false);
|
|
5583
5888
|
const isTurnInFlight = (0, import_react23.useCallback)(() => turnInFlight.current || transport.isAttemptLive, [
|
|
5584
5889
|
transport
|
|
@@ -5590,10 +5895,98 @@ function useAgentChat(options = {}) {
|
|
|
5590
5895
|
const blocked = options.blocked !== void 0 ? options.blocked : quota.blocked;
|
|
5591
5896
|
const blockedRef = (0, import_react23.useRef)(blocked);
|
|
5592
5897
|
blockedRef.current = blocked;
|
|
5898
|
+
const whileRunning = typeof client.enqueueMessage === "function" ? options.whileRunning ?? "queue" : "block";
|
|
5899
|
+
const whileRunningRef = (0, import_react23.useRef)(whileRunning);
|
|
5900
|
+
whileRunningRef.current = whileRunning;
|
|
5901
|
+
const threadForQueue = (0, import_react23.useCallback)(async () => {
|
|
5902
|
+
const known = currentThreadId();
|
|
5903
|
+
if (known !== void 0) return known;
|
|
5904
|
+
if (!isTurnInFlight()) throw new Error("queue: this chat has no thread yet");
|
|
5905
|
+
return new Promise((resolve) => threadWaiters.current.push(resolve));
|
|
5906
|
+
}, [
|
|
5907
|
+
currentThreadId,
|
|
5908
|
+
isTurnInFlight
|
|
5909
|
+
]);
|
|
5910
|
+
const queueCall = (0, import_react23.useCallback)(async (run) => {
|
|
5911
|
+
try {
|
|
5912
|
+
const result = await run();
|
|
5913
|
+
setQueueError(null);
|
|
5914
|
+
return result;
|
|
5915
|
+
} catch (error) {
|
|
5916
|
+
setQueueError(error instanceof Error ? error : new Error(String(error)));
|
|
5917
|
+
throw error;
|
|
5918
|
+
}
|
|
5919
|
+
}, []);
|
|
5920
|
+
const enqueue = (0, import_react23.useCallback)(async (text2, { attachments = [], mode = "queue" } = {}) => {
|
|
5921
|
+
const block = blockedRef.current;
|
|
5922
|
+
if (block != null) throw new QuotaBlockedError(block);
|
|
5923
|
+
const send = client.enqueueMessage;
|
|
5924
|
+
if (typeof send !== "function") throw new AgentBackendUnsupportedError("enqueueMessage");
|
|
5925
|
+
localQueueSequence += 1;
|
|
5926
|
+
const localId = `local-queued-${localQueueSequence}`;
|
|
5927
|
+
setPendingSends((current) => [
|
|
5928
|
+
...current,
|
|
5929
|
+
{
|
|
5930
|
+
id: localId,
|
|
5931
|
+
text: text2,
|
|
5932
|
+
attachments,
|
|
5933
|
+
state: "sending",
|
|
5934
|
+
interrupt: mode === "interrupt",
|
|
5935
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5936
|
+
}
|
|
5937
|
+
]);
|
|
5938
|
+
try {
|
|
5939
|
+
await queueCall(async () => {
|
|
5940
|
+
const threadId = await threadForQueue();
|
|
5941
|
+
const result = await send.call(client, {
|
|
5942
|
+
body: {
|
|
5943
|
+
...turnBody(),
|
|
5944
|
+
threadId,
|
|
5945
|
+
message: text2,
|
|
5946
|
+
mode,
|
|
5947
|
+
...attachments.length > 0 ? {
|
|
5948
|
+
attachments: attachments.map(({ mediaId }) => ({
|
|
5949
|
+
mediaId
|
|
5950
|
+
}))
|
|
5951
|
+
} : {}
|
|
5952
|
+
}
|
|
5953
|
+
});
|
|
5954
|
+
applyQueue(result.queue);
|
|
5955
|
+
if (result.runId !== void 0) {
|
|
5956
|
+
const turn = {
|
|
5957
|
+
messageId: result.messageId,
|
|
5958
|
+
runId: result.runId,
|
|
5959
|
+
text: text2,
|
|
5960
|
+
attachments
|
|
5961
|
+
};
|
|
5962
|
+
if (isTurnInFlight()) handoff.current = turn;
|
|
5963
|
+
else attachTo(turn);
|
|
5964
|
+
}
|
|
5965
|
+
});
|
|
5966
|
+
} finally {
|
|
5967
|
+
setPendingSends((current) => current.filter((item) => item.id !== localId));
|
|
5968
|
+
}
|
|
5969
|
+
}, [
|
|
5970
|
+
client,
|
|
5971
|
+
queueCall,
|
|
5972
|
+
threadForQueue,
|
|
5973
|
+
turnBody,
|
|
5974
|
+
applyQueue,
|
|
5975
|
+
isTurnInFlight
|
|
5976
|
+
]);
|
|
5593
5977
|
const sendMessage = (0, import_react23.useCallback)(async (...args) => {
|
|
5594
5978
|
const block = blockedRef.current;
|
|
5595
5979
|
if (block != null) throw new QuotaBlockedError(block);
|
|
5596
|
-
if (isTurnInFlight())
|
|
5980
|
+
if (isTurnInFlight()) {
|
|
5981
|
+
const mode = whileRunningRef.current;
|
|
5982
|
+
if (mode === "block") return;
|
|
5983
|
+
const draft = draftOf(args[0]);
|
|
5984
|
+
await enqueue(draft.text, {
|
|
5985
|
+
attachments: draft.attachments,
|
|
5986
|
+
mode
|
|
5987
|
+
});
|
|
5988
|
+
return;
|
|
5989
|
+
}
|
|
5597
5990
|
turnInFlight.current = true;
|
|
5598
5991
|
try {
|
|
5599
5992
|
await chatRef.current.sendMessage(...args);
|
|
@@ -5601,8 +5994,122 @@ function useAgentChat(options = {}) {
|
|
|
5601
5994
|
turnInFlight.current = false;
|
|
5602
5995
|
}
|
|
5603
5996
|
}, [
|
|
5997
|
+
isTurnInFlight,
|
|
5998
|
+
enqueue
|
|
5999
|
+
]);
|
|
6000
|
+
const removeQueued = (0, import_react23.useCallback)(async (id) => {
|
|
6001
|
+
const previous = queueStateRef.current;
|
|
6002
|
+
applyQueue({
|
|
6003
|
+
...previous,
|
|
6004
|
+
items: previous.items.filter((item) => item.id !== id)
|
|
6005
|
+
});
|
|
6006
|
+
try {
|
|
6007
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "removeQueuedMessage")(id)));
|
|
6008
|
+
} catch (error) {
|
|
6009
|
+
applyQueue(previous);
|
|
6010
|
+
throw error;
|
|
6011
|
+
}
|
|
6012
|
+
}, [
|
|
6013
|
+
client,
|
|
6014
|
+
queueCall,
|
|
6015
|
+
applyQueue
|
|
6016
|
+
]);
|
|
6017
|
+
const editQueued = (0, import_react23.useCallback)(async (id, text2) => {
|
|
6018
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "updateQueuedMessage")(id, {
|
|
6019
|
+
message: text2
|
|
6020
|
+
})));
|
|
6021
|
+
}, [
|
|
6022
|
+
client,
|
|
6023
|
+
queueCall,
|
|
6024
|
+
applyQueue
|
|
6025
|
+
]);
|
|
6026
|
+
const moveQueued = (0, import_react23.useCallback)(async (id, index) => {
|
|
6027
|
+
const previous = queueStateRef.current;
|
|
6028
|
+
const moving = previous.items.find((item) => item.id === id);
|
|
6029
|
+
if (moving !== void 0) {
|
|
6030
|
+
const rest = previous.items.filter((item) => item.id !== id);
|
|
6031
|
+
rest.splice(Math.max(0, Math.min(rest.length, index)), 0, moving);
|
|
6032
|
+
applyQueue({
|
|
6033
|
+
...previous,
|
|
6034
|
+
items: rest
|
|
6035
|
+
});
|
|
6036
|
+
}
|
|
6037
|
+
try {
|
|
6038
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "updateQueuedMessage")(id, {
|
|
6039
|
+
position: index
|
|
6040
|
+
})));
|
|
6041
|
+
} catch (error) {
|
|
6042
|
+
applyQueue(previous);
|
|
6043
|
+
throw error;
|
|
6044
|
+
}
|
|
6045
|
+
}, [
|
|
6046
|
+
client,
|
|
6047
|
+
queueCall,
|
|
6048
|
+
applyQueue
|
|
6049
|
+
]);
|
|
6050
|
+
const clearQueue = (0, import_react23.useCallback)(async () => {
|
|
6051
|
+
const threadId = currentThreadId();
|
|
6052
|
+
if (threadId === void 0) return;
|
|
6053
|
+
applyQueue(await queueCall(() => requireBackendMethod(client, "clearQueue")(threadId)));
|
|
6054
|
+
}, [
|
|
6055
|
+
client,
|
|
6056
|
+
queueCall,
|
|
6057
|
+
currentThreadId,
|
|
6058
|
+
applyQueue
|
|
6059
|
+
]);
|
|
6060
|
+
const resumeQueue = (0, import_react23.useCallback)(async () => {
|
|
6061
|
+
const threadId = currentThreadId();
|
|
6062
|
+
if (threadId === void 0) return;
|
|
6063
|
+
const before = queueStateRef.current;
|
|
6064
|
+
const result = await queueCall(() => requireBackendMethod(client, "resumeQueue")(threadId));
|
|
6065
|
+
applyQueue({
|
|
6066
|
+
items: result.items,
|
|
6067
|
+
paused: result.paused
|
|
6068
|
+
});
|
|
6069
|
+
if (result.runId !== void 0) {
|
|
6070
|
+
const head = before.items.find((item) => item.id === result.runId) ?? before.items[0];
|
|
6071
|
+
const turn = {
|
|
6072
|
+
messageId: result.runId,
|
|
6073
|
+
runId: result.runId,
|
|
6074
|
+
text: head?.content ?? "",
|
|
6075
|
+
attachments: head?.attachments ?? []
|
|
6076
|
+
};
|
|
6077
|
+
if (isTurnInFlight()) handoff.current = turn;
|
|
6078
|
+
else attachTo(turn);
|
|
6079
|
+
}
|
|
6080
|
+
}, [
|
|
6081
|
+
client,
|
|
6082
|
+
queueCall,
|
|
6083
|
+
currentThreadId,
|
|
6084
|
+
applyQueue,
|
|
5604
6085
|
isTurnInFlight
|
|
5605
6086
|
]);
|
|
6087
|
+
const resumeQueueRef = (0, import_react23.useRef)(resumeQueue);
|
|
6088
|
+
resumeQueueRef.current = resumeQueue;
|
|
6089
|
+
const queuedItems = [
|
|
6090
|
+
...pendingSends.filter((item) => item.interrupt),
|
|
6091
|
+
...queueState.items.map((item) => ({
|
|
6092
|
+
id: item.id,
|
|
6093
|
+
text: item.content,
|
|
6094
|
+
attachments: item.attachments ?? [],
|
|
6095
|
+
state: "queued",
|
|
6096
|
+
interrupt: item.interrupt === true,
|
|
6097
|
+
createdAt: item.createdAt
|
|
6098
|
+
})),
|
|
6099
|
+
...pendingSends.filter((item) => !item.interrupt)
|
|
6100
|
+
];
|
|
6101
|
+
const queue = {
|
|
6102
|
+
items: queuedItems,
|
|
6103
|
+
paused: queueState.paused,
|
|
6104
|
+
isSupported: typeof client.enqueueMessage === "function",
|
|
6105
|
+
add: enqueue,
|
|
6106
|
+
remove: removeQueued,
|
|
6107
|
+
edit: editQueued,
|
|
6108
|
+
move: moveQueued,
|
|
6109
|
+
clear: clearQueue,
|
|
6110
|
+
resume: resumeQueue,
|
|
6111
|
+
error: queueError
|
|
6112
|
+
};
|
|
5606
6113
|
const addToolResult = (0, import_react23.useCallback)(({ tool, toolCallId, output }) => chatRef.current.addToolResult({
|
|
5607
6114
|
tool,
|
|
5608
6115
|
toolCallId,
|
|
@@ -5843,12 +6350,14 @@ function useAgentChat(options = {}) {
|
|
|
5843
6350
|
};
|
|
5844
6351
|
const status = connection.status === "reconnecting" ? "reconnecting" : chat.status;
|
|
5845
6352
|
const isBusy = status === "submitted" || status === "streaming" || status === "reconnecting";
|
|
6353
|
+
const isBusyRef = (0, import_react23.useRef)(isBusy);
|
|
6354
|
+
isBusyRef.current = isBusy;
|
|
5846
6355
|
const files = useAttachments({
|
|
5847
6356
|
...options.composer,
|
|
5848
6357
|
backend: client
|
|
5849
6358
|
});
|
|
5850
6359
|
const [text, setText] = (0, import_react23.useState)("");
|
|
5851
|
-
const blockedBy = blocked != null ? "quota" : files.isUploading ? "uploading" : isBusy ? "busy" : text.trim().length === 0 && files.refs.length === 0 ? "empty" : null;
|
|
6360
|
+
const blockedBy = blocked != null ? "quota" : files.isUploading ? "uploading" : isBusy && whileRunning === "block" ? "busy" : text.trim().length === 0 && files.refs.length === 0 ? "empty" : null;
|
|
5852
6361
|
const composerRef = (0, import_react23.useRef)({
|
|
5853
6362
|
text,
|
|
5854
6363
|
files,
|
|
@@ -5879,6 +6388,17 @@ function useAgentChat(options = {}) {
|
|
|
5879
6388
|
const draft = current.text.trim();
|
|
5880
6389
|
setText("");
|
|
5881
6390
|
current.files.clear();
|
|
6391
|
+
const mode = whileRunningRef.current;
|
|
6392
|
+
if (mode !== "block" && (isTurnInFlight() || isBusyRef.current)) {
|
|
6393
|
+
const attachments = current.files.items.flatMap((item) => item.status === "ready" && item.attachment !== void 0 ? [
|
|
6394
|
+
item.attachment
|
|
6395
|
+
] : []);
|
|
6396
|
+
await enqueue(draft, {
|
|
6397
|
+
attachments,
|
|
6398
|
+
mode
|
|
6399
|
+
});
|
|
6400
|
+
return;
|
|
6401
|
+
}
|
|
5882
6402
|
await sendMessage(files2.length > 0 ? {
|
|
5883
6403
|
text: draft,
|
|
5884
6404
|
files: files2
|
|
@@ -5890,7 +6410,9 @@ function useAgentChat(options = {}) {
|
|
|
5890
6410
|
}
|
|
5891
6411
|
} : void 0);
|
|
5892
6412
|
}, [
|
|
5893
|
-
sendMessage
|
|
6413
|
+
sendMessage,
|
|
6414
|
+
enqueue,
|
|
6415
|
+
isTurnInFlight
|
|
5894
6416
|
]);
|
|
5895
6417
|
const composer = {
|
|
5896
6418
|
text,
|
|
@@ -5925,6 +6447,11 @@ function useAgentChat(options = {}) {
|
|
|
5925
6447
|
...ownCatalog === void 0 ? {
|
|
5926
6448
|
toolCatalog: tools.catalog
|
|
5927
6449
|
} : {},
|
|
6450
|
+
queue: {
|
|
6451
|
+
items: queue.items,
|
|
6452
|
+
paused: queue.paused,
|
|
6453
|
+
remove: removeQueued
|
|
6454
|
+
},
|
|
5928
6455
|
...options.transcript
|
|
5929
6456
|
});
|
|
5930
6457
|
const background = {
|
|
@@ -5959,6 +6486,11 @@ function useAgentChat(options = {}) {
|
|
|
5959
6486
|
transcript,
|
|
5960
6487
|
/** A headless composer: draft, files, send gate, `submit()`. */
|
|
5961
6488
|
composer,
|
|
6489
|
+
/**
|
|
6490
|
+
* Messages sent while a turn was running, waiting their turn (server-side): list, edit, move,
|
|
6491
|
+
* remove, clear, and resume a paused queue.
|
|
6492
|
+
*/
|
|
6493
|
+
queue,
|
|
5962
6494
|
/** The model picker: `list`, `selected`, `select(id)`, `pinToThread(id)`. */
|
|
5963
6495
|
models,
|
|
5964
6496
|
/** The caller's budget (`GET <base>/quota`); `quota.blocked` gates sends. */
|
|
@@ -5989,6 +6521,7 @@ var QuotaBlockedError = class extends Error {
|
|
|
5989
6521
|
};
|
|
5990
6522
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5991
6523
|
0 && (module.exports = {
|
|
6524
|
+
ALL_AGENTS,
|
|
5992
6525
|
AgentBackendUnsupportedError,
|
|
5993
6526
|
AgentChatTransport,
|
|
5994
6527
|
AgentClient,
|