@agent-native/dispatch 0.25.1 → 0.27.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.
Files changed (30) hide show
  1. package/dist/actions/provider-api-register.d.ts +14 -14
  2. package/dist/components/create-app-popover.d.ts +7 -2
  3. package/dist/components/create-app-popover.d.ts.map +1 -1
  4. package/dist/components/create-app-popover.js +4 -3
  5. package/dist/components/create-app-popover.js.map +1 -1
  6. package/dist/components/index.d.ts +1 -0
  7. package/dist/components/index.d.ts.map +1 -1
  8. package/dist/components/index.js +1 -0
  9. package/dist/components/index.js.map +1 -1
  10. package/dist/components/layout/Layout.d.ts.map +1 -1
  11. package/dist/components/layout/Layout.js +42 -2
  12. package/dist/components/layout/Layout.js.map +1 -1
  13. package/dist/components/simple-agents-panel.d.ts +4 -1
  14. package/dist/components/simple-agents-panel.d.ts.map +1 -1
  15. package/dist/components/simple-agents-panel.js +46 -13
  16. package/dist/components/simple-agents-panel.js.map +1 -1
  17. package/dist/components/workspace-app-host.d.ts +3 -1
  18. package/dist/components/workspace-app-host.d.ts.map +1 -1
  19. package/dist/components/workspace-app-host.js +87 -5
  20. package/dist/components/workspace-app-host.js.map +1 -1
  21. package/package.json +3 -3
  22. package/src/components/create-app-popover.spec.tsx +9 -1
  23. package/src/components/create-app-popover.tsx +9 -1
  24. package/src/components/index.ts +1 -0
  25. package/src/components/layout/Layout.app-chat.spec.ts +22 -0
  26. package/src/components/layout/Layout.tsx +72 -3
  27. package/src/components/simple-agents-panel.spec.tsx +81 -1
  28. package/src/components/simple-agents-panel.tsx +156 -71
  29. package/src/components/workspace-app-host.spec.tsx +57 -0
  30. package/src/components/workspace-app-host.tsx +139 -3
