@invergent/agent-chat-react 2.5.5 → 2.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -914,6 +914,64 @@ var ConversationScrollButton = ({
914
914
 
915
915
  // src/components/ai-elements/message.tsx
916
916
  init_utils();
917
+
918
+ // src/lib/oauth-popup.ts
919
+ function openOAuthPopup(url) {
920
+ const w = 600;
921
+ const h = 720;
922
+ const dualLeft = window.screenLeft ?? window.screenX ?? 0;
923
+ const dualTop = window.screenTop ?? window.screenY ?? 0;
924
+ const width = window.innerWidth || document.documentElement.clientWidth || w;
925
+ const height = window.innerHeight || document.documentElement.clientHeight || h;
926
+ const left = dualLeft + Math.max(0, (width - w) / 2);
927
+ const top = dualTop + Math.max(0, (height - h) / 2);
928
+ const features = `popup=yes,width=${w},height=${h},left=${left},top=${top}`;
929
+ const popup = window.open(url, "composio-oauth", features);
930
+ if (!popup) {
931
+ window.open(url, "_blank", "noopener,noreferrer");
932
+ }
933
+ return popup;
934
+ }
935
+ function isComposioConnectUrl(url) {
936
+ if (!url) return false;
937
+ try {
938
+ return new URL(url).hostname === "connect.composio.dev";
939
+ } catch {
940
+ return false;
941
+ }
942
+ }
943
+ function toolkitName(label) {
944
+ const text = (label ?? "").trim();
945
+ const stripped = text.replace(/^connect\s+/i, "").trim();
946
+ return stripped || "your account";
947
+ }
948
+ function ComposioConnectCard({ url, label }) {
949
+ const name = toolkitName(label);
950
+ return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "my-1 inline-flex w-full max-w-sm items-center gap-3 rounded-lg border border-border bg-card p-3 align-middle no-underline", children: [
951
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlugZapIcon, { className: "h-4 w-4" }) }),
952
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex min-w-0 flex-col", children: [
953
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm font-medium text-foreground", children: [
954
+ "Connect ",
955
+ name
956
+ ] }),
957
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
958
+ "Authorize your ",
959
+ name,
960
+ " account to continue."
961
+ ] })
962
+ ] }),
963
+ /* @__PURE__ */ jsxRuntime.jsx(
964
+ Button,
965
+ {
966
+ type: "button",
967
+ size: "xs",
968
+ onClick: () => openOAuthPopup(url),
969
+ className: "ml-auto shrink-0",
970
+ children: "Connect"
971
+ }
972
+ )
973
+ ] });
974
+ }
917
975
  var Message = ({ className, from, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
918
976
  "div",
919
977
  {
@@ -946,6 +1004,21 @@ react.createContext(
946
1004
  null
947
1005
  );
948
1006
  var streamdownPlugins = { cjk: cjk.cjk, code: code.code, math: math.math, mermaid: mermaid.mermaid };
1007
+ function anchorText(children) {
1008
+ if (typeof children === "string") return children;
1009
+ if (Array.isArray(children)) {
1010
+ return children.filter((c) => typeof c === "string").join("");
1011
+ }
1012
+ return "";
1013
+ }
1014
+ var streamdownComponents = {
1015
+ a: ({ href, children, ...props }) => {
1016
+ if (isComposioConnectUrl(href)) {
1017
+ return /* @__PURE__ */ jsxRuntime.jsx(ComposioConnectCard, { url: href, label: anchorText(children) });
1018
+ }
1019
+ return /* @__PURE__ */ jsxRuntime.jsx("a", { href, target: "_blank", rel: "noreferrer", ...props, children });
1020
+ }
1021
+ };
949
1022
  var MessageResponse = react.memo(
950
1023
  ({ className, ...props }) => /* @__PURE__ */ jsxRuntime.jsx(
951
1024
  streamdown.Streamdown,
@@ -955,6 +1028,7 @@ var MessageResponse = react.memo(
955
1028
  className
956
1029
  ),
957
1030
  plugins: streamdownPlugins,
1031
+ components: streamdownComponents,
958
1032
  ...props
959
1033
  }
960
1034
  ),
@@ -2368,6 +2442,51 @@ function ExecuteCodeToolBlock({ tc }) {
2368
2442
  ] }) })
2369
2443
  ] });
2370
2444
  }
