@cadraos/sdk 0.3.8 → 0.3.10

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.
@@ -23,6 +23,10 @@ function mapHandler(h) {
23
23
  type: h.type,
24
24
  agentUuid: h.agent_uuid,
25
25
  teamUuid: h.team_uuid,
26
+ // FINDING-6: carry the remote_agent binding through the wire parse. The
27
+ // engine's remote_agent dispatch reads `remoteAgentUuid`; dropping it here
28
+ // made every published remote_agent lane dispatch-dead.
29
+ remoteAgentUuid: h.remote_agent_uuid,
26
30
  agentRef: h.agent_ref,
27
31
  webhook: h.webhook ? {
28
32
  url: h.webhook.url,
@@ -301,7 +305,7 @@ function useKanbanBoard(options) {
301
305
  try {
302
306
  const full = await adapter.getCard(cardId);
303
307
  setSelectedCard(
304
- (prev) => prev && prev.id === cardId ? full : prev
308
+ (prev) => prev == null || prev.id === cardId ? full : prev
305
309
  );
306
310
  } catch (err) {
307
311
  if (!known) fail(err, "Failed to open card");
@@ -410,6 +414,27 @@ function useKanbanBoard(options) {
410
414
  },
411
415
  [refresh]
412
416
  );
417
+ const deleteCard = react.useCallback(
418
+ async (cardId) => {
419
+ if (!adapter.deleteCard) return false;
420
+ setError(null);
421
+ const prevCards = cards;
422
+ const prevSelected = selectedCard;
423
+ setCards((prev) => prev.filter((c) => c.id !== cardId));
424
+ setSelectedCard((prev) => prev && prev.id === cardId ? null : prev);
425
+ try {
426
+ await adapter.deleteCard(cardId);
427
+ void silentRefresh();
428
+ return true;
429
+ } catch (err) {
430
+ setCards(prevCards);
431
+ setSelectedCard(prevSelected);
432
+ fail(err, "Failed to delete card");
433
+ return false;
434
+ }
435
+ },
436
+ [adapter, cards, selectedCard, silentRefresh, fail]
437
+ );
413
438
  return {
414
439
  board,
415
440
  lanes: board?.lanes ?? [],
@@ -425,6 +450,7 @@ function useKanbanBoard(options) {
425
450
  createCard,
426
451
  updateCard,
427
452
  transitionCard,
453
+ deleteCard,
428
454
  handleRunTerminal
429
455
  };
430
456
  }
@@ -2300,35 +2326,40 @@ function useCardRun(options) {
2300
2326
  function cn9(...classes) {
2301
2327
  return classes.filter(Boolean).join(" ");
2302
2328
  }
2303
- function runStatusTone(status) {
2304
- switch (status) {
2305
- case "running":
2306
- return {
2307
- label: "Running",
2308
- className: "bg-blue-50 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300"
2309
- };
2310
- case "completed":
2311
- return {
2312
- label: "Completed",
2313
- className: "bg-green-50 text-green-700 dark:bg-green-950/40 dark:text-green-300"
2314
- };
2315
- case "failed":
2316
- return {
2317
- label: "Failed",
2318
- className: "bg-red-50 text-red-700 dark:bg-red-950/40 dark:text-red-300"
2319
- };
2320
- case "cancelled":
2321
- return {
2322
- label: "Cancelled",
2323
- className: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
2324
- };
2329
+ function badgeVariantClass(variant) {
2330
+ switch (variant) {
2331
+ case "default":
2332
+ return "bg-primary text-primary-foreground";
2333
+ case "secondary":
2334
+ return "border border-border bg-secondary text-secondary-foreground";
2335
+ case "outline":
2336
+ return "border border-border text-foreground";
2337
+ case "info":
2338
+ return "border border-info/30 bg-info/15 text-info";
2339
+ case "success":
2340
+ return "border border-success/30 bg-success/15 text-success";
2341
+ case "warning":
2342
+ return "border border-warning/40 bg-warning/15 text-warning";
2343
+ case "destructive":
2344
+ return "border border-destructive/40 bg-destructive/15 text-destructive";
2345
+ case "neutral":
2325
2346
  default:
2326
- return {
2327
- label: "Pending",
2328
- className: "bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-300"
2329
- };
2347
+ return "border border-border bg-secondary text-muted-foreground";
2330
2348
  }
2331
2349
  }
2350
+ function runStatusTone(status) {
2351
+ const map = {
2352
+ running: { label: "Running", variant: "info" },
2353
+ completed: { label: "Completed", variant: "success" },
2354
+ failed: { label: "Failed", variant: "destructive" },
2355
+ cancelled: { label: "Cancelled", variant: "neutral" }
2356
+ };
2357
+ const { label, variant } = map[status ?? ""] ?? {
2358
+ label: "Pending",
2359
+ variant: "neutral"
2360
+ };
2361
+ return { label, variant, className: badgeVariantClass(variant) };
2362
+ }
2332
2363
  function CardDrawer({
2333
2364
  card,
2334
2365
  adapter,
@@ -2447,11 +2478,11 @@ function CardDrawer({
2447
2478
  role: "dialog",
2448
2479
  "aria-label": `Card: ${card.title}`,
2449
2480
  className: cn9(
2450
- "flex h-full w-full sm:max-w-sm flex-col bg-white dark:bg-gray-950 border-l border-gray-200 dark:border-gray-800 shadow-xl",
2481
+ "flex h-full w-full sm:max-w-sm flex-col bg-card border-l border-border shadow-xl",
2451
2482
  className
2452
2483
  ),
2453
2484
  children: [
2454
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-gray-200 dark:border-gray-800 px-4 py-3", children: [
2485
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-border px-4 py-3", children: [
2455
2486
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
2456
2487
  isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
2457
2488
  "input",
@@ -2461,14 +2492,14 @@ function CardDrawer({
2461
2492
  onChange: (e) => setEditTitle(e.target.value),
2462
2493
  "aria-label": "Card title",
2463
2494
  placeholder: "Card title",
2464
- className: "w-full rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 py-1 text-base font-semibold text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500/40"
2495
+ className: "w-full rounded-md border border-input bg-background px-2 py-1 text-base font-semibold text-foreground focus:outline-none focus:ring-2 focus:ring-ring/50"
2465
2496
  }
2466
2497
  ) : /* @__PURE__ */ jsxRuntime.jsx(
2467
2498
  "h2",
2468
2499
  {
2469
2500
  className: cn9(
2470
- "text-base font-semibold text-gray-900 dark:text-gray-100 truncate",
2471
- onUpdate && "cursor-text rounded hover:bg-gray-100 dark:hover:bg-gray-800/60 -mx-1 px-1"
2501
+ "text-base font-semibold text-foreground truncate",
2502
+ onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
2472
2503
  ),
2473
2504
  onClick: onUpdate ? () => setIsEditing(true) : void 0,
2474
2505
  title: onUpdate ? "Click to edit" : void 0,
@@ -2476,7 +2507,7 @@ function CardDrawer({
2476
2507
  }
2477
2508
  ),
2478
2509
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
2479
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: card.lane }),
2510
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: card.lane }),
2480
2511
  /* @__PURE__ */ jsxRuntime.jsx(
2481
2512
  "span",
2482
2513
  {
@@ -2496,7 +2527,7 @@ function CardDrawer({
2496
2527
  type: "button",
2497
2528
  onClick: () => setIsEditing(true),
2498
2529
  "aria-label": "Edit card",
2499
- className: "rounded p-1 text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-200",
2530
+ className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
2500
2531
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2501
2532
  "svg",
2502
2533
  {
@@ -2522,7 +2553,7 @@ function CardDrawer({
2522
2553
  type: "button",
2523
2554
  onClick: onClose,
2524
2555
  "aria-label": "Close",
2525
- className: "rounded p-1 text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-200",
2556
+ className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
2526
2557
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2527
2558
  "svg",
2528
2559
  {
@@ -2544,8 +2575,8 @@ function CardDrawer({
2544
2575
  )
2545
2576
  ] })
2546
2577
  ] }),
2547
- isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-gray-200 dark:border-gray-800 px-4 py-3", children: [
2548
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-1 block text-xs font-medium text-gray-500 dark:text-gray-400", children: "Description" }),
2578
+ isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border px-4 py-3", children: [
2579
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Description" }),
2549
2580
  /* @__PURE__ */ jsxRuntime.jsx(
2550
2581
  "textarea",
2551
2582
  {
@@ -2554,7 +2585,7 @@ function CardDrawer({
2554
2585
  "aria-label": "Card description",
2555
2586
  rows: 4,
2556
2587
  placeholder: "Add a description\u2026",
2557
- className: "w-full resize-y rounded-md border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 py-1.5 text-sm text-gray-700 dark:text-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-500/40"
2588
+ className: "w-full resize-y rounded-md border border-input bg-background px-2 py-1.5 text-sm text-foreground focus:outline-none focus:ring-2 focus:ring-ring/50"
2558
2589
  }
2559
2590
  ),
2560
2591
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center justify-end gap-2", children: [
@@ -2564,7 +2595,7 @@ function CardDrawer({
2564
2595
  type: "button",
2565
2596
  onClick: handleCancel,
2566
2597
  disabled: isSaving,
2567
- className: "rounded-md px-3 py-1.5 text-sm font-medium text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-800 disabled:opacity-50",
2598
+ className: "rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground hover:bg-muted disabled:opacity-50",
2568
2599
  children: "Cancel"
2569
2600
  }
2570
2601
  ),
@@ -2574,26 +2605,27 @@ function CardDrawer({
2574
2605
  type: "button",
2575
2606
  onClick: () => void handleSave(),
2576
2607
  disabled: isSaving,
2577
- className: "rounded-md bg-blue-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500/40 disabled:opacity-50",
2608
+ className: "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:opacity-50",
2578
2609
  children: isSaving ? "Saving\u2026" : "Save"
2579
2610
  }
2580
2611
  )
2581
2612
  ] })
2582
- ] }) : (card.description || onUpdate) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-gray-200 dark:border-gray-800 px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(
2613
+ ] }) : (card.description || onUpdate) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(
2583
2614
  "p",
2584
2615
  {
2585
2616
  className: cn9(
2586
2617
  "text-sm whitespace-pre-wrap",
2587
- card.description ? "text-gray-600 dark:text-gray-300" : "italic text-gray-400 dark:text-gray-600",
2588
- onUpdate && "cursor-text rounded hover:bg-gray-100 dark:hover:bg-gray-800/60 -mx-1 px-1"
2618
+ card.description ? "text-muted-foreground" : "italic text-muted-foreground/70",
2619
+ onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
2589
2620
  ),
2590
2621
  onClick: onUpdate ? () => setIsEditing(true) : void 0,
2591
2622
  title: onUpdate ? "Click to edit" : void 0,
2592
2623
  children: card.description || "Add a description\u2026"
2593
2624
  }
2594
2625
  ) }),
2595
- showTransitionButtons ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 border-b border-gray-200 dark:border-gray-800 px-4 py-2", children: [
2596
- isHumanGate && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-auto text-xs font-medium text-amber-600 dark:text-amber-400", children: "Awaiting your approval" }),
2626
+ showTransitionButtons ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 border-b border-border px-4 py-2", children: [
2627
+ isHumanGate && // Human gate = needs-you → warning (amber is reserved for this, F3).
2628
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-auto text-xs font-medium text-warning", children: "Awaiting your approval" }),
2597
2629
  manualEdges.map((t, i) => {
2598
2630
  const toLabel = laneByKey.get(t.toLane)?.label ?? t.toLane;
2599
2631
  const isPrimary = isHumanGate ? i === 0 : false;
@@ -2606,26 +2638,27 @@ function CardDrawer({
2606
2638
  onClick: () => void handleTransition(t.toLane),
2607
2639
  disabled: pendingLane !== null,
2608
2640
  className: cn9(
2609
- "rounded-md px-3 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 disabled:opacity-50",
2610
- isPrimary ? "bg-emerald-600 text-white hover:bg-emerald-700 focus:ring-emerald-500/40" : "border border-gray-300 dark:border-gray-700 text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 focus:ring-blue-500/40"
2641
+ // ≥44px touch (M12/F5) — density kept via padding, not height.
2642
+ "inline-flex min-h-11 items-center justify-center rounded-md px-3 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:opacity-50",
2643
+ isPrimary ? "border border-border bg-secondary text-secondary-foreground hover:bg-muted" : "border border-border text-foreground hover:bg-muted"
2611
2644
  ),
2612
2645
  children: busy ? "Moving\u2026" : label
2613
2646
  },
2614
2647
  t.id
2615
2648
  );
2616
2649
  })
2617
- ] }) : onAdvance && canAdvance && !isEditing && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end border-b border-gray-200 dark:border-gray-800 px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(
2650
+ ] }) : onAdvance && canAdvance && !isEditing && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end border-b border-border px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(
2618
2651
  "button",
2619
2652
  {
2620
2653
  type: "button",
2621
2654
  onClick: () => void handleAdvance(),
2622
2655
  disabled: isAdvancing,
2623
- className: "rounded-md border border-gray-300 dark:border-gray-700 px-3 py-1.5 text-sm font-medium text-gray-700 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-gray-800 focus:outline-none focus:ring-2 focus:ring-blue-500/40 disabled:opacity-50",
2656
+ className: "inline-flex min-h-11 items-center justify-center rounded-md border border-border px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50 disabled:opacity-50",
2624
2657
  children: isAdvancing ? "Advancing\u2026" : "Advance \u2192"
2625
2658
  }
2626
2659
  ) }),
