Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { PageSelectionProvider } from "@/components/page-selection"
import { getSelectionHistory } from "@/data/page-selection"
import { CodingActivity } from "@/components/blocks/coding/git-actions"
import { Suspense } from "react"
import type { Route } from "next"
Expand Down Expand Up @@ -141,8 +143,21 @@ async function WorkspaceContent({
)
}

const selectionScope = JSON.stringify([
result.scope.organizationSession.session.user.id,
result.scope.organization.id,
result.workspace.id,
])
const selectionHistory = (await getSelectionHistory()).filter(
([scope]) => scope === selectionScope
)
return (
<>
<PageSelectionProvider
key={selectionScope}
scope={selectionScope}
basePath={root}
initialHistory={selectionHistory}
>
<ThemeSync theme={preferences.theme} />
<AppShell
sidebar={
Expand Down Expand Up @@ -184,6 +199,6 @@ async function WorkspaceContent({
)}
</AdministrationLayout>
</AppShell>
</>
</PageSelectionProvider>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"use client"

import * as React from "react"
import { useSelectResource } from "@/components/page-selection"
import { BotIcon, MessageSquareQuote } from "lucide-react"
import { useRouter } from "@bprogress/next/app"
import { usePathname, useSearchParams } from "next/navigation"
Expand Down Expand Up @@ -34,6 +35,7 @@ export function LensFilters({
sessions,
to,
}: LensFiltersProps) {
const { select, pending: selectionPending } = useSelectResource(true)
const router = useRouter()
const pathname = usePathname()
const searchParams = useSearchParams()
Expand All @@ -59,14 +61,14 @@ export function LensFilters({

return (
<div
data-pending={pending}
data-pending={pending || selectionPending}
className="bg-background flex min-h-14 flex-col gap-3 border-b px-4 py-2 data-[pending=true]:opacity-70 sm:flex-row sm:items-center sm:justify-between sm:px-6"
>
<div className="flex w-full flex-col gap-2 sm:w-auto sm:flex-row sm:items-center">
<Select
value={selectedAgentName}
onValueChange={(agentName) => update({ agent_name: agentName, session_id: undefined })}
disabled={agents.length === 0}
value={selectedAgentName ?? ""}
onValueChange={(agent_name) => select({ agent_name })}
disabled={agents.length === 0 || pending || selectionPending}
>
<SelectTrigger className="h-8 w-full min-w-0 rounded-md sm:w-64 sm:min-w-52">
<SelectValue placeholder="Agent" />
Expand All @@ -84,9 +86,9 @@ export function LensFilters({
</Select>
{sessions ? (
<Select
value={selectedSessionId}
onValueChange={(sessionID) => update({ session_id: sessionID })}
disabled={sessions.length === 0}
value={selectedSessionId ?? ""}
onValueChange={(session_id) => select({ agent_name: selectedAgentName, session_id })}
disabled={sessions.length === 0 || pending || selectionPending}
>
<SelectTrigger className="h-8 w-full min-w-0 rounded-md sm:w-72 sm:min-w-52">
<SelectValue placeholder="Session" className="truncate" />
Expand All @@ -109,7 +111,6 @@ export function LensFilters({
update({
from: formatDateParam(range.from),
to: formatDateParam(range.to),
...(sessions ? { session_id: undefined } : {}),
})
}
range={{ from: dayjs(from).toDate(), to: dayjs(to).toDate() }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import * as z from "zod"
import { AdministrationPageHeader } from "@/components/administration"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { Skeleton } from "@/components/ui/skeleton"
import { listAgentsCachedQuery } from "@/data/agent.queries"
import { resolvePageSelection, type ResolvedPageSelection } from "@/data/page-selection"
import { RememberPageSelection } from "@/components/page-selection"
import { getMcpGraphAction } from "@/data/lens.actions"
import type { ListAgentActionResponse } from "@/data/types"
import {
McpEmptyState,
McpGraph,
Expand All @@ -33,18 +33,6 @@ type McpSearchParams = {
to?: SearchParamStringInput
}

type McpScope =
| {
agents: NonNullable<ListAgentActionResponse["agents"]>
selectedAgentName?: string
error: undefined
}
| {
agents: undefined
selectedAgentName?: undefined
error: NonNullable<ListAgentActionResponse["error"]>
}

/**
* McpPage renders MCP observability for one selected agent and date range.
*/
Expand All @@ -66,6 +54,9 @@ export default async function McpPage({

const resolved = resolveMcpSearchParams(searchParams)
const workspaceId = workspace.workspace.id
const selection = resolved.then((params) =>
resolvePageSelection(workspace, "lens/mcp", { agent_name: params.agentName })
)

return (
<main className="flex min-w-0 flex-1 flex-col gap-6 p-0">
Expand All @@ -79,10 +70,10 @@ export default async function McpPage({
</div>
}
>
<Filters searchParams={resolved} workspaceId={workspaceId} />
<Filters searchParams={resolved} selection={selection} />
</Suspense>
<Suspense fallback={<McpGraphSkeleton />}>
<Graph searchParams={resolved} workspaceId={workspaceId} />
<Graph searchParams={resolved} workspaceId={workspaceId} selection={selection} />
</Suspense>
</div>
</main>
Expand All @@ -91,58 +82,49 @@ export default async function McpPage({

async function Filters({
searchParams,
workspaceId,
selection,
}: {
searchParams: Promise<ResolvedMcpSearchParams>
workspaceId: string
selection: Promise<ResolvedPageSelection>
}) {
const params = await searchParams
const range = params.range
const scope = await getMcpScope({
agents: listAgentsCachedQuery(undefined, workspaceId),
agentName: params.agentName,
})
if (scope.error) {
return <ErrorPanel message={scope.error.message} />
}

const [{ range }, scope] = await Promise.all([searchParams, selection])
if (scope.error) return <ErrorPanel message={scope.error.message} />
return (
<LensFilters
agents={scope.agents}
selectedAgentName={scope.selectedAgentName}
from={range.from}
to={range.to}
/>
<>
<RememberPageSelection selected={scope.selected} requested={scope.requested} />
<LensFilters
agents={scope.agents}
selectedAgentName={scope.selected.agent_name}
from={range.from}
to={range.to}
/>
</>
)
}

async function Graph({
searchParams,
workspaceId,
selection,
}: {
searchParams: Promise<ResolvedMcpSearchParams>
workspaceId: string
selection: Promise<ResolvedPageSelection>
}) {
const params = await searchParams
const range = params.range
const scope = await getMcpScope({
agents: listAgentsCachedQuery(undefined, workspaceId),
agentName: params.agentName,
})
const scope = await selection

if (scope.error) {
return <ErrorPanel message={scope.error.message} />
}

if (!scope.selectedAgentName) {
if (params.agentName && scope.agents.length > 0) {
return <EmptyState message="The selected agent is no longer accessible" />
}
if (!scope.selected.agent_name) {
return <EmptyState message="No agents available" />
}

const result = await getMcpGraphAction(
{ agentName: scope.selectedAgentName },
{ agentName: scope.selected.agent_name },
{ from: range.from, to: range.to },
workspaceId
)
Expand Down Expand Up @@ -202,30 +184,3 @@ function EmptyState({ message }: { message: string }) {
</div>
)
}

async function getMcpScope({
agents,
agentName,
}: {
agents: Promise<ListAgentActionResponse>
agentName?: string
}): Promise<McpScope> {
const result = await agents
if (result.error) {
return {
agents: undefined,
selectedAgentName: undefined,
error: result.error,
}
}

const selectedAgentName = agentName
? result.agents.find((agent) => agent.name === agentName)?.name
: result.agents[0]?.name

return {
agents: result.agents,
selectedAgentName,
error: undefined,
}
}
Loading