@cadraos/sdk 0.3.7 → 0.3.9

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.
@@ -16,6 +16,10 @@ function mapHandler(h) {
16
16
  type: h.type,
17
17
  agentUuid: h.agent_uuid,
18
18
  teamUuid: h.team_uuid,
19
+ // FINDING-6: carry the remote_agent binding through the wire parse. The
20
+ // engine's remote_agent dispatch reads `remoteAgentUuid`; dropping it here
21
+ // made every published remote_agent lane dispatch-dead.
22
+ remoteAgentUuid: h.remote_agent_uuid,
19
23
  agentRef: h.agent_ref,
20
24
  webhook: h.webhook ? {
21
25
  url: h.webhook.url,
@@ -2293,35 +2297,40 @@ function useCardRun(options) {
2293
2297
  function cn9(...classes) {
2294
2298
  return classes.filter(Boolean).join(" ");
2295
2299
  }
2296
- function runStatusTone(status) {
2297
- switch (status) {
2298
- case "running":
2299
- return {
2300
- label: "Running",
2301
- className: "bg-blue-50 text-blue-700 dark:bg-blue-950/40 dark:text-blue-300"
2302
- };
2303
- case "completed":
2304
- return {
2305
- label: "Completed",
2306
- className: "bg-green-50 text-green-700 dark:bg-green-950/40 dark:text-green-300"
2307
- };
2308
- case "failed":
2309
- return {
2310
- label: "Failed",
2311
- className: "bg-red-50 text-red-700 dark:bg-red-950/40 dark:text-red-300"
2312
- };
2313
- case "cancelled":
2314
- return {
2315
- label: "Cancelled",
2316
- className: "bg-gray-100 text-gray-600 dark:bg-gray-800 dark:text-gray-400"
2317
- };
2300
+ function badgeVariantClass(variant) {
2301
+ switch (variant) {
2302
+ case "default":
2303
+ return "bg-primary text-primary-foreground";
2304
+ case "secondary":
2305
+ return "border border-border bg-secondary text-secondary-foreground";
2306
+ case "outline":
2307
+ return "border border-border text-foreground";
2308
+ case "info":
2309
+ return "border border-info/30 bg-info/15 text-info";
2310
+ case "success":
2311
+ return "border border-success/30 bg-success/15 text-success";
2312
+ case "warning":
2313
+ return "border border-warning/40 bg-warning/15 text-warning";
2314
+ case "destructive":
2315
+ return "border border-destructive/40 bg-destructive/15 text-destructive";
2316
+ case "neutral":
2318
2317
  default:
2319
- return {
2320
- label: "Pending",
2321
- className: "bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-300"
2322
- };
2318
+ return "border border-border bg-secondary text-muted-foreground";
2323
2319
  }
2324
2320
  }
2321
+ function runStatusTone(status) {
2322
+ const map = {
2323
+ running: { label: "Running", variant: "info" },
2324
+ completed: { label: "Completed", variant: "success" },
2325
+ failed: { label: "Failed", variant: "destructive" },
2326
+ cancelled: { label: "Cancelled", variant: "neutral" }
2327
+ };
2328
+ const { label, variant } = map[status ?? ""] ?? {
2329
+ label: "Pending",
2330
+ variant: "neutral"
2331
+ };
2332
+ return { label, variant, className: badgeVariantClass(variant) };
2333
+ }
2325
2334
  function CardDrawer({
2326
2335
  card,
2327
2336
  adapter,
@@ -2440,11 +2449,11 @@ function CardDrawer({
2440
2449
  role: "dialog",
2441
2450
  "aria-label": `Card: ${card.title}`,
2442
2451
  className: cn9(
2443
- "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",
2452
+ "flex h-full w-full sm:max-w-sm flex-col bg-card border-l border-border shadow-xl",
2444
2453
  className
2445
2454
  ),
2446
2455
  children: [
2447
- /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-gray-200 dark:border-gray-800 px-4 py-3", children: [
2456
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-border px-4 py-3", children: [
2448
2457
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
2449
2458
  isEditing ? /* @__PURE__ */ jsx(
2450
2459
  "input",
@@ -2454,14 +2463,14 @@ function CardDrawer({
2454
2463
  onChange: (e) => setEditTitle(e.target.value),
2455
2464
  "aria-label": "Card title",
2456
2465
  placeholder: "Card title",
2457
- 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"
2466
+ 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"
2458
2467
  }
2459
2468
  ) : /* @__PURE__ */ jsx(
2460
2469
  "h2",
2461
2470
  {
2462
2471
  className: cn9(
2463
- "text-base font-semibold text-gray-900 dark:text-gray-100 truncate",
2464
- onUpdate && "cursor-text rounded hover:bg-gray-100 dark:hover:bg-gray-800/60 -mx-1 px-1"
2472
+ "text-base font-semibold text-foreground truncate",
2473
+ onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
2465
2474
  ),
2466
2475
  onClick: onUpdate ? () => setIsEditing(true) : void 0,
2467
2476
  title: onUpdate ? "Click to edit" : void 0,
@@ -2469,7 +2478,7 @@ function CardDrawer({
2469
2478
  }
2470
2479
  ),
2471
2480
  /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2", children: [
2472
- /* @__PURE__ */ jsx("span", { className: "text-xs text-gray-500 dark:text-gray-400", children: card.lane }),
2481
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: card.lane }),
2473
2482
  /* @__PURE__ */ jsx(
2474
2483
  "span",
2475
2484
  {
@@ -2489,7 +2498,7 @@ function CardDrawer({
2489
2498
  type: "button",
2490
2499
  onClick: () => setIsEditing(true),
2491
2500
  "aria-label": "Edit card",
2492
- 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",
2501
+ className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
2493
2502
  children: /* @__PURE__ */ jsxs(
2494
2503
  "svg",
2495
2504
  {
@@ -2515,7 +2524,7 @@ function CardDrawer({
2515
2524
  type: "button",
2516
2525
  onClick: onClose,
2517
2526
  "aria-label": "Close",
2518
- 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",
2527
+ className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
2519
2528
  children: /* @__PURE__ */ jsxs(
2520
2529
  "svg",
2521
2530
  {
@@ -2537,8 +2546,8 @@ function CardDrawer({
2537
2546
  )
2538
2547
  ] })
2539
2548
  ] }),
2540
- isEditing ? /* @__PURE__ */ jsxs("div", { className: "border-b border-gray-200 dark:border-gray-800 px-4 py-3", children: [
2541
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-gray-500 dark:text-gray-400", children: "Description" }),
2549
+ isEditing ? /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-4 py-3", children: [
2550
+ /* @__PURE__ */ jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Description" }),
2542
2551
  /* @__PURE__ */ jsx(
2543
2552
  "textarea",
2544
2553
  {
@@ -2547,7 +2556,7 @@ function CardDrawer({
2547
2556
  "aria-label": "Card description",
2548
2557
  rows: 4,
2549
2558
  placeholder: "Add a description\u2026",
2550
- 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"
2559
+ 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"
2551
2560
  }
2552
2561
  ),
2553
2562
  /* @__PURE__ */ jsxs("div", { className: "mt-3 flex items-center justify-end gap-2", children: [
@@ -2557,7 +2566,7 @@ function CardDrawer({
2557
2566
  type: "button",
2558
2567
  onClick: handleCancel,
2559
2568
  disabled: isSaving,
2560
- 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",
2569
+ className: "rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground hover:bg-muted disabled:opacity-50",
2561
2570
  children: "Cancel"
2562
2571
  }
2563
2572
  ),
@@ -2567,26 +2576,27 @@ function CardDrawer({
2567
2576
  type: "button",
2568
2577
  onClick: () => void handleSave(),
2569
2578
  disabled: isSaving,
2570
- 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",
2579
+ 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",
2571
2580
  children: isSaving ? "Saving\u2026" : "Save"
2572
2581
  }
2573
2582
  )
2574
2583
  ] })
2575
- ] }) : (card.description || onUpdate) && /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200 dark:border-gray-800 px-4 py-3", children: /* @__PURE__ */ jsx(
2584
+ ] }) : (card.description || onUpdate) && /* @__PURE__ */ jsx("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsx(
2576
2585
  "p",
2577
2586
  {
2578
2587
  className: cn9(
2579
2588
  "text-sm whitespace-pre-wrap",
2580
- card.description ? "text-gray-600 dark:text-gray-300" : "italic text-gray-400 dark:text-gray-600",
2581
- onUpdate && "cursor-text rounded hover:bg-gray-100 dark:hover:bg-gray-800/60 -mx-1 px-1"
2589
+ card.description ? "text-muted-foreground" : "italic text-muted-foreground/70",
2590
+ onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
2582
2591
  ),
2583
2592
  onClick: onUpdate ? () => setIsEditing(true) : void 0,
2584
2593
  title: onUpdate ? "Click to edit" : void 0,
2585
2594
  children: card.description || "Add a description\u2026"
2586
2595
  }
2587
2596
  ) }),
2588
- showTransitionButtons ? /* @__PURE__ */ 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: [
2589
- isHumanGate && /* @__PURE__ */ jsx("span", { className: "mr-auto text-xs font-medium text-amber-600 dark:text-amber-400", children: "Awaiting your approval" }),
2597
+ showTransitionButtons ? /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 border-b border-border px-4 py-2", children: [
2598
+ isHumanGate && // Human gate = needs-you → warning (amber is reserved for this, F3).
2599
+ /* @__PURE__ */ jsx("span", { className: "mr-auto text-xs font-medium text-warning", children: "Awaiting your approval" }),
2590
2600
  manualEdges.map((t, i) => {
2591
2601
  const toLabel = laneByKey.get(t.toLane)?.label ?? t.toLane;
2592
2602
  const isPrimary = isHumanGate ? i === 0 : false;
@@ -2599,26 +2609,27 @@ function CardDrawer({
2599
2609
  onClick: () => void handleTransition(t.toLane),
2600
2610
  disabled: pendingLane !== null,
2601
2611
  className: cn9(
2602
- "rounded-md px-3 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 disabled:opacity-50",
2603
- 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"
2612
+ // ≥44px touch (M12/F5) — density kept via padding, not height.
2613
+ "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",
2614
+ isPrimary ? "border border-border bg-secondary text-secondary-foreground hover:bg-muted" : "border border-border text-foreground hover:bg-muted"
2604
2615
  ),
2605
2616
  children: busy ? "Moving\u2026" : label
2606
2617
  },
2607
2618
  t.id
2608
2619
  );
2609
2620
  })
2610
- ] }) : onAdvance && canAdvance && !isEditing && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end border-b border-gray-200 dark:border-gray-800 px-4 py-2", children: /* @__PURE__ */ jsx(
2621
+ ] }) : onAdvance && canAdvance && !isEditing && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end border-b border-border px-4 py-2", children: /* @__PURE__ */ jsx(
2611
2622
  "button",
2612
2623
  {
2613
2624
  type: "button",
2614
2625
  onClick: () => void handleAdvance(),
2615
2626
  disabled: isAdvancing,
2616
- 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",
2627
+ 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",
2617
2628
  children: isAdvancing ? "Advancing\u2026" : "Advance \u2192"
2618
2629
  }
2619
2630
  ) }),
2620
2631
  /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col overflow-hidden", children: [
2621
- !hasExecution ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-6 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400 dark:text-gray-600", children: "No run yet for this card." }) }) : /* @__PURE__ */ jsx(
2632
+ !hasExecution ? /* @__PURE__ */ jsx("div", { className: "flex flex-1 items-center justify-center p-6 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No run yet for this card." }) }) : /* @__PURE__ */ jsx(
2622
2633
  MessageList,
2623
2634
  {
2624
2635
  messages: run.messages,
@@ -2631,14 +2642,14 @@ function CardDrawer({
2631
2642
  className: "flex-1"
2632
2643
  }
2633
2644
  ),
2634
- failed && /* @__PURE__ */ 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: [
2635
- /* @__PURE__ */ jsx("span", { className: "text-sm text-red-700 dark:text-red-300", children: run.error ?? "Run failed." }),
2645
+ failed && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 border-t border-destructive/40 bg-destructive/10 px-4 py-3", children: [
2646
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-destructive", children: run.error ?? "Run failed." }),
2636
2647
  onRetry && /* @__PURE__ */ jsx(
2637
2648
  "button",
2638
2649
  {
2639
2650
  type: "button",
2640
2651
  onClick: () => onRetry(card),
2641
- 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",
2652
+ 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",
2642
2653
  children: "Retry"
2643
2654
  }
2644
2655
  )
@@ -2680,10 +2691,51 @@ function Spinner({ className }) {
2680
2691
  }
2681
2692
  );
2682
2693
  }
2683
- function KanbanCard({ card, onClick, selected, className }) {
2694
+ function ClockIcon2() {
2695
+ return /* @__PURE__ */ jsxs(
2696
+ "svg",
2697
+ {
2698
+ viewBox: "0 0 24 24",
2699
+ fill: "none",
2700
+ stroke: "currentColor",
2701
+ strokeWidth: "2",
2702
+ strokeLinecap: "round",
2703
+ strokeLinejoin: "round",
2704
+ "aria-hidden": true,
2705
+ className: "h-2.5 w-2.5",
2706
+ children: [
2707
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
2708
+ /* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
2709
+ ]
2710
+ }
2711
+ );
2712
+ }
2713
+ function KanbanCard({
2714
+ card,
2715
+ onClick,
2716
+ selected,
2717
+ dueState,
2718
+ className
2719
+ }) {
2684
2720
  const runStatus = card.run?.status;
2685
- const tone = runStatus ? runStatusTone(runStatus) : null;
2686
- const isWorking = runStatus === "running" || runStatus === "pending";
2721
+ const isWorking = runStatus === "running";
2722
+ const failed = runStatus === "failed";
2723
+ let chip = null;
2724
+ if (failed) {
2725
+ chip = { label: "Failed", variant: "destructive" };
2726
+ } else if (dueState === "ready") {
2727
+ chip = { label: "Ready", variant: "success" };
2728
+ } else if (runStatus === "completed") {
2729
+ const t = runStatusTone(runStatus);
2730
+ chip = { label: t.label, variant: t.variant };
2731
+ } else if (dueState === "due") {
2732
+ chip = { label: "Due", variant: "neutral", icon: "clock" };
2733
+ } else if (dueState === "waiting") {
2734
+ chip = { label: "Waiting", variant: "neutral" };
2735
+ } else if (runStatus) {
2736
+ const t = runStatusTone(runStatus);
2737
+ chip = { label: t.label, variant: t.variant };
2738
+ }
2687
2739
  return /* @__PURE__ */ jsxs(
2688
2740
  "button",
2689
2741
  {
@@ -2692,46 +2744,311 @@ function KanbanCard({ card, onClick, selected, className }) {
2692
2744
  "aria-pressed": selected,
2693
2745
  "aria-busy": isWorking,
2694
2746
  className: cn9(
2695
- "relative w-full text-left rounded-lg border bg-white dark:bg-gray-900 p-3 shadow-sm transition",
2696
- "hover:shadow-md hover:border-gray-300 dark:hover:border-gray-600",
2697
- "focus:outline-none focus:ring-2 focus:ring-blue-500/40",
2698
- 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",
2747
+ "relative w-full text-left rounded-lg border bg-card p-3 shadow-sm transition",
2748
+ "hover:shadow-md hover:border-primary/40",
2749
+ "focus:outline-none focus:ring-2 focus:ring-ring/50",
2750
+ // The working card carries the SINGLE focal motion on the board (F11).
2751
+ isWorking ? "border-info/60 ring-2 ring-info/40 animate-pulse" : selected ? "border-primary/60 ring-2 ring-primary/30" : "border-border",
2699
2752
  className
2700
2753
  ),
2701
2754
  children: [
2702
- isWorking && /* @__PURE__ */ jsxs(
2703
- "span",
2704
- {
2705
- "aria-hidden": true,
2706
- className: "pointer-events-none absolute -top-1 -right-1 flex h-3 w-3",
2707
- children: [
2708
- /* @__PURE__ */ jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-amber-400 opacity-75" }),
2709
- /* @__PURE__ */ jsx("span", { className: "relative inline-flex h-3 w-3 rounded-full bg-amber-500" })
2710
- ]
2711
- }
2712
- ),
2713
2755
  /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-2", children: [
2714
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-gray-900 dark:text-gray-100 line-clamp-2", children: card.title }),
2715
- isWorking ? /* @__PURE__ */ 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: [
2716
- /* @__PURE__ */ jsx(Spinner, {}),
2717
- "Working"
2718
- ] }) : tone && /* @__PURE__ */ jsx(
2756
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground line-clamp-2", children: card.title }),
2757
+ isWorking ? /* @__PURE__ */ jsxs(
2719
2758
  "span",
2720
2759
  {
2721
2760
  className: cn9(
2722
- "shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2723
- tone.className
2761
+ "shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2762
+ badgeVariantClass("info")
2724
2763
  ),
2725
- children: tone.label
2764
+ children: [
2765
+ /* @__PURE__ */ jsx(Spinner, {}),
2766
+ "Working"
2767
+ ]
2768
+ }
2769
+ ) : chip && /* @__PURE__ */ jsxs(
2770
+ "span",
2771
+ {
2772
+ className: cn9(
2773
+ "shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2774
+ badgeVariantClass(chip.variant)
2775
+ ),
2776
+ children: [
2777
+ chip.icon === "clock" && /* @__PURE__ */ jsx(ClockIcon2, {}),
2778
+ chip.label
2779
+ ]
2726
2780
  }
2727
2781
  )
2728
2782
  ] }),
2729
- card.description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400 line-clamp-2", children: card.description }),
2730
- card.type && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ 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 }) })
2783
+ card.description && /* @__PURE__ */ jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: card.description }),
2784
+ card.type && /* @__PURE__ */ jsx("div", { className: "mt-2", children: /* @__PURE__ */ jsx("span", { className: "inline-block rounded bg-muted px-1.5 py-0.5 text-[10px] font-medium text-muted-foreground", children: card.type }) })
2731
2785
  ]
2732
2786
  }
2733
2787
  );
