@invergent/agent-chat-react 1.8.0 → 1.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +957 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -1
- package/dist/index.d.ts +127 -1
- package/dist/index.js +946 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { cn } from './chunk-QSC4UIVT.js';
|
|
2
2
|
import { createContext, memo, useRef, useEffect, useCallback, useMemo, useState, lazy, useContext, Suspense, Children } from 'react';
|
|
3
|
-
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet,
|
|
3
|
+
import { ChevronDownIcon, AlertTriangle, ChevronDown, ChevronRight, RefreshCw, File as File$1, FileImage, FileAudio, FileVideo, FileText, FileCode, FileArchive, FileSpreadsheet, InboxIcon, Loader2, AlertCircle, Pause, Play, Ban, TargetIcon, ChevronRightIcon, CalendarClockIcon, UsersIcon, MessageSquareIcon, ZapIcon, Maximize2Icon, FolderOpenIcon, Loader2Icon, UploadIcon, RefreshCwIcon, AlertCircleIcon, ExternalLinkIcon, ClipboardCheckIcon, ShieldAlertIcon, TimerIcon, CircleDotIcon, XIcon, Trash2Icon, SquareIcon, ArrowDownIcon, RotateCcwIcon, MousePointer2Icon, DownloadIcon, TrashIcon, ImageIcon, FileTextIcon, CheckIcon, CopyIcon, FolderIcon, FileIcon, ChevronLeftIcon, MinusIcon, PlusIcon, SearchIcon, GitBranchIcon, ThumbsUpIcon, ThumbsDownIcon, ActivityIcon, EyeIcon, GlobeIcon, ListTodoIcon, CornerDownLeftIcon, XCircleIcon, CheckCircle2Icon, Code, TerminalIcon, CircleCheckIcon, CircleXIcon, SkullIcon, ClockIcon, CheckCircleIcon, CircleIcon } from 'lucide-react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
|
-
import { Collapsible as Collapsible$1, Slot,
|
|
5
|
+
import { Collapsible as Collapsible$1, Slot, Tooltip as Tooltip$1, Separator as Separator$1, Dialog as Dialog$1, ScrollArea as ScrollArea$1, Popover as Popover$1, DropdownMenu as DropdownMenu$1, HoverCard as HoverCard$1, Progress as Progress$1, Tabs as Tabs$1 } from 'radix-ui';
|
|
6
6
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
import { StickToBottom, useStickToBottomContext } from 'use-stick-to-bottom';
|
|
8
8
|
import { cjk } from '@streamdown/cjk';
|
|
@@ -597,6 +597,26 @@ var ConversationScrollButton = ({
|
|
|
597
597
|
}
|
|
598
598
|
);
|
|
599
599
|
};
|
|
600
|
+
function Separator({
|
|
601
|
+
className,
|
|
602
|
+
orientation = "horizontal",
|
|
603
|
+
decorative = true,
|
|
604
|
+
...props
|
|
605
|
+
}) {
|
|
606
|
+
return /* @__PURE__ */ jsx(
|
|
607
|
+
Separator$1.Root,
|
|
608
|
+
{
|
|
609
|
+
"data-slot": "separator",
|
|
610
|
+
decorative,
|
|
611
|
+
orientation,
|
|
612
|
+
className: cn(
|
|
613
|
+
"shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
|
|
614
|
+
className
|
|
615
|
+
),
|
|
616
|
+
...props
|
|
617
|
+
}
|
|
618
|
+
);
|
|
619
|
+
}
|
|
600
620
|
var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsx(
|
|
601
621
|
"div",
|
|
602
622
|
{
|
|
@@ -4205,32 +4225,28 @@ function WebSearchGroupBlock({ tcs }) {
|
|
|
4205
4225
|
}
|
|
4206
4226
|
var ChatMessage = memo(function ChatMessage2({
|
|
4207
4227
|
message,
|
|
4208
|
-
isLast,
|
|
4209
4228
|
onFileSelect
|
|
4210
4229
|
}) {
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
|
|
4226
|
-
|
|
4227
|
-
|
|
4228
|
-
|
|
4229
|
-
|
|
4230
|
-
|
|
4231
|
-
] }) });
|
|
4232
|
-
}
|
|
4233
|
-
return /* @__PURE__ */ jsx(AssistantMessage, { message, isLast, onFileSelect });
|
|
4230
|
+
return /* @__PURE__ */ jsx(Message, { from: "user", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
|
|
4231
|
+
message.content,
|
|
4232
|
+
message.images && message.images.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsx(
|
|
4233
|
+
"img",
|
|
4234
|
+
{
|
|
4235
|
+
src: img.data,
|
|
4236
|
+
alt: "Attached",
|
|
4237
|
+
className: "max-w-64 max-h-48 rounded-lg border border-border object-contain"
|
|
4238
|
+
},
|
|
4239
|
+
i
|
|
4240
|
+
)) }),
|
|
4241
|
+
message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.attachments.map((a, i) => /* @__PURE__ */ jsx(
|
|
4242
|
+
AttachmentChip,
|
|
4243
|
+
{
|
|
4244
|
+
attachment: a,
|
|
4245
|
+
onSelect: onFileSelect
|
|
4246
|
+
},
|
|
4247
|
+
a.path ?? `pending-${i}`
|
|
4248
|
+
)) })
|
|
4249
|
+
] }) });
|
|
4234
4250
|
});
|
|
4235
4251
|
function iconForMime(mime) {
|
|
4236
4252
|
if (!mime) return File$1;
|
|
@@ -4285,28 +4301,6 @@ function AttachmentChip({
|
|
|
4285
4301
|
}
|
|
4286
4302
|
);
|
|
4287
4303
|
}
|
|
4288
|
-
function AssistantMessage({
|
|
4289
|
-
message,
|
|
4290
|
-
isLast,
|
|
4291
|
-
onFileSelect
|
|
4292
|
-
}) {
|
|
4293
|
-
const hasReasoning = !!message.reasoning;
|
|
4294
|
-
const hasToolCalls = !!(message.toolCalls && message.toolCalls.length > 0);
|
|
4295
|
-
const isStreaming = message.status === "streaming" && isLast;
|
|
4296
|
-
const hasContent = !!(message.content && message.content !== message.reasoning);
|
|
4297
|
-
return /* @__PURE__ */ jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxs(MessageContent, { children: [
|
|
4298
|
-
hasReasoning && /* @__PURE__ */ jsxs(Reasoning, { isStreaming: isStreaming && !hasContent && !hasToolCalls, children: [
|
|
4299
|
-
/* @__PURE__ */ jsx(ReasoningTrigger, {}),
|
|
4300
|
-
/* @__PURE__ */ jsx(ReasoningContent, { children: message.reasoning })
|
|
4301
|
-
] }),
|
|
4302
|
-
hasToolCalls && /* @__PURE__ */ jsx("div", { className: "w-full flex flex-col gap-8", children: message.toolCalls.map((tc) => /* @__PURE__ */ jsx(ToolCallBlock, { tc, onFileSelect }, tc.id)) }),
|
|
4303
|
-
hasContent && /* @__PURE__ */ jsx(MessageResponse, { children: message.content }),
|
|
4304
|
-
isStreaming && !hasContent && !hasToolCalls && !hasReasoning && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 py-1 text-muted-foreground", children: [
|
|
4305
|
-
/* @__PURE__ */ jsx("span", { className: "inline-block size-1.5 animate-pulse rounded-full bg-primary" }),
|
|
4306
|
-
/* @__PURE__ */ jsx("span", { className: "text-xs", children: "Thinking..." })
|
|
4307
|
-
] })
|
|
4308
|
-
] }) });
|
|
4309
|
-
}
|
|
4310
4304
|
function InputGroup({ className, ...props }) {
|
|
4311
4305
|
return /* @__PURE__ */ jsx(
|
|
4312
4306
|
"div",
|
|
@@ -5869,6 +5863,11 @@ function ChatComposerInner({
|
|
|
5869
5863
|
{ value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
|
|
5870
5864
|
{ value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
|
|
5871
5865
|
{ value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
|
|
5866
|
+
{ value: "/mission ", label: "/mission", description: "Start an orchestrated rubric-judged mission" },
|
|
5867
|
+
{ value: "/mission status", label: "/mission status", description: "Show current mission status" },
|
|
5868
|
+
{ value: "/mission pause", label: "/mission pause", description: "Pause the mission evaluator" },
|
|
5869
|
+
{ value: "/mission resume", label: "/mission resume", description: "Resume a paused mission" },
|
|
5870
|
+
{ value: "/mission cancel", label: "/mission cancel", description: "Cancel the mission" },
|
|
5872
5871
|
{ value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
|
|
5873
5872
|
{ value: "/loop list", label: "/loop list", description: "List active loops" },
|
|
5874
5873
|
{ value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
|
|
@@ -6174,6 +6173,54 @@ function ChatComposerInner({
|
|
|
6174
6173
|
}
|
|
6175
6174
|
);
|
|
6176
6175
|
}
|
|
6176
|
+
var MIN_CPS = 40;
|
|
6177
|
+
var MAX_CPS = 240;
|
|
6178
|
+
var DRAIN_SECONDS = 0.25;
|
|
6179
|
+
var SNAP_LAG = 800;
|
|
6180
|
+
var MAX_FRAME_DT = 0.1;
|
|
6181
|
+
function useSmoothStream(target, isStreaming) {
|
|
6182
|
+
const [displayedLen, setDisplayedLen] = useState(
|
|
6183
|
+
() => isStreaming ? 0 : target.length
|
|
6184
|
+
);
|
|
6185
|
+
const targetRef = useRef(target);
|
|
6186
|
+
targetRef.current = target;
|
|
6187
|
+
useEffect(() => {
|
|
6188
|
+
setDisplayedLen((cur) => cur > target.length ? target.length : cur);
|
|
6189
|
+
}, [target]);
|
|
6190
|
+
const isActive = isStreaming || displayedLen < target.length;
|
|
6191
|
+
useEffect(() => {
|
|
6192
|
+
if (!isActive) return;
|
|
6193
|
+
let raf = 0;
|
|
6194
|
+
let carry = 0;
|
|
6195
|
+
let lastTime = 0;
|
|
6196
|
+
const tick = (now) => {
|
|
6197
|
+
const dt = lastTime ? Math.min(MAX_FRAME_DT, (now - lastTime) / 1e3) : 0;
|
|
6198
|
+
lastTime = now;
|
|
6199
|
+
setDisplayedLen((current) => {
|
|
6200
|
+
const targetLen = targetRef.current.length;
|
|
6201
|
+
const lag = targetLen - current;
|
|
6202
|
+
if (lag <= 0) return current;
|
|
6203
|
+
if (lag >= SNAP_LAG) {
|
|
6204
|
+
carry = 0;
|
|
6205
|
+
return targetLen;
|
|
6206
|
+
}
|
|
6207
|
+
const cps = Math.min(
|
|
6208
|
+
MAX_CPS,
|
|
6209
|
+
Math.max(MIN_CPS, lag / DRAIN_SECONDS)
|
|
6210
|
+
);
|
|
6211
|
+
carry += cps * dt;
|
|
6212
|
+
const step = Math.floor(carry);
|
|
6213
|
+
if (step <= 0) return current;
|
|
6214
|
+
carry -= step;
|
|
6215
|
+
return Math.min(targetLen, current + step);
|
|
6216
|
+
});
|
|
6217
|
+
raf = requestAnimationFrame(tick);
|
|
6218
|
+
};
|
|
6219
|
+
raf = requestAnimationFrame(tick);
|
|
6220
|
+
return () => cancelAnimationFrame(raf);
|
|
6221
|
+
}, [isActive]);
|
|
6222
|
+
return displayedLen >= target.length ? target : target.slice(0, displayedLen);
|
|
6223
|
+
}
|
|
6177
6224
|
var Artifact = ({ className, ...props }) => /* @__PURE__ */ jsx(
|
|
6178
6225
|
"div",
|
|
6179
6226
|
{
|
|
@@ -6772,7 +6819,12 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
|
|
|
6772
6819
|
}
|
|
6773
6820
|
}
|
|
6774
6821
|
if (effectiveHasContent) {
|
|
6775
|
-
entries.push({
|
|
6822
|
+
entries.push({
|
|
6823
|
+
kind: "text",
|
|
6824
|
+
key: `${msg.id}-text`,
|
|
6825
|
+
content: msg.content,
|
|
6826
|
+
isStreaming
|
|
6827
|
+
});
|
|
6776
6828
|
}
|
|
6777
6829
|
const hasRunningTool = hasToolCalls && msg.toolCalls.some(
|
|
6778
6830
|
(tc) => tc.status === "running"
|
|
@@ -6952,16 +7004,7 @@ function TimelineEntryItem({
|
|
|
6952
7004
|
onFileSelect
|
|
6953
7005
|
}) {
|
|
6954
7006
|
if (entry.kind === "reasoning") {
|
|
6955
|
-
return /* @__PURE__ */
|
|
6956
|
-
/* @__PURE__ */ jsxs(TimelineHeader, { children: [
|
|
6957
|
-
/* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
|
|
6958
|
-
/* @__PURE__ */ jsx(TimelineIndicator, { className: "size-2" })
|
|
6959
|
-
] }),
|
|
6960
|
-
/* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsxs(Reasoning, { isStreaming: entry.isStreaming, children: [
|
|
6961
|
-
/* @__PURE__ */ jsx(ReasoningTrigger, {}),
|
|
6962
|
-
/* @__PURE__ */ jsx(ReasoningContent, { children: entry.reasoning })
|
|
6963
|
-
] }) })
|
|
6964
|
-
] });
|
|
7007
|
+
return /* @__PURE__ */ jsx(ReasoningEntry, { entry, step });
|
|
6965
7008
|
}
|
|
6966
7009
|
if (entry.kind === "tool") {
|
|
6967
7010
|
const rawStatus = effectiveStatus(entry.tc);
|
|
@@ -7013,13 +7056,7 @@ function TimelineEntryItem({
|
|
|
7013
7056
|
] });
|
|
7014
7057
|
}
|
|
7015
7058
|
if (entry.kind === "text") {
|
|
7016
|
-
return /* @__PURE__ */
|
|
7017
|
-
/* @__PURE__ */ jsxs(TimelineHeader, { children: [
|
|
7018
|
-
/* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
|
|
7019
|
-
/* @__PURE__ */ jsx(TimelineIndicator, { className: "size-2 border-none bg-foreground/40" })
|
|
7020
|
-
] }),
|
|
7021
|
-
/* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(MessageResponse, { children: entry.content }) })
|
|
7022
|
-
] });
|
|
7059
|
+
return /* @__PURE__ */ jsx(TextEntry, { entry, step });
|
|
7023
7060
|
}
|
|
7024
7061
|
if (entry.kind === "skill_invoked") {
|
|
7025
7062
|
return /* @__PURE__ */ jsxs(TimelineItem, { step, children: [
|
|
@@ -7063,6 +7100,35 @@ function TimelineEntryItem({
|
|
|
7063
7100
|
/* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) })
|
|
7064
7101
|
] });
|
|
7065
7102
|
}
|
|
7103
|
+
function ReasoningEntry({
|
|
7104
|
+
entry,
|
|
7105
|
+
step
|
|
7106
|
+
}) {
|
|
7107
|
+
const reasoning = useSmoothStream(entry.reasoning, entry.isStreaming);
|
|
7108
|
+
return /* @__PURE__ */ jsxs(TimelineItem, { step, children: [
|
|
7109
|
+
/* @__PURE__ */ jsxs(TimelineHeader, { children: [
|
|
7110
|
+
/* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
|
|
7111
|
+
/* @__PURE__ */ jsx(TimelineIndicator, { className: "size-2" })
|
|
7112
|
+
] }),
|
|
7113
|
+
/* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsxs(Reasoning, { isStreaming: entry.isStreaming, children: [
|
|
7114
|
+
/* @__PURE__ */ jsx(ReasoningTrigger, {}),
|
|
7115
|
+
/* @__PURE__ */ jsx(ReasoningContent, { children: reasoning })
|
|
7116
|
+
] }) })
|
|
7117
|
+
] });
|
|
7118
|
+
}
|
|
7119
|
+
function TextEntry({
|
|
7120
|
+
entry,
|
|
7121
|
+
step
|
|
7122
|
+
}) {
|
|
7123
|
+
const content = useSmoothStream(entry.content, entry.isStreaming);
|
|
7124
|
+
return /* @__PURE__ */ jsxs(TimelineItem, { step, children: [
|
|
7125
|
+
/* @__PURE__ */ jsxs(TimelineHeader, { children: [
|
|
7126
|
+
/* @__PURE__ */ jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
|
|
7127
|
+
/* @__PURE__ */ jsx(TimelineIndicator, { className: "size-2 border-none bg-foreground/40" })
|
|
7128
|
+
] }),
|
|
7129
|
+
/* @__PURE__ */ jsx(TimelineContent, { children: /* @__PURE__ */ jsx(MessageResponse, { children: content }) })
|
|
7130
|
+
] });
|
|
7131
|
+
}
|
|
7066
7132
|
function AssistantGroup({
|
|
7067
7133
|
messages,
|
|
7068
7134
|
lastGlobalIndex,
|
|
@@ -7209,7 +7275,6 @@ function ChatThread({
|
|
|
7209
7275
|
ChatMessage,
|
|
7210
7276
|
{
|
|
7211
7277
|
message: msg,
|
|
7212
|
-
isLast: group.lastGlobalIndex === messages.length - 1,
|
|
7213
7278
|
onFileSelect
|
|
7214
7279
|
},
|
|
7215
7280
|
msg.id
|
|
@@ -10231,9 +10296,817 @@ function useInboxUnreadCount(adapter) {
|
|
|
10231
10296
|
}, [adapter]);
|
|
10232
10297
|
return { unreadCount, hasLoaded, error };
|
|
10233
10298
|
}
|
|
10234
|
-
|
|
10235
|
-
|
|
10299
|
+
function Card({
|
|
10300
|
+
className,
|
|
10301
|
+
size = "default",
|
|
10302
|
+
...props
|
|
10303
|
+
}) {
|
|
10304
|
+
return /* @__PURE__ */ jsx(
|
|
10305
|
+
"div",
|
|
10306
|
+
{
|
|
10307
|
+
"data-slot": "card",
|
|
10308
|
+
"data-size": size,
|
|
10309
|
+
className: cn(
|
|
10310
|
+
"group/card flex flex-col gap-8 overflow-hidden bg-card py-8 text-sm text-card-foreground shadow-sm ring-1 ring-foreground/5 has-[>img:first-child]:pt-0 data-[size=sm]:gap-5 data-[size=sm]:py-5 *:[img:first-child]:rounded-none *:[img:last-child]:rounded-none",
|
|
10311
|
+
className
|
|
10312
|
+
),
|
|
10313
|
+
...props
|
|
10314
|
+
}
|
|
10315
|
+
);
|
|
10316
|
+
}
|
|
10317
|
+
function CardHeader({ className, ...props }) {
|
|
10318
|
+
return /* @__PURE__ */ jsx(
|
|
10319
|
+
"div",
|
|
10320
|
+
{
|
|
10321
|
+
"data-slot": "card-header",
|
|
10322
|
+
className: cn(
|
|
10323
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1.5 rounded-none px-8 group-data-[size=sm]/card:px-5 has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-8 group-data-[size=sm]/card:[.border-b]:pb-5",
|
|
10324
|
+
className
|
|
10325
|
+
),
|
|
10326
|
+
...props
|
|
10327
|
+
}
|
|
10328
|
+
);
|
|
10329
|
+
}
|
|
10330
|
+
function CardTitle({ className, ...props }) {
|
|
10331
|
+
return /* @__PURE__ */ jsx(
|
|
10332
|
+
"div",
|
|
10333
|
+
{
|
|
10334
|
+
"data-slot": "card-title",
|
|
10335
|
+
className: cn(
|
|
10336
|
+
"font-heading text-lg font-semibold tracking-wider uppercase",
|
|
10337
|
+
className
|
|
10338
|
+
),
|
|
10339
|
+
...props
|
|
10340
|
+
}
|
|
10341
|
+
);
|
|
10342
|
+
}
|
|
10343
|
+
function CardDescription({ className, ...props }) {
|
|
10344
|
+
return /* @__PURE__ */ jsx(
|
|
10345
|
+
"div",
|
|
10346
|
+
{
|
|
10347
|
+
"data-slot": "card-description",
|
|
10348
|
+
className: cn("text-sm leading-relaxed text-muted-foreground", className),
|
|
10349
|
+
...props
|
|
10350
|
+
}
|
|
10351
|
+
);
|
|
10352
|
+
}
|
|
10353
|
+
function CardContent({ className, ...props }) {
|
|
10354
|
+
return /* @__PURE__ */ jsx(
|
|
10355
|
+
"div",
|
|
10356
|
+
{
|
|
10357
|
+
"data-slot": "card-content",
|
|
10358
|
+
className: cn("px-8 group-data-[size=sm]/card:px-5", className),
|
|
10359
|
+
...props
|
|
10360
|
+
}
|
|
10361
|
+
);
|
|
10362
|
+
}
|
|
10363
|
+
|
|
10364
|
+
// src/components/missions/mission-derive.ts
|
|
10365
|
+
var ACTIVE_MISSION_STATUSES = /* @__PURE__ */ new Set(["active", "paused"]);
|
|
10366
|
+
function isTerminalMissionStatus(status) {
|
|
10367
|
+
return !ACTIVE_MISSION_STATUSES.has(status);
|
|
10368
|
+
}
|
|
10369
|
+
function missionTaskBucket(status) {
|
|
10370
|
+
if (status === "done") return "done";
|
|
10371
|
+
if (status === "blocked") return "blocked";
|
|
10372
|
+
if (status === "failed" || status === "cancelled") {
|
|
10373
|
+
return "failed_or_cancelled";
|
|
10374
|
+
}
|
|
10375
|
+
return "in_flight";
|
|
10376
|
+
}
|
|
10377
|
+
function groupMissionTasksByBucket(tasks) {
|
|
10378
|
+
const buckets = {
|
|
10379
|
+
in_flight: [],
|
|
10380
|
+
done: [],
|
|
10381
|
+
blocked: [],
|
|
10382
|
+
failed_or_cancelled: []
|
|
10383
|
+
};
|
|
10384
|
+
for (const t of tasks) {
|
|
10385
|
+
buckets[missionTaskBucket(t.status)].push(t);
|
|
10386
|
+
}
|
|
10387
|
+
return buckets;
|
|
10388
|
+
}
|
|
10389
|
+
function deriveMissionWorkerActivityLabel(worker) {
|
|
10390
|
+
const kind = worker.latestEventKind;
|
|
10391
|
+
if (kind === "tool.call" && worker.latestEventSummary) {
|
|
10392
|
+
return `tool: ${truncate2(worker.latestEventSummary, 80)}`;
|
|
10393
|
+
}
|
|
10394
|
+
if (kind === "llm.response") {
|
|
10395
|
+
return "thinking";
|
|
10396
|
+
}
|
|
10397
|
+
if (kind === "tool.result") {
|
|
10398
|
+
return "received tool result";
|
|
10399
|
+
}
|
|
10400
|
+
if (kind && worker.latestEventSummary) {
|
|
10401
|
+
return `${kind}: ${truncate2(worker.latestEventSummary, 60)}`;
|
|
10402
|
+
}
|
|
10403
|
+
return worker.sessionStatus || "active";
|
|
10404
|
+
}
|
|
10405
|
+
function truncate2(s, max) {
|
|
10406
|
+
if (s.length <= max) return s;
|
|
10407
|
+
return `${s.slice(0, max - 1)}\u2026`;
|
|
10408
|
+
}
|
|
10409
|
+
var DEFAULT_POLL_INTERVAL_MS = 5e3;
|
|
10410
|
+
var INITIAL_STATE = {
|
|
10411
|
+
mission: null,
|
|
10412
|
+
tasks: [],
|
|
10413
|
+
workers: [],
|
|
10414
|
+
loading: true,
|
|
10415
|
+
error: null
|
|
10416
|
+
};
|
|
10417
|
+
function requireMissionApi(adapter) {
|
|
10418
|
+
const required = {
|
|
10419
|
+
getMission: adapter.getMission,
|
|
10420
|
+
getMissionTasks: adapter.getMissionTasks,
|
|
10421
|
+
getMissionWorkers: adapter.getMissionWorkers,
|
|
10422
|
+
pauseMission: adapter.pauseMission,
|
|
10423
|
+
resumeMission: adapter.resumeMission,
|
|
10424
|
+
cancelMission: adapter.cancelMission
|
|
10425
|
+
};
|
|
10426
|
+
for (const [name, fn] of Object.entries(required)) {
|
|
10427
|
+
if (typeof fn !== "function") {
|
|
10428
|
+
throw new Error(
|
|
10429
|
+
`MissionDashboard requires adapter.${name} to be implemented; this adapter is missing the mission surface.`
|
|
10430
|
+
);
|
|
10431
|
+
}
|
|
10432
|
+
}
|
|
10433
|
+
return required;
|
|
10434
|
+
}
|
|
10435
|
+
function MissionDashboard({
|
|
10436
|
+
adapter,
|
|
10437
|
+
missionId,
|
|
10438
|
+
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
10439
|
+
onNavigateBack,
|
|
10440
|
+
onOpenTranscript
|
|
10441
|
+
}) {
|
|
10442
|
+
const api = useMemo(() => requireMissionApi(adapter), [adapter]);
|
|
10443
|
+
const [state, setState] = useState(INITIAL_STATE);
|
|
10444
|
+
const [cancelOpen, setCancelOpen] = useState(false);
|
|
10445
|
+
const [cancelCascade, setCancelCascade] = useState(false);
|
|
10446
|
+
const [busy, setBusy] = useState(false);
|
|
10447
|
+
const isMounted = useRef(true);
|
|
10448
|
+
useEffect(() => {
|
|
10449
|
+
isMounted.current = true;
|
|
10450
|
+
return () => {
|
|
10451
|
+
isMounted.current = false;
|
|
10452
|
+
};
|
|
10453
|
+
}, []);
|
|
10454
|
+
const refresh = useCallback(async () => {
|
|
10455
|
+
try {
|
|
10456
|
+
const [mission2, tasksResp, workersResp] = await Promise.all([
|
|
10457
|
+
api.getMission({ missionId }),
|
|
10458
|
+
api.getMissionTasks({ missionId }),
|
|
10459
|
+
api.getMissionWorkers({ missionId })
|
|
10460
|
+
]);
|
|
10461
|
+
if (!isMounted.current) return;
|
|
10462
|
+
setState({
|
|
10463
|
+
mission: mission2,
|
|
10464
|
+
tasks: tasksResp.tasks,
|
|
10465
|
+
workers: workersResp.workers,
|
|
10466
|
+
loading: false,
|
|
10467
|
+
error: null
|
|
10468
|
+
});
|
|
10469
|
+
} catch (err) {
|
|
10470
|
+
if (!isMounted.current) return;
|
|
10471
|
+
setState((prev) => ({
|
|
10472
|
+
...prev,
|
|
10473
|
+
loading: false,
|
|
10474
|
+
error: err instanceof Error ? err.message : String(err)
|
|
10475
|
+
}));
|
|
10476
|
+
}
|
|
10477
|
+
}, [api, missionId]);
|
|
10478
|
+
useEffect(() => {
|
|
10479
|
+
void refresh();
|
|
10480
|
+
}, [refresh]);
|
|
10481
|
+
useEffect(() => {
|
|
10482
|
+
if (!state.mission) return;
|
|
10483
|
+
if (isTerminalMissionStatus(state.mission.status)) return;
|
|
10484
|
+
const id = window.setInterval(() => {
|
|
10485
|
+
void refresh();
|
|
10486
|
+
}, pollIntervalMs);
|
|
10487
|
+
return () => {
|
|
10488
|
+
window.clearInterval(id);
|
|
10489
|
+
};
|
|
10490
|
+
}, [refresh, state.mission, pollIntervalMs]);
|
|
10491
|
+
const taskBuckets = useMemo(
|
|
10492
|
+
() => groupMissionTasksByBucket(state.tasks),
|
|
10493
|
+
[state.tasks]
|
|
10494
|
+
);
|
|
10495
|
+
const runningWorkerCount = useMemo(
|
|
10496
|
+
() => state.workers.filter((w) => w.taskStatus === "running").length,
|
|
10497
|
+
[state.workers]
|
|
10498
|
+
);
|
|
10499
|
+
if (state.loading && !state.mission) {
|
|
10500
|
+
return /* @__PURE__ */ jsx("div", { className: "flex h-full items-center justify-center p-6", children: /* @__PURE__ */ jsx(Loader2, { className: "size-5 animate-spin text-foreground/70" }) });
|
|
10501
|
+
}
|
|
10502
|
+
if (state.error && !state.mission) {
|
|
10503
|
+
return /* @__PURE__ */ jsxs("div", { className: "p-6 space-y-3", children: [
|
|
10504
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-destructive", children: [
|
|
10505
|
+
/* @__PURE__ */ jsx(AlertCircle, { className: "size-4" }),
|
|
10506
|
+
"Failed to load mission: ",
|
|
10507
|
+
state.error
|
|
10508
|
+
] }),
|
|
10509
|
+
/* @__PURE__ */ jsxs(
|
|
10510
|
+
Button,
|
|
10511
|
+
{
|
|
10512
|
+
variant: "outline",
|
|
10513
|
+
size: "sm",
|
|
10514
|
+
onClick: () => {
|
|
10515
|
+
setState(INITIAL_STATE);
|
|
10516
|
+
void refresh();
|
|
10517
|
+
},
|
|
10518
|
+
children: [
|
|
10519
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "size-4" }),
|
|
10520
|
+
" Retry"
|
|
10521
|
+
]
|
|
10522
|
+
}
|
|
10523
|
+
)
|
|
10524
|
+
] });
|
|
10525
|
+
}
|
|
10526
|
+
const mission = state.mission;
|
|
10527
|
+
const doPause = async () => {
|
|
10528
|
+
setBusy(true);
|
|
10529
|
+
try {
|
|
10530
|
+
await api.pauseMission({ missionId });
|
|
10531
|
+
await refresh();
|
|
10532
|
+
} finally {
|
|
10533
|
+
setBusy(false);
|
|
10534
|
+
}
|
|
10535
|
+
};
|
|
10536
|
+
const doResume = async () => {
|
|
10537
|
+
setBusy(true);
|
|
10538
|
+
try {
|
|
10539
|
+
await api.resumeMission({ missionId });
|
|
10540
|
+
await refresh();
|
|
10541
|
+
} finally {
|
|
10542
|
+
setBusy(false);
|
|
10543
|
+
}
|
|
10544
|
+
};
|
|
10545
|
+
const doCancel = async () => {
|
|
10546
|
+
setBusy(true);
|
|
10547
|
+
try {
|
|
10548
|
+
await api.cancelMission({
|
|
10549
|
+
missionId,
|
|
10550
|
+
cascadeToWorkers: cancelCascade
|
|
10551
|
+
});
|
|
10552
|
+
await refresh();
|
|
10553
|
+
setCancelOpen(false);
|
|
10554
|
+
} finally {
|
|
10555
|
+
setBusy(false);
|
|
10556
|
+
}
|
|
10557
|
+
};
|
|
10558
|
+
const statusVariant = mission.status === "satisfied" ? "default" : mission.status === "failed" || mission.status === "cancelled" ? "destructive" : "secondary";
|
|
10559
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-6 p-6 max-w-6xl mx-auto", children: [
|
|
10560
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
10561
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
10562
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
10563
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "wrap-break-word -uppercase text-sm", children: mission.description }),
|
|
10564
|
+
/* @__PURE__ */ jsxs(CardDescription, { className: "wrap-break-word text-foreground/70", children: [
|
|
10565
|
+
"Rubric: ",
|
|
10566
|
+
mission.rubric
|
|
10567
|
+
] }),
|
|
10568
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 pt-2 text-xs text-foreground/70", children: [
|
|
10569
|
+
/* @__PURE__ */ jsx(Badge, { variant: statusVariant, className: "font-mono text-foreground", children: mission.status }),
|
|
10570
|
+
/* @__PURE__ */ jsxs("span", { className: "font-mono text-primary", children: [
|
|
10571
|
+
"Iteration ",
|
|
10572
|
+
mission.iteration,
|
|
10573
|
+
"/",
|
|
10574
|
+
mission.maxIterations
|
|
10575
|
+
] }),
|
|
10576
|
+
mission.lastEvaluationResult ? /* @__PURE__ */ jsxs("span", { children: [
|
|
10577
|
+
"Last verdict:",
|
|
10578
|
+
" ",
|
|
10579
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-primary", children: mission.lastEvaluationResult })
|
|
10580
|
+
] }) : null
|
|
10581
|
+
] })
|
|
10582
|
+
] }),
|
|
10583
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: ACTIVE_MISSION_STATUSES.has(mission.status) ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
10584
|
+
mission.status === "active" ? /* @__PURE__ */ jsxs(
|
|
10585
|
+
Button,
|
|
10586
|
+
{
|
|
10587
|
+
size: "sm",
|
|
10588
|
+
variant: "outline",
|
|
10589
|
+
onClick: doPause,
|
|
10590
|
+
disabled: busy,
|
|
10591
|
+
children: [
|
|
10592
|
+
/* @__PURE__ */ jsx(Pause, { className: "size-4" }),
|
|
10593
|
+
" Pause"
|
|
10594
|
+
]
|
|
10595
|
+
}
|
|
10596
|
+
) : /* @__PURE__ */ jsxs(
|
|
10597
|
+
Button,
|
|
10598
|
+
{
|
|
10599
|
+
size: "sm",
|
|
10600
|
+
variant: "outline",
|
|
10601
|
+
onClick: doResume,
|
|
10602
|
+
disabled: busy,
|
|
10603
|
+
children: [
|
|
10604
|
+
/* @__PURE__ */ jsx(Play, { className: "size-4" }),
|
|
10605
|
+
" Resume"
|
|
10606
|
+
]
|
|
10607
|
+
}
|
|
10608
|
+
),
|
|
10609
|
+
/* @__PURE__ */ jsxs(
|
|
10610
|
+
Button,
|
|
10611
|
+
{
|
|
10612
|
+
size: "sm",
|
|
10613
|
+
variant: "destructive",
|
|
10614
|
+
onClick: () => {
|
|
10615
|
+
setCancelCascade(false);
|
|
10616
|
+
setCancelOpen(true);
|
|
10617
|
+
},
|
|
10618
|
+
disabled: busy,
|
|
10619
|
+
children: [
|
|
10620
|
+
/* @__PURE__ */ jsx(Ban, { className: "size-4" }),
|
|
10621
|
+
" Cancel"
|
|
10622
|
+
]
|
|
10623
|
+
}
|
|
10624
|
+
)
|
|
10625
|
+
] }) : onNavigateBack ? /* @__PURE__ */ jsx(Button, { size: "sm", variant: "outline", onClick: onNavigateBack, children: "Back" }) : null })
|
|
10626
|
+
] }) }),
|
|
10627
|
+
mission.lastEvaluationFeedback ? /* @__PURE__ */ jsxs(CardContent, { className: "space-y-2", children: [
|
|
10628
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs font-semibold uppercase tracking-widest text-foreground/70", children: "Evaluator feedback" }),
|
|
10629
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm whitespace-pre-wrap wrap-break-word", children: mission.lastEvaluationFeedback }),
|
|
10630
|
+
mission.lastEvaluationExplanation ? /* @__PURE__ */ jsx("p", { className: "text-xs text-foreground/70 whitespace-pre-wrap wrap-break-word", children: mission.lastEvaluationExplanation }) : null
|
|
10631
|
+
] }) : null
|
|
10632
|
+
] }),
|
|
10633
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
10634
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { children: [
|
|
10635
|
+
"Tasks (",
|
|
10636
|
+
state.tasks.length,
|
|
10637
|
+
")"
|
|
10638
|
+
] }) }),
|
|
10639
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-4", children: [
|
|
10640
|
+
["in_flight", "blocked", "done", "failed_or_cancelled"].map((bucket) => {
|
|
10641
|
+
const rows = taskBuckets[bucket];
|
|
10642
|
+
if (rows.length === 0) return null;
|
|
10643
|
+
const heading = {
|
|
10644
|
+
in_flight: "In flight",
|
|
10645
|
+
blocked: "Blocked",
|
|
10646
|
+
done: "Done",
|
|
10647
|
+
failed_or_cancelled: "Failed / cancelled"
|
|
10648
|
+
}[bucket];
|
|
10649
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
10650
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs font-semibold uppercase tracking-widest text-foreground/70", children: [
|
|
10651
|
+
heading,
|
|
10652
|
+
" (",
|
|
10653
|
+
rows.length,
|
|
10654
|
+
")"
|
|
10655
|
+
] }),
|
|
10656
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: rows.map((task) => /* @__PURE__ */ jsx(
|
|
10657
|
+
"li",
|
|
10658
|
+
{
|
|
10659
|
+
className: "flex items-start justify-between gap-3 rounded border border-border/40 px-3 py-2 text-sm",
|
|
10660
|
+
children: /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
10661
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10662
|
+
task.agentDefName ? /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground/70", children: task.agentDefName }) : null,
|
|
10663
|
+
task.attemptCount > 1 ? /* @__PURE__ */ jsxs("span", { className: "text-xs text-foreground/70", children: [
|
|
10664
|
+
"attempt ",
|
|
10665
|
+
task.attemptCount,
|
|
10666
|
+
"/",
|
|
10667
|
+
task.maxAttempts
|
|
10668
|
+
] }) : null
|
|
10669
|
+
] }),
|
|
10670
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm wrap-break-word", children: task.goal }),
|
|
10671
|
+
task.parentIds.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "text-xs text-foreground/70", children: [
|
|
10672
|
+
"after:",
|
|
10673
|
+
" ",
|
|
10674
|
+
task.parentIds.map((p) => p.slice(0, 8)).join(", ")
|
|
10675
|
+
] }) : null,
|
|
10676
|
+
task.result ? /* @__PURE__ */ jsx("pre", { className: "text-xs whitespace-pre-wrap bg-muted/30 rounded px-2 py-1 max-h-32 overflow-auto", children: task.result }) : null,
|
|
10677
|
+
task.resultMetadata ? /* @__PURE__ */ jsx("pre", { className: "text-xs whitespace-pre-wrap bg-muted/30 rounded px-2 py-1 max-h-32 overflow-auto", children: JSON.stringify(task.resultMetadata, null, 2) }) : null
|
|
10678
|
+
] })
|
|
10679
|
+
},
|
|
10680
|
+
task.id
|
|
10681
|
+
)) })
|
|
10682
|
+
] }, bucket);
|
|
10683
|
+
}),
|
|
10684
|
+
state.tasks.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-sm text-foreground/70", children: "No tasks spawned yet." }) : null
|
|
10685
|
+
] })
|
|
10686
|
+
] }),
|
|
10687
|
+
/* @__PURE__ */ jsxs(Card, { children: [
|
|
10688
|
+
/* @__PURE__ */ jsx(CardHeader, { children: /* @__PURE__ */ jsxs(CardTitle, { children: [
|
|
10689
|
+
"Live workers (",
|
|
10690
|
+
state.workers.length,
|
|
10691
|
+
")"
|
|
10692
|
+
] }) }),
|
|
10693
|
+
/* @__PURE__ */ jsx(CardContent, { children: state.workers.length === 0 ? /* @__PURE__ */ jsx("div", { className: "text-sm text-foreground/70", children: "No workers attached to this mission right now." }) : /* @__PURE__ */ jsx("ul", { className: "space-y-1.5", children: state.workers.map((w) => {
|
|
10694
|
+
const onTranscriptClick = (e) => {
|
|
10695
|
+
if (onOpenTranscript) {
|
|
10696
|
+
e.preventDefault();
|
|
10697
|
+
onOpenTranscript(w.workerSessionId);
|
|
10698
|
+
}
|
|
10699
|
+
};
|
|
10700
|
+
return /* @__PURE__ */ jsxs(
|
|
10701
|
+
"li",
|
|
10702
|
+
{
|
|
10703
|
+
className: "flex items-start justify-between gap-3 rounded border border-border/40 px-3 py-2 text-sm",
|
|
10704
|
+
children: [
|
|
10705
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
|
|
10706
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
10707
|
+
/* @__PURE__ */ jsx(Badge, { className: "text-foreground/70", children: w.taskStatus }),
|
|
10708
|
+
w.agentDefName ? /* @__PURE__ */ jsx("span", { className: "text-xs text-foreground/70", children: w.agentDefName }) : null
|
|
10709
|
+
] }),
|
|
10710
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm wrap-break-word", children: deriveMissionWorkerActivityLabel(w) }),
|
|
10711
|
+
w.latestEventAt ? /* @__PURE__ */ jsx("div", { className: "text-xs text-foreground/70", children: new Date(w.latestEventAt).toLocaleTimeString() }) : null
|
|
10712
|
+
] }),
|
|
10713
|
+
/* @__PURE__ */ jsx(
|
|
10714
|
+
"a",
|
|
10715
|
+
{
|
|
10716
|
+
href: w.transcriptUrl,
|
|
10717
|
+
target: "_blank",
|
|
10718
|
+
rel: "noreferrer",
|
|
10719
|
+
onClick: onTranscriptClick,
|
|
10720
|
+
className: "text-xs text-primary hover:underline self-start",
|
|
10721
|
+
children: "View session"
|
|
10722
|
+
}
|
|
10723
|
+
)
|
|
10724
|
+
]
|
|
10725
|
+
},
|
|
10726
|
+
w.workerSessionId
|
|
10727
|
+
);
|
|
10728
|
+
}) }) })
|
|
10729
|
+
] }),
|
|
10730
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
10731
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center text-xs text-foreground/70", children: [
|
|
10732
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
10733
|
+
"Auto-refresh every ",
|
|
10734
|
+
pollIntervalMs / 1e3,
|
|
10735
|
+
"s while mission is active."
|
|
10736
|
+
] }),
|
|
10737
|
+
/* @__PURE__ */ jsxs(
|
|
10738
|
+
Button,
|
|
10739
|
+
{
|
|
10740
|
+
variant: "ghost",
|
|
10741
|
+
size: "sm",
|
|
10742
|
+
onClick: () => {
|
|
10743
|
+
void refresh();
|
|
10744
|
+
},
|
|
10745
|
+
disabled: busy,
|
|
10746
|
+
children: [
|
|
10747
|
+
/* @__PURE__ */ jsx(RefreshCw, { className: "size-4" }),
|
|
10748
|
+
" Refresh"
|
|
10749
|
+
]
|
|
10750
|
+
}
|
|
10751
|
+
)
|
|
10752
|
+
] }),
|
|
10753
|
+
/* @__PURE__ */ jsx(
|
|
10754
|
+
Dialog,
|
|
10755
|
+
{
|
|
10756
|
+
open: cancelOpen,
|
|
10757
|
+
onOpenChange: (open) => {
|
|
10758
|
+
if (!open && !busy) setCancelOpen(false);
|
|
10759
|
+
},
|
|
10760
|
+
children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
|
|
10761
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
10762
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: "Cancel mission?" }),
|
|
10763
|
+
/* @__PURE__ */ jsx(DialogDescription, { children: "This terminates the mission and clears its evaluator loop." })
|
|
10764
|
+
] }),
|
|
10765
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
|
|
10766
|
+
/* @__PURE__ */ jsx(
|
|
10767
|
+
"input",
|
|
10768
|
+
{
|
|
10769
|
+
type: "checkbox",
|
|
10770
|
+
checked: cancelCascade,
|
|
10771
|
+
onChange: (e) => setCancelCascade(e.target.checked)
|
|
10772
|
+
}
|
|
10773
|
+
),
|
|
10774
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
10775
|
+
"Also cancel ",
|
|
10776
|
+
/* @__PURE__ */ jsx("strong", { children: runningWorkerCount }),
|
|
10777
|
+
" running worker",
|
|
10778
|
+
runningWorkerCount === 1 ? "" : "s",
|
|
10779
|
+
" (sends an interrupt to each worker session)."
|
|
10780
|
+
] })
|
|
10781
|
+
] }),
|
|
10782
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
10783
|
+
/* @__PURE__ */ jsx(
|
|
10784
|
+
Button,
|
|
10785
|
+
{
|
|
10786
|
+
variant: "outline",
|
|
10787
|
+
size: "sm",
|
|
10788
|
+
onClick: () => setCancelOpen(false),
|
|
10789
|
+
disabled: busy,
|
|
10790
|
+
children: "Keep mission"
|
|
10791
|
+
}
|
|
10792
|
+
),
|
|
10793
|
+
/* @__PURE__ */ jsxs(
|
|
10794
|
+
Button,
|
|
10795
|
+
{
|
|
10796
|
+
variant: "destructive",
|
|
10797
|
+
size: "sm",
|
|
10798
|
+
onClick: doCancel,
|
|
10799
|
+
disabled: busy,
|
|
10800
|
+
children: [
|
|
10801
|
+
busy ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Ban, { className: "size-4" }),
|
|
10802
|
+
"Cancel mission"
|
|
10803
|
+
]
|
|
10804
|
+
}
|
|
10805
|
+
)
|
|
10806
|
+
] })
|
|
10807
|
+
] })
|
|
10808
|
+
}
|
|
10809
|
+
)
|
|
10810
|
+
] });
|
|
10811
|
+
}
|
|
10812
|
+
var DEFAULT_POLL_INTERVAL_MS2 = 3e4;
|
|
10813
|
+
var DEFAULT_STATUS = "active,paused";
|
|
10236
10814
|
function fingerprint(items) {
|
|
10815
|
+
return items.map(
|
|
10816
|
+
(m) => `${m.id}:${m.status}:${m.iteration}:${m.lastEvaluationResult ?? ""}:${m.lastEvaluationAt ?? ""}:${m.pausedReason ?? ""}:${m.updatedAt}`
|
|
10817
|
+
).join("|");
|
|
10818
|
+
}
|
|
10819
|
+
function sortMissions(items) {
|
|
10820
|
+
return [...items].sort((a, b) => {
|
|
10821
|
+
const rank = (s) => s === "active" ? 0 : s === "paused" ? 1 : 2;
|
|
10822
|
+
const diff = rank(a.status) - rank(b.status);
|
|
10823
|
+
if (diff !== 0) return diff;
|
|
10824
|
+
return (a.updatedAt ?? "") > (b.updatedAt ?? "") ? -1 : 1;
|
|
10825
|
+
});
|
|
10826
|
+
}
|
|
10827
|
+
function MissionRow({
|
|
10828
|
+
mission,
|
|
10829
|
+
onOpen,
|
|
10830
|
+
onPause,
|
|
10831
|
+
onResume,
|
|
10832
|
+
onCancel,
|
|
10833
|
+
busy,
|
|
10834
|
+
canMutate
|
|
10835
|
+
}) {
|
|
10836
|
+
const isActive = mission.status === "active";
|
|
10837
|
+
const isPaused = mission.status === "paused";
|
|
10838
|
+
const description = mission.description.trim() || "(no description)";
|
|
10839
|
+
const verdict = mission.lastEvaluationResult;
|
|
10840
|
+
const verdictAt = mission.lastEvaluationAt ? formatDistanceToNow(new Date(mission.lastEvaluationAt), {
|
|
10841
|
+
addSuffix: true
|
|
10842
|
+
}) : null;
|
|
10843
|
+
const meta = [
|
|
10844
|
+
`Iter ${mission.iteration}/${mission.maxIterations}`,
|
|
10845
|
+
verdict ? `Verdict: ${verdict}${verdictAt ? ` (${verdictAt})` : ""}` : null,
|
|
10846
|
+
isPaused && mission.pausedReason ? `Paused: ${mission.pausedReason}` : null
|
|
10847
|
+
].filter(Boolean);
|
|
10848
|
+
const stopActionClick = (e) => {
|
|
10849
|
+
e.stopPropagation();
|
|
10850
|
+
};
|
|
10851
|
+
const openMission = () => onOpen(mission.id);
|
|
10852
|
+
const handleRowKeyDown = (e) => {
|
|
10853
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
10854
|
+
e.preventDefault();
|
|
10855
|
+
openMission();
|
|
10856
|
+
};
|
|
10857
|
+
return /* @__PURE__ */ jsxs(
|
|
10858
|
+
"div",
|
|
10859
|
+
{
|
|
10860
|
+
className: cn(
|
|
10861
|
+
"group flex min-w-0 items-start gap-2 border-l-2 border-l-transparent px-3 py-2 text-sm transition-colors cursor-pointer hover:border-l-primary hover:bg-input"
|
|
10862
|
+
),
|
|
10863
|
+
onClick: openMission,
|
|
10864
|
+
onKeyDown: handleRowKeyDown,
|
|
10865
|
+
role: "button",
|
|
10866
|
+
tabIndex: 0,
|
|
10867
|
+
"aria-label": `Open mission ${description}`,
|
|
10868
|
+
title: "Open mission dashboard",
|
|
10869
|
+
children: [
|
|
10870
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center text-faint", children: /* @__PURE__ */ jsx(TargetIcon, { className: "size-3.5" }) }),
|
|
10871
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx("div", { className: "flex min-w-0 items-start gap-2", children: /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
10872
|
+
/* @__PURE__ */ jsx("div", { className: "truncate text-foreground", children: description }),
|
|
10873
|
+
/* @__PURE__ */ jsx("div", { className: "mt-0.5 flex min-w-0 items-center gap-2", children: /* @__PURE__ */ jsx(
|
|
10874
|
+
Badge,
|
|
10875
|
+
{
|
|
10876
|
+
variant: isActive ? "default" : "secondary",
|
|
10877
|
+
className: "shrink-0",
|
|
10878
|
+
children: mission.status
|
|
10879
|
+
}
|
|
10880
|
+
) }),
|
|
10881
|
+
meta.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-0.5 space-y-0.5 text-xs text-faint", children: meta.map((line) => /* @__PURE__ */ jsx("div", { className: "truncate", children: line }, line)) })
|
|
10882
|
+
] }) }) }),
|
|
10883
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
10884
|
+
isActive && canMutate.pause && /* @__PURE__ */ jsx(
|
|
10885
|
+
"button",
|
|
10886
|
+
{
|
|
10887
|
+
type: "button",
|
|
10888
|
+
className: "rounded p-1 text-faint opacity-70 transition-all hover:bg-line hover:text-foreground disabled:pointer-events-none disabled:opacity-40 group-hover:opacity-100",
|
|
10889
|
+
onClick: (e) => {
|
|
10890
|
+
stopActionClick(e);
|
|
10891
|
+
onPause(mission.id);
|
|
10892
|
+
},
|
|
10893
|
+
"aria-label": "Pause mission",
|
|
10894
|
+
title: "Pause mission",
|
|
10895
|
+
disabled: busy,
|
|
10896
|
+
children: /* @__PURE__ */ jsx(Pause, { className: "size-3.5" })
|
|
10897
|
+
}
|
|
10898
|
+
),
|
|
10899
|
+
isPaused && canMutate.resume && /* @__PURE__ */ jsx(
|
|
10900
|
+
"button",
|
|
10901
|
+
{
|
|
10902
|
+
type: "button",
|
|
10903
|
+
className: "rounded p-1 text-faint opacity-70 transition-all hover:bg-line hover:text-foreground disabled:pointer-events-none disabled:opacity-40 group-hover:opacity-100",
|
|
10904
|
+
onClick: (e) => {
|
|
10905
|
+
stopActionClick(e);
|
|
10906
|
+
onResume(mission.id);
|
|
10907
|
+
},
|
|
10908
|
+
"aria-label": "Resume mission",
|
|
10909
|
+
title: "Resume mission",
|
|
10910
|
+
disabled: busy,
|
|
10911
|
+
children: /* @__PURE__ */ jsx(Play, { className: "size-3.5" })
|
|
10912
|
+
}
|
|
10913
|
+
),
|
|
10914
|
+
(isActive || isPaused) && canMutate.cancel && /* @__PURE__ */ jsx(
|
|
10915
|
+
"button",
|
|
10916
|
+
{
|
|
10917
|
+
type: "button",
|
|
10918
|
+
className: "rounded p-1 text-faint opacity-70 transition-all hover:bg-destructive/10 hover:text-destructive disabled:pointer-events-none disabled:opacity-40 group-hover:opacity-100",
|
|
10919
|
+
onClick: (e) => {
|
|
10920
|
+
stopActionClick(e);
|
|
10921
|
+
onCancel(mission.id);
|
|
10922
|
+
},
|
|
10923
|
+
"aria-label": "Cancel mission",
|
|
10924
|
+
title: "Cancel mission",
|
|
10925
|
+
disabled: busy,
|
|
10926
|
+
children: /* @__PURE__ */ jsx(Ban, { className: "size-3.5" })
|
|
10927
|
+
}
|
|
10928
|
+
)
|
|
10929
|
+
] })
|
|
10930
|
+
]
|
|
10931
|
+
}
|
|
10932
|
+
);
|
|
10933
|
+
}
|
|
10934
|
+
function MissionsPanel({
|
|
10935
|
+
adapter,
|
|
10936
|
+
agentId,
|
|
10937
|
+
title = "Missions",
|
|
10938
|
+
status = DEFAULT_STATUS,
|
|
10939
|
+
hideHeader = false,
|
|
10940
|
+
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS2,
|
|
10941
|
+
onMissionSelect,
|
|
10942
|
+
onMissionPause,
|
|
10943
|
+
onMissionResume,
|
|
10944
|
+
onMissionCancel
|
|
10945
|
+
}) {
|
|
10946
|
+
const [items, setItems] = useState([]);
|
|
10947
|
+
const [error, setError] = useState(null);
|
|
10948
|
+
const [hasEverLoaded, setHasEverLoaded] = useState(false);
|
|
10949
|
+
const [busyMissionId, setBusyMissionId] = useState(null);
|
|
10950
|
+
const [collapsed, setCollapsed] = useState(false);
|
|
10951
|
+
const mounted = useRef(true);
|
|
10952
|
+
const requestId = useRef(0);
|
|
10953
|
+
const lastFingerprint = useRef("");
|
|
10954
|
+
const refetch = useCallback(
|
|
10955
|
+
async (opts) => {
|
|
10956
|
+
if (!adapter.listMissions) {
|
|
10957
|
+
setItems([]);
|
|
10958
|
+
setHasEverLoaded(true);
|
|
10959
|
+
return;
|
|
10960
|
+
}
|
|
10961
|
+
const currentRequestId = ++requestId.current;
|
|
10962
|
+
try {
|
|
10963
|
+
const list = await adapter.listMissions({ agentId, status });
|
|
10964
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
10965
|
+
const next = sortMissions(list.missions);
|
|
10966
|
+
const fp = fingerprint(next);
|
|
10967
|
+
if (fp !== lastFingerprint.current) {
|
|
10968
|
+
lastFingerprint.current = fp;
|
|
10969
|
+
setItems(next);
|
|
10970
|
+
}
|
|
10971
|
+
setError(null);
|
|
10972
|
+
setHasEverLoaded(true);
|
|
10973
|
+
} catch (e) {
|
|
10974
|
+
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
10975
|
+
if (!opts?.silent) {
|
|
10976
|
+
setError(
|
|
10977
|
+
e instanceof Error ? e.message : "Failed to load missions"
|
|
10978
|
+
);
|
|
10979
|
+
setHasEverLoaded(true);
|
|
10980
|
+
}
|
|
10981
|
+
}
|
|
10982
|
+
},
|
|
10983
|
+
[adapter, agentId, status]
|
|
10984
|
+
);
|
|
10985
|
+
useEffect(() => {
|
|
10986
|
+
mounted.current = true;
|
|
10987
|
+
return () => {
|
|
10988
|
+
mounted.current = false;
|
|
10989
|
+
};
|
|
10990
|
+
}, []);
|
|
10991
|
+
useEffect(() => {
|
|
10992
|
+
setError(null);
|
|
10993
|
+
lastFingerprint.current = "";
|
|
10994
|
+
void refetch();
|
|
10995
|
+
}, [refetch]);
|
|
10996
|
+
useEffect(() => {
|
|
10997
|
+
if (!adapter.listMissions || pollIntervalMs <= 0) return;
|
|
10998
|
+
const id = setInterval(() => {
|
|
10999
|
+
void refetch({ silent: true });
|
|
11000
|
+
}, pollIntervalMs);
|
|
11001
|
+
return () => clearInterval(id);
|
|
11002
|
+
}, [adapter.listMissions, pollIntervalMs, refetch]);
|
|
11003
|
+
const activeCount = useMemo(
|
|
11004
|
+
() => items.filter((m) => m.status === "active").length,
|
|
11005
|
+
[items]
|
|
11006
|
+
);
|
|
11007
|
+
const handleOpen = useCallback(
|
|
11008
|
+
(missionId) => {
|
|
11009
|
+
onMissionSelect?.(missionId);
|
|
11010
|
+
},
|
|
11011
|
+
[onMissionSelect]
|
|
11012
|
+
);
|
|
11013
|
+
const handlePause = useCallback(
|
|
11014
|
+
async (missionId) => {
|
|
11015
|
+
if (!adapter.pauseMission || busyMissionId) return;
|
|
11016
|
+
setBusyMissionId(missionId);
|
|
11017
|
+
try {
|
|
11018
|
+
await adapter.pauseMission({ missionId });
|
|
11019
|
+
onMissionPause?.(missionId);
|
|
11020
|
+
await refetch({ silent: true });
|
|
11021
|
+
} catch (e) {
|
|
11022
|
+
setError(e instanceof Error ? e.message : "Failed to pause mission");
|
|
11023
|
+
} finally {
|
|
11024
|
+
if (mounted.current) setBusyMissionId(null);
|
|
11025
|
+
}
|
|
11026
|
+
},
|
|
11027
|
+
[adapter, busyMissionId, onMissionPause, refetch]
|
|
11028
|
+
);
|
|
11029
|
+
const handleResume = useCallback(
|
|
11030
|
+
async (missionId) => {
|
|
11031
|
+
if (!adapter.resumeMission || busyMissionId) return;
|
|
11032
|
+
setBusyMissionId(missionId);
|
|
11033
|
+
try {
|
|
11034
|
+
await adapter.resumeMission({ missionId });
|
|
11035
|
+
onMissionResume?.(missionId);
|
|
11036
|
+
await refetch({ silent: true });
|
|
11037
|
+
} catch (e) {
|
|
11038
|
+
setError(e instanceof Error ? e.message : "Failed to resume mission");
|
|
11039
|
+
} finally {
|
|
11040
|
+
if (mounted.current) setBusyMissionId(null);
|
|
11041
|
+
}
|
|
11042
|
+
},
|
|
11043
|
+
[adapter, busyMissionId, onMissionResume, refetch]
|
|
11044
|
+
);
|
|
11045
|
+
const handleCancel = useCallback(
|
|
11046
|
+
async (missionId) => {
|
|
11047
|
+
if (!adapter.cancelMission || busyMissionId) return;
|
|
11048
|
+
setBusyMissionId(missionId);
|
|
11049
|
+
try {
|
|
11050
|
+
await adapter.cancelMission({ missionId });
|
|
11051
|
+
setItems((current) => {
|
|
11052
|
+
const next = current.filter((m) => m.id !== missionId);
|
|
11053
|
+
lastFingerprint.current = fingerprint(next);
|
|
11054
|
+
return next;
|
|
11055
|
+
});
|
|
11056
|
+
onMissionCancel?.(missionId);
|
|
11057
|
+
await refetch({ silent: true });
|
|
11058
|
+
} catch (e) {
|
|
11059
|
+
setError(e instanceof Error ? e.message : "Failed to cancel mission");
|
|
11060
|
+
} finally {
|
|
11061
|
+
if (mounted.current) setBusyMissionId(null);
|
|
11062
|
+
}
|
|
11063
|
+
},
|
|
11064
|
+
[adapter, busyMissionId, onMissionCancel, refetch]
|
|
11065
|
+
);
|
|
11066
|
+
if (!hasEverLoaded) return null;
|
|
11067
|
+
if (!adapter.listMissions) return null;
|
|
11068
|
+
const showBody = hideHeader || !collapsed;
|
|
11069
|
+
const canMutate = {
|
|
11070
|
+
pause: Boolean(adapter.pauseMission),
|
|
11071
|
+
resume: Boolean(adapter.resumeMission),
|
|
11072
|
+
cancel: Boolean(adapter.cancelMission)
|
|
11073
|
+
};
|
|
11074
|
+
return /* @__PURE__ */ jsxs("section", { className: "min-w-0 border-t border-line", children: [
|
|
11075
|
+
!hideHeader && /* @__PURE__ */ jsxs(
|
|
11076
|
+
"button",
|
|
11077
|
+
{
|
|
11078
|
+
type: "button",
|
|
11079
|
+
className: "flex w-full items-center gap-1.5 px-3 py-2 text-left text-xs font-semibold uppercase tracking-wide text-foreground transition-colors hover:bg-input",
|
|
11080
|
+
onClick: () => setCollapsed((c) => !c),
|
|
11081
|
+
"aria-expanded": !collapsed,
|
|
11082
|
+
children: [
|
|
11083
|
+
/* @__PURE__ */ jsx(TargetIcon, { className: "size-3.5" }),
|
|
11084
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: title }),
|
|
11085
|
+
activeCount > 0 && /* @__PURE__ */ jsx(Badge, { variant: "default", className: "ml-auto", children: activeCount }),
|
|
11086
|
+
collapsed ? /* @__PURE__ */ jsx(ChevronRightIcon, { className: "size-3.5 shrink-0 text-faint" }) : /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3.5 shrink-0 text-faint" })
|
|
11087
|
+
]
|
|
11088
|
+
}
|
|
11089
|
+
),
|
|
11090
|
+
showBody && error && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
|
|
11091
|
+
showBody && !error && items.length === 0 && /* @__PURE__ */ jsx("div", { className: "px-3 py-2 text-xs text-faint", children: "No missions" }),
|
|
11092
|
+
showBody && !error && items.length > 0 && /* @__PURE__ */ jsx("div", { className: cn("pb-2", hideHeader && "pt-1"), children: items.map((mission) => /* @__PURE__ */ jsx(
|
|
11093
|
+
MissionRow,
|
|
11094
|
+
{
|
|
11095
|
+
mission,
|
|
11096
|
+
busy: busyMissionId === mission.id,
|
|
11097
|
+
canMutate,
|
|
11098
|
+
onOpen: handleOpen,
|
|
11099
|
+
onPause: handlePause,
|
|
11100
|
+
onResume: handleResume,
|
|
11101
|
+
onCancel: handleCancel
|
|
11102
|
+
},
|
|
11103
|
+
mission.id
|
|
11104
|
+
)) })
|
|
11105
|
+
] });
|
|
11106
|
+
}
|
|
11107
|
+
var DEFAULT_LIMIT2 = 50;
|
|
11108
|
+
var DEFAULT_POLL_INTERVAL_MS3 = 3e4;
|
|
11109
|
+
function fingerprint2(items) {
|
|
10237
11110
|
return items.map(
|
|
10238
11111
|
(item) => `${item.id}:${item.status}:${item.kind ?? ""}:${item.name ?? ""}:${item.prompt}:${item.scheduleDisplay}:${item.runCount}:${item.nextRunAt ?? ""}:${item.lastRunAt ?? ""}:${item.lastSessionId ?? ""}:${item.lastError ?? ""}:${item.updatedAt}`
|
|
10239
11112
|
).join("|");
|
|
@@ -10383,7 +11256,7 @@ function ScheduledWorkPanel({
|
|
|
10383
11256
|
limit = DEFAULT_LIMIT2,
|
|
10384
11257
|
status = "active",
|
|
10385
11258
|
hideHeader = false,
|
|
10386
|
-
pollIntervalMs =
|
|
11259
|
+
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS3,
|
|
10387
11260
|
onSessionSelect,
|
|
10388
11261
|
onScheduleCancel,
|
|
10389
11262
|
onScheduleRunNow
|
|
@@ -10412,7 +11285,7 @@ function ScheduledWorkPanel({
|
|
|
10412
11285
|
});
|
|
10413
11286
|
if (!mounted.current || currentRequestId !== requestId.current) return;
|
|
10414
11287
|
const nextItems = sortScheduledWork(list.items);
|
|
10415
|
-
const fp =
|
|
11288
|
+
const fp = fingerprint2(nextItems);
|
|
10416
11289
|
if (fp !== lastFingerprint.current) {
|
|
10417
11290
|
lastFingerprint.current = fp;
|
|
10418
11291
|
setItems(nextItems);
|
|
@@ -10483,7 +11356,7 @@ function ScheduledWorkPanel({
|
|
|
10483
11356
|
await adapter.cancelScheduledWork({ scheduleId });
|
|
10484
11357
|
setItems((current) => {
|
|
10485
11358
|
const next = current.filter((item) => item.id !== scheduleId);
|
|
10486
|
-
lastFingerprint.current =
|
|
11359
|
+
lastFingerprint.current = fingerprint2(next);
|
|
10487
11360
|
return next;
|
|
10488
11361
|
});
|
|
10489
11362
|
onScheduleCancel?.(scheduleId);
|
|
@@ -10902,6 +11775,6 @@ function SessionTreePanel({
|
|
|
10902
11775
|
] });
|
|
10903
11776
|
}
|
|
10904
11777
|
|
|
10905
|
-
export { AgentChat, AgentChatAdapterProvider, InboxPanel, MessageResponse, ScheduledWorkPanel, SessionTreePanel, useAgentChatAdapterContext, useAgentChatRuntime, useInboxUnreadCount };
|
|
11778
|
+
export { ACTIVE_MISSION_STATUSES, AgentChat, AgentChatAdapterProvider, InboxPanel, MessageResponse, MissionDashboard, MissionsPanel, ScheduledWorkPanel, SessionTreePanel, deriveMissionWorkerActivityLabel, groupMissionTasksByBucket, isTerminalMissionStatus, missionTaskBucket, useAgentChatAdapterContext, useAgentChatRuntime, useInboxUnreadCount };
|
|
10906
11779
|
//# sourceMappingURL=index.js.map
|
|
10907
11780
|
//# sourceMappingURL=index.js.map
|