@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.
@@ -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,
@@ -2300,35 +2304,40 @@ function useCardRun(options) {
2300
2304
  function cn9(...classes) {
2301
2305
  return classes.filter(Boolean).join(" ");
2302
2306
  }
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
- };
2307
+ function badgeVariantClass(variant) {
2308
+ switch (variant) {
2309
+ case "default":
2310
+ return "bg-primary text-primary-foreground";
2311
+ case "secondary":
2312
+ return "border border-border bg-secondary text-secondary-foreground";
2313
+ case "outline":
2314
+ return "border border-border text-foreground";
2315
+ case "info":
2316
+ return "border border-info/30 bg-info/15 text-info";
2317
+ case "success":
2318
+ return "border border-success/30 bg-success/15 text-success";
2319
+ case "warning":
2320
+ return "border border-warning/40 bg-warning/15 text-warning";
2321
+ case "destructive":
2322
+ return "border border-destructive/40 bg-destructive/15 text-destructive";
2323
+ case "neutral":
2325
2324
  default:
2326
- return {
2327
- label: "Pending",
2328
- className: "bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-300"
2329
- };
2325
+ return "border border-border bg-secondary text-muted-foreground";
2330
2326
  }
2331
2327
  }
2328
+ function runStatusTone(status) {
2329
+ const map = {
2330
+ running: { label: "Running", variant: "info" },
2331
+ completed: { label: "Completed", variant: "success" },
2332
+ failed: { label: "Failed", variant: "destructive" },
2333
+ cancelled: { label: "Cancelled", variant: "neutral" }
2334
+ };
2335
+ const { label, variant } = map[status ?? ""] ?? {
2336
+ label: "Pending",
2337
+ variant: "neutral"
2338
+ };
2339
+ return { label, variant, className: badgeVariantClass(variant) };
2340
+ }
2332
2341
  function CardDrawer({
2333
2342
  card,
2334
2343
  adapter,
@@ -2447,11 +2456,11 @@ function CardDrawer({
2447
2456
  role: "dialog",
2448
2457
  "aria-label": `Card: ${card.title}`,
2449
2458
  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",
2459
+ "flex h-full w-full sm:max-w-sm flex-col bg-card border-l border-border shadow-xl",
2451
2460
  className
2452
2461
  ),
2453
2462
  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: [
2463
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-3 border-b border-border px-4 py-3", children: [
2455
2464
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
2456
2465
  isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
2457
2466
  "input",
@@ -2461,14 +2470,14 @@ function CardDrawer({
2461
2470
  onChange: (e) => setEditTitle(e.target.value),
2462
2471
  "aria-label": "Card title",
2463
2472
  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"
2473
+ 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
2474
  }
2466
2475
  ) : /* @__PURE__ */ jsxRuntime.jsx(
2467
2476
  "h2",
2468
2477
  {
2469
2478
  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"
2479
+ "text-base font-semibold text-foreground truncate",
2480
+ onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
2472
2481
  ),
2473
2482
  onClick: onUpdate ? () => setIsEditing(true) : void 0,
2474
2483
  title: onUpdate ? "Click to edit" : void 0,
@@ -2476,7 +2485,7 @@ function CardDrawer({
2476
2485
  }
2477
2486
  ),
2478
2487
  /* @__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 }),
2488
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: card.lane }),
2480
2489
  /* @__PURE__ */ jsxRuntime.jsx(
2481
2490
  "span",
2482
2491
  {
@@ -2496,7 +2505,7 @@ function CardDrawer({
2496
2505
  type: "button",
2497
2506
  onClick: () => setIsEditing(true),
2498
2507
  "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",
2508
+ className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
2500
2509
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2501
2510
  "svg",
2502
2511
  {
@@ -2522,7 +2531,7 @@ function CardDrawer({
2522
2531
  type: "button",
2523
2532
  onClick: onClose,
2524
2533
  "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",
2534
+ className: "rounded p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
2526
2535
  children: /* @__PURE__ */ jsxRuntime.jsxs(
2527
2536
  "svg",
2528
2537
  {
@@ -2544,8 +2553,8 @@ function CardDrawer({
2544
2553
  )
2545
2554
  ] })
2546
2555
  ] }),
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" }),
2556
+ isEditing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border px-4 py-3", children: [
2557
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "mb-1 block text-xs font-medium text-muted-foreground", children: "Description" }),
2549
2558
  /* @__PURE__ */ jsxRuntime.jsx(
2550
2559
  "textarea",
2551
2560
  {
@@ -2554,7 +2563,7 @@ function CardDrawer({
2554
2563
  "aria-label": "Card description",
2555
2564
  rows: 4,
2556
2565
  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"
2566
+ 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
2567
  }
2559
2568
  ),
2560
2569
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 flex items-center justify-end gap-2", children: [
@@ -2564,7 +2573,7 @@ function CardDrawer({
2564
2573
  type: "button",
2565
2574
  onClick: handleCancel,
2566
2575
  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",
2576
+ className: "rounded-md px-3 py-1.5 text-sm font-medium text-muted-foreground hover:bg-muted disabled:opacity-50",
2568
2577
  children: "Cancel"
2569
2578
  }
2570
2579
  ),
@@ -2574,26 +2583,27 @@ function CardDrawer({
2574
2583
  type: "button",
2575
2584
  onClick: () => void handleSave(),
2576
2585
  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",
2586
+ 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
2587
  children: isSaving ? "Saving\u2026" : "Save"
2579
2588
  }
2580
2589
  )
2581
2590
  ] })
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(
2591
+ ] }) : (card.description || onUpdate) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-border px-4 py-3", children: /* @__PURE__ */ jsxRuntime.jsx(
2583
2592
  "p",
2584
2593
  {
2585
2594
  className: cn9(
2586
2595
  "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"
2596
+ card.description ? "text-muted-foreground" : "italic text-muted-foreground/70",
2597
+ onUpdate && "cursor-text rounded hover:bg-muted/60 -mx-1 px-1"
2589
2598
  ),
2590
2599
  onClick: onUpdate ? () => setIsEditing(true) : void 0,
2591
2600
  title: onUpdate ? "Click to edit" : void 0,
2592
2601
  children: card.description || "Add a description\u2026"
2593
2602
  }
2594
2603
  ) }),
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" }),
2604
+ showTransitionButtons ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center justify-end gap-2 border-b border-border px-4 py-2", children: [
2605
+ isHumanGate && // Human gate = needs-you → warning (amber is reserved for this, F3).
2606
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-auto text-xs font-medium text-warning", children: "Awaiting your approval" }),
2597
2607
  manualEdges.map((t, i) => {
2598
2608
  const toLabel = laneByKey.get(t.toLane)?.label ?? t.toLane;
2599
2609
  const isPrimary = isHumanGate ? i === 0 : false;
@@ -2606,26 +2616,27 @@ function CardDrawer({
2606
2616
  onClick: () => void handleTransition(t.toLane),
2607
2617
  disabled: pendingLane !== null,
2608
2618
  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"
2619
+ // ≥44px touch (M12/F5) — density kept via padding, not height.
2620
+ "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",
2621
+ isPrimary ? "border border-border bg-secondary text-secondary-foreground hover:bg-muted" : "border border-border text-foreground hover:bg-muted"
2611
2622
  ),
2612
2623
  children: busy ? "Moving\u2026" : label
2613
2624
  },
2614
2625
  t.id
2615
2626
  );
2616
2627
  })
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(
2628
+ ] }) : 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
2629
  "button",