2445
+
2446
+ // src/lib/format.ts
2447
+ function formatFileSize(bytes) {
2448
+ if (bytes < 1024) return `${bytes} B`;
2449
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
2450
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
2451
+ }
2452
+ function formatMcpToolLabel(toolName) {
2453
+ const segments = toolName.replace(/^mcp__/, "").split("__");
2454
+ const tool = segments[segments.length - 1] ?? toolName;
2455
+ const branded = tool.replace(/^composio_/i, "");
2456
+ return (branded || tool).split("_").filter(Boolean).map((p) => p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()).join(" ");
2457
+ }
2458
+ function getLanguageHint(path) {
2459
+ const name = path.split("/").pop() ?? path;
2460
+ const dot = name.lastIndexOf(".");
2461
+ if (dot < 0) return "plaintext";
2462
+ const ext = name.slice(dot).toLowerCase();
2463
+ const map = {
2464
+ ".py": "python",
2465
+ ".js": "javascript",
2466
+ ".ts": "typescript",
2467
+ ".tsx": "tsx",
2468
+ ".jsx": "jsx",
2469
+ ".json": "json",
2470
+ ".yaml": "yaml",
2471
+ ".yml": "yaml",
2472
+ ".toml": "toml",
2473
+ ".sql": "sql",
2474
+ ".sh": "bash",
2475
+ ".css": "css",
2476
+ ".html": "html",
2477
+ ".md": "markdown",
2478
+ ".rs": "rust",
2479
+ ".go": "go",
2480
+ ".java": "java",
2481
+ ".rb": "ruby",
2482
+ ".php": "php",
2483
+ ".c": "c",
2484
+ ".cpp": "cpp",
2485
+ ".h": "c",
2486
+ ".hpp": "cpp"
2487
+ };
2488
+ return map[ext] ?? "plaintext";
2489
+ }
2371
2490
  function SessionSearchBlock({ tc }) {
2372
2491
  let query = "";
2373
2492
  try {
@@ -2406,9 +2525,7 @@ function WebToolBlock({ tc }) {
2406
2525
  ] });
2407
2526
  }