@@ -1 +1 @@
1
- {"version":3,"file":"workspace-app-host.js","sourceRoot":"","sources":["../../src/components/workspace-app-host.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n ChatFirstAppPane,\n defaultChatFirstCopy,\n type ChatFirstCopy,\n} from \"@agent-native/core/client/chat-first\";\nimport { useFeatureFlag } from \"@agent-native/core/client/feature-flags\";\nimport {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport { withBuilderUtmTrackingParams } from \"@agent-native/core/shared/builder-link-tracking\";\nimport { IconArrowLeft, IconClockHour4 } from \"@tabler/icons-react\";\nimport { useEffect, useMemo, useRef, useState } from \"react\";\nimport { Link } from \"react-router\";\n\nimport { isEmbedSessionExpiredMessage } from \"../lib/embed-session-recovery\";\nimport {\n workspaceAppDirectHref,\n workspaceAppEmbedTarget,\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../lib/workspace-apps\";\nimport { DISPATCH_WORKSPACE_SSO_FLAG } from \"../shared/feature-flags\";\nimport { ActionQueryError } from \"./action-query-error\";\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Skeleton } from \"./ui/skeleton\";\n\ninterface EmbedSessionResult {\n startUrl: string;\n}\n\ninterface EmbedSessionInput {\n app?: string;\n path?: string;\n url?: string;\n chrome: \"minimal\";\n}\n\nexport function buildChatFirstEmbedSessionInput(\n appId: string,\n path: string,\n): EmbedSessionInput {\n return { app: appId, path, chrome: \"minimal\" };\n}\n\nexport interface WorkspaceAppFrameApp {\n id: string;\n name: string;\n path?: string | null;\n url?: string | null;\n}\n\ninterface WorkspaceAppFrameProps {\n app: WorkspaceAppFrameApp;\n /** Chat-first app tabs use their own route while standalone hosts use app metadata. */\n embedPath?: string;\n copy?: ChatFirstCopy;\n}\n\nexport function WorkspaceAppFrame({\n app,\n embedPath,\n copy = defaultChatFirstCopy,\n}: WorkspaceAppFrameProps) {\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [embedError, setEmbedError] = useState<Error | null>(null);\n const [embedAttempt, setEmbedAttempt] = useState(0);\n const embedFrameRef = useRef<HTMLIFrameElement>(null);\n const workspaceSsoEnabled = useFeatureFlag(DISPATCH_WORKSPACE_SSO_FLAG.key);\n const createEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create_embed_session\", {\n skipActionQueryInvalidation: true,\n });\n const createWorkspaceSsoEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create-workspace-app-embed-session\", {\n skipActionQueryInvalidation: true,\n });\n const appHref = workspaceAppHref({\n id: app.id,\n name: app.name,\n path: app.path ?? \"\",\n url: app.url,\n });\n const embedInput = useMemo<EmbedSessionInput | null>(() => {\n if (embedPath !== undefined) {\n return buildChatFirstEmbedSessionInput(app.id, embedPath);\n }\n if (!appHref) return null;\n return {\n app: app.id,\n ...workspaceAppEmbedTarget({ path: app.path ?? \"\", url: app.url }),\n chrome: \"minimal\",\n };\n }, [app.id, app.path, app.url, appHref, embedPath]);\n\n useEffect(() => {\n if (!embedInput) return;\n let cancelled = false;\n setEmbedUrl(null);\n setEmbedError(null);\n const createSession = workspaceSsoEnabled\n ? createWorkspaceSsoEmbedSession\n : createEmbedSession;\n void createSession\n .mutateAsync(embedInput)\n .then((result) => {\n if (!cancelled) setEmbedUrl(result.startUrl);\n })\n .catch((cause: unknown) => {\n if (cancelled) return;\n setEmbedUrl(\n workspaceAppDirectHref(\n { path: app.path ?? \"\", url: app.url },\n embedPath ?? \"/\",\n ),\n );\n setEmbedError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n });\n return () => {\n cancelled = true;\n };\n }, [\n app.id,\n app.path,\n app.url,\n createEmbedSession.mutateAsync,\n createWorkspaceSsoEmbedSession.mutateAsync,\n embedInput,\n embedPath,\n embedAttempt,\n workspaceSsoEnabled,\n ]);\n\n useEffect(() => {\n const handleEmbedSessionExpired = (event: MessageEvent) => {\n if (\n !isEmbedSessionExpiredMessage(event, embedFrameRef.current, embedUrl)\n ) {\n return;\n }\n setEmbedAttempt((attempt) => attempt + 1);\n };\n\n window.addEventListener(\"message\", handleEmbedSessionExpired);\n return () =>\n window.removeEventListener(\"message\", handleEmbedSessionExpired);\n }, [embedUrl]);\n\n return (\n <ChatFirstAppPane\n app={app}\n status={\n embedUrl\n ? \"ready\"\n : embedError\n ? \"error\"\n : embedInput\n ? \"loading\"\n : \"unresolved\"\n }\n embedUrl={embedUrl}\n errorMessage={embedError?.message}\n onRetry={\n embedInput ? () => setEmbedAttempt((attempt) => attempt + 1) : undefined\n }\n renderEmbed={({ url, title }) => (\n <iframe\n key={url + \":\" + embedAttempt}\n data-dispatch-workspace-app-frame\n src={url}\n title={title ?? app.name}\n ref={embedFrameRef}\n referrerPolicy=\"no-referrer\"\n allow=\"clipboard-read; clipboard-write\"\n className=\"h-full w-full border-0 bg-background\"\n />\n )}\n copy={copy}\n />\n );\n}\n\nexport function WorkspaceAppHost({ appId }: { appId?: string }) {\n const t = useT();\n const appsQuery = useActionQuery(\"list-workspace-apps\", {\n includeAgentCards: false,\n });\n const { data: apps = [], isLoading } = appsQuery;\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n\n if (appsQuery.isError) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl\">\n <ActionQueryError\n error={appsQuery.error}\n onRetry={() => void appsQuery.refetch()}\n />\n </div>\n </div>\n );\n }\n\n if (isLoading && !app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl space-y-3 rounded-xl border bg-card p-6\">\n <Skeleton className=\"h-5 w-48\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-2/3\" />\n </div>\n </div>\n );\n }\n\n if (!app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.appNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.pageNotFoundDescription\")}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (app.status === \"pending\") {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.builderUrl ? (\n <Button asChild>\n <a\n href={withBuilderUtmTrackingParams(app.builderUrl, {\n campaign: \"product\",\n content: \"dispatch_branch\",\n })}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {t(\"dispatch.pages.openBuilderBranch\", {\n defaultValue: \"Open in Builder\",\n })}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-dispatch-workspace-app-host\n className=\"flex h-full min-h-0 flex-col bg-background\"\n >\n <div className=\"min-h-0 flex-1 bg-muted/20\">\n <WorkspaceAppFrame app={app} />\n </div>\n </div>\n );\n}\n\nconst MAX_KEEP_ALIVE_APPS = 3;\n\nexport function WorkspaceAppKeepAlive({\n activeAppId,\n}: {\n activeAppId: string | null;\n}) {\n const [visitedAppIds, setVisitedAppIds] = useState<string[]>(() =>\n activeAppId ? [activeAppId] : [],\n );\n\n useEffect(() => {\n if (!activeAppId) return;\n setVisitedAppIds((current) =>\n [activeAppId, ...current.filter((appId) => appId !== activeAppId)].slice(\n 0,\n MAX_KEEP_ALIVE_APPS,\n ),\n );\n }, [activeAppId]);\n\n const renderedAppIds =\n activeAppId && !visitedAppIds.includes(activeAppId)\n ? [activeAppId, ...visitedAppIds]\n : visitedAppIds;\n\n return (\n <div\n data-dispatch-workspace-app-cache\n className={activeAppId ? \"absolute inset-0 overflow-hidden\" : \"hidden\"}\n >\n {renderedAppIds.map((appId) => {\n const active = appId === activeAppId;\n return (\n <div\n key={appId}\n data-dispatch-workspace-app-cache-entry={appId}\n aria-hidden={!active}\n className={active ? \"h-full min-h-0\" : \"hidden\"}\n >\n <WorkspaceAppHost appId={appId} />\n </div>\n );\n })}\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"workspace-app-host.js","sourceRoot":"","sources":["../../src/components/workspace-app-host.tsx"],"names":[],"mappings":"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","sourcesContent":["import { AgentSidebar } from \"@agent-native/core/client/agent-chat\";\nimport {\n ChatFirstAppPane,\n defaultChatFirstCopy,\n type ChatFirstCopy,\n} from \"@agent-native/core/client/chat-first\";\nimport { useFeatureFlag } from \"@agent-native/core/client/feature-flags\";\nimport {\n useActionMutation,\n useActionQuery,\n} from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport { withBuilderUtmTrackingParams } from \"@agent-native/core/shared/builder-link-tracking\";\nimport {\n IconArrowLeft,\n IconArrowUpRight,\n IconClockHour4,\n IconLock,\n} from \"@tabler/icons-react\";\nimport { useTheme } from \"next-themes\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { Link } from \"react-router\";\n\nimport { isEmbedSessionExpiredMessage } from \"../lib/embed-session-recovery\";\nimport {\n workspaceAppDirectHref,\n workspaceAppEmbedTarget,\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../lib/workspace-apps\";\nimport { DISPATCH_WORKSPACE_SSO_FLAG } from \"../shared/feature-flags\";\nimport { ActionQueryError } from \"./action-query-error\";\nimport { Badge } from \"./ui/badge\";\nimport { Button } from \"./ui/button\";\nimport { Skeleton } from \"./ui/skeleton\";\n\ninterface EmbedSessionResult {\n startUrl: string;\n}\n\ninterface EmbedSessionInput {\n app?: string;\n path?: string;\n url?: string;\n chrome: \"minimal\";\n}\n\ntype WorkspaceAppTheme = \"light\" | \"dark\";\ntype WorkspaceAppAuthState = \"unknown\" | \"authenticated\" | \"unauthenticated\";\n\nfunction resolveWorkspaceAppAuthState(\n rawUrl: string | null,\n): WorkspaceAppAuthState {\n if (!rawUrl) return \"unknown\";\n try {\n const lastSegment = new URL(rawUrl, window.location.origin).pathname\n .split(\"/\")\n .filter(Boolean)\n .at(-1)\n ?.toLowerCase();\n if (\n lastSegment === \"sign-in\" ||\n lastSegment === \"login\" ||\n lastSegment === \"signup\"\n ) {\n return \"unauthenticated\";\n }\n return \"authenticated\";\n } catch {\n return \"unknown\";\n }\n}\n\nfunction buildWorkspaceAppThemeUpdate(theme: WorkspaceAppTheme) {\n return {\n type: \"agent-native-theme-update\" as const,\n theme,\n isDark: theme === \"dark\",\n };\n}\n\nexport function buildChatFirstEmbedSessionInput(\n appId: string,\n path: string,\n): EmbedSessionInput {\n return { app: appId, path, chrome: \"minimal\" };\n}\n\nexport interface WorkspaceAppFrameApp {\n id: string;\n name: string;\n path?: string | null;\n url?: string | null;\n}\n\ninterface WorkspaceAppFrameProps {\n app: WorkspaceAppFrameApp;\n /** Chat-first app tabs use their own route while standalone hosts use app metadata. */\n embedPath?: string;\n /** Chat-first app surfaces own the parent chat rail around the iframe. */\n chatSidebar?: boolean;\n copy?: ChatFirstCopy;\n}\n\nexport function WorkspaceAppFrame({\n app,\n embedPath,\n chatSidebar = false,\n copy = defaultChatFirstCopy,\n}: WorkspaceAppFrameProps) {\n const { resolvedTheme } = useTheme();\n const theme: WorkspaceAppTheme =\n resolvedTheme === \"dark\" || resolvedTheme === \"light\"\n ? resolvedTheme\n : typeof document !== \"undefined\" &&\n document.documentElement.classList.contains(\"dark\")\n ? \"dark\"\n : \"light\";\n const [embedUrl, setEmbedUrl] = useState<string | null>(null);\n const [embedError, setEmbedError] = useState<Error | null>(null);\n const [embedAttempt, setEmbedAttempt] = useState(0);\n const [authState, setAuthState] = useState<WorkspaceAppAuthState>(\"unknown\");\n const embedFrameRef = useRef<HTMLIFrameElement>(null);\n const postThemeToFrame = useCallback(() => {\n embedFrameRef.current?.contentWindow?.postMessage(\n buildWorkspaceAppThemeUpdate(theme),\n \"*\",\n );\n }, [theme]);\n const handleFrameLoad = useCallback(() => {\n postThemeToFrame();\n const frame = embedFrameRef.current;\n let frameUrl = embedUrl;\n try {\n frameUrl = frame?.contentWindow?.location.href ?? frameUrl;\n // coercion-ok: Cross-origin frames cannot expose location; their auth state arrives by message.\n } catch {\n // Cross-origin app frames report their auth state through postMessage.\n }\n const nextAuthState = resolveWorkspaceAppAuthState(frameUrl);\n if (nextAuthState !== \"unknown\") setAuthState(nextAuthState);\n }, [embedUrl, postThemeToFrame]);\n const workspaceSsoEnabled = useFeatureFlag(DISPATCH_WORKSPACE_SSO_FLAG.key);\n const createEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create_embed_session\", {\n skipActionQueryInvalidation: true,\n });\n const createWorkspaceSsoEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create-workspace-app-embed-session\", {\n skipActionQueryInvalidation: true,\n });\n const appHref = workspaceAppHref({\n id: app.id,\n name: app.name,\n path: app.path ?? \"\",\n url: app.url,\n });\n const embedInput = useMemo<EmbedSessionInput | null>(() => {\n if (embedPath !== undefined) {\n return buildChatFirstEmbedSessionInput(app.id, embedPath);\n }\n if (!appHref) return null;\n return {\n app: app.id,\n ...workspaceAppEmbedTarget({ path: app.path ?? \"\", url: app.url }),\n chrome: \"minimal\",\n };\n }, [app.id, app.path, app.url, appHref, embedPath]);\n\n useEffect(() => {\n if (!embedInput) return;\n let cancelled = false;\n setEmbedUrl(null);\n setEmbedError(null);\n setAuthState(\"unknown\");\n const createSession = workspaceSsoEnabled\n ? createWorkspaceSsoEmbedSession\n : createEmbedSession;\n void createSession\n .mutateAsync(embedInput)\n .then((result) => {\n if (!cancelled) setEmbedUrl(result.startUrl);\n })\n .catch((cause: unknown) => {\n if (cancelled) return;\n setEmbedUrl(\n workspaceAppDirectHref(\n { path: app.path ?? \"\", url: app.url },\n embedPath ?? \"/\",\n ),\n );\n setEmbedError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n });\n return () => {\n cancelled = true;\n };\n }, [\n app.id,\n app.path,\n app.url,\n createEmbedSession.mutateAsync,\n createWorkspaceSsoEmbedSession.mutateAsync,\n embedInput,\n embedPath,\n embedAttempt,\n workspaceSsoEnabled,\n ]);\n\n useEffect(() => {\n const handleEmbedSessionExpired = (event: MessageEvent) => {\n if (\n !isEmbedSessionExpiredMessage(event, embedFrameRef.current, embedUrl)\n ) {\n return;\n }\n setEmbedAttempt((attempt) => attempt + 1);\n };\n\n window.addEventListener(\"message\", handleEmbedSessionExpired);\n return () =>\n window.removeEventListener(\"message\", handleEmbedSessionExpired);\n }, [embedUrl]);\n\n useEffect(() => {\n const handleAuthState = (event: MessageEvent) => {\n const frame = embedFrameRef.current;\n if (!frame || event.source !== frame.contentWindow) return;\n if (event.data?.type !== \"agentNative.authState\") return;\n const status = event.data.data?.status;\n if (status === \"authenticated\" || status === \"unauthenticated\") {\n setAuthState(status);\n }\n };\n\n window.addEventListener(\"message\", handleAuthState);\n return () => window.removeEventListener(\"message\", handleAuthState);\n }, []);\n\n useEffect(() => {\n postThemeToFrame();\n }, [embedUrl, postThemeToFrame]);\n\n const appPane = (\n <ChatFirstAppPane\n app={app}\n status={\n embedUrl\n ? \"ready\"\n : embedError\n ? \"error\"\n : embedInput\n ? \"loading\"\n : \"unresolved\"\n }\n embedUrl={embedUrl}\n errorMessage={embedError?.message}\n onRetry={\n embedInput ? () => setEmbedAttempt((attempt) => attempt + 1) : undefined\n }\n renderEmbed={({ url, title }) => (\n <iframe\n key={url + \":\" + embedAttempt}\n data-dispatch-workspace-app-frame\n src={url}\n title={title ?? app.name}\n ref={embedFrameRef}\n onLoad={handleFrameLoad}\n referrerPolicy=\"no-referrer\"\n allow=\"clipboard-read; clipboard-write\"\n className=\"h-full w-full border-0 bg-background\"\n />\n )}\n copy={copy}\n />\n );\n\n if (!chatSidebar) return appPane;\n\n return (\n <AgentSidebar\n position=\"left\"\n defaultOpen\n openStorageKey=\"dispatch-app-chat\"\n storageKey={`dispatch-app-chat:${app.id}`}\n scope={{\n type: \"workspace-app\",\n id: app.id,\n label: app.name,\n contextKey: `workspace-app:${app.id}`,\n }}\n agentChatSurface=\"app\"\n showTabBar\n suppressInlineOpenApp\n dynamicSuggestions={false}\n suggestions={[]}\n emptyStateText={`Ask about ${app.name}`}\n composerSlot={\n authState === \"unauthenticated\" ? (\n <div className=\"flex shrink-0 items-center px-3 pb-1\">\n <button\n type=\"button\"\n data-dispatch-app-sign-in\n aria-label={`Sign in to ${app.name} on the right`}\n title={`Sign in to ${app.name} on the right`}\n onClick={() => embedFrameRef.current?.focus()}\n className=\"inline-flex h-6 shrink-0 items-center gap-1 rounded-full border border-border/70 bg-background/60 px-2 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n >\n <IconLock size={12} stroke={1.8} />\n <span>Sign in on the right</span>\n <IconArrowUpRight size={12} stroke={1.8} />\n </button>\n </div>\n ) : null\n }\n >\n {appPane}\n </AgentSidebar>\n );\n}\n\nexport function WorkspaceAppHost({ appId }: { appId?: string }) {\n const t = useT();\n const appsQuery = useActionQuery(\"list-workspace-apps\", {\n includeAgentCards: false,\n });\n const { data: apps = [], isLoading } = appsQuery;\n const app = useMemo(\n () =>\n (apps as WorkspaceAppSummary[]).find((item) => item.id === appId) ?? null,\n [appId, apps],\n );\n\n if (appsQuery.isError) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl\">\n <ActionQueryError\n error={appsQuery.error}\n onRetry={() => void appsQuery.refetch()}\n />\n </div>\n </div>\n );\n }\n\n if (isLoading && !app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl space-y-3 rounded-xl border bg-card p-6\">\n <Skeleton className=\"h-5 w-48\" />\n <Skeleton className=\"h-4 w-full\" />\n <Skeleton className=\"h-4 w-2/3\" />\n </div>\n </div>\n );\n }\n\n if (!app) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-3\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {t(\"dispatch.pages.appNotFound\")}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.pageNotFoundDescription\")}\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n if (app.status === \"pending\") {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl rounded-xl border bg-card p-6\">\n <Button asChild size=\"sm\" variant=\"ghost\" className=\"-ml-2 mb-4\">\n <Link to=\"/apps\">\n <IconArrowLeft size={15} className=\"mr-1.5\" />\n {t(\"dispatch.nav.apps\")}\n </Link>\n </Button>\n <div className=\"space-y-4\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <h2 className=\"text-base font-semibold text-foreground\">\n {app.name}\n </h2>\n <Badge\n variant=\"outline\"\n className=\"gap-1 border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300\"\n >\n <IconClockHour4 size={12} />\n {t(\"dispatch.pages.building\")}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {t(\"dispatch.pages.appBuildingPrefix\")}{\" \"}\n <span className=\"font-mono text-foreground\">{app.path}</span>{\" \"}\n {t(\"dispatch.pages.appBuildingSuffix\")}\n </p>\n {app.builderUrl ? (\n <Button asChild>\n <a\n href={withBuilderUtmTrackingParams(app.builderUrl, {\n campaign: \"product\",\n content: \"dispatch_branch\",\n })}\n target=\"_blank\"\n rel=\"noreferrer\"\n >\n {t(\"dispatch.pages.openBuilderBranch\", {\n defaultValue: \"Open in Builder\",\n })}\n </a>\n </Button>\n ) : null}\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-dispatch-workspace-app-host\n className=\"flex h-full min-h-0 flex-col bg-background\"\n >\n <div className=\"min-h-0 flex-1 bg-muted/20\">\n <WorkspaceAppFrame app={app} />\n </div>\n </div>\n );\n}\n\nconst MAX_KEEP_ALIVE_APPS = 3;\n\nexport function WorkspaceAppKeepAlive({\n activeAppId,\n}: {\n activeAppId: string | null;\n}) {\n const [visitedAppIds, setVisitedAppIds] = useState<string[]>(() =>\n activeAppId ? [activeAppId] : [],\n );\n\n useEffect(() => {\n if (!activeAppId) return;\n setVisitedAppIds((current) =>\n [activeAppId, ...current.filter((appId) => appId !== activeAppId)].slice(\n 0,\n MAX_KEEP_ALIVE_APPS,\n ),\n );\n }, [activeAppId]);\n\n const renderedAppIds =\n activeAppId && !visitedAppIds.includes(activeAppId)\n ? [activeAppId, ...visitedAppIds]\n : visitedAppIds;\n\n return (\n <div\n data-dispatch-workspace-app-cache\n className={activeAppId ? \"absolute inset-0 overflow-hidden\" : \"hidden\"}\n >\n {renderedAppIds.map((appId) => {\n const active = appId === activeAppId;\n return (\n <div\n key={appId}\n data-dispatch-workspace-app-cache-entry={appId}\n aria-hidden={!active}\n className={active ? \"h-full min-h-0\" : \"hidden\"}\n >\n <WorkspaceAppHost appId={appId} />\n </div>\n );\n })}\n </div>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.25.1",
3
+ "version": "0.27.0",
4
4
  "description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -87,7 +87,7 @@
87
87
  "tailwind-merge": "^3.5.0",
88
88
  "vaul": "^1.1.2",
89
89
  "zod": "^4.3.6",
90
- "@agent-native/toolkit": "0.15.0"
90
+ "@agent-native/toolkit": "0.16.0"
91
91
  },
92
92
  "devDependencies": {
93
93
  "@react-router/dev": "^8.1.0",
@@ -101,7 +101,7 @@
101
101
  "typescript-7": "npm:typescript@^7.0.2",
102
102
  "vite": "8.1.0",
103
103
  "vitest": "^4.1.5",
104
- "@agent-native/core": "0.157.0"
104
+ "@agent-native/core": "0.157.4"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "@agent-native/core": ">=0.8.0",
@@ -154,7 +154,7 @@ describe("CreateAppFlow", () => {
154
154
 
155
155
  async function renderAndSubmit(
156
156
  prompt: string,
157
- props: { onClose?: () => void } = {},
157
+ props: { onClose?: () => void; onCreated?: () => void } = {},
158
158
  ) {
159
159
  await act(async () => {
160
160
  root.render(React.createElement(CreateAppFlow, props));
@@ -237,6 +237,14 @@ describe("CreateAppFlow", () => {
237
237
  expect(onClose).toHaveBeenCalledTimes(1);
238
238
  });
239
239
 
240
+ it("notifies the parent after Builder accepts app creation", async () => {
241
+ const onCreated = vi.fn();
242
+
243
+ await renderAndSubmit("Build a quality dashboard", { onCreated });
244
+
245
+ expect(onCreated).toHaveBeenCalledTimes(1);
246
+ });
247
+
240
248
  it("renders the error affordance and a Try again control for builder-error, without a Connect Builder control", async () => {
241
249
  startWorkspaceAppCreationResponse.result = {
242
250
  mode: "builder-unavailable",
@@ -60,6 +60,10 @@ interface CreateAppPopoverProps {
60
60
  * Override the popover alignment. Defaults to "center" with a 10px offset.
61
61
  */
62
62
  align?: "start" | "center" | "end";
63
+ /**
64
+ * Called after the server accepts a Builder app creation request.
65
+ */
66
+ onCreated?: () => void;
63
67
  }
64
68
 
65
69
  async function fetchJson(url: string, init?: RequestInit): Promise<any> {
@@ -107,9 +111,11 @@ function isErrorFailureReason(reason: string | null): boolean {
107
111
  */
108
112
  export function CreateAppFlow({
109
113
  onClose,
114
+ onCreated,
110
115
  className = "",
111
116
  }: {
112
117
  onClose?: () => void;
118
+ onCreated?: () => void;
113
119
  className?: string;
114
120
  }) {
115
121
  const [step, setStep] = useState<"prompt" | "access">("prompt");
@@ -269,6 +275,7 @@ export function CreateAppFlow({
269
275
  },
270
276
  );
271
277
  if (result?.mode === "builder") {
278
+ onCreated?.();
272
279
  setBranchUrl(result?.url || null);
273
280
  setStatusMessage("Builder branch created.");
274
281
  } else if (result?.mode === "local-agent") {
@@ -634,6 +641,7 @@ export function CreateAppFlow({
634
641
  export function CreateAppPopover({
635
642
  trigger,
636
643
  align = "center",
644
+ onCreated,
637
645
  }: CreateAppPopoverProps) {
638
646
  const [open, setOpen] = useState(false);
639
647
  return (
@@ -656,7 +664,7 @@ export function CreateAppPopover({
656
664
  sideOffset={10}
657
665
  className="w-[calc(100vw-2rem)] rounded-xl p-3 shadow-xl sm:w-[460px]"
658
666
  >
659
- <CreateAppFlow onClose={() => setOpen(false)} />
667
+ <CreateAppFlow onClose={() => setOpen(false)} onCreated={onCreated} />
660
668
  </PopoverContent>
661
669
  </Popover>
662
670
  );
@@ -18,3 +18,4 @@ export { CreateAppPopover, CreateAppFlow } from "./create-app-popover.js";
18
18
  export { AppKeysPopover } from "./app-keys-popover.js";
19
19
  export { ActionQueryError } from "./action-query-error.js";
20
20
  export { SimpleAgentsPanel } from "./simple-agents-panel.js";
21
+ export { WorkspaceAppCard } from "./workspace-app-card.js";
@@ -0,0 +1,22 @@
1
+ import { readFileSync } from "node:fs";
2
+
3
+ import { describe, expect, it } from "vitest";
4
+
5
+ describe("Dispatch workspace app chat rail", () => {
6
+ it("owns the iframe chat toggle and persists app-specific threads", () => {
7
+ const source = readFileSync(
8
+ new URL("./Layout.tsx", import.meta.url),
9
+ "utf8",
10
+ );
11
+
12
+ expect(source).toContain('openStorageKey="dispatch-app-chat"');
13
+ expect(source).toContain(
14
+ "storageKey={`dispatch-app-chat:${workspaceAppId}`}",
15
+ );
16
+ expect(source).toContain("data-dispatch-workspace-app-frame");
17
+ expect(source).toContain("chatSidebar");
18
+ expect(source).toContain(
19
+ 'window.dispatchEvent(new Event("agent-panel:toggle"))',
20
+ );
21
+ });
22
+ });
@@ -1941,6 +1941,39 @@ export function Layout({
1941
1941
  setSidebarCollapsed(true);
1942
1942
  }, [isWorkspaceAppRoute, localPathname]);
1943
1943
 
1944
+ useEffect(() => {
1945
+ if (!workspaceAppRouteActive && !chatFirstAppTakesMain) return;
1946
+
1947
+ const handleWorkspaceAppMessage = (event: MessageEvent) => {
1948
+ if (event.data?.type !== "agentNative.toggleSidebar") return;
1949
+ const frame = [
1950
+ ...document.querySelectorAll<HTMLIFrameElement>(
1951
+ "[data-dispatch-workspace-app-frame]",
1952
+ ),
1953
+ ].find(
1954
+ (candidate) =>
1955
+ candidate
1956
+ .closest("[data-chat-first-surface-content]")
1957
+ ?.getAttribute("aria-hidden") !== "true",
1958
+ );
1959
+ if (!(frame instanceof HTMLIFrameElement)) return;
1960
+ if (event.source !== frame.contentWindow) return;
1961
+
1962
+ const open = event.data.data?.open;
1963
+ if (open === true) {
1964
+ window.dispatchEvent(new Event("agent-panel:open"));
1965
+ } else if (open === false) {
1966
+ window.dispatchEvent(new Event("agent-panel:close"));
1967
+ } else {
1968
+ window.dispatchEvent(new Event("agent-panel:toggle"));
1969
+ }
1970
+ };
1971
+
1972
+ window.addEventListener("message", handleWorkspaceAppMessage);
1973
+ return () =>
1974
+ window.removeEventListener("message", handleWorkspaceAppMessage);
1975
+ }, [chatFirstAppTakesMain, workspaceAppRouteActive]);
1976
+
1944
1977
  useEffect(() => {
1945
1978
  if (typeof window === "undefined" || isWorkspaceAppHostRoute) return;
1946
1979
  try {
@@ -2077,6 +2110,7 @@ export function Layout({
2077
2110
  url: registration.url,
2078
2111
  }}
2079
2112
  embedPath={embedPath}
2113
+ chatSidebar
2080
2114
  copy={chatFirstCopy}
2081
2115
  />
2082
2116
  );
@@ -2300,6 +2334,43 @@ export function Layout({
2300
2334
  renderTab={renderChatFirstSurfaceTab}
2301
2335
  />
2302
2336
  ) : null;
2337
+ const workspaceAppChatName =
2338
+ (workspaceAppId
2339
+ ? chatFirstAppRegistrations.find(
2340
+ (app) => app.id.toLowerCase() === workspaceAppId.toLowerCase(),
2341
+ )?.name
2342
+ : null) ??
2343
+ workspaceAppId ??
2344
+ "Workspace app";
2345
+ const workspaceAppContent =
2346
+ workspaceAppRouteActive && workspaceAppId ? (
2347
+ <AgentSidebar
2348
+ position="left"
2349
+ defaultOpen
2350
+ openStorageKey="dispatch-app-chat"
2351
+ storageKey={`dispatch-app-chat:${workspaceAppId}`}
2352
+ scope={{
2353
+ type: "workspace-app",
2354
+ id: workspaceAppId,
2355
+ label: workspaceAppChatName,
2356
+ contextKey: `workspace-app:${workspaceAppId}`,
2357
+ }}
2358
+ agentChatSurface="app"
2359
+ showTabBar
2360
+ suppressInlineOpenApp
2361
+ dynamicSuggestions={false}
2362
+ suggestions={[]}
2363
+ emptyStateText={`Ask about ${workspaceAppChatName}`}
2364
+ agentPageHref={agentPageHref}
2365
+ onFullscreenRequest={openAskAgentFullscreen}
2366
+ >
2367
+ <WorkspaceAppKeepAlive activeAppId={workspaceAppId} />
2368
+ </AgentSidebar>
2369
+ ) : (
2370
+ <WorkspaceAppKeepAlive
2371
+ activeAppId={workspaceAppRouteActive ? workspaceAppId : null}
2372
+ />
2373
+ );
2303
2374
  const content = isChatRoute ? (
2304
2375
  <div
2305
2376
  className={cn(
@@ -2438,9 +2509,7 @@ export function Layout({
2438
2509
 
2439
2510
  <div className="relative min-w-0 flex-1 overflow-hidden">
2440
2511
  {content}
2441
- <WorkspaceAppKeepAlive
2442
- activeAppId={workspaceAppRouteActive ? workspaceAppId : null}
2443
- />
2512
+ {workspaceAppContent}
2444
2513
  </div>
2445
2514
  </div>
2446
2515
  </HeaderActionsProvider>
@@ -1,10 +1,46 @@
1
- import { describe, expect, it } from "vitest";
1
+ // @vitest-environment happy-dom
2
+ import React, { act } from "react";
3
+ import { createRoot, type Root } from "react-dom/client";
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
5
 
3
6
  import {
4
7
  handleAgentPackMutationSuccess,
5
8
  isPendingWorkspaceResourceApproval,
9
+ SimpleAgentsPanel,
6
10
  } from "./simple-agents-panel";
7
11
 
12
+ const queryState = vi.hoisted(() => ({
13
+ data: [],
14
+ isError: false,
15
+ isLoading: false,
16
+ error: null,
17
+ refetch: vi.fn(),
18
+ }));
19
+
20
+ vi.mock("@agent-native/core/client/agent-chat", () => ({
21
+ navigateWithAgentChatViewTransition: vi.fn(),
22
+ sendToAgentChat: vi.fn(),
23
+ }));
24
+
25
+ vi.mock("@agent-native/core/client/hooks", () => ({
26
+ useActionMutation: () => ({ mutate: vi.fn(), isPending: false }),
27
+ useActionQuery: () => queryState,
28
+ }));
29
+
30
+ vi.mock("@agent-native/core/resources/metadata", () => ({
31
+ parseCustomAgentProfile: vi.fn(),
32
+ }));
33
+
34
+ vi.mock("react-router", () => ({ useNavigate: () => vi.fn() }));
35
+
36
+ vi.mock("sonner", () => ({
37
+ toast: {
38
+ error: vi.fn(),
39
+ info: vi.fn(),
40
+ success: vi.fn(),
41
+ },
42
+ }));
43
+
8
44
  describe("agent pack resource mutations", () => {
9
45
  it("recognizes pending workspace-resource approvals", () => {
10
46
  expect(
@@ -71,3 +107,47 @@ describe("agent pack resource mutations", () => {
71
107
  expect(notifications).toEqual(["Pack file added", "refreshed"]);
72
108
  });
73
109
  });
110
+
111
+ describe("SimpleAgentsPanel", () => {
112
+ let container: HTMLDivElement;
113
+ let root: Root;
114
+
115
+ beforeEach(() => {
116
+ vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
117
+ queryState.data = [];
118
+ queryState.isError = false;
119
+ queryState.isLoading = false;
120
+ queryState.error = null;
121
+ queryState.refetch.mockReset();
122
+ container = document.createElement("div");
123
+ document.body.appendChild(container);
124
+ root = createRoot(container);
125
+ });
126
+
127
+ afterEach(() => {
128
+ act(() => root.unmount());
129
+ container.remove();
130
+ document.body
131
+ .querySelectorAll("[data-radix-portal]")
132
+ .forEach((portal) => portal.remove());
133
+ vi.unstubAllGlobals();
134
+ });
135
+
136
+ it("keeps import and connect available when the workspace has no agents", async () => {
137
+ await act(async () => {
138
+ root.render(<SimpleAgentsPanel />);
139
+ });
140
+
141
+ const importButton = Array.from(container.querySelectorAll("button")).find(
142
+ (button) => button.textContent?.includes("Import or connect"),
143
+ );
144
+ expect(importButton).not.toBeUndefined();
145
+
146
+ await act(async () => {
147
+ importButton?.click();
148
+ });
149
+
150
+ expect(document.body.textContent).toContain("Import an agent");
151
+ expect(document.body.textContent).toContain("Connect endpoint");
152
+ });
153
+ });