2619
2630
  {
2620
2631
  type: "button",
2621
2632
  onClick: () => void handleAdvance(),
2622
2633
  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",
2634
+ 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
2635
  children: isAdvancing ? "Advancing\u2026" : "Advance \u2192"
2625
2636
  }
2626
2637
  ) }),
2627
2638
  /* @__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(
2639
+ !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
2640
  MessageList,
2630
2641
  {
2631
2642
  messages: run.messages,
@@ -2638,14 +2649,14 @@ function CardDrawer({
2638
2649
  className: "flex-1"
2639
2650
  }
2640
2651
  ),
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." }),
2652
+ 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: [
2653
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-destructive", children: run.error ?? "Run failed." }),
2643
2654
  onRetry && /* @__PURE__ */ jsxRuntime.jsx(
2644
2655
  "button",
2645
2656
  {
2646
2657
  type: "button",
2647
2658
  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",
2659
+ 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
2660
  children: "Retry"
2650
2661
  }
2651
2662
  )
@@ -2687,10 +2698,51 @@ function Spinner({ className }) {
2687
2698
  }
2688
2699
  );
2689
2700
  }
2690
- function KanbanCard({ card, onClick, selected, className }) {
2701
+ function ClockIcon2() {
2702
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2703
+ "svg",
2704
+ {
2705
+ viewBox: "0 0 24 24",
2706
+ fill: "none",
2707
+ stroke: "currentColor",
2708
+ strokeWidth: "2",
2709
+ strokeLinecap: "round",
2710
+ strokeLinejoin: "round",
2711
+ "aria-hidden": true,
2712
+ className: "h-2.5 w-2.5",
2713
+ children: [
2714
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
2715
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
2716
+ ]
2717
+ }
2718
+ );
2719
+ }
2720
+ function KanbanCard({
2721
+ card,
2722
+ onClick,
2723
+ selected,
2724
+ dueState,
2725
+ className
2726
+ }) {
2691
2727
  const runStatus = card.run?.status;
2692
- const tone = runStatus ? runStatusTone(runStatus) : null;
2693
- const isWorking = runStatus === "running" || runStatus === "pending";
2728
+ const isWorking = runStatus === "running";
2729
+ const failed = runStatus === "failed";
2730
+ let chip = null;
2731
+ if (failed) {
2732
+ chip = { label: "Failed", variant: "destructive" };
2733
+ } else if (dueState === "ready") {
2734
+ chip = { label: "Ready", variant: "success" };
2735
+ } else if (runStatus === "completed") {
2736
+ const t = runStatusTone(runStatus);
2737
+ chip = { label: t.label, variant: t.variant };
2738
+ } else if (dueState === "due") {
2739
+ chip = { label: "Due", variant: "neutral", icon: "clock" };
2740
+ } else if (dueState === "waiting") {
2741
+ chip = { label: "Waiting", variant: "neutral" };
2742
+ } else if (runStatus) {
2743
+ const t = runStatusTone(runStatus);
2744
+ chip = { label: t.label, variant: t.variant };
2745
+ }
2694
2746
  return /* @__PURE__ */ jsxRuntime.jsxs(
2695
2747
  "button",
2696
2748
  {
@@ -2699,46 +2751,311 @@ function KanbanCard({ card, onClick, selected, className }) {
2699
2751
  "aria-pressed": selected,
2700
2752
  "aria-busy": isWorking,
2701
2753
  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",
2754
+ "relative w-full text-left rounded-lg border bg-card p-3 shadow-sm transition",
2755
+ "hover:shadow-md hover:border-primary/40",
2756
+ "focus:outline-none focus:ring-2 focus:ring-ring/50",
2757
+ // The working card carries the SINGLE focal motion on the board (F11).
2758
+ isWorking ? "border-info/60 ring-2 ring-info/40 animate-pulse" : selected ? "border-primary/60 ring-2 ring-primary/30" : "border-border",
2706
2759
  className
2707
2760
  ),
2708
2761
  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
2762
  /* @__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(
2763
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium text-foreground line-clamp-2", children: card.title }),
2764
+ isWorking ? /* @__PURE__ */ jsxRuntime.jsxs(
2726
2765
  "span",
2727
2766
  {
2728
2767
  className: cn9(
2729
- "shrink-0 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2730
- tone.className
2768
+ "shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2769
+ badgeVariantClass("info")
2731
2770
  ),
2732
- children: tone.label
2771
+ children: [
2772
+ /* @__PURE__ */ jsxRuntime.jsx(Spinner, {}),
2773
+ "Working"
2774
+ ]
2775
+ }
2776
+ ) : chip && /* @__PURE__ */ jsxRuntime.jsxs(
2777
+ "span",
2778
+ {
2779
+ className: cn9(
2780
+ "shrink-0 inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide",
2781
+ badgeVariantClass(chip.variant)
2782
+ ),
2783
+ children: [
2784
+ chip.icon === "clock" && /* @__PURE__ */ jsxRuntime.jsx(ClockIcon2, {}),
2785
+ chip.label
2786
+ ]
2733
2787
  }
2734
2788
  )
2735
2789
  ] }),
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 }) })
2790
+ card.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1.5 text-xs text-muted-foreground line-clamp-2", children: card.description }),
2791
+ 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
2792
  ]