2627
2660
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
2628
- !hasExecution ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center p-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-400 dark:text-gray-600", children: "No run yet for this card." }) }) : /* @__PURE__ */ jsxRuntime.jsx(
2661
+ !hasExecution ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 items-center justify-center p-6 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "No run yet for this card." }) }) : /* @__PURE__ */ jsxRuntime.jsx(
2629
2662
  MessageList,
2630
2663
  {
2631
2664
  messages: run.messages,
@@ -2638,14 +2671,14 @@ function CardDrawer({
2638
2671
  className: "flex-1"
2639
2672
  }
2640
2673
  ),
2641
- failed && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-red-200 dark:border-red-900 bg-red-50 dark:bg-red-950/30 px-4 py-3", children: [
2642
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-red-700 dark:text-red-300", children: run.error ?? "Run failed." }),
2674
+ failed && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-destructive/40 bg-destructive/10 px-4 py-3", children: [
2675
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-destructive", children: run.error ?? "Run failed." }),
2643
2676
  onRetry && /* @__PURE__ */ jsxRuntime.jsx(
2644
2677
  "button",
2645
2678
  {
2646
2679
  type: "button",
2647
2680
  onClick: () => onRetry(card),
2648
- className: "shrink-0 rounded-md bg-red-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500/40",
2681
+ className: "inline-flex min-h-11 shrink-0 items-center justify-center rounded-md border border-border bg-card px-3 py-1.5 text-sm font-medium text-foreground hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
2649
2682
  children: "Retry"
2650
2683
  }
2651
2684
  )
@@ -2687,10 +2720,54 @@ function Spinner({ className }) {
2687
2720
  }
2688
2721
  );
2689
2722
  }