2408
2527
  function MCPToolBlock({ tc }) {
2409
- const segments = tc.toolName.replace(/^mcp__/, "").split("__");
2410
- const toolName = segments[segments.length - 1] ?? tc.toolName;
2411
- const label = toolName.split("_").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
2528
+ const label = formatMcpToolLabel(tc.toolName);
2412
2529
  let summary = "";
2413
2530
  try {
2414
2531
  const args = JSON.parse(tc.args);
@@ -6129,7 +6246,7 @@ function ChatComposerInner({
6129
6246
  }, [buttonMenuOpen, textInput.value]);
6130
6247
  react.useEffect(() => {
6131
6248
  if (showSlashMenu && (menuMode === "skills" || menuMode === "all")) {
6132
- adapter.listSlashCommands?.().then(setAdapterCommands).catch(() => {
6249
+ adapter.listSlashCommands?.().then((commands) => setAdapterCommands(commands.filter((c) => !c.isBuiltin))).catch(() => {
6133
6250
  });
6134
6251
  }
6135
6252
  }, [adapter, showSlashMenu, menuMode]);
@@ -6305,7 +6422,7 @@ function ChatComposerInner({
6305
6422
  type: "button",
6306
6423
  variant: "secondary",
6307
6424
  size: "sm",
6308
- className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent",
6425
+ className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent cursor-pointer",
6309
6426
  onClick: () => {
6310
6427
  setMenuMode("commands");
6311
6428
  setMenuDismissed(false);
@@ -6323,7 +6440,7 @@ function ChatComposerInner({
6323
6440
  type: "button",
6324
6441
  variant: "secondary",
6325
6442
  size: "sm",
6326
- className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent",
6443
+ className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent cursor-pointer",
6327
6444
  onClick: () => {
6328
6445
  setMenuMode("skills");
6329
6446
  setMenuDismissed(false);
@@ -6341,7 +6458,7 @@ function ChatComposerInner({
6341
6458
  type: "button",
6342
6459
  variant: "secondary",
6343
6460
  size: "sm",
6344
- className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent",
6461
+ className: "rounded-sm -uppercase font-display bg-white dark:bg-accent border-2 border-accent cursor-pointer",
6345
6462
  onClick: () => {
6346
6463
  setMenuMode("scheduled");
6347
6464
  setMenuDismissed(false);
@@ -6670,6 +6787,74 @@ function ChatComposerInner({
6670
6787
  }
6671
6788
  );
6672
6789
  }
6790
+ var MAX_LOGOS = 10;
6791
+ function IntegrationsBand({
6792
+ agentId,
6793
+ adapter,
6794
+ onOpenIntegrations
6795
+ }) {
6796
+ const supported = typeof adapter.listComposioConnections === "function" && typeof adapter.authorizeComposioToolkit === "function";
6797
+ const [rows, setRows] = react.useState(null);
6798
+ const refresh = react.useCallback(async () => {
6799
+ if (!adapter.listComposioConnections) return;
6800
+ const res = await adapter.listComposioConnections({ agentId });
6801
+ setRows(res.toolkits);
6802
+ }, [adapter, agentId]);
6803
+ react.useEffect(() => {
6804
+ if (!supported) return;
6805
+ void refresh();
6806
+ const onFocus = () => void refresh();
6807
+ window.addEventListener("focus", onFocus);
6808
+ return () => window.removeEventListener("focus", onFocus);
6809
+ }, [supported, refresh]);
6810
+ if (!supported || rows === null || rows.length === 0) return null;
6811
+ const shown = rows.slice(0, MAX_LOGOS);
6812
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5", children: /* @__PURE__ */ jsxRuntime.jsxs(
6813
+ "button",
6814
+ {
6815
+ type: "button",
6816
+ onClick: onOpenIntegrations,
6817
+ className: "flex w-full items-center gap-2 border border-border border-t-0 rounded-b-lg px-4 py-2.5 text-xs text-muted-foreground transition-colors hover:bg-accent/40 cursor-pointer",
6818
+ children: [
6819
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: "Connect your accounts" }),
6820
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto flex items-center gap-1.5", children: [
6821
+ shown.map((r) => /* @__PURE__ */ jsxRuntime.jsx(Logo, { row: r }, r.toolkit)),
6822
+ /* @__PURE__ */ jsxRuntime.jsx(
6823
+ lucideReact.ChevronRightIcon,
6824
+ {
6825
+ "aria-hidden": true,
6826
+ className: "h-3.5 w-3.5 text-muted-foreground/60"
6827
+ }
6828
+ )
6829
+ ] })
6830
+ ]
6831
+ }
6832
+ ) });
6833
+ }
6834
+ function Logo({ row }) {
6835
+ const [broken, setBroken] = react.useState(false);
6836
+ const label = row.name ?? row.toolkit;
6837
+ if (broken || !row.logo) {
6838
+ return /* @__PURE__ */ jsxRuntime.jsx(
6839
+ "span",
6840
+ {
6841
+ title: label,
6842
+ className: "inline-flex h-4 w-4 items-center justify-center rounded-full bg-muted text-[8px] font-semibold uppercase",
6843
+ children: label.charAt(0)
6844
+ }
6845
+ );
6846
+ }
6847
+ return /* @__PURE__ */ jsxRuntime.jsx(
6848
+ "img",
6849
+ {
6850
+ src: row.logo,
6851
+ alt: label,
6852
+ title: label,
6853
+ className: "h-4 w-4 rounded-sm object-contain",
6854
+ onError: () => setBroken(true)
6855
+ }
6856
+ );
6857
+ }
6673
6858
 
6674
6859
  // src/components/research/research-sources-panel.tsx
6675
6860
  init_utils();
@@ -7614,45 +7799,6 @@ var ErrorMessage = react.memo(function ErrorMessage2({
7614
7799
  });
7615
7800
  init_utils();
7616
7801
 
7617
- // src/lib/format.ts
7618
- function formatFileSize(bytes) {
7619
- if (bytes < 1024) return `${bytes} B`;
7620
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
7621
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
7622
- }
7623
- function getLanguageHint(path) {
7624
- const name = path.split("/").pop() ?? path;
7625
- const dot = name.lastIndexOf(".");
7626
- if (dot < 0) return "plaintext";
7627
- const ext = name.slice(dot).toLowerCase();
7628
- const map = {
7629
- ".py": "python",
7630
- ".js": "javascript",
7631
- ".ts": "typescript",
7632
- ".tsx": "tsx",
7633
- ".jsx": "jsx",
7634
- ".json": "json",
7635
- ".yaml": "yaml",
7636
- ".yml": "yaml",
7637
- ".toml": "toml",
7638
- ".sql": "sql",
7639
- ".sh": "bash",
7640
- ".css": "css",
7641
- ".html": "html",
7642
- ".md": "markdown",
7643
- ".rs": "rust",
7644
- ".go": "go",
7645
- ".java": "java",
7646
- ".rb": "ruby",
7647
- ".php": "php",
7648
- ".c": "c",
7649
- ".cpp": "cpp",
7650
- ".h": "c",
7651
- ".hpp": "cpp"
7652
- };
7653
- return map[ext] ?? "plaintext";
7654
- }
7655
-
7656
7802
  // src/components/ui/skeleton.tsx
7657
7803
  init_utils();
7658
7804
  function Skeleton({ className, ...props }) {
@@ -8682,7 +8828,9 @@ function cancelledToolLabel(toolName) {
8682
8828
  research_memory: "Research",
8683
8829
  research_outline: "Research"
8684
8830
  };
8685
- return map[toolName] ?? toolName;
8831
+ if (map[toolName]) return map[toolName];
8832
+ if (toolName.startsWith("mcp__")) return formatMcpToolLabel(toolName);
8833
+ return toolName;
8686
8834
  }
8687
8835
  function CancelledToolRow({ tc }) {
8688
8836
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-sm", children: [
@@ -9545,7 +9693,9 @@ function ChatThread({
9545
9693
  onViewModeChange,
9546
9694
  deepResearchEnabled = false,
9547
9695
  researchSources = [],
9548
- hideTurnSummary = false
9696
+ hideTurnSummary = false,
9697
+ agentId,
9698
+ onOpenIntegrations
9549
9699
  }) {
9550
9700
  const groups = react.useMemo(() => groupMessages(messages), [messages]);
9551
9701
  const awaitingInput = react.useMemo(() => isAwaitingUserInput(messages), [messages]);
@@ -9585,6 +9735,44 @@ function ChatThread({
9585
9735
  }
9586
9736
  return null;
9587
9737
  }, [messages]);
9738
+ const composerArea = /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-4xl px-3 sm:px-6 pb-3 sm:pb-5 pt-3", children: [
9739
+ retryIndicator && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(RetryBanner, { indicator: retryIndicator }) }),
9740
+ researchSources.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(ResearchSourcesPanel, { sources: researchSources }) }),
9741
+ /* @__PURE__ */ jsxRuntime.jsx(
9742
+ ChatComposer,
9743
+ {
9744
+ onSend,
9745
+ onStop,
9746
+ isRunning,
9747
+ disabled: composerDisabled,
9748
+ disabledReason: composerDisabledReason,
9749
+ tokenUsage,
9750
+ onComposerError,
9751
+ showBrowser,
9752
+ onToggleBrowser,
9753
+ showWorkspace,
9754
+ onToggleWorkspace,
9755
+ canShowBrowser,
9756
+ canShowWorkspace,
9757
+ viewMode,
9758
+ onViewModeChange,
9759
+ deepResearchEnabled
9760
+ }
9761
+ ),
9762
+ onOpenIntegrations && /* @__PURE__ */ jsxRuntime.jsx(
9763
+ IntegrationsBandSlot,
9764
+ {
9765
+ agentId,
9766
+ onOpenIntegrations
9767
+ }
9768
+ )
9769
+ ] });
9770
+ if (messages.length === 0 && !isLoadingHistory && !disabled) {
9771
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-1 flex-col items-center justify-center overflow-hidden bg-background px-3 text-base", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full", children: [
9772
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-8 text-center text-2xl font-bold text-foreground", children: "What should we do ?" }),
9773
+ composerArea
9774
+ ] }) });
9775
+ }
9588
9776
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-1 flex-col overflow-hidden bg-background text-base", children: [
9589
9777
  /* @__PURE__ */ jsxRuntime.jsxs(Conversation, { className: "relative flex-1 min-h-0", children: [
9590
9778
  /* @__PURE__ */ jsxRuntime.jsx(ConversationContent, { className: "mx-auto w-full max-w-4xl", children: messages.length === 0 && isLoadingHistory ? /* @__PURE__ */ jsxRuntime.jsx(
@@ -9594,13 +9782,6 @@ function ChatThread({
9594
9782
  title: "Loading conversation",
9595
9783
  description: "Fetching the session history."
9596
9784
  }
9597
- ) : messages.length === 0 && !disabled ? /* @__PURE__ */ jsxRuntime.jsx(
9598
- ConversationEmptyState,
9599
- {
9600
- icon: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MessageSquareIcon, { className: "size-8 opacity-40" }),
9601
- title: "Start a conversation",
9602
- description: "Ask me anything \u2014 I can search, analyze, write code, and more."
9603
- }
9604
9785
  ) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
9605
9786
  groups.map((group) => {
9606
9787
  if (group.role === "user") {
@@ -9653,33 +9834,23 @@ function ChatThread({
9653
9834
  ] }) }),
9654
9835
  /* @__PURE__ */ jsxRuntime.jsx(ConversationScrollButton, {})
9655
9836
  ] }),
9656
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto w-full max-w-4xl px-3 sm:px-6 pb-3 sm:pb-5 pt-3", children: [
9657
- retryIndicator && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(RetryBanner, { indicator: retryIndicator }) }),
9658
- researchSources.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-2", children: /* @__PURE__ */ jsxRuntime.jsx(ResearchSourcesPanel, { sources: researchSources }) }),
9659
- /* @__PURE__ */ jsxRuntime.jsx(
9660
- ChatComposer,
9661
- {
9662
- onSend,
9663
- onStop,
9664
- isRunning,
9665
- disabled: composerDisabled,
9666
- disabledReason: composerDisabledReason,
9667
- tokenUsage,
9668
- onComposerError,
9669
- showBrowser,
9670
- onToggleBrowser,
9671
- showWorkspace,
9672
- onToggleWorkspace,
9673
- canShowBrowser,
9674
- canShowWorkspace,
9675
- viewMode,
9676
- onViewModeChange,
9677
- deepResearchEnabled
9678
- }
9679
- )
9680
- ] })
9837
+ composerArea
9681
9838
  ] });
