@agent-native/dispatch 0.22.1 → 0.23.1

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.
Files changed (61) hide show
  1. package/dist/actions/provider-api-register.d.ts +1 -1
  2. package/dist/components/app-icon.d.ts +2 -1
  3. package/dist/components/app-icon.d.ts.map +1 -1
  4. package/dist/components/app-icon.js +51 -29
  5. package/dist/components/app-icon.js.map +1 -1
  6. package/dist/components/app-list-row.d.ts +1 -1
  7. package/dist/components/app-list-row.d.ts.map +1 -1
  8. package/dist/components/app-list-row.js +1 -1
  9. package/dist/components/app-list-row.js.map +1 -1
  10. package/dist/components/app-open-actions.d.ts +2 -1
  11. package/dist/components/app-open-actions.d.ts.map +1 -1
  12. package/dist/components/app-open-actions.js +2 -2
  13. package/dist/components/app-open-actions.js.map +1 -1
  14. package/dist/components/dispatch-control-plane.d.ts.map +1 -1
  15. package/dist/components/dispatch-control-plane.js +16 -17
  16. package/dist/components/dispatch-control-plane.js.map +1 -1
  17. package/dist/components/layout/Layout.d.ts.map +1 -1
  18. package/dist/components/layout/Layout.js +137 -66
  19. package/dist/components/layout/Layout.js.map +1 -1
  20. package/dist/components/layout/workspace-apps-rail.d.ts.map +1 -1
  21. package/dist/components/layout/workspace-apps-rail.js +1 -1
  22. package/dist/components/layout/workspace-apps-rail.js.map +1 -1
  23. package/dist/components/other-apps-section.d.ts +3 -1
  24. package/dist/components/other-apps-section.d.ts.map +1 -1
  25. package/dist/components/other-apps-section.js +9 -6
  26. package/dist/components/other-apps-section.js.map +1 -1
  27. package/dist/components/workspace-app-card.d.ts.map +1 -1
  28. package/dist/components/workspace-app-card.js +7 -2
  29. package/dist/components/workspace-app-card.js.map +1 -1
  30. package/dist/components/workspace-template-card.d.ts.map +1 -1
  31. package/dist/components/workspace-template-card.js +2 -5
  32. package/dist/components/workspace-template-card.js.map +1 -1
  33. package/dist/lib/overview-chat.d.ts.map +1 -1
  34. package/dist/lib/overview-chat.js +0 -8
  35. package/dist/lib/overview-chat.js.map +1 -1
  36. package/dist/routes/pages/chat.js +1 -1
  37. package/dist/routes/pages/chat.js.map +1 -1
  38. package/dist/server/lib/mcp-gateway.d.ts.map +1 -1
  39. package/dist/server/lib/mcp-gateway.js +14 -1
  40. package/dist/server/lib/mcp-gateway.js.map +1 -1
  41. package/package.json +3 -3
  42. package/src/components/app-icon.tsx +71 -43
  43. package/src/components/app-list-row.tsx +1 -1
  44. package/src/components/app-open-actions.tsx +4 -2
  45. package/src/components/dispatch-control-plane.spec.tsx +45 -7
  46. package/src/components/dispatch-control-plane.tsx +56 -75
  47. package/src/components/layout/Layout.spec.tsx +53 -0
  48. package/src/components/layout/Layout.tsx +227 -99
  49. package/src/components/layout/workspace-apps-rail.tsx +1 -0
  50. package/src/components/other-apps-section.tsx +61 -40
  51. package/src/components/workspace-app-card.spec.tsx +63 -66
  52. package/src/components/workspace-app-card.tsx +10 -3
  53. package/src/components/workspace-template-card.spec.tsx +7 -8
  54. package/src/components/workspace-template-card.tsx +21 -11
  55. package/src/lib/overview-chat.spec.ts +0 -19
  56. package/src/lib/overview-chat.ts +0 -9
  57. package/src/routes/pages/chat.tsx +1 -1
  58. package/src/server/lib/mcp-gateway.spec.ts +64 -2
  59. package/src/server/lib/mcp-gateway.ts +19 -1
  60. package/src/styles/dispatch-css.spec.ts +11 -0
  61. package/src/styles/dispatch.css +2 -2