2739
2793
  }
2740
2794
  );
2741
2795
  }
2796
+ function CapMeter({ count, cap, className }) {
2797
+ const pct = cap > 0 ? Math.min(100, Math.round(count / cap * 100)) : 0;
2798
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2799
+ "div",
2800
+ {
2801
+ className: cn9(
2802
+ "mt-2 flex items-center gap-2 text-[11px] text-muted-foreground",
2803
+ className
2804
+ ),
2805
+ children: [
2806
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums font-medium", children: [
2807
+ count,
2808
+ "/",
2809
+ cap
2810
+ ] }),
2811
+ /* @__PURE__ */ jsxRuntime.jsx(
2812
+ "span",
2813
+ {
2814
+ role: "progressbar",
2815
+ "aria-valuemin": 0,
2816
+ "aria-valuemax": cap,
2817
+ "aria-valuenow": count,
2818
+ "aria-label": `${count} of ${cap} used`,
2819
+ className: "h-1 flex-1 overflow-hidden rounded-full bg-secondary",
2820
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2821
+ "span",
2822
+ {
2823
+ className: "block h-full rounded-full bg-primary",
2824
+ style: { width: `${pct}%` }
2825
+ }
2826
+ )
2827
+ }
2828
+ )
2829
+ ]
2830
+ }
2831
+ );
2832
+ }
2833
+ function AgentKindIcon({ kind }) {
2834
+ const common = {
2835
+ viewBox: "0 0 24 24",
2836
+ fill: "none",
2837
+ stroke: "currentColor",
2838
+ strokeWidth: 2,
2839
+ strokeLinecap: "round",
2840
+ strokeLinejoin: "round",
2841
+ className: "h-3.5 w-3.5",
2842
+ "aria-hidden": true
2843
+ };
2844
+ if (kind === "remote") {
2845
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...common, children: [
2846
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "2", y: "3", width: "20", height: "14", rx: "2" }),
2847
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 21h8M12 17v4" })
2848
+ ] });
2849
+ }
2850
+ if (kind === "human") {
2851
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...common, children: [
2852
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" }),
2853
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "9", cy: "7", r: "4" })
2854
+ ] });
2855
+ }
2856
+ return /* @__PURE__ */ jsxRuntime.jsxs("svg", { ...common, children: [
2857
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "3", y: "11", width: "18", height: "10", rx: "2" }),
2858
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "5", r: "2" }),
2859
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v4M8 16h.01M16 16h.01" })
2860
+ ] });
2861
+ }
2862
+ function OnlineDot({ online }) {
2863
+ if (online === void 0) return null;
2864
+ return /* @__PURE__ */ jsxRuntime.jsx(
2865
+ "span",
2866
+ {
2867
+ "aria-label": online ? "connected" : "offline",
2868
+ className: cn9(
2869
+ "h-1.5 w-1.5 shrink-0 rounded-full",
2870
+ online ? "bg-success" : "bg-muted-foreground"
2871
+ )
2872
+ }
2873
+ );
2874
+ }
2875
+ function LaneAgentPicker({
2876
+ selected,
2877
+ options,
2878
+ onSelect,
2879
+ canEdit = true,
2880
+ lockedReason,
2881
+ className
2882
+ }) {
2883
+ const [open, setOpen] = react.useState(false);
2884
+ const rootRef = react.useRef(null);
2885
+ const editable = canEdit && !!onSelect && (options?.length ?? 0) > 0;
2886
+ react.useEffect(() => {
2887
+ if (!open) return;
2888
+ const onDocDown = (e) => {
2889
+ if (rootRef.current && !rootRef.current.contains(e.target)) {
2890
+ setOpen(false);
2891
+ }
2892
+ };
2893
+ const onKey = (e) => {
2894
+ if (e.key === "Escape") setOpen(false);
2895
+ };
2896
+ document.addEventListener("mousedown", onDocDown);
2897
+ document.addEventListener("touchstart", onDocDown);
2898
+ document.addEventListener("keydown", onKey);
2899
+ return () => {
2900
+ document.removeEventListener("mousedown", onDocDown);
2901
+ document.removeEventListener("touchstart", onDocDown);
2902
+ document.removeEventListener("keydown", onKey);
2903
+ };
2904
+ }, [open]);
2905
+ const kind = selected?.kind ?? "cadra";
2906
+ const displayLabel = selected?.label ?? "Unassigned";
2907
+ const face = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2908
+ /* @__PURE__ */ jsxRuntime.jsx(
2909
+ "span",
2910
+ {
2911
+ className: cn9(
2912
+ "grid h-5 w-5 shrink-0 place-items-center rounded-full",
2913
+ kind === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
2914
+ ),
2915
+ children: /* @__PURE__ */ jsxRuntime.jsx(AgentKindIcon, { kind })
2916
+ }
2917
+ ),
2918
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate text-left", children: displayLabel }),
2919
+ /* @__PURE__ */ jsxRuntime.jsx(OnlineDot, { online: selected?.online })
2920
+ ] });
2921
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: rootRef, className: cn9("relative mt-2 w-full", className), children: [
2922
+ editable ? /* @__PURE__ */ jsxRuntime.jsxs(
2923
+ "button",
2924
+ {
2925
+ type: "button",
2926
+ onClick: () => setOpen((o) => !o),
2927
+ "aria-haspopup": "listbox",
2928
+ "aria-expanded": open,
2929
+ "aria-label": `Assigned agent: ${displayLabel}. Change`,
2930
+ className: cn9(
2931
+ "flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-card px-2",
2932
+ "text-xs font-semibold text-foreground",
2933
+ "hover:border-primary/50 focus:outline-none focus:ring-2 focus:ring-ring/50"
2934
+ ),
2935
+ children: [
2936
+ face,
2937
+ /* @__PURE__ */ jsxRuntime.jsx(
2938
+ "svg",
2939
+ {
2940
+ viewBox: "0 0 24 24",
2941
+ fill: "none",
2942
+ stroke: "currentColor",
2943
+ strokeWidth: 2,
2944
+ strokeLinecap: "round",
2945
+ strokeLinejoin: "round",
2946
+ "aria-hidden": true,
2947
+ className: "h-3.5 w-3.5 shrink-0 text-muted-foreground",
2948
+ children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M6 9l6 6 6-6" })
2949
+ }
2950
+ )
2951
+ ]
2952
+ }
2953
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
2954
+ "div",
2955
+ {
2956
+ role: "group",
2957
+ "aria-label": `Assigned agent: ${displayLabel}`,
2958
+ className: cn9(
2959
+ "flex min-h-11 w-full items-center gap-1.5 rounded-lg border border-border bg-muted/60 px-2",
2960
+ "text-xs font-semibold text-muted-foreground"
2961
+ ),
2962
+ children: [
2963
+ face,
2964
+ /* @__PURE__ */ jsxRuntime.jsxs(
2965
+ "svg",
2966
+ {
2967
+ viewBox: "0 0 24 24",
2968
+ fill: "none",
2969
+ stroke: "currentColor",
2970
+ strokeWidth: 2,
2971
+ strokeLinecap: "round",
2972
+ strokeLinejoin: "round",
2973
+ "aria-hidden": true,
2974
+ className: "h-3.5 w-3.5 shrink-0",
2975
+ children: [
2976
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
2977
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
2978
+ ]
2979
+ }
2980
+ )
2981
+ ]
2982
+ }
2983
+ ),
2984
+ !editable && lockedReason && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-1.5 flex items-center gap-1 text-[11px] text-muted-foreground", children: [
2985
+ /* @__PURE__ */ jsxRuntime.jsxs(
2986
+ "svg",
2987
+ {
2988
+ viewBox: "0 0 24 24",
2989
+ fill: "none",
2990
+ stroke: "currentColor",
2991
+ strokeWidth: 2,
2992
+ strokeLinecap: "round",
2993
+ strokeLinejoin: "round",
2994
+ "aria-hidden": true,
2995
+ className: "h-3 w-3 shrink-0",
2996
+ children: [
2997
+ /* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "11", width: "14", height: "10", rx: "2" }),
2998
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11V7a4 4 0 0 1 8 0v4" })
2999
+ ]
3000
+ }
3001
+ ),
3002
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: lockedReason })
3003
+ ] }),
3004
+ editable && open && /* @__PURE__ */ jsxRuntime.jsx(
3005
+ "ul",
3006
+ {
3007
+ role: "listbox",
3008
+ "aria-label": "Assign an agent",
3009
+ className: cn9(
3010
+ "absolute z-20 mt-1 max-h-64 w-full overflow-y-auto rounded-lg border border-border",
3011
+ "bg-popover p-1 shadow-lg"
3012
+ ),
3013
+ children: options.map((opt) => {
3014
+ const isSelected = opt.id === selected?.id;
3015
+ return /* @__PURE__ */ jsxRuntime.jsx("li", { role: "option", "aria-selected": isSelected, children: /* @__PURE__ */ jsxRuntime.jsxs(
3016
+ "button",
3017
+ {
3018
+ type: "button",
3019
+ onClick: () => {
3020
+ onSelect(opt.id);
3021
+ setOpen(false);
3022
+ },
3023
+ className: cn9(
3024
+ "flex min-h-11 w-full items-center gap-1.5 rounded-md px-2 text-left text-xs font-medium",
3025
+ "hover:bg-muted focus:outline-none focus:ring-2 focus:ring-ring/50",
3026
+ isSelected ? "text-primary" : "text-foreground"
3027
+ ),
3028
+ children: [
3029
+ /* @__PURE__ */ jsxRuntime.jsx(
3030
+ "span",
3031
+ {
3032
+ className: cn9(
3033
+ "grid h-5 w-5 shrink-0 place-items-center rounded-full",
3034
+ (opt.kind ?? "cadra") === "cadra" ? "bg-primary/15 text-primary" : "bg-secondary text-foreground"
3035
+ ),
3036
+ children: /* @__PURE__ */ jsxRuntime.jsx(AgentKindIcon, { kind: opt.kind ?? "cadra" })
3037
+ }
3038
+ ),
3039
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 flex-1 truncate", children: opt.label }),
3040
+ /* @__PURE__ */ jsxRuntime.jsx(OnlineDot, { online: opt.online })
3041
+ ]
3042
+ }
3043
+ ) }, opt.id);
3044
+ })
3045
+ }
3046
+ )
3047
+ ] });
3048
+ }
3049
+ function dueStateFor(card, lane, now) {
3050
+ const rs = card.run?.status;
3051
+ if (rs === "completed" && !lane.terminal) return "ready";
3052
+ if (card.nextDueAt) {
3053
+ const t = Date.parse(card.nextDueAt);
3054
+ if (!Number.isNaN(t) && t <= now) return "due";
3055
+ }
3056
+ if (lane.trigger === "scheduled" && !rs) return "waiting";
3057
+ return void 0;
3058
+ }
2742
3059
  function KanbanColumn({
2743
3060
  lane,
2744
3061
  cards,
@@ -2746,29 +3063,82 @@ function KanbanColumn({
2746
3063
  selectedCardId,
2747
3064
  onCreateCard,
2748
3065
  showCreate,
3066
+ count,
3067
+ agentPicker,
3068
+ canEditHandlers,
2749
3069
  className
2750
3070
  }) {
3071
+ const headerCount = count ?? cards.length;
3072
+ const isScheduled = lane.trigger === "scheduled";
3073
+ const showCapMeter = count !== void 0 && typeof lane.cap === "number" && lane.cap > 0;
3074
+ const showAgentBadge = lane.handler?.type === "cadra_agent" && !agentPicker;
3075
+ const now = Date.now();
2751
3076
  return /* @__PURE__ */ jsxRuntime.jsxs(
2752
3077
  "div",
2753
3078
  {
2754
3079
  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",
3080
+ "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",
3081
+ isScheduled && "border-border",
2756
3082
  className
2757
3083
  ),
2758
3084
  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
- )
3085
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-b border-border px-3 py-2.5", children: [
3086
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3087
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 min-w-0", children: [
3088
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold text-foreground truncate", children: lane.label }),
3089
+ showAgentBadge && /* @__PURE__ */ jsxRuntime.jsx(
3090
+ "span",
3091
+ {
3092
+ title: lane.handler?.agentRef ? `Agent: ${lane.handler.agentRef}` : "Agent lane",
3093
+ className: cn9(
3094
+ // A static lane marker — NOT a run state. Uses neutral tokens,
3095
+ // never `info` (which means "running" in the F3 vocabulary).
3096
+ "rounded px-1.5 py-0.5 text-[10px] font-medium",
3097
+ badgeVariantClass("neutral")
3098
+ ),
3099
+ children: "agent"
3100
+ }
3101
+ )
3102
+ ] }),
3103
+ /* @__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
3104
  ] }),
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 })
3105
+ isScheduled && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex items-center gap-2 flex-wrap", children: /* @__PURE__ */ jsxRuntime.jsxs(
3106
+ "span",
3107
+ {
3108
+ className: cn9(
3109
+ "inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-semibold",
3110
+ badgeVariantClass("neutral")
3111
+ ),
3112
+ children: [
3113
+ /* @__PURE__ */ jsxRuntime.jsxs(
3114
+ "svg",
3115
+ {
3116
+ viewBox: "0 0 24 24",
3117
+ fill: "none",
3118
+ stroke: "currentColor",
3119
+ strokeWidth: "2",
3120
+ strokeLinecap: "round",
3121
+ strokeLinejoin: "round",
3122
+ "aria-hidden": true,
3123
+ className: "h-3 w-3",
3124
+ children: [
3125
+ /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "9" }),
3126
+ /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 7v5l3 2" })
3127
+ ]
3128
+ }
3129
+ ),
3130
+ "runs on schedule"
3131
+ ]
3132
+ }
3133
+ ) }),
3134
+ showCapMeter && /* @__PURE__ */ jsxRuntime.jsx(CapMeter, { count: headerCount, cap: lane.cap }),
3135
+ agentPicker && /* @__PURE__ */ jsxRuntime.jsx(
3136
+ LaneAgentPicker,
3137
+ {
3138
+ ...agentPicker,
3139
+ canEdit: agentPicker.canEdit ?? canEditHandlers
3140
+ }
3141
+ )
2772
3142
  ] }),
