@personaai/react 0.10.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -2453,20 +2453,38 @@ function useFiles(autoFetch = true) {
2453
2453
 
2454
2454
  // src/hooks/useAgents.ts
2455
2455
  import { useCallback as useCallback7, useEffect as useEffect7, useState as useState7 } from "react";
2456
- function useAgents(autoFetch = true) {
2456
+ function useAgents(options = true) {
2457
+ const opts = typeof options === "boolean" ? { autoFetch: options } : options;
2458
+ const { autoFetch = true, page, limit, search, category, scope } = opts;
2457
2459
  const { fetchWithAuth } = usePersonaContext();
2458
2460
  const [agents, setAgents] = useState7([]);
2461
+ const [pagination, setPagination] = useState7({
2462
+ total: 0,
2463
+ page: page ?? 1,
2464
+ limit: limit ?? 20,
2465
+ pages: 0
2466
+ });
2459
2467
  const [isLoading, setIsLoading] = useState7(false);
2460
2468
  const [error, setError] = useState7(null);
2461
2469
  const fetchAgents = useCallback7(async () => {
2462
2470
  setIsLoading(true);
2463
2471
  setError(null);
2464
2472
  try {
2465
- const res = await fetchWithAuth("/agents");
2473
+ const query = new URLSearchParams();
2474
+ if (page) query.set("page", String(page));
2475
+ if (limit) query.set("limit", String(limit));
2476
+ if (search) query.set("search", search);
2477
+ if (category) query.set("category", category);
2478
+ if (scope) query.set("scope", scope);
2479
+ const queryStr = query.toString() ? `?${query.toString()}` : "";
2480
+ const res = await fetchWithAuth(`/agents${queryStr}`);
2466
2481
  if (!res.ok) throw new Error(`Failed to list agents: ${res.statusText}`);
2467
2482
  const data = await res.json();
2468
- const items = Array.isArray(data) ? data : data?.items || data?.agents || [];
2483
+ const items = Array.isArray(data) ? data : data?.items ?? [];
2469
2484
  setAgents(items);
2485
+ setPagination(
2486
+ data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
2487
+ );
2470
2488
  return items;
2471
2489
  } catch (err) {
2472
2490
  const errorObj = err instanceof Error ? err : new Error(String(err));
@@ -2475,83 +2493,929 @@ function useAgents(autoFetch = true) {
2475
2493
  } finally {
2476
2494
  setIsLoading(false);
2477
2495
  }
2478
- }, [fetchWithAuth]);
2496
+ }, [fetchWithAuth, page, limit, search, category, scope]);
2497
+ const getAgent = useCallback7(
2498
+ async (agentId) => {
2499
+ const res = await fetchWithAuth(`/agents/${agentId}`);
2500
+ if (!res.ok) throw new Error(`Failed to fetch agent: ${res.statusText}`);
2501
+ return res.json();
2502
+ },
2503
+ [fetchWithAuth]
2504
+ );
2505
+ const createAgent = useCallback7(
2506
+ async (input) => {
2507
+ setError(null);
2508
+ try {
2509
+ const res = await fetchWithAuth("/agents", {
2510
+ method: "POST",
2511
+ headers: { "Content-Type": "application/json" },
2512
+ body: JSON.stringify(input)
2513
+ });
2514
+ if (!res.ok) {
2515
+ const errText = await res.text().catch(() => res.statusText);
2516
+ throw new Error(`Failed to create agent (${res.status}): ${errText}`);
2517
+ }
2518
+ const created = await res.json();
2519
+ setAgents((prev) => [created, ...prev]);
2520
+ return created;
2521
+ } catch (err) {
2522
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2523
+ setError(errorObj);
2524
+ throw errorObj;
2525
+ }
2526
+ },
2527
+ [fetchWithAuth]
2528
+ );
2529
+ const updateAgent = useCallback7(
2530
+ async (agentId, input) => {
2531
+ setError(null);
2532
+ try {
2533
+ const res = await fetchWithAuth(`/agents/${agentId}`, {
2534
+ method: "PATCH",
2535
+ headers: { "Content-Type": "application/json" },
2536
+ body: JSON.stringify(input)
2537
+ });
2538
+ if (!res.ok) {
2539
+ const errText = await res.text().catch(() => res.statusText);
2540
+ throw new Error(`Failed to update agent (${res.status}): ${errText}`);
2541
+ }
2542
+ const updated = await res.json();
2543
+ setAgents((prev) => prev.map((a) => a._id === agentId ? updated : a));
2544
+ return updated;
2545
+ } catch (err) {
2546
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2547
+ setError(errorObj);
2548
+ throw errorObj;
2549
+ }
2550
+ },
2551
+ [fetchWithAuth]
2552
+ );
2553
+ const deleteAgent = useCallback7(
2554
+ async (agentId) => {
2555
+ setError(null);
2556
+ try {
2557
+ const res = await fetchWithAuth(`/agents/${agentId}`, { method: "DELETE" });
2558
+ if (!res.ok) {
2559
+ const errText = await res.text().catch(() => res.statusText);
2560
+ throw new Error(`Failed to delete agent (${res.status}): ${errText}`);
2561
+ }
2562
+ setAgents((prev) => prev.filter((a) => a._id !== agentId));
2563
+ } catch (err) {
2564
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2565
+ setError(errorObj);
2566
+ throw errorObj;
2567
+ }
2568
+ },
2569
+ [fetchWithAuth]
2570
+ );
2571
+ const bulkDeleteAgents = useCallback7(
2572
+ async (ids) => {
2573
+ setError(null);
2574
+ try {
2575
+ const res = await fetchWithAuth("/agents/bulk-delete", {
2576
+ method: "POST",
2577
+ headers: { "Content-Type": "application/json" },
2578
+ body: JSON.stringify({ ids })
2579
+ });
2580
+ if (!res.ok) {
2581
+ const errText = await res.text().catch(() => res.statusText);
2582
+ throw new Error(`Failed to bulk-delete agents (${res.status}): ${errText}`);
2583
+ }
2584
+ const result = await res.json();
2585
+ const deletedSet = new Set(result.deleted);
2586
+ setAgents((prev) => prev.filter((a) => !deletedSet.has(a._id)));
2587
+ return result;
2588
+ } catch (err) {
2589
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2590
+ setError(errorObj);
2591
+ throw errorObj;
2592
+ }
2593
+ },
2594
+ [fetchWithAuth]
2595
+ );
2479
2596
  useEffect7(() => {
2480
- if (autoFetch) {
2481
- void fetchAgents();
2482
- }
2597
+ if (autoFetch) void fetchAgents();
2483
2598
  }, [autoFetch, fetchAgents]);
2484
2599
  return {
2485
- agents,
2600
+ agents,
2601
+ pagination,
2602
+ isLoading,
2603
+ error,
2604
+ refetch: fetchAgents,
2605
+ getAgent,
2606
+ createAgent,
2607
+ updateAgent,
2608
+ deleteAgent,
2609
+ bulkDeleteAgents
2610
+ };
2611
+ }
2612
+
2613
+ // src/hooks/useConnection.ts
2614
+ import { useCallback as useCallback8, useEffect as useEffect8, useState as useState8 } from "react";
2615
+ function useConnection(autoCheck = true) {
2616
+ const { fetchWithAuth } = usePersonaContext();
2617
+ const [health, setHealth] = useState8(null);
2618
+ const [isConnected, setIsConnected] = useState8(false);
2619
+ const [isLoading, setIsLoading] = useState8(false);
2620
+ const checkHealth = useCallback8(async () => {
2621
+ setIsLoading(true);
2622
+ try {
2623
+ const res = await fetchWithAuth("/health");
2624
+ if (res.ok) {
2625
+ const data = await res.json();
2626
+ setHealth(data);
2627
+ setIsConnected(true);
2628
+ return data;
2629
+ }
2630
+ setIsConnected(false);
2631
+ return null;
2632
+ } catch {
2633
+ setIsConnected(false);
2634
+ return null;
2635
+ } finally {
2636
+ setIsLoading(false);
2637
+ }
2638
+ }, [fetchWithAuth]);
2639
+ useEffect8(() => {
2640
+ if (autoCheck) {
2641
+ void checkHealth();
2642
+ }
2643
+ }, [autoCheck, checkHealth]);
2644
+ return {
2645
+ isConnected,
2646
+ health,
2647
+ isLoading,
2648
+ checkHealth
2649
+ };
2650
+ }
2651
+
2652
+ // src/hooks/useMcpConnections.ts
2653
+ import { useCallback as useCallback9, useEffect as useEffect9, useState as useState9 } from "react";
2654
+ function useMcpConnections(options = {}) {
2655
+ const { defaultAgentId, fetchWithAuth } = usePersonaContext();
2656
+ const agentId = options.agentId ?? defaultAgentId;
2657
+ const autoFetch = options.autoFetch ?? true;
2658
+ const [connections, setConnections] = useState9([]);
2659
+ const [isLoading, setIsLoading] = useState9(false);
2660
+ const [error, setError] = useState9(null);
2661
+ const fetchConnections = useCallback9(async () => {
2662
+ if (!agentId) return [];
2663
+ setIsLoading(true);
2664
+ setError(null);
2665
+ try {
2666
+ const returnTo = options.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
2667
+ const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : "";
2668
+ const res = await fetchWithAuth(
2669
+ `/agents/${agentId}/mcp-connections${query}`
2670
+ );
2671
+ if (!res.ok)
2672
+ throw new Error(`Failed to load MCP connections: ${res.statusText}`);
2673
+ const data = await res.json();
2674
+ const items = Array.isArray(data) ? data : data?.items || [];
2675
+ setConnections(items);
2676
+ return items;
2677
+ } catch (err) {
2678
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2679
+ setError(errorObj);
2680
+ return [];
2681
+ } finally {
2682
+ setIsLoading(false);
2683
+ }
2684
+ }, [agentId, fetchWithAuth, options.returnTo]);
2685
+ const [isDisconnecting, setIsDisconnecting] = useState9(false);
2686
+ const disconnect = useCallback9(
2687
+ async (mcpId) => {
2688
+ setIsDisconnecting(true);
2689
+ setError(null);
2690
+ try {
2691
+ const res = await fetchWithAuth(`/mcps/${mcpId}/oauth/user/connection`, {
2692
+ method: "DELETE"
2693
+ });
2694
+ if (!res.ok)
2695
+ throw new Error(`Failed to disconnect MCP: ${res.statusText}`);
2696
+ setConnections(
2697
+ (prev) => prev.map(
2698
+ (c) => c.mcpId === mcpId ? { ...c, connected: false, authorizeUrl: null } : c
2699
+ )
2700
+ );
2701
+ void fetchConnections();
2702
+ } catch (err) {
2703
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2704
+ setError(errorObj);
2705
+ throw errorObj;
2706
+ } finally {
2707
+ setIsDisconnecting(false);
2708
+ }
2709
+ },
2710
+ [fetchWithAuth, fetchConnections]
2711
+ );
2712
+ useEffect9(() => {
2713
+ if (autoFetch) void fetchConnections();
2714
+ }, [autoFetch, fetchConnections]);
2715
+ return {
2716
+ connections,
2717
+ /** Convenience filter for the common "show a banner for what's missing" case. */
2718
+ unconnected: connections.filter((c) => !c.connected),
2719
+ isLoading,
2720
+ error,
2721
+ refetch: fetchConnections,
2722
+ disconnect,
2723
+ isDisconnecting
2724
+ };
2725
+ }
2726
+
2727
+ // src/hooks/useMcp.ts
2728
+ import { useCallback as useCallback10 } from "react";
2729
+ function useMcp(options = {}) {
2730
+ const { fetchWithAuth } = usePersonaContext();
2731
+ const defaultMcpId = options.mcpId;
2732
+ const readResource = useCallback10(
2733
+ async (uri, mcpId) => {
2734
+ const id = mcpId ?? defaultMcpId;
2735
+ if (!id) throw new Error("MCP server ID is required to read resource");
2736
+ const res = await fetchWithAuth(
2737
+ `/mcps/${id}/resource?uri=${encodeURIComponent(uri)}`
2738
+ );
2739
+ if (!res.ok) {
2740
+ throw new Error(`Failed to read MCP resource: ${res.statusText}`);
2741
+ }
2742
+ return await res.json();
2743
+ },
2744
+ [fetchWithAuth, defaultMcpId]
2745
+ );
2746
+ const callTool = useCallback10(
2747
+ async (name, args, mcpId) => {
2748
+ const id = mcpId ?? defaultMcpId;
2749
+ if (!id) throw new Error("MCP server ID is required to call tool");
2750
+ const res = await fetchWithAuth(`/mcps/${id}/call-tool`, {
2751
+ method: "POST",
2752
+ headers: { "Content-Type": "application/json" },
2753
+ body: JSON.stringify({ name, arguments: args })
2754
+ });
2755
+ if (!res.ok) {
2756
+ throw new Error(`Failed to call MCP tool: ${res.statusText}`);
2757
+ }
2758
+ return await res.json();
2759
+ },
2760
+ [fetchWithAuth, defaultMcpId]
2761
+ );
2762
+ return {
2763
+ readResource,
2764
+ callTool
2765
+ };
2766
+ }
2767
+
2768
+ // src/hooks/useSkills.ts
2769
+ import { useCallback as useCallback11, useEffect as useEffect10, useState as useState10 } from "react";
2770
+ function useSkills(options = {}) {
2771
+ const { autoFetch = true, page, limit, search, scope } = options;
2772
+ const { fetchWithAuth } = usePersonaContext();
2773
+ const [skills, setSkills] = useState10([]);
2774
+ const [pagination, setPagination] = useState10({
2775
+ total: 0,
2776
+ page: page ?? 1,
2777
+ limit: limit ?? 20,
2778
+ pages: 0
2779
+ });
2780
+ const [isLoading, setIsLoading] = useState10(false);
2781
+ const [error, setError] = useState10(null);
2782
+ const fetchSkills = useCallback11(async () => {
2783
+ setIsLoading(true);
2784
+ setError(null);
2785
+ try {
2786
+ const query = new URLSearchParams();
2787
+ if (page) query.set("page", String(page));
2788
+ if (limit) query.set("limit", String(limit));
2789
+ if (search) query.set("search", search);
2790
+ if (scope) query.set("scope", scope);
2791
+ const queryStr = query.toString() ? `?${query.toString()}` : "";
2792
+ const res = await fetchWithAuth(`/skills${queryStr}`);
2793
+ if (!res.ok) throw new Error(`Failed to list skills: ${res.statusText}`);
2794
+ const data = await res.json();
2795
+ const items = Array.isArray(data) ? data : data?.items ?? [];
2796
+ setSkills(items);
2797
+ setPagination(
2798
+ data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
2799
+ );
2800
+ return items;
2801
+ } catch (err) {
2802
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2803
+ setError(errorObj);
2804
+ return [];
2805
+ } finally {
2806
+ setIsLoading(false);
2807
+ }
2808
+ }, [fetchWithAuth, page, limit, search, scope]);
2809
+ const getSkill = useCallback11(
2810
+ async (skillId) => {
2811
+ const res = await fetchWithAuth(`/skills/${skillId}`);
2812
+ if (!res.ok) throw new Error(`Failed to fetch skill: ${res.statusText}`);
2813
+ return res.json();
2814
+ },
2815
+ [fetchWithAuth]
2816
+ );
2817
+ const createSkill = useCallback11(
2818
+ async (input) => {
2819
+ setError(null);
2820
+ try {
2821
+ const res = await fetchWithAuth("/skills", {
2822
+ method: "POST",
2823
+ headers: { "Content-Type": "application/json" },
2824
+ body: JSON.stringify(input)
2825
+ });
2826
+ if (!res.ok) {
2827
+ const errText = await res.text().catch(() => res.statusText);
2828
+ throw new Error(`Failed to create skill (${res.status}): ${errText}`);
2829
+ }
2830
+ const created = await res.json();
2831
+ setSkills((prev) => [created, ...prev]);
2832
+ return created;
2833
+ } catch (err) {
2834
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2835
+ setError(errorObj);
2836
+ throw errorObj;
2837
+ }
2838
+ },
2839
+ [fetchWithAuth]
2840
+ );
2841
+ const updateSkill = useCallback11(
2842
+ async (skillId, input) => {
2843
+ setError(null);
2844
+ try {
2845
+ const res = await fetchWithAuth(`/skills/${skillId}`, {
2846
+ method: "PATCH",
2847
+ headers: { "Content-Type": "application/json" },
2848
+ body: JSON.stringify(input)
2849
+ });
2850
+ if (!res.ok) {
2851
+ const errText = await res.text().catch(() => res.statusText);
2852
+ throw new Error(`Failed to update skill (${res.status}): ${errText}`);
2853
+ }
2854
+ const updated = await res.json();
2855
+ setSkills((prev) => prev.map((s) => s._id === skillId ? updated : s));
2856
+ return updated;
2857
+ } catch (err) {
2858
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2859
+ setError(errorObj);
2860
+ throw errorObj;
2861
+ }
2862
+ },
2863
+ [fetchWithAuth]
2864
+ );
2865
+ const deleteSkill = useCallback11(
2866
+ async (skillId) => {
2867
+ setError(null);
2868
+ try {
2869
+ const res = await fetchWithAuth(`/skills/${skillId}`, { method: "DELETE" });
2870
+ if (!res.ok) {
2871
+ const errText = await res.text().catch(() => res.statusText);
2872
+ throw new Error(`Failed to delete skill (${res.status}): ${errText}`);
2873
+ }
2874
+ setSkills((prev) => prev.filter((s) => s._id !== skillId));
2875
+ } catch (err) {
2876
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2877
+ setError(errorObj);
2878
+ throw errorObj;
2879
+ }
2880
+ },
2881
+ [fetchWithAuth]
2882
+ );
2883
+ const bulkDeleteSkills = useCallback11(
2884
+ async (ids) => {
2885
+ setError(null);
2886
+ try {
2887
+ const res = await fetchWithAuth("/skills/bulk-delete", {
2888
+ method: "POST",
2889
+ headers: { "Content-Type": "application/json" },
2890
+ body: JSON.stringify({ ids })
2891
+ });
2892
+ if (!res.ok) {
2893
+ const errText = await res.text().catch(() => res.statusText);
2894
+ throw new Error(`Failed to bulk-delete skills (${res.status}): ${errText}`);
2895
+ }
2896
+ const result = await res.json();
2897
+ const deletedSet = new Set(result.deleted);
2898
+ setSkills((prev) => prev.filter((s) => !deletedSet.has(s._id)));
2899
+ return result;
2900
+ } catch (err) {
2901
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2902
+ setError(errorObj);
2903
+ throw errorObj;
2904
+ }
2905
+ },
2906
+ [fetchWithAuth]
2907
+ );
2908
+ const getSkillUsage = useCallback11(
2909
+ async (skillId) => {
2910
+ const res = await fetchWithAuth(`/skills/${skillId}/usage`);
2911
+ if (!res.ok) throw new Error(`Failed to fetch skill usage: ${res.statusText}`);
2912
+ return res.json();
2913
+ },
2914
+ [fetchWithAuth]
2915
+ );
2916
+ useEffect10(() => {
2917
+ if (autoFetch) void fetchSkills();
2918
+ }, [autoFetch, fetchSkills]);
2919
+ return {
2920
+ skills,
2921
+ pagination,
2922
+ isLoading,
2923
+ error,
2924
+ refetch: fetchSkills,
2925
+ getSkill,
2926
+ createSkill,
2927
+ updateSkill,
2928
+ deleteSkill,
2929
+ bulkDeleteSkills,
2930
+ getSkillUsage
2931
+ };
2932
+ }
2933
+
2934
+ // src/hooks/useRcpSources.ts
2935
+ import { useCallback as useCallback12, useEffect as useEffect11, useState as useState11 } from "react";
2936
+ function useRcpSources(options = {}) {
2937
+ const { autoFetch = true, page, limit, search } = options;
2938
+ const { fetchWithAuth } = usePersonaContext();
2939
+ const [rcpSources, setRcpSources] = useState11([]);
2940
+ const [pagination, setPagination] = useState11({
2941
+ total: 0,
2942
+ page: page ?? 1,
2943
+ limit: limit ?? 20,
2944
+ pages: 0
2945
+ });
2946
+ const [isLoading, setIsLoading] = useState11(false);
2947
+ const [error, setError] = useState11(null);
2948
+ const fetchRcpSources = useCallback12(async () => {
2949
+ setIsLoading(true);
2950
+ setError(null);
2951
+ try {
2952
+ const query = new URLSearchParams();
2953
+ if (page) query.set("page", String(page));
2954
+ if (limit) query.set("limit", String(limit));
2955
+ if (search) query.set("search", search);
2956
+ const queryStr = query.toString() ? `?${query.toString()}` : "";
2957
+ const res = await fetchWithAuth(`/rcp-sources${queryStr}`);
2958
+ if (!res.ok) throw new Error(`Failed to list RCP sources: ${res.statusText}`);
2959
+ const data = await res.json();
2960
+ const items = Array.isArray(data) ? data : data?.items ?? [];
2961
+ setRcpSources(items);
2962
+ setPagination(
2963
+ data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
2964
+ );
2965
+ return items;
2966
+ } catch (err) {
2967
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2968
+ setError(errorObj);
2969
+ return [];
2970
+ } finally {
2971
+ setIsLoading(false);
2972
+ }
2973
+ }, [fetchWithAuth, page, limit, search]);
2974
+ const getRcpSource = useCallback12(
2975
+ async (sourceId) => {
2976
+ const res = await fetchWithAuth(`/rcp-sources/${sourceId}`);
2977
+ if (!res.ok) throw new Error(`Failed to fetch RCP source: ${res.statusText}`);
2978
+ return res.json();
2979
+ },
2980
+ [fetchWithAuth]
2981
+ );
2982
+ const createRcpSource = useCallback12(
2983
+ async (input) => {
2984
+ setError(null);
2985
+ try {
2986
+ const res = await fetchWithAuth("/rcp-sources", {
2987
+ method: "POST",
2988
+ headers: { "Content-Type": "application/json" },
2989
+ body: JSON.stringify(input)
2990
+ });
2991
+ if (!res.ok) {
2992
+ const errText = await res.text().catch(() => res.statusText);
2993
+ throw new Error(`Failed to create RCP source (${res.status}): ${errText}`);
2994
+ }
2995
+ const created = await res.json();
2996
+ setRcpSources((prev) => [created, ...prev]);
2997
+ return created;
2998
+ } catch (err) {
2999
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3000
+ setError(errorObj);
3001
+ throw errorObj;
3002
+ }
3003
+ },
3004
+ [fetchWithAuth]
3005
+ );
3006
+ const updateRcpSource = useCallback12(
3007
+ async (sourceId, input) => {
3008
+ setError(null);
3009
+ try {
3010
+ const res = await fetchWithAuth(`/rcp-sources/${sourceId}`, {
3011
+ method: "PATCH",
3012
+ headers: { "Content-Type": "application/json" },
3013
+ body: JSON.stringify(input)
3014
+ });
3015
+ if (!res.ok) {
3016
+ const errText = await res.text().catch(() => res.statusText);
3017
+ throw new Error(`Failed to update RCP source (${res.status}): ${errText}`);
3018
+ }
3019
+ const updated = await res.json();
3020
+ setRcpSources((prev) => prev.map((s) => s._id === sourceId ? updated : s));
3021
+ return updated;
3022
+ } catch (err) {
3023
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3024
+ setError(errorObj);
3025
+ throw errorObj;
3026
+ }
3027
+ },
3028
+ [fetchWithAuth]
3029
+ );
3030
+ const deleteRcpSource = useCallback12(
3031
+ async (sourceId) => {
3032
+ setError(null);
3033
+ try {
3034
+ const res = await fetchWithAuth(`/rcp-sources/${sourceId}`, { method: "DELETE" });
3035
+ if (!res.ok) {
3036
+ const errText = await res.text().catch(() => res.statusText);
3037
+ throw new Error(`Failed to delete RCP source (${res.status}): ${errText}`);
3038
+ }
3039
+ setRcpSources((prev) => prev.filter((s) => s._id !== sourceId));
3040
+ } catch (err) {
3041
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3042
+ setError(errorObj);
3043
+ throw errorObj;
3044
+ }
3045
+ },
3046
+ [fetchWithAuth]
3047
+ );
3048
+ const bulkDeleteRcpSources = useCallback12(
3049
+ async (ids) => {
3050
+ setError(null);
3051
+ try {
3052
+ const res = await fetchWithAuth("/rcp-sources/bulk-delete", {
3053
+ method: "POST",
3054
+ headers: { "Content-Type": "application/json" },
3055
+ body: JSON.stringify({ ids })
3056
+ });
3057
+ if (!res.ok) {
3058
+ const errText = await res.text().catch(() => res.statusText);
3059
+ throw new Error(`Failed to bulk-delete RCP sources (${res.status}): ${errText}`);
3060
+ }
3061
+ const result = await res.json();
3062
+ const deletedSet = new Set(result.deleted);
3063
+ setRcpSources((prev) => prev.filter((s) => !deletedSet.has(s._id)));
3064
+ return result;
3065
+ } catch (err) {
3066
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3067
+ setError(errorObj);
3068
+ throw errorObj;
3069
+ }
3070
+ },
3071
+ [fetchWithAuth]
3072
+ );
3073
+ const getRcpSourceUsage = useCallback12(
3074
+ async (sourceId) => {
3075
+ const res = await fetchWithAuth(`/rcp-sources/${sourceId}/usage`);
3076
+ if (!res.ok) throw new Error(`Failed to fetch RCP source usage: ${res.statusText}`);
3077
+ return res.json();
3078
+ },
3079
+ [fetchWithAuth]
3080
+ );
3081
+ const testRcpSourceConnection = useCallback12(
3082
+ async (sourceId) => {
3083
+ setError(null);
3084
+ try {
3085
+ const res = await fetchWithAuth(`/rcp-sources/${sourceId}/test`, { method: "POST" });
3086
+ if (!res.ok) {
3087
+ const errText = await res.text().catch(() => res.statusText);
3088
+ throw new Error(`Failed to test RCP source connection (${res.status}): ${errText}`);
3089
+ }
3090
+ const result = await res.json();
3091
+ setRcpSources(
3092
+ (prev) => prev.map(
3093
+ (s) => s._id === sourceId ? { ...s, tools: result.tools, lastTestedAt: (/* @__PURE__ */ new Date()).toISOString() } : s
3094
+ )
3095
+ );
3096
+ return result;
3097
+ } catch (err) {
3098
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3099
+ setError(errorObj);
3100
+ throw errorObj;
3101
+ }
3102
+ },
3103
+ [fetchWithAuth]
3104
+ );
3105
+ useEffect11(() => {
3106
+ if (autoFetch) void fetchRcpSources();
3107
+ }, [autoFetch, fetchRcpSources]);
3108
+ return {
3109
+ rcpSources,
3110
+ pagination,
3111
+ isLoading,
3112
+ error,
3113
+ refetch: fetchRcpSources,
3114
+ getRcpSource,
3115
+ createRcpSource,
3116
+ updateRcpSource,
3117
+ deleteRcpSource,
3118
+ bulkDeleteRcpSources,
3119
+ getRcpSourceUsage,
3120
+ testRcpSourceConnection
3121
+ };
3122
+ }
3123
+
3124
+ // src/hooks/useKnowledgeBases.ts
3125
+ import { useCallback as useCallback13, useEffect as useEffect12, useState as useState12 } from "react";
3126
+ function useKnowledgeBases(options = {}) {
3127
+ const { autoFetch = true, page, limit, search, scope } = options;
3128
+ const { fetchWithAuth } = usePersonaContext();
3129
+ const [knowledgeBases, setKnowledgeBases] = useState12([]);
3130
+ const [pagination, setPagination] = useState12({
3131
+ total: 0,
3132
+ page: page ?? 1,
3133
+ limit: limit ?? 20,
3134
+ pages: 0
3135
+ });
3136
+ const [isLoading, setIsLoading] = useState12(false);
3137
+ const [isUploading, setIsUploading] = useState12(false);
3138
+ const [error, setError] = useState12(null);
3139
+ const fetchKnowledgeBases = useCallback13(async () => {
3140
+ setIsLoading(true);
3141
+ setError(null);
3142
+ try {
3143
+ const query = new URLSearchParams();
3144
+ if (page) query.set("page", String(page));
3145
+ if (limit) query.set("limit", String(limit));
3146
+ if (search) query.set("search", search);
3147
+ if (scope) query.set("scope", scope);
3148
+ const queryStr = query.toString() ? `?${query.toString()}` : "";
3149
+ const res = await fetchWithAuth(`/knowledge${queryStr}`);
3150
+ if (!res.ok) throw new Error(`Failed to list knowledge bases: ${res.statusText}`);
3151
+ const data = await res.json();
3152
+ const items = Array.isArray(data) ? data : data?.items ?? [];
3153
+ setKnowledgeBases(items);
3154
+ setPagination(
3155
+ data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
3156
+ );
3157
+ return items;
3158
+ } catch (err) {
3159
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3160
+ setError(errorObj);
3161
+ return [];
3162
+ } finally {
3163
+ setIsLoading(false);
3164
+ }
3165
+ }, [fetchWithAuth, page, limit, search, scope]);
3166
+ const getKnowledgeBase = useCallback13(
3167
+ async (kbId) => {
3168
+ const res = await fetchWithAuth(`/knowledge/${kbId}`);
3169
+ if (!res.ok) throw new Error(`Failed to fetch knowledge base: ${res.statusText}`);
3170
+ return res.json();
3171
+ },
3172
+ [fetchWithAuth]
3173
+ );
3174
+ const createKnowledgeBase = useCallback13(
3175
+ async (input) => {
3176
+ setError(null);
3177
+ try {
3178
+ const res = await fetchWithAuth("/knowledge", {
3179
+ method: "POST",
3180
+ headers: { "Content-Type": "application/json" },
3181
+ body: JSON.stringify(input)
3182
+ });
3183
+ if (!res.ok) {
3184
+ const errText = await res.text().catch(() => res.statusText);
3185
+ throw new Error(`Failed to create knowledge base (${res.status}): ${errText}`);
3186
+ }
3187
+ const created = await res.json();
3188
+ setKnowledgeBases((prev) => [created, ...prev]);
3189
+ return created;
3190
+ } catch (err) {
3191
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3192
+ setError(errorObj);
3193
+ throw errorObj;
3194
+ }
3195
+ },
3196
+ [fetchWithAuth]
3197
+ );
3198
+ const updateKnowledgeBase = useCallback13(
3199
+ async (kbId, input) => {
3200
+ setError(null);
3201
+ try {
3202
+ const res = await fetchWithAuth(`/knowledge/${kbId}`, {
3203
+ method: "PATCH",
3204
+ headers: { "Content-Type": "application/json" },
3205
+ body: JSON.stringify(input)
3206
+ });
3207
+ if (!res.ok) {
3208
+ const errText = await res.text().catch(() => res.statusText);
3209
+ throw new Error(`Failed to update knowledge base (${res.status}): ${errText}`);
3210
+ }
3211
+ const updated = await res.json();
3212
+ setKnowledgeBases((prev) => prev.map((kb) => kb._id === kbId ? updated : kb));
3213
+ return updated;
3214
+ } catch (err) {
3215
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3216
+ setError(errorObj);
3217
+ throw errorObj;
3218
+ }
3219
+ },
3220
+ [fetchWithAuth]
3221
+ );
3222
+ const deleteKnowledgeBase = useCallback13(
3223
+ async (kbId) => {
3224
+ setError(null);
3225
+ try {
3226
+ const res = await fetchWithAuth(`/knowledge/${kbId}`, { method: "DELETE" });
3227
+ if (!res.ok) {
3228
+ const errText = await res.text().catch(() => res.statusText);
3229
+ throw new Error(`Failed to delete knowledge base (${res.status}): ${errText}`);
3230
+ }
3231
+ setKnowledgeBases((prev) => prev.filter((kb) => kb._id !== kbId));
3232
+ } catch (err) {
3233
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3234
+ setError(errorObj);
3235
+ throw errorObj;
3236
+ }
3237
+ },
3238
+ [fetchWithAuth]
3239
+ );
3240
+ const bulkDeleteKnowledgeBases = useCallback13(
3241
+ async (ids) => {
3242
+ setError(null);
3243
+ try {
3244
+ const res = await fetchWithAuth("/knowledge/bulk-delete", {
3245
+ method: "POST",
3246
+ headers: { "Content-Type": "application/json" },
3247
+ body: JSON.stringify({ ids })
3248
+ });
3249
+ if (!res.ok) {
3250
+ const errText = await res.text().catch(() => res.statusText);
3251
+ throw new Error(`Failed to bulk-delete knowledge bases (${res.status}): ${errText}`);
3252
+ }
3253
+ const result = await res.json();
3254
+ const deletedSet = new Set(result.deleted);
3255
+ setKnowledgeBases((prev) => prev.filter((kb) => !deletedSet.has(kb._id)));
3256
+ return result;
3257
+ } catch (err) {
3258
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3259
+ setError(errorObj);
3260
+ throw errorObj;
3261
+ }
3262
+ },
3263
+ [fetchWithAuth]
3264
+ );
3265
+ const getKnowledgeBaseUsage = useCallback13(
3266
+ async (kbId) => {
3267
+ const res = await fetchWithAuth(`/knowledge/${kbId}/usage`);
3268
+ if (!res.ok) throw new Error(`Failed to fetch knowledge base usage: ${res.statusText}`);
3269
+ return res.json();
3270
+ },
3271
+ [fetchWithAuth]
3272
+ );
3273
+ const uploadDocuments = useCallback13(
3274
+ async (kbId, files) => {
3275
+ setIsUploading(true);
3276
+ setError(null);
3277
+ try {
3278
+ const body = new FormData();
3279
+ for (const file of files) {
3280
+ if (file.content instanceof Blob) {
3281
+ body.append("files", file.content, file.filename);
3282
+ } else {
3283
+ body.append("files", {
3284
+ uri: file.content.uri,
3285
+ name: file.content.name ?? file.filename,
3286
+ type: file.content.type ?? file.contentType
3287
+ });
3288
+ }
3289
+ }
3290
+ const res = await fetchWithAuth(`/knowledge/${kbId}/documents`, {
3291
+ method: "POST",
3292
+ body
3293
+ });
3294
+ if (!res.ok) {
3295
+ const errText = await res.text().catch(() => res.statusText);
3296
+ throw new Error(`Failed to upload documents (${res.status}): ${errText}`);
3297
+ }
3298
+ const result = await res.json();
3299
+ setKnowledgeBases(
3300
+ (prev) => prev.map(
3301
+ (kb) => kb._id === kbId ? { ...kb, documentCount: result.documentCount, chunkCount: result.chunkCount } : kb
3302
+ )
3303
+ );
3304
+ return result;
3305
+ } catch (err) {
3306
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3307
+ setError(errorObj);
3308
+ throw errorObj;
3309
+ } finally {
3310
+ setIsUploading(false);
3311
+ }
3312
+ },
3313
+ [fetchWithAuth]
3314
+ );
3315
+ const listDocuments = useCallback13(
3316
+ async (kbId) => {
3317
+ const res = await fetchWithAuth(`/knowledge/${kbId}/documents`);
3318
+ if (!res.ok) throw new Error(`Failed to list documents: ${res.statusText}`);
3319
+ return res.json();
3320
+ },
3321
+ [fetchWithAuth]
3322
+ );
3323
+ const deleteDocument = useCallback13(
3324
+ async (kbId, sourceName) => {
3325
+ setError(null);
3326
+ try {
3327
+ const res = await fetchWithAuth(
3328
+ `/knowledge/${kbId}/documents/${encodeURIComponent(sourceName)}`,
3329
+ { method: "DELETE" }
3330
+ );
3331
+ if (!res.ok) {
3332
+ const errText = await res.text().catch(() => res.statusText);
3333
+ throw new Error(`Failed to delete document (${res.status}): ${errText}`);
3334
+ }
3335
+ const result = await res.json();
3336
+ setKnowledgeBases(
3337
+ (prev) => prev.map(
3338
+ (kb) => kb._id === kbId ? { ...kb, documentCount: result.remainingDocuments, chunkCount: result.remainingChunks } : kb
3339
+ )
3340
+ );
3341
+ return result;
3342
+ } catch (err) {
3343
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3344
+ setError(errorObj);
3345
+ throw errorObj;
3346
+ }
3347
+ },
3348
+ [fetchWithAuth]
3349
+ );
3350
+ const searchKnowledgeBase = useCallback13(
3351
+ async (kbId, query, opts) => {
3352
+ const res = await fetchWithAuth(`/knowledge/${kbId}/search`, {
3353
+ method: "POST",
3354
+ headers: { "Content-Type": "application/json" },
3355
+ body: JSON.stringify({ query, topK: opts?.topK })
3356
+ });
3357
+ if (!res.ok) {
3358
+ const errText = await res.text().catch(() => res.statusText);
3359
+ throw new Error(`Failed to search knowledge base (${res.status}): ${errText}`);
3360
+ }
3361
+ return res.json();
3362
+ },
3363
+ [fetchWithAuth]
3364
+ );
3365
+ useEffect12(() => {
3366
+ if (autoFetch) void fetchKnowledgeBases();
3367
+ }, [autoFetch, fetchKnowledgeBases]);
3368
+ return {
3369
+ knowledgeBases,
3370
+ pagination,
2486
3371
  isLoading,
3372
+ isUploading,
2487
3373
  error,
2488
- refetch: fetchAgents
3374
+ refetch: fetchKnowledgeBases,
3375
+ getKnowledgeBase,
3376
+ createKnowledgeBase,
3377
+ updateKnowledgeBase,
3378
+ deleteKnowledgeBase,
3379
+ bulkDeleteKnowledgeBases,
3380
+ getKnowledgeBaseUsage,
3381
+ uploadDocuments,
3382
+ listDocuments,
3383
+ deleteDocument,
3384
+ search: searchKnowledgeBase
2489
3385
  };
2490
3386
  }
2491
3387
 
2492
- // src/hooks/useConnection.ts
2493
- import { useCallback as useCallback8, useEffect as useEffect8, useState as useState8 } from "react";
2494
- function useConnection(autoCheck = true) {
3388
+ // src/hooks/useMcpAdmin.ts
3389
+ import { useCallback as useCallback14, useEffect as useEffect13, useState as useState13 } from "react";
3390
+ function useMcpAdmin(options = {}) {
3391
+ const { autoFetch = true, page, limit, search } = options;
2495
3392
  const { fetchWithAuth } = usePersonaContext();
2496
- const [health, setHealth] = useState8(null);
2497
- const [isConnected, setIsConnected] = useState8(false);
2498
- const [isLoading, setIsLoading] = useState8(false);
2499
- const checkHealth = useCallback8(async () => {
2500
- setIsLoading(true);
2501
- try {
2502
- const res = await fetchWithAuth("/health");
2503
- if (res.ok) {
2504
- const data = await res.json();
2505
- setHealth(data);
2506
- setIsConnected(true);
2507
- return data;
2508
- }
2509
- setIsConnected(false);
2510
- return null;
2511
- } catch {
2512
- setIsConnected(false);
2513
- return null;
2514
- } finally {
2515
- setIsLoading(false);
2516
- }
2517
- }, [fetchWithAuth]);
2518
- useEffect8(() => {
2519
- if (autoCheck) {
2520
- void checkHealth();
2521
- }
2522
- }, [autoCheck, checkHealth]);
2523
- return {
2524
- isConnected,
2525
- health,
2526
- isLoading,
2527
- checkHealth
2528
- };
2529
- }
2530
-
2531
- // src/hooks/useMcpConnections.ts
2532
- import { useCallback as useCallback9, useEffect as useEffect9, useState as useState9 } from "react";
2533
- function useMcpConnections(options = {}) {
2534
- const { defaultAgentId, fetchWithAuth } = usePersonaContext();
2535
- const agentId = options.agentId ?? defaultAgentId;
2536
- const autoFetch = options.autoFetch ?? true;
2537
- const [connections, setConnections] = useState9([]);
2538
- const [isLoading, setIsLoading] = useState9(false);
2539
- const [error, setError] = useState9(null);
2540
- const fetchConnections = useCallback9(async () => {
2541
- if (!agentId) return [];
3393
+ const [mcps, setMcps] = useState13([]);
3394
+ const [pagination, setPagination] = useState13({
3395
+ total: 0,
3396
+ page: page ?? 1,
3397
+ limit: limit ?? 20,
3398
+ pages: 0
3399
+ });
3400
+ const [isLoading, setIsLoading] = useState13(false);
3401
+ const [error, setError] = useState13(null);
3402
+ const fetchMcps = useCallback14(async () => {
2542
3403
  setIsLoading(true);
2543
3404
  setError(null);
2544
3405
  try {
2545
- const returnTo = options.returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
2546
- const query = returnTo ? `?returnTo=${encodeURIComponent(returnTo)}` : "";
2547
- const res = await fetchWithAuth(
2548
- `/agents/${agentId}/mcp-connections${query}`
2549
- );
2550
- if (!res.ok)
2551
- throw new Error(`Failed to load MCP connections: ${res.statusText}`);
3406
+ const query = new URLSearchParams();
3407
+ if (page) query.set("page", String(page));
3408
+ if (limit) query.set("limit", String(limit));
3409
+ if (search) query.set("search", search);
3410
+ query.set("scope", "mine");
3411
+ const res = await fetchWithAuth(`/mcps?${query.toString()}`);
3412
+ if (!res.ok) throw new Error(`Failed to list MCPs: ${res.statusText}`);
2552
3413
  const data = await res.json();
2553
- const items = Array.isArray(data) ? data : data?.items || [];
2554
- setConnections(items);
3414
+ const items = Array.isArray(data) ? data : data?.items ?? [];
3415
+ setMcps(items);
3416
+ setPagination(
3417
+ data?.pagination ?? { total: items.length, page: 1, limit: items.length, pages: 1 }
3418
+ );
2555
3419
  return items;
2556
3420
  } catch (err) {
2557
3421
  const errorObj = err instanceof Error ? err : new Error(String(err));
@@ -2560,83 +3424,172 @@ function useMcpConnections(options = {}) {
2560
3424
  } finally {
2561
3425
  setIsLoading(false);
2562
3426
  }
2563
- }, [agentId, fetchWithAuth, options.returnTo]);
2564
- useEffect9(() => {
2565
- if (autoFetch) void fetchConnections();
2566
- }, [autoFetch, fetchConnections]);
2567
- return {
2568
- connections,
2569
- /** Convenience filter for the common "show a banner for what's missing" case. */
2570
- unconnected: connections.filter((c) => !c.connected),
2571
- isLoading,
2572
- error,
2573
- refetch: fetchConnections
2574
- };
2575
- }
2576
-
2577
- // src/hooks/useMcp.ts
2578
- import { useCallback as useCallback10 } from "react";
2579
- function useMcp(options = {}) {
2580
- const { fetchWithAuth } = usePersonaContext();
2581
- const defaultMcpId = options.mcpId;
2582
- const readResource = useCallback10(
2583
- async (uri, mcpId) => {
2584
- const id = mcpId ?? defaultMcpId;
2585
- if (!id) throw new Error("MCP server ID is required to read resource");
2586
- const res = await fetchWithAuth(
2587
- `/mcps/${id}/resource?uri=${encodeURIComponent(uri)}`
2588
- );
2589
- if (!res.ok) {
2590
- throw new Error(`Failed to read MCP resource: ${res.statusText}`);
3427
+ }, [fetchWithAuth, page, limit, search]);
3428
+ const getMcp = useCallback14(
3429
+ async (mcpId) => {
3430
+ const res = await fetchWithAuth(`/mcps/${mcpId}`);
3431
+ if (!res.ok) throw new Error(`Failed to fetch MCP: ${res.statusText}`);
3432
+ return res.json();
3433
+ },
3434
+ [fetchWithAuth]
3435
+ );
3436
+ const createMcp = useCallback14(
3437
+ async (input) => {
3438
+ setError(null);
3439
+ try {
3440
+ const res = await fetchWithAuth("/mcps", {
3441
+ method: "POST",
3442
+ headers: { "Content-Type": "application/json" },
3443
+ body: JSON.stringify(input)
3444
+ });
3445
+ if (!res.ok) {
3446
+ const errText = await res.text().catch(() => res.statusText);
3447
+ throw new Error(`Failed to create MCP (${res.status}): ${errText}`);
3448
+ }
3449
+ const created = await res.json();
3450
+ setMcps((prev) => [created, ...prev]);
3451
+ return created;
3452
+ } catch (err) {
3453
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3454
+ setError(errorObj);
3455
+ throw errorObj;
2591
3456
  }
2592
- return await res.json();
2593
3457
  },
2594
- [fetchWithAuth, defaultMcpId]
3458
+ [fetchWithAuth]
2595
3459
  );
2596
- const callTool = useCallback10(
2597
- async (name, args, mcpId) => {
2598
- const id = mcpId ?? defaultMcpId;
2599
- if (!id) throw new Error("MCP server ID is required to call tool");
2600
- const res = await fetchWithAuth(`/mcps/${id}/call-tool`, {
2601
- method: "POST",
2602
- headers: { "Content-Type": "application/json" },
2603
- body: JSON.stringify({ name, arguments: args })
2604
- });
2605
- if (!res.ok) {
2606
- throw new Error(`Failed to call MCP tool: ${res.statusText}`);
3460
+ const updateMcp = useCallback14(
3461
+ async (mcpId, input) => {
3462
+ setError(null);
3463
+ try {
3464
+ const res = await fetchWithAuth(`/mcps/${mcpId}`, {
3465
+ method: "PATCH",
3466
+ headers: { "Content-Type": "application/json" },
3467
+ body: JSON.stringify(input)
3468
+ });
3469
+ if (!res.ok) {
3470
+ const errText = await res.text().catch(() => res.statusText);
3471
+ throw new Error(`Failed to update MCP (${res.status}): ${errText}`);
3472
+ }
3473
+ const updated = await res.json();
3474
+ setMcps((prev) => prev.map((m) => m._id === mcpId ? updated : m));
3475
+ return updated;
3476
+ } catch (err) {
3477
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3478
+ setError(errorObj);
3479
+ throw errorObj;
2607
3480
  }
2608
- return await res.json();
2609
3481
  },
2610
- [fetchWithAuth, defaultMcpId]
3482
+ [fetchWithAuth]
3483
+ );
3484
+ const deleteMcp = useCallback14(
3485
+ async (mcpId) => {
3486
+ setError(null);
3487
+ try {
3488
+ const res = await fetchWithAuth(`/mcps/${mcpId}`, { method: "DELETE" });
3489
+ if (!res.ok) {
3490
+ const errText = await res.text().catch(() => res.statusText);
3491
+ throw new Error(`Failed to delete MCP (${res.status}): ${errText}`);
3492
+ }
3493
+ setMcps((prev) => prev.filter((m) => m._id !== mcpId));
3494
+ } catch (err) {
3495
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3496
+ setError(errorObj);
3497
+ throw errorObj;
3498
+ }
3499
+ },
3500
+ [fetchWithAuth]
3501
+ );
3502
+ const bulkDeleteMcps = useCallback14(
3503
+ async (ids) => {
3504
+ setError(null);
3505
+ try {
3506
+ const res = await fetchWithAuth("/mcps/bulk-delete", {
3507
+ method: "POST",
3508
+ headers: { "Content-Type": "application/json" },
3509
+ body: JSON.stringify({ ids })
3510
+ });
3511
+ if (!res.ok) {
3512
+ const errText = await res.text().catch(() => res.statusText);
3513
+ throw new Error(`Failed to bulk-delete MCPs (${res.status}): ${errText}`);
3514
+ }
3515
+ const result = await res.json();
3516
+ const deletedSet = new Set(result.deleted);
3517
+ setMcps((prev) => prev.filter((m) => !deletedSet.has(m._id)));
3518
+ return result;
3519
+ } catch (err) {
3520
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3521
+ setError(errorObj);
3522
+ throw errorObj;
3523
+ }
3524
+ },
3525
+ [fetchWithAuth]
3526
+ );
3527
+ const testConnection = useCallback14(
3528
+ async (mcpId) => {
3529
+ setError(null);
3530
+ try {
3531
+ const res = await fetchWithAuth(`/mcps/${mcpId}/test`, { method: "POST" });
3532
+ if (!res.ok) {
3533
+ const errText = await res.text().catch(() => res.statusText);
3534
+ throw new Error(`Failed to test MCP connection (${res.status}): ${errText}`);
3535
+ }
3536
+ const result = await res.json();
3537
+ setMcps(
3538
+ (prev) => prev.map(
3539
+ (m) => m._id === mcpId ? { ...m, tools: result.tools, resources: result.resources, resourceTemplates: result.resourceTemplates } : m
3540
+ )
3541
+ );
3542
+ return result;
3543
+ } catch (err) {
3544
+ const errorObj = err instanceof Error ? err : new Error(String(err));
3545
+ setError(errorObj);
3546
+ throw errorObj;
3547
+ }
3548
+ },
3549
+ [fetchWithAuth]
2611
3550
  );
3551
+ useEffect13(() => {
3552
+ if (autoFetch) void fetchMcps();
3553
+ }, [autoFetch, fetchMcps]);
2612
3554
  return {
2613
- readResource,
2614
- callTool
3555
+ mcps,
3556
+ pagination,
3557
+ isLoading,
3558
+ error,
3559
+ refetch: fetchMcps,
3560
+ getMcp,
3561
+ createMcp,
3562
+ updateMcp,
3563
+ deleteMcp,
3564
+ bulkDeleteMcps,
3565
+ testConnection
2615
3566
  };
2616
3567
  }
2617
3568
 
2618
3569
  // src/hooks/useWorkflows.ts
2619
- import { useCallback as useCallback11, useEffect as useEffect10, useState as useState10 } from "react";
3570
+ import { useCallback as useCallback15, useEffect as useEffect14, useState as useState14 } from "react";
2620
3571
  function useWorkflows(options) {
2621
3572
  const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
2622
- const { autoFetch = true, search, status, page, limit } = opts;
3573
+ const { autoFetch = true, search, scope, visibility, isEnabled, page, limit } = opts;
2623
3574
  const { fetchWithAuth } = usePersonaContext();
2624
- const [workflows, setWorkflows] = useState10([]);
2625
- const [pagination, setPagination] = useState10({
3575
+ const [workflows, setWorkflows] = useState14([]);
3576
+ const [pagination, setPagination] = useState14({
2626
3577
  page: page ?? 1,
2627
3578
  limit: limit ?? 20,
2628
3579
  total: 0,
2629
3580
  totalPages: 0
2630
3581
  });
2631
- const [isLoading, setIsLoading] = useState10(false);
2632
- const [error, setError] = useState10(null);
2633
- const fetchWorkflows = useCallback11(async () => {
3582
+ const [isLoading, setIsLoading] = useState14(false);
3583
+ const [error, setError] = useState14(null);
3584
+ const fetchWorkflows = useCallback15(async () => {
2634
3585
  setIsLoading(true);
2635
3586
  setError(null);
2636
3587
  try {
2637
3588
  const params = new URLSearchParams();
2638
3589
  if (search) params.set("search", search);
2639
- if (status) params.set("status", status);
3590
+ if (scope) params.set("scope", scope);
3591
+ if (visibility) params.set("visibility", visibility);
3592
+ if (isEnabled !== void 0) params.set("isEnabled", String(isEnabled));
2640
3593
  if (page) params.set("page", String(page));
2641
3594
  if (limit) params.set("limit", String(limit));
2642
3595
  const queryStr = params.toString() ? `?${params.toString()}` : "";
@@ -2645,14 +3598,17 @@ function useWorkflows(options) {
2645
3598
  throw new Error(`Failed to list workflows: ${res.statusText}`);
2646
3599
  }
2647
3600
  const data = await res.json();
2648
- const items = Array.isArray(data) ? data : data?.items || data?.workflows || [];
3601
+ const items = Array.isArray(data) ? data : data?.items ?? [];
2649
3602
  setWorkflows(items);
2650
3603
  if (data?.pagination) {
2651
3604
  setPagination({
2652
3605
  page: data.pagination.page ?? 1,
2653
3606
  limit: data.pagination.limit ?? items.length,
2654
3607
  total: data.pagination.total ?? items.length,
2655
- totalPages: data.pagination.totalPages ?? 1
3608
+ // Backend's paginationEnvelope() names this field `pages`, not
3609
+ // `totalPages` — this used to always fall through to the `?? 1`
3610
+ // default.
3611
+ totalPages: data.pagination.pages ?? 1
2656
3612
  });
2657
3613
  } else {
2658
3614
  setPagination({
@@ -2670,8 +3626,8 @@ function useWorkflows(options) {
2670
3626
  } finally {
2671
3627
  setIsLoading(false);
2672
3628
  }
2673
- }, [fetchWithAuth, search, status, page, limit]);
2674
- const createWorkflow = useCallback11(
3629
+ }, [fetchWithAuth, search, scope, visibility, isEnabled, page, limit]);
3630
+ const createWorkflow = useCallback15(
2675
3631
  async (input) => {
2676
3632
  setError(null);
2677
3633
  try {
@@ -2695,7 +3651,7 @@ function useWorkflows(options) {
2695
3651
  },
2696
3652
  [fetchWithAuth]
2697
3653
  );
2698
- useEffect10(() => {
3654
+ useEffect14(() => {
2699
3655
  if (autoFetch) {
2700
3656
  void fetchWorkflows();
2701
3657
  }
@@ -2711,17 +3667,17 @@ function useWorkflows(options) {
2711
3667
  }
2712
3668
 
2713
3669
  // src/hooks/useWorkflow.ts
2714
- import { useCallback as useCallback12, useEffect as useEffect11, useState as useState11 } from "react";
3670
+ import { useCallback as useCallback16, useEffect as useEffect15, useState as useState15 } from "react";
2715
3671
  function useWorkflow(workflowId, options) {
2716
3672
  const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
2717
3673
  const { autoFetch = true } = opts;
2718
3674
  const { fetchWithAuth } = usePersonaContext();
2719
- const [workflow, setWorkflow] = useState11(null);
2720
- const [versions, setVersions] = useState11([]);
2721
- const [mermaid, setMermaid] = useState11(null);
2722
- const [isLoading, setIsLoading] = useState11(false);
2723
- const [error, setError] = useState11(null);
2724
- const fetchWorkflow = useCallback12(async () => {
3675
+ const [workflow, setWorkflow] = useState15(null);
3676
+ const [versions, setVersions] = useState15([]);
3677
+ const [mermaid, setMermaid] = useState15(null);
3678
+ const [isLoading, setIsLoading] = useState15(false);
3679
+ const [error, setError] = useState15(null);
3680
+ const fetchWorkflow = useCallback16(async () => {
2725
3681
  if (!workflowId) return null;
2726
3682
  setIsLoading(true);
2727
3683
  setError(null);
@@ -2741,20 +3697,31 @@ function useWorkflow(workflowId, options) {
2741
3697
  setIsLoading(false);
2742
3698
  }
2743
3699
  }, [fetchWithAuth, workflowId]);
2744
- const fetchVersions = useCallback12(async () => {
3700
+ const fetchVersions = useCallback16(async () => {
2745
3701
  if (!workflowId) return [];
2746
3702
  try {
2747
3703
  const res = await fetchWithAuth(`/workflows/${workflowId}/versions`);
2748
3704
  if (!res.ok) return [];
2749
3705
  const data = await res.json();
2750
- const items = Array.isArray(data) ? data : data?.items || [];
3706
+ const items = Array.isArray(data) ? data : data?.items ?? [];
2751
3707
  setVersions(items);
2752
3708
  return items;
2753
3709
  } catch {
2754
3710
  return [];
2755
3711
  }
2756
3712
  }, [fetchWithAuth, workflowId]);
2757
- const fetchMermaid = useCallback12(async () => {
3713
+ const getVersion = useCallback16(
3714
+ async (version) => {
3715
+ if (!workflowId) throw new Error("Workflow ID is required");
3716
+ const res = await fetchWithAuth(`/workflows/${workflowId}/versions/${version}`);
3717
+ if (!res.ok) {
3718
+ throw new Error(`Failed to fetch workflow version (${res.status}): ${res.statusText}`);
3719
+ }
3720
+ return await res.json();
3721
+ },
3722
+ [fetchWithAuth, workflowId]
3723
+ );
3724
+ const fetchMermaid = useCallback16(async () => {
2758
3725
  if (!workflowId) return "";
2759
3726
  try {
2760
3727
  const res = await fetchWithAuth(`/workflows/${workflowId}/mermaid`);
@@ -2767,18 +3734,18 @@ function useWorkflow(workflowId, options) {
2767
3734
  return "";
2768
3735
  }
2769
3736
  }, [fetchWithAuth, workflowId]);
2770
- const saveDraft = useCallback12(
2771
- async (draft) => {
3737
+ const updateWorkflow = useCallback16(
3738
+ async (input) => {
2772
3739
  if (!workflowId) throw new Error("Workflow ID is required");
2773
3740
  setError(null);
2774
- const res = await fetchWithAuth(`/workflows/${workflowId}/draft`, {
2775
- method: "POST",
3741
+ const res = await fetchWithAuth(`/workflows/${workflowId}`, {
3742
+ method: "PATCH",
2776
3743
  headers: { "Content-Type": "application/json" },
2777
- body: JSON.stringify({ draft })
3744
+ body: JSON.stringify(input)
2778
3745
  });
2779
3746
  if (!res.ok) {
2780
3747
  const errText = await res.text().catch(() => res.statusText);
2781
- throw new Error(`Failed to save draft (${res.status}): ${errText}`);
3748
+ throw new Error(`Failed to update workflow (${res.status}): ${errText}`);
2782
3749
  }
2783
3750
  const updated = await res.json();
2784
3751
  setWorkflow(updated);
@@ -2786,27 +3753,41 @@ function useWorkflow(workflowId, options) {
2786
3753
  },
2787
3754
  [fetchWithAuth, workflowId]
2788
3755
  );
2789
- const publish = useCallback12(
2790
- async (summary) => {
3756
+ const saveDraft = useCallback16(
3757
+ async (draft) => {
2791
3758
  if (!workflowId) throw new Error("Workflow ID is required");
2792
3759
  setError(null);
2793
- const res = await fetchWithAuth(`/workflows/${workflowId}/publish`, {
2794
- method: "POST",
3760
+ const res = await fetchWithAuth(`/workflows/${workflowId}/draft`, {
3761
+ method: "PUT",
2795
3762
  headers: { "Content-Type": "application/json" },
2796
- body: JSON.stringify(summary ? { summary } : {})
3763
+ body: JSON.stringify(draft)
2797
3764
  });
2798
3765
  if (!res.ok) {
2799
3766
  const errText = await res.text().catch(() => res.statusText);
2800
- throw new Error(`Failed to publish workflow (${res.status}): ${errText}`);
3767
+ throw new Error(`Failed to save draft (${res.status}): ${errText}`);
2801
3768
  }
2802
- const versionResult = await res.json();
2803
- void fetchWorkflow();
2804
- void fetchVersions();
2805
- return versionResult;
3769
+ const updated = await res.json();
3770
+ setWorkflow(updated);
3771
+ return updated;
2806
3772
  },
2807
- [fetchWithAuth, workflowId, fetchWorkflow, fetchVersions]
3773
+ [fetchWithAuth, workflowId]
2808
3774
  );
2809
- const deleteWorkflow = useCallback12(async () => {
3775
+ const publish = useCallback16(async () => {
3776
+ if (!workflowId) throw new Error("Workflow ID is required");
3777
+ setError(null);
3778
+ const res = await fetchWithAuth(`/workflows/${workflowId}/publish`, {
3779
+ method: "POST"
3780
+ });
3781
+ if (!res.ok) {
3782
+ const errText = await res.text().catch(() => res.statusText);
3783
+ throw new Error(`Failed to publish workflow (${res.status}): ${errText}`);
3784
+ }
3785
+ const versionResult = await res.json();
3786
+ void fetchWorkflow();
3787
+ void fetchVersions();
3788
+ return versionResult;
3789
+ }, [fetchWithAuth, workflowId, fetchWorkflow, fetchVersions]);
3790
+ const deleteWorkflow = useCallback16(async () => {
2810
3791
  if (!workflowId) throw new Error("Workflow ID is required");
2811
3792
  setError(null);
2812
3793
  const res = await fetchWithAuth(`/workflows/${workflowId}`, {
@@ -2817,7 +3798,7 @@ function useWorkflow(workflowId, options) {
2817
3798
  }
2818
3799
  setWorkflow(null);
2819
3800
  }, [fetchWithAuth, workflowId]);
2820
- useEffect11(() => {
3801
+ useEffect15(() => {
2821
3802
  if (autoFetch && workflowId) {
2822
3803
  void fetchWorkflow();
2823
3804
  }
@@ -2830,7 +3811,9 @@ function useWorkflow(workflowId, options) {
2830
3811
  error,
2831
3812
  refetch: fetchWorkflow,
2832
3813
  fetchVersions,
3814
+ getVersion,
2833
3815
  fetchMermaid,
3816
+ updateWorkflow,
2834
3817
  saveDraft,
2835
3818
  publish,
2836
3819
  deleteWorkflow
@@ -2838,25 +3821,25 @@ function useWorkflow(workflowId, options) {
2838
3821
  }
2839
3822
 
2840
3823
  // src/hooks/useWorkflowStream.ts
2841
- import { useCallback as useCallback13, useEffect as useEffect12, useRef as useRef4, useState as useState12 } from "react";
3824
+ import { useCallback as useCallback17, useEffect as useEffect16, useRef as useRef4, useState as useState16 } from "react";
2842
3825
  function useWorkflowStream(workflowId, options) {
2843
3826
  const { baseUrl, getAuthToken, fetchWithAuth, logger } = usePersonaContext();
2844
- const [status, setStatus] = useState12("idle");
2845
- const [runId, setRunId] = useState12(null);
2846
- const [activeNodeId, setActiveNodeId] = useState12(null);
2847
- const [nodeRuns, setNodeRuns] = useState12(
3827
+ const [status, setStatus] = useState16("idle");
3828
+ const [runId, setRunId] = useState16(null);
3829
+ const [activeNodeId, setActiveNodeId] = useState16(null);
3830
+ const [nodeRuns, setNodeRuns] = useState16(
2848
3831
  {}
2849
3832
  );
2850
- const [text, setText] = useState12("");
2851
- const [output, setOutput] = useState12(null);
2852
- const [error, setError] = useState12(null);
2853
- const [events, setEvents] = useState12([]);
3833
+ const [text, setText] = useState16("");
3834
+ const [output, setOutput] = useState16(null);
3835
+ const [error, setError] = useState16(null);
3836
+ const [events, setEvents] = useState16([]);
2854
3837
  const abortControllerRef = useRef4(null);
2855
3838
  const currentRunIdRef = useRef4(null);
2856
3839
  currentRunIdRef.current = runId;
2857
3840
  const optionsRef = useRef4(options);
2858
3841
  optionsRef.current = options;
2859
- const cancel = useCallback13(async () => {
3842
+ const cancel = useCallback17(async () => {
2860
3843
  if (abortControllerRef.current) {
2861
3844
  abortControllerRef.current.abort();
2862
3845
  abortControllerRef.current = null;
@@ -2877,7 +3860,7 @@ function useWorkflowStream(workflowId, options) {
2877
3860
  setStatus("cancelled");
2878
3861
  setActiveNodeId(null);
2879
3862
  }, [fetchWithAuth, logger]);
2880
- const reset = useCallback13(() => {
3863
+ const reset = useCallback17(() => {
2881
3864
  if (abortControllerRef.current) {
2882
3865
  abortControllerRef.current.abort();
2883
3866
  abortControllerRef.current = null;
@@ -2891,7 +3874,7 @@ function useWorkflowStream(workflowId, options) {
2891
3874
  setError(null);
2892
3875
  setEvents([]);
2893
3876
  }, []);
2894
- const processStream = useCallback13(
3877
+ const processStream = useCallback17(
2895
3878
  async (url, method, body, targetRunId) => {
2896
3879
  if (abortControllerRef.current) {
2897
3880
  abortControllerRef.current.abort();
@@ -3056,7 +4039,7 @@ function useWorkflowStream(workflowId, options) {
3056
4039
  },
3057
4040
  [getAuthToken]
3058
4041
  );
3059
- const start = useCallback13(
4042
+ const start = useCallback17(
3060
4043
  async (input, opts) => {
3061
4044
  const targetWfId = opts?.workflowId || workflowId;
3062
4045
  if (!targetWfId) {
@@ -3077,7 +4060,7 @@ function useWorkflowStream(workflowId, options) {
3077
4060
  },
3078
4061
  [workflowId, baseUrl, processStream]
3079
4062
  );
3080
- const resume = useCallback13(
4063
+ const resume = useCallback17(
3081
4064
  async (targetRunId, sinceSeq) => {
3082
4065
  if (!targetRunId) {
3083
4066
  throw new Error("Run ID is required to resume stream");
@@ -3090,7 +4073,7 @@ function useWorkflowStream(workflowId, options) {
3090
4073
  },
3091
4074
  [baseUrl, processStream]
3092
4075
  );
3093
- useEffect12(() => {
4076
+ useEffect16(() => {
3094
4077
  return () => {
3095
4078
  if (abortControllerRef.current) {
3096
4079
  abortControllerRef.current.abort();
@@ -3115,21 +4098,21 @@ function useWorkflowStream(workflowId, options) {
3115
4098
  }
3116
4099
 
3117
4100
  // src/hooks/useWorkflowRuns.ts
3118
- import { useCallback as useCallback14, useEffect as useEffect13, useState as useState13 } from "react";
4101
+ import { useCallback as useCallback18, useEffect as useEffect17, useState as useState17 } from "react";
3119
4102
  function useWorkflowRuns(workflowId, options) {
3120
4103
  const opts = typeof options === "boolean" ? { autoFetch: options } : options ?? {};
3121
4104
  const { autoFetch = true, page = 1, limit = 20 } = opts;
3122
4105
  const { fetchWithAuth } = usePersonaContext();
3123
- const [runs, setRuns] = useState13([]);
3124
- const [pagination, setPagination] = useState13({
4106
+ const [runs, setRuns] = useState17([]);
4107
+ const [pagination, setPagination] = useState17({
3125
4108
  page,
3126
4109
  limit,
3127
4110
  total: 0,
3128
4111
  totalPages: 0
3129
4112
  });
3130
- const [isLoading, setIsLoading] = useState13(false);
3131
- const [error, setError] = useState13(null);
3132
- const fetchRuns = useCallback14(async () => {
4113
+ const [isLoading, setIsLoading] = useState17(false);
4114
+ const [error, setError] = useState17(null);
4115
+ const fetchRuns = useCallback18(async () => {
3133
4116
  if (!workflowId) return [];
3134
4117
  setIsLoading(true);
3135
4118
  setError(null);
@@ -3140,14 +4123,16 @@ function useWorkflowRuns(workflowId, options) {
3140
4123
  throw new Error(`Failed to list workflow runs: ${res.statusText}`);
3141
4124
  }
3142
4125
  const data = await res.json();
3143
- const items = Array.isArray(data) ? data : data?.items || data?.runs || [];
4126
+ const items = Array.isArray(data) ? data : data?.items ?? [];
3144
4127
  setRuns(items);
3145
4128
  if (data?.pagination) {
3146
4129
  setPagination({
3147
4130
  page: data.pagination.page ?? page,
3148
4131
  limit: data.pagination.limit ?? limit,
3149
4132
  total: data.pagination.total ?? items.length,
3150
- totalPages: data.pagination.totalPages ?? 1
4133
+ // Backend's paginationEnvelope() names this field `pages`, not
4134
+ // `totalPages`.
4135
+ totalPages: data.pagination.pages ?? 1
3151
4136
  });
3152
4137
  }
3153
4138
  return items;
@@ -3159,7 +4144,7 @@ function useWorkflowRuns(workflowId, options) {
3159
4144
  setIsLoading(false);
3160
4145
  }
3161
4146
  }, [fetchWithAuth, workflowId, page, limit]);
3162
- const getRun = useCallback14(
4147
+ const getRun = useCallback18(
3163
4148
  async (runId) => {
3164
4149
  if (!runId) throw new Error("Run ID is required");
3165
4150
  const res = await fetchWithAuth(`/workflows/runs/${runId}`);
@@ -3170,7 +4155,7 @@ function useWorkflowRuns(workflowId, options) {
3170
4155
  },
3171
4156
  [fetchWithAuth]
3172
4157
  );
3173
- const cancelRun = useCallback14(
4158
+ const cancelRun = useCallback18(
3174
4159
  async (runId) => {
3175
4160
  if (!runId) throw new Error("Run ID is required");
3176
4161
  const res = await fetchWithAuth(`/workflows/runs/${runId}/cancel`, {
@@ -3185,7 +4170,7 @@ function useWorkflowRuns(workflowId, options) {
3185
4170
  },
3186
4171
  [fetchWithAuth]
3187
4172
  );
3188
- useEffect13(() => {
4173
+ useEffect17(() => {
3189
4174
  if (autoFetch && workflowId) {
3190
4175
  void fetchRuns();
3191
4176
  }
@@ -3226,10 +4211,14 @@ export {
3226
4211
  useChat,
3227
4212
  useConnection,
3228
4213
  useFiles,
4214
+ useKnowledgeBases,
3229
4215
  useMcp,
4216
+ useMcpAdmin,
3230
4217
  useMcpConnections,
3231
4218
  useMemory,
3232
4219
  usePersonaContext,
4220
+ useRcpSources,
4221
+ useSkills,
3233
4222
  useThreads,
3234
4223
  useVoice,
3235
4224
  useWorkflow,