@@ -269,6 +269,9 @@ function persistedChatFirstPane(value) {
269
269
  return null;
270
270
  return {
271
271
  appId: record.appId,
272
+ ...(record.placement === "main" || record.placement === "side"
273
+ ? { placement: record.placement }
274
+ : {}),
272
275
  ...(typeof record.path === "string" ? { path: record.path } : {}),
273
276
  ...(typeof record.view === "string" ? { view: record.view } : {}),
274
277
  };
@@ -417,7 +420,7 @@ function DispatchChatsSection({ onNavigate, showNewChat = false, prelude, chatFi
417
420
  if (threadId)
418
421
  openThread(threadId, { isNew: true });
419
422
  }
420
- return (_jsxs("div", { className: cn("ms-4 min-w-0 space-y-0.5", showNewChat && "dispatch-chat-first-chats"), children: [showNewChat && chatFirstNavigation ? (_jsx("nav", { className: "space-y-0.5 px-2 py-2", children: _jsx(ChatFirstPrimaryNavigation, { copy: chatFirstCopy, onNewChat: () => void handleNewChat(), onOpenIntegrations: chatFirstNavigation.onOpenIntegrations, onOpenScheduled: chatFirstNavigation.onOpenScheduled, onSearch: openCommandMenu }) })) : null, prelude, !chatFirstMode ? (_jsx("div", { className: "flex justify-end px-2 pt-0.5", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-dispatch-chat-source-toggle": true, "aria-pressed": includeExternal, "aria-label": includeExternal
423
+ return (_jsxs("div", { className: cn("min-w-0 space-y-0.5", showNewChat && "dispatch-chat-first-chats"), children: [showNewChat && chatFirstNavigation ? (_jsx("nav", { className: "space-y-0.5 px-0 py-2", children: _jsx(ChatFirstPrimaryNavigation, { copy: chatFirstCopy, onNewChat: () => void handleNewChat(), onOpenIntegrations: chatFirstNavigation.onOpenIntegrations, onOpenScheduled: chatFirstNavigation.onOpenScheduled, onSearch: openCommandMenu }) })) : null, prelude, !chatFirstMode ? (_jsx("div", { className: "flex justify-end px-2 pt-0.5", children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", "data-dispatch-chat-source-toggle": true, "aria-pressed": includeExternal, "aria-label": includeExternal
421
424
  ? t("dispatch.sidebar.showLocalChats", {
422
425
  defaultValue: "Show local chats",
423
426
  })
@@ -432,7 +435,7 @@ function DispatchChatsSection({ onNavigate, showNewChat = false, prelude, chatFi
432
435
  }) })] }) })) : null, !chatFirstMode &&
433
436
  chatsLoading &&
434
437
  visibleThreads.length === 0 &&
435
- Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1", children: [_jsx(Skeleton, { className: "size-3.5 shrink-0 rounded-sm" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] }, `chat-skeleton-${index}`))), chatFirstMode ? (_jsx(ChatFirstChatHistory, { items: chatItems, activeId: displayedActiveThreadId, loading: chatsLoading && visibleThreads.length === 0, loadingLabel: _jsx("div", { className: "space-y-1 px-2 py-1", children: [0, 1, 2].map((index) => (_jsxs("div", { className: "flex items-center gap-2 px-1 py-1", children: [_jsx(Skeleton, { className: "size-3.5 shrink-0 rounded-sm" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] }, index))) }), label: t("dispatch.sidebar.chats", { defaultValue: "Chats" }), onSelect: (threadId) => openThread(threadId), renameMaxLength: 160, onRename: (threadId, title) => void renameThread(threadId, title), labels: {
438
+ Array.from({ length: 3 }).map((_, index) => (_jsxs("div", { className: "flex items-center gap-2 px-3 py-1", children: [_jsx(Skeleton, { className: "size-3.5 shrink-0 rounded-sm" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] }, `chat-skeleton-${index}`))), chatFirstMode && (chatsLoading || visibleThreads.length > 0) ? (_jsx(ChatFirstChatHistory, { items: chatItems, activeId: displayedActiveThreadId, loading: chatsLoading && visibleThreads.length === 0, loadingLabel: _jsx("div", { className: "space-y-1 px-2 py-1", children: [0, 1, 2].map((index) => (_jsxs("div", { className: "flex items-center gap-2 px-1 py-1", children: [_jsx(Skeleton, { className: "size-3.5 shrink-0 rounded-sm" }), _jsx(Skeleton, { className: "h-3 w-3/4 rounded" })] }, index))) }), label: t("dispatch.sidebar.chats", { defaultValue: "Chats" }), onSelect: (threadId) => openThread(threadId), renameMaxLength: 160, onRename: (threadId, title) => void renameThread(threadId, title), labels: {
436
439
  options: (item) => t("dispatch.sidebar.chatOptions", {
437
440
  title: item.titleText ?? "",
438
441
  }),
@@ -515,7 +518,7 @@ export function NavContent({ onNavigate, extensions, chatFirstMode = false, chat
515
518
  }, onOpenAllApps: () => {
516
519
  navigate(dispatchNavLinkTarget("/apps"));
517
520
  onNavigate?.();
518
- }, createAppTrigger: chatFirstCreateAppTrigger, renderIcon: (app) => (_jsx(AppIcon, { id: app.id, name: app.name, size: "sm", className: "size-5 rounded-md" })), copy: chatFirstCopy }));
521
+ }, createAppTrigger: chatFirstCreateAppTrigger, renderIcon: (app) => (_jsx(AppIcon, { id: app.id, name: app.name, size: "sm", className: "size-7 rounded-lg" })), copy: chatFirstCopy }));
519
522
  const renderNavItem = (item) => {
520
523
  const Icon = item.icon;
521
524
  const itemMatchesLocalPath = navItemMatchesPath(item, localPathname);
@@ -564,7 +567,7 @@ export function NavContent({ onNavigate, extensions, chatFirstMode = false, chat
564
567
  navigate(dispatchNavLinkTarget("/admin/automations"));
565
568
  onNavigate?.();
566
569
  },
567
- }, prelude: chatFirstAppsRail })) : (chatFirstAppsRail) })) : null, _jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col overflow-y-auto", chatFirstMode && "hidden"), children: [_jsx("nav", { className: cn("py-2", collapsed ? "px-1.5" : "px-2"), children: _jsx("ul", { className: cn(collapsed ? "flex flex-col items-center gap-1" : "space-y-0.5"), children: primaryNavItems.map(renderNavItem) }) }), _jsxs("div", { className: "mt-auto shrink-0", children: [bottomNavigation, organizationPicker] }), sidebarFooterActions] }), chatFirstMode ? (_jsxs("div", { className: "mt-auto shrink-0", children: [bottomNavigation, organizationPicker, collapsed ? sidebarFooterActions : null] })) : null] }));
570
+ }, prelude: chatFirstAppsRail })) : (chatFirstAppsRail) })) : null, _jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col overflow-y-auto", chatFirstMode && "hidden"), children: [_jsx("nav", { className: cn("py-2", collapsed ? "px-1.5" : "px-2"), children: _jsx("ul", { className: cn(collapsed ? "flex flex-col items-center gap-1" : "space-y-0.5"), children: primaryNavItems.map(renderNavItem) }) }), _jsxs("div", { className: "mt-auto shrink-0", children: [bottomNavigation, organizationPicker] }), sidebarFooterActions] }), chatFirstMode ? (_jsxs("div", { className: "mt-auto shrink-0", children: [bottomNavigation, organizationPicker, sidebarFooterActions] })) : null] }));
568
571
  }