9682
9839
  }
9840
+ function IntegrationsBandSlot({
9841
+ agentId,
9842
+ onOpenIntegrations
9843
+ }) {
9844
+ const { adapter } = useAgentChatAdapterContext();
9845
+ return /* @__PURE__ */ jsxRuntime.jsx(
9846
+ IntegrationsBand,
9847
+ {
9848
+ agentId,
9849
+ adapter,
9850
+ onOpenIntegrations
9851
+ }
9852
+ );
9853
+ }
9683
9854
 
9684
9855
  // src/components/ai-elements/file-tree.tsx
9685
9856
  init_utils();
@@ -11815,7 +11986,8 @@ function AgentChat({
11815
11986
  onMessagesChange,
11816
11987
  disabled,
11817
11988
  onComposerError,
11818
- deepResearchEnabled = false
11989
+ deepResearchEnabled = false,
11990
+ onOpenIntegrations
11819
11991
  }) {
11820
11992
  const [workspacePath, setWorkspacePath] = react.useState(null);
11821
11993
  const [mobileView, setMobileView] = react.useState("chat");
@@ -11965,7 +12137,9 @@ function AgentChat({
11965
12137
  onViewModeChange: runtime.setViewMode,
11966
12138
  deepResearchEnabled,
11967
12139
  researchSources: runtime.researchSources,
11968
- hideTurnSummary
12140
+ hideTurnSummary,
12141
+ agentId,
12142
+ onOpenIntegrations
11969
12143
  }
11970
12144
  )
11971
12145
  }
