@invergent/agent-chat-react 2.12.0 → 2.13.1
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.cjs
CHANGED
|
@@ -2504,7 +2504,7 @@ function TabsTrigger({
|
|
|
2504
2504
|
{
|
|
2505
2505
|
"data-slot": "tabs-trigger",
|
|
2506
2506
|
className: cn(
|
|
2507
|
-
"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",
|
|
2507
|
+
"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",
|
|
2508
2508
|
"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",
|
|
2509
2509
|
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
|
|
2510
2510
|
"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",
|
|
@@ -4291,21 +4291,162 @@ var OTHER_INDEX_OFFSET = 1e6;
|
|
|
4291
4291
|
function emptySelection() {
|
|
4292
4292
|
return { index: -1, other: "" };
|
|
4293
4293
|
}
|
|
4294
|
+
function hasChoices(question) {
|
|
4295
|
+
return (question.choices?.length ?? 0) > 0;
|
|
4296
|
+
}
|
|
4294
4297
|
function buildAnswer(q, sel) {
|
|
4295
4298
|
if (sel.index < 0) return null;
|
|
4296
4299
|
if (sel.index >= OTHER_INDEX_OFFSET) {
|
|
4297
4300
|
const text = sel.other.trim();
|
|
4298
4301
|
if (!text) return null;
|
|
4299
|
-
return { question: q.prompt, answer: text, is_other:
|
|
4302
|
+
return { question: q.prompt, answer: text, is_other: hasChoices(q) };
|
|
4300
4303
|
}
|
|
4301
4304
|
const choice = q.choices?.[sel.index];
|
|
4302
4305
|
if (!choice) return null;
|
|
4303
4306
|
return { question: q.prompt, answer: choice.label, is_other: false };
|
|
4304
4307
|
}
|
|
4305
|
-
function
|
|
4308
|
+
function askQuestionsOf(tc) {
|
|
4309
|
+
return parseArgs(tc.args)?.questions ?? [];
|
|
4310
|
+
}
|
|
4311
|
+
function isConversationalAsk(questions) {
|
|
4312
|
+
return questions.length === 1;
|
|
4313
|
+
}
|
|
4314
|
+
function conversationalAskAcceptsFreeText(question) {
|
|
4315
|
+
return !hasChoices(question) || question.allow_other !== false;
|
|
4316
|
+
}
|
|
4317
|
+
function promptEchoedInContent(content, prompt) {
|
|
4318
|
+
const normalize = (s) => s.replace(/[*_`]/g, "").replace(/\s+/g, " ").trim().toLowerCase();
|
|
4319
|
+
const body = normalize(content ?? "");
|
|
4320
|
+
const question = normalize(prompt);
|
|
4321
|
+
return !!body && !!question && body.endsWith(question);
|
|
4322
|
+
}
|
|
4323
|
+
function AskUserQuestionToolBlock({
|
|
4324
|
+
tc,
|
|
4325
|
+
assistantContent,
|
|
4326
|
+
viewMode = "simple"
|
|
4327
|
+
}) {
|
|
4328
|
+
const questions = react.useMemo(() => askQuestionsOf(tc), [tc.args]);
|
|
4329
|
+
if (questions.length === 0) {
|
|
4330
|
+
return /* @__PURE__ */ jsxRuntime.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" });
|
|
4331
|
+
}
|
|
4332
|
+
if (isConversationalAsk(questions)) {
|
|
4333
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4334
|
+
ConversationalAsk,
|
|
4335
|
+
{
|
|
4336
|
+
tc,
|
|
4337
|
+
question: questions[0],
|
|
4338
|
+
assistantContent
|
|
4339
|
+
}
|
|
4340
|
+
);
|
|
4341
|
+
}
|
|
4342
|
+
return /* @__PURE__ */ jsxRuntime.jsx(BatchAsk, { tc, questions, viewMode });
|
|
4343
|
+
}
|
|
4344
|
+
function ConversationalAsk({
|
|
4345
|
+
tc,
|
|
4346
|
+
question,
|
|
4347
|
+
assistantContent
|
|
4348
|
+
}) {
|
|
4349
|
+
const { adapter, sessionId } = useAgentChatAdapterContext();
|
|
4350
|
+
const [submitting, setSubmitting] = react.useState(false);
|
|
4351
|
+
const [error, setError] = react.useState(null);
|
|
4352
|
+
const answered = tc.askUserQuestionAnswers !== void 0;
|
|
4353
|
+
const pending = !answered && tc.status === "running";
|
|
4354
|
+
const choices = question.choices ?? [];
|
|
4355
|
+
const showPrompt = react.useMemo(
|
|
4356
|
+
() => !promptEchoedInContent(assistantContent, question.prompt),
|
|
4357
|
+
[assistantContent, question.prompt]
|
|
4358
|
+
);
|
|
4359
|
+
react.useEffect(() => {
|
|
4360
|
+
if (!pending || !sessionId) return;
|
|
4361
|
+
const handler = (e) => {
|
|
4362
|
+
if (e.key !== "Escape") return;
|
|
4363
|
+
e.preventDefault();
|
|
4364
|
+
void adapter.pauseSession({ sessionId }).catch(() => {
|
|
4365
|
+
});
|
|
4366
|
+
};
|
|
4367
|
+
window.addEventListener("keydown", handler);
|
|
4368
|
+
return () => window.removeEventListener("keydown", handler);
|
|
4369
|
+
}, [adapter, sessionId, pending]);
|
|
4370
|
+
const pick = react.useCallback(
|
|
4371
|
+
async (label) => {
|
|
4372
|
+
if (!sessionId || submitting) return;
|
|
4373
|
+
setError(null);
|
|
4374
|
+
setSubmitting(true);
|
|
4375
|
+
try {
|
|
4376
|
+
await adapter.submitAskUserQuestionResponse({
|
|
4377
|
+
sessionId,
|
|
4378
|
+
toolCallId: tc.id,
|
|
4379
|
+
responses: [
|
|
4380
|
+
{ question: question.prompt, answer: label, is_other: false }
|
|
4381
|
+
]
|
|
4382
|
+
});
|
|
4383
|
+
} catch (e) {
|
|
4384
|
+
setError(e instanceof Error ? e.message : "Submit failed.");
|
|
4385
|
+
setSubmitting(false);
|
|
4386
|
+
}
|
|
4387
|
+
},
|
|
4388
|
+
[adapter, sessionId, tc.id, question.prompt, submitting]
|
|
4389
|
+
);
|
|
4390
|
+
const stacked = choices.some((c) => !!c.description);
|
|
4391
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
4392
|
+
showPrompt && /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { children: question.prompt }),
|
|
4393
|
+
pending && choices.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4394
|
+
"div",
|
|
4395
|
+
{
|
|
4396
|
+
className: cn(
|
|
4397
|
+
stacked ? "flex flex-col gap-1.5" : "flex flex-wrap gap-2",
|
|
4398
|
+
"max-w-2xl"
|
|
4399
|
+
),
|
|
4400
|
+
role: "group",
|
|
4401
|
+
"aria-label": "Suggested answers",
|
|
4402
|
+
children: choices.map((choice, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4403
|
+
QuickReply,
|
|
4404
|
+
{
|
|
4405
|
+
choice,
|
|
4406
|
+
stacked,
|
|
4407
|
+
disabled: submitting,
|
|
4408
|
+
onSelect: () => void pick(choice.label)
|
|
4409
|
+
},
|
|
4410
|
+
i
|
|
4411
|
+
))
|
|
4412
|
+
}
|
|
4413
|
+
),
|
|
4414
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-destructive", children: error }),
|
|
4415
|
+
!answered && !pending && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs italic text-muted-foreground/70", children: "No answer recorded." })
|
|
4416
|
+
] });
|
|
4417
|
+
}
|
|
4418
|
+
function QuickReply({
|
|
4419
|
+
choice,
|
|
4420
|
+
stacked,
|
|
4421
|
+
disabled,
|
|
4422
|
+
onSelect
|
|
4423
|
+
}) {
|
|
4424
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4425
|
+
"button",
|
|
4426
|
+
{
|
|
4427
|
+
type: "button",
|
|
4428
|
+
disabled,
|
|
4429
|
+
onClick: onSelect,
|
|
4430
|
+
className: cn(
|
|
4431
|
+
"border border-border bg-background text-left text-foreground transition-colors",
|
|
4432
|
+
"hover:border-foreground/30 hover:bg-muted",
|
|
4433
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
4434
|
+
"disabled:cursor-not-allowed disabled:opacity-50",
|
|
4435
|
+
stacked ? "rounded-md px-3 py-2" : "rounded-full px-3.5 py-1.5 text-sm"
|
|
4436
|
+
),
|
|
4437
|
+
children: [
|
|
4438
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("block", stacked && "text-sm"), children: choice.label }),
|
|
4439
|
+
stacked && choice.description && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5 block text-xs text-muted-foreground", children: choice.description })
|
|
4440
|
+
]
|
|
4441
|
+
}
|
|
4442
|
+
);
|
|
4443
|
+
}
|
|
4444
|
+
function BatchAsk({
|
|
4445
|
+
tc,
|
|
4446
|
+
questions,
|
|
4447
|
+
viewMode
|
|
4448
|
+
}) {
|
|
4306
4449
|
const { adapter, sessionId } = useAgentChatAdapterContext();
|
|
4307
|
-
const args = react.useMemo(() => parseArgs(tc.args), [tc.args]);
|
|
4308
|
-
const questions = args?.questions ?? [];
|
|
4309
4450
|
const [active, setActive] = react.useState(0);
|
|
4310
4451
|
const [selections, setSelections] = react.useState(
|
|
4311
4452
|
() => questions.map(emptySelection)
|
|
@@ -4399,11 +4540,8 @@ function AskUserQuestionToolBlock({ tc }) {
|
|
|
4399
4540
|
window.addEventListener("keydown", handler);
|
|
4400
4541
|
return () => window.removeEventListener("keydown", handler);
|
|
4401
4542
|
}, [handleCancel, handleSubmit, locked]);
|
|
4402
|
-
if (questions.length === 0) {
|
|
4403
|
-
return /* @__PURE__ */ jsxRuntime.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" });
|
|
4404
|
-
}
|
|
4405
4543
|
if (locked) {
|
|
4406
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4544
|
+
return /* @__PURE__ */ jsxRuntime.jsx(BatchAskLocked, { tc, questions, viewMode });
|
|
4407
4545
|
}
|
|
4408
4546
|
const current = questions[active];
|
|
4409
4547
|
const currentSel = selections[active] ?? emptySelection();
|
|
@@ -4481,7 +4619,7 @@ function AskUserQuestionToolBlock({ tc }) {
|
|
|
4481
4619
|
] }),
|
|
4482
4620
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-border px-3 py-2", children: [
|
|
4483
4621
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 text-xs text-muted-foreground", children: [
|
|
4484
|
-
|
|
4622
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mr-2 tabular-nums", children: [
|
|
4485
4623
|
answeredCount,
|
|
4486
4624
|
" of ",
|
|
4487
4625
|
questions.length,
|
|
@@ -4490,11 +4628,11 @@ function AskUserQuestionToolBlock({ tc }) {
|
|
|
4490
4628
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/70", children: "Esc to cancel" }),
|
|
4491
4629
|
error && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-2 text-destructive", children: error })
|
|
4492
4630
|
] }),
|
|
4493
|
-
allAnswered
|
|
4631
|
+
allAnswered ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4494
4632
|
"button",
|
|
4495
4633
|
{
|
|
4496
4634
|
type: "button",
|
|
4497
|
-
disabled:
|
|
4635
|
+
disabled: submitting,
|
|
4498
4636
|
onClick: () => void handleSubmit(),
|
|
4499
4637
|
className: cn(
|
|
4500
4638
|
"shrink-0 rounded-md bg-primary px-4 py-1.5 text-sm font-medium text-primary-foreground transition-colors",
|
|
@@ -4604,9 +4742,10 @@ function Radio({ selected }) {
|
|
|
4604
4742
|
}
|
|
4605
4743
|
);
|
|
4606
4744
|
}
|
|
4607
|
-
function
|
|
4745
|
+
function BatchAskLocked({
|
|
4608
4746
|
tc,
|
|
4609
|
-
questions
|
|
4747
|
+
questions,
|
|
4748
|
+
viewMode
|
|
4610
4749
|
}) {
|
|
4611
4750
|
const answers = tc.askUserQuestionAnswers;
|
|
4612
4751
|
const byPrompt = new Map(
|
|
@@ -4631,7 +4770,7 @@ function AskUserQuestionLocked({
|
|
|
4631
4770
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-emerald-500", children: "\u2192" }),
|
|
4632
4771
|
" ",
|
|
4633
4772
|
a.answer,
|
|
4634
|
-
a.is_other && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-[10px] text-muted-foreground/70", children: "(other)" })
|
|
4773
|
+
a.is_other && viewMode === "expert" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "ml-1 text-[10px] text-muted-foreground/70", children: "(other)" })
|
|
4635
4774
|
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "ml-5 text-muted-foreground/60 italic", children: "\u2014 no answer \u2014" })
|
|
4636
4775
|
] }, i);
|
|
4637
4776
|
}) })
|
|
@@ -5036,6 +5175,8 @@ function CodeRunToolBlock({
|
|
|
5036
5175
|
function ToolCallBlock({
|
|
5037
5176
|
tc,
|
|
5038
5177
|
resolvedArtifactName,
|
|
5178
|
+
assistantContent,
|
|
5179
|
+
viewMode,
|
|
5039
5180
|
onFileSelect
|
|
5040
5181
|
}) {
|
|
5041
5182
|
if (tc.toolName.startsWith("mcp__")) {
|
|
@@ -5078,7 +5219,14 @@ function ToolCallBlock({
|
|
|
5078
5219
|
case "skill_manage":
|
|
5079
5220
|
return /* @__PURE__ */ jsxRuntime.jsx(SkillManageToolBlock, { tc });
|
|
5080
5221
|
case "ask_user_question":
|
|
5081
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5222
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5223
|
+
AskUserQuestionToolBlock,
|
|
5224
|
+
{
|
|
5225
|
+
tc,
|
|
5226
|
+
assistantContent,
|
|
5227
|
+
viewMode
|
|
5228
|
+
}
|
|
5229
|
+
);
|
|
5082
5230
|
case "create_artifact":
|
|
5083
5231
|
return /* @__PURE__ */ jsxRuntime.jsx(ArtifactToolBlock, { tc, resolvedName: resolvedArtifactName });
|
|
5084
5232
|
case "delegate_task":
|
|
@@ -5490,6 +5638,22 @@ function CommandEmpty({
|
|
|
5490
5638
|
}
|
|
5491
5639
|
);
|
|
5492
5640
|
}
|
|
5641
|
+
function CommandGroup({
|
|
5642
|
+
className,
|
|
5643
|
+
...props
|
|
5644
|
+
}) {
|
|
5645
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5646
|
+
cmdk.Command.Group,
|
|
5647
|
+
{
|
|
5648
|
+
"data-slot": "command-group",
|
|
5649
|
+
className: cn(
|
|
5650
|
+
"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",
|
|
5651
|
+
className
|
|
5652
|
+
),
|
|
5653
|
+
...props
|
|
5654
|
+
}
|
|
5655
|
+
);
|
|
5656
|
+
}
|
|
5493
5657
|
function CommandItem({
|
|
5494
5658
|
className,
|
|
5495
5659
|
children,
|
|
@@ -8391,13 +8555,20 @@ var PromptInputTextarea = ({
|
|
|
8391
8555
|
var PromptInputFooter = ({
|
|
8392
8556
|
className,
|
|
8393
8557
|
...props
|
|
8394
|
-
}) =>
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8558
|
+
}) => (
|
|
8559
|
+
// flex-wrap: the footer holds a left tools group (add, browser profile,
|
|
8560
|
+
// workspace) and a right group (view mode, send). On a phone those stop
|
|
8561
|
+
// fitting one line as soon as a profile or workspace chip appears, and a
|
|
8562
|
+
// non-wrapping row crushes the icons instead. Wrapping only engages when
|
|
8563
|
+
// the space is genuinely gone, so wider screens are unchanged.
|
|
8564
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
8565
|
+
InputGroupAddon,
|
|
8566
|
+
{
|
|
8567
|
+
align: "block-end",
|
|
8568
|
+
className: cn("flex-wrap justify-between gap-1", className),
|
|
8569
|
+
...props
|
|
8570
|
+
}
|
|
8571
|
+
)
|
|
8401
8572
|
);
|
|
8402
8573
|
var PromptInputTools = ({
|
|
8403
8574
|
className,
|
|
@@ -8511,44 +8682,6 @@ function splitComposerFiles(files) {
|
|
|
8511
8682
|
return { images, pending };
|
|
8512
8683
|
}
|
|
8513
8684
|
|
|
8514
|
-
// src/components/ui/popover.tsx
|
|
8515
|
-
init_utils();
|
|
8516
|
-
function Popover({
|
|
8517
|
-
...props
|
|
8518
|
-
}) {
|
|
8519
|
-
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Root, { "data-slot": "popover", ...props });
|
|
8520
|
-
}
|
|
8521
|
-
function PopoverTrigger({
|
|
8522
|
-
...props
|
|
8523
|
-
}) {
|
|
8524
|
-
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
8525
|
-
}
|
|
8526
|
-
function PopoverContent({
|
|
8527
|
-
className,
|
|
8528
|
-
align = "center",
|
|
8529
|
-
sideOffset = 4,
|
|
8530
|
-
...props
|
|
8531
|
-
}) {
|
|
8532
|
-
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
8533
|
-
radixUi.Popover.Content,
|
|
8534
|
-
{
|
|
8535
|
-
"data-slot": "popover-content",
|
|
8536
|
-
align,
|
|
8537
|
-
sideOffset,
|
|
8538
|
-
className: cn(
|
|
8539
|
-
"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",
|
|
8540
|
-
className
|
|
8541
|
-
),
|
|
8542
|
-
...props
|
|
8543
|
-
}
|
|
8544
|
-
) });
|
|
8545
|
-
}
|
|
8546
|
-
function PopoverAnchor({
|
|
8547
|
-
...props
|
|
8548
|
-
}) {
|
|
8549
|
-
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
8550
|
-
}
|
|
8551
|
-
|
|
8552
8685
|
// src/components/ui/progress.tsx
|
|
8553
8686
|
init_utils();
|
|
8554
8687
|
function Progress({
|
|
@@ -8588,24 +8721,26 @@ var ContextContext = react.createContext(null);
|
|
|
8588
8721
|
var useContextValue = () => {
|
|
8589
8722
|
const context = react.useContext(ContextContext);
|
|
8590
8723
|
if (!context) {
|
|
8591
|
-
throw new Error(
|
|
8724
|
+
throw new Error(
|
|
8725
|
+
"Context components must be used within ContextValueProvider"
|
|
8726
|
+
);
|
|
8592
8727
|
}
|
|
8593
8728
|
return context;
|
|
8594
8729
|
};
|
|
8595
|
-
var
|
|
8730
|
+
var ContextValueProvider = ({
|
|
8596
8731
|
usedTokens,
|
|
8597
8732
|
maxTokens,
|
|
8598
8733
|
usage,
|
|
8599
8734
|
modelId,
|
|
8600
|
-
|
|
8735
|
+
children
|
|
8601
8736
|
}) => {
|
|
8602
8737
|
const contextValue = react.useMemo(
|
|
8603
8738
|
() => ({ maxTokens, modelId, usage, usedTokens }),
|
|
8604
8739
|
[maxTokens, modelId, usage, usedTokens]
|
|
8605
8740
|
);
|
|
8606
|
-
return /* @__PURE__ */ jsxRuntime.jsx(ContextContext.Provider, { value: contextValue, children
|
|
8741
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ContextContext.Provider, { value: contextValue, children });
|
|
8607
8742
|
};
|
|
8608
|
-
var ContextIcon = () => {
|
|
8743
|
+
var ContextIcon = ({ className }) => {
|
|
8609
8744
|
const { usedTokens, maxTokens } = useContextValue();
|
|
8610
8745
|
const circumference = 2 * Math.PI * ICON_RADIUS;
|
|
8611
8746
|
const usedPercent = usedTokens / maxTokens;
|
|
@@ -8614,6 +8749,7 @@ var ContextIcon = () => {
|
|
|
8614
8749
|
"svg",
|
|
8615
8750
|
{
|
|
8616
8751
|
"aria-label": "Model context usage",
|
|
8752
|
+
className,
|
|
8617
8753
|
height: "20",
|
|
8618
8754
|
role: "img",
|
|
8619
8755
|
style: { color: "currentcolor" },
|
|
@@ -8652,19 +8788,6 @@ var ContextIcon = () => {
|
|
|
8652
8788
|
}
|
|
8653
8789
|
);
|
|
8654
8790
|
};
|
|
8655
|
-
var ContextTrigger = ({ children, ...props }) => {
|
|
8656
|
-
return /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: children ?? /* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", variant: "ghost", ...props, children: /* @__PURE__ */ jsxRuntime.jsx(ContextIcon, {}) }) });
|
|
8657
|
-
};
|
|
8658
|
-
var ContextContent = ({
|
|
8659
|
-
className,
|
|
8660
|
-
...props
|
|
8661
|
-
}) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
8662
|
-
PopoverContent,
|
|
8663
|
-
{
|
|
8664
|
-
className: cn("min-w-60 divide-y overflow-hidden p-0", className),
|
|
8665
|
-
...props
|
|
8666
|
-
}
|
|
8667
|
-
);
|
|
8668
8791
|
var ContextContentHeader = ({
|
|
8669
8792
|
children,
|
|
8670
8793
|
className,
|
|
@@ -8844,6 +8967,9 @@ var ContextCacheUsage = ({
|
|
|
8844
8967
|
);
|
|
8845
8968
|
};
|
|
8846
8969
|
|
|
8970
|
+
// src/components/chat/chat-composer.tsx
|
|
8971
|
+
init_utils();
|
|
8972
|
+
|
|
8847
8973
|
// src/components/ui/item.tsx
|
|
8848
8974
|
init_utils();
|
|
8849
8975
|
var itemVariants = classVarianceAuthority.cva(
|
|
@@ -8966,6 +9092,120 @@ function ItemActions({ className, ...props }) {
|
|
|
8966
9092
|
);
|
|
8967
9093
|
}
|
|
8968
9094
|
|
|
9095
|
+
// src/components/ui/popover.tsx
|
|
9096
|
+
init_utils();
|
|
9097
|
+
function Popover({
|
|
9098
|
+
...props
|
|
9099
|
+
}) {
|
|
9100
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Root, { "data-slot": "popover", ...props });
|
|
9101
|
+
}
|
|
9102
|
+
function PopoverTrigger({
|
|
9103
|
+
...props
|
|
9104
|
+
}) {
|
|
9105
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
|
|
9106
|
+
}
|
|
9107
|
+
function PopoverContent({
|
|
9108
|
+
className,
|
|
9109
|
+
align = "center",
|
|
9110
|
+
sideOffset = 4,
|
|
9111
|
+
...props
|
|
9112
|
+
}) {
|
|
9113
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9114
|
+
radixUi.Popover.Content,
|
|
9115
|
+
{
|
|
9116
|
+
"data-slot": "popover-content",
|
|
9117
|
+
align,
|
|
9118
|
+
sideOffset,
|
|
9119
|
+
className: cn(
|
|
9120
|
+
"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",
|
|
9121
|
+
className
|
|
9122
|
+
),
|
|
9123
|
+
...props
|
|
9124
|
+
}
|
|
9125
|
+
) });
|
|
9126
|
+
}
|
|
9127
|
+
function PopoverAnchor({
|
|
9128
|
+
...props
|
|
9129
|
+
}) {
|
|
9130
|
+
return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Anchor, { "data-slot": "popover-anchor", ...props });
|
|
9131
|
+
}
|
|
9132
|
+
var MOBILE_QUERY = "(max-width: 767px)";
|
|
9133
|
+
function subscribe(onChange) {
|
|
9134
|
+
const mql = window.matchMedia(MOBILE_QUERY);
|
|
9135
|
+
mql.addEventListener("change", onChange);
|
|
9136
|
+
return () => mql.removeEventListener("change", onChange);
|
|
9137
|
+
}
|
|
9138
|
+
function useIsMobile() {
|
|
9139
|
+
return react.useSyncExternalStore(
|
|
9140
|
+
subscribe,
|
|
9141
|
+
() => window.matchMedia(MOBILE_QUERY).matches,
|
|
9142
|
+
// No viewport when server-rendered; assume desktop, as the SDK does.
|
|
9143
|
+
() => false
|
|
9144
|
+
);
|
|
9145
|
+
}
|
|
9146
|
+
|
|
9147
|
+
// src/components/ui/responsive-panel.tsx
|
|
9148
|
+
init_utils();
|
|
9149
|
+
function ResponsivePanel({
|
|
9150
|
+
open,
|
|
9151
|
+
onOpenChange,
|
|
9152
|
+
trigger,
|
|
9153
|
+
title,
|
|
9154
|
+
children,
|
|
9155
|
+
align = "start",
|
|
9156
|
+
popoverClassName
|
|
9157
|
+
}) {
|
|
9158
|
+
const isMobile = useIsMobile();
|
|
9159
|
+
if (!isMobile) {
|
|
9160
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange, children: [
|
|
9161
|
+
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: trigger }),
|
|
9162
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9163
|
+
PopoverContent,
|
|
9164
|
+
{
|
|
9165
|
+
side: "top",
|
|
9166
|
+
align,
|
|
9167
|
+
className: cn("w-64 overflow-hidden rounded-xl p-1", popoverClassName),
|
|
9168
|
+
children
|
|
9169
|
+
}
|
|
9170
|
+
)
|
|
9171
|
+
] });
|
|
9172
|
+
}
|
|
9173
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(radixUi.Dialog.Root, { open, onOpenChange, children: [
|
|
9174
|
+
/* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Trigger, { asChild: true, children: trigger }),
|
|
9175
|
+
/* @__PURE__ */ jsxRuntime.jsxs(radixUi.Dialog.Portal, { children: [
|
|
9176
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9177
|
+
radixUi.Dialog.Overlay,
|
|
9178
|
+
{
|
|
9179
|
+
"data-slot": "responsive-panel-overlay",
|
|
9180
|
+
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"
|
|
9181
|
+
}
|
|
9182
|
+
),
|
|
9183
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9184
|
+
radixUi.Dialog.Content,
|
|
9185
|
+
{
|
|
9186
|
+
"data-slot": "responsive-panel-sheet",
|
|
9187
|
+
"aria-describedby": void 0,
|
|
9188
|
+
className: cn(
|
|
9189
|
+
"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",
|
|
9190
|
+
"data-[state=closed]:animate-out data-[state=closed]:slide-out-to-bottom data-[state=open]:animate-in data-[state=open]:slide-in-from-bottom"
|
|
9191
|
+
),
|
|
9192
|
+
children: [
|
|
9193
|
+
/* @__PURE__ */ jsxRuntime.jsx(radixUi.Dialog.Title, { className: "sr-only", children: title }),
|
|
9194
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9195
|
+
"div",
|
|
9196
|
+
{
|
|
9197
|
+
"aria-hidden": "true",
|
|
9198
|
+
className: "mx-auto mt-2.5 mb-1 h-1 w-9 shrink-0 rounded-full bg-muted-foreground/30"
|
|
9199
|
+
}
|
|
9200
|
+
),
|
|
9201
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 overflow-y-auto overscroll-contain p-1", children })
|
|
9202
|
+
]
|
|
9203
|
+
}
|
|
9204
|
+
)
|
|
9205
|
+
] })
|
|
9206
|
+
] });
|
|
9207
|
+
}
|
|
9208
|
+
|
|
8969
9209
|
// src/components/ui/skeleton.tsx
|
|
8970
9210
|
init_utils();
|
|
8971
9211
|
function Skeleton({ className, ...props }) {
|
|
@@ -9139,6 +9379,7 @@ function SkillsEmptyState() {
|
|
|
9139
9379
|
] })
|
|
9140
9380
|
] });
|
|
9141
9381
|
}
|
|
9382
|
+
var TOOLS_ITEM_CLASS = "gap-3 rounded-md px-3 py-2 pointer-coarse:min-h-11 pointer-coarse:text-[15px]";
|
|
9142
9383
|
function ChatComposer(props) {
|
|
9143
9384
|
return /* @__PURE__ */ jsxRuntime.jsx(PromptInputProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(ChatComposerInner, { ...props }) });
|
|
9144
9385
|
}
|
|
@@ -9236,6 +9477,8 @@ function ChatComposerInner({
|
|
|
9236
9477
|
isRunning,
|
|
9237
9478
|
disabled = false,
|
|
9238
9479
|
disabledReason,
|
|
9480
|
+
awaitingAnswer = false,
|
|
9481
|
+
placeholder,
|
|
9239
9482
|
tokenUsage,
|
|
9240
9483
|
onComposerError,
|
|
9241
9484
|
showBrowser = false,
|
|
@@ -9259,7 +9502,6 @@ function ChatComposerInner({
|
|
|
9259
9502
|
onViewModeChange
|
|
9260
9503
|
}) {
|
|
9261
9504
|
const { adapter } = useAgentChatAdapterContext();
|
|
9262
|
-
const [profileMenuOpen, setProfileMenuOpen] = react.useState(false);
|
|
9263
9505
|
const [browserProfiles, setBrowserProfiles] = react.useState(null);
|
|
9264
9506
|
const loadBrowserProfiles = react.useCallback(() => {
|
|
9265
9507
|
if (!adapter.listBrowserProfiles) return;
|
|
@@ -9272,16 +9514,15 @@ function ChatComposerInner({
|
|
|
9272
9514
|
loadBrowserProfiles();
|
|
9273
9515
|
}, [browserProfilesEnabled, browserProfileId, browserProfiles, loadBrowserProfiles]);
|
|
9274
9516
|
const activeBrowserProfile = (browserProfiles ?? []).find((p) => p.id === browserProfileId) ?? null;
|
|
9275
|
-
const profileTriggerLabel = activeBrowserProfile && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "max-w-24 truncate text-xs", children: activeBrowserProfile.name });
|
|
9276
9517
|
const { textInput, attachments } = usePromptInputController();
|
|
9277
|
-
const status = isRunning ? "streaming" : disabled ? "error" : "ready";
|
|
9518
|
+
const status = isRunning && !awaitingAnswer ? "streaming" : disabled ? "error" : "ready";
|
|
9278
9519
|
const [adapterCommands, setAdapterCommands] = react.useState([]);
|
|
9279
9520
|
const [skillsLoading, setSkillsLoading] = react.useState(false);
|
|
9280
9521
|
const [buttonMenuOpen, setButtonMenuOpen] = react.useState(false);
|
|
9281
9522
|
const [menuMode, setMenuMode] = react.useState("all");
|
|
9282
9523
|
const [addMenuOpen, setAddMenuOpen] = react.useState(false);
|
|
9283
9524
|
const [menuDismissed, setMenuDismissed] = react.useState(false);
|
|
9284
|
-
const showSlashMenu = !menuDismissed && (textInput.value.startsWith("/") || buttonMenuOpen);
|
|
9525
|
+
const showSlashMenu = !menuDismissed && !awaitingAnswer && (textInput.value.startsWith("/") || buttonMenuOpen);
|
|
9285
9526
|
react.useEffect(() => {
|
|
9286
9527
|
if (menuDismissed && !textInput.value.startsWith("/") && !buttonMenuOpen) {
|
|
9287
9528
|
setMenuDismissed(false);
|
|
@@ -9477,16 +9718,13 @@ function ChatComposerInner({
|
|
|
9477
9718
|
});
|
|
9478
9719
|
return;
|
|
9479
9720
|
}
|
|
9480
|
-
if (isRunning) {
|
|
9481
|
-
await onStop();
|
|
9482
|
-
}
|
|
9483
9721
|
await onSend(
|
|
9484
9722
|
text,
|
|
9485
9723
|
images.length > 0 ? images : void 0,
|
|
9486
9724
|
pending.length > 0 ? pending : void 0
|
|
9487
9725
|
);
|
|
9488
9726
|
},
|
|
9489
|
-
[onSend,
|
|
9727
|
+
[onSend, onComposerError, disabled]
|
|
9490
9728
|
);
|
|
9491
9729
|
const handlePromptInputError = react.useCallback(
|
|
9492
9730
|
(err) => {
|
|
@@ -9494,6 +9732,59 @@ function ChatComposerInner({
|
|
|
9494
9732
|
},
|
|
9495
9733
|
[onComposerError]
|
|
9496
9734
|
);
|
|
9735
|
+
const [contextOpen, setContextOpen] = react.useState(false);
|
|
9736
|
+
const contextPercentLabel = tokenUsage?.contextWindow ? `${Math.min(100, Math.round(tokenUsage.totalTokens / tokenUsage.contextWindow * 100))}%` : "0%";
|
|
9737
|
+
const canAttach = !disabled && !awaitingAnswer;
|
|
9738
|
+
const showPaneToggles = Boolean(
|
|
9739
|
+
canShowBrowser && onToggleBrowser || canShowWorkspace && onToggleWorkspace
|
|
9740
|
+
);
|
|
9741
|
+
const canPickProfile = Boolean(
|
|
9742
|
+
browserProfilesEnabled && onSelectBrowserProfile && adapter.listBrowserProfiles
|
|
9743
|
+
);
|
|
9744
|
+
const showToolsPanel = canAttach || showPaneToggles || canPickProfile;
|
|
9745
|
+
const viewModeToggle = onViewModeChange ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9746
|
+
ButtonGroup,
|
|
9747
|
+
{
|
|
9748
|
+
"aria-label": "Chat view mode",
|
|
9749
|
+
className: "overflow-hidden rounded-full border border-border bg-muted/40 p-0.5",
|
|
9750
|
+
children: [
|
|
9751
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9752
|
+
PromptInputButton,
|
|
9753
|
+
{
|
|
9754
|
+
"aria-label": "Simple view",
|
|
9755
|
+
"aria-pressed": viewMode === "simple",
|
|
9756
|
+
tooltip: "Simple \u2014 just the conversation",
|
|
9757
|
+
variant: viewMode === "simple" ? "secondary" : "ghost",
|
|
9758
|
+
onClick: () => onViewModeChange("simple"),
|
|
9759
|
+
className: cn(
|
|
9760
|
+
// 32px is a comfortable segment for a cursor and a miss for a
|
|
9761
|
+
// thumb, so touch takes both halves to the 44px floor.
|
|
9762
|
+
"size-8 rounded-full pointer-coarse:size-11",
|
|
9763
|
+
viewMode === "simple" && "shadow-sm"
|
|
9764
|
+
),
|
|
9765
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-[18px] pointer-coarse:size-5" })
|
|
9766
|
+
}
|
|
9767
|
+
),
|
|
9768
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9769
|
+
PromptInputButton,
|
|
9770
|
+
{
|
|
9771
|
+
"aria-label": "Advanced view",
|
|
9772
|
+
"aria-pressed": viewMode === "expert",
|
|
9773
|
+
tooltip: "Advanced \u2014 every step the agent took",
|
|
9774
|
+
variant: viewMode === "expert" ? "secondary" : "ghost",
|
|
9775
|
+
onClick: () => onViewModeChange("expert"),
|
|
9776
|
+
className: cn(
|
|
9777
|
+
// 32px is a comfortable segment for a cursor and a miss for a
|
|
9778
|
+
// thumb, so touch takes both halves to the 44px floor.
|
|
9779
|
+
"size-8 rounded-full pointer-coarse:size-11",
|
|
9780
|
+
viewMode === "expert" && "shadow-sm"
|
|
9781
|
+
),
|
|
9782
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ListTreeIcon, { className: "size-[18px] pointer-coarse:size-5" })
|
|
9783
|
+
}
|
|
9784
|
+
)
|
|
9785
|
+
]
|
|
9786
|
+
}
|
|
9787
|
+
) : null;
|
|
9497
9788
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9498
9789
|
Popover,
|
|
9499
9790
|
{
|
|
@@ -9546,154 +9837,132 @@ function ChatComposerInner({
|
|
|
9546
9837
|
PromptInputTextarea,
|
|
9547
9838
|
{
|
|
9548
9839
|
ref: textareaRef,
|
|
9549
|
-
placeholder: disabled ? disabledReason ?? "Session disabled" : "Send a message...",
|
|
9840
|
+
placeholder: disabled ? disabledReason ?? "Session disabled" : placeholder ?? "Send a message...",
|
|
9550
9841
|
disabled
|
|
9551
9842
|
}
|
|
9552
9843
|
) }),
|
|
9553
9844
|
/* @__PURE__ */ jsxRuntime.jsxs(PromptInputFooter, { children: [
|
|
9554
9845
|
/* @__PURE__ */ jsxRuntime.jsxs(PromptInputTools, { children: [
|
|
9555
|
-
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
{
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
9563
|
-
|
|
9846
|
+
showToolsPanel && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9847
|
+
ResponsivePanel,
|
|
9848
|
+
{
|
|
9849
|
+
open: addMenuOpen,
|
|
9850
|
+
onOpenChange: (open) => {
|
|
9851
|
+
setAddMenuOpen(open);
|
|
9852
|
+
if (open && canPickProfile && !browserProfileLocked && browserProfiles === null) {
|
|
9853
|
+
loadBrowserProfiles();
|
|
9854
|
+
}
|
|
9855
|
+
},
|
|
9856
|
+
title: "Composer tools",
|
|
9857
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9858
|
+
PromptInputButton,
|
|
9859
|
+
{
|
|
9860
|
+
"aria-label": "Composer tools",
|
|
9861
|
+
tooltip: "Attach files, panes and browser profile",
|
|
9862
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlusIcon, { className: "size-4" })
|
|
9863
|
+
}
|
|
9864
|
+
),
|
|
9865
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(Command, { children: /* @__PURE__ */ jsxRuntime.jsxs(CommandList, { className: "max-md:max-h-none", children: [
|
|
9866
|
+
canAttach && /* @__PURE__ */ jsxRuntime.jsxs(CommandGroup, { heading: "Attach", children: [
|
|
9564
9867
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9565
9868
|
CommandItem,
|
|
9566
9869
|
{
|
|
9567
|
-
|
|
9568
|
-
|
|
9870
|
+
onSelect: () => {
|
|
9871
|
+
setAddMenuOpen(false);
|
|
9872
|
+
attachments.openFileDialog();
|
|
9873
|
+
},
|
|
9874
|
+
className: TOOLS_ITEM_CLASS,
|
|
9569
9875
|
children: [
|
|
9570
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
9571
|
-
"Add
|
|
9572
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "ml-auto size-4 shrink-0 text-muted-foreground" })
|
|
9876
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.PaperclipIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9877
|
+
"Add local files"
|
|
9573
9878
|
]
|
|
9574
9879
|
}
|
|
9575
9880
|
),
|
|
9576
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9881
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CommandItem, { disabled: true, className: TOOLS_ITEM_CLASS, children: [
|
|
9882
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CloudIcon, { className: "size-4 shrink-0 text-sky-500" }),
|
|
9883
|
+
"Add from OneDrive"
|
|
9884
|
+
] }),
|
|
9885
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CommandItem, { disabled: true, className: TOOLS_ITEM_CLASS, children: [
|
|
9886
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.HardDriveIcon, { className: "size-4 shrink-0 text-emerald-500" }),
|
|
9887
|
+
"Add from Google Drive"
|
|
9888
|
+
] })
|
|
9889
|
+
] }),
|
|
9890
|
+
showPaneToggles && // Panes stay available while a question is pending —
|
|
9891
|
+
// only attachments are suppressed there.
|
|
9892
|
+
/* @__PURE__ */ jsxRuntime.jsxs(CommandGroup, { heading: "Panes", children: [
|
|
9893
|
+
canShowBrowser && onToggleBrowser && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9577
9894
|
CommandItem,
|
|
9578
9895
|
{
|
|
9579
|
-
|
|
9580
|
-
|
|
9896
|
+
onSelect: onToggleBrowser,
|
|
9897
|
+
"data-checked": showBrowser || void 0,
|
|
9898
|
+
className: TOOLS_ITEM_CLASS,
|
|
9581
9899
|
children: [
|
|
9582
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
9583
|
-
"
|
|
9900
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9901
|
+
"Browser"
|
|
9584
9902
|
]
|
|
9585
9903
|
}
|
|
9586
9904
|
),
|
|
9587
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9905
|
+
canShowWorkspace && onToggleWorkspace && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9588
9906
|
CommandItem,
|
|
9589
9907
|
{
|
|
9590
|
-
onSelect:
|
|
9591
|
-
|
|
9592
|
-
|
|
9593
|
-
},
|
|
9594
|
-
className: "gap-3 rounded-md px-3 py-2 [&>svg:last-child]:hidden",
|
|
9908
|
+
onSelect: onToggleWorkspace,
|
|
9909
|
+
"data-checked": showWorkspace || void 0,
|
|
9910
|
+
className: TOOLS_ITEM_CLASS,
|
|
9595
9911
|
children: [
|
|
9596
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
9597
|
-
"
|
|
9912
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9913
|
+
"Workspace"
|
|
9598
9914
|
]
|
|
9599
9915
|
}
|
|
9600
9916
|
)
|
|
9601
|
-
] })
|
|
9602
|
-
|
|
9603
|
-
|
|
9604
|
-
|
|
9605
|
-
|
|
9606
|
-
|
|
9607
|
-
|
|
9608
|
-
|
|
9609
|
-
"aria-pressed": showBrowser,
|
|
9610
|
-
onClick: onToggleBrowser,
|
|
9611
|
-
tooltip: showBrowser ? "Hide browser pane" : "Show browser pane",
|
|
9612
|
-
className: showBrowser ? "bg-accent text-foreground" : void 0,
|
|
9613
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GlobeIcon, { className: "size-4" })
|
|
9614
|
-
}
|
|
9615
|
-
),
|
|
9616
|
-
browserProfilesEnabled && onSelectBrowserProfile && adapter.listBrowserProfiles && (browserProfileLocked ? (
|
|
9617
|
-
// A profile is bound at session creation; once a session is
|
|
9618
|
-
// active the choice can't change. Render disabled (via
|
|
9619
|
-
// aria-disabled, not the `disabled` attribute, so the
|
|
9620
|
-
// tooltip still shows on hover) with an explanation.
|
|
9621
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9622
|
-
PromptInputButton,
|
|
9623
|
-
{
|
|
9624
|
-
"aria-label": "Select browser profile",
|
|
9625
|
-
"aria-disabled": true,
|
|
9626
|
-
"aria-pressed": !!browserProfileId,
|
|
9627
|
-
tooltip: activeBrowserProfile ? `Browser profile: ${activeBrowserProfile.name} (locked for this session)` : "A browser profile must be chosen before starting the session",
|
|
9628
|
-
className: `cursor-not-allowed opacity-50 ${browserProfileId ? "bg-accent text-foreground" : ""}`,
|
|
9629
|
-
children: [
|
|
9630
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.IdCardIcon, { className: "size-4" }),
|
|
9631
|
-
profileTriggerLabel
|
|
9632
|
-
]
|
|
9633
|
-
}
|
|
9634
|
-
)
|
|
9635
|
-
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9636
|
-
Popover,
|
|
9637
|
-
{
|
|
9638
|
-
open: profileMenuOpen,
|
|
9639
|
-
onOpenChange: (open) => {
|
|
9640
|
-
setProfileMenuOpen(open);
|
|
9641
|
-
if (open) loadBrowserProfiles();
|
|
9642
|
-
},
|
|
9643
|
-
children: [
|
|
9644
|
-
/* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9645
|
-
PromptInputButton,
|
|
9646
|
-
{
|
|
9647
|
-
"aria-label": "Select browser profile",
|
|
9648
|
-
"aria-pressed": !!browserProfileId,
|
|
9649
|
-
tooltip: activeBrowserProfile ? `Browser profile: ${activeBrowserProfile.name}` : "Select browser profile",
|
|
9650
|
-
className: browserProfileId ? "bg-accent text-foreground" : void 0,
|
|
9651
|
-
children: [
|
|
9652
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.IdCardIcon, { className: "size-4" }),
|
|
9653
|
-
profileTriggerLabel
|
|
9654
|
-
]
|
|
9655
|
-
}
|
|
9656
|
-
) }),
|
|
9657
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9658
|
-
PopoverContent,
|
|
9659
|
-
{
|
|
9660
|
-
side: "top",
|
|
9661
|
-
align: "start",
|
|
9662
|
-
className: "w-64 overflow-hidden rounded-xl p-1",
|
|
9663
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(Command, { children: /* @__PURE__ */ jsxRuntime.jsx(CommandList, { children: [
|
|
9664
|
-
{ id: null, name: "No profile" },
|
|
9665
|
-
...browserProfiles ?? []
|
|
9666
|
-
].map((p) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
9917
|
+
] }),
|
|
9918
|
+
canPickProfile && /* @__PURE__ */ jsxRuntime.jsx(CommandGroup, { heading: "Browser profile", children: browserProfileLocked ? (
|
|
9919
|
+
// A profile is bound at session creation and cannot
|
|
9920
|
+
// change mid-session. In a menu there is nothing to
|
|
9921
|
+
// hover, so the reason is stated outright rather
|
|
9922
|
+
// than hidden in a tooltip as it was on the button.
|
|
9923
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9924
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
9667
9925
|
CommandItem,
|
|
9668
9926
|
{
|
|
9669
|
-
|
|
9670
|
-
|
|
9671
|
-
|
|
9672
|
-
|
|
9673
|
-
|
|
9674
|
-
|
|
9675
|
-
|
|
9927
|
+
disabled: true,
|
|
9928
|
+
"data-checked": !!browserProfileId || void 0,
|
|
9929
|
+
className: TOOLS_ITEM_CLASS,
|
|
9930
|
+
children: [
|
|
9931
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.IdCardIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9932
|
+
activeBrowserProfile?.name ?? "No profile"
|
|
9933
|
+
]
|
|
9934
|
+
}
|
|
9935
|
+
),
|
|
9936
|
+
/* @__PURE__ */ jsxRuntime.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." })
|
|
9937
|
+
] })
|
|
9938
|
+
) : (
|
|
9939
|
+
// data-checked lights CommandItem's built-in
|
|
9940
|
+
// trailing check on the selected entry.
|
|
9941
|
+
[
|
|
9942
|
+
{ id: null, name: "No profile" },
|
|
9943
|
+
...browserProfiles ?? []
|
|
9944
|
+
].map((p) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9945
|
+
CommandItem,
|
|
9946
|
+
{
|
|
9947
|
+
onSelect: () => {
|
|
9948
|
+
setAddMenuOpen(false);
|
|
9949
|
+
onSelectBrowserProfile?.(p.id);
|
|
9676
9950
|
},
|
|
9677
|
-
p.id
|
|
9678
|
-
|
|
9679
|
-
|
|
9680
|
-
|
|
9681
|
-
|
|
9682
|
-
|
|
9683
|
-
|
|
9684
|
-
|
|
9685
|
-
|
|
9686
|
-
|
|
9687
|
-
|
|
9688
|
-
"aria-pressed": showWorkspace,
|
|
9689
|
-
onClick: onToggleWorkspace,
|
|
9690
|
-
tooltip: showWorkspace ? "Hide workspace pane" : "Show workspace pane",
|
|
9691
|
-
className: showWorkspace ? "bg-accent text-foreground" : void 0,
|
|
9692
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FolderIcon, { className: "size-4" })
|
|
9951
|
+
"data-checked": (browserProfileId ?? null) === p.id || void 0,
|
|
9952
|
+
className: TOOLS_ITEM_CLASS,
|
|
9953
|
+
children: [
|
|
9954
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.IdCardIcon, { className: "size-4 shrink-0 text-muted-foreground" }),
|
|
9955
|
+
p.name
|
|
9956
|
+
]
|
|
9957
|
+
},
|
|
9958
|
+
p.id ?? "none"
|
|
9959
|
+
))
|
|
9960
|
+
) })
|
|
9961
|
+
] }) })
|
|
9693
9962
|
}
|
|
9694
9963
|
),
|
|
9695
|
-
!disabled && tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsxRuntime.
|
|
9696
|
-
|
|
9964
|
+
!disabled && tokenUsage && tokenUsage.contextWindow > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9965
|
+
ContextValueProvider,
|
|
9697
9966
|
{
|
|
9698
9967
|
usedTokens: tokenUsage.totalTokens,
|
|
9699
9968
|
maxTokens: tokenUsage.contextWindow,
|
|
@@ -9707,82 +9976,68 @@ function ChatComposerInner({
|
|
|
9707
9976
|
inputTokenDetails: void 0,
|
|
9708
9977
|
outputTokenDetails: void 0
|
|
9709
9978
|
},
|
|
9710
|
-
children:
|
|
9711
|
-
|
|
9712
|
-
|
|
9713
|
-
|
|
9714
|
-
|
|
9715
|
-
|
|
9716
|
-
|
|
9717
|
-
|
|
9718
|
-
|
|
9719
|
-
|
|
9720
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextContentBody, { children: tokenUsage.totalTokens > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
9721
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextInputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9722
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Input" }),
|
|
9723
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.inputTokens.toLocaleString() })
|
|
9724
|
-
] }) }),
|
|
9725
|
-
/* @__PURE__ */ jsxRuntime.jsx(ContextOutputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9726
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Output" }),
|
|
9727
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.outputTokens.toLocaleString() })
|
|
9728
|
-
] }) }),
|
|
9729
|
-
tokenUsage.reasoningTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextReasoningUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9730
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
|
|
9731
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.reasoningTokens.toLocaleString() })
|
|
9732
|
-
] }) }),
|
|
9733
|
-
tokenUsage.cachedInputTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextCacheUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
9734
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Cache" }),
|
|
9735
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.cachedInputTokens.toLocaleString() })
|
|
9736
|
-
] }) })
|
|
9737
|
-
] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground text-center py-1", children: "Empty" }) }),
|
|
9738
|
-
tokenUsage.totalTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-end gap-3 bg-secondary p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
9739
|
-
Button,
|
|
9979
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9980
|
+
ResponsivePanel,
|
|
9981
|
+
{
|
|
9982
|
+
open: contextOpen,
|
|
9983
|
+
onOpenChange: setContextOpen,
|
|
9984
|
+
title: "Session context",
|
|
9985
|
+
align: "end",
|
|
9986
|
+
popoverClassName: "min-w-60 divide-y p-0",
|
|
9987
|
+
trigger: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
9988
|
+
PromptInputButton,
|
|
9740
9989
|
{
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9744
|
-
|
|
9990
|
+
"aria-label": `Session context: ${contextPercentLabel} of the window used`,
|
|
9991
|
+
tooltip: "Session context",
|
|
9992
|
+
variant: "ghost",
|
|
9993
|
+
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",
|
|
9994
|
+
children: [
|
|
9995
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextIcon, { className: "size-4" }),
|
|
9996
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] tabular-nums", children: contextPercentLabel })
|
|
9997
|
+
]
|
|
9745
9998
|
}
|
|
9746
|
-
)
|
|
9747
|
-
|
|
9748
|
-
|
|
9999
|
+
),
|
|
10000
|
+
children: [
|
|
10001
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextContentHeader, {}),
|
|
10002
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextContentBody, { children: tokenUsage.totalTokens > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
10003
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextInputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
10004
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Input" }),
|
|
10005
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.inputTokens.toLocaleString() })
|
|
10006
|
+
] }) }),
|
|
10007
|
+
/* @__PURE__ */ jsxRuntime.jsx(ContextOutputUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
10008
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Output" }),
|
|
10009
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.outputTokens.toLocaleString() })
|
|
10010
|
+
] }) }),
|
|
10011
|
+
tokenUsage.reasoningTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextReasoningUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
10012
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Reasoning" }),
|
|
10013
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.reasoningTokens.toLocaleString() })
|
|
10014
|
+
] }) }),
|
|
10015
|
+
tokenUsage.cachedInputTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx(ContextCacheUsage, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs", children: [
|
|
10016
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: "Cache" }),
|
|
10017
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tokenUsage.cachedInputTokens.toLocaleString() })
|
|
10018
|
+
] }) })
|
|
10019
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground text-center py-1", children: "Empty" }) }),
|
|
10020
|
+
tokenUsage.totalTokens > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-end gap-3 bg-secondary p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10021
|
+
Button,
|
|
10022
|
+
{
|
|
10023
|
+
type: "button",
|
|
10024
|
+
size: "xs",
|
|
10025
|
+
className: "pointer-coarse:h-11 pointer-coarse:px-4 pointer-coarse:text-sm",
|
|
10026
|
+
onClick: () => {
|
|
10027
|
+
setContextOpen(false);
|
|
10028
|
+
onSend("/compress");
|
|
10029
|
+
},
|
|
10030
|
+
children: "Compress"
|
|
10031
|
+
}
|
|
10032
|
+
) })
|
|
10033
|
+
]
|
|
10034
|
+
}
|
|
10035
|
+
)
|
|
9749
10036
|
}
|
|
9750
10037
|
)
|
|
9751
10038
|
] }),
|
|
9752
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
9753
|
-
|
|
9754
|
-
ButtonGroup,
|
|
9755
|
-
{
|
|
9756
|
-
"aria-label": "Chat view mode",
|
|
9757
|
-
className: "overflow-hidden rounded-md border border-border",
|
|
9758
|
-
children: [
|
|
9759
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9760
|
-
Button,
|
|
9761
|
-
{
|
|
9762
|
-
type: "button",
|
|
9763
|
-
size: "xs",
|
|
9764
|
-
className: "capitalize font-normal",
|
|
9765
|
-
variant: viewMode === "simple" ? "secondary" : "ghost",
|
|
9766
|
-
"aria-pressed": viewMode === "simple",
|
|
9767
|
-
onClick: () => onViewModeChange("simple"),
|
|
9768
|
-
children: "Simple"
|
|
9769
|
-
}
|
|
9770
|
-
),
|
|
9771
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
9772
|
-
Button,
|
|
9773
|
-
{
|
|
9774
|
-
type: "button",
|
|
9775
|
-
size: "xs",
|
|
9776
|
-
className: "capitalize font-normal",
|
|
9777
|
-
variant: viewMode === "expert" ? "secondary" : "ghost",
|
|
9778
|
-
"aria-pressed": viewMode === "expert",
|
|
9779
|
-
onClick: () => onViewModeChange("expert"),
|
|
9780
|
-
children: "Advanced"
|
|
9781
|
-
}
|
|
9782
|
-
)
|
|
9783
|
-
]
|
|
9784
|
-
}
|
|
9785
|
-
),
|
|
10039
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: [
|
|
10040
|
+
viewModeToggle,
|
|
9786
10041
|
!disabled && /* @__PURE__ */ jsxRuntime.jsx(
|
|
9787
10042
|
PromptInputSubmit,
|
|
9788
10043
|
{
|
|
@@ -11723,16 +11978,34 @@ var WORKING_ON_IT_DELAY_MS = 250;
|
|
|
11723
11978
|
function hasUserFacingAskContent(msg) {
|
|
11724
11979
|
return !!msg.toolCalls?.some((tc) => tc.toolName === "ask_user_question");
|
|
11725
11980
|
}
|
|
11726
|
-
function
|
|
11981
|
+
function pendingAskCall(messages) {
|
|
11727
11982
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
11728
11983
|
const m = messages[i];
|
|
11729
|
-
if (m.role === "user") return
|
|
11984
|
+
if (m.role === "user") return null;
|
|
11730
11985
|
if (m.role !== "assistant") continue;
|
|
11731
|
-
return
|
|
11986
|
+
return m.toolCalls?.find(
|
|
11732
11987
|
(tc) => tc.toolName === "ask_user_question" && tc.status === "running"
|
|
11733
|
-
);
|
|
11988
|
+
) ?? null;
|
|
11734
11989
|
}
|
|
11735
|
-
return
|
|
11990
|
+
return null;
|
|
11991
|
+
}
|
|
11992
|
+
function askComposerPolicy(pending) {
|
|
11993
|
+
if (!pending) return null;
|
|
11994
|
+
const questions = askQuestionsOf(pending);
|
|
11995
|
+
if (!isConversationalAsk(questions)) {
|
|
11996
|
+
return { locked: true, reason: "Answer the questions above to continue." };
|
|
11997
|
+
}
|
|
11998
|
+
const question = questions[0];
|
|
11999
|
+
if (!conversationalAskAcceptsFreeText(question)) {
|
|
12000
|
+
return {
|
|
12001
|
+
locked: true,
|
|
12002
|
+
reason: "Pick one of the options above to continue."
|
|
12003
|
+
};
|
|
12004
|
+
}
|
|
12005
|
+
return {
|
|
12006
|
+
locked: false,
|
|
12007
|
+
placeholder: hasChoices(question) ? "Pick one above, or type your answer\u2026" : "Type your answer\u2026"
|
|
12008
|
+
};
|
|
11736
12009
|
}
|
|
11737
12010
|
function messageToEntries(msg, isLast, artifactFallbacks) {
|
|
11738
12011
|
if (msg.role === "system") {
|
|
@@ -11815,7 +12088,14 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
|
|
|
11815
12088
|
const parsed = parseTerminalResult(tc.result, tc.args);
|
|
11816
12089
|
if (parsed && (parsed.exit_code !== 0 || parsed.error)) continue;
|
|
11817
12090
|
}
|
|
11818
|
-
entries.push({
|
|
12091
|
+
entries.push({
|
|
12092
|
+
kind: "tool",
|
|
12093
|
+
key: tc.id,
|
|
12094
|
+
tc,
|
|
12095
|
+
// Only ask_user_question reads this, to suppress a prompt the
|
|
12096
|
+
// agent already spelled out in its message body.
|
|
12097
|
+
assistantContent: tc.toolName === "ask_user_question" ? msg.content : void 0
|
|
12098
|
+
});
|
|
11819
12099
|
}
|
|
11820
12100
|
}
|
|
11821
12101
|
if (effectiveHasContent) {
|
|
@@ -12060,6 +12340,8 @@ function TimelineEntryItem({
|
|
|
12060
12340
|
{
|
|
12061
12341
|
tc: entry.tc,
|
|
12062
12342
|
resolvedArtifactName: entry.resolvedArtifactName,
|
|
12343
|
+
assistantContent: entry.assistantContent,
|
|
12344
|
+
viewMode: "expert",
|
|
12063
12345
|
onFileSelect
|
|
12064
12346
|
}
|
|
12065
12347
|
),
|
|
@@ -12583,7 +12865,14 @@ function IterationGroup({
|
|
|
12583
12865
|
(tc) => tc.toolName === "ask_user_question"
|
|
12584
12866
|
);
|
|
12585
12867
|
if (askCall) {
|
|
12586
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 py-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12868
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1 py-0.5", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
12869
|
+
AskUserQuestionToolBlock,
|
|
12870
|
+
{
|
|
12871
|
+
tc: askCall,
|
|
12872
|
+
assistantContent: message.content,
|
|
12873
|
+
viewMode: "simple"
|
|
12874
|
+
}
|
|
12875
|
+
) });
|
|
12587
12876
|
}
|
|
12588
12877
|
if (isIterationLive(message)) {
|
|
12589
12878
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -12926,7 +13215,12 @@ function ChatThread({
|
|
|
12926
13215
|
onOpenIntegrations
|
|
12927
13216
|
}) {
|
|
12928
13217
|
const groups = react.useMemo(() => groupMessages(messages), [messages]);
|
|
12929
|
-
const
|
|
13218
|
+
const pendingAsk = react.useMemo(() => pendingAskCall(messages), [messages]);
|
|
13219
|
+
const askPolicy = react.useMemo(
|
|
13220
|
+
() => askComposerPolicy(pendingAsk),
|
|
13221
|
+
[pendingAsk?.id, pendingAsk?.args]
|
|
13222
|
+
);
|
|
13223
|
+
const awaitingInput = askPolicy !== null;
|
|
12930
13224
|
const orbActivity = react.useMemo(
|
|
12931
13225
|
() => deriveOrbActivity(
|
|
12932
13226
|
messages,
|
|
@@ -12935,8 +13229,9 @@ function ChatThread({
|
|
|
12935
13229
|
[messages, viewMode]
|
|
12936
13230
|
);
|
|
12937
13231
|
const showWorkingOnIt = useDelayedRunningIndicator(isRunning) && !awaitingInput;
|
|
12938
|
-
const
|
|
12939
|
-
const
|
|
13232
|
+
const composerLocked = askPolicy?.locked ?? false;
|
|
13233
|
+
const composerDisabled = disabled || composerLocked;
|
|
13234
|
+
const composerDisabledReason = composerLocked ? askPolicy?.reason : disabledReason;
|
|
12940
13235
|
const artifactFallbacks = react.useMemo(() => {
|
|
12941
13236
|
const fallbacks = {};
|
|
12942
13237
|
const createArtifactToolCallIds = [];
|
|
@@ -12981,6 +13276,8 @@ function ChatThread({
|
|
|
12981
13276
|
isRunning,
|
|
12982
13277
|
disabled: composerDisabled,
|
|
12983
13278
|
disabledReason: composerDisabledReason,
|
|
13279
|
+
awaitingAnswer: askPolicy !== null && !askPolicy.locked,
|
|
13280
|
+
placeholder: askPolicy?.placeholder,
|
|
12984
13281
|
tokenUsage,
|
|
12985
13282
|
onComposerError,
|
|
12986
13283
|
showBrowser,
|
|
@@ -13013,10 +13310,16 @@ function ChatThread({
|
|
|
13013
13310
|
)
|
|
13014
13311
|
] });
|
|
13015
13312
|
if (messages.length === 0 && !isLoadingHistory && !disabled) {
|
|
13016
|
-
return
|
|
13017
|
-
|
|
13018
|
-
|
|
13019
|
-
|
|
13313
|
+
return (
|
|
13314
|
+
// A centred hero is a deliberate desktop look. On a phone it leaves the
|
|
13315
|
+
// input floating mid-screen with dead space under it, and the keyboard
|
|
13316
|
+
// then opens across that gap. There the composer keeps its usual place
|
|
13317
|
+
// at the bottom edge and the greeting takes the space above it.
|
|
13318
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col items-center justify-end overflow-hidden bg-background px-3 text-base md:justify-center", children: [
|
|
13319
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full flex-1 flex-col justify-center md:flex-none", children: /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-6 text-center text-2xl font-bold text-foreground md:mb-8", children: "What should we do ?" }) }),
|
|
13320
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full", children: composerArea })
|
|
13321
|
+
] })
|
|
13322
|
+
);
|
|
13020
13323
|
}
|
|
13021
13324
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-background text-base", children: [
|
|
13022
13325
|
/* @__PURE__ */ jsxRuntime.jsxs(Conversation, { className: "relative flex-1 min-h-0", children: [
|
|
@@ -13096,6 +13399,7 @@ function IntegrationsBandSlot({
|
|
|
13096
13399
|
}
|
|
13097
13400
|
);
|
|
13098
13401
|
}
|
|
13402
|
+
init_utils();
|
|
13099
13403
|
|
|
13100
13404
|
// src/components/ai-elements/file-tree.tsx
|
|
13101
13405
|
init_utils();
|
|
@@ -13265,9 +13569,6 @@ function FileTreeFile({
|
|
|
13265
13569
|
}
|
|
13266
13570
|
);
|
|
13267
13571
|
}
|
|
13268
|
-
|
|
13269
|
-
// src/components/workspace/workspace-panel.tsx
|
|
13270
|
-
init_utils();
|
|
13271
13572
|
var SKELETON_WIDTHS2 = [75, 60, 90, 65, 80, 70, 85, 55];
|
|
13272
13573
|
var DEFAULT_WIDTH = 400;
|
|
13273
13574
|
var MIN_WIDTH = 300;
|
|
@@ -13322,14 +13623,22 @@ function RenderEntries({
|
|
|
13322
13623
|
}) {
|
|
13323
13624
|
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: entries.map((entry) => {
|
|
13324
13625
|
if (entry.kind === "dir") {
|
|
13325
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13326
|
-
|
|
13626
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
13627
|
+
FileTreeFolder,
|
|
13327
13628
|
{
|
|
13328
|
-
|
|
13329
|
-
|
|
13330
|
-
|
|
13331
|
-
|
|
13332
|
-
|
|
13629
|
+
name: entry.name,
|
|
13630
|
+
path: entry.path,
|
|
13631
|
+
children: entry.children && entry.children.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13632
|
+
RenderEntries,
|
|
13633
|
+
{
|
|
13634
|
+
entries: entry.children,
|
|
13635
|
+
onDelete,
|
|
13636
|
+
downloadUrlFor
|
|
13637
|
+
}
|
|
13638
|
+
)
|
|
13639
|
+
},
|
|
13640
|
+
entry.path
|
|
13641
|
+
);
|
|
13333
13642
|
}
|
|
13334
13643
|
const fileName2 = entry.name;
|
|
13335
13644
|
return /* @__PURE__ */ jsxRuntime.jsxs(FileTreeFile, { name: entry.name, path: entry.path, children: [
|
|
@@ -13388,6 +13697,7 @@ function WorkspacePanel({
|
|
|
13388
13697
|
const [deleteTarget, setDeleteTarget] = react.useState(null);
|
|
13389
13698
|
const [expandedPaths, setExpandedPaths] = react.useState(/* @__PURE__ */ new Set());
|
|
13390
13699
|
const [width, setWidth] = react.useState(DEFAULT_WIDTH);
|
|
13700
|
+
const narrow = useIsMobile();
|
|
13391
13701
|
const sessionIdRef = react.useRef(sessionId);
|
|
13392
13702
|
const isResizing = react.useRef(false);
|
|
13393
13703
|
const startX = react.useRef(0);
|
|
@@ -13537,7 +13847,14 @@ function WorkspacePanel({
|
|
|
13537
13847
|
setDeleteTarget(null);
|
|
13538
13848
|
}
|
|
13539
13849
|
},
|
|
13540
|
-
[
|
|
13850
|
+
[
|
|
13851
|
+
adapter,
|
|
13852
|
+
disabled,
|
|
13853
|
+
fetchTree,
|
|
13854
|
+
onSelectedPathChange,
|
|
13855
|
+
selectedPath,
|
|
13856
|
+
sessionId
|
|
13857
|
+
]
|
|
13541
13858
|
);
|
|
13542
13859
|
const onResizeStart = react.useCallback(
|
|
13543
13860
|
(event) => {
|
|
@@ -13577,11 +13894,11 @@ function WorkspacePanel({
|
|
|
13577
13894
|
"data-testid": "workspace-panel",
|
|
13578
13895
|
className: cn(
|
|
13579
13896
|
"relative z-10 flex h-full min-h-0 flex-col overflow-hidden border-l border-muted-foreground/20 bg-card",
|
|
13580
|
-
fillParent && "w-full"
|
|
13897
|
+
(fillParent || narrow) && "w-full"
|
|
13581
13898
|
),
|
|
13582
|
-
style: fillParent ? void 0 : { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
13899
|
+
style: fillParent || narrow ? void 0 : { width, minWidth: MIN_WIDTH, maxWidth: MAX_WIDTH },
|
|
13583
13900
|
children: [
|
|
13584
|
-
!fillParent && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13901
|
+
!(fillParent || narrow) && /* @__PURE__ */ jsxRuntime.jsx(
|
|
13585
13902
|
"div",
|
|
13586
13903
|
{
|
|
13587
13904
|
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",
|
|
@@ -13754,10 +14071,18 @@ function ConfirmDialog2({
|
|
|
13754
14071
|
] }),
|
|
13755
14072
|
/* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
|
|
13756
14073
|
/* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "outline", disabled: loading, onClick: onCancel, children: "Cancel" }),
|
|
13757
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
13758
|
-
|
|
13759
|
-
|
|
13760
|
-
|
|
14074
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
14075
|
+
Button,
|
|
14076
|
+
{
|
|
14077
|
+
variant: "destructive",
|
|
14078
|
+
disabled: loading,
|
|
14079
|
+
onClick: handleConfirm,
|
|
14080
|
+
children: [
|
|
14081
|
+
loading && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "mr-1.5 size-3.5 animate-spin" }),
|
|
14082
|
+
confirmLabel
|
|
14083
|
+
]
|
|
14084
|
+
}
|
|
14085
|
+
)
|
|
13761
14086
|
] })
|
|
13762
14087
|
] })
|
|
13763
14088
|
}
|
|
@@ -14056,7 +14381,11 @@ function applyAgentChatEvent(state, event) {
|
|
|
14056
14381
|
case "ask_user_question.response":
|
|
14057
14382
|
return withMessages(
|
|
14058
14383
|
nextState,
|
|
14059
|
-
applyAskUserQuestionResponse(
|
|
14384
|
+
applyAskUserQuestionResponse(
|
|
14385
|
+
nextState.messages,
|
|
14386
|
+
event.eventId,
|
|
14387
|
+
event.data
|
|
14388
|
+
)
|
|
14060
14389
|
);
|
|
14061
14390
|
case "policy.denied":
|
|
14062
14391
|
return applyPolicyDenied(nextState, event.data);
|
|
@@ -14807,34 +15136,59 @@ function applyUserFeedback(messages, data) {
|
|
|
14807
15136
|
}
|
|
14808
15137
|
return messages;
|
|
14809
15138
|
}
|
|
14810
|
-
function applyAskUserQuestionResponse(messages, data) {
|
|
15139
|
+
function applyAskUserQuestionResponse(messages, eventId, data) {
|
|
14811
15140
|
const targetToolId = stringValue2(data.tool_call_id);
|
|
14812
15141
|
const responses = Array.isArray(data.responses) ? data.responses : void 0;
|
|
14813
15142
|
if (!targetToolId || !responses) return messages;
|
|
15143
|
+
const answers = responses.map((response) => {
|
|
15144
|
+
const row = objectValue(response) ?? {};
|
|
15145
|
+
return {
|
|
15146
|
+
question: stringValue2(row.question),
|
|
15147
|
+
answer: stringValue2(row.answer),
|
|
15148
|
+
is_other: Boolean(row.is_other)
|
|
15149
|
+
};
|
|
15150
|
+
});
|
|
14814
15151
|
const next = [...messages];
|
|
14815
15152
|
for (let i = next.length - 1; i >= 0; i--) {
|
|
14816
15153
|
const msg = next[i];
|
|
14817
15154
|
if (!msg?.toolCalls?.some((tc) => tc.id === targetToolId)) continue;
|
|
15155
|
+
const alreadyAnswered = msg.toolCalls.some(
|
|
15156
|
+
(tc) => tc.id === targetToolId && tc.askUserQuestionAnswers !== void 0
|
|
15157
|
+
);
|
|
15158
|
+
if (alreadyAnswered) return messages;
|
|
14818
15159
|
next[i] = {
|
|
14819
15160
|
...msg,
|
|
14820
15161
|
toolCalls: msg.toolCalls.map(
|
|
14821
|
-
(tc) => tc.id === targetToolId ? {
|
|
14822
|
-
...tc,
|
|
14823
|
-
askUserQuestionAnswers: responses.map((response) => {
|
|
14824
|
-
const row = objectValue(response) ?? {};
|
|
14825
|
-
return {
|
|
14826
|
-
question: stringValue2(row.question),
|
|
14827
|
-
answer: stringValue2(row.answer),
|
|
14828
|
-
is_other: Boolean(row.is_other)
|
|
14829
|
-
};
|
|
14830
|
-
})
|
|
14831
|
-
} : tc
|
|
15162
|
+
(tc) => tc.id === targetToolId ? { ...tc, askUserQuestionAnswers: answers } : tc
|
|
14832
15163
|
)
|
|
14833
15164
|
};
|
|
14834
|
-
return next;
|
|
15165
|
+
return appendConversationalAnswer(next, i, eventId, answers);
|
|
14835
15166
|
}
|
|
14836
15167
|
return messages;
|
|
14837
15168
|
}
|
|
15169
|
+
function appendConversationalAnswer(messages, askIndex, eventId, answers) {
|
|
15170
|
+
if (answers.length !== 1) return messages;
|
|
15171
|
+
const answer = answers[0].answer.trim();
|
|
15172
|
+
if (!answer) return messages;
|
|
15173
|
+
const id = `evt-${eventId}`;
|
|
15174
|
+
if (messages.some((m) => m.id === id)) return messages;
|
|
15175
|
+
const next = [...messages];
|
|
15176
|
+
for (let i = askIndex + 1; i < next.length; i++) {
|
|
15177
|
+
const msg = next[i];
|
|
15178
|
+
if (msg.role !== "user" || !msg.id.startsWith("local-")) continue;
|
|
15179
|
+
if (msg.status === "error") continue;
|
|
15180
|
+
next[i] = { ...msg, id, content: answer, status: "complete" };
|
|
15181
|
+
return next;
|
|
15182
|
+
}
|
|
15183
|
+
next.push({
|
|
15184
|
+
id,
|
|
15185
|
+
role: "user",
|
|
15186
|
+
content: answer,
|
|
15187
|
+
createdAt: /* @__PURE__ */ new Date(),
|
|
15188
|
+
status: "complete"
|
|
15189
|
+
});
|
|
15190
|
+
return next;
|
|
15191
|
+
}
|
|
14838
15192
|
function applyPolicyDenied(state, data) {
|
|
14839
15193
|
const messages = [...state.messages];
|
|
14840
15194
|
const policyIdx = findLastAssistantIndex(messages);
|
|
@@ -15526,6 +15880,11 @@ function parseGoalDefinition(content) {
|
|
|
15526
15880
|
var RIGHT_STACK_STYLE = {
|
|
15527
15881
|
["--right-stack-w"]: "440px"
|
|
15528
15882
|
};
|
|
15883
|
+
var MOBILE_PANE_LABELS = {
|
|
15884
|
+
chat: "Chat",
|
|
15885
|
+
browser: "Browser",
|
|
15886
|
+
workspace: "Workspace"
|
|
15887
|
+
};
|
|
15529
15888
|
function AgentChat({
|
|
15530
15889
|
adapter,
|
|
15531
15890
|
agentId,
|
|
@@ -15598,7 +15957,14 @@ function AgentChat({
|
|
|
15598
15957
|
const handleToggleWorkspace = react.useCallback(() => {
|
|
15599
15958
|
setShowWorkspace((prev) => !prev);
|
|
15600
15959
|
}, []);
|
|
15601
|
-
const
|
|
15960
|
+
const mobilePanes = react.useMemo(() => {
|
|
15961
|
+
const panes = ["chat"];
|
|
15962
|
+
if (browserVisible) panes.push("browser");
|
|
15963
|
+
if (workspaceVisible) panes.push("workspace");
|
|
15964
|
+
return panes;
|
|
15965
|
+
}, [browserVisible, workspaceVisible]);
|
|
15966
|
+
const activeMobilePane = mobilePanes.includes(mobileView) ? mobileView : "chat";
|
|
15967
|
+
const showMobileToggle = mobilePanes.length > 1;
|
|
15602
15968
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15603
15969
|
AgentChatAdapterProvider,
|
|
15604
15970
|
{
|
|
@@ -15625,43 +15991,36 @@ function AgentChat({
|
|
|
15625
15991
|
),
|
|
15626
15992
|
style: { direction: "ltr", ...RIGHT_STACK_STYLE },
|
|
15627
15993
|
children: [
|
|
15628
|
-
showMobileToggle && /* @__PURE__ */ jsxRuntime.
|
|
15629
|
-
|
|
15630
|
-
|
|
15631
|
-
|
|
15632
|
-
|
|
15633
|
-
|
|
15634
|
-
"
|
|
15635
|
-
|
|
15636
|
-
|
|
15637
|
-
|
|
15638
|
-
|
|
15639
|
-
|
|
15640
|
-
|
|
15641
|
-
|
|
15642
|
-
|
|
15643
|
-
|
|
15644
|
-
|
|
15645
|
-
|
|
15646
|
-
|
|
15647
|
-
|
|
15648
|
-
|
|
15649
|
-
"flex-1 px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors",
|
|
15650
|
-
mobileView === "workspace" ? "border-primary text-foreground" : "border-transparent text-subtle hover:text-foreground"
|
|
15651
|
-
),
|
|
15652
|
-
children: "Workspace"
|
|
15653
|
-
}
|
|
15654
|
-
)
|
|
15655
|
-
] }),
|
|
15994
|
+
showMobileToggle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
15995
|
+
"div",
|
|
15996
|
+
{
|
|
15997
|
+
"data-testid": "mobile-pane-toggle",
|
|
15998
|
+
className: "md:hidden flex shrink-0 border-b border-line bg-card",
|
|
15999
|
+
children: mobilePanes.map((pane) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
16000
|
+
"button",
|
|
16001
|
+
{
|
|
16002
|
+
type: "button",
|
|
16003
|
+
onClick: () => setMobileView(pane),
|
|
16004
|
+
"aria-pressed": activeMobilePane === pane,
|
|
16005
|
+
className: cn(
|
|
16006
|
+
"flex-1 min-h-11 px-4 py-3 text-sm font-medium border-b-2 -mb-px transition-colors",
|
|
16007
|
+
activeMobilePane === pane ? "border-primary text-foreground" : "border-transparent text-subtle hover:text-foreground"
|
|
16008
|
+
),
|
|
16009
|
+
children: MOBILE_PANE_LABELS[pane]
|
|
16010
|
+
},
|
|
16011
|
+
pane
|
|
16012
|
+
))
|
|
16013
|
+
}
|
|
16014
|
+
),
|
|
15656
16015
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
15657
16016
|
"div",
|
|
15658
16017
|
{
|
|
15659
16018
|
"data-testid": "chat-panel",
|
|
15660
|
-
"data-mobile-view":
|
|
16019
|
+
"data-mobile-view": activeMobilePane,
|
|
15661
16020
|
className: cn(
|
|
15662
|
-
// Phone: full width column, hidden
|
|
16021
|
+
// Phone: full width column, hidden while another pane is picked.
|
|
15663
16022
|
"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
|
|
15664
|
-
showMobileToggle && "data-[mobile-view=workspace]:hidden md:flex!",
|
|
16023
|
+
showMobileToggle && "data-[mobile-view=browser]:hidden data-[mobile-view=workspace]:hidden md:flex!",
|
|
15665
16024
|
// md+: positioning depends on whether the browser/workspace
|
|
15666
16025
|
// right stack is laid out. When the browser pane is shown
|
|
15667
16026
|
// we pin a fixed-width column on the right and absolutely
|
|
@@ -15718,7 +16077,7 @@ function AgentChat({
|
|
|
15718
16077
|
"div",
|
|
15719
16078
|
{
|
|
15720
16079
|
"data-testid": "right-stack",
|
|
15721
|
-
"data-mobile-view":
|
|
16080
|
+
"data-mobile-view": activeMobilePane,
|
|
15722
16081
|
className: cn(
|
|
15723
16082
|
// Phone: full width column, hidden when chat tab active.
|
|
15724
16083
|
"flex min-h-0 min-w-0 flex-1 flex-col overflow-hidden",
|
|
@@ -15736,9 +16095,13 @@ function AgentChat({
|
|
|
15736
16095
|
"div",
|
|
15737
16096
|
{
|
|
15738
16097
|
"data-testid": "browser-panel",
|
|
16098
|
+
"data-mobile-view": activeMobilePane,
|
|
15739
16099
|
className: cn(
|
|
15740
16100
|
"min-h-0 w-full overflow-hidden",
|
|
15741
|
-
|
|
16101
|
+
// With both panes the desktop splits the column in half.
|
|
16102
|
+
// A phone gives whichever pane is selected the whole
|
|
16103
|
+
// height instead — see the mobile toggle above.
|
|
16104
|
+
bothPanesVisible ? "h-full data-[mobile-view=workspace]:hidden md:block! md:h-1/2 md:border-b md:border-line" : "h-full"
|
|
15742
16105
|
),
|
|
15743
16106
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15744
16107
|
BrowserPane,
|
|
@@ -15755,7 +16118,8 @@ function AgentChat({
|
|
|
15755
16118
|
"div",
|
|
15756
16119
|
{
|
|
15757
16120
|
"data-testid": "workspace-panel-frame",
|
|
15758
|
-
|
|
16121
|
+
"data-mobile-view": activeMobilePane,
|
|
16122
|
+
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",
|
|
15759
16123
|
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
15760
16124
|
WorkspacePanel,
|
|
15761
16125
|
{
|
|
@@ -16055,7 +16419,7 @@ function IntegrationsPage({ agentId, adapter, onBack }) {
|
|
|
16055
16419
|
{
|
|
16056
16420
|
type: "button",
|
|
16057
16421
|
onClick: onBack,
|
|
16058
|
-
className: "flex w-fit items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
|
|
16422
|
+
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",
|
|
16059
16423
|
children: [
|
|
16060
16424
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeftIcon, { className: "h-4 w-4" }),
|
|
16061
16425
|
"Back"
|
|
@@ -16199,7 +16563,7 @@ function CodingAgentsPanel({ agentId, adapter, onBack }) {
|
|
|
16199
16563
|
{
|
|
16200
16564
|
type: "button",
|
|
16201
16565
|
onClick: onBack,
|
|
16202
|
-
className: "flex w-fit items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
|
|
16566
|
+
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",
|
|
16203
16567
|
children: [
|
|
16204
16568
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeftIcon, { className: "h-4 w-4" }),
|
|
16205
16569
|
"Back"
|
|
@@ -16340,7 +16704,7 @@ function ProviderCard({
|
|
|
16340
16704
|
type: "button",
|
|
16341
16705
|
disabled: busy,
|
|
16342
16706
|
onClick: submit,
|
|
16343
|
-
className: "inline-flex items-center justify-center gap-1.5 rounded-md border border-border px-3
|
|
16707
|
+
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",
|
|
16344
16708
|
children: [
|
|
16345
16709
|
busy && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
16346
16710
|
connected ? "Update" : "Connect"
|
|
@@ -16360,6 +16724,65 @@ function ProviderCard({
|
|
|
16360
16724
|
] })
|
|
16361
16725
|
] });
|
|
16362
16726
|
}
|
|
16727
|
+
|
|
16728
|
+
// src/components/inbox/inbox-answers.ts
|
|
16729
|
+
function hasChoices2(question) {
|
|
16730
|
+
return (question.choices?.length ?? 0) > 0;
|
|
16731
|
+
}
|
|
16732
|
+
function parseInboxQuestions(item) {
|
|
16733
|
+
const raw = item.payload.questions;
|
|
16734
|
+
if (!Array.isArray(raw)) return [];
|
|
16735
|
+
return raw.map((entry) => {
|
|
16736
|
+
const question = entry;
|
|
16737
|
+
const choices = Array.isArray(question.choices) ? question.choices.map((choice) => ({
|
|
16738
|
+
label: typeof choice.label === "string" ? choice.label : "",
|
|
16739
|
+
description: typeof choice.description === "string" ? choice.description : void 0
|
|
16740
|
+
})).filter((choice) => choice.label) : void 0;
|
|
16741
|
+
return {
|
|
16742
|
+
prompt: typeof question.prompt === "string" ? question.prompt : "",
|
|
16743
|
+
choices,
|
|
16744
|
+
allowOther: question.allow_other !== false
|
|
16745
|
+
};
|
|
16746
|
+
}).filter((question) => question.prompt);
|
|
16747
|
+
}
|
|
16748
|
+
function isOtherAnswer(question, draft) {
|
|
16749
|
+
return hasChoices2(question) && !!draft?.useOther;
|
|
16750
|
+
}
|
|
16751
|
+
function answerText(question, draft) {
|
|
16752
|
+
const fromMenu = hasChoices2(question) && !draft?.useOther;
|
|
16753
|
+
return (fromMenu ? draft?.picked ?? "" : draft?.typed ?? "").trim();
|
|
16754
|
+
}
|
|
16755
|
+
function buildInboxResponse(question, draft) {
|
|
16756
|
+
return {
|
|
16757
|
+
question: question.prompt,
|
|
16758
|
+
answer: answerText(question, draft),
|
|
16759
|
+
is_other: isOtherAnswer(question, draft)
|
|
16760
|
+
};
|
|
16761
|
+
}
|
|
16762
|
+
var TICK_MS = 3e4;
|
|
16763
|
+
function formatExpiresIn(expiresAtIso, nowMs) {
|
|
16764
|
+
const remainingMs = Date.parse(expiresAtIso) - nowMs;
|
|
16765
|
+
if (remainingMs <= 0) return "Expired";
|
|
16766
|
+
if (remainingMs < 6e4) return "Expires in under a minute";
|
|
16767
|
+
const minutes = Math.round(remainingMs / 6e4);
|
|
16768
|
+
return `Expires in ~${minutes} minute${minutes === 1 ? "" : "s"}`;
|
|
16769
|
+
}
|
|
16770
|
+
function useAnswerWindow(expiresAtIso) {
|
|
16771
|
+
const [now, setNow] = react.useState(() => Date.now());
|
|
16772
|
+
react.useEffect(() => {
|
|
16773
|
+
const id = setInterval(() => setNow(Date.now()), TICK_MS);
|
|
16774
|
+
return () => clearInterval(id);
|
|
16775
|
+
}, []);
|
|
16776
|
+
if (!expiresAtIso) {
|
|
16777
|
+
return { expired: false, label: "" };
|
|
16778
|
+
}
|
|
16779
|
+
return {
|
|
16780
|
+
expired: Date.parse(expiresAtIso) <= now,
|
|
16781
|
+
label: formatExpiresIn(expiresAtIso, now)
|
|
16782
|
+
};
|
|
16783
|
+
}
|
|
16784
|
+
|
|
16785
|
+
// src/components/inbox/inbox-panel.tsx
|
|
16363
16786
|
init_utils();
|
|
16364
16787
|
var DEFAULT_LIMIT = 50;
|
|
16365
16788
|
function requireInboxAdapter(adapter) {
|
|
@@ -16394,8 +16817,33 @@ function kindIcon(kind) {
|
|
|
16394
16817
|
if (kind === "progress_checkin") return lucideReact.TimerIcon;
|
|
16395
16818
|
return lucideReact.CircleDotIcon;
|
|
16396
16819
|
}
|
|
16820
|
+
var VIEWS = {
|
|
16821
|
+
active: {
|
|
16822
|
+
statuses: ["pending"],
|
|
16823
|
+
kinds: ["input_required", "action_required", "governance_gate"],
|
|
16824
|
+
empty: "Nothing needs you right now"
|
|
16825
|
+
},
|
|
16826
|
+
updates: {
|
|
16827
|
+
statuses: ["pending"],
|
|
16828
|
+
kinds: ["task_complete", "progress_checkin"],
|
|
16829
|
+
empty: "No news from your agent"
|
|
16830
|
+
},
|
|
16831
|
+
history: {
|
|
16832
|
+
statuses: ["acknowledged", "responded", "expired"],
|
|
16833
|
+
empty: "No history yet"
|
|
16834
|
+
}
|
|
16835
|
+
};
|
|
16836
|
+
var VIEW_NAMES = Object.keys(VIEWS);
|
|
16837
|
+
function belongsToView(item, view) {
|
|
16838
|
+
const kinds = VIEWS[view].kinds;
|
|
16839
|
+
return VIEWS[view].statuses.includes(item.status) && (kinds === void 0 || kinds.includes(item.kind));
|
|
16840
|
+
}
|
|
16397
16841
|
function sortItems(items) {
|
|
16398
|
-
|
|
16842
|
+
const byId = /* @__PURE__ */ new Map();
|
|
16843
|
+
for (const item of items) {
|
|
16844
|
+
if (!byId.has(item.id)) byId.set(item.id, item);
|
|
16845
|
+
}
|
|
16846
|
+
return [...byId.values()].sort((a, b) => {
|
|
16399
16847
|
if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt ? -1 : 1;
|
|
16400
16848
|
return b.id - a.id;
|
|
16401
16849
|
});
|
|
@@ -16409,37 +16857,106 @@ function InboxBody({ body, muted }) {
|
|
|
16409
16857
|
}
|
|
16410
16858
|
);
|
|
16411
16859
|
}
|
|
16860
|
+
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";
|
|
16861
|
+
function errorMessage(error, fallback) {
|
|
16862
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
16863
|
+
}
|
|
16864
|
+
function useAction() {
|
|
16865
|
+
const [error, setError] = react.useState(null);
|
|
16866
|
+
const [busy, setBusy] = react.useState(false);
|
|
16867
|
+
const run = react.useCallback(
|
|
16868
|
+
async (fallback, action) => {
|
|
16869
|
+
setBusy(true);
|
|
16870
|
+
setError(null);
|
|
16871
|
+
try {
|
|
16872
|
+
await action();
|
|
16873
|
+
} catch (err) {
|
|
16874
|
+
setError(errorMessage(err, fallback));
|
|
16875
|
+
} finally {
|
|
16876
|
+
setBusy(false);
|
|
16877
|
+
}
|
|
16878
|
+
},
|
|
16879
|
+
[]
|
|
16880
|
+
);
|
|
16881
|
+
return { error, busy, run };
|
|
16882
|
+
}
|
|
16883
|
+
function ActionError({ message }) {
|
|
16884
|
+
if (!message) return null;
|
|
16885
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { role: "alert", className: "text-sm text-destructive", children: message });
|
|
16886
|
+
}
|
|
16887
|
+
function ExpiryNote({ label, expired }) {
|
|
16888
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16889
|
+
"p",
|
|
16890
|
+
{
|
|
16891
|
+
className: cn(
|
|
16892
|
+
"text-xs",
|
|
16893
|
+
expired ? "text-muted-foreground" : "text-foreground"
|
|
16894
|
+
),
|
|
16895
|
+
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.`
|
|
16896
|
+
}
|
|
16897
|
+
);
|
|
16898
|
+
}
|
|
16899
|
+
var OTHER_OPTION = "other";
|
|
16412
16900
|
function QuestionInput({
|
|
16413
|
-
|
|
16414
|
-
|
|
16901
|
+
question,
|
|
16902
|
+
draft,
|
|
16415
16903
|
disabled,
|
|
16416
|
-
|
|
16417
|
-
|
|
16904
|
+
onPickChoice,
|
|
16905
|
+
onChooseOther,
|
|
16906
|
+
onType
|
|
16418
16907
|
}) {
|
|
16908
|
+
const { prompt, choices, allowOther } = question;
|
|
16909
|
+
const typed = draft?.typed ?? "";
|
|
16910
|
+
const useOther = !!draft?.useOther;
|
|
16419
16911
|
if (choices && choices.length > 0) {
|
|
16420
|
-
|
|
16421
|
-
|
|
16422
|
-
{
|
|
16423
|
-
"aria-label": prompt,
|
|
16424
|
-
className: "h-9 w-full border border-line bg-background px-2 text-sm text-foreground outline-none focus:border-primary disabled:opacity-50",
|
|
16425
|
-
disabled,
|
|
16426
|
-
value,
|
|
16427
|
-
onChange: (event) => onChange(event.target.value),
|
|
16428
|
-
children: [
|
|
16429
|
-
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: "Select" }),
|
|
16430
|
-
choices.map((choice) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: choice.label, children: choice.label }, choice.label))
|
|
16431
|
-
]
|
|
16432
|
-
}
|
|
16912
|
+
const pickedIndex = choices.findIndex(
|
|
16913
|
+
(choice) => choice.label === draft?.picked
|
|
16433
16914
|
);
|
|
16915
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1.5", children: [
|
|
16916
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16917
|
+
"select",
|
|
16918
|
+
{
|
|
16919
|
+
"aria-label": prompt,
|
|
16920
|
+
className: FIELD_CLASS,
|
|
16921
|
+
disabled,
|
|
16922
|
+
value: useOther ? OTHER_OPTION : pickedIndex >= 0 ? String(pickedIndex) : "",
|
|
16923
|
+
onChange: (event) => {
|
|
16924
|
+
const value = event.target.value;
|
|
16925
|
+
if (value === OTHER_OPTION) {
|
|
16926
|
+
onChooseOther();
|
|
16927
|
+
return;
|
|
16928
|
+
}
|
|
16929
|
+
const choice = choices[Number(value)];
|
|
16930
|
+
if (choice) onPickChoice(choice.label);
|
|
16931
|
+
},
|
|
16932
|
+
children: [
|
|
16933
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Select" }),
|
|
16934
|
+
choices.map((choice, index) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: String(index), children: choice.label }, choice.label)),
|
|
16935
|
+
allowOther && /* @__PURE__ */ jsxRuntime.jsx("option", { value: OTHER_OPTION, children: "Other" })
|
|
16936
|
+
]
|
|
16937
|
+
}
|
|
16938
|
+
),
|
|
16939
|
+
useOther && /* @__PURE__ */ jsxRuntime.jsx(
|
|
16940
|
+
"input",
|
|
16941
|
+
{
|
|
16942
|
+
"aria-label": `${prompt} (other)`,
|
|
16943
|
+
className: FIELD_CLASS,
|
|
16944
|
+
placeholder: "Type your answer\u2026",
|
|
16945
|
+
disabled,
|
|
16946
|
+
value: typed,
|
|
16947
|
+
onChange: (event) => onType(event.target.value)
|
|
16948
|
+
}
|
|
16949
|
+
)
|
|
16950
|
+
] });
|
|
16434
16951
|
}
|
|
16435
16952
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
16436
16953
|
"input",
|
|
16437
16954
|
{
|
|
16438
16955
|
"aria-label": prompt,
|
|
16439
|
-
className:
|
|
16956
|
+
className: FIELD_CLASS,
|
|
16440
16957
|
disabled,
|
|
16441
|
-
value,
|
|
16442
|
-
onChange: (event) =>
|
|
16958
|
+
value: typed,
|
|
16959
|
+
onChange: (event) => onType(event.target.value)
|
|
16443
16960
|
}
|
|
16444
16961
|
);
|
|
16445
16962
|
}
|
|
@@ -16450,47 +16967,45 @@ function InboxDetailActions({
|
|
|
16450
16967
|
onSessionSelect,
|
|
16451
16968
|
children
|
|
16452
16969
|
}) {
|
|
16453
|
-
const
|
|
16970
|
+
const { error, busy: deleting, run } = useAction();
|
|
16454
16971
|
async function deleteItem() {
|
|
16455
16972
|
if (!adapter.deleteInboxItem) return;
|
|
16456
|
-
|
|
16457
|
-
try {
|
|
16458
|
-
await onDeleted(item.id);
|
|
16459
|
-
} finally {
|
|
16460
|
-
setDeleting(false);
|
|
16461
|
-
}
|
|
16973
|
+
await run("Failed to delete", () => onDeleted(item.id));
|
|
16462
16974
|
}
|
|
16463
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
16464
|
-
/* @__PURE__ */ jsxRuntime.
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16468
|
-
|
|
16469
|
-
|
|
16470
|
-
|
|
16471
|
-
|
|
16472
|
-
|
|
16473
|
-
|
|
16474
|
-
|
|
16475
|
-
|
|
16476
|
-
|
|
16477
|
-
|
|
16478
|
-
|
|
16479
|
-
|
|
16480
|
-
|
|
16481
|
-
|
|
16482
|
-
|
|
16483
|
-
|
|
16484
|
-
|
|
16485
|
-
|
|
16486
|
-
|
|
16487
|
-
|
|
16488
|
-
|
|
16489
|
-
|
|
16490
|
-
|
|
16491
|
-
|
|
16492
|
-
|
|
16493
|
-
|
|
16975
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
|
|
16976
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionError, { message: error }),
|
|
16977
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap gap-2", children: [
|
|
16978
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16979
|
+
Button,
|
|
16980
|
+
{
|
|
16981
|
+
type: "button",
|
|
16982
|
+
size: "sm",
|
|
16983
|
+
onClick: () => onSessionSelect?.(item.sessionId, item),
|
|
16984
|
+
"aria-label": "Open session",
|
|
16985
|
+
children: [
|
|
16986
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLinkIcon, { className: "size-3.5" }),
|
|
16987
|
+
"Open session"
|
|
16988
|
+
]
|
|
16989
|
+
}
|
|
16990
|
+
),
|
|
16991
|
+
children,
|
|
16992
|
+
adapter.deleteInboxItem && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16993
|
+
Button,
|
|
16994
|
+
{
|
|
16995
|
+
type: "button",
|
|
16996
|
+
size: "sm",
|
|
16997
|
+
variant: "ghost",
|
|
16998
|
+
onClick: () => void deleteItem(),
|
|
16999
|
+
disabled: deleting,
|
|
17000
|
+
"aria-label": "Delete inbox item",
|
|
17001
|
+
title: "Delete inbox item",
|
|
17002
|
+
children: [
|
|
17003
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, { className: "size-3.5" }),
|
|
17004
|
+
deleting ? "Deleting" : "Delete"
|
|
17005
|
+
]
|
|
17006
|
+
}
|
|
17007
|
+
)
|
|
17008
|
+
] })
|
|
16494
17009
|
] });
|
|
16495
17010
|
}
|
|
16496
17011
|
function InputRequiredDetail({
|
|
@@ -16500,36 +17015,31 @@ function InputRequiredDetail({
|
|
|
16500
17015
|
onDeleted,
|
|
16501
17016
|
onSessionSelect
|
|
16502
17017
|
}) {
|
|
16503
|
-
const questions =
|
|
16504
|
-
|
|
16505
|
-
|
|
16506
|
-
|
|
16507
|
-
|
|
16508
|
-
|
|
16509
|
-
|
|
16510
|
-
|
|
16511
|
-
|
|
16512
|
-
|
|
16513
|
-
const canSubmit = questions.every((question) =>
|
|
17018
|
+
const questions = react.useMemo(() => parseInboxQuestions(item), [item]);
|
|
17019
|
+
const [drafts, setDrafts] = react.useState({});
|
|
17020
|
+
const { error, busy: submitting, run } = useAction();
|
|
17021
|
+
const { expired, label: expiryLabel } = useAnswerWindow(item.expiresAt);
|
|
17022
|
+
const pending = item.status === "pending" && !expired;
|
|
17023
|
+
const disabled = !pending || submitting;
|
|
17024
|
+
const editDraft = (prompt, patch) => setDrafts((current) => ({
|
|
17025
|
+
...current,
|
|
17026
|
+
[prompt]: { ...current[prompt], ...patch }
|
|
17027
|
+
}));
|
|
17028
|
+
const canSubmit = questions.length > 0 && questions.every((question) => answerText(question, drafts[question.prompt]));
|
|
16514
17029
|
async function submit() {
|
|
16515
17030
|
const toolCallId = typeof item.payload.tool_call_id === "string" ? item.payload.tool_call_id : "";
|
|
16516
17031
|
if (!toolCallId || !canSubmit) return;
|
|
16517
|
-
|
|
16518
|
-
|
|
16519
|
-
|
|
16520
|
-
|
|
16521
|
-
answer: answers[question.prompt]?.trim() ?? "",
|
|
16522
|
-
is_other: false
|
|
16523
|
-
}));
|
|
17032
|
+
await run("Failed to submit your answer", async () => {
|
|
17033
|
+
const responses = questions.map(
|
|
17034
|
+
(question) => buildInboxResponse(question, drafts[question.prompt])
|
|
17035
|
+
);
|
|
16524
17036
|
await adapter.submitAskUserQuestionResponse({
|
|
16525
17037
|
sessionId: item.sessionId,
|
|
16526
17038
|
toolCallId,
|
|
16527
17039
|
responses
|
|
16528
17040
|
});
|
|
16529
17041
|
onUpdated(await adapter.getInboxItem({ itemId: item.id }));
|
|
16530
|
-
}
|
|
16531
|
-
setSubmitting(false);
|
|
16532
|
-
}
|
|
17042
|
+
});
|
|
16533
17043
|
}
|
|
16534
17044
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
16535
17045
|
item.body && /* @__PURE__ */ jsxRuntime.jsx(InboxBody, { body: item.body, muted: true }),
|
|
@@ -16538,17 +17048,25 @@ function InputRequiredDetail({
|
|
|
16538
17048
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16539
17049
|
QuestionInput,
|
|
16540
17050
|
{
|
|
16541
|
-
|
|
16542
|
-
|
|
17051
|
+
question,
|
|
17052
|
+
draft: drafts[question.prompt],
|
|
16543
17053
|
disabled,
|
|
16544
|
-
|
|
16545
|
-
|
|
16546
|
-
|
|
16547
|
-
|
|
16548
|
-
|
|
17054
|
+
onPickChoice: (label) => (
|
|
17055
|
+
// Dropping the typed value keeps an abandoned draft from
|
|
17056
|
+
// resurfacing if the user switches back to Other.
|
|
17057
|
+
editDraft(question.prompt, {
|
|
17058
|
+
picked: label,
|
|
17059
|
+
typed: "",
|
|
17060
|
+
useOther: false
|
|
17061
|
+
})
|
|
17062
|
+
),
|
|
17063
|
+
onChooseOther: () => editDraft(question.prompt, { useOther: true }),
|
|
17064
|
+
onType: (value) => editDraft(question.prompt, { typed: value })
|
|
16549
17065
|
}
|
|
16550
17066
|
)
|
|
16551
17067
|
] }, question.prompt)),
|
|
17068
|
+
item.status === "pending" && /* @__PURE__ */ jsxRuntime.jsx(ExpiryNote, { label: expiryLabel, expired }),
|
|
17069
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionError, { message: error }),
|
|
16552
17070
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16553
17071
|
InboxDetailActions,
|
|
16554
17072
|
{
|
|
@@ -16578,8 +17096,11 @@ function AckDetail({
|
|
|
16578
17096
|
onDeleted,
|
|
16579
17097
|
onSessionSelect
|
|
16580
17098
|
}) {
|
|
17099
|
+
const { error, busy, run } = useAction();
|
|
16581
17100
|
async function acknowledge() {
|
|
16582
|
-
|
|
17101
|
+
await run("Failed to acknowledge", async () => {
|
|
17102
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
17103
|
+
});
|
|
16583
17104
|
}
|
|
16584
17105
|
const outcome = typeof item.payload.outcome === "string" ? item.payload.outcome : "";
|
|
16585
17106
|
const duration = typeof item.payload.duration_seconds === "number" ? item.payload.duration_seconds : null;
|
|
@@ -16594,6 +17115,7 @@ function AckDetail({
|
|
|
16594
17115
|
duration,
|
|
16595
17116
|
" s)"
|
|
16596
17117
|
] }),
|
|
17118
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionError, { message: error }),
|
|
16597
17119
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16598
17120
|
InboxDetailActions,
|
|
16599
17121
|
{
|
|
@@ -16607,10 +17129,11 @@ function AckDetail({
|
|
|
16607
17129
|
type: "button",
|
|
16608
17130
|
size: "sm",
|
|
16609
17131
|
onClick: () => void acknowledge(),
|
|
17132
|
+
disabled: busy,
|
|
16610
17133
|
"aria-label": "Acknowledge inbox item",
|
|
16611
17134
|
children: [
|
|
16612
17135
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
16613
|
-
"Acknowledge"
|
|
17136
|
+
busy ? "Saving" : "Acknowledge"
|
|
16614
17137
|
]
|
|
16615
17138
|
}
|
|
16616
17139
|
)
|
|
@@ -16625,15 +17148,18 @@ function GovernanceDetail({
|
|
|
16625
17148
|
onDeleted,
|
|
16626
17149
|
onSessionSelect
|
|
16627
17150
|
}) {
|
|
17151
|
+
const { error, busy, run } = useAction();
|
|
16628
17152
|
async function decide(decision) {
|
|
16629
|
-
|
|
16630
|
-
|
|
16631
|
-
|
|
17153
|
+
await run(`Failed to ${decision}`, async () => {
|
|
17154
|
+
onUpdated(
|
|
17155
|
+
await adapter.respondGovernanceInboxItem({ itemId: item.id, decision })
|
|
17156
|
+
);
|
|
17157
|
+
});
|
|
16632
17158
|
}
|
|
16633
17159
|
const toolName = typeof item.payload.tool_name === "string" ? item.payload.tool_name : "tool";
|
|
16634
17160
|
const args = typeof item.payload.arguments_excerpt === "string" ? item.payload.arguments_excerpt : "";
|
|
16635
17161
|
const reason = typeof item.payload.deny_reason === "string" ? item.payload.deny_reason : "";
|
|
16636
|
-
const disabled = item.status !== "pending";
|
|
17162
|
+
const disabled = item.status !== "pending" || busy;
|
|
16637
17163
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
16638
17164
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
16639
17165
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-base font-semibold text-foreground", children: [
|
|
@@ -16644,6 +17170,7 @@ function GovernanceDetail({
|
|
|
16644
17170
|
reason && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: reason })
|
|
16645
17171
|
] }),
|
|
16646
17172
|
args && /* @__PURE__ */ jsxRuntime.jsx("pre", { className: "overflow-x-auto border border-line bg-muted p-3 text-xs", children: args }),
|
|
17173
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionError, { message: error }),
|
|
16647
17174
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16648
17175
|
InboxDetailActions,
|
|
16649
17176
|
{
|
|
@@ -16685,23 +17212,22 @@ function ActionRequiredDetail({
|
|
|
16685
17212
|
onDeleted,
|
|
16686
17213
|
onSessionSelect
|
|
16687
17214
|
}) {
|
|
16688
|
-
const
|
|
17215
|
+
const { error, busy: submitting, run } = useAction();
|
|
16689
17216
|
const actionType = typeof item.payload.action_type === "string" ? item.payload.action_type : "";
|
|
16690
17217
|
const context = typeof item.payload.context === "string" ? item.payload.context : "";
|
|
16691
17218
|
const disabled = item.status !== "pending" || submitting || !adapter.respondActionRequiredInboxItem;
|
|
16692
17219
|
async function complete() {
|
|
16693
|
-
|
|
16694
|
-
|
|
16695
|
-
|
|
16696
|
-
onUpdated(await
|
|
16697
|
-
}
|
|
16698
|
-
setSubmitting(false);
|
|
16699
|
-
}
|
|
17220
|
+
const respond = adapter.respondActionRequiredInboxItem;
|
|
17221
|
+
if (!respond) return;
|
|
17222
|
+
await run("Failed to mark the action complete", async () => {
|
|
17223
|
+
onUpdated(await respond({ itemId: item.id }));
|
|
17224
|
+
});
|
|
16700
17225
|
}
|
|
16701
17226
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
16702
17227
|
item.body && /* @__PURE__ */ jsxRuntime.jsx(InboxBody, { body: item.body }),
|
|
16703
17228
|
context && context !== item.body && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "whitespace-pre-wrap text-sm text-muted-foreground", children: context }),
|
|
16704
17229
|
actionType && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "secondary", children: actionType }),
|
|
17230
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionError, { message: error }),
|
|
16705
17231
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16706
17232
|
InboxDetailActions,
|
|
16707
17233
|
{
|
|
@@ -16734,8 +17260,11 @@ function ProgressDetail({
|
|
|
16734
17260
|
onDeleted,
|
|
16735
17261
|
onSessionSelect
|
|
16736
17262
|
}) {
|
|
17263
|
+
const { error, busy, run } = useAction();
|
|
16737
17264
|
async function acknowledge() {
|
|
16738
|
-
|
|
17265
|
+
await run("Failed to acknowledge", async () => {
|
|
17266
|
+
onUpdated(await adapter.acknowledgeInboxItem({ itemId: item.id }));
|
|
17267
|
+
});
|
|
16739
17268
|
}
|
|
16740
17269
|
const rows = [];
|
|
16741
17270
|
for (const row of [
|
|
@@ -16753,6 +17282,7 @@ function ProgressDetail({
|
|
|
16753
17282
|
/* @__PURE__ */ jsxRuntime.jsx("dt", { className: "w-20 shrink-0", children: String(label) }),
|
|
16754
17283
|
/* @__PURE__ */ jsxRuntime.jsx("dd", { className: "min-w-0", children: String(value) })
|
|
16755
17284
|
] }, String(label))) }),
|
|
17285
|
+
/* @__PURE__ */ jsxRuntime.jsx(ActionError, { message: error }),
|
|
16756
17286
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16757
17287
|
InboxDetailActions,
|
|
16758
17288
|
{
|
|
@@ -16766,10 +17296,11 @@ function ProgressDetail({
|
|
|
16766
17296
|
type: "button",
|
|
16767
17297
|
size: "sm",
|
|
16768
17298
|
onClick: () => void acknowledge(),
|
|
17299
|
+
disabled: busy,
|
|
16769
17300
|
"aria-label": "Acknowledge inbox item",
|
|
16770
17301
|
children: [
|
|
16771
17302
|
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }),
|
|
16772
|
-
"Acknowledge"
|
|
17303
|
+
busy ? "Saving" : "Acknowledge"
|
|
16773
17304
|
]
|
|
16774
17305
|
}
|
|
16775
17306
|
)
|
|
@@ -16782,10 +17313,23 @@ function InboxDetail({
|
|
|
16782
17313
|
adapter,
|
|
16783
17314
|
onUpdated,
|
|
16784
17315
|
onDeleted,
|
|
16785
|
-
onSessionSelect
|
|
17316
|
+
onSessionSelect,
|
|
17317
|
+
onBack
|
|
16786
17318
|
}) {
|
|
16787
17319
|
const Icon = kindIcon(item.kind);
|
|
16788
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "min-w-0 flex-1 overflow-y-auto p-6", children: [
|
|
17320
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "min-w-0 flex-1 overflow-y-auto p-4 md:p-6", children: [
|
|
17321
|
+
onBack && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17322
|
+
"button",
|
|
17323
|
+
{
|
|
17324
|
+
type: "button",
|
|
17325
|
+
onClick: onBack,
|
|
17326
|
+
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",
|
|
17327
|
+
children: [
|
|
17328
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeftIcon, { className: "size-4" }),
|
|
17329
|
+
"Inbox"
|
|
17330
|
+
]
|
|
17331
|
+
}
|
|
17332
|
+
),
|
|
16789
17333
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-5 flex min-w-0 items-start gap-3", children: [
|
|
16790
17334
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex size-8 shrink-0 items-center justify-center border border-line text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "size-4" }) }),
|
|
16791
17335
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
@@ -16857,6 +17401,7 @@ function InboxPanel({
|
|
|
16857
17401
|
const [items, setItems] = react.useState([]);
|
|
16858
17402
|
const [nextCursor, setNextCursor] = react.useState(null);
|
|
16859
17403
|
const [internalSelectedId, setInternalSelectedId] = react.useState(null);
|
|
17404
|
+
const [view, setView] = react.useState("active");
|
|
16860
17405
|
const [loading, setLoading] = react.useState(false);
|
|
16861
17406
|
const [error, setError] = react.useState(null);
|
|
16862
17407
|
const requestId = react.useRef(0);
|
|
@@ -16865,13 +17410,20 @@ function InboxPanel({
|
|
|
16865
17410
|
() => items.find((item) => item.id === selectedItemId) ?? null,
|
|
16866
17411
|
[items, selectedItemId]
|
|
16867
17412
|
);
|
|
17413
|
+
const viewRef = react.useRef(view);
|
|
17414
|
+
viewRef.current = view;
|
|
16868
17415
|
const applyItem = react.useCallback((nextItem) => {
|
|
16869
|
-
setItems(
|
|
16870
|
-
(current) =>
|
|
16871
|
-
|
|
16872
|
-
|
|
16873
|
-
|
|
17416
|
+
setItems((current) => {
|
|
17417
|
+
if (current.some((item) => item.id === nextItem.id)) {
|
|
17418
|
+
return sortItems([nextItem, ...current]);
|
|
17419
|
+
}
|
|
17420
|
+
return belongsToView(nextItem, viewRef.current) ? sortItems([nextItem, ...current]) : current;
|
|
17421
|
+
});
|
|
16874
17422
|
}, []);
|
|
17423
|
+
const clearSelection = react.useCallback(() => {
|
|
17424
|
+
if (selectedId === void 0) setInternalSelectedId(null);
|
|
17425
|
+
onSelectedIdChange?.(null);
|
|
17426
|
+
}, [onSelectedIdChange, selectedId]);
|
|
16875
17427
|
const selectItem = react.useCallback(
|
|
16876
17428
|
async (itemId) => {
|
|
16877
17429
|
if (selectedId === void 0) setInternalSelectedId(itemId);
|
|
@@ -16888,26 +17440,26 @@ function InboxPanel({
|
|
|
16888
17440
|
async (cursor) => {
|
|
16889
17441
|
const id = ++requestId.current;
|
|
16890
17442
|
setLoading(true);
|
|
16891
|
-
setError(null);
|
|
16892
17443
|
try {
|
|
16893
17444
|
const response = await inboxAdapter.listInbox({
|
|
17445
|
+
status: VIEWS[view].statuses,
|
|
17446
|
+
kind: VIEWS[view].kinds,
|
|
16894
17447
|
cursor: cursor ?? void 0,
|
|
16895
17448
|
limit
|
|
16896
17449
|
});
|
|
16897
17450
|
if (id !== requestId.current) return;
|
|
16898
|
-
setItems(
|
|
16899
|
-
(current) => sortItems(cursor ? [...current, ...response.items] : response.items)
|
|
16900
|
-
);
|
|
17451
|
+
setItems((current) => sortItems([...response.items, ...current]));
|
|
16901
17452
|
setNextCursor(response.nextCursor);
|
|
17453
|
+
setError(null);
|
|
16902
17454
|
} catch (err) {
|
|
16903
17455
|
if (id === requestId.current) {
|
|
16904
|
-
setError(err
|
|
17456
|
+
setError(errorMessage(err, "Failed to load inbox"));
|
|
16905
17457
|
}
|
|
16906
17458
|
} finally {
|
|
16907
17459
|
if (id === requestId.current) setLoading(false);
|
|
16908
17460
|
}
|
|
16909
17461
|
},
|
|
16910
|
-
[inboxAdapter, limit]
|
|
17462
|
+
[inboxAdapter, limit, view]
|
|
16911
17463
|
);
|
|
16912
17464
|
react.useEffect(() => {
|
|
16913
17465
|
void load(null);
|
|
@@ -16915,15 +17467,33 @@ function InboxPanel({
|
|
|
16915
17467
|
react.useEffect(() => {
|
|
16916
17468
|
const stream = inboxAdapter.openInboxStream();
|
|
16917
17469
|
stream.addEventListener("item", (event) => {
|
|
16918
|
-
|
|
16919
|
-
|
|
16920
|
-
|
|
17470
|
+
let itemId;
|
|
17471
|
+
try {
|
|
17472
|
+
itemId = JSON.parse(event.data).item_id;
|
|
17473
|
+
} catch {
|
|
17474
|
+
return;
|
|
17475
|
+
}
|
|
17476
|
+
if (typeof itemId !== "number") return;
|
|
17477
|
+
if (viewRef.current === "history") return;
|
|
17478
|
+
void inboxAdapter.getInboxItem({ itemId }).then(applyItem, () => void 0);
|
|
16921
17479
|
});
|
|
16922
17480
|
return () => stream.close();
|
|
16923
17481
|
}, [applyItem, inboxAdapter]);
|
|
16924
17482
|
function updateSelectedItem(item) {
|
|
16925
17483
|
applyItem(item);
|
|
16926
17484
|
}
|
|
17485
|
+
const selectView = react.useCallback(
|
|
17486
|
+
(next) => {
|
|
17487
|
+
if (next === view) return;
|
|
17488
|
+
setView(next);
|
|
17489
|
+
setItems([]);
|
|
17490
|
+
setNextCursor(null);
|
|
17491
|
+
setError(null);
|
|
17492
|
+
if (selectedId === void 0) setInternalSelectedId(null);
|
|
17493
|
+
onSelectedIdChange?.(null);
|
|
17494
|
+
},
|
|
17495
|
+
[onSelectedIdChange, selectedId, view]
|
|
17496
|
+
);
|
|
16927
17497
|
const deleteItem = react.useCallback(
|
|
16928
17498
|
async (itemId) => {
|
|
16929
17499
|
if (!inboxAdapter.deleteInboxItem) return;
|
|
@@ -16937,55 +17507,80 @@ function InboxPanel({
|
|
|
16937
17507
|
[inboxAdapter, onSelectedIdChange, selectedId, selectedItemId]
|
|
16938
17508
|
);
|
|
16939
17509
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-full min-h-0 min-w-0 bg-background text-foreground", children: [
|
|
16940
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16941
|
-
|
|
16942
|
-
|
|
16943
|
-
|
|
16944
|
-
|
|
16945
|
-
|
|
16946
|
-
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
|
|
16950
|
-
|
|
16951
|
-
|
|
17510
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
17511
|
+
"aside",
|
|
17512
|
+
{
|
|
17513
|
+
className: cn(
|
|
17514
|
+
"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",
|
|
17515
|
+
selectedItemId != null ? "hidden md:flex" : "w-full"
|
|
17516
|
+
),
|
|
17517
|
+
children: [
|
|
17518
|
+
!hideHeader && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-h-14 items-center gap-2 border-b border-line px-4", children: [
|
|
17519
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.InboxIcon, { className: "size-4 text-muted-foreground" }),
|
|
17520
|
+
/* @__PURE__ */ jsxRuntime.jsx("h1", { className: "font-semibold", children: title })
|
|
17521
|
+
] }),
|
|
17522
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1 border-b border-line px-2 py-2", children: VIEW_NAMES.map((value) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
16952
17523
|
"button",
|
|
16953
17524
|
{
|
|
16954
17525
|
type: "button",
|
|
16955
|
-
|
|
17526
|
+
onClick: () => selectView(value),
|
|
16956
17527
|
className: cn(
|
|
16957
|
-
"
|
|
16958
|
-
|
|
16959
|
-
!item.readAt && "font-medium text-foreground"
|
|
17528
|
+
"px-2 py-1 text-xs capitalize transition-colors",
|
|
17529
|
+
view === value ? "bg-line font-medium text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
16960
17530
|
),
|
|
16961
|
-
|
|
16962
|
-
children: [
|
|
16963
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
16964
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
16965
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
16966
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs uppercase tracking-wide text-muted-foreground", children: kindLabel(item.kind) }),
|
|
16967
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatRelative(item.createdAt) })
|
|
16968
|
-
] }),
|
|
16969
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block truncate text-sm", children: item.title }),
|
|
16970
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: statusLabel(item.status) })
|
|
16971
|
-
] })
|
|
16972
|
-
]
|
|
17531
|
+
children: value
|
|
16973
17532
|
},
|
|
16974
|
-
|
|
16975
|
-
)
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
|
|
16979
|
-
|
|
16980
|
-
|
|
16981
|
-
className: "
|
|
16982
|
-
|
|
16983
|
-
|
|
16984
|
-
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
|
|
16988
|
-
|
|
17533
|
+
value
|
|
17534
|
+
)) }),
|
|
17535
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
|
|
17536
|
+
error && items.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2 px-4 py-3", children: [
|
|
17537
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-destructive", children: error }),
|
|
17538
|
+
/* @__PURE__ */ jsxRuntime.jsx(Button, { type: "button", size: "sm", onClick: () => void load(null), children: "Try again" })
|
|
17539
|
+
] }),
|
|
17540
|
+
items.length === 0 && !error && !loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-8 text-sm text-muted-foreground", children: VIEWS[view].empty }),
|
|
17541
|
+
items.map((item) => {
|
|
17542
|
+
const Icon = kindIcon(item.kind);
|
|
17543
|
+
const selected = item.id === selectedItemId;
|
|
17544
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
17545
|
+
"button",
|
|
17546
|
+
{
|
|
17547
|
+
type: "button",
|
|
17548
|
+
"aria-label": `Open inbox item ${item.title}`,
|
|
17549
|
+
className: cn(
|
|
17550
|
+
"flex w-full min-w-0 gap-3 border-l-2 px-3 py-3 text-left transition-colors hover:bg-input",
|
|
17551
|
+
selected ? "border-l-primary bg-line text-foreground" : "border-l-transparent text-subtle",
|
|
17552
|
+
!item.readAt && "font-medium text-foreground"
|
|
17553
|
+
),
|
|
17554
|
+
onClick: () => void selectItem(item.id),
|
|
17555
|
+
children: [
|
|
17556
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "mt-0.5 size-4 shrink-0 text-muted-foreground" }),
|
|
17557
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "min-w-0 flex-1", children: [
|
|
17558
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 items-center justify-between gap-2", children: [
|
|
17559
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate text-xs uppercase tracking-wide text-muted-foreground", children: kindLabel(item.kind) }),
|
|
17560
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: formatRelative(item.createdAt) })
|
|
17561
|
+
] }),
|
|
17562
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block truncate text-sm", children: item.title }),
|
|
17563
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 block text-xs text-muted-foreground", children: statusLabel(item.status) })
|
|
17564
|
+
] })
|
|
17565
|
+
]
|
|
17566
|
+
},
|
|
17567
|
+
item.id
|
|
17568
|
+
);
|
|
17569
|
+
}),
|
|
17570
|
+
nextCursor && /* @__PURE__ */ jsxRuntime.jsx(
|
|
17571
|
+
"button",
|
|
17572
|
+
{
|
|
17573
|
+
type: "button",
|
|
17574
|
+
className: "w-full px-3 py-3 text-sm text-muted-foreground hover:bg-input",
|
|
17575
|
+
onClick: () => void load(nextCursor),
|
|
17576
|
+
disabled: loading,
|
|
17577
|
+
children: loading ? "Loading" : "Load more"
|
|
17578
|
+
}
|
|
17579
|
+
)
|
|
17580
|
+
] })
|
|
17581
|
+
]
|
|
17582
|
+
}
|
|
17583
|
+
),
|
|
16989
17584
|
selectedItem ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
16990
17585
|
InboxDetail,
|
|
16991
17586
|
{
|
|
@@ -16993,59 +17588,59 @@ function InboxPanel({
|
|
|
16993
17588
|
adapter: inboxAdapter,
|
|
16994
17589
|
onUpdated: updateSelectedItem,
|
|
16995
17590
|
onDeleted: deleteItem,
|
|
16996
|
-
onSessionSelect
|
|
16997
|
-
|
|
16998
|
-
|
|
17591
|
+
onSessionSelect,
|
|
17592
|
+
onBack: clearSelection
|
|
17593
|
+
},
|
|
17594
|
+
selectedItem.id
|
|
17595
|
+
) : /* @__PURE__ */ jsxRuntime.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" })
|
|
16999
17596
|
] });
|
|
17000
17597
|
}
|
|
17001
17598
|
function useInboxUnreadCount(adapter) {
|
|
17002
17599
|
const [unreadCount, setUnreadCount] = react.useState(0);
|
|
17003
17600
|
const [hasLoaded, setHasLoaded] = react.useState(false);
|
|
17004
17601
|
const [error, setError] = react.useState(null);
|
|
17602
|
+
const requestId = react.useRef(0);
|
|
17603
|
+
const listInbox = adapter.listInbox;
|
|
17604
|
+
const openInboxStream = adapter.openInboxStream;
|
|
17605
|
+
const refetch = react.useCallback(async () => {
|
|
17606
|
+
if (!listInbox) return;
|
|
17607
|
+
const id = ++requestId.current;
|
|
17608
|
+
try {
|
|
17609
|
+
const response = await listInbox({ status: "pending", limit: 200 });
|
|
17610
|
+
if (id !== requestId.current) return;
|
|
17611
|
+
setUnreadCount(response.items.filter((item) => !item.readAt).length);
|
|
17612
|
+
setError(null);
|
|
17613
|
+
} catch (err) {
|
|
17614
|
+
if (id !== requestId.current) return;
|
|
17615
|
+
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
17616
|
+
} finally {
|
|
17617
|
+
if (id === requestId.current) setHasLoaded(true);
|
|
17618
|
+
}
|
|
17619
|
+
}, [listInbox]);
|
|
17005
17620
|
react.useEffect(() => {
|
|
17006
|
-
|
|
17007
|
-
let stream = null;
|
|
17008
|
-
if (!adapter.listInbox || !adapter.openInboxStream) {
|
|
17621
|
+
if (!listInbox || !openInboxStream) {
|
|
17009
17622
|
setError("Inbox is not supported by this adapter.");
|
|
17010
17623
|
setHasLoaded(true);
|
|
17011
|
-
return
|
|
17012
|
-
cancelled = true;
|
|
17013
|
-
};
|
|
17624
|
+
return;
|
|
17014
17625
|
}
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
setUnreadCount(response.items.filter((item) => !item.readAt).length);
|
|
17018
|
-
setHasLoaded(true);
|
|
17019
|
-
}).catch((err) => {
|
|
17020
|
-
if (cancelled) return;
|
|
17021
|
-
setError(err instanceof Error ? err.message : "Failed to load inbox");
|
|
17022
|
-
setHasLoaded(true);
|
|
17023
|
-
});
|
|
17626
|
+
void refetch();
|
|
17627
|
+
let stream = null;
|
|
17024
17628
|
try {
|
|
17025
|
-
stream =
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
17030
|
-
setUnreadCount(ids.length);
|
|
17031
|
-
setHasLoaded(true);
|
|
17032
|
-
});
|
|
17033
|
-
stream.addEventListener("item", () => {
|
|
17034
|
-
if (cancelled) return;
|
|
17035
|
-
setUnreadCount((count) => count + 1);
|
|
17036
|
-
setHasLoaded(true);
|
|
17037
|
-
});
|
|
17038
|
-
stream.onerror = () => {
|
|
17039
|
-
if (!cancelled) setError("Inbox stream disconnected.");
|
|
17040
|
-
};
|
|
17629
|
+
stream = openInboxStream();
|
|
17630
|
+
const onChange = () => void refetch();
|
|
17631
|
+
stream.addEventListener("snapshot", onChange);
|
|
17632
|
+
stream.addEventListener("item", onChange);
|
|
17633
|
+
stream.onerror = () => setError("Inbox stream disconnected.");
|
|
17041
17634
|
} catch (err) {
|
|
17042
|
-
setError(
|
|
17635
|
+
setError(
|
|
17636
|
+
err instanceof Error ? err.message : "Failed to open inbox stream"
|
|
17637
|
+
);
|
|
17043
17638
|
}
|
|
17044
17639
|
return () => {
|
|
17045
|
-
|
|
17640
|
+
requestId.current += 1;
|
|
17046
17641
|
stream?.close();
|
|
17047
17642
|
};
|
|
17048
|
-
}, [
|
|
17643
|
+
}, [listInbox, openInboxStream, refetch]);
|
|
17049
17644
|
return { unreadCount, hasLoaded, error };
|
|
17050
17645
|
}
|
|
17051
17646
|
|