2690
- function KanbanCard({ card, onClick, selected, className }) {
2723
+ function ClockIcon2() {
2724
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2725
+ "svg",
2726
+ {
2727
+ viewBox: "0 0 24 24",
2728
+ fill: "none",
2729
+ stroke: "currentColor",
2730
+ strokeWidth: "2",
2731
+ strokeLinecap: "round",
2732
+ strokeLinejoin: "round",
2733
+ "aria-hidden": true,
2734
+ className: "h-2.5 w-2.5",
2735
+ children: [
2736
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
2737
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
2738
+ ]
2739
+ }
2740
+ );
2741
+ }
2742
+ function KanbanCard({
2743
+ card,
2744
+ onClick,
2745
+ selected,
2746
+ dueState,
2747
+ draggable,
2748
+ onDragStart,
2749
+ onDragEnd,
2750
+ className
2751
+ }) {
2691
2752
  const runStatus = card.run?.status;
2692
- const tone = runStatus ? runStatusTone(runStatus) : null;
2693
- const isWorking = runStatus === "running" || runStatus === "pending";
2753
+ const isWorking = runStatus === "running";
2754
+ const failed = runStatus === "failed";
2755
+ let chip = null;
2756
+ if (failed) {
2757
+ chip = { label: "Failed", variant: "destructive" };
2758
+ } else if (dueState === "ready") {
2759
+ chip = { label: "Ready", variant: "success" };
2760
+ } else if (runStatus === "completed") {
2761
+ const t = runStatusTone(runStatus);
2762
+ chip = { label: t.label, variant: t.variant };
2763
+ } else if (dueState === "due") {
2764
+ chip = { label: "Due", variant: "neutral", icon: "clock" };
2765
+ } else if (dueState === "waiting") {
2766
+ chip = { label: "Waiting", variant: "neutral" };
2767
+ } else if (runStatus) {
2768
+ const t = runStatusTone(runStatus);
2769
+ chip = { label: t.label, variant: t.variant };
2770
+ }
2694
2771
  return /* @__PURE__ */ jsxRuntime.jsxs(
2695
2772
  "button",
2696
2773
  {
@@ -2698,47 +2775,438 @@ function KanbanCard({ card, onClick, selected, className }) {
2698
2775
  onClick: () => onClick?.(card),
2699
2776
  "aria-pressed": selected,
2700
2777
  "aria-busy": isWorking,
2778
+ draggable,
2779
+ onDragStart: draggable ? (e) => {
2780
+ e.dataTransfer.effectAllowed = "move";
2781
+ e.dataTransfer.setData("text/plain", card.id);
2782
+ onDragStart?.(card);
2783
+ } : void 0,
2784
+ onDragEnd: draggable ? () => onDragEnd?.() : void 0,
2701
2785
  className: cn9(
2702
- "relative w-full text-left rounded-lg border bg-white dark:bg-gray-900 p-3 shadow-sm transition",
2703
- "hover:shadow-md hover:border-gray-300 dark:hover:border-gray-600",
2704
- "focus:outline-none focus:ring-2 focus:ring-blue-500/40",
2705
- isWorking ? "border-amber-400 dark:border-amber-500 ring-2 ring-amber-400/50 animate-pulse" : selected ? "border-blue-400 dark:border-blue-500 ring-2 ring-blue-500/30" : "border-gray-200 dark:border-gray-700",
2786
+ // Solid card: fill + border + a faint token top-sheen (adapts to theme),
2787
+ // matching the host's cockpit cards (work-shell v2 visual pass).
2788
+ "relative w-full text-left rounded-xl border bg-card bg-gradient-to-b from-foreground/[0.03] to-transparent p-3 shadow-sm motion-safe:transition-shadow",
2789
+ "hover:shadow-md hover:border-primary/40",
2790
+ "focus:outline-none focus:ring-2 focus:ring-ring/50",
2791
+ draggable && "cursor-grab active:cursor-grabbing",
2792
+ // The working card carries the SINGLE focal motion on the board (F11).
2793
+ isWorking ? "border-info/60 ring-2 ring-info/40 animate-pulse" : selected ? "border-primary/60 ring-2 ring-primary/30" : "border-border",
2706
2794
  className
2707
2795
  ),
2708
2796
  children: [
2709
- isWorking && /* @__PURE__ */ jsxRuntime.jsxs(
2710
- "span",
2711
- {
2712
- "aria-hidden": true,
2713
- className: "pointer-events-none absolute -top-1 -right-1 flex h-3 w-3",
2714
- children: [
2715
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-amber-400 opacity-75" }),
2716
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-3 w-3 rounded-full bg-amber-500" })
2717
- ]
2718
- }
2719
- ),
2720
2797
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2721
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100 line-clamp-2", children: card.title }),
2722
- isWorking ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 inline-flex items-center gap-1 rounded-full bg-amber-100 dark:bg-amber-900/40 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-amber-700 dark:text-amber-300", children: [
2723
- /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}),
2724
- "Working"
2725
- ] }) : tone && /* @__PURE__ */ jsxRuntime.jsx(
2798
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-foreground line-clamp-2", children: card.title }),
2799
+ isWorking ? /* @__PURE__ */ jsxRuntime.jsxs(
2726
2800
  "span",
2727
2801
  {
2728
2802
  className: cn9(
2729
- "shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2730
- tone.className
2803
+ "shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2804
+ badgeVariantClass("info")
2731
2805
  ),
2732
- children: tone.label
2806
+ children: [
2807
+ /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}),
2808
+ "Working"
2809
+ ]
2810
+ }
2811
+ ) : chip && /* @__PURE__ */ jsxRuntime.jsxs(
2812
+ "span",
2813
+ {
2814
+ className: cn9(
2815
+ "shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2816
+ badgeVariantClass(chip.variant)
2817
+ ),
2818
+ children: [
2819
+ chip.icon === "clock" && /* @__PURE__ */ jsxRuntime.jsx(ClockIcon2, {}),
2820
+ chip.label
2821
+ ]
2733
2822
  }
2734
2823
  )
2735
2824
  ] }),
2736
- card.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400 line-clamp-2", children: card.description }),
2737
- card.type && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block rounded bg-gray-100 dark:bg-gray-800 px-1.5 py-0.5 text-[10px] font-medium text-gray-600 dark:text-gray-400", children: card.type }) })
2825
+ card.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: card.description }),
2826
+ card.type && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: card.type }) })
2738
2827
  ]
2739
2828
  }
2740
2829
  );
2741
2830
  }