@@ -12056,6 +12230,198 @@ function delegateTaskTargets(rawArgs) {
12056
12230
  }
12057
12231
  return out;
12058
12232
  }
12233
+ var POLL_INTERVAL_MS = 2e3;
12234
+ var POLL_TIMEOUT_MS = 18e4;
12235
+ var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
12236
+ function titleCase(slug) {
12237
+ return slug.split(/[_-]/).filter(Boolean).map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join(" ");
12238
+ }
12239
+ function IntegrationsPage({ agentId, adapter, onBack }) {
12240
+ const [rows, setRows] = react.useState([]);
12241
+ const [query, setQuery] = react.useState("");
12242
+ const [busy, setBusy] = react.useState(null);
12243
+ const [error, setError] = react.useState(null);
12244
+ const mountedRef = react.useRef(true);
12245
+ react.useEffect(() => {
12246
+ mountedRef.current = true;
12247
+ return () => {
12248
+ mountedRef.current = false;
12249
+ };
12250
+ }, []);
12251
+ const refresh = react.useCallback(async () => {
12252
+ if (!adapter.listComposioConnections) return;
12253
+ const res = await adapter.listComposioConnections({ agentId });
12254
+ setRows(res.toolkits);
12255
+ }, [adapter, agentId]);
12256
+ react.useEffect(() => {
12257
+ void refresh();
12258
+ const onFocus = () => void refresh();
12259
+ window.addEventListener("focus", onFocus);
12260
+ return () => window.removeEventListener("focus", onFocus);
12261
+ }, [refresh]);
12262
+ const grouped = react.useMemo(() => {
12263
+ const q = query.trim().toLowerCase();
12264
+ const filtered = rows.filter(
12265
+ (r) => (r.name ?? titleCase(r.toolkit)).toLowerCase().includes(q)
12266
+ );
12267
+ const byCat = /* @__PURE__ */ new Map();
12268
+ for (const r of filtered) {
12269
+ const cat = r.category || "Other";
12270
+ const list = byCat.get(cat) ?? [];
12271
+ list.push(r);
12272
+ byCat.set(cat, list);
12273
+ }
12274
+ return Array.from(byCat.entries());
12275
+ }, [rows, query]);
12276
+ const waitForConnection = async (toolkit, popup) => {
12277
+ if (!adapter.listComposioConnections) return;
12278
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
12279
+ while (Date.now() < deadline) {
12280
+ await sleep(POLL_INTERVAL_MS);
12281
+ if (!mountedRef.current) return;
12282
+ let toolkits = null;
12283
+ try {
12284
+ const res = await adapter.listComposioConnections({ agentId });
12285
+ toolkits = res.toolkits;
12286
+ if (!mountedRef.current) return;
12287
+ setRows(res.toolkits);
12288
+ } catch {
12289
+ }
12290
+ const connected = toolkits?.find((t) => t.toolkit === toolkit)?.connected;
12291
+ if (connected) {
12292
+ popup?.close();
12293
+ return;
12294
+ }
12295
+ if (popup?.closed) return;
12296
+ }
12297
+ };
12298
+ const connect = async (toolkit) => {
12299
+ if (!adapter.authorizeComposioToolkit) return;
12300
+ setBusy(toolkit);
12301
+ setError(null);
12302
+ try {
12303
+ const { redirectUrl } = await adapter.authorizeComposioToolkit({
12304
+ agentId,
12305
+ toolkit
12306
+ });
12307
+ const popup = redirectUrl ? openOAuthPopup(redirectUrl) : null;
12308
+ await waitForConnection(toolkit, popup);
12309
+ } catch (e) {
12310
+ setError(e instanceof Error ? e.message : "Failed to start the connection");
12311
+ } finally {
12312
+ setBusy(null);
12313
+ }
12314
+ };
12315
+ const disconnect = async (toolkit) => {
12316
+ if (!adapter.disconnectComposioToolkit) return;
12317
+ setBusy(toolkit);
12318
+ setError(null);
12319
+ try {
12320
+ await adapter.disconnectComposioToolkit({ agentId, toolkit });
12321
+ await refresh();
12322
+ } catch (e) {
12323
+ setError(e instanceof Error ? e.message : "Failed to disconnect");
12324
+ } finally {
12325
+ setBusy(null);
12326
+ }
12327
+ };
12328
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mx-auto flex w-full max-w-3xl flex-col gap-6 p-6", children: [
12329
+ /* @__PURE__ */ jsxRuntime.jsxs(
12330
+ "button",
12331
+ {
12332
+ type: "button",
12333
+ onClick: onBack,
12334
+ className: "flex w-fit items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground",
12335
+ children: [
12336
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeftIcon, { className: "h-4 w-4" }),
12337
+ "Back"
12338
+ ]
12339
+ }
12340
+ ),
12341
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
12342
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-2xl font-bold text-foreground", children: "Integrations" }),
12343
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Connect third-party services to enhance your agent's capabilities" })
12344
+ ] }),
12345
+ /* @__PURE__ */ jsxRuntime.jsx(
12346
+ "input",
12347
+ {
12348
+ type: "search",
12349
+ value: query,
12350
+ onChange: (e) => setQuery(e.target.value),
12351
+ placeholder: "Search integrations...",
12352
+ className: "w-full rounded-lg border border-border bg-background px-3 py-2 text-sm"
12353
+ }
12354
+ ),
12355
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { role: "alert", className: "text-xs text-red-500", children: error }),
12356
+ grouped.map(([category, items]) => /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "flex flex-col gap-2", children: [
12357
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-base font-semibold uppercase tracking-wide text-muted-foreground/70", children: category }),
12358
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: items.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
12359
+ ToolkitRow,
12360
+ {
12361
+ row: r,
12362
+ busy: busy === r.toolkit,
12363
+ onConnect: () => connect(r.toolkit),
12364
+ onDisconnect: () => disconnect(r.toolkit)
12365
+ },
12366
+ r.toolkit
12367
+ )) })
12368
+ ] }, category))
12369
+ ] });
12370
+ }
12371
+ function ToolkitRow({
12372
+ row,
12373
+ busy,
12374
+ onConnect,
12375
+ onDisconnect
12376
+ }) {
12377
+ const [open, setOpen] = react.useState(false);
12378
+ const [broken, setBroken] = react.useState(false);
12379
+ const name = row.name ?? titleCase(row.toolkit);
12380
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-border p-3", children: [
12381
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
12382
+ /* @__PURE__ */ jsxRuntime.jsxs(
12383
+ "button",
12384
+ {
12385
+ type: "button",
12386
+ "aria-label": open ? "Collapse" : "Expand",
12387
+ "aria-expanded": open,
12388
+ onClick: () => setOpen((v) => !v),
12389
+ className: "flex min-w-0 flex-1 items-center gap-2 text-left",
12390
+ children: [
12391
+ open ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/60" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "h-3.5 w-3.5 shrink-0 text-muted-foreground/60" }),
12392
+ broken || !row.logo ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex h-5 w-5 items-center justify-center rounded bg-muted text-sm font-semibold uppercase", children: name.charAt(0) }) : /* @__PURE__ */ jsxRuntime.jsx(
12393
+ "img",
12394
+ {
12395
+ src: row.logo,
12396
+ alt: name,
12397
+ className: "h-5 w-5 rounded object-contain",
12398
+ onError: () => setBroken(true)
12399
+ }
12400
+ ),
12401
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium text-foreground", children: name })
12402
+ ]
12403
+ }
12404
+ ),
12405
+ /* @__PURE__ */ jsxRuntime.jsx(
12406
+ "button",
12407
+ {
12408
+ type: "button",
12409
+ disabled: busy,
12410
+ onClick: row.connected ? onDisconnect : onConnect,
12411
+ className: `ml-auto inline-flex min-w-[6rem] items-center justify-center gap-1.5 rounded-md border border-border px-2 py-1 text-xs hover:bg-accent disabled:opacity-60 ${row.connected ? "text-muted-foreground" : "text-foreground"}`,
12412
+ children: busy ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
12413
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "h-3.5 w-3.5 animate-spin" }),
12414
+ row.connected ? "Disconnecting" : "Connecting"
12415
+ ] }) : row.connected ? "Disconnect" : "Connect"
12416
+ }
12417
+ )
12418
+ ] }),
12419
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 pl-7 text-sm text-muted-foreground", children: [
12420
+ row.description && /* @__PURE__ */ jsxRuntime.jsx("p", { children: row.description }),
12421
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 inline-block rounded bg-primary/10 px-1.5 py-0.5 text-sm font-semibold text-primary", children: row.auth_mode === "api_key" ? "API Key" : "OAuth" })
12422
+ ] })
12423
+ ] });
12424
+ }
12059
12425
  init_utils();
12060
12426
  var DEFAULT_LIMIT = 50;
12061
12427
  function requireInboxAdapter(adapter) {
@@ -14531,7 +14897,10 @@ exports.ACTIVE_MISSION_STATUSES = ACTIVE_MISSION_STATUSES;
14531
14897
  exports.AgentChat = AgentChat;
14532
14898
  exports.AgentChatAdapterProvider = AgentChatAdapterProvider;
14533
14899
  exports.CitationText = CitationText;
14900
+ exports.ComposioConnectCard = ComposioConnectCard;
14534
14901
  exports.InboxPanel = InboxPanel;
14902
+ exports.IntegrationsBand = IntegrationsBand;
14903
+ exports.IntegrationsPage = IntegrationsPage;
14535
14904
  exports.MessageResponse = MessageResponse;
14536
14905
  exports.MissionDashboard = MissionDashboard;
14537
14906
  exports.MissionsPanel = MissionsPanel;