@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 CHANGED
@@ -794,6 +794,29 @@ var ConversationScrollButton = ({
794
794
  );
795
795
  };
796
796
 
797
+ // src/components/ui/separator.tsx
798
+ init_utils();
799
+ function Separator({
800
+ className,
801
+ orientation = "horizontal",
802
+ decorative = true,
803
+ ...props
804
+ }) {
805
+ return /* @__PURE__ */ jsxRuntime.jsx(
806
+ radixUi.Separator.Root,
807
+ {
808
+ "data-slot": "separator",
809
+ decorative,
810
+ orientation,
811
+ className: cn(
812
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
813
+ className
814
+ ),
815
+ ...props
816
+ }
817
+ );
818
+ }
819
+
797
820
  // src/components/ai-elements/message.tsx
798
821
  init_utils();
799
822
  var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -4462,32 +4485,28 @@ function WebSearchGroupBlock({ tcs }) {
4462
4485
  }
4463
4486
  var ChatMessage = react.memo(function ChatMessage2({
4464
4487
  message,
4465
- isLast,
4466
4488
  onFileSelect
4467
4489
  }) {
4468
- if (message.role === "user") {
4469
- return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "user", children: /* @__PURE__ */ jsxRuntime.jsxs(MessageContent, { children: [
4470
- message.content,
4471
- message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx(
4472
- "img",
4473
- {
4474
- src: img.data,
4475
- alt: "Attached",
4476
- className: "max-w-64 max-h-48 rounded-lg border border-border object-contain"
4477
- },
4478
- i
4479
- )) }),
4480
- message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.attachments.map((a, i) => /* @__PURE__ */ jsxRuntime.jsx(
4481
- AttachmentChip,
4482
- {
4483
- attachment: a,
4484
- onSelect: onFileSelect
4485
- },
4486
- a.path ?? `pending-${i}`
4487
- )) })
4488
- ] }) });
4489
- }
4490
- return /* @__PURE__ */ jsxRuntime.jsx(AssistantMessage, { message, isLast, onFileSelect });
4490
+ return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "user", children: /* @__PURE__ */ jsxRuntime.jsxs(MessageContent, { children: [
4491
+ message.content,
4492
+ message.images && message.images.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.images.map((img, i) => /* @__PURE__ */ jsxRuntime.jsx(
4493
+ "img",
4494
+ {
4495
+ src: img.data,
4496
+ alt: "Attached",
4497
+ className: "max-w-64 max-h-48 rounded-lg border border-border object-contain"
4498
+ },
4499
+ i
4500
+ )) }),
4501
+ message.attachments && message.attachments.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mt-2", children: message.attachments.map((a, i) => /* @__PURE__ */ jsxRuntime.jsx(
4502
+ AttachmentChip,
4503
+ {
4504
+ attachment: a,
4505
+ onSelect: onFileSelect
4506
+ },
4507
+ a.path ?? `pending-${i}`
4508
+ )) })
4509
+ ] }) });
4491
4510
  });
4492
4511
  function iconForMime(mime) {
4493
4512
  if (!mime) return lucideReact.File;
@@ -4542,28 +4561,6 @@ function AttachmentChip({
4542
4561
  }
4543
4562
  );
4544
4563
  }
4545
- function AssistantMessage({
4546
- message,
4547
- isLast,
4548
- onFileSelect
4549
- }) {
4550
- const hasReasoning = !!message.reasoning;
4551
- const hasToolCalls = !!(message.toolCalls && message.toolCalls.length > 0);
4552
- const isStreaming = message.status === "streaming" && isLast;
4553
- const hasContent = !!(message.content && message.content !== message.reasoning);
4554
- return /* @__PURE__ */ jsxRuntime.jsx(Message, { from: "assistant", children: /* @__PURE__ */ jsxRuntime.jsxs(MessageContent, { children: [
4555
- hasReasoning && /* @__PURE__ */ jsxRuntime.jsxs(Reasoning, { isStreaming: isStreaming && !hasContent && !hasToolCalls, children: [
4556
- /* @__PURE__ */ jsxRuntime.jsx(ReasoningTrigger, {}),
4557
- /* @__PURE__ */ jsxRuntime.jsx(ReasoningContent, { children: message.reasoning })
4558
- ] }),
4559
- hasToolCalls && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full flex flex-col gap-8", children: message.toolCalls.map((tc) => /* @__PURE__ */ jsxRuntime.jsx(ToolCallBlock, { tc, onFileSelect }, tc.id)) }),
4560
- hasContent && /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { children: message.content }),
4561
- isStreaming && !hasContent && !hasToolCalls && !hasReasoning && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 py-1 text-muted-foreground", children: [
4562
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block size-1.5 animate-pulse rounded-full bg-primary" }),
4563
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs", children: "Thinking..." })
4564
- ] })
4565
- ] }) });
4566
- }
4567
4564
 
4568
4565
  // src/components/ui/command.tsx
4569
4566
  init_utils();
