@agent-native/dispatch 0.23.5 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/list-connected-agents.d.ts +1 -0
- package/dist/actions/list-connected-agents.js +5 -1
- package/dist/actions/list-connected-agents.js.map +1 -1
- package/dist/actions/list-dispatch-usage-metrics.d.ts +2 -0
- package/dist/actions/list-dispatch-usage-metrics.js +11 -1
- package/dist/actions/list-dispatch-usage-metrics.js.map +1 -1
- package/dist/actions/remix-workspace-template.js +4 -3
- package/dist/actions/remix-workspace-template.js.map +1 -1
- package/dist/actions/scaffold-workspace-app.js +3 -2
- package/dist/actions/scaffold-workspace-app.js.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +3 -2
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/actions/view-screen.js +11 -1
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/app-icon.js +1 -1
- package/dist/components/app-icon.js.map +1 -1
- package/dist/components/connected-app-card.d.ts.map +1 -1
- package/dist/components/connected-app-card.js +2 -1
- package/dist/components/connected-app-card.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +6 -7
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/workspace-template-card.d.ts.map +1 -1
- package/dist/components/workspace-template-card.js +8 -14
- package/dist/components/workspace-template-card.js.map +1 -1
- package/dist/hooks/use-navigation-state.d.ts +2 -0
- package/dist/hooks/use-navigation-state.d.ts.map +1 -1
- package/dist/hooks/use-navigation-state.js +10 -0
- package/dist/hooks/use-navigation-state.js.map +1 -1
- package/dist/lib/embed-session-recovery.d.ts +3 -0
- package/dist/lib/embed-session-recovery.d.ts.map +1 -0
- package/dist/lib/embed-session-recovery.js +18 -0
- package/dist/lib/embed-session-recovery.js.map +1 -0
- package/dist/lib/other-apps.d.ts +2 -0
- package/dist/lib/other-apps.d.ts.map +1 -1
- package/dist/lib/other-apps.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +4 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +7 -0
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/lib/workspace-apps.test.d.ts +2 -0
- package/dist/lib/workspace-apps.test.d.ts.map +1 -0
- package/dist/lib/workspace-apps.test.js +16 -0
- package/dist/lib/workspace-apps.test.js.map +1 -0
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +10 -11
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/chat.d.ts.map +1 -1
- package/dist/routes/pages/chat.js +7 -2
- package/dist/routes/pages/chat.js.map +1 -1
- package/dist/routes/pages/metrics.d.ts.map +1 -1
- package/dist/routes/pages/metrics.js +143 -17
- package/dist/routes/pages/metrics.js.map +1 -1
- package/dist/routes/pages/settings.d.ts.map +1 -1
- package/dist/routes/pages/settings.js +13 -1
- package/dist/routes/pages/settings.js.map +1 -1
- package/dist/server/lib/usage-metrics-store.d.ts +17 -0
- package/dist/server/lib/usage-metrics-store.d.ts.map +1 -1
- package/dist/server/lib/usage-metrics-store.js +187 -30
- package/dist/server/lib/usage-metrics-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.js +2 -0
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/integrations.d.ts.map +1 -1
- package/dist/server/plugins/integrations.js +1 -0
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/list-connected-agents.spec.ts +73 -0
- package/src/actions/list-connected-agents.ts +6 -2
- package/src/actions/list-dispatch-usage-metrics.ts +11 -1
- package/src/actions/remix-workspace-template.spec.ts +29 -0
- package/src/actions/remix-workspace-template.ts +13 -6
- package/src/actions/scaffold-workspace-app.ts +12 -5
- package/src/actions/start-workspace-app-creation.ts +8 -2
- package/src/actions/view-screen.ts +13 -1
- package/src/components/app-icon.spec.tsx +34 -0
- package/src/components/app-icon.tsx +1 -1
- package/src/components/connected-app-card.tsx +3 -1
- package/src/components/dispatch-control-plane.spec.tsx +8 -1
- package/src/components/layout/Layout.spec.tsx +3 -0
- package/src/components/layout/Layout.tsx +13 -9
- package/src/components/workspace-template-card.spec.tsx +1 -1
- package/src/components/workspace-template-card.tsx +9 -16
- package/src/hooks/use-navigation-state.ts +12 -0
- package/src/lib/embed-session-recovery.spec.ts +63 -0
- package/src/lib/embed-session-recovery.ts +26 -0
- package/src/lib/other-apps.ts +2 -0
- package/src/lib/workspace-apps.test.ts +20 -0
- package/src/lib/workspace-apps.ts +10 -0
- package/src/routes/pages/apps.$appId.tsx +10 -11
- package/src/routes/pages/chat.spec.tsx +10 -0
- package/src/routes/pages/chat.tsx +8 -0
- package/src/routes/pages/metrics.tsx +452 -102
- package/src/routes/pages/settings.tsx +13 -1
- package/src/server/lib/usage-metrics-store.spec.ts +117 -0
- package/src/server/lib/usage-metrics-store.ts +249 -30
- package/src/server/plugins/agent-chat.spec.ts +4 -0
- package/src/server/plugins/agent-chat.ts +2 -0
- package/src/server/plugins/integrations.ts +1 -0
- package/src/styles/dispatch.css +9 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"apps.$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\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, useParams } from \"react-router\";\n\nimport { ActionQueryError } from \"../../components/action-query-error\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { Spinner } from \"../../components/ui/spinner\";\nimport {\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../../lib/workspace-apps\";\n\ninterface EmbedSessionResult {\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n}\n\ninterface EmbedSessionInput {\n app: string;\n path?: string;\n url?: string;\n chrome: \"minimal\";\n}\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\nexport default function WorkspaceAppRoute() {\n const t = useT();\n const { appId } = useParams();\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 const href = app ? workspaceAppHref(app) : null;\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 createEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create_embed_session\", {\n skipActionQueryInvalidation: true,\n });\n const embedInput = useMemo<EmbedSessionInput | null>(() => {\n if (!app || !href) return null;\n const path = app.path.trim();\n if (path.startsWith(\"/\")) {\n return { app: app.id, path, chrome: \"minimal\" };\n }\n return { app: app.id, url: href, chrome: \"minimal\" };\n }, [app?.id, app?.path, href]);\n\n useEffect(() => {\n if (!app || app.status === \"pending\" || !embedInput) return;\n let cancelled = false;\n setEmbedUrl(null);\n setEmbedError(null);\n void createEmbedSession\n .mutateAsync(embedInput)\n .then((result) => {\n if (!cancelled) setEmbedUrl(result.startUrl);\n })\n .catch((cause: unknown) => {\n if (!cancelled) {\n setEmbedError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n }\n });\n return () => {\n cancelled = true;\n };\n }, [\n app?.id,\n app?.path,\n app?.status,\n createEmbedSession.mutateAsync,\n embedAttempt,\n embedInput,\n ]);\n\n useEffect(() => {\n const handleEmbedSessionExpired = (event: MessageEvent) => {\n if (\n event.data?.type !== \"agentNative.embedSessionExpired\" ||\n event.source !== embedFrameRef.current?.contentWindow\n ) {\n return;\n }\n setEmbedAttempt((attempt) => attempt + 1);\n };\n\n window.addEventListener(\"message\", handleEmbedSessionExpired);\n return () =>\n window.removeEventListener(\"message\", handleEmbedSessionExpired);\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.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.branch\", { branch: app.branchName })}\n </p>\n ) : null}\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 </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 {embedUrl ? (\n <iframe\n data-dispatch-workspace-app-frame\n src={embedUrl}\n title={app.name}\n ref={embedFrameRef}\n referrerPolicy=\"no-referrer\"\n className=\"h-full w-full border-0 bg-background\"\n />\n ) : embedError ? (\n <div className=\"flex h-full items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl\">\n <ActionQueryError\n error={embedError}\n onRetry={() => setEmbedAttempt((attempt) => attempt + 1)}\n />\n </div>\n </div>\n ) : (\n <div className=\"flex h-full items-center justify-center\">\n <Spinner className=\"size-5 text-muted-foreground\" />\n <span className=\"sr-only\">{t(\"dispatch.pages.loading\")}</span>\n </div>\n )}\n </div>\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"apps.$appId.js","sourceRoot":"","sources":["../../../src/routes/pages/apps.$appId.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\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, useParams } from \"react-router\";\n\nimport { ActionQueryError } from \"../../components/action-query-error\";\nimport { Badge } from \"../../components/ui/badge\";\nimport { Button } from \"../../components/ui/button\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { Spinner } from \"../../components/ui/spinner\";\nimport { isEmbedSessionExpiredMessage } from \"../../lib/embed-session-recovery\";\nimport {\n workspaceAppEmbedTarget,\n workspaceAppHref,\n type WorkspaceAppSummary,\n} from \"../../lib/workspace-apps\";\n\ninterface EmbedSessionResult {\n startUrl: string;\n targetPath?: string;\n expiresAt?: number;\n app: string;\n}\n\ninterface EmbedSessionInput {\n app: string;\n path?: string;\n url?: string;\n chrome: \"minimal\";\n}\n\nexport function meta() {\n return [{ title: \"Workspace app - Dispatch\" }];\n}\n\nexport default function WorkspaceAppRoute() {\n const t = useT();\n const { appId } = useParams();\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 const href = app ? workspaceAppHref(app) : null;\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 createEmbedSession = useActionMutation<\n EmbedSessionResult,\n EmbedSessionInput\n >(\"create_embed_session\", {\n skipActionQueryInvalidation: true,\n });\n const embedInput = useMemo<EmbedSessionInput | null>(() => {\n if (!app || !href) return null;\n return {\n app: app.id,\n ...workspaceAppEmbedTarget(app),\n chrome: \"minimal\",\n };\n }, [app?.id, app?.path, app?.url, href]);\n\n useEffect(() => {\n if (!app || app.status === \"pending\" || !embedInput) return;\n let cancelled = false;\n setEmbedUrl(null);\n setEmbedError(null);\n void createEmbedSession\n .mutateAsync(embedInput)\n .then((result) => {\n if (!cancelled) setEmbedUrl(result.startUrl);\n })\n .catch((cause: unknown) => {\n if (!cancelled) {\n setEmbedError(\n cause instanceof Error ? cause : new Error(String(cause)),\n );\n }\n });\n return () => {\n cancelled = true;\n };\n }, [\n app?.id,\n app?.path,\n app?.status,\n createEmbedSession.mutateAsync,\n embedAttempt,\n embedInput,\n ]);\n\n useEffect(() => {\n const handleEmbedSessionExpired = (event: MessageEvent) => {\n if (!isEmbedSessionExpiredMessage(event, embedFrameRef.current, null))\n return;\n setEmbedAttempt((attempt) => attempt + 1);\n };\n\n window.addEventListener(\"message\", handleEmbedSessionExpired);\n return () =>\n window.removeEventListener(\"message\", handleEmbedSessionExpired);\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.branchName ? (\n <p className=\"text-xs text-muted-foreground\">\n {t(\"dispatch.pages.branch\", { branch: app.branchName })}\n </p>\n ) : null}\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 </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 {embedUrl ? (\n <iframe\n key={`${embedUrl}:${embedAttempt}`}\n data-dispatch-workspace-app-frame\n src={embedUrl}\n title={app.name}\n ref={embedFrameRef}\n referrerPolicy=\"no-referrer\"\n className=\"h-full w-full border-0 bg-background\"\n />\n ) : embedError ? (\n <div className=\"flex h-full items-center justify-center p-6\">\n <div className=\"w-full max-w-2xl\">\n <ActionQueryError\n error={embedError}\n onRetry={() => setEmbedAttempt((attempt) => attempt + 1)}\n />\n </div>\n </div>\n ) : (\n <div className=\"flex h-full items-center justify-center\">\n <Spinner className=\"size-5 text-muted-foreground\" />\n <span className=\"sr-only\">{t(\"dispatch.pages.loading\")}</span>\n </div>\n )}\n </div>\n </div>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/chat.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/chat.tsx"],"names":[],"mappings":"AAuFA,wBAAgB,IAAI;;IAEnB;AAED,MAAM,CAAC,OAAO,UAAU,SAAS,oDAuIhC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { AgentChatSurface, markAgentChatHomeHandoff, } from "@agent-native/core/client/agent-chat";
|
|
2
|
+
import { AgentChatSurface, markAgentChatHomeHandoff, readChatFirstMode, } from "@agent-native/core/client/agent-chat";
|
|
3
3
|
import { appBasePath, appPath } from "@agent-native/core/client/api-path";
|
|
4
4
|
import { useT } from "@agent-native/core/client/i18n";
|
|
5
5
|
import { useCallback, useEffect, useMemo, useRef, } from "react";
|
|
6
6
|
import { useLocation, useNavigate } from "react-router";
|
|
7
|
+
import { useDispatchExtensions } from "../../components/layout/Layout.js";
|
|
7
8
|
import { submitOverviewPrompt } from "../../lib/overview-chat.js";
|
|
8
9
|
function chatThreadPath(threadId) {
|
|
9
10
|
return threadId ? `/chat/${encodeURIComponent(threadId)}` : "/chat";
|
|
@@ -58,6 +59,10 @@ export default function ChatRoute() {
|
|
|
58
59
|
const navigate = useNavigate();
|
|
59
60
|
const routeThreadId = threadIdFromPath(location.pathname);
|
|
60
61
|
const handledStateIds = useRef(new Set());
|
|
62
|
+
const extensions = useDispatchExtensions();
|
|
63
|
+
const suppressInlineOpenApp = extensions?.chatFirst === true ||
|
|
64
|
+
readChatFirstMode() ||
|
|
65
|
+
new URLSearchParams(location.search).get("chatFirst") === "1";
|
|
61
66
|
const navigateThreadUrl = useCallback((path, options) => navigate(dispatchNavTarget(path), options), [navigate]);
|
|
62
67
|
const threadUrlSync = useMemo(() => ({
|
|
63
68
|
routeThreadId: routeThreadId ?? null,
|
|
@@ -117,7 +122,7 @@ export default function ChatRoute() {
|
|
|
117
122
|
window.addEventListener("agentNative.chatRunning", handleChatRunning);
|
|
118
123
|
return () => window.removeEventListener("agentNative.chatRunning", handleChatRunning);
|
|
119
124
|
}, []);
|
|
120
|
-
return (_jsx("div", { className: "flex h-full min-h-0 flex-col bg-background", children: _jsx(DispatchAgentChatSurface, { mode: "page", chatViewTransition: true, className: "dispatch-chat-panel px-4 sm:px-6", defaultMode: "chat", storageKey: "dispatch", threadUrlSync: threadUrlSync, showHeader: false, showTabBar: false, dynamicSuggestions: false, suggestions: [], emptyStateText: t("dispatch.pages.chatAcrossAppsDescription", {
|
|
125
|
+
return (_jsx("div", { className: "flex h-full min-h-0 flex-col bg-background", children: _jsx(DispatchAgentChatSurface, { mode: "page", chatViewTransition: true, className: "dispatch-chat-panel px-4 sm:px-6", defaultMode: "chat", storageKey: "dispatch", threadUrlSync: threadUrlSync, showHeader: false, showTabBar: false, dynamicSuggestions: false, suppressInlineOpenApp: suppressInlineOpenApp, suggestions: [], emptyStateText: t("dispatch.pages.chatAcrossAppsDescription", {
|
|
121
126
|
defaultValue: "Route work, inspect status, or create something new from one place.",
|
|
122
127
|
}), emptyStateDisplay: "hidden", ...(!prompt?.message
|
|
123
128
|
? {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/routes/pages/chat.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n AgentChatSurface,\n markAgentChatHomeHandoff,\n} from \"@agent-native/core/client/agent-chat\";\nimport { appBasePath, appPath } from \"@agent-native/core/client/api-path\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type ComponentProps,\n} from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\n\nimport { submitOverviewPrompt } from \"../../lib/overview-chat\";\n\nfunction chatThreadPath(threadId: string | null): string {\n return threadId ? `/chat/${encodeURIComponent(threadId)}` : \"/chat\";\n}\n\nfunction stripBasePath(pathname: string): string {\n const basePath = appBasePath();\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\n// The chat surface renders for both `/chat` and the `/chat/:threadId` deep\n// link. The thread id is read from the pathname (not `useParams`) because the\n// param is owned by the nested deep-link route, not this leaf component.\nfunction threadIdFromPath(pathname: string): string | null {\n const match = stripBasePath(pathname).match(/^\\/chat\\/([^/]+)/);\n if (!match) return null;\n try {\n const value = decodeURIComponent(match[1]).trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\n// Mirror the basename handling Dispatch's nav links use: pass a router-local\n// path when the live URL is already under the mount, otherwise prefix it.\nfunction dispatchNavTarget(path: string): string {\n if (typeof window === \"undefined\") return path;\n const basePath = appBasePath();\n if (!basePath) return path;\n const pathname = window.location.pathname;\n const routerHasBasename =\n pathname === basePath || pathname.startsWith(`${basePath}/`);\n return routerHasBasename ? path : appPath(path);\n}\n\ninterface DispatchThreadUrlSync {\n routeThreadId: string | null;\n getPath: (threadId: string | null) => string;\n navigate: (path: string, options?: { replace?: boolean }) => void;\n}\n\ntype DispatchAgentChatSurfaceProps = ComponentProps<typeof AgentChatSurface> & {\n threadUrlSync?: DispatchThreadUrlSync;\n};\n\nfunction DispatchAgentChatSurface(props: DispatchAgentChatSurfaceProps) {\n return <AgentChatSurface {...props} />;\n}\n\ninterface DispatchChatLocationState {\n dispatchPrompt?: {\n id?: string | number;\n message?: string;\n selectedModel?: string | null;\n selectedEngine?: string | null;\n selectedEffort?: ComponentProps<typeof AgentChatSurface>[\"selectedEffort\"];\n };\n dispatchThread?: {\n id?: string | number;\n threadId?: string;\n };\n}\n\nexport function meta() {\n return [{ title: \"Chat — Dispatch\" }];\n}\n\nexport default function ChatRoute() {\n const t = useT();\n const location = useLocation();\n const navigate = useNavigate();\n const routeThreadId = threadIdFromPath(location.pathname);\n const handledStateIds = useRef(new Set<string>());\n\n const navigateThreadUrl = useCallback(\n (path: string, options?: { replace?: boolean }) =>\n navigate(dispatchNavTarget(path), options),\n [navigate],\n );\n const threadUrlSync = useMemo<DispatchThreadUrlSync>(\n () => ({\n routeThreadId: routeThreadId ?? null,\n getPath: chatThreadPath,\n navigate: navigateThreadUrl,\n }),\n [routeThreadId, navigateThreadUrl],\n );\n const state = location.state as DispatchChatLocationState | null;\n const prompt = state?.dispatchPrompt;\n const thread = state?.dispatchThread;\n\n useEffect(() => {\n const message = prompt?.message?.trim();\n const threadId = thread?.threadId?.trim();\n if (!message && !threadId) return;\n\n const stateId = String(\n prompt?.id ?? thread?.id ?? `${message ?? \"\"}:${threadId ?? \"\"}`,\n );\n if (handledStateIds.current.has(stateId)) return;\n handledStateIds.current.add(stateId);\n\n const timer = window.setTimeout(() => {\n if (threadId) {\n window.dispatchEvent(\n new CustomEvent(\"agent-chat:open-thread\", {\n detail: { threadId },\n }),\n );\n }\n if (message) {\n submitOverviewPrompt(message, prompt?.selectedModel, {\n openSidebar: false,\n selectedEngine: prompt?.selectedEngine,\n selectedEffort: prompt?.selectedEffort,\n });\n }\n navigate(`${location.pathname}${location.search}${location.hash}`, {\n replace: true,\n state: null,\n });\n }, 0);\n\n return () => window.clearTimeout(timer);\n }, [\n location.hash,\n location.pathname,\n location.search,\n navigate,\n prompt?.id,\n prompt?.message,\n prompt?.selectedModel,\n prompt?.selectedEngine,\n prompt?.selectedEffort,\n thread?.id,\n thread?.threadId,\n ]);\n\n useEffect(() => {\n function handleChatRunning(event: Event) {\n const detail = (event as CustomEvent).detail;\n if (detail?.isRunning === true) markAgentChatHomeHandoff(\"dispatch\");\n }\n\n window.addEventListener(\"agentNative.chatRunning\", handleChatRunning);\n return () =>\n window.removeEventListener(\"agentNative.chatRunning\", handleChatRunning);\n }, []);\n\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n <DispatchAgentChatSurface\n mode=\"page\"\n chatViewTransition\n className=\"dispatch-chat-panel px-4 sm:px-6\"\n defaultMode=\"chat\"\n storageKey=\"dispatch\"\n threadUrlSync={threadUrlSync}\n showHeader={false}\n showTabBar={false}\n dynamicSuggestions={false}\n suggestions={[]}\n emptyStateText={t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n emptyStateDisplay=\"hidden\"\n {...(!prompt?.message\n ? {\n centerComposerWhenEmpty: true,\n composerLayoutVariant: \"hero\" as const,\n }\n : {})}\n composerPlaceholder={t(\"dispatch.pages.chatPromptPlaceholder\", {\n defaultValue: \"Ask Dispatch...\",\n })}\n composerSlot={\n !prompt?.message ? (\n <div className=\"dispatch-chat-intro\">\n <h1>\n {t(\"dispatch.pages.chatAcrossApps\", {\n defaultValue: \"Chat across your apps\",\n })}\n </h1>\n <p>\n {t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n </p>\n </div>\n ) : null\n }\n />\n </div>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/routes/pages/chat.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n AgentChatSurface,\n markAgentChatHomeHandoff,\n readChatFirstMode,\n} from \"@agent-native/core/client/agent-chat\";\nimport { appBasePath, appPath } from \"@agent-native/core/client/api-path\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type ComponentProps,\n} from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\n\nimport { useDispatchExtensions } from \"../../components/layout/Layout\";\nimport { submitOverviewPrompt } from \"../../lib/overview-chat\";\n\nfunction chatThreadPath(threadId: string | null): string {\n return threadId ? `/chat/${encodeURIComponent(threadId)}` : \"/chat\";\n}\n\nfunction stripBasePath(pathname: string): string {\n const basePath = appBasePath();\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\n// The chat surface renders for both `/chat` and the `/chat/:threadId` deep\n// link. The thread id is read from the pathname (not `useParams`) because the\n// param is owned by the nested deep-link route, not this leaf component.\nfunction threadIdFromPath(pathname: string): string | null {\n const match = stripBasePath(pathname).match(/^\\/chat\\/([^/]+)/);\n if (!match) return null;\n try {\n const value = decodeURIComponent(match[1]).trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\n// Mirror the basename handling Dispatch's nav links use: pass a router-local\n// path when the live URL is already under the mount, otherwise prefix it.\nfunction dispatchNavTarget(path: string): string {\n if (typeof window === \"undefined\") return path;\n const basePath = appBasePath();\n if (!basePath) return path;\n const pathname = window.location.pathname;\n const routerHasBasename =\n pathname === basePath || pathname.startsWith(`${basePath}/`);\n return routerHasBasename ? path : appPath(path);\n}\n\ninterface DispatchThreadUrlSync {\n routeThreadId: string | null;\n getPath: (threadId: string | null) => string;\n navigate: (path: string, options?: { replace?: boolean }) => void;\n}\n\ntype DispatchAgentChatSurfaceProps = ComponentProps<typeof AgentChatSurface> & {\n threadUrlSync?: DispatchThreadUrlSync;\n};\n\nfunction DispatchAgentChatSurface(props: DispatchAgentChatSurfaceProps) {\n return <AgentChatSurface {...props} />;\n}\n\ninterface DispatchChatLocationState {\n dispatchPrompt?: {\n id?: string | number;\n message?: string;\n selectedModel?: string | null;\n selectedEngine?: string | null;\n selectedEffort?: ComponentProps<typeof AgentChatSurface>[\"selectedEffort\"];\n };\n dispatchThread?: {\n id?: string | number;\n threadId?: string;\n };\n}\n\nexport function meta() {\n return [{ title: \"Chat — Dispatch\" }];\n}\n\nexport default function ChatRoute() {\n const t = useT();\n const location = useLocation();\n const navigate = useNavigate();\n const routeThreadId = threadIdFromPath(location.pathname);\n const handledStateIds = useRef(new Set<string>());\n const extensions = useDispatchExtensions();\n const suppressInlineOpenApp =\n extensions?.chatFirst === true ||\n readChatFirstMode() ||\n new URLSearchParams(location.search).get(\"chatFirst\") === \"1\";\n\n const navigateThreadUrl = useCallback(\n (path: string, options?: { replace?: boolean }) =>\n navigate(dispatchNavTarget(path), options),\n [navigate],\n );\n const threadUrlSync = useMemo<DispatchThreadUrlSync>(\n () => ({\n routeThreadId: routeThreadId ?? null,\n getPath: chatThreadPath,\n navigate: navigateThreadUrl,\n }),\n [routeThreadId, navigateThreadUrl],\n );\n const state = location.state as DispatchChatLocationState | null;\n const prompt = state?.dispatchPrompt;\n const thread = state?.dispatchThread;\n\n useEffect(() => {\n const message = prompt?.message?.trim();\n const threadId = thread?.threadId?.trim();\n if (!message && !threadId) return;\n\n const stateId = String(\n prompt?.id ?? thread?.id ?? `${message ?? \"\"}:${threadId ?? \"\"}`,\n );\n if (handledStateIds.current.has(stateId)) return;\n handledStateIds.current.add(stateId);\n\n const timer = window.setTimeout(() => {\n if (threadId) {\n window.dispatchEvent(\n new CustomEvent(\"agent-chat:open-thread\", {\n detail: { threadId },\n }),\n );\n }\n if (message) {\n submitOverviewPrompt(message, prompt?.selectedModel, {\n openSidebar: false,\n selectedEngine: prompt?.selectedEngine,\n selectedEffort: prompt?.selectedEffort,\n });\n }\n navigate(`${location.pathname}${location.search}${location.hash}`, {\n replace: true,\n state: null,\n });\n }, 0);\n\n return () => window.clearTimeout(timer);\n }, [\n location.hash,\n location.pathname,\n location.search,\n navigate,\n prompt?.id,\n prompt?.message,\n prompt?.selectedModel,\n prompt?.selectedEngine,\n prompt?.selectedEffort,\n thread?.id,\n thread?.threadId,\n ]);\n\n useEffect(() => {\n function handleChatRunning(event: Event) {\n const detail = (event as CustomEvent).detail;\n if (detail?.isRunning === true) markAgentChatHomeHandoff(\"dispatch\");\n }\n\n window.addEventListener(\"agentNative.chatRunning\", handleChatRunning);\n return () =>\n window.removeEventListener(\"agentNative.chatRunning\", handleChatRunning);\n }, []);\n\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n <DispatchAgentChatSurface\n mode=\"page\"\n chatViewTransition\n className=\"dispatch-chat-panel px-4 sm:px-6\"\n defaultMode=\"chat\"\n storageKey=\"dispatch\"\n threadUrlSync={threadUrlSync}\n showHeader={false}\n showTabBar={false}\n dynamicSuggestions={false}\n suppressInlineOpenApp={suppressInlineOpenApp}\n suggestions={[]}\n emptyStateText={t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n emptyStateDisplay=\"hidden\"\n {...(!prompt?.message\n ? {\n centerComposerWhenEmpty: true,\n composerLayoutVariant: \"hero\" as const,\n }\n : {})}\n composerPlaceholder={t(\"dispatch.pages.chatPromptPlaceholder\", {\n defaultValue: \"Ask Dispatch...\",\n })}\n composerSlot={\n !prompt?.message ? (\n <div className=\"dispatch-chat-intro\">\n <h1>\n {t(\"dispatch.pages.chatAcrossApps\", {\n defaultValue: \"Chat across your apps\",\n })}\n </h1>\n <p>\n {t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n </p>\n </div>\n ) : null\n }\n />\n </div>\n );\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/metrics.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"metrics.d.ts","sourceRoot":"","sources":["../../../src/routes/pages/metrics.tsx"],"names":[],"mappings":"AAqCA,wBAAgB,IAAI;;IAEnB;AAg4BD,MAAM,CAAC,OAAO,UAAU,YAAY,oDA+LnC"}
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
|
|
2
3
|
import { useActionQuery } from "@agent-native/core/client/hooks";
|
|
3
4
|
import { useT } from "@agent-native/core/client/i18n";
|
|
4
|
-
import { IconActivity, IconAlertTriangle, IconApps, IconChartBar,
|
|
5
|
+
import { IconActivity, IconAlertTriangle, IconArrowUpRight, IconApps, IconChartBar, IconCoin, IconMessages, IconUsersGroup, } from "@tabler/icons-react";
|
|
5
6
|
import { useMemo, useState } from "react";
|
|
7
|
+
import { Link, useSearchParams } from "react-router";
|
|
8
|
+
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from "recharts";
|
|
6
9
|
import { DispatchShell } from "../../components/dispatch-shell.js";
|
|
7
10
|
import { Alert, AlertDescription, AlertTitle } from "../../components/ui/alert.js";
|
|
8
11
|
import { Badge } from "../../components/ui/badge.js";
|
|
9
12
|
import { Button } from "../../components/ui/button.js";
|
|
13
|
+
import { ChartContainer, ChartTooltip, ChartTooltipContent, } from "../../components/ui/chart.js";
|
|
14
|
+
import { Select, SelectContent, SelectGroup, SelectItem, SelectTrigger, SelectValue, } from "../../components/ui/select.js";
|
|
10
15
|
import { Skeleton } from "../../components/ui/skeleton.js";
|
|
11
16
|
import { cn } from "../../lib/utils.js";
|
|
12
17
|
export function meta() {
|
|
@@ -76,6 +81,32 @@ function timeAgo(timestamp) {
|
|
|
76
81
|
return `${Math.floor(diff / 3_600_000)}h ago`;
|
|
77
82
|
return `${Math.floor(diff / 86_400_000)}d ago`;
|
|
78
83
|
}
|
|
84
|
+
function formatTrendDate(value) {
|
|
85
|
+
const date = new Date(`${value}T12:00:00`);
|
|
86
|
+
return date.toLocaleDateString(undefined, {
|
|
87
|
+
month: "short",
|
|
88
|
+
day: "numeric",
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function completeTrendRows(rows) {
|
|
92
|
+
if (rows.length < 2)
|
|
93
|
+
return rows;
|
|
94
|
+
const byDate = new Map(rows.map((row) => [row.date, row]));
|
|
95
|
+
const start = new Date(`${rows[0].date}T12:00:00`);
|
|
96
|
+
const end = new Date(`${rows[rows.length - 1].date}T12:00:00`);
|
|
97
|
+
const completed = [];
|
|
98
|
+
for (const cursor = start; cursor <= end; cursor.setDate(cursor.getDate() + 1)) {
|
|
99
|
+
const date = cursor.toISOString().slice(0, 10);
|
|
100
|
+
completed.push(byDate.get(date) ?? {
|
|
101
|
+
date,
|
|
102
|
+
costCents: 0,
|
|
103
|
+
calls: 0,
|
|
104
|
+
chatCalls: 0,
|
|
105
|
+
activeUsers: 0,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return completed;
|
|
109
|
+
}
|
|
79
110
|
function displayApp(value) {
|
|
80
111
|
const trimmed = value?.trim();
|
|
81
112
|
if (!trimmed || trimmed === "unattributed")
|
|
@@ -102,6 +133,78 @@ function Panel({ title, icon, children, action, }) {
|
|
|
102
133
|
function LoadingMetrics() {
|
|
103
134
|
return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5", children: Array.from({ length: 5 }).map((_, index) => (_jsxs("div", { className: "rounded-lg bg-card p-4", children: [_jsx(Skeleton, { className: "mb-4 h-4 w-24" }), _jsx(Skeleton, { className: "h-7 w-20" }), _jsx(Skeleton, { className: "mt-3 h-3 w-28" })] }, index))) }), _jsx(Skeleton, { className: "h-80 rounded-lg" })] }));
|
|
104
135
|
}
|
|
136
|
+
function ScopeSelector({ value, onChange, }) {
|
|
137
|
+
return (_jsx("div", { className: "flex rounded-md bg-card p-0.5", "aria-label": "Usage scope", children: [
|
|
138
|
+
["me", "My usage"],
|
|
139
|
+
["workspace", "Workspace"],
|
|
140
|
+
].map(([scope, label]) => (_jsx(Button, { type: "button", variant: value === scope ? "secondary" : "ghost", size: "sm", className: "h-7 px-3 text-xs", "aria-pressed": value === scope, onClick: () => onChange(scope), children: label }, scope))) }));
|
|
141
|
+
}
|
|
142
|
+
function UserSelector({ value, users, onChange, }) {
|
|
143
|
+
return (_jsxs(Select, { value: value ?? "all", onValueChange: (nextValue) => onChange(nextValue === "all" ? null : nextValue), children: [_jsx(SelectTrigger, { className: "h-8 w-[210px] text-xs", "aria-label": "Filter usage by user", children: _jsx(SelectValue, { placeholder: "All users" }) }), _jsx(SelectContent, { children: _jsxs(SelectGroup, { children: [_jsx(SelectItem, { value: "all", children: "All users" }), users.map((user) => (_jsx(SelectItem, { value: user.email, children: user.email }, user.email)))] }) })] }));
|
|
144
|
+
}
|
|
145
|
+
function UsageTrend({ rows, billing, }) {
|
|
146
|
+
const chartData = completeTrendRows(rows).map((row) => ({
|
|
147
|
+
...row,
|
|
148
|
+
spend: displayAmountFromCostCents(row.costCents, billing),
|
|
149
|
+
}));
|
|
150
|
+
return (_jsx(Panel, { title: billing.unit === "builder-credits"
|
|
151
|
+
? "Credit usage trend"
|
|
152
|
+
: "Usage trend", icon: _jsx(IconChartBar, { size: 16 }), action: _jsxs("div", { className: "hidden items-center gap-3 text-[11px] text-muted-foreground sm:flex", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "size-2 rounded-full bg-[hsl(var(--dispatch-brand-blue))]" }), billing.shortLabel] }), _jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "size-2 rounded-full bg-muted-foreground" }), "Calls"] })] }), children: chartData.length === 0 ? (_jsx("div", { className: "flex min-h-56 items-center justify-center rounded-md border border-dashed px-4 text-sm text-muted-foreground", children: "No usage in this window yet." })) : (_jsx(ChartContainer, { config: {
|
|
153
|
+
spend: {
|
|
154
|
+
label: billing.shortLabel,
|
|
155
|
+
color: "hsl(var(--dispatch-brand-blue))",
|
|
156
|
+
},
|
|
157
|
+
calls: {
|
|
158
|
+
label: "Calls",
|
|
159
|
+
color: "hsl(var(--muted-foreground))",
|
|
160
|
+
},
|
|
161
|
+
}, className: "h-[280px] w-full aspect-auto", children: _jsxs(AreaChart, { data: chartData, margin: { top: 8, right: 8, left: 12, bottom: 0 }, children: [_jsx("defs", { children: _jsxs("linearGradient", { id: "usage-trend-fill", x1: "0", y1: "0", x2: "0", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "var(--color-spend)", stopOpacity: 0.24 }), _jsx("stop", { offset: "100%", stopColor: "var(--color-spend)", stopOpacity: 0 })] }) }), _jsx(CartesianGrid, { vertical: false, stroke: "hsl(var(--border))", strokeDasharray: "3 3" }), _jsx(XAxis, { dataKey: "date", stroke: "hsl(var(--muted-foreground))", fontSize: 11, axisLine: false, tickLine: false, tickMargin: 8, minTickGap: 28, tickFormatter: formatTrendDate }), _jsx(YAxis, { yAxisId: "spend", stroke: "hsl(var(--muted-foreground))", fontSize: 11, axisLine: false, tickLine: false, tickMargin: 8, width: 50, tickFormatter: (value) => billing.unit === "builder-credits"
|
|
162
|
+
? formatCredits(Number(value))
|
|
163
|
+
: formatSpend(Number(value), billing) }), _jsx(YAxis, { yAxisId: "calls", orientation: "right", hide: true }), _jsx(ChartTooltip, { cursor: false, content: _jsx(ChartTooltipContent, { labelFormatter: (value) => formatTrendDate(String(value)), formatter: (value, name) => [
|
|
164
|
+
name === "spend"
|
|
165
|
+
? billing.unit === "builder-credits"
|
|
166
|
+
? formatCredits(Number(value))
|
|
167
|
+
: formatSpend(Number(value), billing)
|
|
168
|
+
: `${formatNumber(Number(value))} calls`,
|
|
169
|
+
name === "spend" ? billing.shortLabel : "Calls",
|
|
170
|
+
] }) }), _jsx(Area, { yAxisId: "spend", dataKey: "spend", type: "monotone", stroke: "var(--color-spend)", strokeWidth: 2, fill: "url(#usage-trend-fill)", fillOpacity: 1, dot: false, activeDot: { r: 4, strokeWidth: 2 }, isAnimationActive: false }), _jsx(Area, { yAxisId: "calls", dataKey: "calls", type: "monotone", stroke: "var(--color-calls)", strokeWidth: 1.5, fill: "none", dot: false, activeDot: { r: 3, strokeWidth: 2 }, isAnimationActive: false })] }) })) }));
|
|
171
|
+
}
|
|
172
|
+
function ReviewUsageButton({ metrics, billing, }) {
|
|
173
|
+
function reviewUsage() {
|
|
174
|
+
const topApps = metrics.byApp
|
|
175
|
+
.slice(0, 5)
|
|
176
|
+
.map((row) => `${displayApp(row.key)}: ${formatSpend(row.costCents, billing)} / ${row.calls} calls`)
|
|
177
|
+
.join("; ");
|
|
178
|
+
const topLabels = metrics.byLabel
|
|
179
|
+
.slice(0, 5)
|
|
180
|
+
.map((row) => `${row.label}: ${formatSpend(row.costCents, billing)} / ${row.calls} calls`)
|
|
181
|
+
.join("; ");
|
|
182
|
+
const recentPrompts = metrics.recent
|
|
183
|
+
.slice(0, 8)
|
|
184
|
+
.map((row) => {
|
|
185
|
+
const prompt = row.prompt
|
|
186
|
+
? row.prompt.slice(0, 180)
|
|
187
|
+
: "prompt not captured";
|
|
188
|
+
return `${timeAgo(row.createdAt)} | ${displayApp(row.app)} | ${row.label} | ${prompt}`;
|
|
189
|
+
})
|
|
190
|
+
.join("\n");
|
|
191
|
+
sendToAgentChat({
|
|
192
|
+
message: "Review this LLM usage and explain where the spend is going. Identify repeated, background, or unexpectedly expensive work, cite the strongest evidence, and suggest concrete fixes. Call out missing attribution instead of guessing.",
|
|
193
|
+
context: [
|
|
194
|
+
`Dispatch usage scope: ${metrics.viewScope === "workspace" ? "workspace" : "my account"}.`,
|
|
195
|
+
`Lookback: ${metrics.sinceDays} days. Viewer: ${metrics.access.viewerEmail}.`,
|
|
196
|
+
`Total: ${formatSpend(metrics.totals.costCents, billing)}, ${metrics.totals.calls} calls, ${metrics.totals.chatCalls} chat calls, ${formatTokens(metrics.totals.inputTokens + metrics.totals.outputTokens)} input/output tokens.`,
|
|
197
|
+
`Top apps: ${topApps || "none"}.`,
|
|
198
|
+
`Top work types: ${topLabels || "none"}.`,
|
|
199
|
+
`Recent prompt evidence (bounded to the latest 8 rows):\n${recentPrompts || "none"}`,
|
|
200
|
+
].join("\n"),
|
|
201
|
+
submit: true,
|
|
202
|
+
openSidebar: true,
|
|
203
|
+
chatTarget: "local",
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: reviewUsage, children: "Ask agent" }));
|
|
207
|
+
}
|
|
105
208
|
function AppSpendRows({ rows, billing, }) {
|
|
106
209
|
const max = maxSpend(rows, billing);
|
|
107
210
|
if (rows.length === 0) {
|
|
@@ -111,13 +214,6 @@ function AppSpendRows({ rows, billing, }) {
|
|
|
111
214
|
width: barWidth(displayAmountFromCostCents(row.costCents, billing), max),
|
|
112
215
|
} }) })] }, row.key))) }));
|
|
113
216
|
}
|
|
114
|
-
function DailyActivity({ rows }) {
|
|
115
|
-
const max = Math.max(1, rows.reduce((value, row) => Math.max(value, row.calls), 0));
|
|
116
|
-
if (rows.length === 0) {
|
|
117
|
-
return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No activity in this window." }));
|
|
118
|
-
}
|
|
119
|
-
return (_jsx("div", { className: "flex h-44 items-end gap-1", children: rows.map((row) => (_jsxs("div", { className: "group flex min-w-0 flex-1 flex-col items-center gap-2", children: [_jsx("div", { className: "relative flex h-36 w-full items-end rounded-sm bg-muted/60", children: _jsx("div", { className: "w-full rounded-sm bg-foreground transition group-hover:bg-primary", style: { height: `${Math.max(4, (row.calls / max) * 100)}%` } }) }), _jsx("div", { className: "hidden text-[10px] text-muted-foreground sm:block", children: row.date.slice(5) })] }, row.date))) }));
|
|
120
|
-
}
|
|
121
217
|
function AppAccessTable({ rows, billing, }) {
|
|
122
218
|
const visibleRows = rows.filter((row) => !row.isDispatch);
|
|
123
219
|
if (visibleRows.length === 0) {
|
|
@@ -145,14 +241,38 @@ function CompactBreakdown({ rows, empty, billing, }) {
|
|
|
145
241
|
}
|
|
146
242
|
function RecentTable({ rows, billing, }) {
|
|
147
243
|
if (rows.length === 0) {
|
|
148
|
-
return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No
|
|
244
|
+
return (_jsx("div", { className: "rounded-lg border border-dashed px-4 py-8 text-sm text-muted-foreground", children: "No prompts or LLM calls in this window." }));
|
|
149
245
|
}
|
|
150
|
-
return (_jsx("div", { className: "
|
|
246
|
+
return (_jsx("div", { className: "divide-y", children: rows.slice(0, 10).map((row) => (_jsxs("article", { className: "-mx-1 px-1 py-3 first:pt-0 last:pb-0", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsx("p", { className: "min-w-0 whitespace-pre-wrap text-sm leading-6 text-foreground", children: row.prompt ||
|
|
247
|
+
(row.promptSource === "unavailable"
|
|
248
|
+
? "Prompt unavailable - linked thread data could not be read."
|
|
249
|
+
: "Prompt not captured for this call.") }), _jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: timeAgo(row.createdAt) })] }), _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-1.5 text-xs text-muted-foreground", children: [_jsx(Badge, { variant: "outline", children: displayApp(row.app) }), _jsx(Badge, { variant: "secondary", children: row.label }), _jsx("span", { children: row.model }), _jsx("span", { "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { children: formatSpend(row.costCents, billing) }), row.ownerEmail ? (_jsxs(_Fragment, { children: [_jsx("span", { "aria-hidden": "true", children: "\u00B7" }), _jsx("span", { className: "max-w-64 truncate", children: row.ownerEmail })] })) : null, row.threadId ? (_jsxs(Link, { to: `/admin/thread-debug?threadId=${encodeURIComponent(row.threadId)}`, className: "inline-flex items-center gap-1 rounded-md border px-2 py-0.5 font-medium text-foreground transition-colors hover:bg-muted", children: ["Inspect thread", _jsx(IconArrowUpRight, { size: 12 })] })) : null] })] }, row.id))) }));
|
|
151
250
|
}
|
|
152
251
|
export default function MetricsRoute() {
|
|
153
252
|
const t = useT();
|
|
154
253
|
const [sinceDays, setSinceDays] = useState(30);
|
|
155
|
-
const
|
|
254
|
+
const [searchParams, setSearchParams] = useSearchParams();
|
|
255
|
+
const scope = searchParams.get("scope") === "workspace" ? "workspace" : "me";
|
|
256
|
+
const userEmail = scope === "workspace" ? searchParams.get("user") || null : null;
|
|
257
|
+
function setScope(nextScope) {
|
|
258
|
+
const next = new URLSearchParams(searchParams);
|
|
259
|
+
if (nextScope === "me")
|
|
260
|
+
next.delete("scope");
|
|
261
|
+
else
|
|
262
|
+
next.set("scope", nextScope);
|
|
263
|
+
if (nextScope === "me")
|
|
264
|
+
next.delete("user");
|
|
265
|
+
setSearchParams(next, { replace: true });
|
|
266
|
+
}
|
|
267
|
+
function setUserEmail(nextUserEmail) {
|
|
268
|
+
const next = new URLSearchParams(searchParams);
|
|
269
|
+
if (nextUserEmail)
|
|
270
|
+
next.set("user", nextUserEmail);
|
|
271
|
+
else
|
|
272
|
+
next.delete("user");
|
|
273
|
+
setSearchParams(next, { replace: true });
|
|
274
|
+
}
|
|
275
|
+
const { data, isLoading, error } = useActionQuery("list-dispatch-usage-metrics", { sinceDays, scope, userEmail: userEmail ?? undefined });
|
|
156
276
|
const metrics = data;
|
|
157
277
|
const billing = metrics?.billing ?? USD_BILLING;
|
|
158
278
|
const totalTokens = useMemo(() => {
|
|
@@ -165,12 +285,18 @@ export default function MetricsRoute() {
|
|
|
165
285
|
}, [metrics]);
|
|
166
286
|
return (_jsx(DispatchShell, { title: t("dispatch.nav.metrics"), description: billing.unit === "builder-credits"
|
|
167
287
|
? t("dispatch.pages.metricsDescriptionBuilder")
|
|
168
|
-
: t("dispatch.pages.metricsDescriptionLlm"), children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsx("div", { className: "text-sm text-
|
|
169
|
-
|
|
170
|
-
|
|
288
|
+
: t("dispatch.pages.metricsDescriptionLlm"), children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: metrics?.selectedUserEmail
|
|
289
|
+
? `${metrics.selectedUserEmail}'s usage`
|
|
290
|
+
: scope === "workspace"
|
|
291
|
+
? "Workspace usage"
|
|
292
|
+
: "Your usage" }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: metrics?.selectedUserEmail
|
|
293
|
+
? "Filtered to this workspace member"
|
|
294
|
+
: scope === "workspace"
|
|
295
|
+
? `${metrics?.access.totalUsers ?? 0} users with access`
|
|
296
|
+
: metrics?.access.viewerEmail || "Signed-in account" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(ScopeSelector, { value: scope, onChange: setScope }), scope === "workspace" && metrics ? (_jsx(UserSelector, { value: metrics.selectedUserEmail ?? userEmail, users: metrics.availableUsers ?? [], onChange: setUserEmail })) : null, _jsx(RangeSelector, { value: sinceDays, onChange: setSinceDays })] })] }), error ? (_jsxs(Alert, { variant: "destructive", children: [_jsx(IconAlertTriangle, { className: "h-4 w-4" }), _jsx(AlertTitle, { children: t("dispatch.pages.metricsUnavailable") }), _jsx(AlertDescription, { children: error instanceof Error
|
|
171
297
|
? error.message
|
|
172
|
-
: t("dispatch.pages.unableToLoadUsage") })] })) : null, isLoading && !metrics ? _jsx(LoadingMetrics, {}) : null, metrics ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5", children: [_jsx(MetricCard, { label: billing.label, value: formatSpend(metrics.totals.costCents, billing), detail: `${formatTokens(totalTokens)} total tokens`, icon: _jsx(IconCoin, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.llmCalls"), value: formatNumber(metrics.totals.calls), detail: `${formatNumber(metrics.totals.chatCalls)} chat turns`, icon: _jsx(IconActivity, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.activeUsers"), value: formatNumber(metrics.totals.activeUsers), detail: `${formatNumber(metrics.access.totalUsers)} users with access`, icon: _jsx(IconUsersGroup, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.workspaceAppsStat"), value: formatNumber(metrics.totals.workspaceApps), detail: `${formatNumber(metrics.byApp.length)} with usage`, icon: _jsx(IconApps, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.chatThreads"), value: formatNumber(metrics.totals.chatThreads), detail: `${formatNumber(metrics.totals.chatMessages)} messages`, icon: _jsx(IconMessages, { size: 17 }) })] }),
|
|
173
|
-
|
|
174
|
-
|
|
298
|
+
: t("dispatch.pages.unableToLoadUsage") })] })) : null, isLoading && !metrics ? _jsx(LoadingMetrics, {}) : null, metrics ? (_jsxs(_Fragment, { children: [_jsx(UsageTrend, { rows: metrics.daily, billing: billing }), _jsxs("div", { className: "flex items-center justify-between gap-3 border-y py-3", children: [_jsx("span", { className: "text-sm text-muted-foreground", children: "Review this usage with the agent" }), _jsx(ReviewUsageButton, { metrics: metrics, billing: billing })] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-2 xl:grid-cols-5", children: [_jsx(MetricCard, { label: billing.label, value: formatSpend(metrics.totals.costCents, billing), detail: `${formatTokens(totalTokens)} total tokens`, icon: _jsx(IconCoin, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.llmCalls"), value: formatNumber(metrics.totals.calls), detail: `${formatNumber(metrics.totals.chatCalls)} chat turns`, icon: _jsx(IconActivity, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.activeUsers"), value: formatNumber(metrics.totals.activeUsers), detail: `${formatNumber(metrics.access.totalUsers)} users with access`, icon: _jsx(IconUsersGroup, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.workspaceAppsStat"), value: formatNumber(metrics.totals.workspaceApps), detail: `${formatNumber(metrics.byApp.length)} with usage`, icon: _jsx(IconApps, { size: 17 }) }), _jsx(MetricCard, { label: t("dispatch.pages.chatThreads"), value: formatNumber(metrics.totals.chatThreads), detail: `${formatNumber(metrics.totals.chatMessages)} messages`, icon: _jsx(IconMessages, { size: 17 }) })] }), _jsx(Panel, { title: billing.unit === "builder-credits"
|
|
299
|
+
? "Credit spend by app"
|
|
300
|
+
: "Spend by app", icon: _jsx(IconApps, { size: 16 }), children: _jsx(AppSpendRows, { rows: metrics.byApp, billing: billing }) }), _jsx(Panel, { title: "Recent prompts", icon: _jsx(IconMessages, { size: 16 }), action: _jsxs("span", { className: "text-xs text-muted-foreground", children: ["Latest ", Math.min(metrics.recent.length, 10), " of", " ", metrics.recent.length] }), children: _jsx(RecentTable, { rows: metrics.recent, billing: billing }) }), _jsx(Panel, { title: "Access By App", icon: _jsx(IconApps, { size: 16 }), children: _jsx(AppAccessTable, { rows: metrics.appAccess, billing: billing }) }), _jsx(Panel, { title: "Users", icon: _jsx(IconUsersGroup, { size: 16 }), children: _jsx(UserTable, { rows: metrics.byUser, billing: billing }) }), _jsxs("div", { className: "grid gap-4 lg:grid-cols-2", children: [_jsx(Panel, { title: "Models", icon: _jsx(IconChartBar, { size: 16 }), children: _jsx(CompactBreakdown, { rows: metrics.byModel, empty: "No model usage in this window.", billing: billing }) }), _jsx(Panel, { title: "Work Types", icon: _jsx(IconActivity, { size: 16 }), children: _jsx(CompactBreakdown, { rows: metrics.byLabel, empty: "No labeled usage in this window.", billing: billing }) })] })] })) : null] }) }));
|
|
175
301
|
}
|
|
176
302
|
//# sourceMappingURL=metrics.js.map
|