2734
2788
  }
2789
+ function CapMeter({ count, cap, className }) {
2790
+ const pct = cap > 0 ? Math.min(100, Math.round(count / cap * 100)) : 0;
2791
+ return /* @__PURE__ */ jsxs(
2792
+ "div",
2793
+ {
2794
+ className: cn9(
2795
+ "mt-2 flex items-center gap-2 text-[11px] text-muted-foreground",
2796
+ className
2797
+ ),
2798
+ children: [
2799
+ /* @__PURE__ */ jsxs("span", { className: "tabular-nums font-medium", children: [
2800
+ count,
2801
+ "/",
2802
+ cap
2803
+ ] }),
2804
+ /* @__PURE__ */ jsx(
2805
+ "span",
2806
+ {
2807
+ role: "progressbar",
2808
+ "aria-valuemin": 0,
2809
+ "aria-valuemax": cap,
2810
+ "aria-valuenow": count,
2811
+ "aria-label": `${count} of ${cap} used`,
2812
+ className: "h-1 flex-1 overflow-hidden rounded-full bg-secondary",
2813
+ children: /* @__PURE__ */ jsx(
2814
+ "span",
2815
+ {
2816
+ className: "block h-full rounded-full bg-primary",
2817
+ style: { width: `${pct}%` }
2818
+ }
2819
+ )
2820
+ }
2821
+ )
2822
+ ]
2823
+ }
2824
+ );
2825
+ }
2826
+ function AgentKindIcon({ kind }) {
2827
+ const common = {
2828
+ viewBox: "0 0 24 24",
2829
+ fill: "none",
2830
+ stroke: "currentColor",
2831
+ strokeWidth: 2,
2832
+ strokeLinecap: "round",
2833
+ strokeLinejoin: "round",
2834
+ className: "h-3.5 w-3.5",
2835
+ "aria-hidden": true
2836
+ };
2837
+ if (kind === "remote") {
2838
+ return /* @__PURE__ */ jsxs("svg", { ...common, children: [
2839
+ /* @__PURE__ */ jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
2840
+ /* @__PURE__ */ jsx("path", { d: "M8 21h8M12 17v4" })
2841
+ ] });
2842
+ }
2843
+ if (kind === "human") {
2844
+ return /* @__PURE__ */ jsxs("svg", { ...common, children: [
2845
+ /* @__PURE__ */ jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
2846
+ /* @__PURE__ */ jsx("circle", { cx: "9", cy: "7", r: "4" })
2847
+ ] });
2848
+ }
2849
+ return /* @__PURE__ */ jsxs("svg", { ...common, children: [
2850
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "11", width: "18", height: "10", rx: "2" }),
2851
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "2" }),
2852
+ /* @__PURE__ */ jsx("path", { d: "M12 7v4M8 16h.01M16 16h.01" })
2853
+ ] });
2854
+ }
2855
+ function OnlineDot({ online }) {
2856
+ if (online === void 0) return null;
2857
+ return /* @__PURE__ */ jsx(
2858
+ "span",
2859
+ {
2860
+ "aria-label": online ? "connected" : "offline",
2861
+ className: cn9(
2862
+ "h-1.5 w-1.5 shrink-0 rounded-full",
2863
+ online ? "bg-success" : "bg-muted-foreground"
2864
+ )
2865
+ }
2866
+ );
2867
+ }
2868
+ function LaneAgentPicker({
2869
+ selected,
2870
+ options,
2871
+ onSelect,
2872
+ canEdit = true,
2873
+ lockedReason,
2874
+ className
2875
+ }) {
2876
+ const [open, setOpen] = useState(false);
2877
+ const rootRef = useRef(null);
2878
+ const editable = canEdit && !!onSelect && (options?.length ?? 0) > 0;
2879
+ useEffect(() => {
2880
+ if (!open) return;
2881
+ const onDocDown = (e) => {
2882
+ if (rootRef.current && !rootRef.current.contains(e.target)) {
2883
+ setOpen(false);
2884
+ }
2885
+ };
2886
+ const onKey = (e) => {
2887
+ if (e.key === "Escape") setOpen(false);
2888
+ };
2889
+ document.addEventListener("mousedown", onDocDown);
2890
+ document.addEventListener("touchstart", onDocDown);
2891
+ document.addEventListener("keydown", onKey);
2892
+ return () => {
2893
+ document.removeEventListener("mousedown", onDocDown);
2894
+ document.removeEventListener("touchstart", onDocDown);
2895
+ document.removeEventListener("keydown", onKey);
2896
+ };
2897
+ }, [open]);
2898
+ const kind = selected?.kind ?? "cadra";
2899
+ const displayLabel = selected?.label ?? "Unassigned";
2900
+ const face = /* @__PURE__ */ jsxs(Fragment, { children: [
2901
+ /* @__PURE__ */ jsx(
2902
+ "span",
2903
+ {
2904
+ className: cn9(
2905
+ "grid h-5 w-5 shrink-0 place-items-center rounded-full",
2906
+ kind === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
2907
+ ),
2908
+ children: /* @__PURE__ */ jsx(AgentKindIcon, { kind })
2909
+ }
2910
+ ),
2911
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: displayLabel }),
2912
+ /* @__PURE__ */ jsx(OnlineDot, { online: selected?.online })
2913
+ ] });
2914
+ return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn9("relative mt-2 w-full", className), children: [
2915
+ editable ? /* @__PURE__ */ jsxs(
2916
+ "button",
2917
+ {
2918
+ type: "button",
2919
+ onClick: () => setOpen((o) => !o),
2920
+ "aria-haspopup": "listbox",
2921
+ "aria-expanded": open,
2922
+ "aria-label": `Assigned agent: ${displayLabel}. Change`,
2923
+ className: cn9(
2924
+ "flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-card px-2",
2925
+ "text-xs font-semibold text-foreground",
2926
+ "hover:border-primary/50 focus:outline-none focus:ring-2 focus:ring-ring/50"
2927
+ ),
2928
+ children: [
2929
+ face,
2930
+ /* @__PURE__ */ jsx(
2931
+ "svg",
2932
+ {
2933
+ viewBox: "0 0 24 24",
2934
+ fill: "none",
2935
+ stroke: "currentColor",
2936
+ strokeWidth: 2,
2937
+ strokeLinecap: "round",
2938
+ strokeLinejoin: "round",
2939
+ "aria-hidden": true,
2940
+ className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
2941
+ children: /* @__PURE__ */ jsx("path", { d: "M6 9l6 6 6-6" })
2942
+ }
2943
+ )
2944
+ ]
2945
+ }
2946
+ ) : /* @__PURE__ */ jsxs(
2947
+ "div",
2948
+ {
2949
+ role: "group",
2950
+ "aria-label": `Assigned agent: ${displayLabel}`,
2951
+ className: cn9(
2952
+ "flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-muted/60 px-2",
2953
+ "text-xs font-semibold text-muted-foreground"
2954
+ ),
2955
+ children: [
2956
+ face,
2957
+ /* @__PURE__ */ jsxs(
2958
+ "svg",
2959
+ {
2960
+ viewBox: "0 0 24 24",
2961
+ fill: "none",
2962
+ stroke: "currentColor",
2963
+ strokeWidth: 2,
2964
+ strokeLinecap: "round",
2965
+ strokeLinejoin: "round",
2966
+ "aria-hidden": true,
2967
+ className: "h-3.5 w-3.5 shrink-0",
2968
+ children: [
2969
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
2970
+ /* @__PURE__ */ jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
2971
+ ]
2972
+ }
2973
+ )
2974
+ ]
2975
+ }
2976
+ ),
2977
+ !editable && lockedReason && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2978
+ /* @__PURE__ */ jsxs(
2979
+ "svg",
2980
+ {
2981
+ viewBox: "0 0 24 24",
2982
+ fill: "none",
2983
+ stroke: "currentColor",
2984
+ strokeWidth: 2,
2985
+ strokeLinecap: "round",
2986
+ strokeLinejoin: "round",
2987
+ "aria-hidden": true,
2988
+ className: "h-3 w-3 shrink-0",
2989
+ children: [
2990
+ /* @__PURE__ */ jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
2991
+ /* @__PURE__ */ jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
2992
+ ]
2993
+ }
2994
+ ),
2995
+ /* @__PURE__ */ jsx("span", { children: lockedReason })
2996
+ ] }),
2997
+ editable && open && /* @__PURE__ */ jsx(
2998
+ "ul",
2999
+ {
3000
+ role: "listbox",
3001
+ "aria-label": "Assign an agent",
3002
+ className: cn9(
3003
+ "absolute z-20 mt-1 max-h-64 w-full overflow-y-auto rounded-lg border border-border",
3004
+ "bg-popover p-1 shadow-lg"
3005
+ ),
3006
+ children: options.map((opt) => {
3007
+ const isSelected = opt.id === selected?.id;
3008
+ return /* @__PURE__ */ jsx("li", { role: "option", "aria-selected": isSelected, children: /* @__PURE__ */ jsxs(
3009
+ "button",
3010
+ {
3011
+ type: "button",
3012
+ onClick: () => {
3013
+ onSelect(opt.id);
3014
+ setOpen(false);
3015
+ },
3016
+ className: cn9(
3017
+ "flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium",
3018
+ "hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
3019
+ isSelected ? "text-primary" : "text-foreground"
3020
+ ),
3021
+ children: [
3022
+ /* @__PURE__ */ jsx(
3023
+ "span",
3024
+ {
3025
+ className: cn9(
3026
+ "grid h-5 w-5 shrink-0 place-items-center rounded-full",
3027
+ (opt.kind ?? "cadra") === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
3028
+ ),
3029
+ children: /* @__PURE__ */ jsx(AgentKindIcon, { kind: opt.kind ?? "cadra" })
3030
+ }
3031
+ ),
3032
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: opt.label }),
3033
+ /* @__PURE__ */ jsx(OnlineDot, { online: opt.online })
3034
+ ]
3035
+ }
3036
+ ) }, opt.id);
3037
+ })
3038
+ }
3039
+ )
3040
+ ] });
3041
+ }
3042
+ function dueStateFor(card, lane, now) {
3043
+ const rs = card.run?.status;
3044
+ if (rs === "completed" && !lane.terminal) return "ready";
3045
+ if (card.nextDueAt) {
3046
+ const t = Date.parse(card.nextDueAt);
3047
+ if (!Number.isNaN(t) && t <= now) return "due";
3048
+ }
3049
+ if (lane.trigger === "scheduled" && !rs) return "waiting";
3050
+ return void 0;
3051
+ }
2735
3052
  function KanbanColumn({
2736
3053
  lane,
2737
3054
  cards,
@@ -2739,29 +3056,82 @@ function KanbanColumn({
2739
3056
  selectedCardId,
2740
3057
  onCreateCard,
2741
3058
  showCreate,
3059
+ count,
3060
+ agentPicker,
3061
+ canEditHandlers,
2742
3062
  className
2743
3063
  }) {
3064
+ const headerCount = count ?? cards.length;
3065
+ const isScheduled = lane.trigger === "scheduled";
3066
+ const showCapMeter = count !== void 0 && typeof lane.cap === "number" && lane.cap > 0;
3067
+ const showAgentBadge = lane.handler?.type === "cadra_agent" && !agentPicker;
3068
+ const now = Date.now();
2744
3069
  return /* @__PURE__ */ jsxs(
2745
3070
  "div",
2746
3071
  {
2747
3072
  className: cn9(
2748
- "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",
3073
+ "flex w-[85vw] max-w-sm snap-start shrink-0 flex-col rounded-xl bg-muted/40 border border-border sm:w-72 sm:max-w-none",
3074
+ isScheduled && "border-border",
2749
3075
  className
2750
3076
  ),
2751
3077
  children: [
2752
- /* @__PURE__ */ 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: [
2753
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
2754
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-gray-700 dark:text-gray-200 truncate", children: lane.label }),
2755
- lane.handler?.type === "cadra_agent" && /* @__PURE__ */ jsx(
2756
- "span",
2757
- {
2758
- title: lane.handler.agentRef ? `Agent: ${lane.handler.agentRef}` : "Agent lane",
2759
- 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",
2760
- children: "agent"
2761
- }
2762
- )
3078
+ /* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2.5", children: [
3079
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3080
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
3081
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-foreground truncate", children: lane.label }),
3082
+ showAgentBadge && /* @__PURE__ */ jsx(
3083
+ "span",
3084
+ {
3085
+ title: lane.handler?.agentRef ? `Agent: ${lane.handler.agentRef}` : "Agent lane",
3086
+ className: cn9(
3087
+ // A static lane marker — NOT a run state. Uses neutral tokens,
3088
+ // never `info` (which means "running" in the F3 vocabulary).
3089
+ "rounded px-1.5 py-0.5 text-[10px] font-medium",
3090
+ badgeVariantClass("neutral")
3091
+ ),
3092
+ children: "agent"
3093
+ }
3094
+ )
3095
+ ] }),
3096
+ /* @__PURE__ */ 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 })
2763
3097
  ] }),