@@ -6156,6 +6153,11 @@ function ChatComposerInner({
6156
6153
  { value: "/goal pause", label: "/goal pause", description: "Pause automatic goal continuation" },
6157
6154
  { value: "/goal resume", label: "/goal resume", description: "Resume a paused goal" },
6158
6155
  { value: "/goal clear", label: "/goal clear", description: "Clear the current goal" },
6156
+ { value: "/mission ", label: "/mission", description: "Start an orchestrated rubric-judged mission" },
6157
+ { value: "/mission status", label: "/mission status", description: "Show current mission status" },
6158
+ { value: "/mission pause", label: "/mission pause", description: "Pause the mission evaluator" },
6159
+ { value: "/mission resume", label: "/mission resume", description: "Resume a paused mission" },
6160
+ { value: "/mission cancel", label: "/mission cancel", description: "Cancel the mission" },
6159
6161
  { value: "/loop", label: "/loop", description: "Schedule recurring prompt" },
6160
6162
  { value: "/loop list", label: "/loop list", description: "List active loops" },
6161
6163
  { value: "/loop cancel", label: "/loop cancel", description: "Cancel a loop by ID" }
@@ -6461,6 +6463,54 @@ function ChatComposerInner({
6461
6463
  }
6462
6464
  );
6463
6465
  }
6466
+ var MIN_CPS = 40;
6467
+ var MAX_CPS = 240;
6468
+ var DRAIN_SECONDS = 0.25;
6469
+ var SNAP_LAG = 800;
6470
+ var MAX_FRAME_DT = 0.1;
6471
+ function useSmoothStream(target, isStreaming) {
6472
+ const [displayedLen, setDisplayedLen] = react.useState(
6473
+ () => isStreaming ? 0 : target.length
6474
+ );
6475
+ const targetRef = react.useRef(target);
6476
+ targetRef.current = target;
6477
+ react.useEffect(() => {
6478
+ setDisplayedLen((cur) => cur > target.length ? target.length : cur);
6479
+ }, [target]);
6480
+ const isActive = isStreaming || displayedLen < target.length;
6481
+ react.useEffect(() => {
6482
+ if (!isActive) return;
6483
+ let raf = 0;
6484
+ let carry = 0;
6485
+ let lastTime = 0;
6486
+ const tick = (now) => {
6487
+ const dt = lastTime ? Math.min(MAX_FRAME_DT, (now - lastTime) / 1e3) : 0;
6488
+ lastTime = now;
6489
+ setDisplayedLen((current) => {
6490
+ const targetLen = targetRef.current.length;
6491
+ const lag = targetLen - current;
6492
+ if (lag <= 0) return current;
6493
+ if (lag >= SNAP_LAG) {
6494
+ carry = 0;
6495
+ return targetLen;
6496
+ }
6497
+ const cps = Math.min(
6498
+ MAX_CPS,
6499
+ Math.max(MIN_CPS, lag / DRAIN_SECONDS)
6500
+ );
6501
+ carry += cps * dt;
6502
+ const step = Math.floor(carry);
6503
+ if (step <= 0) return current;
6504
+ carry -= step;
6505
+ return Math.min(targetLen, current + step);
6506
+ });
6507
+ raf = requestAnimationFrame(tick);
6508
+ };
6509
+ raf = requestAnimationFrame(tick);
6510
+ return () => cancelAnimationFrame(raf);
6511
+ }, [isActive]);
6512
+ return displayedLen >= target.length ? target : target.slice(0, displayedLen);
6513
+ }
6464
6514
 
6465
6515
  // src/components/ai-elements/artifact.tsx
6466
6516
  init_utils();
@@ -7069,7 +7119,12 @@ function messageToEntries(msg, isLast, artifactFallbacks) {
7069
7119
  }
7070
7120
  }
7071
7121
  if (effectiveHasContent) {
7072
- entries.push({ kind: "text", key: `${msg.id}-text`, content: msg.content });
7122
+ entries.push({
7123
+ kind: "text",
7124
+ key: `${msg.id}-text`,
7125
+ content: msg.content,
7126
+ isStreaming
7127
+ });
7073
7128
  }
7074
7129
  const hasRunningTool = hasToolCalls && msg.toolCalls.some(
7075
7130
  (tc) => tc.status === "running"
@@ -7249,16 +7304,7 @@ function TimelineEntryItem({
7249
7304
  onFileSelect
7250
7305
  }) {
7251
7306
  if (entry.kind === "reasoning") {
7252
- return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
7253
- /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
7254
- /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
7255
- /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: "size-2" })
7256
- ] }),
7257
- /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(Reasoning, { isStreaming: entry.isStreaming, children: [
7258
- /* @__PURE__ */ jsxRuntime.jsx(ReasoningTrigger, {}),
7259
- /* @__PURE__ */ jsxRuntime.jsx(ReasoningContent, { children: entry.reasoning })
7260
- ] }) })
7261
- ] });
7307
+ return /* @__PURE__ */ jsxRuntime.jsx(ReasoningEntry, { entry, step });
7262
7308
  }
7263
7309
  if (entry.kind === "tool") {
7264
7310
  const rawStatus = effectiveStatus(entry.tc);
@@ -7310,13 +7356,7 @@ function TimelineEntryItem({
7310
7356
  ] });
7311
7357
  }
7312
7358
  if (entry.kind === "text") {
7313
- return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
7314
- /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
7315
- /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
7316
- /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: "size-2 border-none bg-foreground/40" })
7317
- ] }),
7318
- /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { children: entry.content }) })
7319
- ] });
7359
+ return /* @__PURE__ */ jsxRuntime.jsx(TextEntry, { entry, step });
7320
7360
  }
7321
7361
  if (entry.kind === "skill_invoked") {
7322
7362
  return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
@@ -7360,6 +7400,35 @@ function TimelineEntryItem({
7360
7400
  /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(Shimmer, { duration: 3, spread: 3, className: "text-sm", children: "Working on it..." }) })
7361
7401
  ] });
7362
7402
  }
