@invergent/agent-chat-react 2.12.0 → 2.13.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/dist/index.cjs +1155 -560
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -2
- package/dist/index.d.ts +25 -2
- package/dist/index.js +1144 -555
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { cn, __publicField } from './chunk-47MF6JUW.js';
|
|
2
2
|
import { createContext, memo, useRef, useEffect, useCallback, useMemo, useState, useLayoutEffect, forwardRef, useId, lazy, useContext, useSyncExternalStore, Fragment as Fragment$1, Children, Suspense } from 'react';
|
|
3
|
-
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, CreditCard, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, PlugZapIcon, ChevronRightIcon, ArrowLeftIcon, Loader2Icon, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, Maximize2Icon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, CheckIcon, Clock, ArrowRight, TerminalIcon, SparklesIcon, ClockIcon,
|
|
3
|
+
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, CreditCard, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, PlugZapIcon, ChevronRightIcon, ArrowLeftIcon, Loader2Icon, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, Maximize2Icon, FolderOpenIcon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, ChevronLeftIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, CheckIcon, Clock, ArrowRight, ListTreeIcon, TerminalIcon, SparklesIcon, ClockIcon, PaperclipIcon, CloudIcon, HardDriveIcon, GlobeIcon, FolderIcon, IdCardIcon, PlusIcon, CopyIcon, FileIcon, MinusIcon, SearchIcon, CircleDot, XCircle, CheckCircle2, ArrowUpIcon, CircleCheckIcon, ThumbsUpIcon, ThumbsDownIcon, BookOpenIcon, GitBranchIcon, ActivityIcon, EyeIcon, ListTodoIcon, BotIcon, ListIcon, PenLineIcon, FileEditIcon, WrenchIcon, FileCodeIcon, CodeIcon, FileArchiveIcon, FileVideoIcon, FileAudioIcon, FileImageIcon, FileChartLineIcon, FileSpreadsheetIcon, XCircleIcon, CheckCircle2Icon, Code, CircleXIcon, SkullIcon, CheckCircleIcon, CircleIcon } from 'lucide-react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
5
|
import { Collapsible as Collapsible$1, Slot, Tooltip as Tooltip$1, Progress as Progress$1, Tabs as Tabs$1, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Popover as Popover$1 } from 'radix-ui';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -2275,7 +2275,7 @@ function TabsTrigger({
|
|
|
2275
2275
|
{
|
|
2276
2276
|
"data-slot": "tabs-trigger",
|
|
2277
2277
|
className: cn(
|
|
2278
|
-
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-2 border border-transparent px-4 py-1.5 text-xs font-semibold tracking-wider whitespace-nowrap text-foreground/60 uppercase transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start group-data-vertical/tabs:px-4 group-data-vertical/tabs:py-2 hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
2278
|
+
"relative inline-flex min-h-11 h-[calc(100%-1px)] flex-1 items-center justify-center gap-2 border border-transparent px-4 py-1.5 pointer-fine:min-h-0 text-xs font-semibold tracking-wider whitespace-nowrap text-foreground/60 uppercase transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start group-data-vertical/tabs:px-4 group-data-vertical/tabs:py-2 hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-2.5 has-data-[icon=inline-start]:pl-2.5 dark:text-muted-foreground dark:hover:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5",
|
|
2279
2279
|
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
|
|
2280
2280
|
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
|
|
2281
2281
|
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",
|
|
@@ -4033,21 +4033,162 @@ var OTHER_INDEX_OFFSET = 1e6;
|
|
|
4033
4033
|
function emptySelection() {
|
|
4034
4034
|
return { index: -1, other: "" };
|
|
4035
4035
|
}
|
|
4036
|
+
function hasChoices(question) {
|
|
4037
|
+
return (question.choices?.length ?? 0) > 0;
|
|
4038
|
+
}
|
|
4036
4039
|
function buildAnswer(q, sel) {
|
|
4037
4040
|
if (sel.index < 0) return null;
|
|
4038
4041
|
if (sel.index >= OTHER_INDEX_OFFSET) {
|
|
4039
4042
|
const text = sel.other.trim();
|
|
4040
4043
|
if (!text) return null;
|
|
4041
|
-
return { question: q.prompt, answer: text, is_other:
|
|
4044
|
+
return { question: q.prompt, answer: text, is_other: hasChoices(q) };
|
|
4042
4045
|
}
|
|
4043
4046
|
const choice = q.choices?.[sel.index];
|
|
4044
4047
|
if (!choice) return null;
|
|
4045
4048
|
return { question: q.prompt, answer: choice.label, is_other: false };
|
|
4046
4049
|
}
|
|
4047
|
-
function
|
|
4050
|
+
function askQuestionsOf(tc) {
|
|
4051
|
+
return parseArgs(tc.args)?.questions ?? [];
|
|
4052
|
+
}
|
|
4053
|
+
function isConversationalAsk(questions) {
|
|
4054
|
+
return questions.length === 1;
|
|
4055
|
+
}
|
|
4056
|
+
function conversationalAskAcceptsFreeText(question) {
|
|
4057
|
+
return !hasChoices(question) || question.allow_other !== false;
|
|
4058
|
+
}
|
|
4059
|
+
function promptEchoedInContent(content, prompt) {
|
|
4060
|
+
const normalize = (s) => s.replace(/[*_`]/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
4061
|
+
const body = normalize(content ?? "");
|
|
4062
|
+
const question = normalize(prompt);
|
|
4063
|
+
return !!body && !!question && body.endsWith(question);
|
|
4064
|
+
}
|
|
4065
|
+
function AskUserQuestionToolBlock({
|
|
4066
|
+
tc,
|
|
4067
|
+
assistantContent,
|
|
4068
|
+
viewMode = "simple"
|
|
4069
|
+
}) {
|
|
4070
|
+
const questions = useMemo(() => askQuestionsOf(tc), [tc.args]);
|
|
4071
|
+
if (questions.length === 0) {
|
|
4072
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-destructive/50 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: "ask_user_question: no questions provided" });
|
|
4073
|
+
}
|
|
4074
|
+
if (isConversationalAsk(questions)) {
|
|
4075
|
+
return /* @__PURE__ */ jsx(
|
|
4076
|
+
ConversationalAsk,
|
|
4077
|
+
{
|
|
4078
|
+
tc,
|
|
4079
|
+
question: questions[0],
|
|
4080
|
+
assistantContent
|
|
4081
|
+
}
|
|
4082
|
+
);
|
|
4083
|
+
}
|
|
4084
|
+
return /* @__PURE__ */ jsx(BatchAsk, { tc, questions, viewMode });
|
|
4085
|
+
}
|
|
4086
|
+
function ConversationalAsk({
|
|
4087
|
+
tc,
|
|
4088
|
+
question,
|
|
4089
|
+
assistantContent
|
|
4090
|
+
}) {
|
|
4091
|
+
const { adapter, sessionId } = useAgentChatAdapterContext();
|
|
4092
|
+
const [submitting, setSubmitting] = useState(false);
|
|
4093
|
+
const [error, setError] = useState(null);
|
|
4094
|
+
const answered = tc.askUserQuestionAnswers !== void 0;
|
|
4095
|
+
const pending = !answered && tc.status === "running";
|
|
4096
|
+
const choices = question.choices ?? [];
|
|
4097
|
+
const showPrompt = useMemo(
|
|
4098
|
+
() => !promptEchoedInContent(assistantContent, question.prompt),
|
|
4099
|
+
[assistantContent, question.prompt]
|
|
4100
|
+
);
|
|
4101
|
+
useEffect(() => {
|
|
4102
|
+
if (!pending || !sessionId) return;
|
|
4103
|
+
const handler = (e) => {
|
|
4104
|
+
if (e.key !== "Escape") return;
|
|
4105
|
+
e.preventDefault();
|
|
4106
|
+
void adapter.pauseSession({ sessionId }).catch(() => {
|
|
4107
|
+
});
|
|
4108
|
+
};
|
|
4109
|
+
window.addEventListener("keydown", handler);
|
|
4110
|
+
return () => window.removeEventListener("keydown", handler);
|
|
4111
|
+
}, [adapter, sessionId, pending]);
|
|
4112
|
+
const pick = useCallback(
|
|
4113
|
+
async (label) => {
|
|
4114
|
+
if (!sessionId || submitting) return;
|
|
4115
|
+
setError(null);
|
|
4116
|
+
setSubmitting(true);
|
|
4117
|
+
try {
|
|
4118
|
+
await adapter.submitAskUserQuestionResponse({
|
|
4119
|
+
sessionId,
|
|
4120
|
+
toolCallId: tc.id,
|
|
4121
|
+
responses: [
|
|
4122
|
+
{ question: question.prompt, answer: label, is_other: false }
|
|
4123
|
+
]
|
|
4124
|
+
});
|
|
4125
|
+
} catch (e) {
|
|
4126
|
+
setError(e instanceof Error ? e.message : "Submit failed.");
|
|
4127
|
+
setSubmitting(false);
|
|
4128
|
+
}
|
|
4129
|
+
},
|
|
4130
|
+
[adapter, sessionId, tc.id, question.prompt, submitting]
|
|
4131
|
+
);
|
|
4132
|
+
const stacked = choices.some((c) => !!c.description);
|
|
4133
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
4134
|
+
showPrompt && /* @__PURE__ */ jsx(MessageResponse, { children: question.prompt }),
|
|
4135
|
+
pending && choices.length > 0 && /* @__PURE__ */ jsx(
|
|
4136
|
+
"div",
|
|
4137
|
+
{
|
|
4138
|
+
className: cn(
|
|
4139
|
+
stacked ? "flex flex-col gap-1.5" : "flex flex-wrap gap-2",
|
|
4140
|
+
"max-w-2xl"
|
|
4141
|
+
),
|
|
4142
|
+
role: "group",
|
|
4143
|
+
"aria-label": "Suggested answers",
|
|
4144
|
+
children: choices.map((choice, i) => /* @__PURE__ */ jsx(
|
|
4145
|
+
QuickReply,
|
|
4146
|
+
{
|
|
4147
|
+
choice,
|
|
4148
|
+
stacked,
|
|
4149
|
+
disabled: submitting,
|
|
4150
|
+
onSelect: () => void pick(choice.label)
|
|
4151
|
+
},
|
|
4152
|
+
i
|
|
4153
|
+
))
|
|
4154
|
+
}
|
|
4155
|
+
),
|
|
4156
|
+
error && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: error }),
|
|
4157
|
+
!answered && !pending && /* @__PURE__ */ jsx("p", { className: "text-xs italic text-muted-foreground/70", children: "No answer recorded." })
|
|
4158
|
+
] });
|
|
4159
|
+
}
|
|
4160
|
+
function QuickReply({
|
|
4161
|
+
choice,
|
|
4162
|
+
stacked,
|
|
4163
|
+
disabled,
|
|
4164
|
+
onSelect
|
|
4165
|
+
}) {
|
|
4166
|
+
return /* @__PURE__ */ jsxs(
|
|
4167
|
+
"button",
|
|
4168
|
+
{
|
|
4169
|
+
type: "button",
|
|
4170
|
+
disabled,
|
|
4171
|
+
onClick: onSelect,
|
|
4172
|
+
className: cn(
|
|
4173
|
+
"border border-border bg-background text-left text-foreground transition-colors",
|
|
4174
|
+
"hover:border-foreground/30 hover:bg-muted",
|
|
4175
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
4176
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
4177
|
+
stacked ? "rounded-md px-3 py-2" : "rounded-full px-3.5 py-1.5 text-sm"
|
|
4178
|
+
),
|
|
4179
|
+
children: [
|
|
4180
|
+
/* @__PURE__ */ jsx("span", { className: cn("block", stacked && "text-sm"), children: choice.label }),
|
|
4181
|
+
stacked && choice.description && /* @__PURE__ */ jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: choice.description })
|
|
4182
|
+
]
|
|
4183
|
+
}
|
|
4184
|
+
);
|
|
4185
|
+
}
|
|
4186
|
+
function BatchAsk({
|
|
4187
|
+
tc,
|
|
4188
|
+
questions,
|
|
4189
|
+
viewMode
|
|
4190
|
+
}) {
|
|
4048
4191
|
const { adapter, sessionId } = useAgentChatAdapterContext();
|
|
4049
|
-
const args = useMemo(() => parseArgs(tc.args), [tc.args]);
|
|
4050
|
-
const questions = args?.questions ?? [];
|
|
4051
4192
|
const [active, setActive] = useState(0);
|
|
4052
4193
|
const [selections, setSelections] = useState(
|
|
4053
4194
|
() => questions.map(emptySelection)
|
|
@@ -4141,11 +4282,8 @@ function AskUserQuestionToolBlock({ tc }) {
|
|
|
4141
4282
|
window.addEventListener("keydown", handler);
|
|
4142
4283
|
return () => window.removeEventListener("keydown", handler);
|
|
4143
4284
|
}, [handleCancel, handleSubmit, locked]);
|
|
4144
|
-
if (questions.length === 0) {
|
|
4145
|
-
return /* @__PURE__ */ jsx("div", { className: "rounded-md border border-destructive/50 bg-destructive/5 px-3 py-2 text-xs text-destructive", children: "ask_user_question: no questions provided" });
|
|
4146
|
-
}
|
|
4147
4285
|
if (locked) {
|
|
4148
|
-
return /* @__PURE__ */ jsx(
|
|
4286
|
+
return /* @__PURE__ */ jsx(BatchAskLocked, { tc, questions, viewMode });
|
|
4149
4287
|
}
|
|
4150
4288
|
const current = questions[active];
|
|
4151
4289
|
const currentSel = selections[active] ?? emptySelection();
|
|
@@ -4223,7 +4361,7 @@ function AskUserQuestionToolBlock({ tc }) {
|
|
|
4223
4361
|
] }),
|
|
4224
4362
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-border px-3 py-2", children: [
|
|
4225
4363
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 text-xs text-muted-foreground", children: [
|
|
4226
|
-
|
|
4364
|
+
/* @__PURE__ */ jsxs("span", { className: "mr-2 tabular-nums", children: [
|
|
4227
4365
|
answeredCount,
|
|
4228
4366
|
" of ",
|
|
4229
4367
|
questions.length,
|
|
@@ -4232,11 +4370,11 @@ function AskUserQuestionToolBlock({ tc }) {
|
|
|
4232
4370
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground/70", children: "Esc to cancel" }),
|
|
4233
4371
|
error && /* @__PURE__ */ jsx("span", { className: "ml-2 text-destructive", children: error })
|
|
4234
4372
|
] }),
|
|
4235
|
-
allAnswered
|
|
4373
|
+
allAnswered ? /* @__PURE__ */ jsx(
|
|
4236
4374
|
"button",
|
|
4237
4375
|
{
|
|
4238
4376
|
type: "button",
|
|
4239
|
-
disabled:
|
|
4377
|
+
disabled: submitting,
|
|
4240
4378
|
onClick: () => void handleSubmit(),
|
|
4241
4379
|
className: cn(
|
|
4242
4380
|
"shrink-0 rounded-md bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground transition-colors",
|
|
@@ -4346,9 +4484,10 @@ function Radio({ selected }) {
|
|
|
4346
4484
|
}
|
|
4347
4485
|
);
|
|
4348
4486
|
}
|
|
4349
|
-
function
|
|
4487
|
+
function BatchAskLocked({
|
|
4350
4488
|
tc,
|
|
4351
|
-
questions
|
|
4489
|
+
questions,
|
|
4490
|
+
viewMode
|
|
4352
4491
|
}) {
|
|
4353
4492
|
const answers = tc.askUserQuestionAnswers;
|
|
4354
4493
|
const byPrompt = new Map(
|
|
@@ -4373,7 +4512,7 @@ function AskUserQuestionLocked({
|
|
|
4373
4512
|
/* @__PURE__ */ jsx("span", { className: "text-emerald-500", children: "\u2192" }),
|
|
4374
4513
|
" ",
|
|
4375
4514
|
a.answer,
|
|
4376
|
-
a.is_other && /* @__PURE__ */ jsx("span", { className: "ml-1 text-[10px] text-muted-foreground/70", children: "(other)" })
|
|
4515
|
+
a.is_other && viewMode === "expert" && /* @__PURE__ */ jsx("span", { className: "ml-1 text-[10px] text-muted-foreground/70", children: "(other)" })
|
|
4377
4516
|
] }) : /* @__PURE__ */ jsx("div", { className: "ml-5 text-muted-foreground/60 italic", children: "\u2014 no answer \u2014" })
|
|
4378
4517
|
] }, i);
|
|
4379
4518
|
}) })
|
|
@@ -4772,6 +4911,8 @@ function CodeRunToolBlock({
|
|
|
4772
4911
|
function ToolCallBlock({
|
|
4773
4912
|
tc,
|
|
4774
4913
|
resolvedArtifactName,
|
|
4914
|
+
assistantContent,
|
|
4915
|
+
viewMode,
|
|
4775
4916
|
onFileSelect
|
|
4776
4917
|
}) {
|
|
4777
4918
|
if (tc.toolName.startsWith("mcp__")) {
|
|
@@ -4814,7 +4955,14 @@ function ToolCallBlock({
|
|
|
4814
4955
|
case "skill_manage":
|
|
4815
4956
|
return /* @__PURE__ */ jsx(SkillManageToolBlock, { tc });
|
|
4816
4957
|
case "ask_user_question":
|
|
4817
|
-
return /* @__PURE__ */ jsx(
|
|
4958
|
+
return /* @__PURE__ */ jsx(
|
|
4959
|
+
AskUserQuestionToolBlock,
|
|
4960
|
+
{
|
|
4961
|
+
tc,
|
|
4962
|
+
assistantContent,
|
|
4963
|
+
viewMode
|
|
4964
|
+
}
|
|
4965
|
+
);
|
|
4818
4966
|
case "create_artifact":
|
|
4819
4967
|
return /* @__PURE__ */ jsx(ArtifactToolBlock, { tc, resolvedName: resolvedArtifactName });
|
|
4820
4968
|
case "delegate_task":
|
|
@@ -5219,6 +5367,22 @@ function CommandEmpty({
|
|
|
5219
5367
|
}
|
|
5220
5368
|
);
|
|
5221
5369
|
}
|
|
5370
|
+
function CommandGroup({
|
|
5371
|
+
className,
|
|
5372
|
+
...props
|
|
5373
|
+
}) {
|
|
5374
|
+
return /* @__PURE__ */ jsx(
|
|
5375
|
+
Command$1.Group,
|
|
5376
|
+
{
|
|
5377
|
+
"data-slot": "command-group",
|
|
5378
|
+
className: cn(
|
|
5379
|
+
"overflow-hidden p-1.5 text-foreground **:[[cmdk-group-heading]]:mb-1 **:[[cmdk-group-heading]]:rounded-sm **:[[cmdk-group-heading]]:bg-orange-100 **:[[cmdk-group-heading]]:px-3 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-sm **:[[cmdk-group-heading]]:font-semibold **:[[cmdk-group-heading]]:tracking-wider **:[[cmdk-group-heading]]:text-foreground dark:**:[[cmdk-group-heading]]:bg-orange-400/40 dark:**:[[cmdk-group-heading]]:text-foreground/90",
|
|
5380
|
+
className
|
|
5381
|
+
),
|
|
5382
|
+
...props
|
|
5383
|
+
}
|
|
5384
|
+
);
|
|
5385
|
+
}
|
|
5222
5386
|
function CommandItem({
|
|
5223
5387
|
className,
|
|
5224
5388
|
children,
|
|
@@ -8114,13 +8278,20 @@ var PromptInputTextarea = ({
|
|
|
8114
8278
|
var PromptInputFooter = ({
|
|
8115
8279
|
className,
|
|
8116
8280
|
...props
|
|
8117
|
-
}) =>
|
|
8118
|
-
|
|
8119
|
-
|
|
8120
|
-
|
|
8121
|
-
|
|
8122
|
-
|
|
8123
|
-
|
|
8281
|
+
}) => (
|
|
8282
|
+
// flex-wrap: the footer holds a left tools group (add, browser profile,
|
|
8283
|
+
// workspace) and a right group (view mode, send). On a phone those stop
|
|
8284
|
+
// fitting one line as soon as a profile or workspace chip appears, and a
|
|
8285
|
+
// non-wrapping row crushes the icons instead. Wrapping only engages when
|
|
8286
|
+
// the space is genuinely gone, so wider screens are unchanged.
|
|
8287
|
+
/* @__PURE__ */ jsx(
|
|
8288
|
+
InputGroupAddon,
|
|
8289
|
+
{
|
|
8290
|
+
align: "block-end",
|
|
8291
|
+
className: cn("flex-wrap justify-between gap-1", className),
|
|
8292
|
+
...props
|
|
8293
|
+
}
|
|
8294
|
+
)
|
|
8124
8295
|
);
|
|
8125
8296
|
var PromptInputTools = ({
|
|
8126
8297
|
className,
|
|
@@ -8233,41 +8404,6 @@ function splitComposerFiles(files) {
|
|
|
8233
8404
|
}
|
|
8234
8405
|
return { images, pending };
|
|
8235
8406
|
}
|
|
8236
|
-
function Popover({
|
|
8237
|
-
...props
|
|
8238
|
-
}) {
|
|
8239
|
-
return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
|
|
8240
|
-
}
|
|
8241
|
-
function PopoverTrigger({
|
|
8242
|
-
...props
|
|
8243
|
-
}) {
|
|
8244
|
-
return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
8245
|
-
}
|
|
8246
|
-
function PopoverContent({
|
|
8247
|
-
className,
|
|
8248
|
-
align = "center",
|
|
8249
|
-
sideOffset = 4,
|
|
8250
|
-
...props
|
|
8251
|
-
}) {
|
|
8252
|
-
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
8253
|
-
Popover$1.Content,
|
|
8254
|
-
{
|
|
8255
|
-
"data-slot": "popover-content",
|
|
8256
|
-
align,
|
|
8257
|
-
sideOffset,
|
|
8258
|
-
className: cn(
|
|
8259
|
-
"z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-4 rounded-none bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-150 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
8260
|
-
className
|
|
8261
|
-
),
|
|
8262
|
-
...props
|
|
8263
|
-
}
|
|
8264
|
-
) });
|
|
8265
|
-
}
|
|
8266
|
-
function PopoverAnchor({
|
|
8267
|
-
...props
|
|
8268
|
-
}) {
|
|
8269
|
-
return /* @__PURE__ */ jsx(Popover$1.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
8270
|
-
}
|
|
8271
8407
|
function Progress({
|
|
8272
8408
|
className,
|
|
8273
8409
|
value,
|
|
@@ -8302,24 +8438,26 @@ var ContextContext = createContext(null);
|
|
|
8302
8438
|
var useContextValue = () => {
|
|
8303
8439
|
const context = useContext(ContextContext);
|
|
8304
8440
|
if (!context) {
|
|
8305
|
-
throw new Error(
|
|
8441
|
+
throw new Error(
|
|
8442
|
+
"Context components must be used within ContextValueProvider"
|
|
8443
|
+
);
|
|
8306
8444
|
}
|
|
8307
8445
|
return context;
|
|
8308
8446
|
};
|
|
8309
|
-
var
|
|
8447
|
+
var ContextValueProvider = ({
|
|
8310
8448
|
usedTokens,
|
|
8311
8449
|
maxTokens,
|
|
8312
8450
|
usage,
|
|
8313
8451
|
modelId,
|
|
8314
|
-
|
|
8452
|
+
children
|
|
8315
8453
|
}) => {
|
|
8316
8454
|
const contextValue = useMemo(
|
|
8317
8455
|
() => ({ maxTokens, modelId, usage, usedTokens }),
|
|
8318
8456
|
[maxTokens, modelId, usage, usedTokens]
|
|
8319
8457
|
);
|
|
8320
|
-
return /* @__PURE__ */ jsx(ContextContext.Provider, { value: contextValue, children
|
|
8458
|
+
return /* @__PURE__ */ jsx(ContextContext.Provider, { value: contextValue, children });
|
|
8321
8459
|
};
|
|
8322
|
-
var ContextIcon = () => {
|
|
8460
|
+
var ContextIcon = ({ className }) => {
|
|
8323
8461
|
const { usedTokens, maxTokens } = useContextValue();
|
|
8324
8462
|
const circumference = 2 * Math.PI * ICON_RADIUS;
|
|
8325
8463
|
const usedPercent = usedTokens / maxTokens;
|
|
@@ -8328,6 +8466,7 @@ var ContextIcon = () => {
|
|
|
8328
8466
|
"svg",
|
|
8329
8467
|
{
|
|
8330
8468
|
"aria-label": "Model context usage",
|
|
8469
|
+
className,
|
|
8331
8470
|
height: "20",
|
|
8332
8471
|
role: "img",
|
|
8333
8472
|
style: { color: "currentcolor" },
|
|
@@ -8366,19 +8505,6 @@ var ContextIcon = () => {
|
|
|
8366
8505
|
}
|
|
8367
8506
|
);
|
|
8368
8507
|
};
|
|
8369
|
-
var ContextTrigger = ({ children, ...props }) => {
|
|
8370
|
-
return /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsx(ContextIcon, {}) }) });
|
|
8371
|
-
};
|
|
8372
|
-
var ContextContent = ({
|
|
8373
|
-
className,
|
|
8374
|
-
...props
|
|
8375
|
-
}) => /* @__PURE__ */ jsx(
|
|
8376
|
-
PopoverContent,
|
|
8377
|
-
{
|
|
8378
|
-
className: cn("min-w-60 divide-y overflow-hidden p-0", className),
|
|
8379
|
-
...props
|
|
8380
|
-
}
|
|
8381
|
-
);
|
|
8382
8508
|
var ContextContentHeader = ({
|
|
8383
8509
|
children,
|
|
8384
8510
|
className,
|
|
@@ -8676,6 +8802,114 @@ function ItemActions({ className, ...props }) {
|
|
|
8676
8802
|
}
|
|
8677
8803
|
);
|
|
8678
8804
|
}
|
|
8805
|
+
function Popover({
|
|
8806
|
+
...props
|
|
8807
|
+
}) {
|
|
8808
|
+
return /* @__PURE__ */ jsx(Popover$1.Root, { "data-slot": "popover", ...props });
|
|
8809
|
+
}
|
|
8810
|
+
function PopoverTrigger({
|
|
8811
|
+
...props
|
|
8812
|
+
}) {
|
|
8813
|
+
return /* @__PURE__ */ jsx(Popover$1.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
8814
|
+
}
|
|
8815
|
+
function PopoverContent({
|
|
8816
|
+
className,
|
|
8817
|
+
align = "center",
|
|
8818
|
+
sideOffset = 4,
|
|
8819
|
+
...props
|
|
8820
|
+
}) {
|
|
8821
|
+
return /* @__PURE__ */ jsx(Popover$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
8822
|
+
Popover$1.Content,
|
|
8823
|
+
{
|
|
8824
|
+
"data-slot": "popover-content",
|
|
8825
|
+
align,
|
|
8826
|
+
sideOffset,
|
|
8827
|
+
className: cn(
|
|
8828
|
+
"z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-4 rounded-none bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-150 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
|
8829
|
+
className
|
|
8830
|
+
),
|
|
8831
|
+
...props
|
|
8832
|
+
}
|
|
8833
|
+
) });
|
|
8834
|
+
}
|
|
8835
|
+
function PopoverAnchor({
|
|
8836
|
+
...props
|
|
8837
|
+
}) {
|
|
8838
|
+
return /* @__PURE__ */ jsx(Popover$1.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
8839
|
+
}
|
|
8840
|
+
var MOBILE_QUERY = "(max-width: 767px)";
|
|
8841
|
+
function subscribe(onChange) {
|
|
8842
|
+
const mql = window.matchMedia(MOBILE_QUERY);
|
|
8843
|
+
mql.addEventListener("change", onChange);
|
|
8844
|
+
return () => mql.removeEventListener("change", onChange);
|
|
8845
|
+
}
|
|
8846
|
+
function useIsMobile() {
|
|
8847
|
+
return useSyncExternalStore(
|
|
8848
|
+
subscribe,
|
|
8849
|
+
() => window.matchMedia(MOBILE_QUERY).matches,
|
|
8850
|
+
// No viewport when server-rendered; assume desktop, as the SDK does.
|
|
8851
|
+
() => false
|
|
8852
|
+
);
|
|
8853
|
+
}
|
|
8854
|
+
function ResponsivePanel({
|
|
8855
|
+
open,
|
|
8856
|
+
onOpenChange,
|
|
8857
|
+
trigger,
|
|
8858
|
+
title,
|
|
8859
|
+
children,
|
|
8860
|
+
align = "start",
|
|
8861
|
+
popoverClassName
|
|
8862
|
+
}) {
|
|
8863
|
+
const isMobile = useIsMobile();
|
|
8864
|
+
if (!isMobile) {
|
|
8865
|
+
return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange, children: [
|
|
8866
|
+
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: trigger }),
|
|
8867
|
+
/* @__PURE__ */ jsx(
|
|
8868
|
+
PopoverContent,
|
|
8869
|
+
{
|
|
8870
|
+
side: "top",
|
|
8871
|
+
align,
|
|
8872
|
+
className: cn("w-64 overflow-hidden rounded-xl p-1", popoverClassName),
|
|
8873
|
+
children
|
|
8874
|
+
}
|
|
8875
|
+
)
|
|
8876
|
+
] });
|
|
8877
|
+
}
|
|
8878
|
+
return /* @__PURE__ */ jsxs(Dialog$1.Root, { open, onOpenChange, children: [
|
|
8879
|
+
/* @__PURE__ */ jsx(Dialog$1.Trigger, { asChild: true, children: trigger }),
|
|
8880
|
+
/* @__PURE__ */ jsxs(Dialog$1.Portal, { children: [
|
|
8881
|
+
/* @__PURE__ */ jsx(
|
|
8882
|
+
Dialog$1.Overlay,
|
|
8883
|
+
{
|
|
8884
|
+
"data-slot": "responsive-panel-overlay",
|
|
8885
|
+
className: "fixed inset-0 z-50 bg-black/40 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0"
|
|
8886
|
+
}
|
|
8887
|
+
),
|
|
8888
|
+
/* @__PURE__ */ jsxs(
|
|
8889
|
+
Dialog$1.Content,
|
|
8890
|
+
{
|
|
8891
|
+
"data-slot": "responsive-panel-sheet",
|
|
8892
|
+
"aria-describedby": void 0,
|
|
8893
|
+
className: cn(
|
|
8894
|
+
"fixed inset-x-0 bottom-0 z-50 flex max-h-[80svh] flex-col overflow-hidden rounded-t-2xl border-t border-border bg-popover pb-[env(safe-area-inset-bottom)] text-popover-foreground shadow-lg",
|
|
8895
|
+
"data-[state=closed]:animate-out data-[state=closed]:slide-out-to-bottom data-[state=open]:animate-in data-[state=open]:slide-in-from-bottom"
|
|
8896
|
+
),
|
|
8897
|
+
children: [
|
|
8898
|
+
/* @__PURE__ */ jsx(Dialog$1.Title, { className: "sr-only", children: title }),
|
|
8899
|
+
/* @__PURE__ */ jsx(
|
|
8900
|
+
"div",
|
|
8901
|
+
{
|
|
8902
|
+
"aria-hidden": "true",
|
|
8903
|
+
className: "mx-auto mt-2.5 mb-1 h-1 w-9 shrink-0 rounded-full bg-muted-foreground/30"
|
|
8904
|
+
}
|
|
8905
|
+
),
|
|
8906
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain p-1", children })
|
|
8907
|
+
]
|
|
8908
|
+
}
|
|
8909
|
+
)
|
|
8910
|
+
] })
|
|
8911
|
+
] });
|
|
8912
|
+
}
|
|
8679
8913
|
function Skeleton({ className, ...props }) {
|
|
8680
8914
|
return /* @__PURE__ */ jsx(
|
|
8681
8915
|
"div",
|
|
@@ -8847,6 +9081,7 @@ function SkillsEmptyState() {
|
|
|
8847
9081
|
] })
|
|
8848
9082
|
] });
|
|
8849
9083
|
}
|
|
9084
|
+
var TOOLS_ITEM_CLASS = "gap-3 rounded-md px-3 py-2 pointer-coarse:min-h-11 pointer-coarse:text-[15px]";
|
|
8850
9085
|
function ChatComposer(props) {
|
|
8851
9086
|
return /* @__PURE__ */ jsx(PromptInputProvider, { children: /* @__PURE__ */ jsx(ChatComposerInner, { ...props }) });
|
|
8852
9087
|
}
|
|
@@ -8944,6 +9179,8 @@ function ChatComposerInner({
|
|
|
8944
9179
|
isRunning,
|
|
8945
9180
|
disabled = false,
|
|
8946
9181
|
disabledReason,
|
|
9182
|
+
awaitingAnswer = false,
|
|
9183
|
+
placeholder,
|
|
8947
9184
|
tokenUsage,
|
|
8948
9185
|
onComposerError,
|
|
8949
9186
|
showBrowser = false,
|
|
@@ -8967,7 +9204,6 @@ function ChatComposerInner({
|
|
|
8967
9204
|
onViewModeChange
|
|
8968
9205
|
}) {
|
|
8969
9206
|
const { adapter } = useAgentChatAdapterContext();
|
|
8970
|
-
const [profileMenuOpen, setProfileMenuOpen] = useState(false);
|
|
8971
9207
|
const [browserProfiles, setBrowserProfiles] = useState(null);
|
|
8972
9208
|
const loadBrowserProfiles = useCallback(() => {
|
|
8973
9209
|
if (!adapter.listBrowserProfiles) return;
|
|
@@ -8980,16 +9216,15 @@ function ChatComposerInner({
|
|
|
8980
9216
|
loadBrowserProfiles();
|
|
8981
9217
|
}, [browserProfilesEnabled, browserProfileId, browserProfiles, loadBrowserProfiles]);
|
|
8982
9218
|
const activeBrowserProfile = (browserProfiles ?? []).find((p) => p.id === browserProfileId) ?? null;
|
|
8983
|
-
const profileTriggerLabel = activeBrowserProfile && /* @__PURE__ */ jsx("span", { className: "max-w-24 truncate text-xs", children: activeBrowserProfile.name });
|
|
8984
9219
|
const { textInput, attachments } = usePromptInputController();
|
|
8985
|
-
const status = isRunning ? "streaming" : disabled ? "error" : "ready";
|
|
9220
|
+
const status = isRunning && !awaitingAnswer ? "streaming" : disabled ? "error" : "ready";
|
|
8986
9221
|
const [adapterCommands, setAdapterCommands] = useState([]);
|
|
8987
9222
|
const [skillsLoading, setSkillsLoading] = useState(false);
|
|
8988
9223
|
const [buttonMenuOpen, setButtonMenuOpen] = useState(false);
|
|
8989
9224
|
const [menuMode, setMenuMode] = useState("all");
|
|
8990
9225
|
const [addMenuOpen, setAddMenuOpen] = useState(false);
|
|
8991
9226
|
const [menuDismissed, setMenuDismissed] = useState(false);
|
|
8992
|
-
const showSlashMenu = !menuDismissed && (textInput.value.startsWith("/") || buttonMenuOpen);
|
|
9227
|
+
const showSlashMenu = !menuDismissed && !awaitingAnswer && (textInput.value.startsWith("/") || buttonMenuOpen);
|
|
8993
9228
|
useEffect(() => {
|
|
8994
9229
|
if (menuDismissed && !textInput.value.startsWith("/") && !buttonMenuOpen) {
|
|
8995
9230
|
setMenuDismissed(false);
|
|
@@ -9185,16 +9420,13 @@ function ChatComposerInner({
|
|
|
9185
9420
|
});
|
|
9186
9421
|
return;
|
|
9187
9422
|
}
|
|
9188
|
-
if (isRunning) {
|
|
9189
|
-
await onStop();
|
|
9190
|
-
}
|
|
9191
9423
|
await onSend(
|
|
9192
9424
|
text,
|
|
9193
9425
|
images.length > 0 ? images : void 0,
|
|
9194
9426
|
pending.length > 0 ? pending : void 0
|
|
9195
9427
|
);
|
|
9196
9428
|
},
|
|
9197
|
-
[onSend,
|
|
9429
|
+
[onSend, onComposerError, disabled]
|
|
9198
9430
|
);
|
|
9199
9431
|
const handlePromptInputError = useCallback(
|
|
9200
9432
|
(err) => {
|
|
@@ -9202,6 +9434,59 @@ function ChatComposerInner({
|
|
|
9202
9434
|
},
|
|
9203
9435
|
[onComposerError]
|
|
9204
9436
|
);
|
|
9437
|
+
const [contextOpen, setContextOpen] = useState(false);
|
|
9438
|
+
const contextPercentLabel = tokenUsage?.contextWindow ? `${Math.min(100, Math.round(tokenUsage.totalTokens / tokenUsage.contextWindow * 100))}%` : "0%";
|
|
9439
|
+
const canAttach = !disabled && !awaitingAnswer;
|
|
9440
|
+
const showPaneToggles = Boolean(
|
|
9441
|
+
canShowBrowser && onToggleBrowser || canShowWorkspace && onToggleWorkspace
|
|
9442
|
+
);
|
|
9443
|
+
const canPickProfile = Boolean(
|
|
9444
|
+
browserProfilesEnabled && onSelectBrowserProfile && adapter.listBrowserProfiles
|
|
9445
|
+
);
|
|
9446
|
+
const showToolsPanel = canAttach || showPaneToggles || canPickProfile;
|
|
9447
|
+
const viewModeToggle = onViewModeChange ? /* @__PURE__ */ jsxs(
|
|
9448
|
+
ButtonGroup,
|
|
9449
|
+
{
|
|
9450
|
+
"aria-label": "Chat view mode",
|
|
9451
|
+
className: "overflow-hidden rounded-full border border-border bg-muted/40 p-0.5",
|
|
9452
|
+
children: [
|
|
9453
|
+
/* @__PURE__ */ jsx(
|
|
9454
|
+
PromptInputButton,
|
|
9455
|
+
{
|
|
9456
|
+
"aria-label": "Simple view",
|
|
9457
|
+
"aria-pressed": viewMode === "simple",
|
|
9458
|
+
tooltip: "Simple \u2014 just the conversation",
|
|
9459
|
+
variant: viewMode === "simple" ? "secondary" : "ghost",
|
|
9460
|
+
onClick: () => onViewModeChange("simple"),
|
|
9461
|
+
className: cn(
|
|
9462
|
+
// 32px is a comfortable segment for a cursor and a miss for a
|
|
9463
|
+
// thumb, so touch takes both halves to the 44px floor.
|
|
9464
|
+
"size-8 rounded-full pointer-coarse:size-11",
|
|
9465
|
+
viewMode === "simple" && "shadow-sm"
|
|
9466
|
+
),
|
|
9467
|
+
children: /* @__PURE__ */ jsx(MessageSquareIcon, { className: "size-[18px] pointer-coarse:size-5" })
|
|
9468
|
+
}
|
|
9469
|
+
),
|
|
9470
|
+
/* @__PURE__ */ jsx(
|
|
9471
|
+
PromptInputButton,
|
|
9472
|
+
{
|
|
9473
|
+
"aria-label": "Advanced view",
|
|
9474
|
+
"aria-pressed": viewMode === "expert",
|
|
9475
|
+
tooltip: "Advanced \u2014 every step the agent took",
|
|
9476
|
+
variant: viewMode === "expert" ? "secondary" : "ghost",
|
|
9477
|
+
onClick: () => onViewModeChange("expert"),
|
|
9478
|
+
className: cn(
|
|
9479
|
+
// 32px is a comfortable segment for a cursor and a miss for a
|
|
9480
|
+
// thumb, so touch takes both halves to the 44px floor.
|
|
9481
|
+
"size-8 rounded-full pointer-coarse:size-11",
|
|
9482
|
+
viewMode === "expert" && "shadow-sm"
|
|
9483
|
+
),
|
|
9484
|
+
children: /* @__PURE__ */ jsx(ListTreeIcon, { className: "size-[18px] pointer-coarse:size-5" })
|
|
9485
|
+
}
|
|
9486
|
+
)
|
|
9487
|
+
]
|
|
9488
|
+
}
|
|
9489
|
+
) : null;
|
|
9205
9490
|
return /* @__PURE__ */ jsxs(
|
|
9206
9491
|
Popover,
|
|
9207
9492
|
{
|
|
@@ -9254,154 +9539,132 @@ function ChatComposerInner({
|
|
|
9254
9539
|
PromptInputTextarea,
|
|
9255
9540
|
{
|
|
9256
9541
|
ref: textareaRef,
|
|
9257
|
-
placeholder: disabled ? disabledReason ?? "Session disabled" : "Send a message...",
|
|
9542
|
+
placeholder: disabled ? disabledReason ?? "Session disabled" : placeholder ?? "Send a message...",
|
|
9258
9543
|
disabled
|
|
9259
9544
|
}
|
|
9260
9545
|
) }),
|
|
9261
9546
|
/* @__PURE__ */ jsxs(PromptInputFooter, { children: [
|
|
9262
9547
|
/* @__PURE__ */ jsxs(PromptInputTools, { children: [
|
|
9263
|
-
|
|
9264
|
-
|
|
9265
|
-
|
|
9266
|
-
|
|
9267
|
-
{
|
|
9268
|
-
|
|
9269
|
-
|
|
9270
|
-
|
|
9271
|
-
|
|
9548
|
+
showToolsPanel && /* @__PURE__ */ jsx(
|
|
9549
|
+
ResponsivePanel,
|
|
9550
|
+
{
|
|
9551
|
+
open: addMenuOpen,
|
|
9552
|
+
onOpenChange: (open) => {
|
|
9553
|
+
setAddMenuOpen(open);
|
|
9554
|
+
if (open && canPickProfile && !browserProfileLocked && browserProfiles === null) {
|
|
9555
|
+
loadBrowserProfiles();
|
|
9556
|
+
}
|
|
9557
|
+
},
|
|
9558
|
+
title: "Composer tools",
|
|
9559
|
+
trigger: /* @__PURE__ */ jsx(
|
|
9560
|
+
PromptInputButton,
|
|
9561
|
+
{
|
|
9562
|
+
"aria-label": "Composer tools",
|
|
9563
|
+
tooltip: "Attach files, panes and browser profile",
|
|
9564
|
+
children: /* @__PURE__ */ jsx(PlusIcon, { className: "size-4" })
|
|
9565
|
+
}
|
|
9566
|
+
),
|
|
9567
|
+
children: /* @__PURE__ */ jsx(Command, { children: /* @__PURE__ */ jsxs(CommandList, { className: "max-md:max-h-none", children: [
|
|
9568
|
+
canAttach && /* @__PURE__ */ jsxs(CommandGroup, { heading: "Attach", children: [
|
|
9272
9569
|
/* @__PURE__ */ jsxs(
|
|
9273
9570
|
CommandItem,
|
|
9274
9571
|
{
|
|
9275
|
-
|
|
9276
|
-
|
|
9572
|
+
onSelect: () => {
|
|
9573
|
+
setAddMenuOpen(false);
|
|
9574
|
+
attachments.openFileDialog();
|
|
9575
|
+
},
|
|
9576
|
+
className: TOOLS_ITEM_CLASS,
|
|
9277
9577
|
children: [
|
|
9278
|
-
/* @__PURE__ */ jsx(
|
|
9279
|
-
"Add
|
|
9280
|
-
/* @__PURE__ */ jsx(ChevronRightIcon, { className: "ml-auto size-4 shrink-0 text-muted-foreground" })
|
|
9578
|
+
/* @__PURE__ */ jsx(PaperclipIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9579
|
+
"Add local files"
|
|
9281
9580
|
]
|
|
9282
9581
|
}
|
|
9283
9582
|
),
|
|
9284
|
-
/* @__PURE__ */ jsxs(
|
|
9583
|
+
/* @__PURE__ */ jsxs(CommandItem, { disabled: true, className: TOOLS_ITEM_CLASS, children: [
|
|
9584
|
+
/* @__PURE__ */ jsx(CloudIcon, { className: "size-4 shrink-0 text-sky-500" }),
|
|
9585
|
+
"Add from OneDrive"
|
|
9586
|
+
] }),
|
|
9587
|
+
/* @__PURE__ */ jsxs(CommandItem, { disabled: true, className: TOOLS_ITEM_CLASS, children: [
|
|
9588
|
+
/* @__PURE__ */ jsx(HardDriveIcon, { className: "size-4 shrink-0 text-emerald-500" }),
|
|
9589
|
+
"Add from Google Drive"
|
|
9590
|
+
] })
|
|
9591
|
+
] }),
|
|
9592
|
+
showPaneToggles && // Panes stay available while a question is pending —
|
|
9593
|
+
// only attachments are suppressed there.
|
|
9594
|
+
/* @__PURE__ */ jsxs(CommandGroup, { heading: "Panes", children: [
|
|
9595
|
+
canShowBrowser && onToggleBrowser && /* @__PURE__ */ jsxs(
|
|
9285
9596
|
CommandItem,
|
|
9286
9597
|
{
|
|
9287
|
-
|
|
9288
|
-
|
|
9598
|
+
onSelect: onToggleBrowser,
|
|
9599
|
+
"data-checked": showBrowser || void 0,
|
|
9600
|
+
className: TOOLS_ITEM_CLASS,
|
|
9289
9601
|
children: [
|
|
9290
|
-
/* @__PURE__ */ jsx(
|
|
9291
|
-
"
|
|
9602
|
+
/* @__PURE__ */ jsx(GlobeIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9603
|
+
"Browser"
|
|
9292
9604
|
]
|
|
9293
9605
|
}
|
|
9294
9606
|
),
|
|
9295
|
-
/* @__PURE__ */ jsxs(
|
|
9607
|
+
canShowWorkspace && onToggleWorkspace && /* @__PURE__ */ jsxs(
|
|
9296
9608
|
CommandItem,
|
|
9297
9609
|
{
|
|
9298
|
-
onSelect:
|
|
9299
|
-
|
|
9300
|
-
|
|
9301
|
-
},
|
|
9302
|
-
className: "gap-3 rounded-md px-3 py-2 [&>svg:last-child]:hidden",
|
|
9610
|
+
onSelect: onToggleWorkspace,
|
|
9611
|
+
"data-checked": showWorkspace || void 0,
|
|
9612
|
+
className: TOOLS_ITEM_CLASS,
|
|
9303
9613
|
children: [
|
|
9304
|
-
/* @__PURE__ */ jsx(
|
|
9305
|
-
"
|
|
9614
|
+
/* @__PURE__ */ jsx(FolderIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9615
|
+
"Workspace"
|
|
9306
9616
|
]
|
|
9307
9617
|
}
|
|
9308
9618
|
)
|
|
9309
|
-
] })
|
|
9310
|
-
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9314
|
-
|
|
9315
|
-
|
|
9316
|
-
|
|
9317
|
-
"aria-pressed": showBrowser,
|
|
9318
|
-
onClick: onToggleBrowser,
|
|
9319
|
-
tooltip: showBrowser ? "Hide browser pane" : "Show browser pane",
|
|
9320
|
-
className: showBrowser ? "bg-accent text-foreground" : void 0,
|
|
9321
|
-
children: /* @__PURE__ */ jsx(GlobeIcon, { className: "size-4" })
|
|
9322
|
-
}
|
|
9323
|
-
),
|
|
9324
|
-
browserProfilesEnabled && onSelectBrowserProfile && adapter.listBrowserProfiles && (browserProfileLocked ? (
|
|
9325
|
-
// A profile is bound at session creation; once a session is
|
|
9326
|
-
// active the choice can't change. Render disabled (via
|
|
9327
|
-
// aria-disabled, not the `disabled` attribute, so the
|
|
9328
|
-
// tooltip still shows on hover) with an explanation.
|
|
9329
|
-
/* @__PURE__ */ jsxs(
|
|
9330
|
-
PromptInputButton,
|
|
9331
|
-
{
|
|
9332
|
-
"aria-label": "Select browser profile",
|
|
9333
|
-
"aria-disabled": true,
|
|
9334
|
-
"aria-pressed": !!browserProfileId,
|
|
9335
|
-
tooltip: activeBrowserProfile ? `Browser profile: ${activeBrowserProfile.name} (locked for this session)` : "A browser profile must be chosen before starting the session",
|
|
9336
|
-
className: `cursor-not-allowed opacity-50 ${browserProfileId ? "bg-accent text-foreground" : ""}`,
|
|
9337
|
-
children: [
|
|
9338
|
-
/* @__PURE__ */ jsx(IdCardIcon, { className: "size-4" }),
|
|
9339
|
-
profileTriggerLabel
|
|
9340
|
-
]
|
|
9341
|
-
}
|
|
9342
|
-
)
|
|
9343
|
-
) : /* @__PURE__ */ jsxs(
|
|
9344
|
-
Popover,
|
|
9345
|
-
{
|
|
9346
|
-
open: profileMenuOpen,
|
|
9347
|
-
onOpenChange: (open) => {
|
|
9348
|
-
setProfileMenuOpen(open);
|
|
9349
|
-
if (open) loadBrowserProfiles();
|
|
9350
|
-
},
|
|
9351
|
-
children: [
|
|
9352
|
-
/* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
|
|
9353
|
-
PromptInputButton,
|
|
9354
|
-
{
|
|
9355
|
-
"aria-label": "Select browser profile",
|
|
9356
|
-
"aria-pressed": !!browserProfileId,
|
|
9357
|
-
tooltip: activeBrowserProfile ? `Browser profile: ${activeBrowserProfile.name}` : "Select browser profile",
|
|
9358
|
-
className: browserProfileId ? "bg-accent text-foreground" : void 0,
|
|
9359
|
-
children: [
|
|
9360
|
-
/* @__PURE__ */ jsx(IdCardIcon, { className: "size-4" }),
|
|
9361
|
-
profileTriggerLabel
|
|
9362
|
-
]
|
|
9363
|
-
}
|
|
9364
|
-
) }),
|
|
9365
|
-
/* @__PURE__ */ jsx(
|
|
9366
|
-
PopoverContent,
|
|
9367
|
-
{
|
|
9368
|
-
side: "top",
|
|
9369
|
-
align: "start",
|
|
9370
|
-
className: "w-64 overflow-hidden rounded-xl p-1",
|
|
9371
|
-
children: /* @__PURE__ */ jsx(Command, { children: /* @__PURE__ */ jsx(CommandList, { children: [
|
|
9372
|
-
{ id: null, name: "No profile" },
|
|
9373
|
-
...browserProfiles ?? []
|
|
9374
|
-
].map((p) => /* @__PURE__ */ jsx(
|
|
9619
|
+
] }),
|
|
9620
|
+
canPickProfile && /* @__PURE__ */ jsx(CommandGroup, { heading: "Browser profile", children: browserProfileLocked ? (
|
|
9621
|
+
// A profile is bound at session creation and cannot
|
|
9622
|
+
// change mid-session. In a menu there is nothing to
|
|
9623
|
+
// hover, so the reason is stated outright rather
|
|
9624
|
+
// than hidden in a tooltip as it was on the button.
|
|
9625
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9626
|
+
/* @__PURE__ */ jsxs(
|
|
9375
9627
|
CommandItem,
|
|
9376
9628
|
{
|
|
9377
|
-
|
|
9378
|
-
|
|
9379
|
-
|
|
9380
|
-
|
|
9381
|
-
|
|
9382
|
-
|
|
9383
|
-
|
|
9629
|
+
disabled: true,
|
|
9630
|
+
"data-checked": !!browserProfileId || void 0,
|
|
9631
|
+
className: TOOLS_ITEM_CLASS,
|
|
9632
|
+
children: [
|
|
9633
|
+
/* @__PURE__ */ jsx(IdCardIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9634
|
+
activeBrowserProfile?.name ?? "No profile"
|
|
9635
|
+
]
|
|
9636
|
+
}
|
|
9637
|
+
),
|
|
9638
|
+
/* @__PURE__ */ jsx("p", { className: "px-3 pt-0.5 pb-1.5 text-xs text-muted-foreground", children: activeBrowserProfile ? "Locked for this session." : "A profile can only be chosen before the session starts." })
|
|
9639
|
+
] })
|
|
9640
|
+
) : (
|
|
9641
|
+
// data-checked lights CommandItem's built-in
|
|
9642
|
+
// trailing check on the selected entry.
|
|
9643
|
+
[
|
|
9644
|
+
{ id: null, name: "No profile" },
|
|
9645
|
+
...browserProfiles ?? []
|
|
9646
|
+
].map((p) => /* @__PURE__ */ jsxs(
|
|
9647
|
+
CommandItem,
|
|
9648
|
+
{
|
|
9649
|
+
onSelect: () => {
|
|
9650
|
+
setAddMenuOpen(false);
|
|
9651
|
+
onSelectBrowserProfile?.(p.id);
|
|
9384
9652
|
},
|
|
9385
|
-
p.id
|
|
9386
|
-
|
|
9387
|
-
|
|
9388
|
-
|
|
9389
|
-
|
|
9390
|
-
|
|
9391
|
-
|
|
9392
|
-
|
|
9393
|
-
|
|
9394
|
-
|
|
9395
|
-
|
|
9396
|
-
"aria-pressed": showWorkspace,
|
|
9397
|
-
onClick: onToggleWorkspace,
|
|
9398
|
-
tooltip: showWorkspace ? "Hide workspace pane" : "Show workspace pane",
|
|
9399
|
-
className: showWorkspace ? "bg-accent text-foreground" : void 0,
|
|
9400
|
-
children: /* @__PURE__ */ jsx(FolderIcon, { className: "size-4" })
|
|
9653
|
+
"data-checked": (browserProfileId ?? null) === p.id || void 0,
|
|
9654
|
+
className: TOOLS_ITEM_CLASS,
|
|
9655
|
+
children: [
|
|
9656
|
+
/* @__PURE__ */ jsx(IdCardIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9657
|
+
p.name
|
|
9658
|
+
]
|
|
9659
|
+
},
|
|
9660
|
+
p.id ?? "none"
|
|
9661
|
+
))
|
|
9662
|
+
) })
|
|
9663
|
+
] }) })
|
|
9401
9664
|
}
|
|
9402
9665
|
),
|
|
9403
|
-
!disabled && tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */
|
|
9404
|
-
|
|
9666
|
+
!disabled && tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsx(
|
|
9667
|
+
ContextValueProvider,
|
|
9405
9668
|
{
|
|
9406
9669
|
usedTokens: tokenUsage.totalTokens,
|
|
9407
9670
|
maxTokens: tokenUsage.contextWindow,
|
|
@@ -9415,82 +9678,68 @@ function ChatComposerInner({
|
|
|
9415
9678
|
inputTokenDetails: void 0,
|
|
9416
9679
|
outputTokenDetails: void 0
|
|
9417
9680
|
},
|
|
9418
|
-
children:
|
|
9419
|
-
|
|
9420
|
-
|
|
9421
|
-
|
|
9422
|
-
|
|
9423
|
-
|
|
9424
|
-
|
|
9425
|
-
|
|
9426
|
-
|
|
9427
|
-
|
|
9428
|
-
/* @__PURE__ */ jsx(ContextContentBody, { children: tokenUsage.totalTokens > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9429
|
-
/* @__PURE__ */ jsx(ContextInputUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9430
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Input" }),
|
|
9431
|
-
/* @__PURE__ */ jsx("span", { children: tokenUsage.inputTokens.toLocaleString() })
|
|
9432
|
-
] }) }),
|
|
9433
|
-
/* @__PURE__ */ jsx(ContextOutputUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9434
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Output" }),
|
|
9435
|
-
/* @__PURE__ */ jsx("span", { children: tokenUsage.outputTokens.toLocaleString() })
|
|
9436
|
-
] }) }),
|
|
9437
|
-
tokenUsage.reasoningTokens > 0 && /* @__PURE__ */ jsx(ContextReasoningUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9438
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
|
|
9439
|
-
/* @__PURE__ */ jsx("span", { children: tokenUsage.reasoningTokens.toLocaleString() })
|
|
9440
|
-
] }) }),
|
|
9441
|
-
tokenUsage.cachedInputTokens > 0 && /* @__PURE__ */ jsx(ContextCacheUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9442
|
-
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Cache" }),
|
|
9443
|
-
/* @__PURE__ */ jsx("span", { children: tokenUsage.cachedInputTokens.toLocaleString() })
|
|
9444
|
-
] }) })
|
|
9445
|
-
] }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center py-1", children: "Empty" }) }),
|
|
9446
|
-
tokenUsage.totalTokens > 0 && /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-end gap-3 bg-secondary p-2", children: /* @__PURE__ */ jsx(
|
|
9447
|
-
Button,
|
|
9681
|
+
children: /* @__PURE__ */ jsxs(
|
|
9682
|
+
ResponsivePanel,
|
|
9683
|
+
{
|
|
9684
|
+
open: contextOpen,
|
|
9685
|
+
onOpenChange: setContextOpen,
|
|
9686
|
+
title: "Session context",
|
|
9687
|
+
align: "end",
|
|
9688
|
+
popoverClassName: "min-w-60 divide-y p-0",
|
|
9689
|
+
trigger: /* @__PURE__ */ jsxs(
|
|
9690
|
+
PromptInputButton,
|
|
9448
9691
|
{
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9692
|
+
"aria-label": `Session context: ${contextPercentLabel} of the window used`,
|
|
9693
|
+
tooltip: "Session context",
|
|
9694
|
+
variant: "ghost",
|
|
9695
|
+
className: "h-9 gap-1 rounded-md border-0 bg-transparent px-1.5 text-muted-foreground hover:bg-accent/60 hover:text-foreground pointer-coarse:h-11",
|
|
9696
|
+
children: [
|
|
9697
|
+
/* @__PURE__ */ jsx(ContextIcon, { className: "size-4" }),
|
|
9698
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] tabular-nums", children: contextPercentLabel })
|
|
9699
|
+
]
|
|
9453
9700
|
}
|
|
9454
|
-
)
|
|
9455
|
-
|
|
9456
|
-
|
|
9701
|
+
),
|
|
9702
|
+
children: [
|
|
9703
|
+
/* @__PURE__ */ jsx(ContextContentHeader, {}),
|
|
9704
|
+
/* @__PURE__ */ jsx(ContextContentBody, { children: tokenUsage.totalTokens > 0 ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
9705
|
+
/* @__PURE__ */ jsx(ContextInputUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9706
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Input" }),
|
|
9707
|
+
/* @__PURE__ */ jsx("span", { children: tokenUsage.inputTokens.toLocaleString() })
|
|
9708
|
+
] }) }),
|
|
9709
|
+
/* @__PURE__ */ jsx(ContextOutputUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9710
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Output" }),
|
|
9711
|
+
/* @__PURE__ */ jsx("span", { children: tokenUsage.outputTokens.toLocaleString() })
|
|
9712
|
+
] }) }),
|
|
9713
|
+
tokenUsage.reasoningTokens > 0 && /* @__PURE__ */ jsx(ContextReasoningUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9714
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
|
|
9715
|
+
/* @__PURE__ */ jsx("span", { children: tokenUsage.reasoningTokens.toLocaleString() })
|
|
9716
|
+
] }) }),
|
|
9717
|
+
tokenUsage.cachedInputTokens > 0 && /* @__PURE__ */ jsx(ContextCacheUsage, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9718
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Cache" }),
|
|
9719
|
+
/* @__PURE__ */ jsx("span", { children: tokenUsage.cachedInputTokens.toLocaleString() })
|
|
9720
|
+
] }) })
|
|
9721
|
+
] }) : /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground text-center py-1", children: "Empty" }) }),
|
|
9722
|
+
tokenUsage.totalTokens > 0 && /* @__PURE__ */ jsx("div", { className: "flex w-full items-center justify-end gap-3 bg-secondary p-2", children: /* @__PURE__ */ jsx(
|
|
9723
|
+
Button,
|
|
9724
|
+
{
|
|
9725
|
+
type: "button",
|
|
9726
|
+
size: "xs",
|
|
9727
|
+
className: "pointer-coarse:h-11 pointer-coarse:px-4 pointer-coarse:text-sm",
|
|
9728
|
+
onClick: () => {
|
|
9729
|
+
setContextOpen(false);
|
|
9730
|
+
onSend("/compress");
|
|
9731
|
+
},
|
|
9732
|
+
children: "Compress"
|
|
9733
|
+
}
|
|
9734
|
+
) })
|
|
9735
|
+
]
|
|
9736
|
+
}
|
|
9737
|
+
)
|
|
9457
9738
|
}
|
|
9458
9739
|
)
|
|
9459
9740
|
] }),
|
|
9460
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
9461
|
-
|
|
9462
|
-
ButtonGroup,
|
|
9463
|
-
{
|
|
9464
|
-
"aria-label": "Chat view mode",
|
|
9465
|
-
className: "overflow-hidden rounded-md border border-border",
|
|
9466
|
-
children: [
|
|
9467
|
-
/* @__PURE__ */ jsx(
|
|
9468
|
-
Button,
|
|
9469
|
-
{
|
|
9470
|
-
type: "button",
|
|
9471
|
-
size: "xs",
|
|
9472
|
-
className: "capitalize font-normal",
|
|
9473
|
-
variant: viewMode === "simple" ? "secondary" : "ghost",
|
|
9474
|
-
"aria-pressed": viewMode === "simple",
|
|
9475
|
-
onClick: () => onViewModeChange("simple"),
|
|
9476
|
-
children: "Simple"
|
|
9477
|
-
}
|
|
9478
|
-
),
|
|
9479
|
-
/* @__PURE__ */ jsx(
|
|
9480
|
-
Button,
|
|
9481
|
-
{
|
|
9482
|
-
type: "button",
|
|
9483
|
-
size: "xs",
|
|
9484
|
-
className: "capitalize font-normal",
|
|
9485
|
-
variant: viewMode === "expert" ? "secondary" : "ghost",
|
|
9486
|
-
"aria-pressed": viewMode === "expert",
|
|
9487
|
-
onClick: () => onViewModeChange("expert"),
|
|
9488
|
-
children: "Advanced"
|
|
9489
|
-
}
|
|
9490
|
-
)
|
|
9491
|
-
]
|
|
9492
|
-
}
|
|
9493
|
-
),
|
|
9741
|
+
/* @__PURE__ */ jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
|
|
9742
|
+
viewModeToggle,
|
|
9494
9743
|
!disabled && /* @__PURE__ */ jsx(
|
|
9495
9744
|
PromptInputSubmit,
|
|
9496
9745
|
{
|
|
@@ -11410,16 +11659,34 @@ var WORKING_ON_IT_DELAY_MS = 250;
|
|
|
11410
11659
|
function hasUserFacingAskContent(msg) {
|
|
11411
11660
|
return !!msg.toolCalls?.some((tc) => tc.toolName === "ask_user_question");
|
|
11412
11661
|
}
|
|
11413
|
-
function
|
|
11662
|
+
function pendingAskCall(messages) {
|
|
11414
11663
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
11415
11664
|
const m = messages[i];
|
|
11416
|
-
if (m.role === "user") return
|
|
11665
|
+
if (m.role === "user") return null;
|
|
11417
11666
|
if (m.role !== "assistant") continue;
|
|
11418
|
-
return
|
|
11667
|
+
return m.toolCalls?.find(
|
|
11419
11668
|
(tc) => tc.toolName === "ask_user_question" && tc.status === "running"
|
|
11420
|
-
);
|
|
11669
|
+
) ?? null;
|
|
11421
11670
|
}
|
|
11422
|
-
return
|
|
11671
|
+
return null;
|
|
11672
|
+
}
|
|
11673
|
+
function askComposerPolicy(pending) {
|
|
11674
|
+
if (!pending) return null;
|
|
11675
|
+
const questions = askQuestionsOf(pending);
|
|
11676
|
+
if (!isConversationalAsk(questions)) {
|
|
11677
|
+
return { locked: true, reason: "Answer the questions above to continue." };
|
|
11678
|
+
}
|
|
11679
|
+
const question = questions[0];
|
|
11680
|
+
if (!conversationalAskAcceptsFreeText(question)) {
|
|
11681
|
+
return {
|
|
11682
|
+
locked: true,
|
|
11683
|
+
reason: "Pick one of the options above to continue."
|
|
11684
|
+
};
|
|
11685
|
+
}
|
|
11686
|
+
return {
|
|
11687
|
+
locked: false,
|
|
11688
|
+
placeholder: hasChoices(question) ? "Pick one above, or type your answer\u2026" : "Type your answer\u2026"
|
|
11689
|
+
};
|
|
11423
11690
|
}
|
|
11424
11691
|
function messageToEntries(msg, isLast, artifactFallbacks) {
|
|
11425
11692
|
if (msg.role === "system") {
|
|
@@ -11502,7 +11769,14 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
|
|
|
11502
11769
|
const parsed = parseTerminalResult(tc.result, tc.args);
|
|
11503
11770
|
if (parsed && (parsed.exit_code !== 0 || parsed.error)) continue;
|
|
11504
11771
|
}
|
|
11505
|
-
entries.push({
|
|
11772
|
+
entries.push({
|
|
11773
|
+
kind: "tool",
|
|
11774
|
+
key: tc.id,
|
|
11775
|
+
tc,
|
|
11776
|
+
// Only ask_user_question reads this, to suppress a prompt the
|
|
11777
|
+
// agent already spelled out in its message body.
|
|
11778
|
+
assistantContent: tc.toolName === "ask_user_question" ? msg.content : void 0
|
|
11779
|
+
});
|
|
11506
11780
|
}
|
|
11507
11781
|
}
|
|
11508
11782
|
if (effectiveHasContent) {
|
|
@@ -11747,6 +12021,8 @@ function TimelineEntryItem({
|
|
|
11747
12021
|
{
|
|
11748
12022
|
tc: entry.tc,
|
|
11749
12023
|
resolvedArtifactName: entry.resolvedArtifactName,
|
|
12024
|
+
assistantContent: entry.assistantContent,
|
|
12025
|
+
viewMode: "expert",
|
|
11750
12026
|
onFileSelect
|
|
11751
12027
|
}
|
|
11752
12028
|
),
|
|
@@ -12270,7 +12546,14 @@ function IterationGroup({
|
|
|
12270
12546
|
(tc) => tc.toolName === "ask_user_question"
|
|
12271
12547
|
);
|
|
12272
12548
|
if (askCall) {
|
|
12273
|
-
return /* @__PURE__ */ jsx("div", { className: "px-1 py-0.5", children: /* @__PURE__ */ jsx(
|
|
12549
|
+
return /* @__PURE__ */ jsx("div", { className: "px-1 py-0.5", children: /* @__PURE__ */ jsx(
|
|
12550
|
+
AskUserQuestionToolBlock,
|
|
12551
|
+
{
|
|
12552
|
+
tc: askCall,
|
|
12553
|
+
assistantContent: message.content,
|
|
12554
|
+
viewMode: "simple"
|
|
12555
|
+
}
|
|
12556
|
+
) });
|
|
12274
12557
|
}
|
|
12275
12558
|
if (isIterationLive(message)) {
|
|
12276
12559
|
return /* @__PURE__ */ jsx(
|
|
@@ -12613,7 +12896,12 @@ function ChatThread({
|
|
|
12613
12896
|
onOpenIntegrations
|
|
12614
12897
|
}) {
|
|
12615
12898
|
const groups = useMemo(() => groupMessages(messages), [messages]);
|
|
12616
|
-
const
|
|
12899
|
+
const pendingAsk = useMemo(() => pendingAskCall(messages), [messages]);
|
|
12900
|
+
const askPolicy = useMemo(
|
|
12901
|
+
() => askComposerPolicy(pendingAsk),
|
|
12902
|
+
[pendingAsk?.id, pendingAsk?.args]
|
|
12903
|
+
);
|
|
12904
|
+
const awaitingInput = askPolicy !== null;
|
|
12617
12905
|
const orbActivity = useMemo(
|
|
12618
12906
|
() => deriveOrbActivity(
|
|
12619
12907
|
messages,
|
|
@@ -12622,8 +12910,9 @@ function ChatThread({
|
|
|
12622
12910
|
[messages, viewMode]
|
|
12623
12911
|
);
|
|
12624
12912
|
const showWorkingOnIt = useDelayedRunningIndicator(isRunning) && !awaitingInput;
|
|
12625
|
-
const
|
|
12626
|
-
const
|
|
12913
|
+
const composerLocked = askPolicy?.locked ?? false;
|
|
12914
|
+
const composerDisabled = disabled || composerLocked;
|
|
12915
|
+
const composerDisabledReason = composerLocked ? askPolicy?.reason : disabledReason;
|
|
12627
12916
|
const artifactFallbacks = useMemo(() => {
|
|
12628
12917
|
const fallbacks = {};
|
|
12629
12918
|
const createArtifactToolCallIds = [];
|
|
@@ -12668,6 +12957,8 @@ function ChatThread({
|
|
|
12668
12957
|
isRunning,
|
|
12669
12958
|
disabled: composerDisabled,
|
|
12670
12959
|
disabledReason: composerDisabledReason,
|
|
12960
|
+
awaitingAnswer: askPolicy !== null && !askPolicy.locked,
|
|
12961
|
+
placeholder: askPolicy?.placeholder,
|
|
12671
12962
|
tokenUsage,
|
|
12672
12963
|
onComposerError,
|
|
12673
12964
|
showBrowser,
|
|
@@ -12700,10 +12991,16 @@ function ChatThread({
|
|
|
12700
12991
|
)
|
|
12701
12992
|
] });
|
|
12702
12993
|
if (messages.length === 0 && !isLoadingHistory && !disabled) {
|
|
12703
|
-
return
|
|
12704
|
-
|
|
12705
|
-
|
|
12706
|
-
|
|
12994
|
+
return (
|
|
12995
|
+
// A centred hero is a deliberate desktop look. On a phone it leaves the
|
|
12996
|
+
// input floating mid-screen with dead space under it, and the keyboard
|
|
12997
|
+
// then opens across that gap. There the composer keeps its usual place
|
|
12998
|
+
// at the bottom edge and the greeting takes the space above it.
|
|
12999
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-end overflow-hidden bg-background px-3 text-base md:justify-center", children: [
|
|
13000
|
+
/* @__PURE__ */ jsx("div", { className: "flex w-full flex-1 flex-col justify-center md:flex-none", children: /* @__PURE__ */ jsx("h2", { className: "mb-6 text-center text-2xl font-bold text-foreground md:mb-8", children: "What should we do ?" }) }),
|
|
13001
|
+
/* @__PURE__ */ jsx("div", { className: "w-full", children: composerArea })
|
|
13002
|
+
] })
|
|
13003
|
+
);
|
|
12707
13004
|
}
|
|
12708
13005
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-background text-base", children: [
|
|
12709
13006
|
/* @__PURE__ */ jsxs(Conversation, { className: "relative flex-1 min-h-0", children: [
|
|
@@ -13003,14 +13300,22 @@ function RenderEntries({
|
|
|
13003
13300
|
}) {
|
|
13004
13301
|
return /* @__PURE__ */ jsx(Fragment, { children: entries.map((entry) => {
|
|
13005
13302
|
if (entry.kind === "dir") {
|
|
13006
|
-
return /* @__PURE__ */ jsx(
|
|
13007
|
-
|
|
13303
|
+
return /* @__PURE__ */ jsx(
|
|
13304
|
+
FileTreeFolder,
|
|
13008
13305
|
{
|
|
13009
|
-
|
|
13010
|
-
|
|
13011
|
-
|
|
13012
|
-
|
|
13013
|
-
|
|
13306
|
+
name: entry.name,
|
|
13307
|
+
path: entry.path,
|
|
13308
|
+
children: entry.children && entry.children.length > 0 && /* @__PURE__ */ jsx(
|
|
13309
|
+
RenderEntries,
|
|
13310
|
+
{
|
|
13311
|
+
entries: entry.children,
|
|
13312
|
+
onDelete,
|
|
13313
|
+
downloadUrlFor
|
|
13314
|
+
}
|
|
13315
|
+
)
|
|
13316
|
+
},
|
|
13317
|
+
entry.path
|
|
13318
|
+
);
|
|
13014
13319
|
}
|
|
13015
13320
|
const fileName2 = entry.name;
|
|
13016
13321
|
return /* @__PURE__ */ jsxs(FileTreeFile, { name: entry.name, path: entry.path, children: [
|
|
@@ -13069,6 +13374,7 @@ function WorkspacePanel({
|
|
|
13069
13374
|
const [deleteTarget, setDeleteTarget] = useState(null);
|
|
13070
13375
|
const [expandedPaths, setExpandedPaths] = useState(/* @__PURE__ */ new Set());
|
|
13071
13376
|
const [width, setWidth] = useState(DEFAULT_WIDTH);
|
|
13377
|
+
const narrow = useIsMobile();
|
|
13072
13378
|
const sessionIdRef = useRef(sessionId);
|
|
13073
13379
|
const isResizing = useRef(false);
|
|
13074
13380
|
const startX = useRef(0);
|
|
@@ -13218,7 +13524,14 @@ function WorkspacePanel({
|
|
|
13218
13524
|
setDeleteTarget(null);
|
|
13219
13525
|
}
|
|
13220
13526
|
},
|
|
13221
|
-
[
|
|
13527
|
+
[
|
|
13528
|
+
adapter,
|
|
13529
|
+
disabled,
|
|
13530
|
+
fetchTree,
|
|
13531
|
+
onSelectedPathChange,
|
|
13532
|
+
selectedPath,
|
|
13533
|
+
sessionId
|
|
13534
|
+
]
|
|
13222
13535
|
);
|
|
13223
13536
|
const onResizeStart = useCallback(
|
|
13224
13537
|
(event) => {
|
|
@@ -13258,11 +13571,11 @@ function WorkspacePanel({
|
|
|
13258
13571
|
"data-testid": "workspace-panel",
|
|
13259
13572
|
className: cn(
|
|
13260
13573
|
"relative z-10 flex h-full min-h-0 flex-col overflow-hidden border-l border-muted-foreground/20 bg-card",
|
|
13261
|
-
fillParent && "w-full"
|
|
13574
|
+
(fillParent || narrow) && "w-full"
|
|
13262
13575
|
),
|
|
13263
|
-
style: fillParent ? void 0 : { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
13576
|
+
style: fillParent || narrow ? void 0 : { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
13264
13577
|
children: [
|
|
13265
|
-
!fillParent && /* @__PURE__ */ jsx(
|
|
13578
|
+
!(fillParent || narrow) && /* @__PURE__ */ jsx(
|
|
13266
13579
|
"div",
|
|
13267
13580
|
{
|
|
13268
13581
|
className: "absolute inset-y-0 left-0 z-20 w-1.5 cursor-col-resize transition-colors hover:bg-primary/20 active:bg-primary/30",
|
|
@@ -13435,10 +13748,18 @@ function ConfirmDialog2({
|
|
|
13435
13748
|
] }),
|
|
13436
13749
|
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
13437
13750
|
/* @__PURE__ */ jsx(Button, { variant: "outline", disabled: loading, onClick: onCancel, children: "Cancel" }),
|
|
13438
|
-
/* @__PURE__ */ jsxs(
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13751
|
+
/* @__PURE__ */ jsxs(
|
|
13752
|
+
Button,
|
|
13753
|
+
{
|
|
13754
|
+
variant: "destructive",
|
|
13755
|
+
disabled: loading,
|
|
13756
|
+
onClick: handleConfirm,
|
|
13757
|
+
children: [
|
|
13758
|
+
loading && /* @__PURE__ */ jsx(Loader2Icon, { className: "mr-1.5 size-3.5 animate-spin" }),
|
|
13759
|
+
confirmLabel
|
|
13760
|
+
]
|
|
13761
|
+
}
|
|
13762
|
+
)
|
|
13442
13763
|
] })
|
|
13443
13764
|
] })
|
|
13444
13765
|
}
|
|
@@ -13734,7 +14055,11 @@ function applyAgentChatEvent(state, event) {
|
|
|
13734
14055
|
case "ask_user_question.response":
|
|
13735
14056
|
return withMessages(
|
|
13736
14057
|
nextState,
|
|
13737
|
-
applyAskUserQuestionResponse(
|
|
14058
|
+
applyAskUserQuestionResponse(
|
|
14059
|
+
nextState.messages,
|
|
14060
|
+
event.eventId,
|
|
14061
|
+
event.data
|
|
14062
|
+
)
|
|
13738
14063
|
);
|
|
13739
14064
|
case "policy.denied":
|
|
13740
14065
|
return applyPolicyDenied(nextState, event.data);
|
|
@@ -14485,34 +14810,59 @@ function applyUserFeedback(messages, data) {
|
|
|
14485
14810
|
}
|
|
14486
14811
|
return messages;
|
|
14487
14812
|
}
|
|
14488
|
-
function applyAskUserQuestionResponse(messages, data) {
|
|
14813
|
+
function applyAskUserQuestionResponse(messages, eventId, data) {
|
|
14489
14814
|
const targetToolId = stringValue2(data.tool_call_id);
|
|
14490
14815
|
const responses = Array.isArray(data.responses) ? data.responses : void 0;
|
|
14491
14816
|
if (!targetToolId || !responses) return messages;
|
|
14817
|
+
const answers = responses.map((response) => {
|
|
14818
|
+
const row = objectValue(response) ?? {};
|
|
14819
|
+
return {
|
|
14820
|
+
question: stringValue2(row.question),
|
|
14821
|
+
answer: stringValue2(row.answer),
|
|
14822
|
+
is_other: Boolean(row.is_other)
|
|
14823
|
+
};
|
|
14824
|
+
});
|
|
14492
14825
|
const next = [...messages];
|
|
14493
14826
|
for (let i = next.length - 1; i >= 0; i--) {
|
|
14494
14827
|
const msg = next[i];
|
|
14495
14828
|
if (!msg?.toolCalls?.some((tc) => tc.id === targetToolId)) continue;
|
|
14829
|
+
const alreadyAnswered = msg.toolCalls.some(
|
|
14830
|
+
(tc) => tc.id === targetToolId && tc.askUserQuestionAnswers !== void 0
|
|
14831
|
+
);
|
|
14832
|
+
if (alreadyAnswered) return messages;
|
|
14496
14833
|
next[i] = {
|
|
14497
14834
|
...msg,
|
|
14498
14835
|
toolCalls: msg.toolCalls.map(
|
|
14499
|
-
(tc) => tc.id === targetToolId ? {
|
|
14500
|
-
...tc,
|
|
14501
|
-
askUserQuestionAnswers: responses.map((response) => {
|
|
14502
|
-
const row = objectValue(response) ?? {};
|
|
14503
|
-
return {
|
|
14504
|
-
question: stringValue2(row.question),
|
|
14505
|
-
answer: stringValue2(row.answer),
|
|
14506
|
-
is_other: Boolean(row.is_other)
|
|
14507
|
-
};
|
|
14508
|
-
})
|
|
14509
|
-
} : tc
|
|
14836
|
+
(tc) => tc.id === targetToolId ? { ...tc, askUserQuestionAnswers: answers } : tc
|
|
14510
14837
|
)
|
|
14511
14838
|
};
|
|
14512
|
-
return next;
|
|
14839
|
+
return appendConversationalAnswer(next, i, eventId, answers);
|
|
14513
14840
|
}
|
|
14514
14841
|
return messages;
|
|
14515
14842
|
}
|
|
14843
|
+
function appendConversationalAnswer(messages, askIndex, eventId, answers) {
|
|
14844
|
+
if (answers.length !== 1) return messages;
|
|
14845
|
+
const answer = answers[0].answer.trim();
|
|
14846
|
+
if (!answer) return messages;
|
|
14847
|
+
const id = `evt-${eventId}`;
|
|
14848
|
+
if (messages.some((m) => m.id === id)) return messages;
|
|
14849
|
+
const next = [...messages];
|
|
14850
|
+
for (let i = askIndex + 1; i < next.length; i++) {
|
|
14851
|
+
const msg = next[i];
|
|
14852
|
+
if (msg.role !== "user" || !msg.id.startsWith("local-")) continue;
|
|
14853
|
+
if (msg.status === "error") continue;
|
|
14854
|
+
next[i] = { ...msg, id, content: answer, status: "complete" };
|
|
14855
|
+
return next;
|
|
14856
|
+
}
|
|
14857
|
+
next.push({
|
|
14858
|
+
id,
|
|
14859
|
+
role: "user",
|
|
14860
|
+
content: answer,
|
|
14861
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
14862
|
+
status: "complete"
|
|
14863
|
+
});
|
|
14864
|
+
return next;
|
|
14865
|
+
}
|
|
14516
14866
|
function applyPolicyDenied(state, data) {
|
|
14517
14867
|
const messages = [...state.messages];
|
|
14518
14868
|
const policyIdx = findLastAssistantIndex(messages);
|
|
@@ -15204,6 +15554,11 @@ function parseGoalDefinition(content) {
|
|
|
15204
15554
|
var RIGHT_STACK_STYLE = {
|
|
15205
15555
|
["--right-stack-w"]: "440px"
|
|
15206
15556
|
};
|
|
15557
|
+
var MOBILE_PANE_LABELS = {
|
|
15558
|
+
chat: "Chat",
|
|
15559
|
+
browser: "Browser",
|
|
15560
|
+
workspace: "Workspace"
|
|
15561
|
+
};
|
|
15207
15562
|
function AgentChat({
|
|
15208
15563
|
adapter,
|
|
15209
15564
|
agentId,
|
|
@@ -15276,7 +15631,14 @@ function AgentChat({
|
|
|
15276
15631
|
const handleToggleWorkspace = useCallback(() => {
|
|
15277
15632
|
setShowWorkspace((prev) => !prev);
|
|
15278
15633
|
}, []);
|
|
15279
|
-
const
|
|
15634
|
+
const mobilePanes = useMemo(() => {
|
|
15635
|
+
const panes = ["chat"];
|
|
15636
|
+
if (browserVisible) panes.push("browser");
|
|
15637
|
+
if (workspaceVisible) panes.push("workspace");
|
|
15638
|
+
return panes;
|
|
15639
|
+
}, [browserVisible, workspaceVisible]);
|
|
15640
|
+
const activeMobilePane = mobilePanes.includes(mobileView) ? mobileView : "chat";
|
|
15641
|
+
const showMobileToggle = mobilePanes.length > 1;
|
|
15280
15642
|
return /* @__PURE__ */ jsx(
|
|
15281
15643
|
AgentChatAdapterProvider,
|
|
15282
15644
|
{
|
|
@@ -15303,43 +15665,36 @@ function AgentChat({
|
|
|
15303
15665
|
),
|
|
15304
15666
|
style: { direction: "ltr", ...RIGHT_STACK_STYLE },
|
|
15305
15667
|
children: [
|
|
15306
|
-
showMobileToggle && /* @__PURE__ */
|
|
15307
|
-
|
|
15308
|
-
|
|
15309
|
-
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
"
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
|
|
15320
|
-
|
|
15321
|
-
|
|
15322
|
-
|
|
15323
|
-
|
|
15324
|
-
|
|
15325
|
-
|
|
15326
|
-
|
|
15327
|
-
"flex-1 px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors",
|
|
15328
|
-
mobileView === "workspace" ? "border-primary text-foreground" : "border-transparent text-subtle hover:text-foreground"
|
|
15329
|
-
),
|
|
15330
|
-
children: "Workspace"
|
|
15331
|
-
}
|
|
15332
|
-
)
|
|
15333
|
-
] }),
|
|
15668
|
+
showMobileToggle && /* @__PURE__ */ jsx(
|
|
15669
|
+
"div",
|
|
15670
|
+
{
|
|
15671
|
+
"data-testid": "mobile-pane-toggle",
|
|
15672
|
+
className: "md:hidden flex shrink-0 border-b border-line bg-card",
|
|
15673
|
+
children: mobilePanes.map((pane) => /* @__PURE__ */ jsx(
|
|
15674
|
+
"button",
|
|
15675
|
+
{
|
|
15676
|
+
type: "button",
|
|
15677
|
+
onClick: () => setMobileView(pane),
|
|
15678
|
+
"aria-pressed": activeMobilePane === pane,
|
|
15679
|
+
className: cn(
|
|
15680
|
+
"flex-1 min-h-11 px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors",
|
|
15681
|
+
activeMobilePane === pane ? "border-primary text-foreground" : "border-transparent text-subtle hover:text-foreground"
|
|
15682
|
+
),
|
|
15683
|
+
children: MOBILE_PANE_LABELS[pane]
|
|
15684
|
+
},
|
|
15685
|
+
pane
|
|
15686
|
+
))
|
|
15687
|
+
}
|
|
15688
|
+
),
|
|
15334
15689
|
/* @__PURE__ */ jsx(
|
|
15335
15690
|
"div",
|
|
15336
15691
|
{
|
|
15337
15692
|
"data-testid": "chat-panel",
|
|
15338
|
-
"data-mobile-view":
|
|
15693
|
+
"data-mobile-view": activeMobilePane,
|
|
15339
15694
|
className: cn(
|
|
15340
|
-
// Phone: full width column, hidden
|
|
15695
|
+
// Phone: full width column, hidden while another pane is picked.
|
|
15341
15696
|
"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
|
|
15342
|
-
showMobileToggle && "data-[mobile-view=workspace]:hidden md:flex!",
|
|
15697
|
+
showMobileToggle && "data-[mobile-view=browser]:hidden data-[mobile-view=workspace]:hidden md:flex!",
|
|
15343
15698
|
// md+: positioning depends on whether the browser/workspace
|
|
15344
15699
|
// right stack is laid out. When the browser pane is shown
|
|
15345
15700
|
// we pin a fixed-width column on the right and absolutely
|
|
@@ -15396,7 +15751,7 @@ function AgentChat({
|
|
|
15396
15751
|
"div",
|
|
15397
15752
|
{
|
|
15398
15753
|
"data-testid": "right-stack",
|
|
15399
|
-
"data-mobile-view":
|
|
15754
|
+
"data-mobile-view": activeMobilePane,
|
|
15400
15755
|
className: cn(
|
|
15401
15756
|
// Phone: full width column, hidden when chat tab active.
|
|
15402
15757
|
"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
|
|
@@ -15414,9 +15769,13 @@ function AgentChat({
|
|
|
15414
15769
|
"div",
|
|
15415
15770
|
{
|
|
15416
15771
|
"data-testid": "browser-panel",
|
|
15772
|
+
"data-mobile-view": activeMobilePane,
|
|
15417
15773
|
className: cn(
|
|
15418
15774
|
"min-h-0 w-full overflow-hidden",
|
|
15419
|
-
|
|
15775
|
+
// With both panes the desktop splits the column in half.
|
|
15776
|
+
// A phone gives whichever pane is selected the whole
|
|
15777
|
+
// height instead — see the mobile toggle above.
|
|
15778
|
+
bothPanesVisible ? "h-full data-[mobile-view=workspace]:hidden md:block! md:h-1/2 md:border-b md:border-line" : "h-full"
|
|
15420
15779
|
),
|
|
15421
15780
|
children: /* @__PURE__ */ jsx(
|
|
15422
15781
|
BrowserPane,
|
|
@@ -15433,7 +15792,8 @@ function AgentChat({
|
|
|
15433
15792
|
"div",
|
|
15434
15793
|
{
|
|
15435
15794
|
"data-testid": "workspace-panel-frame",
|
|
15436
|
-
|
|
15795
|
+
"data-mobile-view": activeMobilePane,
|
|
15796
|
+
className: bothPanesVisible ? "h-full min-h-0 w-full overflow-hidden data-[mobile-view=browser]:hidden md:block! md:h-1/2" : "min-h-0 h-full",
|
|
15437
15797
|
children: /* @__PURE__ */ jsx(
|
|
15438
15798
|
WorkspacePanel,
|
|
15439
15799
|
{
|
|
@@ -15733,7 +16093,7 @@ function IntegrationsPage({ agentId, adapter, onBack }) {
|
|
|
15733
16093
|
{
|
|
15734
16094
|
type: "button",
|
|
15735
16095
|
onClick: onBack,
|
|
15736
|
-
className: "flex w-fit items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
|
|
16096
|
+
className: "-mx-2 inline-flex min-h-11 w-fit items-center gap-1.5 rounded-lg px-2 text-sm text-muted-foreground pointer-fine:mx-0 pointer-fine:min-h-0 pointer-fine:px-0 pointer-fine:hover:text-foreground",
|
|
15737
16097
|
children: [
|
|
15738
16098
|
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-4 w-4" }),
|
|
15739
16099
|
"Back"
|
|
@@ -15877,7 +16237,7 @@ function CodingAgentsPanel({ agentId, adapter, onBack }) {
|
|
|
15877
16237
|
{
|
|
15878
16238
|
type: "button",
|
|
15879
16239
|
onClick: onBack,
|
|
15880
|
-
className: "flex w-fit items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
|
|
16240
|
+
className: "-mx-2 inline-flex min-h-11 w-fit items-center gap-1.5 rounded-lg px-2 text-sm text-muted-foreground pointer-fine:mx-0 pointer-fine:min-h-0 pointer-fine:px-0 pointer-fine:hover:text-foreground",
|
|
15881
16241
|
children: [
|
|
15882
16242
|
/* @__PURE__ */ jsx(ArrowLeftIcon, { className: "h-4 w-4" }),
|
|
15883
16243
|
"Back"
|
|
@@ -16018,7 +16378,7 @@ function ProviderCard({
|
|
|
16018
16378
|
type: "button",
|
|
16019
16379
|
disabled: busy,
|
|
16020
16380
|
onClick: submit,
|
|
16021
|
-
className: "inline-flex items-center justify-center gap-1.5 rounded-md border border-border px-3
|
|
16381
|
+
className: "inline-flex min-h-11 items-center justify-center gap-1.5 rounded-md border border-border px-3 text-xs text-foreground disabled:opacity-60 pointer-fine:min-h-0 pointer-fine:py-1.5 pointer-fine:hover:bg-accent",
|
|
16022
16382
|
children: [
|
|
16023
16383
|
busy && /* @__PURE__ */ jsx(Loader2Icon, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
16024
16384
|
connected ? "Update" : "Connect"
|
|
@@ -16038,6 +16398,63 @@ function ProviderCard({
|
|
|
16038
16398
|
] })
|
|
16039
16399
|
] });
|
|
16040
16400
|
}
|
|
16401
|
+
|
|
16402
|
+
// src/components/inbox/inbox-answers.ts
|
|
16403
|
+
function hasChoices2(question) {
|
|
16404
|
+
return (question.choices?.length ?? 0) > 0;
|
|
16405
|
+
}
|
|
16406
|
+
function parseInboxQuestions(item) {
|
|
16407
|
+
const raw = item.payload.questions;
|
|
16408
|
+
if (!Array.isArray(raw)) return [];
|
|
16409
|
+
return raw.map((entry) => {
|
|
16410
|
+
const question = entry;
|
|
16411
|
+
const choices = Array.isArray(question.choices) ? question.choices.map((choice) => ({
|
|
16412
|
+
label: typeof choice.label === "string" ? choice.label : "",
|
|
16413
|
+
description: typeof choice.description === "string" ? choice.description : void 0
|
|
16414
|
+
})).filter((choice) => choice.label) : void 0;
|
|
16415
|
+
return {
|
|
16416
|
+
prompt: typeof question.prompt === "string" ? question.prompt : "",
|
|
16417
|
+
choices,
|
|
16418
|
+
allowOther: question.allow_other !== false
|
|
16419
|
+
};
|
|
16420
|
+
}).filter((question) => question.prompt);
|
|
16421
|
+
}
|
|
16422
|
+
function isOtherAnswer(question, draft) {
|
|
16423
|
+
return hasChoices2(question) && !!draft?.useOther;
|
|
16424
|
+
}
|
|
16425
|
+
function answerText(question, draft) {
|
|
16426
|
+
const fromMenu = hasChoices2(question) && !draft?.useOther;
|
|
16427
|
+
return (fromMenu ? draft?.picked ?? "" : draft?.typed ?? "").trim();
|
|
16428
|
+
}
|
|
16429
|
+
function buildInboxResponse(question, draft) {
|
|
16430
|
+
return {
|
|
16431
|
+
question: question.prompt,
|
|
16432
|
+
answer: answerText(question, draft),
|
|
16433
|
+
is_other: isOtherAnswer(question, draft)
|
|
16434
|
+
};
|
|
16435
|
+
}
|
|
16436
|
+
var TICK_MS = 3e4;
|
|
16437
|
+
function formatExpiresIn(expiresAtIso, nowMs) {
|
|
16438
|
+
const remainingMs = Date.parse(expiresAtIso) - nowMs;
|
|
16439
|
+
if (remainingMs <= 0) return "Expired";
|
|
16440
|
+
if (remainingMs < 6e4) return "Expires in under a minute";
|
|
16441
|
+
const minutes = Math.round(remainingMs / 6e4);
|
|
16442
|
+
return `Expires in ~${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
16443
|
+
}
|
|
16444
|
+
function useAnswerWindow(expiresAtIso) {
|
|
16445
|
+
const [now, setNow] = useState(() => Date.now());
|
|
16446
|
+
useEffect(() => {
|
|
16447
|
+
const id = setInterval(() => setNow(Date.now()), TICK_MS);
|
|
16448
|
+
return () => clearInterval(id);
|
|
16449
|
+
}, []);
|
|
16450
|
+
if (!expiresAtIso) {
|
|
16451
|
+
return { expired: false, label: "" };
|
|
16452
|
+
}
|
|
16453
|
+
return {
|
|
16454
|
+
expired: Date.parse(expiresAtIso) <= now,
|
|
16455
|
+
label: formatExpiresIn(expiresAtIso, now)
|
|
16456
|
+
};
|
|
16457
|
+
}
|
|
16041
16458
|
var DEFAULT_LIMIT = 50;
|
|
16042
16459
|
function requireInboxAdapter(adapter) {
|
|
16043
16460
|
if (!adapter.listInbox || !adapter.getInboxItem || !adapter.markInboxItemRead || !adapter.acknowledgeInboxItem || !adapter.respondGovernanceInboxItem || !adapter.openInboxStream) {
|
|
@@ -16071,8 +16488,33 @@ function kindIcon(kind) {
|
|
|
16071
16488
|
if (kind === "progress_checkin") return TimerIcon;
|
|
16072
16489
|
return CircleDotIcon;
|
|
16073
16490
|
}
|
|
16491
|
+
var VIEWS = {
|
|
16492
|
+
active: {
|
|
16493
|
+
statuses: ["pending"],
|
|
16494
|
+
kinds: ["input_required", "action_required", "governance_gate"],
|
|
16495
|
+
empty: "Nothing needs you right now"
|
|
16496
|
+
},
|
|
16497
|
+
updates: {
|
|
16498
|
+
statuses: ["pending"],
|
|
16499
|
+
kinds: ["task_complete", "progress_checkin"],
|
|
16500
|
+
empty: "No news from your agent"
|
|
16501
|
+
},
|
|
16502
|
+
history: {
|
|
16503
|
+
statuses: ["acknowledged", "responded", "expired"],
|
|
16504
|
+
empty: "No history yet"
|
|
16505
|
+
}
|
|
16506
|
+
};
|
|
16507
|
+
var VIEW_NAMES = Object.keys(VIEWS);
|
|
16508
|
+
function belongsToView(item, view) {
|
|
16509
|
+
const kinds = VIEWS[view].kinds;
|
|
16510
|
+
return VIEWS[view].statuses.includes(item.status) && (kinds === void 0 || kinds.includes(item.kind));
|
|
16511
|
+
}
|
|
16074
16512
|
function sortItems(items) {
|
|
16075
|
-
|
|
16513
|
+
const byId = /* @__PURE__ */ new Map();
|
|
16514
|
+
for (const item of items) {
|
|
16515
|
+
if (!byId.has(item.id)) byId.set(item.id, item);
|
|
16516
|
+
}
|
|
16517
|
+
return [...byId.values()].sort((a, b) => {
|
|
16076
16518
|
if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt ? -1 : 1;
|
|
16077
16519
|
return b.id - a.id;
|
|
16078
16520
|
});
|
|
@@ -16086,37 +16528,106 @@ function InboxBody({ body, muted }) {
|
|
|
16086
16528
|
}
|
|
16087
16529
|
);
|
|
16088
16530
|
}
|
|
16531
|
+
var FIELD_CLASS = "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50";
|
|
16532
|
+
function errorMessage(error, fallback) {
|
|
16533
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
16534
|
+
}
|
|
16535
|
+
function useAction() {
|
|
16536
|
+
const [error, setError] = useState(null);
|
|
16537
|
+
const [busy, setBusy] = useState(false);
|
|
16538
|
+
const run = useCallback(
|
|
16539
|
+
async (fallback, action) => {
|
|
16540
|
+
setBusy(true);
|
|
16541
|
+
setError(null);
|
|
16542
|
+
try {
|
|
16543
|
+
await action();
|
|
16544
|
+
} catch (err) {
|
|
16545
|
+
setError(errorMessage(err, fallback));
|
|
16546
|
+
} finally {
|
|
16547
|
+
setBusy(false);
|
|
16548
|
+
}
|
|
16549
|
+
},
|
|
16550
|
+
[]
|
|
16551
|
+
);
|
|
16552
|
+
return { error, busy, run };
|
|
16553
|
+
}
|
|
16554
|
+
function ActionError({ message }) {
|
|
16555
|
+
if (!message) return null;
|
|
16556
|
+
return /* @__PURE__ */ jsx("p", { role: "alert", className: "text-sm text-destructive", children: message });
|
|
16557
|
+
}
|
|
16558
|
+
function ExpiryNote({ label, expired }) {
|
|
16559
|
+
return /* @__PURE__ */ jsx(
|
|
16560
|
+
"p",
|
|
16561
|
+
{
|
|
16562
|
+
className: cn(
|
|
16563
|
+
"text-xs",
|
|
16564
|
+
expired ? "text-muted-foreground" : "text-foreground"
|
|
16565
|
+
),
|
|
16566
|
+
children: expired ? "The agent stopped waiting for this answer and moved on, so it can no longer be submitted." : `${label}. Deleting it will not answer the agent; it only clears it from your inbox.`
|
|
16567
|
+
}
|
|
16568
|
+
);
|
|
16569
|
+
}
|
|
16570
|
+
var OTHER_OPTION = "other";
|
|
16089
16571
|
function QuestionInput({
|
|
16090
|
-
|
|
16091
|
-
|
|
16572
|
+
question,
|
|
16573
|
+
draft,
|
|
16092
16574
|
disabled,
|
|
16093
|
-
|
|
16094
|
-
|
|
16575
|
+
onPickChoice,
|
|
16576
|
+
onChooseOther,
|
|
16577
|
+
onType
|
|
16095
16578
|
}) {
|
|
16579
|
+
const { prompt, choices, allowOther } = question;
|
|
16580
|
+
const typed = draft?.typed ?? "";
|
|
16581
|
+
const useOther = !!draft?.useOther;
|
|
16096
16582
|
if (choices && choices.length > 0) {
|
|
16097
|
-
|
|
16098
|
-
|
|
16099
|
-
{
|
|
16100
|
-
"aria-label": prompt,
|
|
16101
|
-
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
16102
|
-
disabled,
|
|
16103
|
-
value,
|
|
16104
|
-
onChange: (event) => onChange(event.target.value),
|
|
16105
|
-
children: [
|
|
16106
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Select" }),
|
|
16107
|
-
choices.map((choice) => /* @__PURE__ */ jsx("option", { value: choice.label, children: choice.label }, choice.label))
|
|
16108
|
-
]
|
|
16109
|
-
}
|
|
16583
|
+
const pickedIndex = choices.findIndex(
|
|
16584
|
+
(choice) => choice.label === draft?.picked
|
|
16110
16585
|
);
|
|
16586
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
16587
|
+
/* @__PURE__ */ jsxs(
|
|
16588
|
+
"select",
|
|
16589
|
+
{
|
|
16590
|
+
"aria-label": prompt,
|
|
16591
|
+
className: FIELD_CLASS,
|
|
16592
|
+
disabled,
|
|
16593
|
+
value: useOther ? OTHER_OPTION : pickedIndex >= 0 ? String(pickedIndex) : "",
|
|
16594
|
+
onChange: (event) => {
|
|
16595
|
+
const value = event.target.value;
|
|
16596
|
+
if (value === OTHER_OPTION) {
|
|
16597
|
+
onChooseOther();
|
|
16598
|
+
return;
|
|
16599
|
+
}
|
|
16600
|
+
const choice = choices[Number(value)];
|
|
16601
|
+
if (choice) onPickChoice(choice.label);
|
|
16602
|
+
},
|
|
16603
|
+
children: [
|
|
16604
|
+
/* @__PURE__ */ jsx("option", { value: "", disabled: true, children: "Select" }),
|
|
16605
|
+
choices.map((choice, index) => /* @__PURE__ */ jsx("option", { value: String(index), children: choice.label }, choice.label)),
|
|
16606
|
+
allowOther && /* @__PURE__ */ jsx("option", { value: OTHER_OPTION, children: "Other" })
|
|
16607
|
+
]
|
|
16608
|
+
}
|
|
16609
|
+
),
|
|
16610
|
+
useOther && /* @__PURE__ */ jsx(
|
|
16611
|
+
"input",
|
|
16612
|
+
{
|
|
16613
|
+
"aria-label": `${prompt} (other)`,
|
|
16614
|
+
className: FIELD_CLASS,
|
|
16615
|
+
placeholder: "Type your answer\u2026",
|
|
16616
|
+
disabled,
|
|
16617
|
+
value: typed,
|
|
16618
|
+
onChange: (event) => onType(event.target.value)
|
|
16619
|
+
}
|
|
16620
|
+
)
|
|
16621
|
+
] });
|
|
16111
16622
|
}
|
|
16112
16623
|
return /* @__PURE__ */ jsx(
|
|
16113
16624
|
"input",
|
|
16114
16625
|
{
|
|
16115
16626
|
"aria-label": prompt,
|
|
16116
|
-
className:
|
|
16627
|
+
className: FIELD_CLASS,
|
|
16117
16628
|
disabled,
|
|
16118
|
-
value,
|
|
16119
|
-
onChange: (event) =>
|
|
16629
|
+
value: typed,
|
|
16630
|
+
onChange: (event) => onType(event.target.value)
|
|
16120
16631
|
}
|
|
16121
16632
|
);
|
|
16122
16633
|
}
|
|
@@ -16127,47 +16638,45 @@ function InboxDetailActions({
|
|
|
16127
16638
|
onSessionSelect,
|
|
16128
16639
|
children
|
|
16129
16640
|
}) {
|
|
16130
|
-
const
|
|
16641
|
+
const { error, busy: deleting, run } = useAction();
|
|
16131
16642
|
async function deleteItem() {
|
|
16132
16643
|
if (!adapter.deleteInboxItem) return;
|
|
16133
|
-
|
|
16134
|
-
try {
|
|
16135
|
-
await onDeleted(item.id);
|
|
16136
|
-
} finally {
|
|
16137
|
-
setDeleting(false);
|
|
16138
|
-
}
|
|
16644
|
+
await run("Failed to delete", () => onDeleted(item.id));
|
|
16139
16645
|
}
|
|
16140
|
-
return /* @__PURE__ */ jsxs("div", { className: "
|
|
16141
|
-
/* @__PURE__ */
|
|
16142
|
-
|
|
16143
|
-
|
|
16144
|
-
|
|
16145
|
-
|
|
16146
|
-
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16161
|
-
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16167
|
-
|
|
16168
|
-
|
|
16169
|
-
|
|
16170
|
-
|
|
16646
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
16647
|
+
/* @__PURE__ */ jsx(ActionError, { message: error }),
|
|
16648
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
16649
|
+
/* @__PURE__ */ jsxs(
|
|
16650
|
+
Button,
|
|
16651
|
+
{
|
|
16652
|
+
type: "button",
|
|
16653
|
+
size: "sm",
|
|
16654
|
+
onClick: () => onSessionSelect?.(item.sessionId, item),
|
|
16655
|
+
"aria-label": "Open session",
|
|
16656
|
+
children: [
|
|
16657
|
+
/* @__PURE__ */ jsx(ExternalLinkIcon, { className: "size-3.5" }),
|
|
16658
|
+
"Open session"
|
|
16659
|
+
]
|
|
16660
|
+
}
|
|
16661
|
+
),
|
|
16662
|
+
children,
|
|
16663
|
+
adapter.deleteInboxItem && /* @__PURE__ */ jsxs(
|
|
16664
|
+
Button,
|
|
16665
|
+
{
|
|
16666
|
+
type: "button",
|
|
16667
|
+
size: "sm",
|
|
16668
|
+
variant: "ghost",
|
|
16669
|
+
onClick: () => void deleteItem(),
|
|
16670
|
+
disabled: deleting,
|
|
16671
|
+
"aria-label": "Delete inbox item",
|
|
16672
|
+
title: "Delete inbox item",
|
|
16673
|
+
children: [
|
|
16674
|
+
/* @__PURE__ */ jsx(Trash2Icon, { className: "size-3.5" }),
|
|
16675
|
+
deleting ? "Deleting" : "Delete"
|
|
16676
|
+
]
|
|
16677
|
+
}
|
|
16678
|
+
)
|
|
16679
|
+
] })
|
|
16171
16680
|
] });
|
|
16172
16681
|
}
|
|
16173
16682
|
function InputRequiredDetail({
|
|
@@ -16177,36 +16686,31 @@ function InputRequiredDetail({
|
|
|
16177
16686
|
onDeleted,
|
|
16178
16687
|
onSessionSelect
|
|
16179
16688
|
}) {
|
|
16180
|
-
const questions =
|
|
16181
|
-
|
|
16182
|
-
|
|
16183
|
-
|
|
16184
|
-
|
|
16185
|
-
|
|
16186
|
-
|
|
16187
|
-
|
|
16188
|
-
|
|
16189
|
-
|
|
16190
|
-
const canSubmit = questions.every((question) =>
|
|
16689
|
+
const questions = useMemo(() => parseInboxQuestions(item), [item]);
|
|
16690
|
+
const [drafts, setDrafts] = useState({});
|
|
16691
|
+
const { error, busy: submitting, run } = useAction();
|
|
16692
|
+
const { expired, label: expiryLabel } = useAnswerWindow(item.expiresAt);
|
|
16693
|
+
const pending = item.status === "pending" && !expired;
|
|
16694
|
+
const disabled = !pending || submitting;
|
|
16695
|
+
const editDraft = (prompt, patch) => setDrafts((current) => ({
|
|
16696
|
+
...current,
|
|
16697
|
+
[prompt]: { ...current[prompt], ...patch }
|
|
16698
|
+
}));
|
|
16699
|
+
const canSubmit = questions.length > 0 && questions.every((question) => answerText(question, drafts[question.prompt]));
|
|
16191
16700
|
async function submit() {
|
|
16192
16701
|
const toolCallId = typeof item.payload.tool_call_id === "string" ? item.payload.tool_call_id : "";
|
|
16193
16702
|
if (!toolCallId || !canSubmit) return;
|
|
16194
|
-
|
|
16195
|
-
|
|
16196
|
-
|
|
16197
|
-
|
|
16198
|
-
answer: answers[question.prompt]?.trim() ?? "",
|
|
16199
|
-
is_other: false
|
|
16200
|
-
}));
|
|
16703
|
+
await run("Failed to submit your answer", async () => {
|
|
16704
|
+
const responses = questions.map(
|
|
16705
|
+
(question) => buildInboxResponse(question, drafts[question.prompt])
|
|
16706
|
+
);
|
|
16201
16707
|
await adapter.submitAskUserQuestionResponse({
|
|
16202
16708
|
sessionId: item.sessionId,
|
|
16203
16709
|
toolCallId,
|
|
16204
16710
|
responses
|
|
16205
16711
|
});
|
|
16206
16712
|
onUpdated(await adapter.getInboxItem({ itemId: item.id }));
|
|
16207
|
-
}
|
|
16208
|
-
setSubmitting(false);
|
|
16209
|
-
}
|
|
16713
|
+
});
|
|
16210
16714
|
}
|
|
16211
16715
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
16212
16716
|
item.body && /* @__PURE__ */ jsx(InboxBody, { body: item.body, muted: true }),
|
|
@@ -16215,17 +16719,25 @@ function InputRequiredDetail({
|
|
|
16215
16719
|
/* @__PURE__ */ jsx(
|
|
16216
16720
|
QuestionInput,
|
|
16217
16721
|
{
|
|
16218
|
-
|
|
16219
|
-
|
|
16722
|
+
question,
|
|
16723
|
+
draft: drafts[question.prompt],
|
|
16220
16724
|
disabled,
|
|
16221
|
-
|
|
16222
|
-
|
|
16223
|
-
|
|
16224
|
-
|
|
16225
|
-
|
|
16725
|
+
onPickChoice: (label) => (
|
|
16726
|
+
// Dropping the typed value keeps an abandoned draft from
|
|
16727
|
+
// resurfacing if the user switches back to Other.
|
|
16728
|
+
editDraft(question.prompt, {
|
|
16729
|
+
picked: label,
|
|
16730
|
+
typed: "",
|
|
16731
|
+
useOther: false
|
|
16732
|
+
})
|
|
16733
|
+
),
|
|
16734
|
+
onChooseOther: () => editDraft(question.prompt, { useOther: true }),
|
|
16735
|
+
onType: (value) => editDraft(question.prompt, { typed: value })
|
|
16226
16736
|
}
|
|
16227
16737
|
)
|
|
16228
16738
|
] }, question.prompt)),
|
|
16739
|
+
item.status === "pending" && /* @__PURE__ */ jsx(ExpiryNote, { label: expiryLabel, expired }),
|
|
16740
|
+
/* @__PURE__ */ jsx(ActionError, { message: error }),
|
|
16229
16741
|
/* @__PURE__ */ jsx(
|
|
16230
16742
|
InboxDetailActions,
|
|
16231
16743
|
{
|
|
@@ -16255,8 +16767,11 @@ function AckDetail({
|
|
|
16255
16767
|
onDeleted,
|
|
16256
16768
|
onSessionSelect
|
|
16257
16769
|
}) {
|
|
16770
|
+
const { error, busy, run } = useAction();
|
|
16258
16771
|
async function acknowledge() {
|
|
16259
|
-
|
|
16772
|
+
await run("Failed to acknowledge", async () => {
|
|
16773
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
16774
|
+
});
|
|
16260
16775
|
}
|
|
16261
16776
|
const outcome = typeof item.payload.outcome === "string" ? item.payload.outcome : "";
|
|
16262
16777
|
const duration = typeof item.payload.duration_seconds === "number" ? item.payload.duration_seconds : null;
|
|
@@ -16271,6 +16786,7 @@ function AckDetail({
|
|
|
16271
16786
|
duration,
|
|
16272
16787
|
" s)"
|
|
16273
16788
|
] }),
|
|
16789
|
+
/* @__PURE__ */ jsx(ActionError, { message: error }),
|
|
16274
16790
|
/* @__PURE__ */ jsx(
|
|
16275
16791
|
InboxDetailActions,
|
|
16276
16792
|
{
|
|
@@ -16284,10 +16800,11 @@ function AckDetail({
|
|
|
16284
16800
|
type: "button",
|
|
16285
16801
|
size: "sm",
|
|
16286
16802
|
onClick: () => void acknowledge(),
|
|
16803
|
+
disabled: busy,
|
|
16287
16804
|
"aria-label": "Acknowledge inbox item",
|
|
16288
16805
|
children: [
|
|
16289
16806
|
/* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5" }),
|
|
16290
|
-
"Acknowledge"
|
|
16807
|
+
busy ? "Saving" : "Acknowledge"
|
|
16291
16808
|
]
|
|
16292
16809
|
}
|
|
16293
16810
|
)
|
|
@@ -16302,15 +16819,18 @@ function GovernanceDetail({
|
|
|
16302
16819
|
onDeleted,
|
|
16303
16820
|
onSessionSelect
|
|
16304
16821
|
}) {
|
|
16822
|
+
const { error, busy, run } = useAction();
|
|
16305
16823
|
async function decide(decision) {
|
|
16306
|
-
|
|
16307
|
-
|
|
16308
|
-
|
|
16824
|
+
await run(`Failed to ${decision}`, async () => {
|
|
16825
|
+
onUpdated(
|
|
16826
|
+
await adapter.respondGovernanceInboxItem({ itemId: item.id, decision })
|
|
16827
|
+
);
|
|
16828
|
+
});
|
|
16309
16829
|
}
|
|
16310
16830
|
const toolName = typeof item.payload.tool_name === "string" ? item.payload.tool_name : "tool";
|
|
16311
16831
|
const args = typeof item.payload.arguments_excerpt === "string" ? item.payload.arguments_excerpt : "";
|
|
16312
16832
|
const reason = typeof item.payload.deny_reason === "string" ? item.payload.deny_reason : "";
|
|
16313
|
-
const disabled = item.status !== "pending";
|
|
16833
|
+
const disabled = item.status !== "pending" || busy;
|
|
16314
16834
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
16315
16835
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
16316
16836
|
/* @__PURE__ */ jsxs("div", { className: "text-base font-semibold text-foreground", children: [
|
|
@@ -16321,6 +16841,7 @@ function GovernanceDetail({
|
|
|
16321
16841
|
reason && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: reason })
|
|
16322
16842
|
] }),
|
|
16323
16843
|
args && /* @__PURE__ */ jsx("pre", { className: "overflow-x-auto border border-line bg-muted p-3 text-xs", children: args }),
|
|
16844
|
+
/* @__PURE__ */ jsx(ActionError, { message: error }),
|
|
16324
16845
|
/* @__PURE__ */ jsxs(
|
|
16325
16846
|
InboxDetailActions,
|
|
16326
16847
|
{
|
|
@@ -16362,23 +16883,22 @@ function ActionRequiredDetail({
|
|
|
16362
16883
|
onDeleted,
|
|
16363
16884
|
onSessionSelect
|
|
16364
16885
|
}) {
|
|
16365
|
-
const
|
|
16886
|
+
const { error, busy: submitting, run } = useAction();
|
|
16366
16887
|
const actionType = typeof item.payload.action_type === "string" ? item.payload.action_type : "";
|
|
16367
16888
|
const context = typeof item.payload.context === "string" ? item.payload.context : "";
|
|
16368
16889
|
const disabled = item.status !== "pending" || submitting || !adapter.respondActionRequiredInboxItem;
|
|
16369
16890
|
async function complete() {
|
|
16370
|
-
|
|
16371
|
-
|
|
16372
|
-
|
|
16373
|
-
onUpdated(await
|
|
16374
|
-
}
|
|
16375
|
-
setSubmitting(false);
|
|
16376
|
-
}
|
|
16891
|
+
const respond = adapter.respondActionRequiredInboxItem;
|
|
16892
|
+
if (!respond) return;
|
|
16893
|
+
await run("Failed to mark the action complete", async () => {
|
|
16894
|
+
onUpdated(await respond({ itemId: item.id }));
|
|
16895
|
+
});
|
|
16377
16896
|
}
|
|
16378
16897
|
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
16379
16898
|
item.body && /* @__PURE__ */ jsx(InboxBody, { body: item.body }),
|
|
16380
16899
|
context && context !== item.body && /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: context }),
|
|
16381
16900
|
actionType && /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: actionType }),
|
|
16901
|
+
/* @__PURE__ */ jsx(ActionError, { message: error }),
|
|
16382
16902
|
/* @__PURE__ */ jsx(
|
|
16383
16903
|
InboxDetailActions,
|
|
16384
16904
|
{
|
|
@@ -16411,8 +16931,11 @@ function ProgressDetail({
|
|
|
16411
16931
|
onDeleted,
|
|
16412
16932
|
onSessionSelect
|
|
16413
16933
|
}) {
|
|
16934
|
+
const { error, busy, run } = useAction();
|
|
16414
16935
|
async function acknowledge() {
|
|
16415
|
-
|
|
16936
|
+
await run("Failed to acknowledge", async () => {
|
|
16937
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
16938
|
+
});
|
|
16416
16939
|
}
|
|
16417
16940
|
const rows = [];
|
|
16418
16941
|
for (const row of [
|
|
@@ -16430,6 +16953,7 @@ function ProgressDetail({
|
|
|
16430
16953
|
/* @__PURE__ */ jsx("dt", { className: "w-20 shrink-0", children: String(label) }),
|
|
16431
16954
|
/* @__PURE__ */ jsx("dd", { className: "min-w-0", children: String(value) })
|
|
16432
16955
|
] }, String(label))) }),
|
|
16956
|
+
/* @__PURE__ */ jsx(ActionError, { message: error }),
|
|
16433
16957
|
/* @__PURE__ */ jsx(
|
|
16434
16958
|
InboxDetailActions,
|
|
16435
16959
|
{
|
|
@@ -16443,10 +16967,11 @@ function ProgressDetail({
|
|
|
16443
16967
|
type: "button",
|
|
16444
16968
|
size: "sm",
|
|
16445
16969
|
onClick: () => void acknowledge(),
|
|
16970
|
+
disabled: busy,
|
|
16446
16971
|
"aria-label": "Acknowledge inbox item",
|
|
16447
16972
|
children: [
|
|
16448
16973
|
/* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5" }),
|
|
16449
|
-
"Acknowledge"
|
|
16974
|
+
busy ? "Saving" : "Acknowledge"
|
|
16450
16975
|
]
|
|
16451
16976
|
}
|
|
16452
16977
|
)
|
|
@@ -16459,10 +16984,23 @@ function InboxDetail({
|
|
|
16459
16984
|
adapter,
|
|
16460
16985
|
onUpdated,
|
|
16461
16986
|
onDeleted,
|
|
16462
|
-
onSessionSelect
|
|
16987
|
+
onSessionSelect,
|
|
16988
|
+
onBack
|
|
16463
16989
|
}) {
|
|
16464
16990
|
const Icon = kindIcon(item.kind);
|
|
16465
|
-
return /* @__PURE__ */ jsxs("section", { className: "min-w-0 flex-1 overflow-y-auto p-6", children: [
|
|
16991
|
+
return /* @__PURE__ */ jsxs("section", { className: "min-w-0 flex-1 overflow-y-auto p-4 md:p-6", children: [
|
|
16992
|
+
onBack && /* @__PURE__ */ jsxs(
|
|
16993
|
+
"button",
|
|
16994
|
+
{
|
|
16995
|
+
type: "button",
|
|
16996
|
+
onClick: onBack,
|
|
16997
|
+
className: "-ml-2 mb-2 inline-flex min-h-11 items-center gap-1.5 px-2 text-sm font-medium text-muted-foreground md:hidden",
|
|
16998
|
+
children: [
|
|
16999
|
+
/* @__PURE__ */ jsx(ChevronLeftIcon, { className: "size-4" }),
|
|
17000
|
+
"Inbox"
|
|
17001
|
+
]
|
|
17002
|
+
}
|
|
17003
|
+
),
|
|
16466
17004
|
/* @__PURE__ */ jsxs("div", { className: "mb-5 flex min-w-0 items-start gap-3", children: [
|
|
16467
17005
|
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex size-8 shrink-0 items-center justify-center border border-line text-muted-foreground", children: /* @__PURE__ */ jsx(Icon, { className: "size-4" }) }),
|
|
16468
17006
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
@@ -16534,6 +17072,7 @@ function InboxPanel({
|
|
|
16534
17072
|
const [items, setItems] = useState([]);
|
|
16535
17073
|
const [nextCursor, setNextCursor] = useState(null);
|
|
16536
17074
|
const [internalSelectedId, setInternalSelectedId] = useState(null);
|
|
17075
|
+
const [view, setView] = useState("active");
|
|
16537
17076
|
const [loading, setLoading] = useState(false);
|
|
16538
17077
|
const [error, setError] = useState(null);
|
|
16539
17078
|
const requestId = useRef(0);
|
|
@@ -16542,13 +17081,20 @@ function InboxPanel({
|
|
|
16542
17081
|
() => items.find((item) => item.id === selectedItemId) ?? null,
|
|
16543
17082
|
[items, selectedItemId]
|
|
16544
17083
|
);
|
|
17084
|
+
const viewRef = useRef(view);
|
|
17085
|
+
viewRef.current = view;
|
|
16545
17086
|
const applyItem = useCallback((nextItem) => {
|
|
16546
|
-
setItems(
|
|
16547
|
-
(current) =>
|
|
16548
|
-
|
|
16549
|
-
|
|
16550
|
-
|
|
17087
|
+
setItems((current) => {
|
|
17088
|
+
if (current.some((item) => item.id === nextItem.id)) {
|
|
17089
|
+
return sortItems([nextItem, ...current]);
|
|
17090
|
+
}
|
|
17091
|
+
return belongsToView(nextItem, viewRef.current) ? sortItems([nextItem, ...current]) : current;
|
|
17092
|
+
});
|
|
16551
17093
|
}, []);
|
|
17094
|
+
const clearSelection = useCallback(() => {
|
|
17095
|
+
if (selectedId === void 0) setInternalSelectedId(null);
|
|
17096
|
+
onSelectedIdChange?.(null);
|
|
17097
|
+
}, [onSelectedIdChange, selectedId]);
|
|
16552
17098
|
const selectItem = useCallback(
|
|
16553
17099
|
async (itemId) => {
|
|
16554
17100
|
if (selectedId === void 0) setInternalSelectedId(itemId);
|
|
@@ -16565,26 +17111,26 @@ function InboxPanel({
|
|
|
16565
17111
|
async (cursor) => {
|
|
16566
17112
|
const id = ++requestId.current;
|
|
16567
17113
|
setLoading(true);
|
|
16568
|
-
setError(null);
|
|
16569
17114
|
try {
|
|
16570
17115
|
const response = await inboxAdapter.listInbox({
|
|
17116
|
+
status: VIEWS[view].statuses,
|
|
17117
|
+
kind: VIEWS[view].kinds,
|
|
16571
17118
|
cursor: cursor ?? void 0,
|
|
16572
17119
|
limit
|
|
16573
17120
|
});
|
|
16574
17121
|
if (id !== requestId.current) return;
|
|
16575
|
-
setItems(
|
|
16576
|
-
(current) => sortItems(cursor ? [...current, ...response.items] : response.items)
|
|
16577
|
-
);
|
|
17122
|
+
setItems((current) => sortItems([...response.items, ...current]));
|
|
16578
17123
|
setNextCursor(response.nextCursor);
|
|
17124
|
+
setError(null);
|
|
16579
17125
|
} catch (err) {
|
|
16580
17126
|
if (id === requestId.current) {
|
|
16581
|
-
setError(err
|
|
17127
|
+
setError(errorMessage(err, "Failed to load inbox"));
|
|
16582
17128
|
}
|
|
16583
17129
|
} finally {
|
|
16584
17130
|
if (id === requestId.current) setLoading(false);
|
|
16585
17131
|
}
|
|
16586
17132
|
},
|
|
16587
|
-
[inboxAdapter, limit]
|
|
17133
|
+
[inboxAdapter, limit, view]
|
|
16588
17134
|
);
|
|
16589
17135
|
useEffect(() => {
|
|
16590
17136
|
void load(null);
|
|
@@ -16592,15 +17138,33 @@ function InboxPanel({
|
|
|
16592
17138
|
useEffect(() => {
|
|
16593
17139
|
const stream = inboxAdapter.openInboxStream();
|
|
16594
17140
|
stream.addEventListener("item", (event) => {
|
|
16595
|
-
|
|
16596
|
-
|
|
16597
|
-
|
|
17141
|
+
let itemId;
|
|
17142
|
+
try {
|
|
17143
|
+
itemId = JSON.parse(event.data).item_id;
|
|
17144
|
+
} catch {
|
|
17145
|
+
return;
|
|
17146
|
+
}
|
|
17147
|
+
if (typeof itemId !== "number") return;
|
|
17148
|
+
if (viewRef.current === "history") return;
|
|
17149
|
+
void inboxAdapter.getInboxItem({ itemId }).then(applyItem, () => void 0);
|
|
16598
17150
|
});
|
|
16599
17151
|
return () => stream.close();
|
|
16600
17152
|
}, [applyItem, inboxAdapter]);
|
|
16601
17153
|
function updateSelectedItem(item) {
|
|
16602
17154
|
applyItem(item);
|
|
16603
17155
|
}
|
|
17156
|
+
const selectView = useCallback(
|
|
17157
|
+
(next) => {
|
|
17158
|
+
if (next === view) return;
|
|
17159
|
+
setView(next);
|
|
17160
|
+
setItems([]);
|
|
17161
|
+
setNextCursor(null);
|
|
17162
|
+
setError(null);
|
|
17163
|
+
if (selectedId === void 0) setInternalSelectedId(null);
|
|
17164
|
+
onSelectedIdChange?.(null);
|
|
17165
|
+
},
|
|
17166
|
+
[onSelectedIdChange, selectedId, view]
|
|
17167
|
+
);
|
|
16604
17168
|
const deleteItem = useCallback(
|
|
16605
17169
|
async (itemId) => {
|
|
16606
17170
|
if (!inboxAdapter.deleteInboxItem) return;
|
|
@@ -16614,55 +17178,80 @@ function InboxPanel({
|
|
|
16614
17178
|
[inboxAdapter, onSelectedIdChange, selectedId, selectedItemId]
|
|
16615
17179
|
);
|
|
16616
17180
|
return /* @__PURE__ */ jsxs("div", { className: "flex h-full min-h-0 min-w-0 bg-background text-foreground", children: [
|
|
16617
|
-
/* @__PURE__ */ jsxs(
|
|
16618
|
-
|
|
16619
|
-
|
|
16620
|
-
|
|
16621
|
-
|
|
16622
|
-
|
|
16623
|
-
|
|
16624
|
-
|
|
16625
|
-
|
|
16626
|
-
|
|
16627
|
-
|
|
16628
|
-
|
|
17181
|
+
/* @__PURE__ */ jsxs(
|
|
17182
|
+
"aside",
|
|
17183
|
+
{
|
|
17184
|
+
className: cn(
|
|
17185
|
+
"flex min-w-0 flex-col border-line md:w-80 md:min-w-72 md:max-w-sm md:shrink-0 md:border-r",
|
|
17186
|
+
selectedItemId != null ? "hidden md:flex" : "w-full"
|
|
17187
|
+
),
|
|
17188
|
+
children: [
|
|
17189
|
+
!hideHeader && /* @__PURE__ */ jsxs("div", { className: "flex min-h-14 items-center gap-2 border-b border-line px-4", children: [
|
|
17190
|
+
/* @__PURE__ */ jsx(InboxIcon, { className: "size-4 text-muted-foreground" }),
|
|
17191
|
+
/* @__PURE__ */ jsx("h1", { className: "font-semibold", children: title })
|
|
17192
|
+
] }),
|
|
17193
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b border-line px-2 py-2", children: VIEW_NAMES.map((value) => /* @__PURE__ */ jsx(
|
|
16629
17194
|
"button",
|
|
16630
17195
|
{
|
|
16631
17196
|
type: "button",
|
|
16632
|
-
|
|
17197
|
+
onClick: () => selectView(value),
|
|
16633
17198
|
className: cn(
|
|
16634
|
-
"
|
|
16635
|
-
|
|
16636
|
-
!item.readAt && "font-medium text-foreground"
|
|
17199
|
+
"px-2 py-1 text-xs capitalize transition-colors",
|
|
17200
|
+
view === value ? "bg-line font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
16637
17201
|
),
|
|
16638
|
-
|
|
16639
|
-
children: [
|
|
16640
|
-
/* @__PURE__ */ jsx(Icon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
16641
|
-
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
16642
|
-
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
16643
|
-
/* @__PURE__ */ jsx("span", { className: "truncate text-xs uppercase tracking-wide text-muted-foreground", children: kindLabel(item.kind) }),
|
|
16644
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatRelative(item.createdAt) })
|
|
16645
|
-
] }),
|
|
16646
|
-
/* @__PURE__ */ jsx("span", { className: "mt-1 block truncate text-sm", children: item.title }),
|
|
16647
|
-
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: statusLabel(item.status) })
|
|
16648
|
-
] })
|
|
16649
|
-
]
|
|
17202
|
+
children: value
|
|
16650
17203
|
},
|
|
16651
|
-
|
|
16652
|
-
)
|
|
16653
|
-
|
|
16654
|
-
|
|
16655
|
-
|
|
16656
|
-
|
|
16657
|
-
|
|
16658
|
-
className: "
|
|
16659
|
-
|
|
16660
|
-
|
|
16661
|
-
|
|
16662
|
-
|
|
16663
|
-
|
|
16664
|
-
|
|
16665
|
-
|
|
17204
|
+
value
|
|
17205
|
+
)) }),
|
|
17206
|
+
/* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
|
|
17207
|
+
error && items.length === 0 && /* @__PURE__ */ jsxs("div", { className: "space-y-2 px-4 py-3", children: [
|
|
17208
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
17209
|
+
/* @__PURE__ */ jsx(Button, { type: "button", size: "sm", onClick: () => void load(null), children: "Try again" })
|
|
17210
|
+
] }),
|
|
17211
|
+
items.length === 0 && !error && !loading && /* @__PURE__ */ jsx("div", { className: "px-4 py-8 text-sm text-muted-foreground", children: VIEWS[view].empty }),
|
|
17212
|
+
items.map((item) => {
|
|
17213
|
+
const Icon = kindIcon(item.kind);
|
|
17214
|
+
const selected = item.id === selectedItemId;
|
|
17215
|
+
return /* @__PURE__ */ jsxs(
|
|
17216
|
+
"button",
|
|
17217
|
+
{
|
|
17218
|
+
type: "button",
|
|
17219
|
+
"aria-label": `Open inbox item ${item.title}`,
|
|
17220
|
+
className: cn(
|
|
17221
|
+
"flex w-full min-w-0 gap-3 border-l-2 px-3 py-3 text-left transition-colors hover:bg-input",
|
|
17222
|
+
selected ? "border-l-primary bg-line text-foreground" : "border-l-transparent text-subtle",
|
|
17223
|
+
!item.readAt && "font-medium text-foreground"
|
|
17224
|
+
),
|
|
17225
|
+
onClick: () => void selectItem(item.id),
|
|
17226
|
+
children: [
|
|
17227
|
+
/* @__PURE__ */ jsx(Icon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
17228
|
+
/* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
17229
|
+
/* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
17230
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-xs uppercase tracking-wide text-muted-foreground", children: kindLabel(item.kind) }),
|
|
17231
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatRelative(item.createdAt) })
|
|
17232
|
+
] }),
|
|
17233
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block truncate text-sm", children: item.title }),
|
|
17234
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: statusLabel(item.status) })
|
|
17235
|
+
] })
|
|
17236
|
+
]
|
|
17237
|
+
},
|
|
17238
|
+
item.id
|
|
17239
|
+
);
|
|
17240
|
+
}),
|
|
17241
|
+
nextCursor && /* @__PURE__ */ jsx(
|
|
17242
|
+
"button",
|
|
17243
|
+
{
|
|
17244
|
+
type: "button",
|
|
17245
|
+
className: "w-full px-3 py-3 text-sm text-muted-foreground hover:bg-input",
|
|
17246
|
+
onClick: () => void load(nextCursor),
|
|
17247
|
+
disabled: loading,
|
|
17248
|
+
children: loading ? "Loading" : "Load more"
|
|
17249
|
+
}
|
|
17250
|
+
)
|
|
17251
|
+
] })
|
|
17252
|
+
]
|
|
17253
|
+
}
|
|
17254
|
+
),
|
|
16666
17255
|
selectedItem ? /* @__PURE__ */ jsx(
|
|
16667
17256
|
InboxDetail,
|
|
16668
17257
|
{
|
|
@@ -16670,59 +17259,59 @@ function InboxPanel({
|
|
|
16670
17259
|
adapter: inboxAdapter,
|
|
16671
17260
|
onUpdated: updateSelectedItem,
|
|
16672
17261
|
onDeleted: deleteItem,
|
|
16673
|
-
onSessionSelect
|
|
16674
|
-
|
|
16675
|
-
|
|
17262
|
+
onSessionSelect,
|
|
17263
|
+
onBack: clearSelection
|
|
17264
|
+
},
|
|
17265
|
+
selectedItem.id
|
|
17266
|
+
) : /* @__PURE__ */ jsx("main", { className: "hidden min-w-0 flex-1 items-center justify-center px-4 text-sm text-muted-foreground md:flex", children: "Select an item" })
|
|
16676
17267
|
] });
|
|
16677
17268
|
}
|
|
16678
17269
|
function useInboxUnreadCount(adapter) {
|
|
16679
17270
|
const [unreadCount, setUnreadCount] = useState(0);
|
|
16680
17271
|
const [hasLoaded, setHasLoaded] = useState(false);
|
|
16681
17272
|
const [error, setError] = useState(null);
|
|
17273
|
+
const requestId = useRef(0);
|
|
17274
|
+
const listInbox = adapter.listInbox;
|
|
17275
|
+
const openInboxStream = adapter.openInboxStream;
|
|
17276
|
+
const refetch = useCallback(async () => {
|
|
17277
|
+
if (!listInbox) return;
|
|
17278
|
+
const id = ++requestId.current;
|
|
17279
|
+
try {
|
|
17280
|
+
const response = await listInbox({ status: "pending", limit: 200 });
|
|
17281
|
+
if (id !== requestId.current) return;
|
|
17282
|
+
setUnreadCount(response.items.filter((item) => !item.readAt).length);
|
|
17283
|
+
setError(null);
|
|
17284
|
+
} catch (err) {
|
|
17285
|
+
if (id !== requestId.current) return;
|
|
17286
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
17287
|
+
} finally {
|
|
17288
|
+
if (id === requestId.current) setHasLoaded(true);
|
|
17289
|
+
}
|
|
17290
|
+
}, [listInbox]);
|
|
16682
17291
|
useEffect(() => {
|
|
16683
|
-
|
|
16684
|
-
let stream = null;
|
|
16685
|
-
if (!adapter.listInbox || !adapter.openInboxStream) {
|
|
17292
|
+
if (!listInbox || !openInboxStream) {
|
|
16686
17293
|
setError("Inbox is not supported by this adapter.");
|
|
16687
17294
|
setHasLoaded(true);
|
|
16688
|
-
return
|
|
16689
|
-
cancelled = true;
|
|
16690
|
-
};
|
|
17295
|
+
return;
|
|
16691
17296
|
}
|
|
16692
|
-
|
|
16693
|
-
|
|
16694
|
-
setUnreadCount(response.items.filter((item) => !item.readAt).length);
|
|
16695
|
-
setHasLoaded(true);
|
|
16696
|
-
}).catch((err) => {
|
|
16697
|
-
if (cancelled) return;
|
|
16698
|
-
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
16699
|
-
setHasLoaded(true);
|
|
16700
|
-
});
|
|
17297
|
+
void refetch();
|
|
17298
|
+
let stream = null;
|
|
16701
17299
|
try {
|
|
16702
|
-
stream =
|
|
16703
|
-
|
|
16704
|
-
|
|
16705
|
-
|
|
16706
|
-
|
|
16707
|
-
setUnreadCount(ids.length);
|
|
16708
|
-
setHasLoaded(true);
|
|
16709
|
-
});
|
|
16710
|
-
stream.addEventListener("item", () => {
|
|
16711
|
-
if (cancelled) return;
|
|
16712
|
-
setUnreadCount((count) => count + 1);
|
|
16713
|
-
setHasLoaded(true);
|
|
16714
|
-
});
|
|
16715
|
-
stream.onerror = () => {
|
|
16716
|
-
if (!cancelled) setError("Inbox stream disconnected.");
|
|
16717
|
-
};
|
|
17300
|
+
stream = openInboxStream();
|
|
17301
|
+
const onChange = () => void refetch();
|
|
17302
|
+
stream.addEventListener("snapshot", onChange);
|
|
17303
|
+
stream.addEventListener("item", onChange);
|
|
17304
|
+
stream.onerror = () => setError("Inbox stream disconnected.");
|
|
16718
17305
|
} catch (err) {
|
|
16719
|
-
setError(
|
|
17306
|
+
setError(
|
|
17307
|
+
err instanceof Error ? err.message : "Failed to open inbox stream"
|
|
17308
|
+
);
|
|
16720
17309
|
}
|
|
16721
17310
|
return () => {
|
|
16722
|
-
|
|
17311
|
+
requestId.current += 1;
|
|
16723
17312
|
stream?.close();
|
|
16724
17313
|
};
|
|
16725
|
-
}, [
|
|
17314
|
+
}, [listInbox, openInboxStream, refetch]);
|
|
16726
17315
|
return { unreadCount, hasLoaded, error };
|
|
16727
17316
|
}
|
|
16728
17317
|
function Card({
|