2764
- /* @__PURE__ */ 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 })
3098
+ isScheduled && /* @__PURE__ */ jsx("div", { className: "mt-2 flex items-center gap-2 flex-wrap", children: /* @__PURE__ */ jsxs(
3099
+ "span",
3100
+ {
3101
+ className: cn9(
3102
+ "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-semibold",
3103
+ badgeVariantClass("neutral")
3104
+ ),
3105
+ children: [
3106
+ /* @__PURE__ */ jsxs(
3107
+ "svg",
3108
+ {
3109
+ viewBox: "0 0 24 24",
3110
+ fill: "none",
3111
+ stroke: "currentColor",
3112
+ strokeWidth: "2",
3113
+ strokeLinecap: "round",
3114
+ strokeLinejoin: "round",
3115
+ "aria-hidden": true,
3116
+ className: "h-3 w-3",
3117
+ children: [
3118
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "9" }),
3119
+ /* @__PURE__ */ jsx("path", { d: "M12 7v5l3 2" })
3120
+ ]
3121
+ }
3122
+ ),
3123
+ "runs on schedule"
3124
+ ]
3125
+ }
3126
+ ) }),
3127
+ showCapMeter && /* @__PURE__ */ jsx(CapMeter, { count: headerCount, cap: lane.cap }),
3128
+ agentPicker && /* @__PURE__ */ jsx(
3129
+ LaneAgentPicker,
3130
+ {
3131
+ ...agentPicker,
3132
+ canEdit: agentPicker.canEdit ?? canEditHandlers
3133
+ }
3134
+ )
2765
3135
  ] }),