2831
+ function CapMeter({ count, cap, className }) {
2832
+ const pct = cap > 0 ? Math.min(100, Math.round(count / cap * 100)) : 0;
2833
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2834
+ "div",
2835
+ {
2836
+ className: cn9(
2837
+ "mt-2 flex items-center gap-2 text-[11px] text-muted-foreground",
2838
+ className
2839
+ ),
2840
+ children: [
2841
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums font-medium", children: [
2842
+ count,
2843
+ "/",
2844
+ cap
2845
+ ] }),
2846
+ /* @__PURE__ */ jsxRuntime.jsx(
2847
+ "span",
2848
+ {
2849
+ role: "progressbar",
2850
+ "aria-valuemin": 0,
2851
+ "aria-valuemax": cap,
2852
+ "aria-valuenow": count,
2853
+ "aria-label": `${count} of ${cap} used`,
2854
+ className: "h-1 flex-1 overflow-hidden rounded-full bg-secondary",
2855
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2856
+ "span",
2857
+ {
2858
+ className: "block h-full rounded-full bg-primary",
2859
+ style: { width: `${pct}%` }
2860
+ }
2861
+ )
2862
+ }
2863
+ )
2864
+ ]
2865
+ }
2866
+ );
2867
+ }
2868
+ var AGENT_GROUPS = [
2869
+ { kind: "cadra", label: "Cadra agents" },
2870
+ { kind: "remote", label: "Your own agents" },
2871
+ { kind: "human", label: "Teammates" }
2872
+ ];
2873
+ function AgentKindIcon({ kind }) {
2874
+ const common = {
2875
+ viewBox: "0 0 24 24",
2876
+ fill: "none",
2877
+ stroke: "currentColor",
2878
+ strokeWidth: 2,
2879
+ strokeLinecap: "round",
2880
+ strokeLinejoin: "round",
2881
+ className: "h-3.5 w-3.5",
2882
+ "aria-hidden": true
2883
+ };
2884
+ if (kind === "remote") {
2885
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...common, children: [
2886
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
2887
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 21h8M12 17v4" })
2888
+ ] });
2889
+ }
2890
+ if (kind === "human") {
2891
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...common, children: [
2892
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
2893
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "7", r: "4" })
2894
+ ] });
2895
+ }
2896
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...common, children: [
2897
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "11", width: "18", height: "10", rx: "2" }),
2898
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "5", r: "2" }),
2899
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v4M8 16h.01M16 16h.01" })
2900
+ ] });
2901
+ }
2902
+ function OnlineDot({ online }) {
2903
+ if (online === void 0) return null;
2904
+ return /* @__PURE__ */ jsxRuntime.jsx(
2905
+ "span",
2906
+ {
2907
+ "aria-label": online ? "connected" : "offline",
2908
+ className: cn9(
2909
+ "h-1.5 w-1.5 shrink-0 rounded-full",
2910
+ online ? "bg-success" : "bg-muted-foreground"
2911
+ )
2912
+ }
2913
+ );
2914
+ }
2915
+ function RemoteBadge() {
2916
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded-full border border-accent/40 bg-accent/15 px-1.5 py-px text-[10px] font-semibold leading-4 text-accent", children: "Remote" });
2917
+ }
2918
+ function LaneAgentPicker({
2919
+ selected,
2920
+ options,
2921
+ onSelect,
2922
+ canEdit = true,
2923
+ lockedReason,
2924
+ onEditAgent,
2925
+ className
2926
+ }) {
2927
+ const [open, setOpen] = react.useState(false);
2928
+ const [placement, setPlacement] = react.useState(null);
2929
+ const rootRef = react.useRef(null);
2930
+ const triggerRef = react.useRef(null);
2931
+ const editable = canEdit && !!onSelect && (options?.length ?? 0) > 0;
2932
+ const openMenu = () => {
2933
+ const el = triggerRef.current;
2934
+ if (el && typeof window !== "undefined") {
2935
+ const rect = el.getBoundingClientRect();
2936
+ const vw = window.innerWidth || 0;
2937
+ const vh = window.innerHeight || 0;
2938
+ const margin = 8;
2939
+ const gap = 4;
2940
+ const desired = 288;
2941
+ const width = Math.max(
2942
+ Math.min(rect.width || 224, Math.max(vw - margin * 2, 160)),
2943
+ 160
2944
+ );
2945
+ const left = Math.min(
2946
+ Math.max(rect.left, margin),
2947
+ Math.max(vw - width - margin, margin)
2948
+ );
2949
+ const spaceBelow = vh - rect.bottom - gap - margin;
2950
+ const spaceAbove = rect.top - gap - margin;
2951
+ const flipUp = spaceBelow < desired && spaceAbove > spaceBelow;
2952
+ const maxHeight = Math.max(
2953
+ Math.min(desired, flipUp ? spaceAbove : spaceBelow),
2954
+ 120
2955
+ );
2956
+ setPlacement(
2957
+ flipUp ? { bottom: Math.max(vh - rect.top + gap, margin), left, width, maxHeight } : { top: rect.bottom + gap, left, width, maxHeight }
2958
+ );
2959
+ } else {
2960
+ setPlacement(null);
2961
+ }
2962
+ setOpen(true);
2963
+ };
2964
+ react.useEffect(() => {
2965
+ if (!open) return;
2966
+ const onDocDown = (e) => {
2967
+ if (rootRef.current && !rootRef.current.contains(e.target)) {
2968
+ setOpen(false);
2969
+ }
2970
+ };
2971
+ const onKey = (e) => {
2972
+ if (e.key === "Escape") setOpen(false);
2973
+ };
2974
+ document.addEventListener("mousedown", onDocDown);
2975
+ document.addEventListener("touchstart", onDocDown);
2976
+ document.addEventListener("keydown", onKey);
2977
+ return () => {
2978
+ document.removeEventListener("mousedown", onDocDown);
2979
+ document.removeEventListener("touchstart", onDocDown);
2980
+ document.removeEventListener("keydown", onKey);
2981
+ };
2982
+ }, [open]);
2983
+ const kind = selected?.kind ?? "cadra";
2984
+ const displayLabel = selected?.label ?? "Unassigned";
2985
+ const face = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2986
+ /* @__PURE__ */ jsxRuntime.jsx(
2987
+ "span",
2988
+ {
2989
+ className: cn9(
2990
+ "grid h-5 w-5 shrink-0 place-items-center rounded-full",
2991
+ kind === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
2992
+ ),
2993
+ children: /* @__PURE__ */ jsxRuntime.jsx(AgentKindIcon, { kind })
2994
+ }
2995
+ ),
2996
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: displayLabel }),
2997
+ kind === "remote" && /* @__PURE__ */ jsxRuntime.jsx(RemoteBadge, {}),
2998
+ /* @__PURE__ */ jsxRuntime.jsx(OnlineDot, { online: selected?.online })
2999
+ ] });
3000
+ const grouped = AGENT_GROUPS.map((group) => ({
3001
+ ...group,
3002
+ items: (options ?? []).filter((opt) => (opt.kind ?? "cadra") === group.kind)
3003
+ })).filter((group) => group.items.length > 0);
3004
+ const menuStyle = placement ? {
3005
+ position: "fixed",
3006
+ top: placement.top,
3007
+ bottom: placement.bottom,
3008
+ left: placement.left,
3009
+ width: placement.width,
3010
+ maxHeight: placement.maxHeight
3011
+ } : void 0;
3012
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: cn9("relative mt-2 w-full", className), children: [
3013
+ editable ? /* @__PURE__ */ jsxRuntime.jsxs(
3014
+ "button",
3015
+ {
3016
+ ref: triggerRef,
3017
+ type: "button",
3018
+ onClick: () => open ? setOpen(false) : openMenu(),
3019
+ "aria-haspopup": "listbox",
3020
+ "aria-expanded": open,
3021
+ "aria-label": `Assigned agent: ${displayLabel}. Change`,
3022
+ className: cn9(
3023
+ "flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-card px-2",
3024
+ "text-xs font-semibold text-foreground",
3025
+ "hover:border-primary/50 focus:outline-none focus:ring-2 focus:ring-ring/50"
3026
+ ),
3027
+ children: [
3028
+ face,
3029
+ /* @__PURE__ */ jsxRuntime.jsx(
3030
+ "svg",
3031
+ {
3032
+ viewBox: "0 0 24 24",
3033
+ fill: "none",
3034
+ stroke: "currentColor",
3035
+ strokeWidth: 2,
3036
+ strokeLinecap: "round",
3037
+ strokeLinejoin: "round",
3038
+ "aria-hidden": true,
3039
+ className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
3040
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 9l6 6 6-6" })
3041
+ }
3042
+ )
3043
+ ]
3044
+ }
3045
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
3046
+ "div",
3047
+ {
3048
+ role: "group",
3049
+ "aria-label": `Assigned agent: ${displayLabel}`,
3050
+ className: cn9(
3051
+ "flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-muted/60 px-2",
3052
+ "text-xs font-semibold text-muted-foreground"
3053
+ ),
3054
+ children: [
3055
+ face,
3056
+ /* @__PURE__ */ jsxRuntime.jsxs(
3057
+ "svg",
3058
+ {
3059
+ viewBox: "0 0 24 24",
3060
+ fill: "none",
3061
+ stroke: "currentColor",
3062
+ strokeWidth: 2,
3063
+ strokeLinecap: "round",
3064
+ strokeLinejoin: "round",
3065
+ "aria-hidden": true,
3066
+ className: "h-3.5 w-3.5 shrink-0",
3067
+ children: [
3068
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
3069
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
3070
+ ]
3071
+ }
3072
+ )
3073
+ ]
3074
+ }
3075
+ ),
3076
+ !editable && lockedReason && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center gap-1 text-[11px] text-muted-foreground", children: [
3077
+ /* @__PURE__ */ jsxRuntime.jsxs(
3078
+ "svg",
3079
+ {
3080
+ viewBox: "0 0 24 24",
3081
+ fill: "none",
3082
+ stroke: "currentColor",
3083
+ strokeWidth: 2,
3084
+ strokeLinecap: "round",
3085
+ strokeLinejoin: "round",
3086
+ "aria-hidden": true,
3087
+ className: "h-3 w-3 shrink-0",
3088
+ children: [
3089
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
3090
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
3091
+ ]
3092
+ }
3093
+ ),
3094
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: lockedReason })
3095
+ ] }),
3096
+ editable && open && /* @__PURE__ */ jsxRuntime.jsxs(
3097
+ "div",
3098
+ {
3099
+ style: menuStyle,
3100
+ className: cn9(
3101
+ "flex flex-col overflow-hidden rounded-lg border border-border bg-popover shadow-lg",
3102
+ menuStyle ? "z-50" : "absolute z-20 mt-1 max-h-64 w-full"
3103
+ ),
3104
+ children: [
3105
+ /* @__PURE__ */ jsxRuntime.jsx(
3106
+ "ul",
3107
+ {
3108
+ role: "listbox",
3109
+ "aria-label": "Assign an agent",
3110
+ className: "min-h-0 flex-1 overflow-y-auto p-1",
3111
+ children: grouped.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("li", { role: "presentation", children: [
3112
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 pb-1 pt-2 text-[10px] font-semibold uppercase tracking-wide text-muted-foreground", children: group.label }),
3113
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { role: "group", "aria-label": group.label, children: group.items.map((opt) => {
3114
+ const isSelected = opt.id === selected?.id;
3115
+ const optKind = opt.kind ?? "cadra";
3116
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { role: "option", "aria-selected": isSelected, children: /* @__PURE__ */ jsxRuntime.jsxs(
3117
+ "button",
3118
+ {
3119
+ type: "button",
3120
+ onClick: () => {
3121
+ onSelect(opt.id);
3122
+ setOpen(false);
3123
+ },
3124
+ className: cn9(
3125
+ "flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium",
3126
+ "hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
3127
+ isSelected ? "text-primary" : "text-foreground"
3128
+ ),
3129
+ children: [
3130
+ /* @__PURE__ */ jsxRuntime.jsx(
3131
+ "span",
3132
+ {
3133
+ className: cn9(
3134
+ "grid h-5 w-5 shrink-0 place-items-center rounded-full",
3135
+ optKind === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
3136
+ ),
3137
+ children: /* @__PURE__ */ jsxRuntime.jsx(AgentKindIcon, { kind: optKind })
3138
+ }
3139
+ ),
3140
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: opt.label }),
3141
+ optKind === "remote" && /* @__PURE__ */ jsxRuntime.jsx(RemoteBadge, {}),
3142
+ /* @__PURE__ */ jsxRuntime.jsx(OnlineDot, { online: opt.online }),
3143
+ isSelected && /* @__PURE__ */ jsxRuntime.jsx(
3144
+ "svg",
3145
+ {
3146
+ viewBox: "0 0 24 24",
3147
+ fill: "none",
3148
+ stroke: "currentColor",
3149
+ strokeWidth: 2,
3150
+ strokeLinecap: "round",
3151
+ strokeLinejoin: "round",
3152
+ "aria-hidden": true,
3153
+ className: "h-3.5 w-3.5 shrink-0",
3154
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M20 6L9 17l-5-5" })
3155
+ }
3156
+ )
3157
+ ]
3158
+ }
3159
+ ) }, opt.id);
3160
+ }) })
3161
+ ] }, group.kind))
3162
+ }
3163
+ ),
3164
+ onEditAgent && selected && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-t border-border p-1", children: /* @__PURE__ */ jsxRuntime.jsxs(
3165
+ "button",
3166
+ {
3167
+ type: "button",
3168
+ onClick: () => {
3169
+ onEditAgent(selected.id);
3170
+ setOpen(false);
3171
+ },
3172
+ className: cn9(
3173
+ "flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium text-foreground",
3174
+ "hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50"
3175
+ ),
3176
+ children: [
3177
+ /* @__PURE__ */ jsxRuntime.jsx(
3178
+ "svg",
3179
+ {
3180
+ viewBox: "0 0 24 24",
3181
+ fill: "none",
3182
+ stroke: "currentColor",
3183
+ strokeWidth: 2,
3184
+ strokeLinecap: "round",
3185
+ strokeLinejoin: "round",
3186
+ "aria-hidden": true,
3187
+ className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
3188
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M17 3a2.85 2.83 0 1 1 4 4L7.5 20.5 2 22l1.5-5.5Z" })
3189
+ }
3190
+ ),
3191
+ "Edit this agent"
3192
+ ]
3193
+ }
3194
+ ) })
3195
+ ]
3196
+ }
3197
+ )
3198
+ ] });
3199
+ }
3200
+ function dueStateFor(card, lane, now) {
3201
+ const rs = card.run?.status;
3202
+ if (rs === "completed" && !lane.terminal) return "ready";
3203
+ if (card.nextDueAt) {
3204
+ const t = Date.parse(card.nextDueAt);
3205
+ if (!Number.isNaN(t) && t <= now) return "due";
3206
+ }
3207
+ if (lane.trigger === "scheduled" && !rs) return "waiting";
3208
+ return void 0;
3209
+ }
2742
3210
  function KanbanColumn({
2743
3211
  lane,
2744
3212
  cards,
@@ -2746,29 +3214,100 @@ function KanbanColumn({
2746
3214
  selectedCardId,
2747
3215
  onCreateCard,
2748
3216
  showCreate,
3217
+ count,
3218
+ agentPicker,
3219
+ canEditHandlers,
3220
+ draggableCards,
3221
+ dragActive,
3222
+ dropAllowed,
3223
+ onCardDragStart,
3224
+ onCardDragEnd,
3225
+ onLaneDrop,
2749
3226
  className
2750
3227
  }) {
3228
+ const headerCount = count ?? cards.length;
3229
+ const isScheduled = lane.trigger === "scheduled";
3230
+ const showCapMeter = count !== void 0 && typeof lane.cap === "number" && lane.cap > 0;
3231
+ const showAgentBadge = lane.handler?.type === "cadra_agent" && !agentPicker;
3232
+ const now = Date.now();
2751
3233
  return /* @__PURE__ */ jsxRuntime.jsxs(
2752
3234
  "div",
2753
3235
  {
3236
+ onDragOver: dropAllowed ? (e) => {
3237
+ e.preventDefault();
3238
+ e.dataTransfer.dropEffect = "move";
3239
+ } : void 0,
3240
+ onDrop: dropAllowed && onLaneDrop ? (e) => {
3241
+ e.preventDefault();
3242
+ onLaneDrop(lane);
3243
+ } : void 0,
2754
3244
  className: cn9(
2755
- "flex w-[85vw] max-w-sm snap-start shrink-0 flex-col rounded-xl bg-gray-50 dark:bg-gray-900/50 border border-gray-200 dark:border-gray-800 sm:w-72 sm:max-w-none",
3245
+ "flex w-[85vw] max-w-sm snap-start shrink-0 flex-col rounded-xl bg-muted/40 border border-border transition sm:w-72 sm:max-w-none",
3246
+ isScheduled && "border-border",
3247
+ // MV4 drag affordance: allowed targets glow; other lanes dim so the
3248
+ // valid drop zones read at a glance.
3249
+ dragActive && dropAllowed && "border-primary/60 ring-2 ring-primary/40 bg-primary/5",
3250
+ dragActive && !dropAllowed && "opacity-55",
2756
3251
  className
2757
3252
  ),
2758
3253
  children: [
2759
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5 border-b border-gray-200 dark:border-gray-800", children: [
2760
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
2761
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 truncate", children: lane.label }),
2762
- lane.handler?.type === "cadra_agent" && /* @__PURE__ */ jsxRuntime.jsx(
2763
- "span",
2764
- {
2765
- title: lane.handler.agentRef ? `Agent: ${lane.handler.agentRef}` : "Agent lane",
2766
- className: "rounded bg-violet-100 dark:bg-violet-950/40 px-1.5 py-0.5 text-[10px] font-medium text-violet-700 dark:text-violet-300",
2767
- children: "agent"
2768
- }
2769
- )
3254
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border px-3 py-2.5", children: [
3255
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3256
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
3257
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-foreground truncate", children: lane.label }),
3258
+ showAgentBadge && /* @__PURE__ */ jsxRuntime.jsx(
3259
+ "span",
3260
+ {
3261
+ title: lane.handler?.agentRef ? `Agent: ${lane.handler.agentRef}` : "Agent lane",
3262
+ className: cn9(
3263
+ // A static lane marker — NOT a run state. Uses neutral tokens,
3264
+ // never `info` (which means "running" in the F3 vocabulary).
3265
+ "rounded px-1.5 py-0.5 text-[10px] font-medium",
3266
+ badgeVariantClass("neutral")
3267
+ ),
3268
+ children: "agent"
3269
+ }
3270
+ )
3271
+ ] }),
3272
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded-full bg-secondary px-2 py-0.5 text-[11px] font-medium text-muted-foreground tabular-nums", children: headerCount })
2770
3273
  ] }),