2773
3143
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col gap-2 overflow-y-auto p-2", children: [
2774
3144
  cards.map((card) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -2776,21 +3146,23 @@ function KanbanColumn({
2776
3146
  {
2777
3147
  card,
2778
3148
  onClick: onCardClick,
2779
- selected: card.id === selectedCardId
3149
+ selected: card.id === selectedCardId,
3150
+ dueState: dueStateFor(card, lane, now)
2780
3151
  },
2781
3152
  card.id
2782
3153
  )),
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" }),
3154
+ cards.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-1 py-4 text-center text-xs text-muted-foreground", children: "No cards" }),
2784
3155
  showCreate && onCreateCard && /* @__PURE__ */ jsxRuntime.jsx(
2785
3156
  "button",
2786
3157
  {
2787
3158
  type: "button",
2788
3159
  onClick: () => onCreateCard(lane),
2789
3160
  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"
3161
+ // ≥44px touch (M12/F5) — density kept via padding, not height.
3162
+ "mt-1 inline-flex min-h-11 w-full items-center justify-center rounded-lg border border-dashed border-border px-3 py-2",
3163
+ "text-sm font-medium text-muted-foreground",
3164
+ "hover:border-primary/50 hover:text-foreground transition",
3165
+ "focus:outline-none focus:ring-2 focus:ring-ring/40"
2794
3166
  ),
2795
3167
  children: "+ New card"
2796
3168
  }
@@ -2805,14 +3177,14 @@ function SkeletonCard() {
2805
3177
  "div",
2806
3178
  {
2807
3179
  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"
3180
+ "w-full rounded-lg border border-border",
3181
+ "bg-card p-3 shadow-sm"
2810
3182
  ),
2811
3183
  "aria-hidden": "true",
2812
3184
  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" })
3185
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-3/4 rounded bg-muted" }),
3186
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 h-2.5 w-1/2 rounded bg-muted" }),
3187
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-3 h-4 w-1/3 rounded bg-muted" })
2816
3188
  ]
2817
3189
  }