2766
3136
  /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col gap-2 overflow-y-auto p-2", children: [
2767
3137
  cards.map((card) => /* @__PURE__ */ jsx(
@@ -2769,21 +3139,23 @@ function KanbanColumn({
2769
3139
  {
2770
3140
  card,
2771
3141
  onClick: onCardClick,
2772
- selected: card.id === selectedCardId
3142
+ selected: card.id === selectedCardId,
3143
+ dueState: dueStateFor(card, lane, now)
2773
3144
  },
2774
3145
  card.id
2775
3146
  )),
2776
- cards.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-1 py-4 text-center text-xs text-gray-400 dark:text-gray-600", children: "No cards" }),
3147
+ cards.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-1 py-4 text-center text-xs text-muted-foreground", children: "No cards" }),
2777
3148
  showCreate && onCreateCard && /* @__PURE__ */ jsx(
2778
3149
  "button",
2779
3150
  {
2780
3151
  type: "button",
2781
3152
  onClick: () => onCreateCard(lane),
2782
3153
  className: cn9(
2783
- "mt-1 w-full rounded-lg border border-dashed border-gray-300 dark:border-gray-700 px-3 py-2",
2784
- "text-sm font-medium text-gray-500 dark:text-gray-400",
2785
- "hover:border-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition",
2786
- "focus:outline-none focus:ring-2 focus:ring-blue-500/40"
3154
+ // ≥44px touch (M12/F5) — density kept via padding, not height.
3155
+ "mt-1 inline-flex min-h-11 w-full items-center justify-center rounded-lg border border-dashed border-border px-3 py-2",
3156
+ "text-sm font-medium text-muted-foreground",
3157
+ "hover:border-primary/50 hover:text-foreground transition",
3158
+ "focus:outline-none focus:ring-2 focus:ring-ring/40"
2787
3159
  ),
2788
3160
  children: "+ New card"
2789
3161
  }
@@ -2798,14 +3170,14 @@ function SkeletonCard() {
2798
3170
  "div",
2799
3171
  {
2800
3172
  className: cn9(
2801
- "w-full rounded-lg border border-gray-200 dark:border-gray-700",
2802
- "bg-white dark:bg-gray-900 p-3 shadow-sm"
3173
+ "w-full rounded-lg border border-border",
3174
+ "bg-card p-3 shadow-sm"
2803
3175
  ),
2804
3176
  "aria-hidden": "true",
2805
3177
  children: [
2806
- /* @__PURE__ */ jsx("div", { className: "h-3.5 w-3/4 rounded bg-gray-200 dark:bg-gray-800" }),
2807
- /* @__PURE__ */ jsx("div", { className: "mt-2 h-2.5 w-1/2 rounded bg-gray-200 dark:bg-gray-800" }),
2808
- /* @__PURE__ */ jsx("div", { className: "mt-3 h-4 w-1/3 rounded bg-gray-200 dark:bg-gray-800" })
3178
+ /* @__PURE__ */ jsx("div", { className: "h-3.5 w-3/4 rounded bg-muted" }),
3179
+ /* @__PURE__ */ jsx("div", { className: "mt-2 h-2.5 w-1/2 rounded bg-muted" }),
3180
+ /* @__PURE__ */ jsx("div", { className: "mt-3 h-4 w-1/3 rounded bg-muted" })
2809
3181
  ]
2810
3182
  }
2811
3183
  );
@@ -2816,14 +3188,14 @@ function SkeletonColumn({ cardCount }) {
2816
3188
  {
2817
3189
  className: cn9(
2818
3190
  "flex w-72 shrink-0 flex-col rounded-xl",
2819
- "bg-gray-50 dark:bg-gray-900/50",
2820
- "border border-gray-200 dark:border-gray-800"
3191
+ "bg-muted/40",
3192
+ "border border-border"
2821
3193
  ),
2822
3194
  "aria-hidden": "true",
2823
3195
  children: [
2824
- /* @__PURE__ */ 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: [
2825
- /* @__PURE__ */ jsx("div", { className: "h-3.5 w-24 rounded bg-gray-200 dark:bg-gray-800" }),
2826
- /* @__PURE__ */ jsx("div", { className: "h-4 w-6 rounded-full bg-gray-200 dark:bg-gray-800" })
3196
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5 border-b border-border", children: [
3197
+ /* @__PURE__ */ jsx("div", { className: "h-3.5 w-24 rounded bg-muted" }),
3198
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-6 rounded-full bg-muted" })
2827
3199
  ] }),
2828
3200
  /* @__PURE__ */ jsx("div", { className: "flex flex-1 flex-col gap-2 p-2", children: Array.from({ length: cardCount }).map((_, i) => (
2829
3201
  // eslint-disable-next-line react/no-array-index-key
@@ -2852,6 +3224,67 @@ function KanbanBoardSkeleton({
2852
3224
  }
2853
3225
  );
2854
3226
  }
3227
+ function formatCountdown(nextTickAt) {
3228
+ if (!nextTickAt) return null;
3229
+ const target = Date.parse(nextTickAt);
3230
+ if (Number.isNaN(target)) return null;
3231
+ const ms = target - Date.now();
3232
+ if (ms <= 0) return "any moment";
3233
+ const mins = Math.round(ms / 6e4);
3234
+ if (mins < 1) return "under a minute";
3235
+ if (mins < 60) return `${mins}m`;
3236
+ const hrs = Math.floor(mins / 60);
3237
+ const rem = mins % 60;
3238
+ return rem > 0 ? `${hrs}h ${rem}m` : `${hrs}h`;
3239
+ }
3240
+ var DOT_TONE = {
3241
+ "on-schedule": "bg-success",
3242
+ due: "bg-info",
3243
+ paused: "bg-muted-foreground"
3244
+ };
3245
+ var DEFAULT_LABEL = {
3246
+ "on-schedule": "On schedule",
3247
+ due: "Running now",
3248
+ paused: "Paused"
3249
+ };
3250
+ function ScheduleStatus({
3251
+ nextTickAt,
3252
+ dueByLane,
3253
+ label,
3254
+ tone = "on-schedule",
3255
+ className
3256
+ }) {
3257
+ const countdown = formatCountdown(nextTickAt);
3258
+ const totalDue = dueByLane ? Object.values(dueByLane).reduce((a, b) => a + b, 0) : 0;
3259
+ return /* @__PURE__ */ jsxs(
3260
+ "div",
3261
+ {
3262
+ className: cn9(
3263
+ "flex items-center gap-2 text-xs text-muted-foreground",
3264
+ className
3265
+ ),
3266
+ children: [
3267
+ /* @__PURE__ */ jsx(
3268
+ "span",
3269
+ {
3270
+ "aria-hidden": true,
3271
+ className: cn9("h-2 w-2 shrink-0 rounded-full", DOT_TONE[tone])
3272
+ }
3273
+ ),
3274
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: label ?? DEFAULT_LABEL[tone] }),
3275
+ countdown && /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
3276
+ "\xB7 next run in ",
3277
+ countdown
3278
+ ] }),
3279
+ totalDue > 0 && /* @__PURE__ */ jsxs("span", { className: "tabular-nums", children: [
3280
+ "\xB7 ",
3281
+ totalDue,
3282
+ " due"
3283
+ ] })
3284
+ ]
3285
+ }
3286
+ );
3287
+ }
2855
3288
  function KanbanBoard({
2856
3289
  adapter,
2857
3290
  onCreateCard,
@@ -2862,6 +3295,11 @@ function KanbanBoard({
2862
3295
  enableEdit = true,
2863
3296
  enableAdvance = true,
2864
3297
  pollIntervalMs,
3298
+ laneCounts,
3299
+ canEditHandlers,
3300
+ laneAgentPicker,
3301
+ scheduleStatus,
3302
+ focusCardId,
2865
3303
  className
2866
3304
  }) {
2867
3305
  const {
@@ -2878,6 +3316,16 @@ function KanbanBoard({
2878
3316
  transitionCard,
2879
3317
  handleRunTerminal
2880
3318
  } = useKanbanBoard({ adapter, onError, onCardCreated, pollIntervalMs });
3319
+ const lastFocusedRef = useRef(void 0);
3320
+ useEffect(() => {
3321
+ if (!focusCardId) {
3322
+ lastFocusedRef.current = void 0;
3323
+ return;
3324
+ }
3325
+ if (lastFocusedRef.current === focusCardId) return;
3326
+ lastFocusedRef.current = focusCardId;
3327
+ void openCard(focusCardId);
3328
+ }, [focusCardId]);
2881
3329
  const handleCardClick = useCallback(
2882
3330
  (card) => {
2883
3331
  void openCard(card.id);
@@ -2914,10 +3362,11 @@ function KanbanBoard({
2914
3362
  const selectedLane = selectedCard ? lanes.find((l) => l.key === selectedCard.lane) : void 0;
2915
3363
  const canAdvanceSelected = !!selectedLane && !selectedLane.terminal;
2916
3364
  return /* @__PURE__ */ jsxs("div", { className: cn9("flex h-full w-full flex-col", className), children: [
2917
- error && /* @__PURE__ */ 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 }),
3365
+ error && /* @__PURE__ */ jsx("div", { className: "m-3 rounded-md border border-destructive/40 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: error }),
3366
+ scheduleStatus && /* @__PURE__ */ jsx("div", { className: "shrink-0 border-b border-border px-4 py-2", children: /* @__PURE__ */ jsx(ScheduleStatus, { ...scheduleStatus }) }),
2918
3367
  /* @__PURE__ */ jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
2919
3368
  lanes.length === 0 && isLoading ? /* @__PURE__ */ jsx(KanbanBoardSkeleton, { className: "flex-1 overflow-x-auto p-4" }) : /* @__PURE__ */ 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: [
2920
- lanes.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-gray-400 dark:text-gray-600", children: "No lanes configured." }),
3369
+ lanes.length === 0 && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "No lanes configured." }),
2921
3370
  lanes.map((lane) => /* @__PURE__ */ jsx(
2922
3371
  KanbanColumn,
2923
3372
  {
@@ -2926,7 +3375,10 @@ function KanbanBoard({
2926
3375
  onCardClick: handleCardClick,
2927
3376
  selectedCardId: selectedCard?.id,
2928
3377
  onCreateCard: handleCreate,
2929
- showCreate: enableCreate && lane.key === initialLaneKey
3378
+ showCreate: enableCreate && lane.key === initialLaneKey,
3379
+ count: laneCounts?.[lane.key],
3380
+ agentPicker: laneAgentPicker?.(lane),
3381
+ canEditHandlers
2930
3382
  },
2931
3383
  lane.id ?? lane.key
2932
3384
  ))
@@ -3668,15 +4120,6 @@ var ExecutionStreamControllerImpl = class {
3668
4120
  sseClient = null;
3669
4121
  status = "connecting";
3670
4122
  reconnectAttempts = 0;
3671
- // CAD-39 / webchat dual-plane create-connect race. `hasConnected` flips true on
3672
- // the first successful SSE open; `notFoundRetries` bounds retries of a 404
3673
- // encountered BEFORE we ever connect (the run row is persisted slightly AFTER
3674
- // the client opens the stream — the norm on the webchat path where msg-api owns
3675
- // the executionId and cadra persists the agent_executions row via an async
3676
- // worker callback). See handleConnectionError.
3677
- hasConnected = false;
3678
- notFoundRetries = 0;
3679
- maxConnectRaceRetries = 3;
3680
4123
  seenEventIds = /* @__PURE__ */ new Set();
3681
4124
  _lastEventId = null;
3682
4125
  activityTimeoutId = null;
@@ -3816,8 +4259,6 @@ var ExecutionStreamControllerImpl = class {
3816
4259
  if (connected) {
3817
4260
  this.status = "connected";
3818
4261
  this.reconnectAttempts = 0;
3819
- this.hasConnected = true;
3820
- this.notFoundRetries = 0;
3821
4262
  this.resetActivityTimeout();
3822
4263
  }
3823
4264
  this.emit("connectionChange", { connected, usingFallback: false });
@@ -4107,38 +4548,6 @@ var ExecutionStreamControllerImpl = class {
4107
4548
  return;
4108
4549
  }
4109
4550
  const msg = error instanceof Error ? error.message : String(error);
4110
- const isNotFound = /\b404\b/.test(msg);
4111
- const isAuthFailure = /\b40[13]\b/.test(msg) || /INVALID_API_KEY|INVALID_REQUEST/.test(msg);
4112
- if (isNotFound && !isAuthFailure && !this.hasConnected) {
4113
- if (this.notFoundRetries < this.maxConnectRaceRetries) {
4114
- this.notFoundRetries++;
4115
- this.status = "reconnecting";
4116
- const delay = Math.min(
4117
- this.config.reconnectBaseDelayMs * this.notFoundRetries,
4118
- this.config.reconnectMaxDelayMs
4119
- );
4120
- await new Promise((resolve) => setTimeout(resolve, delay));
4121
- if (this.status === "closed") {
4122
- return;
4123
- }
4124
- try {
4125
- await this.connectSSE();
4126
- } catch (retryError) {
4127
- await this.handleConnectionError(retryError);
4128
- }
4129
- return;
4130
- }
4131
- if (this.pollingEnabled) {
4132
- if (this.sseClient) {
4133
- this.sseClient.disconnect();
4134
- this.sseClient = null;
4135
- }
4136
- this.status = "fallback_polling";
4137
- this.emit("connectionChange", { connected: false, usingFallback: true });
4138
- this.startPolling();
4139
- return;
4140
- }
4141
- }
4142
4551
  if (/\b4\d\d\b/.test(msg) || /INVALID_API_KEY|INVALID_REQUEST/.test(msg)) {
4143
4552
  if (this.sseClient) {
4144
4553
  this.sseClient.disconnect();
@@ -4388,6 +4797,7 @@ var AgentsAPI = class extends BaseResourceAPI {
4388
4797
  super(http, "/api/v1/agents");
4389
4798
  this.getStreamConfig = getStreamConfig;
4390
4799
  }
4800
+ getStreamConfig;
4391
4801
  // ===== LIST Operations =====
4392
4802
  /**
4393
4803
  * List all agents in the organization
@@ -5293,6 +5703,7 @@ var SessionsAPI = class extends BaseResourceAPI {
5293
5703
  super(http, "/api/v1/sessions");
5294
5704
  this.getStreamConfig = getStreamConfig;
5295
5705
  }
5706
+ getStreamConfig;
5296
5707
  // ===== LIST Operations =====
5297
5708
  /**
5298
5709
  * List all sessions in the organization
@@ -5612,6 +6023,7 @@ var KanbanAPI = class extends BaseResourceAPI {
5612
6023
  super(http, "/api/v1/kanban");
5613
6024
  this.getStreamConfig = getStreamConfig;
5614
6025
  }
6026
+ getStreamConfig;
5615
6027
  // ===== BOARD =====
5616
6028
  /**
5617
6029
  * Get the current org's board (lanes in declared order). Auto-provisions
@@ -5813,6 +6225,7 @@ var RemoteAgentsAPI = class extends BaseResourceAPI {
5813
6225
  super(http, "/api/v1/remote-agents");
5814
6226
  this.getStreamConfig = getStreamConfig;
5815
6227
  }
6228
+ getStreamConfig;
5816
6229
  /**
5817
6230
  * List all remote agents in the organization
5818
6231
  *
@@ -6186,6 +6599,6 @@ function createDefaultKanbanAdapter(config) {
6186
6599
  };
6187
6600
  }
6188
6601
 
6189
- export { AgentBoard, BoardConfigClient, CadraLifecycleSource, CardDrawer, KanbanBoard, KanbanCard as KanbanCardTile, KanbanColumn, createDefaultKanbanAdapter, mapDefinitionToBoard, toResolvedLifecycle, useCardRun, useKanbanBoard };
6602
+ export { AgentBoard, BoardConfigClient, CadraLifecycleSource, CapMeter, CardDrawer, KanbanBoard, KanbanCard as KanbanCardTile, KanbanColumn, LaneAgentPicker, ScheduleStatus, badgeVariantClass, cn9 as cn, createDefaultKanbanAdapter, mapDefinitionToBoard, runStatusTone, toResolvedLifecycle, useCardRun, useKanbanBoard };
6190
6603
  //# sourceMappingURL=index.js.map
6191
6604
  //# sourceMappingURL=index.js.map