refactor(web): read plugin profile from query cache (#40471)

This commit is contained in:
yyh
2026-08-11 11:39:26 +00:00
committed by GitHub
parent 7352944dec
commit 324b0f97ff
17 changed files with 85 additions and 115 deletions
@@ -3,6 +3,7 @@ import type { Credential, PluginPayload } from '../types'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { seedAccountProfileQuery } from '@/test/console/account-profile'
import { render } from '@/test/console/render'
import { AuthCategory, CredentialTypeEnum } from '../types'
@@ -44,14 +45,6 @@ const mockUserProfile = {
avatar_url: '',
}
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: mockUserProfile,
isCurrentWorkspaceManager: mockIsCurrentWorkspaceManager(),
workspacePermissionKeys: ['credential.use', 'credential.create', 'credential.manage'],
}))
})
vi.mock('@/context/workspace-state', async () => {
const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture')
return createWorkspaceStateModuleMock(() => ({
@@ -90,6 +83,7 @@ const createConsoleQueryClient = () =>
const createWrapper = () => {
const testQueryClient = createConsoleQueryClient()
seedAccountProfileQuery(testQueryClient, mockUserProfile)
return ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={testQueryClient}>{children}</QueryClientProvider>
)
@@ -6,7 +6,7 @@ import { fireEvent, screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { render } from '@/test/console/render'
import { renderWithAccountProfile as render } from '@/test/console/account-profile'
import { AuthCategory } from '../../types'
const { mockToast } = vi.hoisted(() => {
@@ -32,10 +32,6 @@ vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({ userProfile: {} }))
})
const mockAddPluginCredential = vi.fn().mockResolvedValue({})
const mockUpdatePluginCredential = vi.fn().mockResolvedValue({})
const defaultCredentialSchemas = [
@@ -1,22 +1,12 @@
import type { PluginPayload } from '../../types'
import type { FormSchema } from '@/app/components/base/form/types'
import { QueryClient } from '@tanstack/react-query'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createAccountProfileQueryClient } from '@/test/console/account-profile'
import { createQueryClientWrapper } from '@/test/console/query-client'
import { AuthCategory } from '../../types'
const createWrapper = () =>
createQueryClientWrapper(
new QueryClient({
defaultOptions: {
queries: {
retry: false,
gcTime: 0,
},
},
}),
)
const createWrapper = () => createQueryClientWrapper(createAccountProfileQueryClient())
// Mock API hooks - these make network requests so must be mocked
const mockGetPluginOAuthUrl = vi.fn()
@@ -111,10 +101,6 @@ vi.mock('@langgenius/dify-ui/toast', () => ({
toast: mockToast,
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({ userProfile: {} }))
})
// Factory function for creating test PluginPayload
const createPluginPayload = (overrides: Partial<PluginPayload> = {}): PluginPayload => ({
category: AuthCategory.tool,
@@ -8,9 +8,9 @@ import {
PopoverTrigger,
} from '@langgenius/dify-ui/popover'
import { RadioGroup, RadioItem } from '@langgenius/dify-ui/radio'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import { userProfileAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { PermissionLevel } from '@/models/permission'
export type CredentialPermission =
@@ -28,7 +28,10 @@ const optionClassName =
const PermissionSelector = ({ disabled, permission, onChange }: PermissionSelectorProps) => {
const { t } = useTranslation()
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const isOnlyMe = permission === PermissionLevel.onlyMe
const isAllTeamMembers = permission === PermissionLevel.allTeamMembers
const permissionLabel = t(($) => $['auth.whoCanUse'], { ns: 'plugin' })
@@ -3,6 +3,7 @@ import type { Credential, PluginPayload } from '../../types'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { act, fireEvent, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { seedAccountProfileQuery } from '@/test/console/account-profile'
import { render } from '@/test/console/render'
import { AuthCategory, CredentialTypeEnum } from '../../types'
import Authorized from '../index'
@@ -87,13 +88,6 @@ const mockConsoleState = vi.hoisted(() => ({
workspacePermissionKeys: ['credential.use', 'credential.create', 'credential.manage'] as string[],
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: mockConsoleState.userProfile,
workspacePermissionKeys: mockConsoleState.workspacePermissionKeys,
}))
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => ({
@@ -137,6 +131,7 @@ const createConsoleQueryClient = () =>
const createWrapper = () => {
const testQueryClient = createConsoleQueryClient()
seedAccountProfileQuery(testQueryClient, mockConsoleState.userProfile)
return ({ children }: { children: ReactNode }) => (
<QueryClientProvider client={testQueryClient}>{children}</QueryClientProvider>
)
@@ -1,17 +1,10 @@
import type { Credential } from '../../types'
import { cleanup, fireEvent, screen } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { render } from '@/test/console/render'
import { renderWithAccountProfile as render } from '@/test/console/account-profile'
import { CredentialTypeEnum } from '../../types'
import Item from '../item'
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: { id: 'test-user' },
workspacePermissionKeys: ['credential.use', 'credential.create', 'credential.manage'],
}))
})
vi.mock('@/context/permission-state', async () => {
const { createPermissionStateModuleMock } = await import('@/test/console/state-fixture')
return createPermissionStateModuleMock(() => ({
@@ -4,13 +4,13 @@ import { cn } from '@langgenius/dify-ui/cn'
import { StatusDot } from '@langgenius/dify-ui/status-dot'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { RiInformationLine } from '@remixicon/react'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { memo, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Badge from '@/app/components/base/badge'
import Input from '@/app/components/base/input'
import { userProfileIdAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { useCredentialPermissions } from '@/hooks/use-credential-permissions'
import { CredentialTypeEnum } from '../types'
@@ -50,7 +50,10 @@ const Item = ({
const { canUseCredential, canManageCredential } = useCredentialPermissions()
const isOAuth = credential.credential_type === CredentialTypeEnum.OAUTH2
const isPersonal = credential.visibility === 'only_me'
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
// Borrowed-from-teammate: the backend explicitly flagged this row as another member's
// only_me credential, returned only because the current node still references it.
// Fallback heuristic (created_by mismatch on a selected row) is kept for backends
@@ -15,8 +15,8 @@ import {
} from '@langgenius/dify-ui/select'
import { Switch } from '@langgenius/dify-ui/switch'
import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useBoolean } from 'ahooks'
import { useAtomValue } from 'jotai'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Infotip } from '@/app/components/base/infotip'
@@ -33,7 +33,7 @@ import MixedVariableTextInput from '@/app/components/workflow/nodes/tool/compone
import ToolDatePicker from '@/app/components/workflow/nodes/tool/components/tool-date-picker'
import ToolDateRangePicker from '@/app/components/workflow/nodes/tool/components/tool-date-range-picker'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
import { userProfileAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import {
createPickerProps,
getFieldFlags,
@@ -68,8 +68,10 @@ const ReasoningConfigForm: React.FC<Props> = ({
}) => {
const { t } = useTranslation()
const language = useLanguage()
const userProfile = useAtomValue(userProfileAtom)
const timezone = userProfile.timezone ?? 'UTC'
const { data: timezone } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.timezone ?? 'UTC',
})
const handleAutomatic = (key: string, val: boolean, type: string) => {
onChange(updateInputAutoState(value, key, val, type))
@@ -1,12 +1,12 @@
'use client'
import type { AppDetailResponse } from '@/models/app'
import type { AppSSO } from '@/types/app'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { useQuery, useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { useCallback, useMemo, useState } from 'react'
import { BlockEnum } from '@/app/components/workflow/types'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { fetchAppDetail } from '@/service/apps'
import {
useInvalidateMCPServerDetail,
@@ -35,7 +35,10 @@ export const useMCPServiceCardState = (appInfo: AppInfo, triggerModeDisabled: bo
const { mutateAsync: updateMCPServer } = useUpdateMCPServer()
const { mutateAsync: refreshMCPServerCode, isPending: genLoading } = useRefreshMCPServerCode()
const invalidateMCPServerDetail = useInvalidateMCPServerDetail()
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const canManageMCP = useMemo(
-4
View File
@@ -10,10 +10,6 @@ export const userProfileAtom = atom((get) => {
return get(accountProfileQueryAtom).data.profile
})
export const userProfileIdAtom = atom((get) => {
return get(userProfileAtom).id
})
export const accountProfileMetaAtom = atom((get) => {
return get(accountProfileQueryAtom).data.meta
})
@@ -3,6 +3,7 @@ import type React from 'react'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { screen, waitFor, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { seedAccountProfileQuery } from '@/test/console/account-profile'
import { seedSystemFeatures } from '@/test/console/query-data'
import { render } from '@/test/console/render'
import { ServiceApiAccessCard } from '../service-api-access-card'
@@ -44,21 +45,6 @@ vi.mock('@/hooks/use-timestamp', () => ({
}),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: { id: 'user-1' },
currentWorkspace: { id: 'workspace-1' },
workspacePermissionKeys: ['app.acl.edit'],
langGeniusVersionInfo: {
current_env: 'PRODUCTION',
current_version: '',
latest_version: '',
version: '',
release_notes: '',
},
}))
})
vi.mock('@/context/workspace-state', async () => {
const { createWorkspaceStateModuleMock } = await import('@/test/console/state-fixture')
return createWorkspaceStateModuleMock(() => ({
@@ -107,6 +93,13 @@ vi.mock('@/context/version-state', async () => {
vi.mock('@/service/client', () => ({
consoleQuery: {
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
systemFeatures: {
get: {
queryKey: () => ['system-features'],
@@ -256,6 +249,7 @@ function createConsoleQueryClient(webAppAuthEnabled = true) {
enabled: webAppAuthEnabled,
},
})
seedAccountProfileQuery(queryClient, { id: 'user-1' })
return queryClient
}
@@ -6,8 +6,8 @@ import { useSuspenseQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { userProfileIdAtom } from '@/context/account-state'
import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import { isAccessMode } from '@/models/access-control'
import dynamic from '@/next/dynamic'
@@ -27,7 +27,10 @@ export function WebAppAccessControlButton({ agent }: { agent?: AgentAppDetailWit
...systemFeaturesQueryOptions(),
select: (systemFeatures) => systemFeatures.webapp_auth.enabled,
})
const currentUserId = useAtomValue(userProfileIdAtom)
const { data: currentUserId } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile.id,
})
const workspacePermissionKeys = useAtomValue(workspacePermissionKeysAtom)
const { canReleaseAndVersion: canManageWebAppAccessControl } = getAppACLCapabilities(
agent?.permission_keys,
@@ -15,6 +15,7 @@ import {
agentComposerSavedDraftAtom,
isAgentComposerDirtyAtom,
} from '@/features/agent-v2/agent-composer/store'
import { seedAccountProfileQuery } from '@/test/console/account-profile'
import { AgentOrchestrateReadOnlyContext } from '../../read-only-context'
import { AgentTools } from '../index'
@@ -48,13 +49,6 @@ vi.mock('@/app/components/workflow/block-selector/tool-picker', () => ({
ToolPickerContent: () => <div>Mock tool picker</div>,
}))
vi.mock('@/context/account-state', async () => {
const { atom } = await vi.importActual<typeof import('jotai')>('jotai')
return {
userProfileIdAtom: atom('user-1'),
}
})
vi.mock('@/app/components/workflow/block-icon', () => ({
default: ({ toolIcon }: { toolIcon?: string | { content: string; background: string } }) => (
<span aria-hidden data-testid="tool-icon">
@@ -392,6 +386,7 @@ function renderAgentTools(initialDraft: AgentSoulConfigFormState = agentToolsDra
},
},
})
seedAccountProfileQuery(queryClient, { id: 'user-1' })
return render(
<QueryClientProvider client={queryClient}>
@@ -410,6 +405,7 @@ function renderAgentToolsWithStore(initialDraft: AgentSoulConfigFormState = agen
},
},
})
seedAccountProfileQuery(queryClient, { id: 'user-1' })
const store = createStore()
store.set(agentComposerDraftAtom, initialDraft)
store.set(agentComposerSavedDraftAtom, initialDraft)
@@ -436,6 +432,7 @@ function renderReadonlyAgentTools(initialDraft: AgentSoulConfigFormState = agent
},
},
})
seedAccountProfileQuery(queryClient, { id: 'user-1' })
return render(
<QueryClientProvider client={queryClient}>
@@ -14,6 +14,7 @@ import { agentComposerDraftAtom } from '@/features/agent-v2/agent-composer/store
import { agentComposerModelAtom } from '@/features/agent-v2/agent-composer/store-modules/model'
import { agentComposerPromptAtom } from '@/features/agent-v2/agent-composer/store-modules/prompt'
import { consoleQuery } from '@/service/client'
import { seedAccountProfileQuery } from '@/test/console/account-profile'
import { render } from '@/test/console/render'
import { seedRegisteredConsoleStateFixture } from '@/test/console/state-fixture'
import { TransferMethod } from '@/types/app'
@@ -188,16 +189,6 @@ vi.mock('@/app/components/base/chat/chat/hooks', () => ({
),
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: {
avatar_url: '',
name: 'User',
},
}))
})
vi.mock('@/app/components/header/account-setting/model-provider-page/hooks', () => ({
useTextGenerationCurrentProviderAndModelAndModelList: () => ({
textGenerationModelList: [
@@ -239,6 +230,13 @@ vi.mock('@/service/client', async () => {
},
},
consoleQuery: {
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
agent: {
byAgentId: {
chatMessages: {
@@ -275,6 +273,7 @@ function renderPreviewChat(
},
},
})
seedAccountProfileQuery(queryClient, { avatar_url: '', name: 'User' })
store.set(agentComposerModelAtom, {
provider: 'openai',
model: 'gpt-4',
@@ -349,6 +348,7 @@ function renderPreviewChatWithConversationHarness() {
},
},
})
seedAccountProfileQuery(queryClient, { avatar_url: '', name: 'User' })
store.set(agentComposerModelAtom, {
provider: 'openai',
model: 'gpt-4',
@@ -374,6 +374,7 @@ function renderPreviewChatWithClearCommandHarness() {
},
},
})
seedAccountProfileQuery(queryClient, { avatar_url: '', name: 'User' })
store.set(agentComposerModelAtom, {
provider: 'openai',
model: 'gpt-4',
@@ -1,6 +1,6 @@
import type { AgentConfigSnapshotSummaryResponse } from '@dify/contracts/api/console/agent/types.gen'
import { fireEvent, screen } from '@testing-library/react'
import { render } from '@/test/console/render'
import { renderWithAccountProfile as render } from '@/test/console/account-profile'
import { AgentPreviewVersionsPanel } from '../versions-panel'
const versions: AgentConfigSnapshotSummaryResponse[] = [
@@ -47,6 +47,13 @@ vi.mock('@/hooks/use-timestamp', () => ({
vi.mock('@/service/client', () => ({
consoleQuery: {
account: {
profile: {
get: {
queryKey: () => [['console', 'account', 'profile', 'get'], { type: 'query' }],
},
},
},
agent: {
byAgentId: {
versions: {
@@ -59,17 +66,6 @@ vi.mock('@/service/client', () => ({
},
}))
vi.mock('@/context/account-state', async () => {
const { createAccountStateModuleMock } = await import('@/test/console/state-fixture')
return createAccountStateModuleMock(() => ({
userProfile: {
id: 'user-1',
name: 'Alice',
email: 'alice@example.com',
},
}))
})
describe('AgentPreviewVersionsPanel', () => {
beforeEach(() => {
vi.clearAllMocks()
@@ -86,6 +82,7 @@ describe('AgentPreviewVersionsPanel', () => {
onSelectVersion={handleSelectVersion}
onClose={vi.fn()}
/>,
{ accountProfile: { id: 'user-1', name: 'Alice', email: 'alice@example.com' } },
)
fireEvent.click(screen.getByRole('button', { name: /Initial release/i }))
@@ -103,6 +100,7 @@ describe('AgentPreviewVersionsPanel', () => {
onSelectVersion={handleSelectVersion}
onClose={vi.fn()}
/>,
{ accountProfile: { id: 'user-1', name: 'Alice', email: 'alice@example.com' } },
)
fireEvent.click(screen.getByRole('button', { name: /currentDraft/i }))
@@ -120,6 +118,7 @@ describe('AgentPreviewVersionsPanel', () => {
onSelectVersion={vi.fn()}
onClose={vi.fn()}
/>,
{ accountProfile: { id: 'user-1', name: 'Alice', email: 'alice@example.com' } },
)
fireEvent.click(screen.getByRole('button', { name: /filter/i }))
@@ -137,6 +136,7 @@ describe('AgentPreviewVersionsPanel', () => {
onSelectVersion={vi.fn()}
onClose={vi.fn()}
/>,
{ accountProfile: { id: 'user-1', name: 'Alice', email: 'alice@example.com' } },
)
fireEvent.click(screen.getByRole('button', { name: /filter/i }))
@@ -13,8 +13,7 @@ import type { Inputs } from '@/models/debug'
import { Avatar } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
import { toast } from '@langgenius/dify-ui/toast'
import { useQueryClient } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { useQueryClient, useSuspenseQuery } from '@tanstack/react-query'
import { useCallback, useImperativeHandle, useLayoutEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { AgentRosterResponseContent } from '@/app/components/base/chat/chat/answer/agent-roster-response-content'
@@ -22,8 +21,8 @@ import { useChat } from '@/app/components/base/chat/chat/hooks'
import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useTextGenerationCurrentProviderAndModelAndModelList } from '@/app/components/header/account-setting/model-provider-page/hooks'
import { userProfileAtom } from '@/context/account-state'
import { useDocLink } from '@/context/i18n'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import dynamic from '@/next/dynamic'
import { consoleClient, consoleQuery } from '@/service/client'
import { buildChatConfig, getAgentSoulInputs, getAgentSoulInputsForm } from './chat-config'
@@ -124,7 +123,10 @@ export function AgentPreviewChatConversation({
const { t } = useTranslation('agentV2')
const docLink = useDocLink()
const queryClient = useQueryClient()
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const sendInterruptedRef = useRef(false)
const [isSendPending, setIsSendPending] = useState(false)
const notifySendInterrupted = useCallback(() => {
@@ -1,11 +1,10 @@
'use client'
import type { AgentVersionFilter } from './filter'
import { useQuery } from '@tanstack/react-query'
import { useAtomValue } from 'jotai'
import { useQuery, useSuspenseQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { userProfileAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { consoleQuery } from '@/service/client'
import { CurrentDraftItem } from './current-draft-item'
import { VersionFilter } from './filter'
@@ -28,7 +27,10 @@ export function AgentPreviewVersionsPanel({
const { t } = useTranslation('agentV2')
const { t: tCommon } = useTranslation('common')
const { t: tWorkflow } = useTranslation('workflow')
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const [filterValue, setFilterValue] = useState<AgentVersionFilter>('all')
const versionsQuery = useQuery(
consoleQuery.agent.byAgentId.versions.get.queryOptions({