2771
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 rounded-full bg-gray-200 dark:bg-gray-800 px-2 py-0.5 text-[11px] font-medium text-gray-600 dark:text-gray-400", children: cards.length })
3274
+ isScheduled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex items-center gap-2 flex-wrap", children: /* @__PURE__ */ jsxRuntime.jsxs(
3275
+ "span",
3276
+ {
3277
+ className: cn9(
3278
+ "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-semibold",
3279
+ badgeVariantClass("neutral")
3280
+ ),
3281
+ children: [
3282
+ /* @__PURE__ */ jsxRuntime.jsxs(
3283
+ "svg",
3284
+ {
3285
+ viewBox: "0 0 24 24",
3286
+ fill: "none",
3287
+ stroke: "currentColor",
3288
+ strokeWidth: "2",
3289
+ strokeLinecap: "round",
3290
+ strokeLinejoin: "round",
3291
+ "aria-hidden": true,
3292
+ className: "h-3 w-3",
3293
+ children: [
3294
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
3295
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
3296
+ ]
3297
+ }
3298
+ ),
3299
+ "runs on schedule"
3300
+ ]
3301
+ }
3302
+ ) }),
3303
+ showCapMeter && /* @__PURE__ */ jsxRuntime.jsx(CapMeter, { count: headerCount, cap: lane.cap }),
3304
+ agentPicker && /* @__PURE__ */ jsxRuntime.jsx(
3305
+ LaneAgentPicker,
3306
+ {
3307
+ ...agentPicker,
3308
+ canEdit: agentPicker.canEdit ?? canEditHandlers
3309
+ }
3310
+ )
2772
3311
  ] }),