7403
+ function ReasoningEntry({
7404
+ entry,
7405
+ step
7406
+ }) {
7407
+ const reasoning = useSmoothStream(entry.reasoning, entry.isStreaming);
7408
+ return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
7409
+ /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
7410
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
7411
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: "size-2" })
7412
+ ] }),
7413
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsxs(Reasoning, { isStreaming: entry.isStreaming, children: [
7414
+ /* @__PURE__ */ jsxRuntime.jsx(ReasoningTrigger, {}),
7415
+ /* @__PURE__ */ jsxRuntime.jsx(ReasoningContent, { children: reasoning })
7416
+ ] }) })
7417
+ ] });
7418
+ }
7419
+ function TextEntry({
7420
+ entry,
7421
+ step
7422
+ }) {
7423
+ const content = useSmoothStream(entry.content, entry.isStreaming);
7424
+ return /* @__PURE__ */ jsxRuntime.jsxs(TimelineItem, { step, children: [
7425
+ /* @__PURE__ */ jsxRuntime.jsxs(TimelineHeader, { children: [
7426
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineSeparator, { style: { backgroundColor: "var(--color-border)" } }),
7427
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineIndicator, { className: "size-2 border-none bg-foreground/40" })
7428
+ ] }),
7429
+ /* @__PURE__ */ jsxRuntime.jsx(TimelineContent, { children: /* @__PURE__ */ jsxRuntime.jsx(MessageResponse, { children: content }) })
7430
+ ] });
7431
+ }
7363
7432
  function AssistantGroup({
7364
7433
  messages,
7365
7434
  lastGlobalIndex,
@@ -7506,7 +7575,6 @@ function ChatThread({
7506
7575
  ChatMessage,
7507
7576
  {
7508
7577
  message: msg,
7509
- isLast: group.lastGlobalIndex === messages.length - 1,
7510
7578
  onFileSelect
7511
7579
  },
7512
7580
  msg.id
@@ -10538,10 +10606,822 @@ function useInboxUnreadCount(adapter) {
10538
10606
  }, [adapter]);
10539
10607
  return { unreadCount, hasLoaded, error };
10540
10608
  }
10609
+
10610
+ // src/components/ui/card.tsx
10541
10611
  init_utils();
10542
- var DEFAULT_LIMIT2 = 50;
10543
- var DEFAULT_POLL_INTERVAL_MS = 3e4;
10612
+ function Card({
10613
+ className,
10614
+ size = "default",
10615
+ ...props
10616
+ }) {
10617
+ return /* @__PURE__ */ jsxRuntime.jsx(
10618
+ "div",
10619
+ {
10620
+ "data-slot": "card",
10621
+ "data-size": size,
10622
+ className: cn(
10623
+ "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",
10624
+ className
10625
+ ),
10626
+ ...props
10627
+ }
10628
+ );
10629
+ }
10630
+ function CardHeader({ className, ...props }) {
10631
+ return /* @__PURE__ */ jsxRuntime.jsx(
10632
+ "div",
10633
+ {
10634
+ "data-slot": "card-header",
10635
+ className: cn(
10636
+ "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",
10637
+ className
10638
+ ),
10639
+ ...props
10640
+ }
10641
+ );
10642
+ }
10643
+ function CardTitle({ className, ...props }) {
10644
+ return /* @__PURE__ */ jsxRuntime.jsx(
10645
+ "div",
10646
+ {
10647
+ "data-slot": "card-title",
10648
+ className: cn(
10649
+ "font-heading text-lg font-semibold tracking-wider uppercase",
10650
+ className
10651
+ ),
10652
+ ...props
10653
+ }
10654
+ );
10655
+ }
10656
+ function CardDescription({ className, ...props }) {
10657
+ return /* @__PURE__ */ jsxRuntime.jsx(
10658
+ "div",
10659
+ {
10660
+ "data-slot": "card-description",
10661
+ className: cn("text-sm leading-relaxed text-muted-foreground", className),
10662
+ ...props
10663
+ }
10664
+ );
10665
+ }
10666
+ function CardContent({ className, ...props }) {
10667
+ return /* @__PURE__ */ jsxRuntime.jsx(
10668
+ "div",
10669
+ {
10670
+ "data-slot": "card-content",
10671
+ className: cn("px-8 group-data-[size=sm]/card:px-5", className),
10672
+ ...props
10673
+ }
10674
+ );
10675
+ }
10676
+
10677
+ // src/components/missions/mission-derive.ts
10678
+ var ACTIVE_MISSION_STATUSES = /* @__PURE__ */ new Set(["active", "paused"]);
10679
+ function isTerminalMissionStatus(status) {
10680
+ return !ACTIVE_MISSION_STATUSES.has(status);
10681
+ }
10682
+ function missionTaskBucket(status) {
10683
+ if (status === "done") return "done";
10684
+ if (status === "blocked") return "blocked";
10685
+ if (status === "failed" || status === "cancelled") {
10686
+ return "failed_or_cancelled";
10687
+ }
10688
+ return "in_flight";
10689
+ }
10690
+ function groupMissionTasksByBucket(tasks) {
10691
+ const buckets = {
10692
+ in_flight: [],
10693
+ done: [],
10694
+ blocked: [],
10695
+ failed_or_cancelled: []
10696
+ };
10697
+ for (const t of tasks) {
10698
+ buckets[missionTaskBucket(t.status)].push(t);
10699
+ }
10700
+ return buckets;
10701
+ }
10702
+ function deriveMissionWorkerActivityLabel(worker) {
10703
+ const kind = worker.latestEventKind;
10704
+ if (kind === "tool.call" && worker.latestEventSummary) {
10705
+ return `tool: ${truncate2(worker.latestEventSummary, 80)}`;
10706
+ }
10707
+ if (kind === "llm.response") {
10708
+ return "thinking";
10709
+ }
10710
+ if (kind === "tool.result") {
10711
+ return "received tool result";
10712
+ }
10713
+ if (kind && worker.latestEventSummary) {
10714
+ return `${kind}: ${truncate2(worker.latestEventSummary, 60)}`;
10715
+ }
10716
+ return worker.sessionStatus || "active";
10717
+ }
10718
+ function truncate2(s, max) {
10719
+ if (s.length <= max) return s;
10720
+ return `${s.slice(0, max - 1)}\u2026`;
10721
+ }
10722
+ var DEFAULT_POLL_INTERVAL_MS = 5e3;
10723
+ var INITIAL_STATE = {
10724
+ mission: null,
10725
+ tasks: [],
10726
+ workers: [],
10727
+ loading: true,
10728
+ error: null
10729
+ };
10730
+ function requireMissionApi(adapter) {
10731
+ const required = {
10732
+ getMission: adapter.getMission,
10733
+ getMissionTasks: adapter.getMissionTasks,
10734
+ getMissionWorkers: adapter.getMissionWorkers,
10735
+ pauseMission: adapter.pauseMission,
10736
+ resumeMission: adapter.resumeMission,
10737
+ cancelMission: adapter.cancelMission
10738
+ };
10739
+ for (const [name, fn] of Object.entries(required)) {
10740
+ if (typeof fn !== "function") {
10741
+ throw new Error(
10742
+ `MissionDashboard requires adapter.${name} to be implemented; this adapter is missing the mission surface.`
10743
+ );
10744
+ }
10745
+ }
10746
+ return required;
10747
+ }
10748
+ function MissionDashboard({
10749
+ adapter,
10750
+ missionId,
10751
+ pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
10752
+ onNavigateBack,
10753
+ onOpenTranscript
10754
+ }) {
10755
+ const api = react.useMemo(() => requireMissionApi(adapter), [adapter]);
10756
+ const [state, setState] = react.useState(INITIAL_STATE);
10757
+ const [cancelOpen, setCancelOpen] = react.useState(false);
10758
+ const [cancelCascade, setCancelCascade] = react.useState(false);
10759
+ const [busy, setBusy] = react.useState(false);
10760
+ const isMounted = react.useRef(true);
10761
+ react.useEffect(() => {
10762
+ isMounted.current = true;
10763
+ return () => {
10764
+ isMounted.current = false;
10765
+ };
10766
+ }, []);
10767
+ const refresh = react.useCallback(async () => {
10768
+ try {
10769
+ const [mission2, tasksResp, workersResp] = await Promise.all([
10770
+ api.getMission({ missionId }),
10771
+ api.getMissionTasks({ missionId }),
10772
+ api.getMissionWorkers({ missionId })
10773
+ ]);
10774
+ if (!isMounted.current) return;
10775
+ setState({
10776
+ mission: mission2,
10777
+ tasks: tasksResp.tasks,
10778
+ workers: workersResp.workers,
10779
+ loading: false,
10780
+ error: null
10781
+ });
10782
+ } catch (err) {
10783
+ if (!isMounted.current) return;
10784
+ setState((prev) => ({
10785
+ ...prev,
10786
+ loading: false,
10787
+ error: err instanceof Error ? err.message : String(err)
10788
+ }));
10789
+ }
10790
+ }, [api, missionId]);
10791
+ react.useEffect(() => {
10792
+ void refresh();
10793
+ }, [refresh]);
10794
+ react.useEffect(() => {
10795
+ if (!state.mission) return;
10796
+ if (isTerminalMissionStatus(state.mission.status)) return;
10797
+ const id = window.setInterval(() => {
10798
+ void refresh();
10799
+ }, pollIntervalMs);
10800
+ return () => {
10801
+ window.clearInterval(id);
10802
+ };
10803
+ }, [refresh, state.mission, pollIntervalMs]);
10804
+ const taskBuckets = react.useMemo(
10805
+ () => groupMissionTasksByBucket(state.tasks),
10806
+ [state.tasks]
10807
+ );
10808
+ const runningWorkerCount = react.useMemo(
10809
+ () => state.workers.filter((w) => w.taskStatus === "running").length,
10810
+ [state.workers]
10811
+ );
10812
+ if (state.loading && !state.mission) {
10813
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-full items-center justify-center p-6", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-5 animate-spin text-foreground/70" }) });
10814
+ }
10815
+ if (state.error && !state.mission) {
10816
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-6 space-y-3", children: [
10817
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-destructive", children: [
10818
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "size-4" }),
10819
+ "Failed to load mission: ",
10820
+ state.error
10821
+ ] }),
10822
+ /* @__PURE__ */ jsxRuntime.jsxs(
10823
+ Button,
10824
+ {
10825
+ variant: "outline",
10826
+ size: "sm",
10827
+ onClick: () => {
10828
+ setState(INITIAL_STATE);
10829
+ void refresh();
10830
+ },
10831
+ children: [
10832
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "size-4" }),
10833
+ " Retry"
10834
+ ]
10835
+ }
10836
+ )
10837
+ ] });
10838
+ }
10839
+ const mission = state.mission;
10840
+ const doPause = async () => {
10841
+ setBusy(true);
10842
+ try {
10843
+ await api.pauseMission({ missionId });
10844
+ await refresh();
10845
+ } finally {
10846
+ setBusy(false);
10847
+ }
10848
+ };
10849
+ const doResume = async () => {
10850
+ setBusy(true);
10851
+ try {
10852
+ await api.resumeMission({ missionId });
10853
+ await refresh();
10854
+ } finally {
10855
+ setBusy(false);
10856
+ }
10857
+ };
10858
+ const doCancel = async () => {
10859
+ setBusy(true);
10860
+ try {
10861
+ await api.cancelMission({
10862
+ missionId,
10863
+ cascadeToWorkers: cancelCascade
10864
+ });
10865
+ await refresh();
10866
+ setCancelOpen(false);
10867
+ } finally {
10868
+ setBusy(false);
10869
+ }
10870
+ };
10871
+ const statusVariant = mission.status === "satisfied" ? "default" : mission.status === "failed" || mission.status === "cancelled" ? "destructive" : "secondary";
10872
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-6 p-6 max-w-6xl mx-auto", children: [
10873
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
10874
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-4", children: [
10875
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1", children: [
10876
+ /* @__PURE__ */ jsxRuntime.jsx(CardTitle, { className: "wrap-break-word -uppercase text-sm", children: mission.description }),
10877
+ /* @__PURE__ */ jsxRuntime.jsxs(CardDescription, { className: "wrap-break-word text-foreground/70", children: [
10878
+ "Rubric: ",
10879
+ mission.rubric
10880
+ ] }),
10881
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 pt-2 text-xs text-foreground/70", children: [
10882
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: statusVariant, className: "font-mono text-foreground", children: mission.status }),
10883
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "font-mono text-primary", children: [
10884
+ "Iteration ",
10885
+ mission.iteration,
10886
+ "/",
10887
+ mission.maxIterations
10888
+ ] }),
10889
+ mission.lastEvaluationResult ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
10890
+ "Last verdict:",
10891
+ " ",
10892
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-primary", children: mission.lastEvaluationResult })
10893
+ ] }) : null
10894
+ ] })
10895
+ ] }),
10896
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: ACTIVE_MISSION_STATUSES.has(mission.status) ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
10897
+ mission.status === "active" ? /* @__PURE__ */ jsxRuntime.jsxs(
10898
+ Button,
10899
+ {
10900
+ size: "sm",
10901
+ variant: "outline",
10902
+ onClick: doPause,
10903
+ disabled: busy,
10904
+ children: [
10905
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, { className: "size-4" }),
10906
+ " Pause"
10907
+ ]
10908
+ }
10909
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
10910
+ Button,
10911
+ {
10912
+ size: "sm",
10913
+ variant: "outline",
10914
+ onClick: doResume,
10915
+ disabled: busy,
10916
+ children: [
10917
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "size-4" }),
10918
+ " Resume"
10919
+ ]
10920
+ }
10921
+ ),
10922
+ /* @__PURE__ */ jsxRuntime.jsxs(
10923
+ Button,
10924
+ {
10925
+ size: "sm",
10926
+ variant: "destructive",
10927
+ onClick: () => {
10928
+ setCancelCascade(false);
10929
+ setCancelOpen(true);
10930
+ },
10931
+ disabled: busy,
10932
+ children: [
10933
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "size-4" }),
10934
+ " Cancel"
10935
+ ]
10936
+ }
10937
+ )
10938
+ ] }) : onNavigateBack ? /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "sm", variant: "outline", onClick: onNavigateBack, children: "Back" }) : null })
10939
+ ] }) }),
10940
+ mission.lastEvaluationFeedback ? /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "space-y-2", children: [
10941
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-semibold uppercase tracking-widest text-foreground/70", children: "Evaluator feedback" }),
10942
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm whitespace-pre-wrap wrap-break-word", children: mission.lastEvaluationFeedback }),
10943
+ mission.lastEvaluationExplanation ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-foreground/70 whitespace-pre-wrap wrap-break-word", children: mission.lastEvaluationExplanation }) : null
10944
+ ] }) : null
10945
+ ] }),
10946
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
10947
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { children: [
10948
+ "Tasks (",
10949
+ state.tasks.length,
10950
+ ")"
10951
+ ] }) }),
10952
+ /* @__PURE__ */ jsxRuntime.jsxs(CardContent, { className: "space-y-4", children: [
10953
+ ["in_flight", "blocked", "done", "failed_or_cancelled"].map((bucket) => {
10954
+ const rows = taskBuckets[bucket];
10955
+ if (rows.length === 0) return null;
10956
+ const heading = {
10957
+ in_flight: "In flight",
10958
+ blocked: "Blocked",
10959
+ done: "Done",
10960
+ failed_or_cancelled: "Failed / cancelled"
10961
+ }[bucket];
10962
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
10963
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs font-semibold uppercase tracking-widest text-foreground/70", children: [
10964
+ heading,
10965
+ " (",
10966
+ rows.length,
10967
+ ")"
10968
+ ] }),
10969
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1.5", children: rows.map((task) => /* @__PURE__ */ jsxRuntime.jsx(
10970
+ "li",
10971
+ {
10972
+ className: "flex items-start justify-between gap-3 rounded border border-border/40 px-3 py-2 text-sm",
10973
+ children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
10974
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
10975
+ task.agentDefName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-foreground/70", children: task.agentDefName }) : null,
10976
+ task.attemptCount > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-foreground/70", children: [
10977
+ "attempt ",
10978
+ task.attemptCount,
10979
+ "/",
10980
+ task.maxAttempts
10981
+ ] }) : null
10982
+ ] }),
10983
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm wrap-break-word", children: task.goal }),
10984
+ task.parentIds.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-foreground/70", children: [
10985
+ "after:",
10986
+ " ",
10987
+ task.parentIds.map((p) => p.slice(0, 8)).join(", ")
10988
+ ] }) : null,
10989
+ task.result ? /* @__PURE__ */ jsxRuntime.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,
10990
+ task.resultMetadata ? /* @__PURE__ */ jsxRuntime.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
10991
+ ] })
10992
+ },
10993
+ task.id
10994
+ )) })
10995
+ ] }, bucket);
10996
+ }),
10997
+ state.tasks.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-foreground/70", children: "No tasks spawned yet." }) : null
10998
+ ] })
10999
+ ] }),
11000
+ /* @__PURE__ */ jsxRuntime.jsxs(Card, { children: [
11001
+ /* @__PURE__ */ jsxRuntime.jsx(CardHeader, { children: /* @__PURE__ */ jsxRuntime.jsxs(CardTitle, { children: [
11002
+ "Live workers (",
11003
+ state.workers.length,
11004
+ ")"
11005
+ ] }) }),
11006
+ /* @__PURE__ */ jsxRuntime.jsx(CardContent, { children: state.workers.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-foreground/70", children: "No workers attached to this mission right now." }) : /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1.5", children: state.workers.map((w) => {
11007
+ const onTranscriptClick = (e) => {
11008
+ if (onOpenTranscript) {
11009
+ e.preventDefault();
11010
+ onOpenTranscript(w.workerSessionId);
11011
+ }
11012
+ };
11013
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11014
+ "li",
11015
+ {
11016
+ className: "flex items-start justify-between gap-3 rounded border border-border/40 px-3 py-2 text-sm",
11017
+ children: [
11018
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
11019
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
11020
+ /* @__PURE__ */ jsxRuntime.jsx(Badge, { className: "text-foreground/70", children: w.taskStatus }),
11021
+ w.agentDefName ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-foreground/70", children: w.agentDefName }) : null
11022
+ ] }),
11023
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm wrap-break-word", children: deriveMissionWorkerActivityLabel(w) }),
11024
+ w.latestEventAt ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-foreground/70", children: new Date(w.latestEventAt).toLocaleTimeString() }) : null
11025
+ ] }),
11026
+ /* @__PURE__ */ jsxRuntime.jsx(
11027
+ "a",
11028
+ {
11029
+ href: w.transcriptUrl,
11030
+ target: "_blank",
11031
+ rel: "noreferrer",
11032
+ onClick: onTranscriptClick,
11033
+ className: "text-xs text-primary hover:underline self-start",
11034
+ children: "View session"
11035
+ }
11036
+ )
11037
+ ]
11038
+ },
11039
+ w.workerSessionId
11040
+ );
11041
+ }) }) })
11042
+ ] }),
11043
+ /* @__PURE__ */ jsxRuntime.jsx(Separator, {}),
11044
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex justify-between items-center text-xs text-foreground/70", children: [
11045
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
11046
+ "Auto-refresh every ",
11047
+ pollIntervalMs / 1e3,
11048
+ "s while mission is active."
11049
+ ] }),
11050
+ /* @__PURE__ */ jsxRuntime.jsxs(
11051
+ Button,
11052
+ {
11053
+ variant: "ghost",
11054
+ size: "sm",
11055
+ onClick: () => {
11056
+ void refresh();
11057
+ },
11058
+ disabled: busy,
11059
+ children: [
11060
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "size-4" }),
11061
+ " Refresh"
11062
+ ]
11063
+ }
11064
+ )
11065
+ ] }),
11066
+ /* @__PURE__ */ jsxRuntime.jsx(
11067
+ Dialog,
11068
+ {
11069
+ open: cancelOpen,
11070
+ onOpenChange: (open) => {
11071
+ if (!open && !busy) setCancelOpen(false);
11072
+ },
11073
+ children: /* @__PURE__ */ jsxRuntime.jsxs(DialogContent, { className: "sm:max-w-md", children: [
11074
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogHeader, { children: [
11075
+ /* @__PURE__ */ jsxRuntime.jsx(DialogTitle, { children: "Cancel mission?" }),
11076
+ /* @__PURE__ */ jsxRuntime.jsx(DialogDescription, { children: "This terminates the mission and clears its evaluator loop." })
11077
+ ] }),
11078
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
11079
+ /* @__PURE__ */ jsxRuntime.jsx(
11080
+ "input",
11081
+ {
11082
+ type: "checkbox",
11083
+ checked: cancelCascade,
11084
+ onChange: (e) => setCancelCascade(e.target.checked)
11085
+ }
11086
+ ),
11087
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
11088
+ "Also cancel ",
11089
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: runningWorkerCount }),
11090
+ " running worker",
11091
+ runningWorkerCount === 1 ? "" : "s",
11092
+ " (sends an interrupt to each worker session)."
11093
+ ] })
11094
+ ] }),
11095
+ /* @__PURE__ */ jsxRuntime.jsxs(DialogFooter, { children: [
11096
+ /* @__PURE__ */ jsxRuntime.jsx(
11097
+ Button,
11098
+ {
11099
+ variant: "outline",
11100
+ size: "sm",
11101
+ onClick: () => setCancelOpen(false),
11102
+ disabled: busy,
11103
+ children: "Keep mission"
11104
+ }
11105
+ ),
11106
+ /* @__PURE__ */ jsxRuntime.jsxs(
11107
+ Button,
11108
+ {
11109
+ variant: "destructive",
11110
+ size: "sm",
11111
+ onClick: doCancel,
11112
+ disabled: busy,
11113
+ children: [
11114
+ busy ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "size-4" }),
11115
+ "Cancel mission"
11116
+ ]
11117
+ }
11118
+ )
11119
+ ] })
11120
+ ] })
11121
+ }
11122
+ )
11123
+ ] });
11124
+ }
11125
+ init_utils();
11126
+ var DEFAULT_POLL_INTERVAL_MS2 = 3e4;
11127
+ var DEFAULT_STATUS = "active,paused";
10544
11128
  function fingerprint(items) {
11129
+ return items.map(
11130
+ (m) => `${m.id}:${m.status}:${m.iteration}:${m.lastEvaluationResult ?? ""}:${m.lastEvaluationAt ?? ""}:${m.pausedReason ?? ""}:${m.updatedAt}`
11131
+ ).join("|");
11132
+ }
11133
+ function sortMissions(items) {
11134
+ return [...items].sort((a, b) => {
11135
+ const rank = (s) => s === "active" ? 0 : s === "paused" ? 1 : 2;
11136
+ const diff = rank(a.status) - rank(b.status);
11137
+ if (diff !== 0) return diff;
11138
+ return (a.updatedAt ?? "") > (b.updatedAt ?? "") ? -1 : 1;
11139
+ });
11140
+ }
11141
+ function MissionRow({
11142
+ mission,
11143
+ onOpen,
11144
+ onPause,
11145
+ onResume,
11146
+ onCancel,
11147
+ busy,
11148
+ canMutate
11149
+ }) {
11150
+ const isActive = mission.status === "active";
11151
+ const isPaused = mission.status === "paused";
11152
+ const description = mission.description.trim() || "(no description)";
11153
+ const verdict = mission.lastEvaluationResult;
11154
+ const verdictAt = mission.lastEvaluationAt ? dateFns.formatDistanceToNow(new Date(mission.lastEvaluationAt), {
11155
+ addSuffix: true
11156
+ }) : null;
11157
+ const meta = [
11158
+ `Iter ${mission.iteration}/${mission.maxIterations}`,
11159
+ verdict ? `Verdict: ${verdict}${verdictAt ? ` (${verdictAt})` : ""}` : null,
11160
+ isPaused && mission.pausedReason ? `Paused: ${mission.pausedReason}` : null
11161
+ ].filter(Boolean);
11162
+ const stopActionClick = (e) => {
11163
+ e.stopPropagation();
11164
+ };
11165
+ const openMission = () => onOpen(mission.id);
11166
+ const handleRowKeyDown = (e) => {
11167
+ if (e.key !== "Enter" && e.key !== " ") return;
11168
+ e.preventDefault();
11169
+ openMission();
11170
+ };
11171
+ return /* @__PURE__ */ jsxRuntime.jsxs(
11172
+ "div",
11173
+ {
11174
+ className: cn(
11175
+ "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"
11176
+ ),
11177
+ onClick: openMission,
11178
+ onKeyDown: handleRowKeyDown,
11179
+ role: "button",
11180
+ tabIndex: 0,
11181
+ "aria-label": `Open mission ${description}`,
11182
+ title: "Open mission dashboard",
11183
+ children: [
11184
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex size-5 shrink-0 items-center justify-center text-faint", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TargetIcon, { className: "size-3.5" }) }),
11185
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex min-w-0 items-start gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
11186
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate text-foreground", children: description }),
11187
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 flex min-w-0 items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsx(
11188
+ Badge,
11189
+ {
11190
+ variant: isActive ? "default" : "secondary",
11191
+ className: "shrink-0",
11192
+ children: mission.status
11193
+ }
11194
+ ) }),
11195
+ meta.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-0.5 space-y-0.5 text-xs text-faint", children: meta.map((line) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "truncate", children: line }, line)) })
11196
+ ] }) }) }),
11197
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
11198
+ isActive && canMutate.pause && /* @__PURE__ */ jsxRuntime.jsx(
11199
+ "button",
11200
+ {
11201
+ type: "button",
11202
+ 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",
11203
+ onClick: (e) => {
11204
+ stopActionClick(e);
11205
+ onPause(mission.id);
11206
+ },
11207
+ "aria-label": "Pause mission",
11208
+ title: "Pause mission",
11209
+ disabled: busy,
11210
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pause, { className: "size-3.5" })
11211
+ }
11212
+ ),
11213
+ isPaused && canMutate.resume && /* @__PURE__ */ jsxRuntime.jsx(
11214
+ "button",
11215
+ {
11216
+ type: "button",
11217
+ 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",
11218
+ onClick: (e) => {
11219
+ stopActionClick(e);
11220
+ onResume(mission.id);
11221
+ },
11222
+ "aria-label": "Resume mission",
11223
+ title: "Resume mission",
11224
+ disabled: busy,
11225
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Play, { className: "size-3.5" })
11226
+ }
11227
+ ),
11228
+ (isActive || isPaused) && canMutate.cancel && /* @__PURE__ */ jsxRuntime.jsx(
11229
+ "button",
11230
+ {
11231
+ type: "button",
11232
+ 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",
11233
+ onClick: (e) => {
11234
+ stopActionClick(e);
11235
+ onCancel(mission.id);
11236
+ },
11237
+ "aria-label": "Cancel mission",
11238
+ title: "Cancel mission",
11239
+ disabled: busy,
11240
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "size-3.5" })
11241
+ }
11242
+ )
11243
+ ] })
11244
+ ]
11245
+ }
11246
+ );
11247
+ }
11248
+ function MissionsPanel({
11249
+ adapter,
11250
+ agentId,
11251
+ title = "Missions",
11252
+ status = DEFAULT_STATUS,
11253
+ hideHeader = false,
11254
+ pollIntervalMs = DEFAULT_POLL_INTERVAL_MS2,
11255
+ onMissionSelect,
11256
+ onMissionPause,
11257
+ onMissionResume,
11258
+ onMissionCancel
11259
+ }) {
11260
+ const [items, setItems] = react.useState([]);
11261
+ const [error, setError] = react.useState(null);
11262
+ const [hasEverLoaded, setHasEverLoaded] = react.useState(false);
11263
+ const [busyMissionId, setBusyMissionId] = react.useState(null);
11264
+ const [collapsed, setCollapsed] = react.useState(false);
11265
+ const mounted = react.useRef(true);
11266
+ const requestId = react.useRef(0);
11267
+ const lastFingerprint = react.useRef("");
11268
+ const refetch = react.useCallback(
11269
+ async (opts) => {
11270
+ if (!adapter.listMissions) {
11271
+ setItems([]);
11272
+ setHasEverLoaded(true);
11273
+ return;
11274
+ }
11275
+ const currentRequestId = ++requestId.current;
11276
+ try {
11277
+ const list = await adapter.listMissions({ agentId, status });
11278
+ if (!mounted.current || currentRequestId !== requestId.current) return;
11279
+ const next = sortMissions(list.missions);
11280
+ const fp = fingerprint(next);
11281
+ if (fp !== lastFingerprint.current) {
11282
+ lastFingerprint.current = fp;
11283
+ setItems(next);
11284
+ }
11285
+ setError(null);
11286
+ setHasEverLoaded(true);
11287
+ } catch (e) {
11288
+ if (!mounted.current || currentRequestId !== requestId.current) return;
11289
+ if (!opts?.silent) {
11290
+ setError(
11291
+ e instanceof Error ? e.message : "Failed to load missions"
11292
+ );
11293
+ setHasEverLoaded(true);
11294
+ }
11295
+ }
11296
+ },
11297
+ [adapter, agentId, status]
11298
+ );
11299
+ react.useEffect(() => {
11300
+ mounted.current = true;
11301
+ return () => {
11302
+ mounted.current = false;
11303
+ };
11304
+ }, []);
11305
+ react.useEffect(() => {
11306
+ setError(null);
11307
+ lastFingerprint.current = "";
11308
+ void refetch();
11309
+ }, [refetch]);
11310
+ react.useEffect(() => {
11311
+ if (!adapter.listMissions || pollIntervalMs <= 0) return;
11312
+ const id = setInterval(() => {
11313
+ void refetch({ silent: true });
11314
+ }, pollIntervalMs);
11315
+ return () => clearInterval(id);
11316
+ }, [adapter.listMissions, pollIntervalMs, refetch]);
11317
+ const activeCount = react.useMemo(
11318
+ () => items.filter((m) => m.status === "active").length,
11319
+ [items]
11320
+ );
11321
+ const handleOpen = react.useCallback(
11322
+ (missionId) => {
11323
+ onMissionSelect?.(missionId);
11324
+ },
11325
+ [onMissionSelect]
11326
+ );
11327
+ const handlePause = react.useCallback(
11328
+ async (missionId) => {
11329
+ if (!adapter.pauseMission || busyMissionId) return;
11330
+ setBusyMissionId(missionId);
11331
+ try {
11332
+ await adapter.pauseMission({ missionId });
11333
+ onMissionPause?.(missionId);
11334
+ await refetch({ silent: true });
11335
+ } catch (e) {
11336
+ setError(e instanceof Error ? e.message : "Failed to pause mission");
11337
+ } finally {
11338
+ if (mounted.current) setBusyMissionId(null);
11339
+ }
11340
+ },
11341
+ [adapter, busyMissionId, onMissionPause, refetch]
11342
+ );
11343
+ const handleResume = react.useCallback(
11344
+ async (missionId) => {
11345
+ if (!adapter.resumeMission || busyMissionId) return;
11346
+ setBusyMissionId(missionId);
11347
+ try {
11348
+ await adapter.resumeMission({ missionId });
11349
+ onMissionResume?.(missionId);
11350
+ await refetch({ silent: true });
11351
+ } catch (e) {
11352
+ setError(e instanceof Error ? e.message : "Failed to resume mission");
11353
+ } finally {
11354
+ if (mounted.current) setBusyMissionId(null);
11355
+ }
11356
+ },
11357
+ [adapter, busyMissionId, onMissionResume, refetch]
11358
+ );
11359
+ const handleCancel = react.useCallback(
11360
+ async (missionId) => {
11361
+ if (!adapter.cancelMission || busyMissionId) return;
11362
+ setBusyMissionId(missionId);
11363
+ try {
11364
+ await adapter.cancelMission({ missionId });
11365
+ setItems((current) => {
11366
+ const next = current.filter((m) => m.id !== missionId);
11367
+ lastFingerprint.current = fingerprint(next);
11368
+ return next;
11369
+ });
11370
+ onMissionCancel?.(missionId);
11371
+ await refetch({ silent: true });
11372
+ } catch (e) {
11373
+ setError(e instanceof Error ? e.message : "Failed to cancel mission");
11374
+ } finally {
11375
+ if (mounted.current) setBusyMissionId(null);
11376
+ }
11377
+ },
11378
+ [adapter, busyMissionId, onMissionCancel, refetch]
11379
+ );
11380
+ if (!hasEverLoaded) return null;
11381
+ if (!adapter.listMissions) return null;
11382
+ const showBody = hideHeader || !collapsed;
11383
+ const canMutate = {
11384
+ pause: Boolean(adapter.pauseMission),
11385
+ resume: Boolean(adapter.resumeMission),
11386
+ cancel: Boolean(adapter.cancelMission)
11387
+ };
11388
+ return /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "min-w-0 border-t border-line", children: [
11389
+ !hideHeader && /* @__PURE__ */ jsxRuntime.jsxs(
11390
+ "button",
11391
+ {
11392
+ type: "button",
11393
+ 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",
11394
+ onClick: () => setCollapsed((c) => !c),
11395
+ "aria-expanded": !collapsed,
11396
+ children: [
11397
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.TargetIcon, { className: "size-3.5" }),
11398
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: title }),
11399
+ activeCount > 0 && /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant: "default", className: "ml-auto", children: activeCount }),
11400
+ collapsed ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-3.5 shrink-0 text-faint" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-3.5 shrink-0 text-faint" })
11401
+ ]
11402
+ }
11403
+ ),
11404
+ showBody && error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-destructive", children: error }),
11405
+ showBody && !error && items.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3 py-2 text-xs text-faint", children: "No missions" }),
11406
+ showBody && !error && items.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("pb-2", hideHeader && "pt-1"), children: items.map((mission) => /* @__PURE__ */ jsxRuntime.jsx(
11407
+ MissionRow,
11408
+ {
11409
+ mission,
11410
+ busy: busyMissionId === mission.id,
11411
+ canMutate,
11412
+ onOpen: handleOpen,
11413
+ onPause: handlePause,
11414
+ onResume: handleResume,
11415
+ onCancel: handleCancel
11416
+ },
11417
+ mission.id
11418
+ )) })
11419
+ ] });
11420
+ }
11421
+ init_utils();
11422
+ var DEFAULT_LIMIT2 = 50;
11423
+ var DEFAULT_POLL_INTERVAL_MS3 = 3e4;
11424
+ function fingerprint2(items) {
10545
11425
  return items.map(
10546
11426
  (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}`
10547
11427
  ).join("|");
@@ -10691,7 +11571,7 @@ function ScheduledWorkPanel({
10691
11571
  limit = DEFAULT_LIMIT2,
10692
11572
  status = "active",
10693
11573
  hideHeader = false,
10694
- pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
11574
+ pollIntervalMs = DEFAULT_POLL_INTERVAL_MS3,
10695
11575
  onSessionSelect,
10696
11576
  onScheduleCancel,
10697
11577
  onScheduleRunNow
@@ -10720,7 +11600,7 @@ function ScheduledWorkPanel({
10720
11600
  });
10721
11601
  if (!mounted.current || currentRequestId !== requestId.current) return;
10722
11602
  const nextItems = sortScheduledWork(list.items);
10723
- const fp = fingerprint(nextItems);
11603
+ const fp = fingerprint2(nextItems);
10724
11604
  if (fp !== lastFingerprint.current) {
10725
11605
  lastFingerprint.current = fp;
10726
11606
  setItems(nextItems);
@@ -10791,7 +11671,7 @@ function ScheduledWorkPanel({
10791
11671
  await adapter.cancelScheduledWork({ scheduleId });
10792
11672
  setItems((current) => {
10793
11673
  const next = current.filter((item) => item.id !== scheduleId);
10794
- lastFingerprint.current = fingerprint(next);
11674
+ lastFingerprint.current = fingerprint2(next);
10795
11675
  return next;
10796
11676
  });
10797
11677
  onScheduleCancel?.(scheduleId);
@@ -11211,12 +12091,19 @@ function SessionTreePanel({
11211
12091
  ] });
11212
12092
  }
11213
12093
 
12094
+ exports.ACTIVE_MISSION_STATUSES = ACTIVE_MISSION_STATUSES;
11214
12095
  exports.AgentChat = AgentChat;
11215
12096
  exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
11216
12097
  exports.InboxPanel = InboxPanel;
11217
12098
  exports.MessageResponse = MessageResponse;
12099
+ exports.MissionDashboard = MissionDashboard;
12100
+ exports.MissionsPanel = MissionsPanel;
11218
12101
  exports.ScheduledWorkPanel = ScheduledWorkPanel;
11219
12102
  exports.SessionTreePanel = SessionTreePanel;
12103
+ exports.deriveMissionWorkerActivityLabel = deriveMissionWorkerActivityLabel;
12104
+ exports.groupMissionTasksByBucket = groupMissionTasksByBucket;
12105
+ exports.isTerminalMissionStatus = isTerminalMissionStatus;
12106
+ exports.missionTaskBucket = missionTaskBucket;
11220
12107
  exports.useAgentChatAdapterContext = useAgentChatAdapterContext;
11221
12108
  exports.useAgentChatRuntime = useAgentChatRuntime;
11222
12109
  exports.useInboxUnreadCount = useInboxUnreadCount;