2818
3190
  );
@@ -2823,14 +3195,14 @@ function SkeletonColumn({ cardCount }) {
2823
3195
  {
2824
3196
  className: cn9(
2825
3197
  "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"
3198
+ "bg-muted/40",
3199
+ "border border-border"
2828
3200
  ),
2829
3201
  "aria-hidden": "true",
2830
3202
  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" })
3203
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-2 px-3 py-2.5 border-b border-border", children: [
3204
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-24 rounded bg-muted" }),
3205
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-6 rounded-full bg-muted" })
2834
3206
  ] }),
2835
3207
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col gap-2 p-2", children: Array.from({ length: cardCount }).map((_, i) => (
2836
3208
  // eslint-disable-next-line react/no-array-index-key
@@ -2859,6 +3231,67 @@ function KanbanBoardSkeleton({
2859
3231
  }
2860
3232
  );
2861
3233
  }
3234
+ function formatCountdown(nextTickAt) {
3235
+ if (!nextTickAt) return null;
3236
+ const target = Date.parse(nextTickAt);
3237
+ if (Number.isNaN(target)) return null;
3238
+ const ms = target - Date.now();
3239
+ if (ms <= 0) return "any moment";
3240
+ const mins = Math.round(ms / 6e4);
3241
+ if (mins < 1) return "under a minute";
3242
+ if (mins < 60) return `${mins}m`;
3243
+ const hrs = Math.floor(mins / 60);
3244
+ const rem = mins % 60;
3245
+ return rem > 0 ? `${hrs}h ${rem}m` : `${hrs}h`;
3246
+ }
3247
+ var DOT_TONE = {
3248
+ "on-schedule": "bg-success",
3249
+ due: "bg-info",
3250
+ paused: "bg-muted-foreground"
3251
+ };
3252
+ var DEFAULT_LABEL = {
3253
+ "on-schedule": "On schedule",
3254
+ due: "Running now",
3255
+ paused: "Paused"
3256
+ };
3257
+ function ScheduleStatus({
3258
+ nextTickAt,
3259
+ dueByLane,
3260
+ label,
3261
+ tone = "on-schedule",
3262
+ className
3263
+ }) {
3264
+ const countdown = formatCountdown(nextTickAt);
3265
+ const totalDue = dueByLane ? Object.values(dueByLane).reduce((a, b) => a + b, 0) : 0;
3266
+ return /* @__PURE__ */ jsxRuntime.jsxs(
3267
+ "div",
3268
+ {
3269
+ className: cn9(
3270
+ "flex items-center gap-2 text-xs text-muted-foreground",
3271
+ className
3272
+ ),
3273
+ children: [
3274
+ /* @__PURE__ */ jsxRuntime.jsx(
3275
+ "span",
3276
+ {
3277
+ "aria-hidden": true,
3278
+ className: cn9("h-2 w-2 shrink-0 rounded-full", DOT_TONE[tone])
3279
+ }
3280
+ ),
3281
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: label ?? DEFAULT_LABEL[tone] }),
3282
+ countdown && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums", children: [
3283
+ "\xB7 next run in ",
3284
+ countdown
3285
+ ] }),
3286
+ totalDue > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "tabular-nums", children: [
3287
+ "\xB7 ",
3288
+ totalDue,
3289
+ " due"
3290
+ ] })
3291
+ ]
3292
+ }
3293
+ );
3294
+ }
2862
3295
  function KanbanBoard({
2863
3296
  adapter,
2864
3297
  onCreateCard,
@@ -2869,6 +3302,11 @@ function KanbanBoard({
2869
3302
  enableEdit = true,
2870
3303
  enableAdvance = true,
2871
3304
  pollIntervalMs,
3305
+ laneCounts,
3306
+ canEditHandlers,
3307
+ laneAgentPicker,
3308
+ scheduleStatus,
3309
+ focusCardId,
2872
3310
  className
2873
3311
  }) {
2874
3312
  const {
@@ -2885,6 +3323,16 @@ function KanbanBoard({
2885
3323
  transitionCard,
2886
3324
  handleRunTerminal
2887
3325
  } = useKanbanBoard({ adapter, onError, onCardCreated, pollIntervalMs });
3326
+ const lastFocusedRef = react.useRef(void 0);
3327
+ react.useEffect(() => {
3328
+ if (!focusCardId) {
3329
+ lastFocusedRef.current = void 0;
3330
+ return;
3331
+ }
3332
+ if (lastFocusedRef.current === focusCardId) return;
3333
+ lastFocusedRef.current = focusCardId;
3334
+ void openCard(focusCardId);
3335
+ }, [focusCardId]);
2888
3336
  const handleCardClick = react.useCallback(
2889
3337
  (card) => {
2890
3338
  void openCard(card.id);
@@ -2921,10 +3369,11 @@ function KanbanBoard({
2921
3369
  const selectedLane = selectedCard ? lanes.find((l) => l.key === selectedCard.lane) : void 0;
2922
3370
  const canAdvanceSelected = !!selectedLane && !selectedLane.terminal;
2923
3371
  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 }),
3372
+ 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 }),
3373
+ scheduleStatus && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0 border-b border-border px-4 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(ScheduleStatus, { ...scheduleStatus }) }),
2925
3374
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 overflow-hidden", children: [
2926
3375
  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." }),
3376
+ lanes.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "No lanes configured." }),
2928
3377
  lanes.map((lane) => /* @__PURE__ */ jsxRuntime.jsx(
2929
3378
  KanbanColumn,
2930
3379
  {
@@ -2933,7 +3382,10 @@ function KanbanBoard({
2933
3382
  onCardClick: handleCardClick,
2934
3383
  selectedCardId: selectedCard?.id,
2935
3384
  onCreateCard: handleCreate,
2936
- showCreate: enableCreate && lane.key === initialLaneKey
3385
+ showCreate: enableCreate && lane.key === initialLaneKey,
3386
+ count: laneCounts?.[lane.key],
3387
+ agentPicker: laneAgentPicker?.(lane),
3388
+ canEditHandlers
2937
3389
  },
2938
3390
  lane.id ?? lane.key
2939
3391
  ))
@@ -3675,15 +4127,6 @@ var ExecutionStreamControllerImpl = class {
3675
4127
  sseClient = null;
3676
4128
  status = "connecting";
3677
4129
  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
4130
  seenEventIds = /* @__PURE__ */ new Set();
3688
4131
  _lastEventId = null;
3689
4132
  activityTimeoutId = null;
@@ -3823,8 +4266,6 @@ var ExecutionStreamControllerImpl = class {
3823
4266
  if (connected) {
3824
4267
  this.status = "connected";
3825
4268
  this.reconnectAttempts = 0;
3826
- this.hasConnected = true;
3827
- this.notFoundRetries = 0;
3828
4269
  this.resetActivityTimeout();
3829
4270
  }
3830
4271
  this.emit("connectionChange", { connected, usingFallback: false });
@@ -4114,38 +4555,6 @@ var ExecutionStreamControllerImpl = class {
4114
4555
  return;
4115
4556
  }
4116
4557
  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
4558
  if (/\b4\d\d\b/.test(msg) || /INVALID_API_KEY|INVALID_REQUEST/.test(msg)) {
4150
4559
  if (this.sseClient) {
4151
4560
  this.sseClient.disconnect();
@@ -4395,6 +4804,7 @@ var AgentsAPI = class extends BaseResourceAPI {
4395
4804
  super(http, "/api/v1/agents");
4396
4805
  this.getStreamConfig = getStreamConfig;
4397
4806
  }
4807
+ getStreamConfig;
4398
4808
  // ===== LIST Operations =====
4399
4809
  /**
4400
4810
  * List all agents in the organization
@@ -5300,6 +5710,7 @@ var SessionsAPI = class extends BaseResourceAPI {
5300
5710
  super(http, "/api/v1/sessions");
5301
5711
  this.getStreamConfig = getStreamConfig;
5302
5712
  }
5713
+ getStreamConfig;
5303
5714
  // ===== LIST Operations =====
5304
5715
  /**
5305
5716
  * List all sessions in the organization
@@ -5619,6 +6030,7 @@ var KanbanAPI = class extends BaseResourceAPI {
5619
6030
  super(http, "/api/v1/kanban");
5620
6031
  this.getStreamConfig = getStreamConfig;
5621
6032
  }
6033
+ getStreamConfig;
5622
6034
  // ===== BOARD =====
5623
6035
  /**
5624
6036
  * Get the current org's board (lanes in declared order). Auto-provisions
@@ -5820,6 +6232,7 @@ var RemoteAgentsAPI = class extends BaseResourceAPI {
5820
6232
  super(http, "/api/v1/remote-agents");
5821
6233
  this.getStreamConfig = getStreamConfig;
5822
6234
  }
6235
+ getStreamConfig;
5823
6236
  /**
5824
6237
  * List all remote agents in the organization
5825
6238
  *
@@ -6196,12 +6609,18 @@ function createDefaultKanbanAdapter(config) {
6196
6609
  exports.AgentBoard = AgentBoard;
6197
6610
  exports.BoardConfigClient = BoardConfigClient;
6198
6611
  exports.CadraLifecycleSource = CadraLifecycleSource;
6612
+ exports.CapMeter = CapMeter;
6199
6613
  exports.CardDrawer = CardDrawer;
6200
6614
  exports.KanbanBoard = KanbanBoard;
6201
6615
  exports.KanbanCardTile = KanbanCard;
6202
6616
  exports.KanbanColumn = KanbanColumn;
6617
+ exports.LaneAgentPicker = LaneAgentPicker;
6618
+ exports.ScheduleStatus = ScheduleStatus;
6619
+ exports.badgeVariantClass = badgeVariantClass;
6620
+ exports.cn = cn9;
6203
6621
  exports.createDefaultKanbanAdapter = createDefaultKanbanAdapter;
6204
6622
  exports.mapDefinitionToBoard = mapDefinitionToBoard;
6623
+ exports.runStatusTone = runStatusTone;
6205
6624
  exports.toResolvedLifecycle = toResolvedLifecycle;
6206
6625
  exports.useCardRun = useCardRun;
6207
6626
  exports.useKanbanBoard = useKanbanBoard;