2773
3312
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-2 overflow-y-auto p-2", children: [
2774
3313
  cards.map((card) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -2776,21 +3315,26 @@ function KanbanColumn({
2776
3315
  {
2777
3316
  card,
2778
3317
  onClick: onCardClick,
2779
- selected: card.id === selectedCardId
3318
+ selected: card.id === selectedCardId,
3319
+ dueState: dueStateFor(card, lane, now),
3320
+ draggable: draggableCards,
3321
+ onDragStart: onCardDragStart,
3322
+ onDragEnd: onCardDragEnd
2780
3323
  },
2781
3324
  card.id
2782
3325
  )),
2783
- cards.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-1 py-4 text-center text-xs text-gray-400 dark:text-gray-600", children: "No cards" }),
3326
+ cards.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-1 py-4 text-center text-xs text-muted-foreground", children: "No cards" }),
2784
3327
  showCreate && onCreateCard && /* @__PURE__ */ jsxRuntime.jsx(
2785
3328
  "button",
2786
3329
  {
2787
3330
  type: "button",
2788
3331
  onClick: () => onCreateCard(lane),
2789
3332
  className: cn9(
2790
- "mt-1 w-full rounded-lg border border-dashed border-gray-300 dark:border-gray-700 px-3 py-2",
2791
- "text-sm font-medium text-gray-500 dark:text-gray-400",
2792
- "hover:border-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition",
2793
- "focus:outline-none focus:ring-2 focus:ring-blue-500/40"
3333
+ // ≥44px touch (M12/F5) — density kept via padding, not height.
3334
+ "mt-1 inline-flex min-h-11 w-full items-center justify-center rounded-lg border border-dashed border-border px-3 py-2",
3335
+ "text-sm font-medium text-muted-foreground",
3336
+ "hover:border-primary/50 hover:text-foreground transition",
3337
+ "focus:outline-none focus:ring-2 focus:ring-ring/40"
2794
3338
  ),
2795
3339
  children: "+ New card"
2796
3340
  }
@@ -2805,14 +3349,14 @@ function SkeletonCard() {
2805
3349
  "div",
2806
3350
  {
2807
3351
  className: cn9(
2808
- "w-full rounded-lg border border-gray-200 dark:border-gray-700",
2809
- "bg-white dark:bg-gray-900 p-3 shadow-sm"
3352
+ "w-full rounded-lg border border-border",
3353
+ "bg-card p-3 shadow-sm"
2810
3354
  ),
2811
3355
  "aria-hidden": "true",
2812
3356
  children: [
2813
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-3/4 rounded bg-gray-200 dark:bg-gray-800" }),
2814
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 h-2.5 w-1/2 rounded bg-gray-200 dark:bg-gray-800" }),
2815
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 h-4 w-1/3 rounded bg-gray-200 dark:bg-gray-800" })
3357
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-3/4 rounded bg-muted" }),
3358
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 h-2.5 w-1/2 rounded bg-muted" }),
3359
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 h-4 w-1/3 rounded bg-muted" })
2816
3360
  ]
2817
3361
  }
2818
3362
  );
@@ -2823,14 +3367,14 @@ function SkeletonColumn({ cardCount }) {
2823
3367
  {
2824
3368
  className: cn9(
2825
3369
  "flex w-72 shrink-0 flex-col rounded-xl",
2826
- "bg-gray-50 dark:bg-gray-900/50",
2827
- "border border-gray-200 dark:border-gray-800"
3370
+ "bg-muted/40",
3371
+ "border border-border"
2828
3372
  ),
2829
3373
  "aria-hidden": "true",
2830
3374
  children: [
2831
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5 border-b border-gray-200 dark:border-gray-800", children: [
2832
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-24 rounded bg-gray-200 dark:bg-gray-800" }),
2833
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-6 rounded-full bg-gray-200 dark:bg-gray-800" })
3375
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5 border-b border-border", children: [
3376
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-24 rounded bg-muted" }),
3377
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-6 rounded-full bg-muted" })
2834
3378
  ] }),
2835
3379
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col gap-2 p-2", children: Array.from({ length: cardCount }).map((_, i) => (
2836
3380
  // eslint-disable-next-line react/no-array-index-key
@@ -2859,6 +3403,67 @@ function KanbanBoardSkeleton({
2859
3403
  }
2860
3404
  );
2861
3405
  }
3406
+ function formatCountdown(nextTickAt) {
3407
+ if (!nextTickAt) return null;
3408
+ const target = Date.parse(nextTickAt);
3409
+ if (Number.isNaN(target)) return null;
3410
+ const ms = target - Date.now();
3411
+ if (ms <= 0) return "any moment";
3412
+ const mins = Math.round(ms / 6e4);
3413
+ if (mins < 1) return "under a minute";
3414
+ if (mins < 60) return `${mins}m`;
3415
+ const hrs = Math.floor(mins / 60);
3416
+ const rem = mins % 60;
3417
+ return rem > 0 ? `${hrs}h ${rem}m` : `${hrs}h`;
3418
+ }
3419
+ var DOT_TONE = {
3420
+ "on-schedule": "bg-success",
3421
+ due: "bg-info",
3422
+ paused: "bg-muted-foreground"
3423
+ };
3424
+ var DEFAULT_LABEL = {
3425
+ "on-schedule": "On schedule",
3426
+ due: "Running now",
3427
+ paused: "Paused"
3428
+ };
3429
+ function ScheduleStatus({
3430
+ nextTickAt,
3431
+ dueByLane,
3432
+ label,
3433
+ tone = "on-schedule",
3434
+ className
3435
+ }) {
3436
+ const countdown = formatCountdown(nextTickAt);
3437
+ const totalDue = dueByLane ? Object.values(dueByLane).reduce((a, b) => a + b, 0) : 0;
3438
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3439
+ "div",
3440
+ {
3441
+ className: cn9(
3442
+ "flex items-center gap-2 text-xs text-muted-foreground",
3443
+ className
3444
+ ),
3445
+ children: [
3446
+ /* @__PURE__ */ jsxRuntime.jsx(
3447
+ "span",
3448
+ {
3449
+ "aria-hidden": true,
3450
+ className: cn9("h-2 w-2 shrink-0 rounded-full", DOT_TONE[tone])
3451
+ }
3452
+ ),
3453
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: label ?? DEFAULT_LABEL[tone] }),
3454
+ countdown && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums", children: [
3455
+ "\xB7 next run in ",
3456
+ countdown
3457
+ ] }),
3458
+ totalDue > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums", children: [
3459
+ "\xB7 ",
3460
+ totalDue,
3461
+ " due"
3462
+ ] })
3463
+ ]
3464
+ }
3465
+ );
3466
+ }
2862
3467
  function KanbanBoard({
2863
3468
  adapter,
2864
3469
  onCreateCard,
@@ -2869,6 +3474,13 @@ function KanbanBoard({
2869
3474
  enableEdit = true,
2870
3475
  enableAdvance = true,
2871
3476
  pollIntervalMs,
3477
+ laneCounts,
3478
+ canEditHandlers,
3479
+ laneAgentPicker,
3480
+ scheduleStatus,
3481
+ focusCardId,
3482
+ renderCardDrawer,
3483
+ enableDrag,
2872
3484
  className
2873
3485
  }) {
2874
3486
  const {
@@ -2883,8 +3495,19 @@ function KanbanBoard({
2883
3495
  createCard,
2884
3496
  updateCard,
2885
3497
  transitionCard,
3498
+ deleteCard,
2886
3499
  handleRunTerminal
2887
3500
  } = useKanbanBoard({ adapter, onError, onCardCreated, pollIntervalMs });
3501
+ const lastFocusedRef = react.useRef(void 0);
3502
+ react.useEffect(() => {
3503
+ if (!focusCardId) {
3504
+ lastFocusedRef.current = void 0;
3505
+ return;
3506
+ }
3507
+ if (lastFocusedRef.current === focusCardId) return;
3508
+ lastFocusedRef.current = focusCardId;
3509
+ void openCard(focusCardId);
3510
+ }, [focusCardId]);
2888
3511
  const handleCardClick = react.useCallback(
2889
3512
  (card) => {
2890
3513
  void openCard(card.id);
@@ -2917,14 +3540,53 @@ function KanbanBoard({
2917
3540
  (cardId, toLane) => transitionCard({ cardId, targetLane: toLane }),
2918
3541
  [transitionCard]
2919
3542
  );
3543
+ const [dragging, setDragging] = react.useState(null);
3544
+ const draggingRef = react.useRef(null);
3545
+ const dropInFlightRef = react.useRef(null);
3546
+ const allowedDropLanes = react.useMemo(() => {
3547
+ if (!dragging || !board?.transitions) return /* @__PURE__ */ new Set();
3548
+ return new Set(
3549
+ board.transitions.filter((t) => t.fromLane === dragging.lane && t.manualAllowed).map((t) => t.toLane)
3550
+ );
3551
+ }, [dragging, board?.transitions]);
3552
+ const handleCardDragStart = react.useCallback((card) => {
3553
+ const d = { id: card.id, lane: card.lane };
3554
+ draggingRef.current = d;
3555
+ setDragging(d);
3556
+ }, []);
3557
+ const handleCardDragEnd = react.useCallback(() => {
3558
+ draggingRef.current = null;
3559
+ setDragging(null);
3560
+ }, []);
3561
+ const handleLaneDrop = react.useCallback(
3562
+ (lane) => {
3563
+ const cur = draggingRef.current;
3564
+ draggingRef.current = null;
3565
+ setDragging(null);
3566
+ if (!cur || lane.key === cur.lane) return;
3567
+ if (dropInFlightRef.current === cur.id) return;
3568
+ const allowed = (board?.transitions ?? []).some(
3569
+ (t) => t.fromLane === cur.lane && t.toLane === lane.key && t.manualAllowed
3570
+ );
3571
+ if (!allowed) return;
3572
+ dropInFlightRef.current = cur.id;
3573
+ void Promise.resolve(transitionCard({ cardId: cur.id, targetLane: lane.key })).finally(
3574
+ () => {
3575
+ if (dropInFlightRef.current === cur.id) dropInFlightRef.current = null;
3576
+ }
3577
+ );
3578
+ },
3579
+ [board?.transitions, transitionCard]
3580
+ );
2920
3581
  const initialLaneKey = lanes.find((l) => !l.terminal)?.key ?? lanes[0]?.key;
2921
3582
  const selectedLane = selectedCard ? lanes.find((l) => l.key === selectedCard.lane) : void 0;
2922
3583
  const canAdvanceSelected = !!selectedLane && !selectedLane.terminal;
2923
3584
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn9("flex h-full w-full flex-col", className), children: [
2924
- error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "m-3 rounded-md border border-red-200 dark:border-red-900 bg-red-50 dark:bg-red-950/30 px-3 py-2 text-sm text-red-700 dark:text-red-300", children: error }),
3585
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "m-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
3586
+ scheduleStatus && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 border-b border-border px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScheduleStatus, { ...scheduleStatus }) }),
2925
3587
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
2926
3588
  lanes.length === 0 && isLoading ? /* @__PURE__ */ jsxRuntime.jsx(KanbanBoardSkeleton, { className: "flex-1 overflow-x-auto p-4" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 items-start gap-4 overflow-x-auto p-4 snap-x snap-mandatory no-scrollbar sm:snap-none", children: [
2927
- lanes.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-gray-400 dark:text-gray-600", children: "No lanes configured." }),
3589
+ lanes.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "No lanes configured." }),
2928
3590
  lanes.map((lane) => /* @__PURE__ */ jsxRuntime.jsx(
2929
3591
  KanbanColumn,
2930
3592
  {
@@ -2933,12 +3595,35 @@ function KanbanBoard({
2933
3595
  onCardClick: handleCardClick,
2934
3596
  selectedCardId: selectedCard?.id,
2935
3597
  onCreateCard: handleCreate,
2936
- showCreate: enableCreate && lane.key === initialLaneKey
3598
+ showCreate: enableCreate && lane.key === initialLaneKey,
3599
+ count: laneCounts?.[lane.key],
3600
+ agentPicker: laneAgentPicker?.(lane),
3601
+ canEditHandlers,
3602
+ draggableCards: enableDrag && enableAdvance,
3603
+ dragActive: !!dragging,
3604
+ dropAllowed: !!dragging && dragging.lane !== lane.key && allowedDropLanes.has(lane.key),
3605
+ onCardDragStart: handleCardDragStart,
3606
+ onCardDragEnd: handleCardDragEnd,
3607
+ onLaneDrop: handleLaneDrop
2937
3608
  },
2938
3609
  lane.id ?? lane.key
2939
3610
  ))
2940
3611
  ] }),
2941
- selectedCard && /* @__PURE__ */ jsxRuntime.jsx(
3612
+ selectedCard && (renderCardDrawer ? renderCardDrawer({
3613
+ card: selectedCard,
3614
+ adapter,
3615
+ lanes,
3616
+ transitions: enableAdvance ? board?.transitions : void 0,
3617
+ canAdvance: canAdvanceSelected,
3618
+ onClose: closeCard,
3619
+ onRetry: onRetryCard ? handleRetry : void 0,
3620
+ onUpdate: enableEdit ? handleUpdate : void 0,
3621
+ onAdvance: enableAdvance ? handleAdvance : void 0,
3622
+ onTransition: enableAdvance ? handleTransition : void 0,
3623
+ onDelete: adapter.deleteCard ? deleteCard : void 0,
3624
+ onRunTerminal: handleRunTerminal,
3625
+ onError
3626
+ }) : /* @__PURE__ */ jsxRuntime.jsx(
2942
3627
  CardDrawer,
2943
3628
  {
2944
3629
  card: selectedCard,
@@ -2954,7 +3639,7 @@ function KanbanBoard({
2954
3639
  onRunTerminal: handleRunTerminal,
2955
3640
  onError
2956
3641
  }
2957
- )
3642
+ ))
2958
3643
  ] })
2959
3644
  ] });