569
572
  export function Layout({ children, extensions, agentPageHref, }) {
570
573
  const t = useT();
@@ -584,26 +587,50 @@ export function Layout({ children, extensions, agentPageHref, }) {
584
587
  const localPathname = localDispatchPath(location.pathname);
585
588
  const isChatRoute = localPathname === "/chat" || localPathname.startsWith("/chat/");
586
589
  const chatFirstSurfaceScope = threadIdFromPath(localPathname) ?? "new";
587
- const isWorkspaceAppHostRoute = localPathname.startsWith("/apps/");
590
+ const isWorkspaceAppRoute = localPathname.startsWith("/apps/");
591
+ const isWorkspaceAppHostRoute = isWorkspaceAppRoute &&
592
+ typeof window !== "undefined" &&
593
+ new URLSearchParams(window.location.search).get("embedded") === "1";
588
594
  const [chatFirstPreference, setChatFirstPreference] = useState(() => readChatFirstMode());
589
595
  const chatFirstEmbedded = typeof window !== "undefined" &&
590
596
  new URLSearchParams(window.location.search).get("chatFirst") === "1";
591
597
  const chatFirstMode = extensions?.chatFirst === true || chatFirstPreference || chatFirstEmbedded;
598
+ const chatFirstHasActiveChat = chatFirstSurfaceScope !== "new";
592
599
  const [chatFirstAppLayout, setChatFirstAppLayout] = useState(() => readChatFirstAppLayout());
593
600
  const chatFirstAppLayoutHydratedRef = useRef(false);
594
- const chatFirstAppsQuery = useActionQuery("list-workspace-apps", { includeAgentCards: false }, { enabled: chatFirstMode && isChatRoute });
601
+ const chatFirstAppsQuery = useActionQuery("list-workspace-apps", { includeAgentCards: false }, { enabled: chatFirstMode });
602
+ const chatFirstGrantedAppsQuery = useActionQuery("list_apps", {}, { enabled: chatFirstMode });
595
603
  const chatFirstWorkspaceApps = useMemo(() => mergeChatFirstWorkspaceApps(chatFirstAppsQuery.data), [chatFirstAppsQuery.data]);
596
- const chatFirstAppRegistrations = useMemo(() => chatFirstWorkspaceApps.map((app) => ({
597
- id: app.id,
598
- name: app.name,
599
- path: app.path,
600
- url: app.url,
601
- enabled: app.status !== "pending" && app.archived !== true,
602
- })), [chatFirstWorkspaceApps]);
603
- const chatFirstAppItems = useMemo(() => chatFirstWorkspaceApps.map((app) => ({
604
+ const chatFirstAppRegistrations = useMemo(() => {
605
+ const registrations = new Map();
606
+ for (const app of chatFirstWorkspaceApps) {
607
+ registrations.set(app.id.toLowerCase(), {
608
+ id: app.id,
609
+ name: app.name,
610
+ path: app.path,
611
+ url: app.url,
612
+ enabled: app.status !== "pending" && app.archived !== true,
613
+ });
614
+ }
615
+ for (const app of chatFirstGrantedAppsQuery.data?.apps ?? []) {
616
+ const id = app.id.trim();
617
+ if (!id || registrations.has(id.toLowerCase()))
618
+ continue;
619
+ registrations.set(id.toLowerCase(), {
620
+ id,
621
+ name: app.name,
622
+ url: app.url,
623
+ enabled: true,
624
+ });
625
+ }
626
+ return [...registrations.values()];
627
+ }, [chatFirstGrantedAppsQuery.data?.apps, chatFirstWorkspaceApps]);
628
+ const chatFirstAppItems = useMemo(() => chatFirstAppRegistrations
629
+ .filter((app) => app.enabled)
630
+ .map((app) => ({
604
631
  id: app.id,
605
- name: app.name,
606
- })), [chatFirstWorkspaceApps]);
632
+ name: app.name ?? app.id,
633
+ })), [chatFirstAppRegistrations]);
607
634
  const chatFirstCopy = useMemo(() => createDispatchChatFirstCopy(t), [t]);
608
635
  const createChatFirstEmbedSession = useActionMutation("create_embed_session", { skipActionQueryInvalidation: true });
609
636
  const [chatFirstPane, setChatFirstPane] = useState(null);
@@ -627,10 +654,21 @@ export function Layout({ children, extensions, agentPageHref, }) {
627
654
  const previousChatFirstSurfaceScopeRef = useRef(chatFirstSurfaceScope);
628
655
  const previousChatFirstSurfaceTabCountRef = useRef(null);
629
656
  const activeChatFirstSurfaceTab = useMemo(() => chatFirstSurfaceTabs.tabs.find((tab) => tab.id === chatFirstSurfaceTabs.activeTabId) ?? null, [chatFirstSurfaceTabs]);
630
- const activeChatFirstApp = useMemo(() => activeChatFirstSurfaceTab?.kind === "app" &&
657
+ const chatFirstAppTakesMain = chatFirstMode &&
658
+ isChatRoute &&
659
+ activeChatFirstSurfaceTab?.kind === "app" &&
660
+ activeChatFirstSurfaceTab.placement === "main";
661
+ const activeChatFirstAppRegistration = useMemo(() => activeChatFirstSurfaceTab?.kind === "app" &&
631
662
  activeChatFirstSurfaceTab.appId
632
- ? (chatFirstWorkspaceApps.find((app) => app.id === activeChatFirstSurfaceTab.appId) ?? null)
633
- : null, [activeChatFirstSurfaceTab, chatFirstWorkspaceApps]);
663
+ ? (chatFirstAppRegistrations.find((app) => app.id === activeChatFirstSurfaceTab.appId) ?? null)
664
+ : null, [activeChatFirstSurfaceTab, chatFirstAppRegistrations]);
665
+ const activeChatFirstApp = useMemo(() => activeChatFirstAppRegistration
666
+ ? {
667
+ id: activeChatFirstAppRegistration.id,
668
+ name: activeChatFirstAppRegistration.name ??
669
+ activeChatFirstAppRegistration.id,
670
+ }
671
+ : null, [activeChatFirstAppRegistration]);
634
672
  const chatFirstAgentActivities = useMemo(() => (chatFirstAgentsQuery.data?.threads ?? []).map((thread) => ({
635
673
  sessionId: thread.id,
636
674
  title: thread.title || chatFirstCopy("agentsTitle", { name: thread.id }),
@@ -641,7 +679,8 @@ export function Layout({ children, extensions, agentPageHref, }) {
641
679
  const chatFirstEmbedPath = activeChatFirstSurfaceTab?.kind === "app"
642
680
  ? (activeChatFirstSurfaceTab.path ??
643
681
  chatFirstPane?.path ??
644
- activeChatFirstApp?.path)
682
+ activeChatFirstAppRegistration?.path ??
683
+ "/")
645
684
  : null;
646
685
  useEffect(() => {
647
686
  if (!chatFirstMode || !isChatRoute || !activeChatFirstApp) {
@@ -688,25 +727,47 @@ export function Layout({ children, extensions, agentPageHref, }) {
688
727
  setChatFirstNotice("The app pane opened locally, but its focus could not be synced.");
689
728
  });
690
729
  }, []);
691
- const openChatFirstPane = useCallback((pane) => {
730
+ const openChatFirstPane = useCallback((pane, placement = "side") => {
731
+ if (placement === "side" && !chatFirstHasActiveChat)
732
+ return;
692
733
  setChatFirstNotice(null);
693
734
  closeChatFirstSessionWatch();
735
+ if (!isChatRoute) {
736
+ navigateWithAgentChatViewTransition(navigate, dispatchNavLinkTarget("/chat"));
737
+ }
694
738
  const app = chatFirstAppRegistrations.find((candidate) => candidate.id === pane.appId);
695
739
  chatFirstSurfaceTabsStore.open({
696
740
  id: chatFirstSurfaceTabId("app", `${pane.appId}:${pane.path ?? pane.view ?? "/"}`),
697
741
  kind: "app",
698
742
  title: app?.name ?? pane.appId,
699
743
  appId: pane.appId,
744
+ placement,
700
745
  ...(pane.path ? { path: pane.path } : {}),
701
746
  ...(pane.view ? { view: pane.view } : {}),
702
747
  });
703
- persistChatFirstPane(pane);
748
+ setChatFirstSurfacePanelOpen(true);
749
+ persistChatFirstPane({ ...pane, placement });
704
750
  }, [
705
751
  chatFirstAppRegistrations,
706
752
  chatFirstSurfaceTabsStore,
753
+ chatFirstHasActiveChat,
707
754
  persistChatFirstPane,
755
+ isChatRoute,
756
+ navigate,
757
+ setChatFirstSurfacePanelOpen,
708
758
  ]);
709
759
  const openChatFirstSurface = useCallback((kind) => {
760
+ if (kind === "browser") {
761
+ persistChatFirstPane(null);
762
+ closeChatFirstSessionWatch();
763
+ chatFirstSurfaceTabsStore.open({
764
+ id: chatFirstSurfaceTabId("browser", "homepage"),
765
+ kind: "browser",
766
+ title: "Browser",
767
+ url: "https://www.google.com/",
768
+ });
769
+ return;
770
+ }
710
771
  if (kind !== "agents")
711
772
  return;
712
773
  persistChatFirstPane(null);
@@ -723,6 +784,20 @@ export function Layout({ children, extensions, agentPageHref, }) {
723
784
  setChatFirstNotice(chatFirstBrowserResolutionMessage(resolution.reason));
724
785
  return;
725
786
  }
787
+ if (resolution.target.openExternally && typeof window !== "undefined") {
788
+ try {
789
+ // Builder's Visual Editor rejects iframe ancestors with CSP/X-Frame-
790
+ // Options. Prefer the real browser tab; the browser pane below is a
791
+ // visible fallback when popup policy blocks this non-click event.
792
+ if (window.open(resolution.target.url, "_blank", "noopener,noreferrer")) {
793
+ return;
794
+ }
795
+ }
796
+ catch (error) {
797
+ console.warn("[chat-first] external browser open failed; keeping link fallback", error);
798
+ // Fall through to the non-iframe browser pane below.
799
+ }
800
+ }
726
801
  persistChatFirstPane(null);
727
802
  closeChatFirstSessionWatch();
728
803
  setChatFirstNotice(null);
@@ -734,7 +809,7 @@ export function Layout({ children, extensions, agentPageHref, }) {
734
809
  });
735
810
  }, [chatFirstSurfaceTabsStore, persistChatFirstPane]);
736
811
  const resolveChatFirstOpenApp = useCallback((detail) => {
737
- if (chatFirstAppsQuery.isLoading) {
812
+ if (chatFirstAppsQuery.isLoading || chatFirstGrantedAppsQuery.isLoading) {
738
813
  pendingChatFirstOpenAppRef.current = detail;
739
814
  return;
740
815
  }
@@ -756,10 +831,9 @@ export function Layout({ children, extensions, agentPageHref, }) {
756
831
  chatFirstAppRegistrations,
757
832
  chatFirstAppsQuery.isError,
758
833
  chatFirstAppsQuery.isLoading,
834
+ chatFirstGrantedAppsQuery.isLoading,
759
835
  openChatFirstPane,
760
836
  ]);
761
- const sidebarBeforeAppRef = useRef(null);
762
- const sidebarAutoCollapsedRef = useRef(false);
763
837
  const chatHomeHandoffActive = useAgentChatHomeHandoff({
764
838
  storageKey: "dispatch",
765
839
  activePath: localPathname,
@@ -811,7 +885,7 @@ export function Layout({ children, extensions, agentPageHref, }) {
811
885
  });
812
886
  }, []);
813
887
  useEffect(() => {
814
- if (!chatFirstMode || !isChatRoute) {
888
+ if (!chatFirstMode) {
815
889
  setChatFirstPane(null);
816
890
  closeChatFirstSessionWatch();
817
891
  chatFirstSurfaceTabsStore.closeAll();
@@ -861,8 +935,9 @@ export function Layout({ children, extensions, agentPageHref, }) {
861
935
  ? undefined
862
936
  : window.location.origin,
863
937
  });
864
- if (resolution.status === "ready")
865
- openChatFirstPane(resolution.target);
938
+ if (resolution.status === "ready") {
939
+ openChatFirstPane(resolution.target, persisted.placement);
940
+ }
866
941
  })
867
942
  .catch(() => {
868
943
  if (active) {
@@ -904,7 +979,7 @@ export function Layout({ children, extensions, agentPageHref, }) {
904
979
  useEffect(() => {
905
980
  const tabCount = chatFirstSurfaceTabs.tabs.length;
906
981
  const previousTabCount = previousChatFirstSurfaceTabCountRef.current;
907
- if (!chatFirstMode || !isChatRoute) {
982
+ if (!chatFirstMode || !chatFirstHasActiveChat) {
908
983
  setChatFirstSurfacePanelOpen(false);
909
984
  }
910
985
  else if (tabCount > 0 &&
@@ -919,6 +994,7 @@ export function Layout({ children, extensions, agentPageHref, }) {
919
994
  previousChatFirstSurfaceTabCountRef.current = tabCount;
920
995
  }, [
921
996
  chatFirstMode,
997
+ chatFirstHasActiveChat,
922
998
  setChatFirstSurfacePanelOpen,
923
999
  chatFirstSurfaceTabs.tabs.length,
924
1000
  isChatRoute,
@@ -933,13 +1009,17 @@ export function Layout({ children, extensions, agentPageHref, }) {
933
1009
  }, [activeChatFirstSurfaceTab, chatFirstSessionWatch.target]);
934
1010
  useEffect(() => {
935
1011
  const pending = pendingChatFirstOpenAppRef.current;
936
- if (!pending || chatFirstAppsQuery.isLoading)
1012
+ if (!pending ||
1013
+ chatFirstAppsQuery.isLoading ||
1014
+ chatFirstGrantedAppsQuery.isLoading)
937
1015
  return;
938
1016
  resolveChatFirstOpenApp(pending);
939
1017
  }, [
940
1018
  chatFirstAppsQuery.data,
941
1019
  chatFirstAppsQuery.isError,
942
1020
  chatFirstAppsQuery.isLoading,
1021
+ chatFirstGrantedAppsQuery.data,
1022
+ chatFirstGrantedAppsQuery.isLoading,
943
1023
  resolveChatFirstOpenApp,
944
1024
  ]);
945
1025
  useEffect(() => {
@@ -959,26 +1039,6 @@ export function Layout({ children, extensions, agentPageHref, }) {
959
1039
  chatFirstSurfaceTabsStore,
960
1040
  isChatRoute,
961
1041
  ]);
962
- useEffect(() => {
963
- if (isWorkspaceAppHostRoute) {
964
- if (!sidebarAutoCollapsedRef.current) {
965
- sidebarAutoCollapsedRef.current = true;
966
- sidebarBeforeAppRef.current = sidebarCollapsed;
967
- }
968
- if (!sidebarCollapsed)
969
- setSidebarCollapsed(true);
970
- return;
971
- }
972
- if (!sidebarAutoCollapsedRef.current)
973
- return;
974
- sidebarAutoCollapsedRef.current = false;
975
- const previousSidebarState = sidebarBeforeAppRef.current;
976
- sidebarBeforeAppRef.current = null;
977
- if (previousSidebarState !== null &&
978
- previousSidebarState !== sidebarCollapsed) {
979
- setSidebarCollapsed(previousSidebarState);
980
- }
981
- }, [isWorkspaceAppHostRoute, sidebarCollapsed]);
982
1042
  useEffect(() => {
983
1043
  if (typeof window === "undefined" || isWorkspaceAppHostRoute)
984
1044
  return;
@@ -995,6 +1055,7 @@ export function Layout({ children, extensions, agentPageHref, }) {
995
1055
  closeChatFirstSessionWatch();
996
1056
  persistChatFirstPane({
997
1057
  appId: tab.appId,
1058
+ placement: tab.placement,
998
1059
  ...(tab.path ? { path: tab.path } : {}),
999
1060
  ...(tab.view ? { view: tab.view } : {}),
1000
1061
  });
@@ -1042,8 +1103,14 @@ export function Layout({ children, extensions, agentPageHref, }) {
1042
1103
  if (tab.kind === "app") {
1043
1104
  if (tab.id !== chatFirstSurfaceTabs.activeTabId)
1044
1105
  return null;
1045
- const app = tab.appId
1046
- ? (chatFirstWorkspaceApps.find((candidate) => candidate.id === tab.appId) ?? null)
1106
+ const registration = tab.appId
1107
+ ? (chatFirstAppRegistrations.find((candidate) => candidate.id === tab.appId) ?? null)
1108
+ : null;
1109
+ const app = registration
1110
+ ? {
1111
+ id: registration.id,
1112
+ name: registration.name ?? registration.id,
1113
+ }
1047
1114
  : null;
1048
1115
  return (_jsx(ChatFirstAppPane, { app: app, status: chatFirstEmbedError
1049
1116
  ? "error"
@@ -1087,7 +1154,7 @@ export function Layout({ children, extensions, agentPageHref, }) {
1087
1154
  chatFirstEmbedUrl,
1088
1155
  chatFirstSessionWatch.target,
1089
1156
  chatFirstSurfaceTabs.activeTabId,
1090
- chatFirstWorkspaceApps,
1157
+ chatFirstAppRegistrations,
1091
1158
  closeChatFirstSurfaceTab,
1092
1159
  renderChatFirstWatchChat,
1093
1160
  ]);
@@ -1107,27 +1174,31 @@ export function Layout({ children, extensions, agentPageHref, }) {
1107
1174
  t("dispatch.sidebar.suggestionRouteSlack"),
1108
1175
  t("dispatch.sidebar.suggestionGrantKey"),
1109
1176
  ];
1110
- const appContent = (_jsxs("div", { className: "relative flex h-full min-w-0 flex-1 flex-col overflow-hidden", children: [showHeader ? _jsx(Header, { onOpenMobile: () => setMobileOpen(true) }) : null, _jsx(InvitationBanner, {}), isChatRoute && chatFirstMode ? (_jsx(ChatFirstSurfacePanelToggle, { open: chatFirstSurfacePanel.open, onToggle: chatFirstSurfacePanel.toggle, className: "absolute right-3 top-2 z-[4]" })) : null, isChatRoute && chatFirstMode && chatFirstNotice ? (_jsxs("div", { className: "dispatch-chat-first-notice", role: "status", "aria-live": "polite", children: [_jsx("span", { children: chatFirstNotice }), _jsx("button", { type: "button", onClick: () => setChatFirstNotice(null), "aria-label": "Dismiss app notice", children: "Dismiss" })] })) : null, _jsx("main", { className: cn("flex-1", isChatRoute || isWorkspaceAppHostRoute
1177
+ const appContent = (_jsxs("div", { className: "relative flex h-full min-w-0 flex-1 flex-col overflow-hidden", children: [showHeader ? _jsx(Header, { onOpenMobile: () => setMobileOpen(true) }) : null, _jsx(InvitationBanner, {}), isChatRoute && chatFirstMode && chatFirstHasActiveChat ? (_jsx(ChatFirstSurfacePanelToggle, { open: chatFirstSurfacePanel.open, onToggle: chatFirstSurfacePanel.toggle, className: "absolute right-3 top-2 z-[4]" })) : null, isChatRoute && chatFirstMode && chatFirstNotice ? (_jsxs("div", { className: "dispatch-chat-first-notice", role: "status", "aria-live": "polite", children: [_jsx("span", { children: chatFirstNotice }), _jsx("button", { type: "button", onClick: () => setChatFirstNotice(null), "aria-label": "Dismiss app notice", children: "Dismiss" })] })) : null, _jsx("main", { className: cn("flex-1", isChatRoute || isWorkspaceAppRoute
1111
1178
  ? "min-h-0 overflow-hidden"
1112
1179
  : "overflow-y-auto"), children: showHeader ? (_jsx("div", { className: "mx-auto max-w-7xl space-y-10 px-4 py-6 sm:px-6", children: children })) : (children) })] }));
1113
- const content = isChatRoute ? (_jsxs("div", { className: cn("agent-layout-main-surface flex min-w-0 flex-1 overflow-hidden", chatFirstMode && "dispatch-chat-first-surface"), children: [appContent, chatFirstMode && chatFirstSurfacePanel.open ? (_jsxs(ChatFirstSurfacePanel, { width: chatFirstSurfaceResize.width, onResizePointerDown: chatFirstSurfaceResize.onPointerDown, copy: chatFirstCopy, children: [_jsx(ChatFirstSurfaceTabs, { tabs: chatFirstSurfaceTabs.tabs, activeTabId: chatFirstSurfaceTabs.activeTabId, onActivate: activateChatFirstSurfaceTab, onClose: closeChatFirstSurfaceTab, onCloseOthers: (tab) => {
1114
- activateChatFirstSurfaceTab(tab);
1115
- chatFirstSurfaceTabsStore.closeOthers(tab.id);
1116
- }, onCloseToRight: (tab) => {
1117
- const targetIndex = chatFirstSurfaceTabs.tabs.findIndex((candidate) => candidate.id === tab.id);
1118
- const activeIndex = chatFirstSurfaceTabs.tabs.findIndex((candidate) => candidate.id === chatFirstSurfaceTabs.activeTabId);
1119
- if (activeIndex > targetIndex)
1120
- activateChatFirstSurfaceTab(tab);
1121
- chatFirstSurfaceTabsStore.closeToRight(tab.id);
1122
- }, onCloseAll: closeAllChatFirstSurfaceTabs, onOpenSurface: openChatFirstSurface, copy: chatFirstCopy }), chatFirstSurfaceTabs.tabs.length > 0 ? (_jsx(ChatFirstSurfaceContent, { tabs: chatFirstSurfaceTabs.tabs, activeTabId: chatFirstSurfaceTabs.activeTabId, renderTab: renderChatFirstSurfaceTab })) : null] })) : null] })) : (_jsx(AgentSidebar, { position: "right", defaultOpen: false, agentPageHref: agentPageHref, chatViewTransition: true, storageKey: "dispatch", openOnChatRunning: chatHomeHandoffActive, onFullscreenRequest: openAskAgentFullscreen, emptyStateText: t("dispatch.sidebar.emptyAgentText"), suggestions: sidebarSuggestions, children: appContent }));
1180
+ const chatFirstSurfaceTabsBar = activeChatFirstSurfaceTab?.kind === "app" ? null : (_jsx(ChatFirstSurfaceTabs, { tabs: chatFirstSurfaceTabs.tabs, activeTabId: chatFirstSurfaceTabs.activeTabId, onActivate: activateChatFirstSurfaceTab, onClose: closeChatFirstSurfaceTab, onCloseOthers: (tab) => {
1181
+ activateChatFirstSurfaceTab(tab);
1182
+ chatFirstSurfaceTabsStore.closeOthers(tab.id);
1183
+ }, onCloseToRight: (tab) => {
1184
+ const targetIndex = chatFirstSurfaceTabs.tabs.findIndex((candidate) => candidate.id === tab.id);
1185
+ const activeIndex = chatFirstSurfaceTabs.tabs.findIndex((candidate) => candidate.id === chatFirstSurfaceTabs.activeTabId);
1186
+ if (activeIndex > targetIndex)
1187
+ activateChatFirstSurfaceTab(tab);
1188
+ chatFirstSurfaceTabsStore.closeToRight(tab.id);
1189
+ }, onCloseAll: closeAllChatFirstSurfaceTabs, onOpenSurface: openChatFirstSurface, apps: chatFirstAppItems, onOpenApp: (app) => openChatFirstPane({ appId: app.id }, "side"), renderAppIcon: (app) => (_jsx(AppIcon, { id: app.id, name: app.name, size: "sm", className: "size-7 rounded-lg" })), copy: chatFirstCopy }));
1190
+ const chatFirstSurfaceContent = chatFirstSurfaceTabs.tabs.length > 0 ? (_jsx(ChatFirstSurfaceContent, { tabs: chatFirstSurfaceTabs.tabs, activeTabId: chatFirstSurfaceTabs.activeTabId, renderTab: renderChatFirstSurfaceTab })) : null;
1191
+ const content = isChatRoute ? (_jsx("div", { className: cn("agent-layout-main-surface flex min-w-0 flex-1 overflow-hidden", chatFirstMode && "dispatch-chat-first-surface"), children: chatFirstAppTakesMain ? (_jsx("div", { className: "flex min-w-0 flex-1 flex-col overflow-hidden", "data-dispatch-chat-first-main-app": true, children: chatFirstSurfaceContent })) : (_jsxs(_Fragment, { children: [appContent, chatFirstMode &&
1192
+ chatFirstHasActiveChat &&
1193
+ chatFirstSurfacePanel.open ? (_jsxs(ChatFirstSurfacePanel, { width: chatFirstSurfaceResize.width, onResizePointerDown: chatFirstSurfaceResize.onPointerDown, copy: chatFirstCopy, children: [chatFirstSurfaceTabsBar, chatFirstSurfaceContent] })) : null] })) })) : isWorkspaceAppRoute ? (appContent) : (_jsx(AgentSidebar, { position: "right", defaultOpen: false, agentPageHref: agentPageHref, chatViewTransition: true, storageKey: "dispatch", openOnChatRunning: chatHomeHandoffActive, onFullscreenRequest: openAskAgentFullscreen, emptyStateText: t("dispatch.sidebar.emptyAgentText"), suggestions: sidebarSuggestions, children: appContent }));
1123
1194
  return (_jsx(DispatchExtensionsContext.Provider, { value: extensions, children: _jsx(HeaderActionsProvider, { children: _jsxs("div", { className: "agent-layout-shell flex h-screen w-full overflow-hidden bg-background", children: [_jsx("aside", { "data-collapsed": sidebarCollapsed ? "true" : "false", className: cn("agent-layout-left-drawer hidden shrink-0 flex-col border-e !border-e-sidebar-border bg-sidebar text-sidebar-foreground transition-[width] duration-200 ease-out lg:flex", sidebarCollapsed ? "w-14" : "w-56"), children: _jsx(NavContent, { extensions: extensions, chatFirstMode: chatFirstMode, chatFirstEmbedded: chatFirstEmbedded, collapsed: sidebarCollapsed, chatFirstAppLayout: chatFirstAppLayout, onChatFirstAppLayoutChange: persistChatFirstAppLayout, chatFirstApps: chatFirstAppItems, chatFirstAppsLoading: chatFirstAppsQuery.isLoading, chatFirstAppsError: chatFirstAppsQuery.isError
1124
1195
  ? chatFirstCopy("appsLoadError")
1125
1196
  : null, chatFirstActiveAppId: activeChatFirstSurfaceTab?.kind === "app"
1126
1197
  ? activeChatFirstSurfaceTab.appId
1127
- : undefined, onChatFirstAppOpen: (app) => openChatFirstPane({ appId: app.id }), onChatFirstAppsRetry: () => void chatFirstAppsQuery.refetch(), collapsible: true, onCollapsedChange: setSidebarCollapsed }) }), _jsx(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: _jsxs(SheetContent, { side: "left", className: "w-72 p-0 bg-sidebar text-sidebar-foreground [&>button]:hidden", children: [_jsx(SheetTitle, { className: "sr-only", children: t("dispatch.nav.navigation") }), _jsx(SheetDescription, { className: "sr-only", children: t("dispatch.nav.navigationDescription") }), _jsx("div", { className: "flex h-full w-full flex-col", children: _jsx(NavContent, { extensions: extensions, chatFirstMode: chatFirstMode, chatFirstEmbedded: chatFirstEmbedded, collapsed: false, chatFirstAppLayout: chatFirstAppLayout, onChatFirstAppLayoutChange: persistChatFirstAppLayout, chatFirstApps: chatFirstAppItems, chatFirstAppsLoading: chatFirstAppsQuery.isLoading, chatFirstAppsError: chatFirstAppsQuery.isError
1198
+ : undefined, onChatFirstAppOpen: (app) => openChatFirstPane({ appId: app.id }, "main"), onChatFirstAppsRetry: () => void chatFirstAppsQuery.refetch(), collapsible: true, onCollapsedChange: setSidebarCollapsed }) }), _jsx(Sheet, { open: mobileOpen, onOpenChange: setMobileOpen, children: _jsxs(SheetContent, { side: "left", className: "w-72 bg-sidebar p-0 text-sidebar-foreground [&>button]:hidden", children: [_jsx(SheetTitle, { className: "sr-only", children: t("dispatch.nav.navigation") }), _jsx(SheetDescription, { className: "sr-only", children: t("dispatch.nav.navigationDescription") }), _jsx("div", { className: "flex h-full w-full flex-col", children: _jsx(NavContent, { extensions: extensions, chatFirstMode: chatFirstMode, chatFirstEmbedded: chatFirstEmbedded, collapsed: false, chatFirstAppLayout: chatFirstAppLayout, onChatFirstAppLayoutChange: persistChatFirstAppLayout, chatFirstApps: chatFirstAppItems, chatFirstAppsLoading: chatFirstAppsQuery.isLoading, chatFirstAppsError: chatFirstAppsQuery.isError
1128
1199
  ? chatFirstCopy("appsLoadError")
1129
1200
  : null, chatFirstActiveAppId: activeChatFirstSurfaceTab?.kind === "app"
1130
1201
  ? activeChatFirstSurfaceTab.appId
1131
- : undefined, onChatFirstAppOpen: (app) => openChatFirstPane({ appId: app.id }), onChatFirstAppsRetry: () => void chatFirstAppsQuery.refetch(), onNavigate: () => setMobileOpen(false) }) })] }) }), content] }) }) }));
1202
+ : undefined, onChatFirstAppOpen: (app) => openChatFirstPane({ appId: app.id }, "main"), onChatFirstAppsRetry: () => void chatFirstAppsQuery.refetch(), onNavigate: () => setMobileOpen(false) }) })] }) }), content] }) }) }));
1132
1203
  }
1133
1204
  //# sourceMappingURL=Layout.js.map