2960
3645
  }
@@ -2979,6 +3664,7 @@ function composeAdapter(source, boardConfigUuid, version, cardSource) {
2979
3664
  transitionCard(input) {
2980
3665
  return cardSource.transitionCard(input);
2981
3666
  },
3667
+ ...cardSource.deleteCard ? { deleteCard: (id) => cardSource.deleteCard(id) } : {},
2982
3668
  ...cardSource.createRunStream ? { createRunStream: (id) => cardSource.createRunStream(id) } : {}
2983
3669
  };
2984
3670
  }
@@ -3675,15 +4361,6 @@ var ExecutionStreamControllerImpl = class {
3675
4361
  sseClient = null;
3676
4362
  status = "connecting";
3677
4363
  reconnectAttempts = 0;
3678
- // CAD-39 / webchat dual-plane create-connect race. `hasConnected` flips true on
3679
- // the first successful SSE open; `notFoundRetries` bounds retries of a 404
3680
- // encountered BEFORE we ever connect (the run row is persisted slightly AFTER
3681
- // the client opens the stream — the norm on the webchat path where msg-api owns
3682
- // the executionId and cadra persists the agent_executions row via an async
3683
- // worker callback). See handleConnectionError.
3684
- hasConnected = false;
3685
- notFoundRetries = 0;
3686
- maxConnectRaceRetries = 3;
3687
4364
  seenEventIds = /* @__PURE__ */ new Set();
3688
4365
  _lastEventId = null;
3689
4366
  activityTimeoutId = null;
@@ -3823,8 +4500,6 @@ var ExecutionStreamControllerImpl = class {
3823
4500
  if (connected) {
3824
4501
  this.status = "connected";
3825
4502
  this.reconnectAttempts = 0;
3826
- this.hasConnected = true;
3827
- this.notFoundRetries = 0;
3828
4503
  this.resetActivityTimeout();
3829
4504
  }
3830
4505
  this.emit("connectionChange", { connected, usingFallback: false });
@@ -4114,38 +4789,6 @@ var ExecutionStreamControllerImpl = class {
4114
4789
  return;
4115
4790
  }
4116
4791
  const msg = error instanceof Error ? error.message : String(error);
4117
- const isNotFound = /\b404\b/.test(msg);
4118
- const isAuthFailure = /\b40[13]\b/.test(msg) || /INVALID_API_KEY|INVALID_REQUEST/.test(msg);
4119
- if (isNotFound && !isAuthFailure && !this.hasConnected) {
4120
- if (this.notFoundRetries < this.maxConnectRaceRetries) {
4121
- this.notFoundRetries++;
4122
- this.status = "reconnecting";
4123
- const delay = Math.min(
4124
- this.config.reconnectBaseDelayMs * this.notFoundRetries,
4125
- this.config.reconnectMaxDelayMs
4126
- );
4127
- await new Promise((resolve) => setTimeout(resolve, delay));
4128
- if (this.status === "closed") {
4129
- return;
4130
- }
4131
- try {
4132
- await this.connectSSE();
4133
- } catch (retryError) {
4134
- await this.handleConnectionError(retryError);
4135
- }
4136
- return;
4137
- }
4138
- if (this.pollingEnabled) {
4139
- if (this.sseClient) {
4140
- this.sseClient.disconnect();
4141
- this.sseClient = null;
4142
- }
4143
- this.status = "fallback_polling";
4144
- this.emit("connectionChange", { connected: false, usingFallback: true });
4145
- this.startPolling();
4146
- return;
4147
- }
4148
- }
4149
4792
  if (/\b4\d\d\b/.test(msg) || /INVALID_API_KEY|INVALID_REQUEST/.test(msg)) {
4150
4793
  if (this.sseClient) {
4151
4794
  this.sseClient.disconnect();
@@ -6196,12 +6839,18 @@ function createDefaultKanbanAdapter(config) {
6196
6839
  exports.AgentBoard = AgentBoard;
6197
6840
  exports.BoardConfigClient = BoardConfigClient;
6198
6841
  exports.CadraLifecycleSource = CadraLifecycleSource;
6842
+ exports.CapMeter = CapMeter;
6199
6843
  exports.CardDrawer = CardDrawer;
6200
6844
  exports.KanbanBoard = KanbanBoard;
6201
6845
  exports.KanbanCardTile = KanbanCard;
6202
6846
  exports.KanbanColumn = KanbanColumn;
6847
+ exports.LaneAgentPicker = LaneAgentPicker;
6848
+ exports.ScheduleStatus = ScheduleStatus;
6849
+ exports.badgeVariantClass = badgeVariantClass;
6850
+ exports.cn = cn9;
6203
6851
  exports.createDefaultKanbanAdapter = createDefaultKanbanAdapter;
6204
6852
  exports.mapDefinitionToBoard = mapDefinitionToBoard;
6853
+ exports.runStatusTone = runStatusTone;
6205
6854
  exports.toResolvedLifecycle = toResolvedLifecycle;
6206
6855
  exports.useCardRun = useCardRun;
6207
6856
  exports.useKanbanBoard = useKanbanBoard;