@burdenoff/microfe-vibecontrols 2026.522.6 → 2026.522.8

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.
@@ -1 +1 @@
1
- {"version":3,"file":"VibesPage.js","names":[],"sources":["../../src/pages/VibesPage.tsx"],"sourcesContent":["import { useState, useMemo, useRef, useEffect, useCallback } from 'react';\nimport { Plus, Zap, Grid, List, RefreshCw, Archive, Play } from 'lucide-react';\nimport { VibeCard, VibeList } from '@/components/vibes';\nimport type { Vibe } from '@/components/vibes';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport {\n useListVibesQuery,\n useCreateVibeMutation,\n useUpdateVibeStatusMutation,\n useArchiveVibeMutation,\n useListAgentsLazyQuery,\n type ListAgentsQuery,\n} from '@/generated/wspace-operations';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\n\ntype Agent = NonNullable<ListAgentsQuery['vibecontrolsAgents']>[number];\nimport {\n PermissionGate,\n PermissionButton,\n AccessDenied,\n} from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport {\n buildProtocolUrl,\n getIdePreset,\n resolveEffectiveIdeCommand,\n} from '@/components/agents/idePresets';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\ntype ViewMode = 'grid' | 'list';\n\n/**\n * Vibes (projects/repositories) management page\n */\nexport function VibesPage() {\n const { onVibeActivate } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const { t } = useI18n();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const statusOptions = [\n { value: 'all', label: tr('vibesPage.statusAll', 'All Statuses') },\n { value: 'ACTIVE', label: tr('vibesPage.statusActive', 'Active') },\n { value: 'INACTIVE', label: tr('vibesPage.statusInactive', 'Inactive') },\n { value: 'ARCHIVED', label: tr('vibesPage.statusArchived', 'Archived') },\n { value: 'ERROR', label: tr('vibesPage.statusError', 'Error') },\n ];\n\n const typeOptions = [\n { value: 'all', label: tr('vibesPage.typeAll', 'All Types') },\n { value: 'PROJECT', label: tr('vibesPage.typeProject', 'Project') },\n { value: 'REPOSITORY', label: tr('vibesPage.typeRepository', 'Repository') },\n { value: 'WORKSPACE', label: tr('vibesPage.typeWorkspace', 'Workspace') },\n { value: 'MONOREPO', label: tr('vibesPage.typeMonorepo', 'Monorepo') },\n { value: 'PACKAGE', label: tr('vibesPage.typePackage', 'Package') },\n { value: 'CUSTOM', label: tr('vibesPage.typeCustom', 'Custom') },\n ];\n\n // Check permissions\n const canListVibes = hasPermission(VIBE_PERMISSIONS.VIBE_LIST);\n const canUpdateVibe = hasPermission(VIBE_PERMISSIONS.VIBE_UPDATE);\n\n // Skip queries until permissions are loaded and user has permission\n // This prevents FORBIDDEN errors from triggering auth error handlers\n const shouldSkipQueries = permissionsLoading || !canListVibes;\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [typeFilter, setTypeFilter] = useState('all');\n const [agentFilter, setAgentFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [createLoading, setCreateLoading] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string; path?: string }>({});\n const [pageError, setPageError] = useState<string | null>(null);\n const [selectedVibeIds, setSelectedVibeIds] = useState<Set<string>>(new Set());\n const [bulkAction, setBulkAction] = useState<'activate' | 'archive' | null>(null);\n const pageErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (pageErrorTimer.current) clearTimeout(pageErrorTimer.current);\n };\n }, []);\n\n const clearPageError = useCallback(() => {\n setPageError(null);\n if (pageErrorTimer.current) {\n clearTimeout(pageErrorTimer.current);\n pageErrorTimer.current = null;\n }\n }, []);\n\n // Form refs for create modal\n const nameRef = useRef<HTMLInputElement>(null);\n const pathRef = useRef<HTMLInputElement>(null);\n const agentRef = useRef<HTMLSelectElement>(null);\n const typeRef = useRef<HTMLSelectElement>(null);\n\n const [createVibe] = useCreateVibeMutation();\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_VIBE');\n const [updateVibeStatus] = useUpdateVibeStatusMutation();\n const [archiveVibe] = useArchiveVibeMutation();\n\n // Memoise variables so a stable identity keeps Apollo from treating each\n // render as a new query (which caused multiple loading flashes).\n // NOTE: we intentionally do NOT include `searchQuery` in the query\n // variables — the list is filtered client-side by name below, and keying\n // the query on `searchQuery` caused a fresh refetch on every keystroke\n // (previously mitigated by an unusable `limit: 1000` fire-hose). Rely on\n // the default backend pagination and filter client-side within the page.\n const listVibesVariables = useMemo(\n () => ({\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as\n | 'PROJECT'\n | 'REPOSITORY'\n | 'WORKSPACE'\n | 'MONOREPO'\n | 'PACKAGE'\n | 'CUSTOM')\n : undefined,\n // Multi-agent: server-side filter takes a list. Single-select UI maps\n // to a one-element array; \"all\" clears the filter.\n attachedAgentIds: agentFilter !== 'all' ? [agentFilter] : undefined,\n },\n }),\n [statusFilter, typeFilter, agentFilter]\n );\n\n // When no filters are applied, the variables match the InitialData\n // prefetch (no filters) — use cache-first to avoid a redundant network\n // hit on every page visit. When user actually narrows by filter, fall\n // back to cache-and-network for fresh server-side filtering.\n const noVibeFilters = statusFilter === 'all' && typeFilter === 'all' && agentFilter === 'all';\n const vibesFetchPolicy = noVibeFilters ? 'cache-first' : 'cache-and-network';\n\n // Fetch vibes from GraphQL API. cache-and-network returns the cached list\n // immediately while revalidating once in the background — avoids the\n // \"loading → cache → network → cache\" double-flash seen with cache-first\n // plus mutation refetchQueries. `notifyOnNetworkStatusChange` keeps the\n // loading flag true during refetches so the UI can show a spinner rather\n // than showing stale data silently.\n const { data, loading, error, refetch } = useListVibesQuery({\n variables: listVibesVariables,\n fetchPolicy: vibesFetchPolicy,\n nextFetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n notifyOnNetworkStatusChange: true,\n });\n\n // Cached agents (already prefetched by InitialDataPrefetcher) cover the\n // initial agent-filter dropdown without firing any network query. The\n // lazy ListAgentsQuery below is only triggered on first interaction with\n // the agent filter (in case the prefetch was stale).\n const { agents: cachedAgentsForFilter } = useCachedInitialData();\n const [fetchAgents, { data: agentsData }] = useListAgentsLazyQuery({\n fetchPolicy: 'cache-first',\n });\n const triggeredAgentsFetchRef = useRef(false);\n const handleAgentFilterInteract = useCallback(() => {\n if (triggeredAgentsFetchRef.current || shouldSkipQueries) return;\n triggeredAgentsFetchRef.current = true;\n void fetchAgents({ variables: { pagination: { limit: 100 } } });\n }, [fetchAgents, shouldSkipQueries]);\n\n // Build agent options for filter — prefer freshly-fetched data once the\n // dropdown has been opened, otherwise use the cached set from\n // InitialDataPrefetcher.\n const agentOptions = useMemo(() => {\n const live = agentsData?.vibecontrolsAgents ?? null;\n const source: Array<{ id: string; name: string }> = live\n ? live.map((a: Agent) => ({ id: a.id, name: a.name }))\n : cachedAgentsForFilter.map((a) => ({ id: a.id, name: a.name }));\n return [\n { value: 'all', label: tr('vibesPage.allAgents', 'All Agents') },\n ...source.map((agent) => ({\n value: agent.id,\n label: agent.name,\n })),\n ];\n }, [agentsData?.vibecontrolsAgents, cachedAgentsForFilter, tr]);\n\n // Map GraphQL response to Vibe type\n const vibes: Vibe[] = useMemo(() => {\n const rawVibes = data?.vibes?.items ?? [];\n return rawVibes.map((vibe: NonNullable<typeof rawVibes>[number]) => ({\n id: vibe.id,\n workspaceId: vibe.workspaceId,\n name: vibe.name,\n description: vibe.description || null,\n type: vibe.type as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: vibe.path || null,\n parentId: vibe.parentId || null,\n gitBranch: vibe.gitBranch || null,\n gitRemote: vibe.gitRemote || null,\n gitStatus: vibe.gitStatus\n ? typeof vibe.gitStatus === 'string'\n ? vibe.gitStatus\n : typeof vibe.gitStatus === 'object' &&\n vibe.gitStatus !== null &&\n 'status' in vibe.gitStatus\n ? String((vibe.gitStatus as Record<string, unknown>).status)\n : JSON.stringify(vibe.gitStatus)\n : null,\n environmentVariables: (vibe.environmentVariables as Record<string, string>) || {},\n tags: vibe.tags || [],\n metadata: (vibe.metadata as Record<string, unknown>) || {},\n createdAt: vibe.createdAt,\n updatedAt: vibe.updatedAt,\n agents: (vibe.agents ?? []).map((a: NonNullable<typeof vibe.agents>[number]) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n ownerUserId: a.ownerUserId ?? null,\n })),\n }));\n }, [data?.vibes?.items]);\n\n // Filter vibes based on search (client-side filtering for search)\n const filteredVibes = useMemo(() => {\n return vibes.filter((vibe) => {\n // Search filter\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n vibe.name.toLowerCase().includes(query) ||\n vibe.description?.toLowerCase().includes(query) ||\n vibe.path?.toLowerCase().includes(query) ||\n vibe.tags?.some((tag) => tag.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n\n return true;\n });\n }, [vibes, searchQuery]);\n\n const selectedVibes = useMemo(\n () => vibes.filter((vibe) => selectedVibeIds.has(vibe.id)),\n [vibes, selectedVibeIds]\n );\n const activatableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ACTIVE');\n const archivableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ARCHIVED');\n const allVisibleSelected =\n filteredVibes.length > 0 && filteredVibes.every((vibe) => selectedVibeIds.has(vibe.id));\n const partiallySelected =\n filteredVibes.some((vibe) => selectedVibeIds.has(vibe.id)) && !allVisibleSelected;\n\n const handleVibeSelect = (vibe: Vibe) => {\n navigate(`/vibes/${vibe.id}`);\n };\n\n const handleVibeActivate = async (vibe: Vibe) => {\n try {\n const result = await updateVibeStatus({\n variables: { id: vibe.id, status: 'ACTIVE' as const },\n });\n if (result.data?.updateVibeStatus) {\n onVibeActivate?.({ id: vibe.id, name: vibe.name, type: vibe.type, status: 'ACTIVE' });\n }\n } catch (err) {\n console.error('Failed to activate vibe:', err);\n }\n };\n\n const handleVibeArchive = async (vibe: Vibe) => {\n try {\n await archiveVibe({ variables: { id: vibe.id } });\n } catch (err) {\n console.error('Failed to archive vibe:', err);\n }\n };\n\n // Honour the user's preferred IDE + per-Vibe override when opening from\n // the card. Falls back to `vscode://` for IDEs without a registered URL\n // protocol; for richer launching (agent-exec, browser editor) use the\n // VibeOpenInEditorMenu on the Vibe detail page.\n const preferredIdeCommand = useVibeControlsStore((s) => s.preferredIdeCommand);\n const vibeIdeOverrides = useVibeControlsStore((s) => s.vibeIdeOverrides);\n\n const handleOpenInEditor = (vibe: Vibe) => {\n if (!vibe.path) {\n setPageError(tr('vibesPage.openEditorPathMissing', 'No path available to open in editor'));\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 3000);\n return;\n }\n const cmd = resolveEffectiveIdeCommand(vibe.id, preferredIdeCommand, vibeIdeOverrides);\n const preset = getIdePreset(cmd);\n const url = preset ? buildProtocolUrl(preset, vibe.path) : `vscode://file/${vibe.path}`;\n if (url) {\n window.location.href = url;\n } else {\n setPageError(\n tr(\n 'vibesPage.openEditorNoProtocol',\n `${preset?.label ?? cmd} has no URL handler — open this Vibe and use the editor menu.`\n )\n );\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 4000);\n }\n };\n\n const handleCreateVibe = () => {\n setCreateError(null);\n setShowCreateForm(true);\n // Ensure the create-form agent dropdown has options even if the user\n // never opened the page-level agent filter.\n handleAgentFilterInteract();\n };\n\n const toggleVibeSelection = (vibe: Vibe, checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVibes = (checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n filteredVibes.forEach((vibe) => {\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVibeIds(new Set());\n\n const handleBulkActivate = async () => {\n if (activatableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n activatableVibes.map((vibe) =>\n updateVibeStatus({ variables: { id: vibe.id, status: 'ACTIVE' as const } })\n )\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Activated ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleBulkArchive = async () => {\n if (archivableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n archivableVibes.map((vibe) => archiveVibe({ variables: { id: vibe.id } }))\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Archived ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleSubmitCreateVibe = async () => {\n const name = nameRef.current?.value?.trim();\n const path = pathRef.current?.value?.trim();\n const agentId = agentRef.current?.value;\n const type = typeRef.current?.value;\n\n const errors: { name?: string; path?: string } = {};\n if (!name) errors.name = tr('vibesPage.nameRequired', 'Name is required');\n if (!path) errors.path = tr('vibesPage.pathRequired', 'Path is required');\n if (errors.name || errors.path) {\n setFieldErrors(errors);\n setCreateError(tr('vibesPage.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n // Validate type against allowed enum values\n const validTypes = [\n 'PROJECT',\n 'REPOSITORY',\n 'WORKSPACE',\n 'MONOREPO',\n 'PACKAGE',\n 'CUSTOM',\n ] as const;\n const resolvedType = validTypes.includes(type as (typeof validTypes)[number])\n ? (type as (typeof validTypes)[number])\n : 'PROJECT';\n\n setCreateLoading(true);\n setCreateError(null);\n try {\n const result = await createVibe({\n variables: {\n input: {\n name,\n path,\n type: resolvedType,\n agentId: agentId || undefined,\n },\n },\n });\n if (!result.data?.createVibe?.id) {\n throw new Error(tr('vibesPage.failedCreate', 'Failed to create vibe'));\n }\n const newVibeId = result.data.createVibe.id;\n setShowCreateForm(false);\n setSuccessMessage(tr('vibesPage.createdSuccess', 'Vibe created successfully'));\n await flushTags(newVibeId);\n await refetch();\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n setCreateError(null);\n setFieldErrors({});\n setShowQuotaExhausted(true);\n return;\n }\n\n setCreateError(\n err instanceof Error ? err.message : tr('vibesPage.failedCreate', 'Failed to create vibe')\n );\n } finally {\n setCreateLoading(false);\n }\n };\n\n // Stats\n const stats = {\n total: vibes.length,\n active: vibes.filter((v) => v.status === 'ACTIVE').length,\n inactive: vibes.filter((v) => v.status === 'INACTIVE').length,\n };\n\n // Collapse guard states into a single MotionPage shell so transitioning\n // between loading / error / data does not unmount+remount and replay the\n // entrance animation. Contents are swapped in-place.\n const showPermissionsLoading = permissionsLoading;\n const showAccessDenied =\n !permissionsLoading &&\n !!permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !canListVibes;\n const showInitialLoading =\n !showPermissionsLoading && !showAccessDenied && loading && vibes.length === 0;\n const showInitialError =\n !showPermissionsLoading && !showAccessDenied && !!error && vibes.length === 0;\n const showDataShell = !showPermissionsLoading && !showAccessDenied && !showInitialError;\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {showPermissionsLoading && (\n <LoadingState message={tr('vibesPage.loadingPermissions', 'Loading permissions...')} />\n )}\n {showAccessDenied && (\n <AccessDenied\n title={tr('vibesPage.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'vibesPage.accessDeniedDescription',\n \"You don't have permission to view vibes. Contact your workspace administrator for access.\"\n )}\n />\n )}\n {showInitialError && (\n <ErrorState\n title={tr('vibesPage.failedLoad', 'Failed to load vibes')}\n message={error?.message ?? ''}\n onRetry={() => refetch()}\n />\n )}\n {showDataShell && (\n <>\n <PageHeader\n title={tr('vibesPage.title', 'Vibes')}\n description={tr('vibesPage.descriptionActive', '{{active}} of {{total}} vibes active', {\n active: stats.active,\n total: stats.total,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('vibesPage.refreshTitle', 'Refresh vibes')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton\n permission={VIBE_PERMISSIONS.VIBE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tr(\n 'vibesPage.deniedCreateTooltip',\n \"You don't have permission to create vibes\"\n )}\n >\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionButton>\n </div>\n }\n />\n\n {/* Filters Bar */}\n <div className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('vibesPage.searchPlaceholder', 'Search vibes...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"w-4 h-4 text-text-secondary\" />\n </button>\n <button\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.listViewTitle', 'List view')}\n >\n <List className=\"w-4 h-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters - wrap on all screens */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('vibesPage.filterStatus', 'Status')}\n value={statusFilter}\n options={statusOptions}\n onChange={setStatusFilter}\n />\n <FilterDropdown\n label={tr('vibesPage.filterType', 'Type')}\n value={typeFilter}\n options={typeOptions}\n onChange={setTypeFilter}\n />\n <div onMouseDown={handleAgentFilterInteract} onFocus={handleAgentFilterInteract}>\n <FilterDropdown\n label={tr('vibesPage.filterAgent', 'Agent')}\n value={agentFilter}\n options={agentOptions}\n onChange={setAgentFilter}\n />\n </div>\n </div>\n </div>\n\n {/* Results Count */}\n {searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all' ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('vibesPage.showingResults', 'Showing {{filtered}} of {{total}} vibes', {\n filtered: filteredVibes.length,\n total: vibes.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('vibesPage.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n ) : null}\n\n {filteredVibes.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVibes}\n ariaLabel=\"Select all visible vibes\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all visible\n </span>\n </div>\n {selectedVibeIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedVibeIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction('activate')}\n disabled={activatableVibes.length === 0}\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play className=\"w-4 h-4\" />\n Activate Selected\n {activatableVibes.length > 0 ? ` (${activatableVibes.length})` : ''}\n </span>\n </button>\n <button\n onClick={() => setBulkAction('archive')}\n disabled={archivableVibes.length === 0}\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Archive className=\"w-4 h-4\" />\n Archive Selected\n {archivableVibes.length > 0 ? ` (${archivableVibes.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Vibes Display */}\n {showInitialLoading ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredVibes.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n {viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredVibes.map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard\n vibe={vibe}\n onOpen={handleVibeSelect}\n onActivate={canUpdateVibe ? handleVibeActivate : undefined}\n onArchive={canUpdateVibe ? handleVibeArchive : undefined}\n onOpenInEditor={handleOpenInEditor}\n selected={selectedVibeIds.has(vibe.id)}\n onToggleSelect={(checked) => toggleVibeSelection(vibe, checked)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionList key=\"list\">\n <VibeList\n vibes={filteredVibes}\n onOpen={handleVibeSelect}\n onActivate={handleVibeActivate}\n onArchive={handleVibeArchive}\n onOpenInEditor={handleOpenInEditor}\n selectedVibeIds={selectedVibeIds}\n onToggleSelect={toggleVibeSelection}\n onToggleAllVisible={toggleAllVisibleVibes}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n />\n </MotionList>\n )}\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Zap className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyNoVibesFound', 'No vibes found')\n : tr('vibesPage.emptyNoVibesConfigured', 'No vibes configured')\n }\n description={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'vibesPage.emptyNoVibesDescription',\n 'Add your first vibe to start managing your development projects'\n )\n }\n action={\n !searchQuery &&\n statusFilter === 'all' &&\n typeFilter === 'all' &&\n agentFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <PermissionGate permission={VIBE_PERMISSIONS.VIBE_CREATE}>\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionGate>\n <GetHelpButton prompt=\"What is a Vibe in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n <ConfirmDialog\n isOpen={bulkAction === 'activate'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkActivate}\n title=\"Activate selected vibes\"\n message={`Activate ${activatableVibes.length} selected vibe${activatableVibes.length === 1 ? '' : 's'}?`}\n confirmLabel=\"Activate\"\n variant=\"default\"\n />\n\n <ConfirmDialog\n isOpen={bulkAction === 'archive'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkArchive}\n title=\"Archive selected vibes\"\n message={`Archive ${archivableVibes.length} selected vibe${archivableVibes.length === 1 ? '' : 's'}? Archived vibes will be hidden from active workflows.`}\n confirmLabel=\"Archive\"\n variant=\"warning\"\n />\n\n {/* Page-level Error Toast */}\n {pageError && (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-error-bg text-status-error-text rounded-md shadow-lg text-sm\"\n >\n {pageError}\n <button\n onClick={clearPageError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('vibesPage.dismiss', 'Dismiss')}\n </button>\n </div>\n )}\n\n {/* Create Vibe Modal */}\n {showCreateForm && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('vibesPage.addNewVibe', 'Add New Vibe')}\n </h2>\n <div className=\"space-y-4\">\n {createError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {createError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.nameLabel', 'Name *')}\n </label>\n <input\n ref={nameRef}\n type=\"text\"\n placeholder={tr('vibesPage.namePlaceholder', 'My Project')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.pathLabel', 'Path *')}\n </label>\n <input\n ref={pathRef}\n type=\"text\"\n placeholder={tr('vibesPage.pathPlaceholder', '/path/to/project')}\n onChange={() =>\n fieldErrors.path && setFieldErrors((e) => ({ ...e, path: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.path ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.path && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.path}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.agentLabel', 'Agent')}\n </label>\n <select\n ref={agentRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"\">{tr('vibesPage.selectAgent', 'Select an agent')}</option>\n {agentOptions\n .filter((opt) => opt.value !== 'all')\n .map((agent) => (\n <option key={agent.value} value={agent.value}>\n {agent.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"PROJECT\">{tr('vibesPage.typeProject', 'Project')}</option>\n <option value=\"REPOSITORY\">\n {tr('vibesPage.typeRepository', 'Repository')}\n </option>\n <option value=\"WORKSPACE\">\n {tr('vibesPage.typeWorkspace', 'Workspace')}\n </option>\n <option value=\"MONOREPO\">{tr('vibesPage.typeMonorepo', 'Monorepo')}</option>\n <option value=\"PACKAGE\">{tr('vibesPage.typePackage', 'Package')}</option>\n <option value=\"CUSTOM\">{tr('vibesPage.typeCustom', 'Custom')}</option>\n </select>\n </div>\n </div>\n <div className=\"mt-4 border-t border-border-default pt-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibesPage.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" onDraftChange={setDraftTagIds} />\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCreateForm(false)}\n disabled={createLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('vibesPage.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleSubmitCreateVibe}\n disabled={createLoading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {createLoading\n ? tr('vibesPage.creating', 'Creating...')\n : tr('vibesPage.createVibe', 'Create Vibe')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Vibe Quota Reached\"\n message=\"You've used all available vibe creation quota on your current plan. Upgrade your plan to create more vibes.\"\n />\n </>\n )}\n </MotionPage>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"VibesPage.js","names":[],"sources":["../../src/pages/VibesPage.tsx"],"sourcesContent":["import { useState, useMemo, useRef, useEffect, useCallback } from 'react';\nimport { Plus, Zap, Grid, List, RefreshCw, Archive, Play } from 'lucide-react';\nimport { VibeCard, VibeList } from '@/components/vibes';\nimport type { Vibe } from '@/components/vibes';\nimport { EntityTagPicker } from '@/components/shared/EntityTagPicker';\nimport { PageSkeleton } from '@/components/shared/PageSkeleton';\nimport { useDraftEntityTags } from '@/hooks/useDraftEntityTags';\nimport {\n PageHeader,\n SearchInput,\n FilterDropdown,\n EmptyState,\n LoadingState,\n ErrorState,\n ConfirmDialog,\n QuotaExhaustedDialog,\n SelectionCheckbox,\n GetHelpButton,\n} from '@/components/shared';\nimport { MotionPage, MotionList, MotionItem } from '@burdenoff/fe-libs/shared/motion';\nimport { AnimatePresence } from 'framer-motion';\nimport { useVibeControls, useVibeNavigate } from '@/providers/VibeControlsProvider';\nimport {\n useListVibesQuery,\n useCreateVibeMutation,\n useUpdateVibeStatusMutation,\n useArchiveVibeMutation,\n useListAgentsLazyQuery,\n type ListAgentsQuery,\n} from '@/generated/wspace-operations';\nimport { useCachedInitialData } from '@/hooks/useCachedInitialData';\n\ntype Agent = NonNullable<ListAgentsQuery['vibecontrolsAgents']>[number];\nimport {\n PermissionGate,\n PermissionButton,\n AccessDenied,\n} from '@burdenoff/fe-libs/shared/components';\nimport { usePermissions } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { VIBE_PERMISSIONS } from '@/constants/permissions';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport {\n buildProtocolUrl,\n getIdePreset,\n resolveEffectiveIdeCommand,\n} from '@/components/agents/idePresets';\nimport { useVibeControlsStore } from '@/store/vibeControlsStore';\n\ntype ViewMode = 'grid' | 'list';\n\n/**\n * Vibes (projects/repositories) management page\n */\nexport function VibesPage() {\n const { onVibeActivate } = useVibeControls();\n const navigate = useVibeNavigate();\n const { hasPermission, isLoading: permissionsLoading, permissions } = usePermissions();\n const { t } = useI18n();\n\n const tr = useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const template = translated === key ? fallback : translated;\n\n if (!params) return template;\n\n return Object.entries(params).reduce(\n (acc, [paramKey, paramValue]) => acc.replace(`{{${paramKey}}}`, String(paramValue)),\n template\n );\n },\n [t]\n );\n\n const statusOptions = [\n { value: 'all', label: tr('vibesPage.statusAll', 'All Statuses') },\n { value: 'ACTIVE', label: tr('vibesPage.statusActive', 'Active') },\n { value: 'INACTIVE', label: tr('vibesPage.statusInactive', 'Inactive') },\n { value: 'ARCHIVED', label: tr('vibesPage.statusArchived', 'Archived') },\n { value: 'ERROR', label: tr('vibesPage.statusError', 'Error') },\n ];\n\n const typeOptions = [\n { value: 'all', label: tr('vibesPage.typeAll', 'All Types') },\n { value: 'PROJECT', label: tr('vibesPage.typeProject', 'Project') },\n { value: 'REPOSITORY', label: tr('vibesPage.typeRepository', 'Repository') },\n { value: 'WORKSPACE', label: tr('vibesPage.typeWorkspace', 'Workspace') },\n { value: 'MONOREPO', label: tr('vibesPage.typeMonorepo', 'Monorepo') },\n { value: 'PACKAGE', label: tr('vibesPage.typePackage', 'Package') },\n { value: 'CUSTOM', label: tr('vibesPage.typeCustom', 'Custom') },\n ];\n\n // Check permissions\n const canListVibes = hasPermission(VIBE_PERMISSIONS.VIBE_LIST);\n const canUpdateVibe = hasPermission(VIBE_PERMISSIONS.VIBE_UPDATE);\n\n // Skip queries until permissions are loaded and user has permission\n // This prevents FORBIDDEN errors from triggering auth error handlers\n const shouldSkipQueries = permissionsLoading || !canListVibes;\n\n const [searchQuery, setSearchQuery] = useState('');\n const [statusFilter, setStatusFilter] = useState('all');\n const [typeFilter, setTypeFilter] = useState('all');\n const [agentFilter, setAgentFilter] = useState('all');\n const [viewMode, setViewMode] = useState<ViewMode>('grid');\n const [showCreateForm, setShowCreateForm] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n const [createLoading, setCreateLoading] = useState(false);\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ name?: string; path?: string }>({});\n const [pageError, setPageError] = useState<string | null>(null);\n const [selectedVibeIds, setSelectedVibeIds] = useState<Set<string>>(new Set());\n const [bulkAction, setBulkAction] = useState<'activate' | 'archive' | null>(null);\n const pageErrorTimer = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n return () => {\n if (pageErrorTimer.current) clearTimeout(pageErrorTimer.current);\n };\n }, []);\n\n const clearPageError = useCallback(() => {\n setPageError(null);\n if (pageErrorTimer.current) {\n clearTimeout(pageErrorTimer.current);\n pageErrorTimer.current = null;\n }\n }, []);\n\n // Form refs for create modal\n const nameRef = useRef<HTMLInputElement>(null);\n const pathRef = useRef<HTMLInputElement>(null);\n const agentRef = useRef<HTMLSelectElement>(null);\n const typeRef = useRef<HTMLSelectElement>(null);\n\n const [createVibe] = useCreateVibeMutation();\n const { setDraftTagIds, flushTags } = useDraftEntityTags('VIBECONTROLS_VIBE');\n const [updateVibeStatus] = useUpdateVibeStatusMutation();\n const [archiveVibe] = useArchiveVibeMutation();\n\n // Memoise variables so a stable identity keeps Apollo from treating each\n // render as a new query (which caused multiple loading flashes).\n // NOTE: we intentionally do NOT include `searchQuery` in the query\n // variables — the list is filtered client-side by name below, and keying\n // the query on `searchQuery` caused a fresh refetch on every keystroke\n // (previously mitigated by an unusable `limit: 1000` fire-hose). Rely on\n // the default backend pagination and filter client-side within the page.\n const listVibesVariables = useMemo(\n () => ({\n filter: {\n status:\n statusFilter !== 'all'\n ? (statusFilter as 'ACTIVE' | 'INACTIVE' | 'ARCHIVED' | 'ERROR')\n : undefined,\n type:\n typeFilter !== 'all'\n ? (typeFilter as\n | 'PROJECT'\n | 'REPOSITORY'\n | 'WORKSPACE'\n | 'MONOREPO'\n | 'PACKAGE'\n | 'CUSTOM')\n : undefined,\n // Multi-agent: server-side filter takes a list. Single-select UI maps\n // to a one-element array; \"all\" clears the filter.\n attachedAgentIds: agentFilter !== 'all' ? [agentFilter] : undefined,\n },\n }),\n [statusFilter, typeFilter, agentFilter]\n );\n\n // When no filters are applied, the variables match the InitialData\n // prefetch (no filters) — use cache-first to avoid a redundant network\n // hit on every page visit. When user actually narrows by filter, fall\n // back to cache-and-network for fresh server-side filtering.\n const noVibeFilters = statusFilter === 'all' && typeFilter === 'all' && agentFilter === 'all';\n const vibesFetchPolicy = noVibeFilters ? 'cache-first' : 'cache-and-network';\n\n // Fetch vibes from GraphQL API. cache-and-network returns the cached list\n // immediately while revalidating once in the background — avoids the\n // \"loading → cache → network → cache\" double-flash seen with cache-first\n // plus mutation refetchQueries. `notifyOnNetworkStatusChange` keeps the\n // loading flag true during refetches so the UI can show a spinner rather\n // than showing stale data silently.\n const { data, loading, error, refetch } = useListVibesQuery({\n variables: listVibesVariables,\n fetchPolicy: vibesFetchPolicy,\n nextFetchPolicy: 'cache-first',\n skip: shouldSkipQueries,\n notifyOnNetworkStatusChange: true,\n });\n\n // Cached agents (already prefetched by InitialDataPrefetcher) cover the\n // initial agent-filter dropdown without firing any network query. The\n // lazy ListAgentsQuery below is only triggered on first interaction with\n // the agent filter (in case the prefetch was stale).\n const { agents: cachedAgentsForFilter } = useCachedInitialData();\n const [fetchAgents, { data: agentsData }] = useListAgentsLazyQuery({\n fetchPolicy: 'cache-first',\n });\n const triggeredAgentsFetchRef = useRef(false);\n const handleAgentFilterInteract = useCallback(() => {\n if (triggeredAgentsFetchRef.current || shouldSkipQueries) return;\n triggeredAgentsFetchRef.current = true;\n void fetchAgents({ variables: { pagination: { limit: 100 } } });\n }, [fetchAgents, shouldSkipQueries]);\n\n // Build agent options for filter — prefer freshly-fetched data once the\n // dropdown has been opened, otherwise use the cached set from\n // InitialDataPrefetcher.\n const agentOptions = useMemo(() => {\n const live = agentsData?.vibecontrolsAgents ?? null;\n const source: Array<{ id: string; name: string }> = live\n ? live.map((a: Agent) => ({ id: a.id, name: a.name }))\n : cachedAgentsForFilter.map((a) => ({ id: a.id, name: a.name }));\n return [\n { value: 'all', label: tr('vibesPage.allAgents', 'All Agents') },\n ...source.map((agent) => ({\n value: agent.id,\n label: agent.name,\n })),\n ];\n }, [agentsData?.vibecontrolsAgents, cachedAgentsForFilter, tr]);\n\n // Map GraphQL response to Vibe type\n const vibes: Vibe[] = useMemo(() => {\n const rawVibes = data?.vibes?.items ?? [];\n return rawVibes.map((vibe: NonNullable<typeof rawVibes>[number]) => ({\n id: vibe.id,\n workspaceId: vibe.workspaceId,\n name: vibe.name,\n description: vibe.description || null,\n type: vibe.type as Vibe['type'],\n status: vibe.status as Vibe['status'],\n path: vibe.path || null,\n parentId: vibe.parentId || null,\n gitBranch: vibe.gitBranch || null,\n gitRemote: vibe.gitRemote || null,\n gitStatus: vibe.gitStatus\n ? typeof vibe.gitStatus === 'string'\n ? vibe.gitStatus\n : typeof vibe.gitStatus === 'object' &&\n vibe.gitStatus !== null &&\n 'status' in vibe.gitStatus\n ? String((vibe.gitStatus as Record<string, unknown>).status)\n : JSON.stringify(vibe.gitStatus)\n : null,\n environmentVariables: (vibe.environmentVariables as Record<string, string>) || {},\n tags: vibe.tags || [],\n metadata: (vibe.metadata as Record<string, unknown>) || {},\n createdAt: vibe.createdAt,\n updatedAt: vibe.updatedAt,\n agents: (vibe.agents ?? []).map((a: NonNullable<typeof vibe.agents>[number]) => ({\n id: a.id,\n name: a.name,\n isActive: a.isActive,\n ownerUserId: a.ownerUserId ?? null,\n })),\n }));\n }, [data?.vibes?.items]);\n\n // Filter vibes based on search (client-side filtering for search)\n const filteredVibes = useMemo(() => {\n return vibes.filter((vibe) => {\n // Search filter\n if (searchQuery) {\n const query = searchQuery.toLowerCase();\n const matchesSearch =\n vibe.name.toLowerCase().includes(query) ||\n vibe.description?.toLowerCase().includes(query) ||\n vibe.path?.toLowerCase().includes(query) ||\n vibe.tags?.some((tag) => tag.toLowerCase().includes(query));\n if (!matchesSearch) return false;\n }\n\n return true;\n });\n }, [vibes, searchQuery]);\n\n const selectedVibes = useMemo(\n () => vibes.filter((vibe) => selectedVibeIds.has(vibe.id)),\n [vibes, selectedVibeIds]\n );\n const activatableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ACTIVE');\n const archivableVibes = selectedVibes.filter((vibe) => vibe.status !== 'ARCHIVED');\n const allVisibleSelected =\n filteredVibes.length > 0 && filteredVibes.every((vibe) => selectedVibeIds.has(vibe.id));\n const partiallySelected =\n filteredVibes.some((vibe) => selectedVibeIds.has(vibe.id)) && !allVisibleSelected;\n\n const handleVibeSelect = (vibe: Vibe) => {\n navigate(`/vibes/${vibe.id}`);\n };\n\n const handleVibeActivate = async (vibe: Vibe) => {\n try {\n const result = await updateVibeStatus({\n variables: { id: vibe.id, status: 'ACTIVE' as const },\n });\n if (result.data?.updateVibeStatus) {\n onVibeActivate?.({ id: vibe.id, name: vibe.name, type: vibe.type, status: 'ACTIVE' });\n }\n } catch (err) {\n console.error('Failed to activate vibe:', err);\n }\n };\n\n const handleVibeArchive = async (vibe: Vibe) => {\n try {\n await archiveVibe({ variables: { id: vibe.id } });\n } catch (err) {\n console.error('Failed to archive vibe:', err);\n }\n };\n\n // Honour the user's preferred IDE + per-Vibe override when opening from\n // the card. Falls back to `vscode://` for IDEs without a registered URL\n // protocol; for richer launching (agent-exec, browser editor) use the\n // VibeOpenInEditorMenu on the Vibe detail page.\n const preferredIdeCommand = useVibeControlsStore((s) => s.preferredIdeCommand);\n const vibeIdeOverrides = useVibeControlsStore((s) => s.vibeIdeOverrides);\n\n const handleOpenInEditor = (vibe: Vibe) => {\n if (!vibe.path) {\n setPageError(tr('vibesPage.openEditorPathMissing', 'No path available to open in editor'));\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 3000);\n return;\n }\n const cmd = resolveEffectiveIdeCommand(vibe.id, preferredIdeCommand, vibeIdeOverrides);\n const preset = getIdePreset(cmd);\n const url = preset ? buildProtocolUrl(preset, vibe.path) : `vscode://file/${vibe.path}`;\n if (url) {\n window.location.href = url;\n } else {\n setPageError(\n tr(\n 'vibesPage.openEditorNoProtocol',\n `${preset?.label ?? cmd} has no URL handler — open this Vibe and use the editor menu.`\n )\n );\n pageErrorTimer.current = setTimeout(() => {\n setPageError(null);\n pageErrorTimer.current = null;\n }, 4000);\n }\n };\n\n const handleCreateVibe = () => {\n setCreateError(null);\n setShowCreateForm(true);\n // Ensure the create-form agent dropdown has options even if the user\n // never opened the page-level agent filter.\n handleAgentFilterInteract();\n };\n\n const toggleVibeSelection = (vibe: Vibe, checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n return next;\n });\n };\n\n const toggleAllVisibleVibes = (checked: boolean) => {\n setSelectedVibeIds((prev) => {\n const next = new Set(prev);\n filteredVibes.forEach((vibe) => {\n if (checked) {\n next.add(vibe.id);\n } else {\n next.delete(vibe.id);\n }\n });\n return next;\n });\n };\n\n const clearSelection = () => setSelectedVibeIds(new Set());\n\n const handleBulkActivate = async () => {\n if (activatableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n activatableVibes.map((vibe) =>\n updateVibeStatus({ variables: { id: vibe.id, status: 'ACTIVE' as const } })\n )\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Activated ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleBulkArchive = async () => {\n if (archivableVibes.length === 0) return;\n\n const results = await Promise.allSettled(\n archivableVibes.map((vibe) => archiveVibe({ variables: { id: vibe.id } }))\n );\n\n const succeeded = results.filter((result) => result.status === 'fulfilled').length;\n const failed = results.length - succeeded;\n\n setSuccessMessage(\n `Archived ${succeeded} vibe${succeeded === 1 ? '' : 's'}${failed > 0 ? ` • ${failed} failed` : ''}`\n );\n setTimeout(() => setSuccessMessage(null), 4000);\n clearSelection();\n setBulkAction(null);\n await refetch();\n };\n\n const handleSubmitCreateVibe = async () => {\n const name = nameRef.current?.value?.trim();\n const path = pathRef.current?.value?.trim();\n const agentId = agentRef.current?.value;\n const type = typeRef.current?.value;\n\n const errors: { name?: string; path?: string } = {};\n if (!name) errors.name = tr('vibesPage.nameRequired', 'Name is required');\n if (!path) errors.path = tr('vibesPage.pathRequired', 'Path is required');\n if (errors.name || errors.path) {\n setFieldErrors(errors);\n setCreateError(tr('vibesPage.fillRequired', 'Please fill in all required fields'));\n return;\n }\n setFieldErrors({});\n\n // Validate type against allowed enum values\n const validTypes = [\n 'PROJECT',\n 'REPOSITORY',\n 'WORKSPACE',\n 'MONOREPO',\n 'PACKAGE',\n 'CUSTOM',\n ] as const;\n const resolvedType = validTypes.includes(type as (typeof validTypes)[number])\n ? (type as (typeof validTypes)[number])\n : 'PROJECT';\n\n setCreateLoading(true);\n setCreateError(null);\n try {\n const result = await createVibe({\n variables: {\n input: {\n name,\n path,\n type: resolvedType,\n agentId: agentId || undefined,\n },\n },\n });\n if (!result.data?.createVibe?.id) {\n throw new Error(tr('vibesPage.failedCreate', 'Failed to create vibe'));\n }\n const newVibeId = result.data.createVibe.id;\n setShowCreateForm(false);\n setSuccessMessage(tr('vibesPage.createdSuccess', 'Vibe created successfully'));\n await flushTags(newVibeId);\n await refetch();\n setTimeout(() => setSuccessMessage(null), 3000);\n } catch (err) {\n if (isQuotaExhaustedError(err)) {\n setShowCreateForm(false);\n setCreateError(null);\n setFieldErrors({});\n setShowQuotaExhausted(true);\n return;\n }\n\n setCreateError(\n err instanceof Error ? err.message : tr('vibesPage.failedCreate', 'Failed to create vibe')\n );\n } finally {\n setCreateLoading(false);\n }\n };\n\n // Stats\n const stats = {\n total: vibes.length,\n active: vibes.filter((v) => v.status === 'ACTIVE').length,\n inactive: vibes.filter((v) => v.status === 'INACTIVE').length,\n };\n\n // Collapse guard states into a single MotionPage shell so transitioning\n // between loading / error / data does not unmount+remount and replay the\n // entrance animation. Contents are swapped in-place.\n const showPermissionsLoading = permissionsLoading;\n const showAccessDenied =\n !permissionsLoading &&\n !!permissions &&\n (permissions.permissionStrings?.length ?? 0) > 0 &&\n !canListVibes;\n const showInitialLoading =\n !showPermissionsLoading && !showAccessDenied && loading && vibes.length === 0;\n const showInitialError =\n !showPermissionsLoading && !showAccessDenied && !!error && vibes.length === 0;\n const showDataShell = !showPermissionsLoading && !showAccessDenied && !showInitialError;\n\n return (\n <MotionPage className=\"space-y-6 p-4 sm:p-6 bg-bg-surface min-h-screen\">\n {showPermissionsLoading && (\n <LoadingState message={tr('vibesPage.loadingPermissions', 'Loading permissions...')} />\n )}\n {showAccessDenied && (\n <AccessDenied\n title={tr('vibesPage.accessDeniedTitle', 'Access Denied')}\n description={tr(\n 'vibesPage.accessDeniedDescription',\n \"You don't have permission to view vibes. Contact your workspace administrator for access.\"\n )}\n />\n )}\n {showInitialError && (\n <ErrorState\n title={tr('vibesPage.failedLoad', 'Failed to load vibes')}\n message={error?.message ?? ''}\n onRetry={() => refetch()}\n />\n )}\n {showDataShell && (\n <>\n <PageHeader\n title={tr('vibesPage.title', 'Vibes')}\n description={tr('vibesPage.descriptionActive', '{{active}} of {{total}} vibes active', {\n active: stats.active,\n total: stats.total,\n })}\n actions={\n <div className=\"flex items-center gap-2\">\n <button\n onClick={() => refetch()}\n disabled={loading}\n className=\"flex items-center gap-2 px-3 py-2 text-text-secondary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n title={tr('vibesPage.refreshTitle', 'Refresh vibes')}\n >\n <RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />\n </button>\n <PermissionButton\n permission={VIBE_PERMISSIONS.VIBE_CREATE}\n deniedBehavior=\"disable\"\n deniedTooltip={tr(\n 'vibesPage.deniedCreateTooltip',\n \"You don't have permission to create vibes\"\n )}\n >\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50 disabled:cursor-not-allowed\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionButton>\n </div>\n }\n />\n\n {/* Filters Bar */}\n <div className=\"space-y-3\">\n {/* Row 1: Search + View Toggle */}\n <div className=\"flex items-center gap-2\">\n <SearchInput\n value={searchQuery}\n onChange={setSearchQuery}\n placeholder={tr('vibesPage.searchPlaceholder', 'Search vibes...')}\n className=\"flex-1 sm:w-64 sm:flex-none\"\n />\n {/* View Toggle */}\n <div className=\"flex items-center gap-1 bg-bg-sunken rounded-md p-1 flex-shrink-0\">\n <button\n onClick={() => setViewMode('grid')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'grid' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.gridViewTitle', 'Grid view')}\n >\n <Grid className=\"w-4 h-4 text-text-secondary\" />\n </button>\n <button\n onClick={() => setViewMode('list')}\n className={`p-1.5 sm:p-2 rounded ${viewMode === 'list' ? 'bg-bg-surface shadow-sm' : 'hover:bg-bg-surface/50'}`}\n title={tr('vibesPage.listViewTitle', 'List view')}\n >\n <List className=\"w-4 h-4 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Filters - wrap on all screens */}\n <div className=\"flex items-center gap-2 sm:gap-3 flex-wrap\">\n <FilterDropdown\n label={tr('vibesPage.filterStatus', 'Status')}\n value={statusFilter}\n options={statusOptions}\n onChange={setStatusFilter}\n />\n <div onMouseDown={handleAgentFilterInteract} onFocus={handleAgentFilterInteract}>\n <FilterDropdown\n label={tr('vibesPage.filterAgent', 'Agent')}\n value={agentFilter}\n options={agentOptions}\n onChange={setAgentFilter}\n />\n </div>\n </div>\n </div>\n\n {/* Results Count */}\n {searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all' ? (\n <p className=\"text-sm text-text-secondary\">\n {tr('vibesPage.showingResults', 'Showing {{filtered}} of {{total}} vibes', {\n filtered: filteredVibes.length,\n total: vibes.length,\n })}\n {loading && (\n <span className=\"ml-2 text-text-muted\">\n {tr('vibesPage.refreshing', '(refreshing...)')}\n </span>\n )}\n </p>\n ) : null}\n\n {filteredVibes.length > 0 && (\n <div className=\"rounded-lg border border-border-default bg-bg-surface p-4\">\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <div className=\"flex flex-wrap items-center gap-3\">\n <div className=\"flex items-center gap-2\">\n <SelectionCheckbox\n checked={allVisibleSelected}\n indeterminate={partiallySelected}\n onChange={toggleAllVisibleVibes}\n ariaLabel=\"Select all visible vibes\"\n />\n <span className=\"text-sm font-medium text-text-primary\">\n Select all visible\n </span>\n </div>\n {selectedVibeIds.size > 0 && (\n <button\n onClick={clearSelection}\n className=\"text-sm text-text-secondary hover:text-text-primary\"\n >\n Clear selection\n </button>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"rounded-full bg-bg-sunken px-3 py-1 text-sm font-medium text-text-secondary\">\n {selectedVibeIds.size} selected\n </span>\n <button\n onClick={() => setBulkAction('activate')}\n disabled={activatableVibes.length === 0}\n title=\"Mark selected vibes as ACTIVE so they appear in the default vibe list and can run sessions/tunnels via their attached agent(s).\"\n className=\"rounded-md border border-status-success-border px-3 py-2 text-sm font-medium text-status-success-text hover:bg-status-success-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Play className=\"w-4 h-4\" />\n Activate Selected\n {activatableVibes.length > 0 ? ` (${activatableVibes.length})` : ''}\n </span>\n </button>\n <button\n onClick={() => setBulkAction('archive')}\n disabled={archivableVibes.length === 0}\n className=\"rounded-md border border-status-warning-border px-3 py-2 text-sm font-medium text-status-warning-text hover:bg-status-warning-bg disabled:cursor-not-allowed disabled:opacity-50\"\n >\n <span className=\"inline-flex items-center gap-2\">\n <Archive className=\"w-4 h-4\" />\n Archive Selected\n {archivableVibes.length > 0 ? ` (${archivableVibes.length})` : ''}\n </span>\n </button>\n </div>\n </div>\n </div>\n )}\n\n {/* Vibes Display */}\n {showInitialLoading ? (\n <PageSkeleton variant=\"cards\" count={6} />\n ) : filteredVibes.length > 0 ? (\n <AnimatePresence mode=\"wait\">\n {viewMode === 'grid' ? (\n <MotionList\n key=\"grid\"\n stagger={0.04}\n className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4\"\n >\n {filteredVibes.map((vibe) => (\n <MotionItem key={vibe.id}>\n <VibeCard\n vibe={vibe}\n onOpen={handleVibeSelect}\n onActivate={canUpdateVibe ? handleVibeActivate : undefined}\n onArchive={canUpdateVibe ? handleVibeArchive : undefined}\n onOpenInEditor={handleOpenInEditor}\n selected={selectedVibeIds.has(vibe.id)}\n onToggleSelect={(checked) => toggleVibeSelection(vibe, checked)}\n />\n </MotionItem>\n ))}\n </MotionList>\n ) : (\n <MotionList key=\"list\">\n <VibeList\n vibes={filteredVibes}\n onOpen={handleVibeSelect}\n onActivate={handleVibeActivate}\n onArchive={handleVibeArchive}\n onOpenInEditor={handleOpenInEditor}\n selectedVibeIds={selectedVibeIds}\n onToggleSelect={toggleVibeSelection}\n onToggleAllVisible={toggleAllVisibleVibes}\n allVisibleSelected={allVisibleSelected}\n partiallySelected={partiallySelected}\n />\n </MotionList>\n )}\n </AnimatePresence>\n ) : (\n <EmptyState\n icon={<Zap className=\"w-8 h-8 text-text-secondary\" />}\n title={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyNoVibesFound', 'No vibes found')\n : tr('vibesPage.emptyNoVibesConfigured', 'No vibes configured')\n }\n description={\n searchQuery ||\n statusFilter !== 'all' ||\n typeFilter !== 'all' ||\n agentFilter !== 'all'\n ? tr('vibesPage.emptyAdjustSearch', 'Try adjusting your search or filters')\n : tr(\n 'vibesPage.emptyNoVibesDescription',\n 'Add your first vibe to start managing your development projects'\n )\n }\n action={\n !searchQuery &&\n statusFilter === 'all' &&\n typeFilter === 'all' &&\n agentFilter === 'all' ? (\n <div className=\"flex items-center gap-2\">\n <PermissionGate permission={VIBE_PERMISSIONS.VIBE_CREATE}>\n <button\n onClick={handleCreateVibe}\n className=\"flex items-center gap-2 px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors\"\n >\n <Plus className=\"w-4 h-4\" />\n {tr('vibesPage.addVibe', 'Add Vibe')}\n </button>\n </PermissionGate>\n <GetHelpButton prompt=\"What is a Vibe in VibeControls and how do I create one?\" />\n </div>\n ) : undefined\n }\n />\n )}\n\n {/* Success Toast */}\n {successMessage && (\n <div className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-success-bg text-status-success-text rounded-md shadow-lg text-sm\">\n {successMessage}\n </div>\n )}\n\n <ConfirmDialog\n isOpen={bulkAction === 'activate'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkActivate}\n title=\"Activate selected vibes\"\n message={`Activate ${activatableVibes.length} selected vibe${activatableVibes.length === 1 ? '' : 's'}?`}\n confirmLabel=\"Activate\"\n variant=\"default\"\n />\n\n <ConfirmDialog\n isOpen={bulkAction === 'archive'}\n onClose={() => setBulkAction(null)}\n onConfirm={handleBulkArchive}\n title=\"Archive selected vibes\"\n message={`Archive ${archivableVibes.length} selected vibe${archivableVibes.length === 1 ? '' : 's'}? Archived vibes will be hidden from active workflows.`}\n confirmLabel=\"Archive\"\n variant=\"warning\"\n />\n\n {/* Page-level Error Toast */}\n {pageError && (\n <div\n role=\"alert\"\n className=\"fixed bottom-4 right-4 z-50 px-4 py-3 bg-status-error-bg text-status-error-text rounded-md shadow-lg text-sm\"\n >\n {pageError}\n <button\n onClick={clearPageError}\n className=\"ml-3 text-xs underline opacity-80 hover:opacity-100\"\n >\n {tr('vibesPage.dismiss', 'Dismiss')}\n </button>\n </div>\n )}\n\n {/* Create Vibe Modal */}\n {showCreateForm && (\n <div className=\"fixed inset-0 bg-bg-overlay flex items-center justify-center z-50 p-4\">\n <div className=\"bg-bg-surface rounded-lg p-6 w-full max-w-lg\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-4\">\n {tr('vibesPage.addNewVibe', 'Add New Vibe')}\n </h2>\n <div className=\"space-y-4\">\n {createError && (\n <div className=\"p-3 bg-status-error-bg text-status-error-text text-sm rounded-md\">\n {createError}\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.nameLabel', 'Name *')}\n </label>\n <input\n ref={nameRef}\n type=\"text\"\n placeholder={tr('vibesPage.namePlaceholder', 'My Project')}\n onChange={() =>\n fieldErrors.name && setFieldErrors((e) => ({ ...e, name: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.name ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.name && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.name}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.pathLabel', 'Path *')}\n </label>\n <input\n ref={pathRef}\n type=\"text\"\n placeholder={tr('vibesPage.pathPlaceholder', '/path/to/project')}\n onChange={() =>\n fieldErrors.path && setFieldErrors((e) => ({ ...e, path: undefined }))\n }\n className={`w-full px-3 py-2 bg-bg-sunken border rounded-md text-text-primary placeholder:text-text-placeholder ${fieldErrors.path ? 'border-status-error-border' : 'border-border-default'}`}\n />\n {fieldErrors.path && (\n <p className=\"mt-1 text-xs text-status-error-text\">{fieldErrors.path}</p>\n )}\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.agentLabel', 'Agent')}\n </label>\n <select\n ref={agentRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"\">{tr('vibesPage.selectAgent', 'Select an agent')}</option>\n {agentOptions\n .filter((opt) => opt.value !== 'all')\n .map((agent) => (\n <option key={agent.value} value={agent.value}>\n {agent.label}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibesPage.typeLabel', 'Type')}\n </label>\n <select\n ref={typeRef}\n className=\"w-full px-3 py-2 bg-bg-sunken border border-border-default rounded-md text-text-primary\"\n >\n <option value=\"PROJECT\">{tr('vibesPage.typeProject', 'Project')}</option>\n <option value=\"REPOSITORY\">\n {tr('vibesPage.typeRepository', 'Repository')}\n </option>\n <option value=\"WORKSPACE\">\n {tr('vibesPage.typeWorkspace', 'Workspace')}\n </option>\n <option value=\"MONOREPO\">{tr('vibesPage.typeMonorepo', 'Monorepo')}</option>\n <option value=\"PACKAGE\">{tr('vibesPage.typePackage', 'Package')}</option>\n <option value=\"CUSTOM\">{tr('vibesPage.typeCustom', 'Custom')}</option>\n </select>\n </div>\n </div>\n <div className=\"mt-4 border-t border-border-default pt-4\">\n <h3 className=\"text-sm font-medium text-text-secondary mb-2\">\n {tr('vibesPage.tags', 'Tags')}\n </h3>\n <EntityTagPicker entityType=\"VIBECONTROLS_VIBE\" onDraftChange={setDraftTagIds} />\n </div>\n <div className=\"flex flex-col sm:flex-row justify-end gap-3 mt-6\">\n <button\n onClick={() => setShowCreateForm(false)}\n disabled={createLoading}\n className=\"px-4 py-2 text-text-primary hover:bg-bg-sunken rounded-md transition-colors disabled:opacity-50\"\n >\n {tr('vibesPage.cancel', 'Cancel')}\n </button>\n <button\n onClick={handleSubmitCreateVibe}\n disabled={createLoading}\n className=\"px-4 py-2 bg-action-primary-bg text-action-primary-text rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {createLoading\n ? tr('vibesPage.creating', 'Creating...')\n : tr('vibesPage.createVibe', 'Create Vibe')}\n </button>\n </div>\n </div>\n </div>\n )}\n\n <QuotaExhaustedDialog\n isOpen={showQuotaExhausted}\n onClose={() => setShowQuotaExhausted(false)}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate('/billing');\n }}\n title=\"Vibe Quota Reached\"\n message=\"You've used all available vibe creation quota on your current plan. Upgrade your plan to create more vibes.\"\n />\n </>\n )}\n </MotionPage>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsDA,SAAgB,IAAY;CAC1B,IAAM,EAAE,sBAAmB,IAAiB,EACtC,IAAW,IAAiB,EAC5B,EAAE,kBAAe,WAAW,GAAoB,mBAAgB,IAAgB,EAChF,EAAE,SAAM,IAAS,EAEjB,IAAK,GACR,GAAa,GAAkB,MAAqD;EACnF,IAAM,IAAa,EAAE,GAAK,EAAO,EAC3B,IAAW,MAAe,IAAM,IAAW;AAIjD,SAFK,IAEE,OAAO,QAAQ,EAAO,CAAC,QAC3B,GAAK,CAAC,GAAU,OAAgB,EAAI,QAAQ,KAAK,EAAS,KAAK,OAAO,EAAW,CAAC,EACnF,EACD,GALmB;IAOtB,CAAC,EAAE,CACJ,EAEK,KAAgB;EACpB;GAAE,OAAO;GAAO,OAAO,EAAG,uBAAuB,eAAe;GAAE;EAClE;GAAE,OAAO;GAAU,OAAO,EAAG,0BAA0B,SAAS;GAAE;EAClE;GAAE,OAAO;GAAY,OAAO,EAAG,4BAA4B,WAAW;GAAE;EACxE;GAAE,OAAO;GAAY,OAAO,EAAG,4BAA4B,WAAW;GAAE;EACxE;GAAE,OAAO;GAAS,OAAO,EAAG,yBAAyB,QAAQ;GAAE;EAChE;AAGwB,GAAG,qBAAqB,YAAY,EAChC,EAAG,yBAAyB,UAAU,EACnC,EAAG,4BAA4B,aAAa,EAC7C,EAAG,2BAA2B,YAAY,EAC3C,EAAG,0BAA0B,WAAW,EACzC,EAAG,yBAAyB,UAAU,EACvC,EAAG,wBAAwB,SAAS;CAIhE,IAAM,IAAe,EAAc,EAAiB,UAAU,EACxD,IAAgB,EAAc,EAAiB,YAAY,EAI3D,IAAoB,KAAsB,CAAC,GAE3C,CAAC,GAAa,MAAkB,EAAS,GAAG,EAC5C,CAAC,GAAc,MAAmB,EAAS,MAAM,EACjD,CAAC,GAAY,MAAiB,EAAS,MAAM,EAC7C,CAAC,GAAa,MAAkB,EAAS,MAAM,EAC/C,CAAC,GAAU,MAAe,EAAmB,OAAO,EACpD,CAAC,IAAgB,KAAqB,EAAS,GAAM,EACrD,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAC7D,CAAC,GAAe,MAAoB,EAAS,GAAM,EACnD,CAAC,IAAgB,KAAqB,EAAwB,KAAK,EACnE,CAAC,IAAoB,KAAyB,EAAS,GAAM,EAC7D,CAAC,GAAa,KAAkB,EAA2C,EAAE,CAAC,EAC9E,CAAC,IAAW,KAAgB,EAAwB,KAAK,EACzD,CAAC,GAAiB,KAAsB,kBAAsB,IAAI,KAAK,CAAC,EACxE,CAAC,IAAY,KAAiB,EAAwC,KAAK,EAC3E,IAAiB,EAA6C,KAAK;AAEzE,gBACe;AACX,EAAI,EAAe,WAAS,aAAa,EAAe,QAAQ;IAEjE,EAAE,CAAC;CAEN,IAAM,KAAiB,QAAkB;AAEvC,EADA,EAAa,KAAK,EAClB,AAEE,EAAe,aADf,aAAa,EAAe,QAAQ,EACX;IAE1B,EAAE,CAAC,EAGA,KAAU,EAAyB,KAAK,EACxC,KAAU,EAAyB,KAAK,EACxC,KAAW,EAA0B,KAAK,EAC1C,KAAU,EAA0B,KAAK,EAEzC,CAAC,MAAc,GAAuB,EACtC,EAAE,oBAAgB,kBAAc,GAAmB,oBAAoB,EACvE,CAAC,MAAoB,GAA6B,EAClD,CAAC,MAAe,GAAwB,EA+CxC,EAAE,UAAM,YAAS,WAAO,eAAY,EAAkB;EAC1D,WAvCyB,SAClB,EACL,QAAQ;GACN,QACE,MAAiB,QAEb,KAAA,IADC;GAEP,MACE,MAAe,QAQX,KAAA,IAPC;GAUP,kBAAkB,MAAgB,QAAwB,KAAA,IAAhB,CAAC,EAAY;GACxD,EACF,GACD;GAAC;GAAc;GAAY;GAAY,CACxC;EAiBC,aAXoB,MAAiB,SAAS,MAAe,SAAS,MAAgB,QAC/C,gBAAgB;EAWvD,iBAAiB;EACjB,MAAM;EACN,6BAA6B;EAC9B,CAAC,EAMI,EAAE,QAAQ,OAA0B,IAAsB,EAC1D,CAAC,IAAa,EAAE,MAAM,QAAgB,EAAuB,EACjE,aAAa,eACd,CAAC,EACI,KAA0B,EAAO,GAAM,EACvC,IAA4B,QAAkB;AAC9C,KAAwB,WAAW,MACvC,GAAwB,UAAU,IAC7B,GAAY,EAAE,WAAW,EAAE,YAAY,EAAE,OAAO,KAAK,EAAE,EAAE,CAAC;IAC9D,CAAC,IAAa,EAAkB,CAAC,EAK9B,KAAe,QAAc;EACjC,IAAM,IAAO,IAAY,sBAAsB,MACzC,IAA8C,IAChD,EAAK,KAAK,OAAc;GAAE,IAAI,EAAE;GAAI,MAAM,EAAE;GAAM,EAAE,GACpD,GAAsB,KAAK,OAAO;GAAE,IAAI,EAAE;GAAI,MAAM,EAAE;GAAM,EAAE;AAClE,SAAO,CACL;GAAE,OAAO;GAAO,OAAO,EAAG,uBAAuB,aAAa;GAAE,EAChE,GAAG,EAAO,KAAK,OAAW;GACxB,OAAO,EAAM;GACb,OAAO,EAAM;GACd,EAAE,CACJ;IACA;EAAC,IAAY;EAAoB;EAAuB;EAAG,CAAC,EAGzD,IAAgB,SACH,IAAM,OAAO,SAAS,EAAE,EACzB,KAAK,OAAgD;EACnE,IAAI,EAAK;EACT,aAAa,EAAK;EAClB,MAAM,EAAK;EACX,aAAa,EAAK,eAAe;EACjC,MAAM,EAAK;EACX,QAAQ,EAAK;EACb,MAAM,EAAK,QAAQ;EACnB,UAAU,EAAK,YAAY;EAC3B,WAAW,EAAK,aAAa;EAC7B,WAAW,EAAK,aAAa;EAC7B,WAAW,EAAK,YACZ,OAAO,EAAK,aAAc,WACxB,EAAK,YACL,OAAO,EAAK,aAAc,YACxB,EAAK,cAAc,QACnB,YAAY,EAAK,YACjB,OAAQ,EAAK,UAAsC,OAAO,GAC1D,KAAK,UAAU,EAAK,UAAU,GAClC;EACJ,sBAAuB,EAAK,wBAAmD,EAAE;EACjF,MAAM,EAAK,QAAQ,EAAE;EACrB,UAAW,EAAK,YAAwC,EAAE;EAC1D,WAAW,EAAK;EAChB,WAAW,EAAK;EAChB,SAAS,EAAK,UAAU,EAAE,EAAE,KAAK,OAAgD;GAC/E,IAAI,EAAE;GACN,MAAM,EAAE;GACR,UAAU,EAAE;GACZ,aAAa,EAAE,eAAe;GAC/B,EAAE;EACJ,EAAE,EACF,CAAC,IAAM,OAAO,MAAM,CAAC,EAGlB,IAAgB,QACb,EAAM,QAAQ,MAAS;AAE5B,MAAI,GAAa;GACf,IAAM,IAAQ,EAAY,aAAa;AAMvC,OAAI,EAJF,EAAK,KAAK,aAAa,CAAC,SAAS,EAAM,IACvC,EAAK,aAAa,aAAa,CAAC,SAAS,EAAM,IAC/C,EAAK,MAAM,aAAa,CAAC,SAAS,EAAM,IACxC,EAAK,MAAM,MAAM,MAAQ,EAAI,aAAa,CAAC,SAAS,EAAM,CAAC,EACzC,QAAO;;AAG7B,SAAO;GACP,EACD,CAAC,GAAO,EAAY,CAAC,EAElB,KAAgB,QACd,EAAM,QAAQ,MAAS,EAAgB,IAAI,EAAK,GAAG,CAAC,EAC1D,CAAC,GAAO,EAAgB,CACzB,EACK,IAAmB,GAAc,QAAQ,MAAS,EAAK,WAAW,SAAS,EAC3E,IAAkB,GAAc,QAAQ,MAAS,EAAK,WAAW,WAAW,EAC5E,IACJ,EAAc,SAAS,KAAK,EAAc,OAAO,MAAS,EAAgB,IAAI,EAAK,GAAG,CAAC,EACnF,KACJ,EAAc,MAAM,MAAS,EAAgB,IAAI,EAAK,GAAG,CAAC,IAAI,CAAC,GAE3D,MAAoB,MAAe;AACvC,IAAS,UAAU,EAAK,KAAK;IAGzB,KAAqB,OAAO,MAAe;AAC/C,MAAI;AAIF,IAHe,MAAM,GAAiB,EACpC,WAAW;IAAE,IAAI,EAAK;IAAI,QAAQ;IAAmB,EACtD,CAAC,EACS,MAAM,oBACf,IAAiB;IAAE,IAAI,EAAK;IAAI,MAAM,EAAK;IAAM,MAAM,EAAK;IAAM,QAAQ;IAAU,CAAC;WAEhF,GAAK;AACZ,WAAQ,MAAM,4BAA4B,EAAI;;IAI5C,KAAoB,OAAO,MAAe;AAC9C,MAAI;AACF,SAAM,GAAY,EAAE,WAAW,EAAE,IAAI,EAAK,IAAI,EAAE,CAAC;WAC1C,GAAK;AACZ,WAAQ,MAAM,2BAA2B,EAAI;;IAQ3C,KAAsB,IAAsB,MAAM,EAAE,oBAAoB,EACxE,KAAmB,IAAsB,MAAM,EAAE,iBAAiB,EAElE,MAAsB,MAAe;AACzC,MAAI,CAAC,EAAK,MAAM;AAEd,GADA,EAAa,EAAG,mCAAmC,sCAAsC,CAAC,EAC1F,EAAe,UAAU,iBAAiB;AAExC,IADA,EAAa,KAAK,EAClB,EAAe,UAAU;MACxB,IAAK;AACR;;EAEF,IAAM,IAAM,GAA2B,EAAK,IAAI,IAAqB,GAAiB,EAChF,IAAS,GAAa,EAAI,EAC1B,IAAM,IAAS,GAAiB,GAAQ,EAAK,KAAK,GAAG,iBAAiB,EAAK;AACjF,EAAI,IACF,OAAO,SAAS,OAAO,KAEvB,EACE,EACE,kCACA,GAAG,GAAQ,SAAS,EAAI,+DACzB,CACF,EACD,EAAe,UAAU,iBAAiB;AAExC,GADA,EAAa,KAAK,EAClB,EAAe,UAAU;KACxB,IAAK;IAIN,WAAyB;AAK7B,EAJA,EAAe,KAAK,EACpB,EAAkB,GAAK,EAGvB,GAA2B;IAGvB,MAAuB,GAAY,MAAqB;AAC5D,KAAoB,MAAS;GAC3B,IAAM,IAAO,IAAI,IAAI,EAAK;AAM1B,UALI,IACF,EAAK,IAAI,EAAK,GAAG,GAEjB,EAAK,OAAO,EAAK,GAAG,EAEf;IACP;IAGE,MAAyB,MAAqB;AAClD,KAAoB,MAAS;GAC3B,IAAM,IAAO,IAAI,IAAI,EAAK;AAQ1B,UAPA,EAAc,SAAS,MAAS;AAC9B,IAAI,IACF,EAAK,IAAI,EAAK,GAAG,GAEjB,EAAK,OAAO,EAAK,GAAG;KAEtB,EACK;IACP;IAGE,UAAuB,kBAAmB,IAAI,KAAK,CAAC,EAEpD,KAAqB,YAAY;AACrC,MAAI,EAAiB,WAAW,EAAG;EAEnC,IAAM,IAAU,MAAM,QAAQ,WAC5B,EAAiB,KAAK,MACpB,GAAiB,EAAE,WAAW;GAAE,IAAI,EAAK;GAAI,QAAQ;GAAmB,EAAE,CAAC,CAC5E,CACF,EAEK,IAAY,EAAQ,QAAQ,MAAW,EAAO,WAAW,YAAY,CAAC,QACtE,IAAS,EAAQ,SAAS;AAQhC,EANA,EACE,aAAa,EAAU,OAAO,MAAc,IAAI,KAAK,MAAM,IAAS,IAAI,MAAM,EAAO,WAAW,KACjG,EACD,iBAAiB,EAAkB,KAAK,EAAE,IAAK,EAC/C,GAAgB,EAChB,EAAc,KAAK,EACnB,MAAM,GAAS;IAGX,KAAoB,YAAY;AACpC,MAAI,EAAgB,WAAW,EAAG;EAElC,IAAM,IAAU,MAAM,QAAQ,WAC5B,EAAgB,KAAK,MAAS,GAAY,EAAE,WAAW,EAAE,IAAI,EAAK,IAAI,EAAE,CAAC,CAAC,CAC3E,EAEK,IAAY,EAAQ,QAAQ,MAAW,EAAO,WAAW,YAAY,CAAC,QACtE,IAAS,EAAQ,SAAS;AAQhC,EANA,EACE,YAAY,EAAU,OAAO,MAAc,IAAI,KAAK,MAAM,IAAS,IAAI,MAAM,EAAO,WAAW,KAChG,EACD,iBAAiB,EAAkB,KAAK,EAAE,IAAK,EAC/C,GAAgB,EAChB,EAAc,KAAK,EACnB,MAAM,GAAS;IAGX,KAAyB,YAAY;EACzC,IAAM,IAAO,GAAQ,SAAS,OAAO,MAAM,EACrC,IAAO,GAAQ,SAAS,OAAO,MAAM,EACrC,IAAU,GAAS,SAAS,OAC5B,IAAO,GAAQ,SAAS,OAExB,IAA2C,EAAE;AAGnD,MAFK,MAAM,EAAO,OAAO,EAAG,0BAA0B,mBAAmB,GACpE,MAAM,EAAO,OAAO,EAAG,0BAA0B,mBAAmB,GACrE,EAAO,QAAQ,EAAO,MAAM;AAE9B,GADA,EAAe,EAAO,EACtB,EAAe,EAAG,0BAA0B,qCAAqC,CAAC;AAClF;;AAEF,IAAe,EAAE,CAAC;EAWlB,IAAM,KARa;GACjB;GACA;GACA;GACA;GACA;GACA;GACD,CAC+B,SAAS,EAAoC,GACxE,IACD;AAGJ,EADA,GAAiB,GAAK,EACtB,EAAe,KAAK;AACpB,MAAI;GACF,IAAM,IAAS,MAAM,GAAW,EAC9B,WAAW,EACT,OAAO;IACL;IACA;IACA,MAAM;IACN,SAAS,KAAW,KAAA;IACrB,EACF,EACF,CAAC;AACF,OAAI,CAAC,EAAO,MAAM,YAAY,GAC5B,OAAU,MAAM,EAAG,0BAA0B,wBAAwB,CAAC;GAExE,IAAM,IAAY,EAAO,KAAK,WAAW;AAKzC,GAJA,EAAkB,GAAM,EACxB,EAAkB,EAAG,4BAA4B,4BAA4B,CAAC,EAC9E,MAAM,GAAU,EAAU,EAC1B,MAAM,GAAS,EACf,iBAAiB,EAAkB,KAAK,EAAE,IAAK;WACxC,GAAK;AACZ,OAAI,GAAsB,EAAI,EAAE;AAI9B,IAHA,EAAkB,GAAM,EACxB,EAAe,KAAK,EACpB,EAAe,EAAE,CAAC,EAClB,EAAsB,GAAK;AAC3B;;AAGF,KACE,aAAe,QAAQ,EAAI,UAAU,EAAG,0BAA0B,wBAAwB,CAC3F;YACO;AACR,MAAiB,GAAM;;IAKrB,KAAQ;EACZ,OAAO,EAAM;EACb,QAAQ,EAAM,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC;EACnD,UAAU,EAAM,QAAQ,MAAM,EAAE,WAAW,WAAW,CAAC;EACxD,EAKK,IAAyB,GACzB,IACJ,CAAC,KACD,CAAC,CAAC,MACD,EAAY,mBAAmB,UAAU,KAAK,KAC/C,CAAC,GACG,KACJ,CAAC,KAA0B,CAAC,KAAoB,KAAW,EAAM,WAAW,GACxE,KACJ,CAAC,KAA0B,CAAC,KAAoB,CAAC,CAAC,MAAS,EAAM,WAAW,GACxE,KAAgB,CAAC,KAA0B,CAAC,KAAoB,CAAC;AAEvE,QACE,kBAAC,IAAD;EAAY,WAAU;YAAtB;GACG,KACC,kBAAC,IAAD,EAAc,SAAS,EAAG,gCAAgC,yBAAyB,EAAI,CAAA;GAExF,KACC,kBAAC,IAAD;IACE,OAAO,EAAG,+BAA+B,gBAAgB;IACzD,aAAa,EACX,qCACA,4FACD;IACD,CAAA;GAEH,MACC,kBAAC,IAAD;IACE,OAAO,EAAG,wBAAwB,uBAAuB;IACzD,SAAS,IAAO,WAAW;IAC3B,eAAe,GAAS;IACxB,CAAA;GAEH,MACC,kBAAA,IAAA,EAAA,UAAA;IACE,kBAAC,IAAD;KACE,OAAO,EAAG,mBAAmB,QAAQ;KACrC,aAAa,EAAG,+BAA+B,wCAAwC;MACrF,QAAQ,GAAM;MACd,OAAO,GAAM;MACd,CAAC;KACF,SACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,UAAD;OACE,eAAe,GAAS;OACxB,UAAU;OACV,WAAU;OACV,OAAO,EAAG,0BAA0B,gBAAgB;iBAEpD,kBAAC,IAAD,EAAW,WAAW,WAAW,IAAU,iBAAiB,MAAQ,CAAA;OAC7D,CAAA,EACT,kBAAC,IAAD;OACE,YAAY,EAAiB;OAC7B,gBAAe;OACf,eAAe,EACb,iCACA,4CACD;iBAED,kBAAC,UAAD;QACE,SAAS;QACT,WAAU;kBAFZ,CAIE,kBAAC,IAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAG,qBAAqB,WAAW,CAC7B;;OACQ,CAAA,CACf;;KAER,CAAA;IAGF,kBAAC,OAAD;KAAK,WAAU;eAAf,CAEE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD;OACE,OAAO;OACP,UAAU;OACV,aAAa,EAAG,+BAA+B,kBAAkB;OACjE,WAAU;OACV,CAAA,EAEF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,UAAD;QACE,eAAe,GAAY,OAAO;QAClC,WAAW,wBAAwB,MAAa,SAAS,4BAA4B;QACrF,OAAO,EAAG,2BAA2B,YAAY;kBAEjD,kBAAC,IAAD,EAAM,WAAU,+BAAgC,CAAA;QACzC,CAAA,EACT,kBAAC,UAAD;QACE,eAAe,GAAY,OAAO;QAClC,WAAW,wBAAwB,MAAa,SAAS,4BAA4B;QACrF,OAAO,EAAG,2BAA2B,YAAY;kBAEjD,kBAAC,IAAD,EAAM,WAAU,+BAAgC,CAAA;QACzC,CAAA,CACL;SACF;SAGN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,OAAO,EAAG,0BAA0B,SAAS;OAC7C,OAAO;OACP,SAAS;OACT,UAAU;OACV,CAAA,EACF,kBAAC,OAAD;OAAK,aAAa;OAA2B,SAAS;iBACpD,kBAAC,GAAD;QACE,OAAO,EAAG,yBAAyB,QAAQ;QAC3C,OAAO;QACP,SAAS;QACT,UAAU;QACV,CAAA;OACE,CAAA,CACF;QACF;;IAGL,KACD,MAAiB,SACjB,MAAe,SACf,MAAgB,QACd,kBAAC,KAAD;KAAG,WAAU;eAAb,CACG,EAAG,4BAA4B,2CAA2C;MACzE,UAAU,EAAc;MACxB,OAAO,EAAM;MACd,CAAC,EACD,KACC,kBAAC,QAAD;MAAM,WAAU;gBACb,EAAG,wBAAwB,kBAAkB;MACzC,CAAA,CAEP;SACF;IAEH,EAAc,SAAS,KACtB,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,IAAD;SACE,SAAS;SACT,eAAe;SACf,UAAU;SACV,WAAU;SACV,CAAA,EACF,kBAAC,QAAD;SAAM,WAAU;mBAAwC;SAEjD,CAAA,CACH;WACL,EAAgB,OAAO,KACtB,kBAAC,UAAD;QACE,SAAS;QACT,WAAU;kBACX;QAEQ,CAAA,CAEP;UAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,EAAgB,MAAK,YACjB;;QACP,kBAAC,UAAD;SACE,eAAe,EAAc,WAAW;SACxC,UAAU,EAAiB,WAAW;SACtC,OAAM;SACN,WAAU;mBAEV,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,IAAD,EAAM,WAAU,WAAY,CAAA;;WAE3B,EAAiB,SAAS,IAAI,KAAK,EAAiB,OAAO,KAAK;WAC5D;;SACA,CAAA;QACT,kBAAC,UAAD;SACE,eAAe,EAAc,UAAU;SACvC,UAAU,EAAgB,WAAW;SACrC,WAAU;mBAEV,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,IAAD,EAAS,WAAU,WAAY,CAAA;;WAE9B,EAAgB,SAAS,IAAI,KAAK,EAAgB,OAAO,KAAK;WAC1D;;SACA,CAAA;QACL;SACF;;KACF,CAAA;IAIP,KACC,kBAAC,IAAD;KAAc,SAAQ;KAAQ,OAAO;KAAK,CAAA,GACxC,EAAc,SAAS,IACzB,kBAAC,IAAD;KAAiB,MAAK;eACnB,MAAa,SACZ,kBAAC,GAAD;MAEE,SAAS;MACT,WAAU;gBAET,EAAc,KAAK,MAClB,kBAAC,IAAD,EAAA,UACE,kBAAC,IAAD;OACQ;OACN,QAAQ;OACR,YAAY,IAAgB,KAAqB,KAAA;OACjD,WAAW,IAAgB,KAAoB,KAAA;OAC/C,gBAAgB;OAChB,UAAU,EAAgB,IAAI,EAAK,GAAG;OACtC,iBAAiB,MAAY,GAAoB,GAAM,EAAQ;OAC/D,CAAA,EACS,EAVI,EAAK,GAUT,CACb;MACS,EAjBP,OAiBO,GAEb,kBAAC,GAAD,EAAA,UACE,kBAAC,IAAD;MACE,OAAO;MACP,QAAQ;MACR,YAAY;MACZ,WAAW;MACX,gBAAgB;MACC;MACjB,gBAAgB;MAChB,oBAAoB;MACA;MACD;MACnB,CAAA,EACS,EAbG,OAaH;KAEC,CAAA,GAElB,kBAAC,IAAD;KACE,MAAM,kBAAC,IAAD,EAAK,WAAU,+BAAgC,CAAA;KACrD,OACE,KACA,MAAiB,SACjB,MAAe,SACf,MAAgB,QACZ,EAAG,+BAA+B,iBAAiB,GACnD,EAAG,oCAAoC,sBAAsB;KAEnE,aACE,KACA,MAAiB,SACjB,MAAe,SACf,MAAgB,QACZ,EAAG,+BAA+B,uCAAuC,GACzE,EACE,qCACA,kEACD;KAEP,QACE,CAAC,KACD,MAAiB,SACjB,MAAe,SACf,MAAgB,QACd,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,IAAD;OAAgB,YAAY,EAAiB;iBAC3C,kBAAC,UAAD;QACE,SAAS;QACT,WAAU;kBAFZ,CAIE,kBAAC,IAAD,EAAM,WAAU,WAAY,CAAA,EAC3B,EAAG,qBAAqB,WAAW,CAC7B;;OACM,CAAA,EACjB,kBAAC,IAAD,EAAe,QAAO,2DAA4D,CAAA,CAC9E;UACJ,KAAA;KAEN,CAAA;IAIH,MACC,kBAAC,OAAD;KAAK,WAAU;eACZ;KACG,CAAA;IAGR,kBAAC,GAAD;KACE,QAAQ,OAAe;KACvB,eAAe,EAAc,KAAK;KAClC,WAAW;KACX,OAAM;KACN,SAAS,YAAY,EAAiB,OAAO,gBAAgB,EAAiB,WAAW,IAAI,KAAK,IAAI;KACtG,cAAa;KACb,SAAQ;KACR,CAAA;IAEF,kBAAC,GAAD;KACE,QAAQ,OAAe;KACvB,eAAe,EAAc,KAAK;KAClC,WAAW;KACX,OAAM;KACN,SAAS,WAAW,EAAgB,OAAO,gBAAgB,EAAgB,WAAW,IAAI,KAAK,IAAI;KACnG,cAAa;KACb,SAAQ;KACR,CAAA;IAGD,MACC,kBAAC,OAAD;KACE,MAAK;KACL,WAAU;eAFZ,CAIG,IACD,kBAAC,UAAD;MACE,SAAS;MACT,WAAU;gBAET,EAAG,qBAAqB,UAAU;MAC5B,CAAA,CACL;;IAIP,MACC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAG,wBAAwB,eAAe;QACxC,CAAA;OACL,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACG,KACC,kBAAC,OAAD;UAAK,WAAU;oBACZ;UACG,CAAA;SAER,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,SAAD;WAAO,WAAU;qBACd,EAAG,uBAAuB,SAAS;WAC9B,CAAA;UACR,kBAAC,SAAD;WACE,KAAK;WACL,MAAK;WACL,aAAa,EAAG,6BAA6B,aAAa;WAC1D,gBACE,EAAY,QAAQ,GAAgB,OAAO;YAAE,GAAG;YAAG,MAAM,KAAA;YAAW,EAAE;WAExE,WAAW,uGAAuG,EAAY,OAAO,+BAA+B;WACpK,CAAA;UACD,EAAY,QACX,kBAAC,KAAD;WAAG,WAAU;qBAAuC,EAAY;WAAS,CAAA;UAEvE,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA;UACE,kBAAC,SAAD;WAAO,WAAU;qBACd,EAAG,uBAAuB,SAAS;WAC9B,CAAA;UACR,kBAAC,SAAD;WACE,KAAK;WACL,MAAK;WACL,aAAa,EAAG,6BAA6B,mBAAmB;WAChE,gBACE,EAAY,QAAQ,GAAgB,OAAO;YAAE,GAAG;YAAG,MAAM,KAAA;YAAW,EAAE;WAExE,WAAW,uGAAuG,EAAY,OAAO,+BAA+B;WACpK,CAAA;UACD,EAAY,QACX,kBAAC,KAAD;WAAG,WAAU;qBAAuC,EAAY;WAAS,CAAA;UAEvE,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UAAO,WAAU;oBACd,EAAG,wBAAwB,QAAQ;UAC9B,CAAA,EACR,kBAAC,UAAD;UACE,KAAK;UACL,WAAU;oBAFZ,CAIE,kBAAC,UAAD;WAAQ,OAAM;qBAAI,EAAG,yBAAyB,kBAAkB;WAAU,CAAA,EACzE,GACE,QAAQ,MAAQ,EAAI,UAAU,MAAM,CACpC,KAAK,MACJ,kBAAC,UAAD;WAA0B,OAAO,EAAM;qBACpC,EAAM;WACA,EAFI,EAAM,MAEV,CACT,CACG;YACL,EAAA,CAAA;SACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;UAAO,WAAU;oBACd,EAAG,uBAAuB,OAAO;UAC5B,CAAA,EACR,kBAAC,UAAD;UACE,KAAK;UACL,WAAU;oBAFZ;WAIE,kBAAC,UAAD;YAAQ,OAAM;sBAAW,EAAG,yBAAyB,UAAU;YAAU,CAAA;WACzE,kBAAC,UAAD;YAAQ,OAAM;sBACX,EAAG,4BAA4B,aAAa;YACtC,CAAA;WACT,kBAAC,UAAD;YAAQ,OAAM;sBACX,EAAG,2BAA2B,YAAY;YACpC,CAAA;WACT,kBAAC,UAAD;YAAQ,OAAM;sBAAY,EAAG,0BAA0B,WAAW;YAAU,CAAA;WAC5E,kBAAC,UAAD;YAAQ,OAAM;sBAAW,EAAG,yBAAyB,UAAU;YAAU,CAAA;WACzE,kBAAC,UAAD;YAAQ,OAAM;sBAAU,EAAG,wBAAwB,SAAS;YAAU,CAAA;WAC/D;YACL,EAAA,CAAA;SACF;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAG,kBAAkB,OAAO;SAC1B,CAAA,EACL,kBAAC,IAAD;SAAiB,YAAW;SAAoB,eAAe;SAAkB,CAAA,CAC7E;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,UAAD;SACE,eAAe,EAAkB,GAAM;SACvC,UAAU;SACV,WAAU;mBAET,EAAG,oBAAoB,SAAS;SAC1B,CAAA,EACT,kBAAC,UAAD;SACE,SAAS;SACT,UAAU;SACV,WAAU;mBAET,IACG,EAAG,sBAAsB,cAAc,GACvC,EAAG,wBAAwB,cAAc;SACtC,CAAA,CACL;;OACF;;KACF,CAAA;IAGR,kBAAC,IAAD;KACE,QAAQ;KACR,eAAe,EAAsB,GAAM;KAC3C,iBAAiB;AAEf,MADA,EAAsB,GAAM,EAC5B,EAAS,WAAW;;KAEtB,OAAM;KACN,SAAQ;KACR,CAAA;IACD,EAAA,CAAA;GAEM"}
@@ -7,7 +7,7 @@ function i() {
7
7
  return "/workspaces/graphql";
8
8
  }
9
9
  function a(e) {
10
- return e.includes("Cloudflare Tunnel error") || e.includes("cloudflare.com/5xx") ? "Agent tunnel is unreachable. The Cloudflare tunnel may have disconnected. Check if the agent is running and the tunnel is active." : e.includes("<!DOCTYPE") || e.includes("<html") ? "Unexpected HTML response. The agent may be unreachable or the tunnel may have changed." : e.includes("ECONNREFUSED") || e.includes("ETIMEDOUT") || e.includes("fetch failed") ? "Cannot connect to the agent. Check if it is running and accessible." : e.includes("Could not resolve authentication method") || e.includes("ANTHROPIC_API_KEY") || e.includes("ANTHROPIC_AUTH_TOKEN") ? "Claude SDK auth is not configured on the agent. Open the AI Config tab and save either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN." : e.includes("NOT_FOUND") || e.includes("502") || e.includes("503") ? "Agent service unavailable. The agent may be restarting or the service endpoint was not found." : e.length > 200 ? e.slice(0, 200) + "..." : e;
10
+ return e.includes("Cloudflare Tunnel error") || e.includes("cloudflare.com/5xx") ? "Agent tunnel is unreachable. The Cloudflare tunnel may have disconnected. Check if the agent is running and the tunnel is active." : e.includes("<!DOCTYPE") || e.includes("<html") ? "Unexpected HTML response. The agent may be unreachable or the tunnel may have changed." : e.includes("ECONNREFUSED") || e.includes("ETIMEDOUT") || e.includes("fetch failed") ? "Cannot connect to the agent. Check if it is running and accessible." : e.includes("Could not resolve authentication method") || e.includes("ANTHROPIC_API_KEY") || e.includes("ANTHROPIC_AUTH_TOKEN") ? "Claude SDK auth is not configured on the agent. Open the AI Config tab and save either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN." : e.includes("NOT_FOUND") || e.includes(" 404") ? "This feature is not installed on the agent. Install the matching vibe plugin from the agent CLI (e.g. `vibe plugin install <name>`), then refresh." : e.includes("502") || e.includes("503") ? "Agent service unavailable. The agent may be restarting — try again in a few seconds." : e.length > 200 ? e.slice(0, 200) + "..." : e;
11
11
  }
12
12
  function o(e, t) {
13
13
  let n = t.startsWith("/") ? t : `/${t}`;
@@ -1 +1 @@
1
- {"version":3,"file":"aiApi.js","names":[],"sources":["../../src/services/aiApi.ts"],"sourcesContent":["/**\n * AI API service layer\n *\n * Wraps workspace-owned AI prompt APIs and vibecontrols-agent AI REST API\n * endpoints exposed through wspace-vibecontrols-svc.\n *\n * Backend route prefix: /api/ai (mounted by vibe-plugin-ai v4.0.0)\n */\n\nimport {\n getStoredAuthToken,\n getStoredWorkspaceId,\n getStoredWorkspaceToken,\n} from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n AgentManagerMessage,\n FileAttachment,\n McpServer,\n ProviderMode,\n} from '@/types/agentManager';\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\n// Reads from ActiveContextStorage (URL → storage fallback chain). Previously\n// parsed only `?workspace=` from window.location.search, which is empty when\n// the user navigates between MFE routes that don't carry the param. Result:\n// gateway received no `x-workspace-id`, backend `agentAiGet` resolver threw\n// `WorkspaceRequiredError` and the chat pane surfaced \"Unexpected error\".\nfunction getWorkspaceId(): string {\n return getStoredWorkspaceId() ?? '';\n}\n\nfunction getGatewayEndpoint(): string {\n if (typeof window === 'undefined') return '/workspaces/graphql';\n return '/workspaces/graphql';\n}\n\n/**\n * Sanitize error messages — strip HTML, Cloudflare error pages, and long technical details.\n * Returns a clean, user-friendly error string.\n */\nfunction sanitizeError(raw: string): string {\n // Detect Cloudflare tunnel errors (HTML error pages)\n if (raw.includes('Cloudflare Tunnel error') || raw.includes('cloudflare.com/5xx')) {\n return 'Agent tunnel is unreachable. The Cloudflare tunnel may have disconnected. Check if the agent is running and the tunnel is active.';\n }\n // Detect HTML responses\n if (raw.includes('<!DOCTYPE') || raw.includes('<html')) {\n return 'Unexpected HTML response. The agent may be unreachable or the tunnel may have changed.';\n }\n // Detect common network errors\n if (raw.includes('ECONNREFUSED') || raw.includes('ETIMEDOUT') || raw.includes('fetch failed')) {\n return 'Cannot connect to the agent. Check if it is running and accessible.';\n }\n if (\n raw.includes('Could not resolve authentication method') ||\n raw.includes('ANTHROPIC_API_KEY') ||\n raw.includes('ANTHROPIC_AUTH_TOKEN')\n ) {\n return 'Claude SDK auth is not configured on the agent. Open the AI Config tab and save either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN.';\n }\n // Detect proxy errors\n if (raw.includes('NOT_FOUND') || raw.includes('502') || raw.includes('503')) {\n return 'Agent service unavailable. The agent may be restarting or the service endpoint was not found.';\n }\n // Truncate very long errors\n if (raw.length > 200) {\n return raw.slice(0, 200) + '...';\n }\n return raw;\n}\n\n/**\n * Identifier for an agent + its profile name. Every aiApi function takes\n * one of these so the canonical path `/api/profiles/<profile>/<rest>` can\n * be constructed at every callsite. Callers should pass\n * `{ id: agent.id, profile: agent.metadata?.profile ?? 'default' }`.\n */\nexport interface AgentRef {\n id: string;\n profile: string;\n}\n\n/**\n * Build the canonical agent-API path for a given profile + path segment.\n * The svc-side proxy validates that paths are exactly of the form\n * `/api/profiles/<name>/<rest>` (or one of the bare carve-outs), so every\n * callsite builds the path explicitly via this helper instead of the\n * proxy doing any rewriting.\n *\n * Example: `agentApiPath('default', '/ai/sessions')` →\n * `/api/profiles/default/ai/sessions`.\n */\nexport function agentApiPath(profile: string, segment: string): string {\n const seg = segment.startsWith('/') ? segment : `/${segment}`;\n return `/api/profiles/${encodeURIComponent(profile)}${seg}`;\n}\n\nasync function gatewayFetch<T = unknown>(\n query: string,\n variables: Record<string, unknown>\n): Promise<T> {\n const res = await fetch(getGatewayEndpoint(), {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(getStoredAuthToken() ? { Authorization: `Bearer ${getStoredAuthToken()}` } : {}),\n ...(getStoredWorkspaceToken()\n ? { 'X-Workspace-Authorization': `Bearer ${getStoredWorkspaceToken()}` }\n : {}),\n ...(getWorkspaceId() ? { 'x-workspace-id': getWorkspaceId() } : {}),\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeError(`Gateway error ${res.status}: ${text || res.statusText}`));\n }\n\n const json = (await res.json()) as { data?: T; errors?: Array<{ message?: string }> };\n if (json.errors?.length) {\n const msg = json.errors[0]?.message || 'GraphQL request failed';\n throw new Error(sanitizeError(msg));\n }\n if (!json.data) throw new Error('GraphQL request returned no data');\n return json.data;\n}\n\nexport async function agentAiGet<T = unknown>(\n agentId: string,\n path: string,\n query?: Record<string, unknown>\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiGet: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `query AgentAiGet($agentId: ID!, $path: String!, $query: JSON) {\n agentAiGet(agentId: $agentId, path: $path, query: $query) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, query: query ?? null }\n );\n\n if (!data.agentAiGet.ok) {\n throw new Error(\n sanitizeError(data.agentAiGet.error || `Agent API error ${data.agentAiGet.status}`)\n );\n }\n return data.agentAiGet.data;\n}\n\nexport async function agentAiMutate<T = unknown>(\n agentId: string,\n path: string,\n method: string,\n body?: unknown\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiMutate: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `mutation AgentAiMutate($agentId: ID!, $path: String!, $method: String!, $body: JSON) {\n agentAiMutate(agentId: $agentId, path: $path, method: $method, body: $body) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, method, body: body ?? null }\n );\n\n if (!data.agentAiMutate.ok) {\n throw new Error(\n sanitizeError(data.agentAiMutate.error || `Agent API error ${data.agentAiMutate.status}`)\n );\n }\n return data.agentAiMutate.data;\n}\n\nexport async function getAgentConfigValue(agent: AgentRef, key: string): Promise<string | null> {\n try {\n const data = await agentAiGet<{ key?: string; value?: string }>(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`)\n );\n return typeof data.value === 'string' ? data.value : null;\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('configuration key not found')\n ) {\n return null;\n }\n throw error;\n }\n}\n\nexport async function setAgentConfigValue(\n agent: AgentRef,\n key: string,\n value: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'PUT',\n { value }\n );\n}\n\nexport async function deleteAgentConfigValue(agent: AgentRef, key: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'DELETE'\n );\n}\n\nfunction qs(params?: Record<string, unknown>): string {\n if (!params) return '';\n const entries = Object.entries(params).filter(\n ([, v]) => v !== undefined && v !== null && v !== ''\n );\n if (entries.length === 0) return '';\n const search = new URLSearchParams();\n for (const [k, v] of entries) search.set(k, String(v));\n return `?${search.toString()}`;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport interface AITemplate {\n id: string;\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n variables: Record<string, unknown>;\n tags: string[];\n isShared?: boolean;\n usageCount?: number;\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIContext {\n id: string;\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AISession {\n id: string;\n name: string;\n agentType: string;\n providerPlugin?: string;\n status: 'active' | 'idle' | 'processing' | 'error' | 'terminated';\n stats?: Record<string, unknown>;\n config?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n terminatedAt?: string | null;\n}\n\nexport interface AISessionLog {\n id: string;\n sessionId: string;\n type: 'input' | 'output' | 'thinking' | 'event' | 'error' | 'metadata';\n content: string;\n message: string;\n tokenCount?: number;\n model?: string;\n durationMs?: number;\n agentMetadata?: Record<string, unknown>;\n timestamp: string;\n createdAt: string;\n}\n\nexport interface AISessionStats {\n session: { id: string; name: string; agentType: string; status: string; createdAt: string };\n stats: {\n totalLogs: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalDurationMs: number;\n logsByType: Record<string, number>;\n };\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n promptCount: number;\n averageLatency?: number;\n}\n\nexport interface AIDispatch {\n id: string;\n templateId?: string | null;\n content: string;\n prompt: string;\n resolvedVariables?: Record<string, unknown>;\n contextIds?: string[];\n sessionId?: string | null;\n status: string;\n result?: Record<string, unknown> | null;\n scheduledAt?: string | null;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIQueueItem {\n id: string;\n dispatchedPromptId: string;\n sessionId: string;\n priority: number;\n status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';\n attempts: number;\n maxAttempts: number;\n lastError?: string | null;\n createdAt?: string;\n}\n\nexport interface AIStatsOverview {\n totalSessions: number;\n activeSessions: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n totalRequests: number;\n sessionsByAgentType: Record<string, number>;\n queuedPrompts: number;\n}\n\nexport interface AIUsageStats {\n usage: Record<\n string,\n { inputTokens: number; outputTokens: number; requestCount: number; sessionCount: number }\n >;\n byProvider: Array<{\n provider: string;\n tokens: number;\n sessions: number;\n cost?: number;\n inputTokens: number;\n outputTokens: number;\n requestCount: number;\n }>;\n byStatus: Array<{ status: string; count: number }>;\n}\n\nexport interface AIProvider {\n pluginName: string;\n name?: string;\n displayName?: string;\n isDefault: boolean;\n currentMode?: ProviderMode;\n defaultMode?: ProviderMode;\n supportedModes?: ProviderMode[];\n unsupportedModes?: ProviderMode[];\n modes?: Record<ProviderMode, boolean>;\n prereqApiPrefix?: string;\n}\n\nexport interface AIApiParameter {\n name: string;\n type: string;\n required: boolean;\n description: string;\n}\n\nexport interface AIApiEndpoint {\n id: string;\n method: string;\n path: string;\n description: string;\n parameters?: AIApiParameter[];\n body?: AIApiParameter[];\n}\n\nexport interface AITool {\n name: string;\n displayName: string;\n description: string;\n installed: boolean;\n version?: string;\n configFiles?: string[];\n foundConfigs?: string[];\n installCommand?: string;\n}\n\n// ── Paginated response wrapper ───────────────────────────────────────\n\nexport interface PaginatedResponse<T> {\n items: T[];\n total: number;\n hasMore: boolean;\n}\n\ninterface RawAITemplate extends Omit<AITemplate, 'variables' | 'tags' | 'prompt'> {\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[] | null;\n}\n\ninterface RawAISessionLog extends Omit<AISessionLog, 'message' | 'timestamp'> {\n message?: string;\n timestamp?: string;\n}\n\ninterface RawAISessionStats {\n session?: AISessionStats['session'];\n stats?: Partial<AISessionStats['stats']>;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n promptCount?: number;\n averageLatency?: number;\n}\n\ninterface RawAIDispatch extends Omit<AIDispatch, 'prompt'> {\n prompt?: string;\n}\n\ninterface RawAIStatsOverview {\n totalSessions?: number;\n activeSessions?: number;\n totalInputTokens?: number;\n totalOutputTokens?: number;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n totalRequests?: number;\n sessionsByAgentType?: Record<string, number>;\n queuedPrompts?: number;\n}\n\ninterface RawAIUsageStats {\n usage?: AIUsageStats['usage'];\n byProvider?: AIUsageStats['byProvider'];\n byStatus?: AIUsageStats['byStatus'];\n}\n\ninterface RawAIApiEndpoint {\n method: string;\n path: string;\n description: string;\n parameters?: Record<string, { type: string; required: boolean; description: string }>;\n body?: Record<string, { type: string; required: boolean; description: string }>;\n}\n\nfunction normalizeTemplate(template: RawAITemplate): AITemplate {\n const variables = Array.isArray(template.variables)\n ? Object.fromEntries(template.variables.map((name) => [String(name), '']))\n : (template.variables ?? {});\n\n return {\n ...template,\n description: template.description ?? null,\n variables,\n tags: template.tags ?? [],\n };\n}\n\nfunction normalizeSessionLog(log: RawAISessionLog): AISessionLog {\n return {\n ...log,\n message: log.message ?? log.content,\n timestamp: log.timestamp ?? log.createdAt,\n };\n}\n\nfunction normalizeSessionStats(stats: RawAISessionStats): AISessionStats {\n const statBlock = stats.stats ?? {};\n const inputTokens = stats.inputTokens ?? statBlock.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? statBlock.totalOutputTokens ?? 0;\n const promptCount = stats.promptCount ?? statBlock.totalLogs ?? 0;\n const totalDurationMs = statBlock.totalDurationMs ?? 0;\n\n return {\n session: stats.session ?? {\n id: '',\n name: '',\n agentType: '',\n status: '',\n createdAt: '',\n },\n stats: {\n totalLogs: statBlock.totalLogs ?? 0,\n totalInputTokens: statBlock.totalInputTokens ?? inputTokens,\n totalOutputTokens: statBlock.totalOutputTokens ?? outputTokens,\n totalDurationMs,\n logsByType: statBlock.logsByType ?? {},\n },\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n promptCount,\n averageLatency:\n stats.averageLatency ?? (promptCount > 0 ? totalDurationMs / promptCount : undefined),\n };\n}\n\nfunction normalizeDispatch(dispatch: RawAIDispatch): AIDispatch {\n return {\n ...dispatch,\n prompt: dispatch.prompt ?? dispatch.content,\n };\n}\n\nfunction normalizeStatsOverview(stats: RawAIStatsOverview): AIStatsOverview {\n const inputTokens = stats.inputTokens ?? stats.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? stats.totalOutputTokens ?? 0;\n\n return {\n totalSessions: stats.totalSessions ?? 0,\n activeSessions: stats.activeSessions ?? 0,\n totalInputTokens: stats.totalInputTokens ?? inputTokens,\n totalOutputTokens: stats.totalOutputTokens ?? outputTokens,\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n totalRequests: stats.totalRequests ?? 0,\n sessionsByAgentType: stats.sessionsByAgentType ?? {},\n queuedPrompts: stats.queuedPrompts ?? 0,\n };\n}\n\nfunction normalizeUsageStats(usageStats: RawAIUsageStats): AIUsageStats {\n const usage = usageStats.usage ?? {};\n const byProvider =\n usageStats.byProvider ??\n Object.entries(usage).map(([provider, stats]) => ({\n provider,\n tokens: stats.inputTokens + stats.outputTokens,\n sessions: stats.sessionCount,\n inputTokens: stats.inputTokens,\n outputTokens: stats.outputTokens,\n requestCount: stats.requestCount,\n }));\n\n return {\n usage,\n byProvider,\n byStatus: usageStats.byStatus ?? [],\n };\n}\n\nfunction normalizeApiEndpoint(endpoint: RawAIApiEndpoint): AIApiEndpoint {\n const normalizeFields = (\n fields?: Record<string, { type: string; required: boolean; description: string }>\n ): AIApiParameter[] =>\n Object.entries(fields ?? {}).map(([name, config]) => ({\n name,\n type: config.type,\n required: config.required,\n description: config.description,\n }));\n\n return {\n id: `${endpoint.method.toUpperCase()} ${endpoint.path}`,\n method: endpoint.method,\n path: endpoint.path,\n description: endpoint.description,\n parameters: normalizeFields(endpoint.parameters),\n body: normalizeFields(endpoint.body),\n };\n}\n\n// ── Tools ─────────────────────────────────────────────────────────────\n\nexport async function fetchTools(agent: AgentRef): Promise<AITool[]> {\n const data = await agentAiGet<{ tools: AITool[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/tools')\n );\n return data.tools;\n}\n\n// ── Templates (workspace-owned AI prompts) ───────────────────────────\n\nconst AI_PROMPT_FIELDS = `\n id\n name\n description\n content\n category\n variables\n isShared\n usageCount\n tags\n metadata\n createdAt\n updatedAt\n`;\n\nconst LIST_AI_PROMPTS_QUERY = `query ListAIPrompts($filter: AIPromptFilterInput, $pagination: PaginationInput) {\n aiPrompts(filter: $filter, pagination: $pagination) {\n items {\n ${AI_PROMPT_FIELDS}\n }\n total\n hasMore\n }\n}`;\n\nconst CREATE_AI_PROMPT_MUTATION = `mutation CreateAIPrompt($input: CreateAIPromptInput!) {\n createAIPrompt(input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst UPDATE_AI_PROMPT_MUTATION = `mutation UpdateAIPrompt($id: ID!, $input: UpdateAIPromptInput!) {\n updateAIPrompt(id: $id, input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst DELETE_AI_PROMPT_MUTATION = `mutation DeleteAIPrompt($id: ID!) {\n deleteAIPrompt(id: $id)\n}`;\n\nconst RENDER_AI_PROMPT_MUTATION = `mutation RenderAIPrompt($id: ID!, $variables: JSON) {\n renderAIPrompt(id: $id, variables: $variables) {\n promptId\n rendered\n }\n}`;\n\nconst RECORD_AI_PROMPT_USAGE_MUTATION = `mutation RecordAIPromptUsage($id: ID!) {\n recordAIPromptUsage(id: $id) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\ntype AITemplateInput = {\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n status?: string | null;\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[];\n isShared?: boolean;\n metadata?: Record<string, unknown>;\n};\n\ntype AITemplateUpdateInput = Partial<AITemplateInput>;\n\nfunction enumInput(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim().toUpperCase() : undefined;\n}\n\nfunction numberInput(value: unknown, fallback: number): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return fallback;\n}\n\nfunction buildAIPromptListVariables(params?: Record<string, unknown>): {\n filter: Record<string, unknown> | null;\n pagination: { limit: number; offset: number };\n} {\n const filter: Record<string, unknown> = {};\n const search = typeof params?.search === 'string' ? params.search.trim() : '';\n if (search) filter.search = search;\n\n const category = enumInput(params?.category);\n if (category) filter.category = category;\n\n const status = enumInput(params?.status);\n if (status) filter.status = status;\n\n if (typeof params?.isShared === 'boolean') filter.isShared = params.isShared;\n if (Array.isArray(params?.tags)) filter.tags = params.tags.map(String).filter(Boolean);\n if (typeof params?.createdBy === 'string' && params.createdBy.trim()) {\n filter.createdBy = params.createdBy.trim();\n }\n\n return {\n filter: Object.keys(filter).length > 0 ? filter : null,\n pagination: {\n limit: Math.min(Math.max(numberInput(params?.limit, 50), 1), 500),\n offset: Math.max(numberInput(params?.offset, 0), 0),\n },\n };\n}\n\nfunction toAIPromptInput(input: AITemplateInput | AITemplateUpdateInput): Record<string, unknown> {\n const promptInput: Record<string, unknown> = {};\n\n if (input.name !== undefined) promptInput.name = input.name;\n if (input.content !== undefined) promptInput.content = input.content;\n if (input.description !== undefined) promptInput.description = input.description;\n if (input.variables !== undefined) promptInput.variables = input.variables;\n if (input.tags !== undefined) promptInput.tags = input.tags;\n if (input.isShared !== undefined) promptInput.isShared = input.isShared;\n if (input.metadata !== undefined) promptInput.metadata = input.metadata;\n\n const category = enumInput(input.category);\n if (category) promptInput.category = category;\n\n const status = enumInput(input.status);\n if (status) promptInput.status = status;\n\n return promptInput;\n}\n\nexport async function fetchTemplates(\n _agentId: string,\n params?: Record<string, unknown>\n): Promise<AITemplate[]> {\n const data = await gatewayFetch<{ aiPrompts: PaginatedResponse<RawAITemplate> }>(\n LIST_AI_PROMPTS_QUERY,\n buildAIPromptListVariables(params)\n );\n return data.aiPrompts.items.map(normalizeTemplate);\n}\n\nexport async function createTemplate(\n _agentId: string,\n input: AITemplateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ createAIPrompt: RawAITemplate }>(CREATE_AI_PROMPT_MUTATION, {\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.createAIPrompt);\n}\n\nexport async function recordTemplateUsage(_agentId: string, id: string): Promise<AITemplate> {\n const data = await gatewayFetch<{ recordAIPromptUsage: RawAITemplate }>(\n RECORD_AI_PROMPT_USAGE_MUTATION,\n { id }\n );\n return normalizeTemplate(data.recordAIPromptUsage);\n}\n\nexport async function updateTemplate(\n _agentId: string,\n id: string,\n input: AITemplateUpdateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ updateAIPrompt: RawAITemplate }>(UPDATE_AI_PROMPT_MUTATION, {\n id,\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.updateAIPrompt);\n}\n\nexport async function deleteTemplate(_agentId: string, id: string): Promise<void> {\n await gatewayFetch<{ deleteAIPrompt: boolean }>(DELETE_AI_PROMPT_MUTATION, { id });\n}\n\nexport async function renderTemplate(\n _agentId: string,\n id: string,\n variables: Record<string, unknown>\n): Promise<{ rendered: string }> {\n const data = await gatewayFetch<{ renderAIPrompt: { promptId: string; rendered: string } }>(\n RENDER_AI_PROMPT_MUTATION,\n { id, variables }\n );\n return { rendered: data.renderAIPrompt.rendered };\n}\n\n// ── Contexts (backend route: /ai/contexts under canonical profile prefix) ──\n\nexport async function fetchContexts(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIContext[]> {\n const data = await agentAiGet<PaginatedResponse<AIContext>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n params\n );\n return data.items;\n}\n\nexport async function createContext(\n agent: AgentRef,\n input: {\n name: string;\n type: string;\n content: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n }\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n 'POST',\n input\n );\n}\n\nexport async function updateContext(\n agent: AgentRef,\n id: string,\n input: Partial<{\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata: Record<string, unknown>;\n }>\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, `/ai/contexts/${id}`),\n 'PUT',\n input\n );\n}\n\nexport async function deleteContext(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/contexts/${id}`), 'DELETE');\n}\n\n// ── Sessions (backend route: /ai/sessions) ──────────────────────────\n\nexport async function fetchSessions(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AISession[]> {\n const data = await agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params\n );\n return data.items;\n}\n\nexport async function fetchSessionsPaginated(\n agent: AgentRef,\n params?: { limit?: number; offset?: number; search?: string; agentType?: string; status?: string }\n): Promise<PaginatedResponse<AISession>> {\n return agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params as Record<string, unknown>\n );\n}\n\nexport async function createSession(\n agent: AgentRef,\n input: { name: string; agentType: string; config?: Record<string, unknown> }\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n 'POST',\n input\n );\n}\n\nexport async function sendPromptToSession(\n agent: AgentRef,\n sessionId: string,\n prompt: string,\n contexts?: string[],\n options?: {\n agentType?: string;\n model?: string;\n mode?: string;\n conversationHistory?: Array<{ role: string; content: string }>;\n }\n): Promise<{\n response: string;\n tokens?: { input: number; output: number };\n [key: string]: unknown;\n}> {\n const data = await agentAiMutate<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/send`),\n 'POST',\n {\n prompt,\n contexts,\n ...(options?.agentType ? { agentType: options.agentType } : {}),\n ...(options?.model ? { model: options.model } : {}),\n ...(options?.mode ? { mode: options.mode } : {}),\n ...(options?.conversationHistory?.length\n ? { conversationHistory: options.conversationHistory }\n : {}),\n }\n );\n const rawTokens = data.tokens as Record<string, unknown> | undefined;\n\n return {\n ...data,\n response: String(data.response ?? data.content ?? data.message ?? ''),\n tokens:\n rawTokens && (typeof rawTokens.input === 'number' || typeof rawTokens.output === 'number')\n ? {\n input: Number(rawTokens.input ?? 0),\n output: Number(rawTokens.output ?? 0),\n }\n : undefined,\n };\n}\n\nexport async function fetchSessionLogs(\n agent: AgentRef,\n sessionId: string,\n params?: Record<string, unknown>\n): Promise<AISessionLog[]> {\n const data = await agentAiGet<PaginatedResponse<RawAISessionLog>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/logs`),\n params\n );\n return data.items.map(normalizeSessionLog);\n}\n\nexport async function fetchSessionStats(\n agent: AgentRef,\n sessionId: string\n): Promise<AISessionStats> {\n return normalizeSessionStats(\n await agentAiGet<RawAISessionStats>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/stats`)\n )\n );\n}\n\nexport async function updateSessionConfig(\n agent: AgentRef,\n sessionId: string,\n config: Record<string, unknown>\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/config`),\n 'PUT',\n config\n );\n}\n\nexport async function terminateSession(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/sessions/${sessionId}`), 'DELETE');\n}\n\n// ── Prompt Composition & Dispatch (backend route: /ai/dispatch) ──\n\nexport async function composePrompt(\n agent: AgentRef,\n input: {\n templateId?: string;\n variables?: Record<string, unknown>;\n content?: string;\n contextIds?: string[];\n sessionId?: string;\n }\n): Promise<{ dispatch: AIDispatch; preview: string }> {\n return agentAiMutate<{ dispatch: AIDispatch; preview: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/compose'),\n 'POST',\n input\n );\n}\n\nexport async function sendDispatch(\n agent: AgentRef,\n input: {\n dispatchId?: string;\n prompt?: string;\n content?: string;\n sessionId?: string;\n contextIds?: string[];\n scheduledAt?: string;\n }\n): Promise<{ status: string; dispatchId: string; sessionId: string }> {\n return agentAiMutate<{ status: string; dispatchId: string; sessionId: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/send'),\n 'POST',\n {\n ...input,\n content: input.content ?? input.prompt,\n }\n );\n}\n\nexport async function fetchDispatchHistory(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIDispatch[]> {\n const data = await agentAiGet<PaginatedResponse<RawAIDispatch>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/history'),\n params\n );\n return data.items.map(normalizeDispatch);\n}\n\n// ── Queue (backend route: /ai/queue) ─────────────────────────────\n\nexport async function fetchQueueItems(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIQueueItem[]> {\n const data = await agentAiGet<PaginatedResponse<AIQueueItem>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue'),\n params\n );\n return data.items;\n}\n\nexport async function enqueueItem(\n agent: AgentRef,\n input: {\n dispatchedPromptId: string;\n sessionId: string;\n priority?: number;\n scheduledAt?: string;\n maxAttempts?: number;\n }\n): Promise<AIQueueItem> {\n return agentAiMutate<AIQueueItem>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue/enqueue'),\n 'POST',\n input\n );\n}\n\nexport async function cancelQueueItem(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/queue/${id}/cancel`), 'POST');\n}\n\n// ── Stats (backend route: /ai/stats) ─────────────────────────────\n\nexport async function fetchStatsOverview(agent: AgentRef): Promise<AIStatsOverview> {\n return normalizeStatsOverview(\n await agentAiGet<RawAIStatsOverview>(\n agent.id,\n agentApiPath(agent.profile, '/ai/stats/overview')\n )\n );\n}\n\nexport async function fetchUsageStats(agent: AgentRef): Promise<AIUsageStats> {\n return normalizeUsageStats(\n await agentAiGet<RawAIUsageStats>(agent.id, agentApiPath(agent.profile, '/ai/stats/usage'))\n );\n}\n\n// ── Providers (backend route: /ai/sessions/providers) ────────────\n\nexport async function fetchProviders(agent: AgentRef): Promise<AIProvider[]> {\n const data = await agentAiGet<{ providers: AIProvider[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/providers')\n );\n return data.providers;\n}\n\n// ── API Descriptor (backend route: /ai/descriptor) ───────────────\n\nexport async function fetchApiDescriptor(agent: AgentRef): Promise<AIApiEndpoint[]> {\n const data = await agentAiGet<{ name: string; version: string; endpoints: RawAIApiEndpoint[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/descriptor')\n );\n return data.endpoints.map(normalizeApiEndpoint);\n}\n\n// ── Agent Manager extensions ────────────────────────────────────────\n\nexport interface AIModelInfo {\n id: string;\n name: string;\n provider: string;\n contextWindow?: number;\n maxOutputTokens?: number;\n}\n\nexport interface SearchResult {\n sessionId: string;\n sessionName: string;\n messageId?: string;\n content: string;\n timestamp: string;\n score?: number;\n}\n\nexport async function renameSession(\n agent: AgentRef,\n sessionId: string,\n name: string\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}`),\n 'PATCH',\n { name }\n );\n}\n\nexport async function bulkDeleteSessions(agent: AgentRef, ids: string[]): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, '/ai/sessions/bulk-delete'), 'POST', {\n ids,\n });\n}\n\nexport async function fetchSessionMessages(\n agent: AgentRef,\n sessionId: string\n): Promise<AgentManagerMessage[]> {\n const data = await agentAiGet<{ messages: AgentManagerMessage[] }>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/messages`)\n );\n return data.messages;\n}\n\nexport async function searchSessions(\n agent: AgentRef,\n query: string,\n params?: Record<string, unknown>\n): Promise<SearchResult[]> {\n const data = await agentAiGet<{ results: SearchResult[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/search'),\n { query, ...params }\n );\n return data.results;\n}\n\nexport async function fetchMcpServers(agent: AgentRef): Promise<McpServer[]> {\n const data = await agentAiGet<{ servers: McpServer[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/mcp/servers')\n );\n return data.servers;\n}\n\nexport async function switchSessionModel(\n agent: AgentRef,\n sessionId: string,\n model: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/model`),\n 'PUT',\n { model }\n );\n}\n\nexport async function switchSessionSdk(\n agent: AgentRef,\n sessionId: string,\n sdk: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/sdk`),\n 'PUT',\n { sdk }\n );\n}\n\nexport async function switchSessionMode(\n agent: AgentRef,\n sessionId: string,\n mode: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/mode`),\n 'PUT',\n { mode }\n );\n}\n\nexport async function cancelSessionRequest(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/cancel`),\n 'POST'\n );\n}\n\nexport async function fetchModels(agent: AgentRef, provider?: string): Promise<AIModelInfo[]> {\n const data = await agentAiGet<{ models: AIModelInfo[] }>(\n agent.id,\n agentApiPath(\n agent.profile,\n provider ? `/ai/models/${encodeURIComponent(provider)}` : '/ai/models'\n )\n );\n return data.models;\n}\n\nexport async function exportSession(\n agent: AgentRef,\n sessionId: string,\n format: 'json' | 'markdown'\n): Promise<Blob> {\n const data = await agentAiGet<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/export`),\n { format }\n );\n const content = format === 'json' ? JSON.stringify(data, null, 2) : String(data.content ?? '');\n const mimeType = format === 'json' ? 'application/json' : 'text/markdown';\n return new Blob([content], { type: mimeType });\n}\n\nexport async function uploadSessionFile(\n agent: AgentRef,\n sessionId: string,\n file: File\n): Promise<FileAttachment> {\n const arrayBuffer = await file.arrayBuffer();\n const base64 = btoa(\n new Uint8Array(arrayBuffer).reduce((acc, byte) => acc + String.fromCharCode(byte), '')\n );\n return agentAiMutate<FileAttachment>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/files`),\n 'POST',\n {\n name: file.name,\n mimeType: file.type,\n size: file.size,\n content: base64,\n }\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"aiApi.js","names":[],"sources":["../../src/services/aiApi.ts"],"sourcesContent":["/**\n * AI API service layer\n *\n * Wraps workspace-owned AI prompt APIs and vibecontrols-agent AI REST API\n * endpoints exposed through wspace-vibecontrols-svc.\n *\n * Backend route prefix: /api/ai (mounted by vibe-plugin-ai v4.0.0)\n */\n\nimport {\n getStoredAuthToken,\n getStoredWorkspaceId,\n getStoredWorkspaceToken,\n} from '@burdenoff/fe-libs/shared/graphql';\nimport type {\n AgentManagerMessage,\n FileAttachment,\n McpServer,\n ProviderMode,\n} from '@/types/agentManager';\n\n// ── Helpers ───────────────────────────────────────────────────────────\n\n// Reads from ActiveContextStorage (URL → storage fallback chain). Previously\n// parsed only `?workspace=` from window.location.search, which is empty when\n// the user navigates between MFE routes that don't carry the param. Result:\n// gateway received no `x-workspace-id`, backend `agentAiGet` resolver threw\n// `WorkspaceRequiredError` and the chat pane surfaced \"Unexpected error\".\nfunction getWorkspaceId(): string {\n return getStoredWorkspaceId() ?? '';\n}\n\nfunction getGatewayEndpoint(): string {\n if (typeof window === 'undefined') return '/workspaces/graphql';\n return '/workspaces/graphql';\n}\n\n/**\n * Sanitize error messages — strip HTML, Cloudflare error pages, and long technical details.\n * Returns a clean, user-friendly error string.\n */\nfunction sanitizeError(raw: string): string {\n // Detect Cloudflare tunnel errors (HTML error pages)\n if (raw.includes('Cloudflare Tunnel error') || raw.includes('cloudflare.com/5xx')) {\n return 'Agent tunnel is unreachable. The Cloudflare tunnel may have disconnected. Check if the agent is running and the tunnel is active.';\n }\n // Detect HTML responses\n if (raw.includes('<!DOCTYPE') || raw.includes('<html')) {\n return 'Unexpected HTML response. The agent may be unreachable or the tunnel may have changed.';\n }\n // Detect common network errors\n if (raw.includes('ECONNREFUSED') || raw.includes('ETIMEDOUT') || raw.includes('fetch failed')) {\n return 'Cannot connect to the agent. Check if it is running and accessible.';\n }\n if (\n raw.includes('Could not resolve authentication method') ||\n raw.includes('ANTHROPIC_API_KEY') ||\n raw.includes('ANTHROPIC_AUTH_TOKEN')\n ) {\n return 'Claude SDK auth is not configured on the agent. Open the AI Config tab and save either ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN.';\n }\n // Detect proxy errors / missing plugin endpoints. 404 on a plugin route\n // almost always means the plugin isn't installed on this agent yet — give\n // the user the actionable next step instead of a vague \"unavailable\".\n if (raw.includes('NOT_FOUND') || raw.includes(' 404')) {\n return 'This feature is not installed on the agent. Install the matching vibe plugin from the agent CLI (e.g. `vibe plugin install <name>`), then refresh.';\n }\n if (raw.includes('502') || raw.includes('503')) {\n return 'Agent service unavailable. The agent may be restarting — try again in a few seconds.';\n }\n // Truncate very long errors\n if (raw.length > 200) {\n return raw.slice(0, 200) + '...';\n }\n return raw;\n}\n\n/**\n * Identifier for an agent + its profile name. Every aiApi function takes\n * one of these so the canonical path `/api/profiles/<profile>/<rest>` can\n * be constructed at every callsite. Callers should pass\n * `{ id: agent.id, profile: agent.metadata?.profile ?? 'default' }`.\n */\nexport interface AgentRef {\n id: string;\n profile: string;\n}\n\n/**\n * Build the canonical agent-API path for a given profile + path segment.\n * The svc-side proxy validates that paths are exactly of the form\n * `/api/profiles/<name>/<rest>` (or one of the bare carve-outs), so every\n * callsite builds the path explicitly via this helper instead of the\n * proxy doing any rewriting.\n *\n * Example: `agentApiPath('default', '/ai/sessions')` →\n * `/api/profiles/default/ai/sessions`.\n */\nexport function agentApiPath(profile: string, segment: string): string {\n const seg = segment.startsWith('/') ? segment : `/${segment}`;\n return `/api/profiles/${encodeURIComponent(profile)}${seg}`;\n}\n\nasync function gatewayFetch<T = unknown>(\n query: string,\n variables: Record<string, unknown>\n): Promise<T> {\n const res = await fetch(getGatewayEndpoint(), {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(getStoredAuthToken() ? { Authorization: `Bearer ${getStoredAuthToken()}` } : {}),\n ...(getStoredWorkspaceToken()\n ? { 'X-Workspace-Authorization': `Bearer ${getStoredWorkspaceToken()}` }\n : {}),\n ...(getWorkspaceId() ? { 'x-workspace-id': getWorkspaceId() } : {}),\n },\n body: JSON.stringify({ query, variables }),\n });\n\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(sanitizeError(`Gateway error ${res.status}: ${text || res.statusText}`));\n }\n\n const json = (await res.json()) as { data?: T; errors?: Array<{ message?: string }> };\n if (json.errors?.length) {\n const msg = json.errors[0]?.message || 'GraphQL request failed';\n throw new Error(sanitizeError(msg));\n }\n if (!json.data) throw new Error('GraphQL request returned no data');\n return json.data;\n}\n\nexport async function agentAiGet<T = unknown>(\n agentId: string,\n path: string,\n query?: Record<string, unknown>\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiGet: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `query AgentAiGet($agentId: ID!, $path: String!, $query: JSON) {\n agentAiGet(agentId: $agentId, path: $path, query: $query) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, query: query ?? null }\n );\n\n if (!data.agentAiGet.ok) {\n throw new Error(\n sanitizeError(data.agentAiGet.error || `Agent API error ${data.agentAiGet.status}`)\n );\n }\n return data.agentAiGet.data;\n}\n\nexport async function agentAiMutate<T = unknown>(\n agentId: string,\n path: string,\n method: string,\n body?: unknown\n): Promise<T> {\n const data = await gatewayFetch<{\n agentAiMutate: {\n ok: boolean;\n status: number;\n data: T;\n text?: string | null;\n error?: string | null;\n };\n }>(\n `mutation AgentAiMutate($agentId: ID!, $path: String!, $method: String!, $body: JSON) {\n agentAiMutate(agentId: $agentId, path: $path, method: $method, body: $body) {\n ok\n status\n data\n text\n error\n }\n }`,\n { agentId, path, method, body: body ?? null }\n );\n\n if (!data.agentAiMutate.ok) {\n throw new Error(\n sanitizeError(data.agentAiMutate.error || `Agent API error ${data.agentAiMutate.status}`)\n );\n }\n return data.agentAiMutate.data;\n}\n\nexport async function getAgentConfigValue(agent: AgentRef, key: string): Promise<string | null> {\n try {\n const data = await agentAiGet<{ key?: string; value?: string }>(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`)\n );\n return typeof data.value === 'string' ? data.value : null;\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.toLowerCase().includes('configuration key not found')\n ) {\n return null;\n }\n throw error;\n }\n}\n\nexport async function setAgentConfigValue(\n agent: AgentRef,\n key: string,\n value: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'PUT',\n { value }\n );\n}\n\nexport async function deleteAgentConfigValue(agent: AgentRef, key: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/config/${encodeURIComponent(key)}`),\n 'DELETE'\n );\n}\n\nfunction qs(params?: Record<string, unknown>): string {\n if (!params) return '';\n const entries = Object.entries(params).filter(\n ([, v]) => v !== undefined && v !== null && v !== ''\n );\n if (entries.length === 0) return '';\n const search = new URLSearchParams();\n for (const [k, v] of entries) search.set(k, String(v));\n return `?${search.toString()}`;\n}\n\n// ── Types ─────────────────────────────────────────────────────────────\n\nexport interface AITemplate {\n id: string;\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n variables: Record<string, unknown>;\n tags: string[];\n isShared?: boolean;\n usageCount?: number;\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIContext {\n id: string;\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AISession {\n id: string;\n name: string;\n agentType: string;\n providerPlugin?: string;\n status: 'active' | 'idle' | 'processing' | 'error' | 'terminated';\n stats?: Record<string, unknown>;\n config?: Record<string, unknown>;\n createdAt?: string;\n updatedAt?: string;\n terminatedAt?: string | null;\n}\n\nexport interface AISessionLog {\n id: string;\n sessionId: string;\n type: 'input' | 'output' | 'thinking' | 'event' | 'error' | 'metadata';\n content: string;\n message: string;\n tokenCount?: number;\n model?: string;\n durationMs?: number;\n agentMetadata?: Record<string, unknown>;\n timestamp: string;\n createdAt: string;\n}\n\nexport interface AISessionStats {\n session: { id: string; name: string; agentType: string; status: string; createdAt: string };\n stats: {\n totalLogs: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalDurationMs: number;\n logsByType: Record<string, number>;\n };\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n promptCount: number;\n averageLatency?: number;\n}\n\nexport interface AIDispatch {\n id: string;\n templateId?: string | null;\n content: string;\n prompt: string;\n resolvedVariables?: Record<string, unknown>;\n contextIds?: string[];\n sessionId?: string | null;\n status: string;\n result?: Record<string, unknown> | null;\n scheduledAt?: string | null;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface AIQueueItem {\n id: string;\n dispatchedPromptId: string;\n sessionId: string;\n priority: number;\n status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled';\n attempts: number;\n maxAttempts: number;\n lastError?: string | null;\n createdAt?: string;\n}\n\nexport interface AIStatsOverview {\n totalSessions: number;\n activeSessions: number;\n totalInputTokens: number;\n totalOutputTokens: number;\n totalTokens: number;\n inputTokens: number;\n outputTokens: number;\n totalRequests: number;\n sessionsByAgentType: Record<string, number>;\n queuedPrompts: number;\n}\n\nexport interface AIUsageStats {\n usage: Record<\n string,\n { inputTokens: number; outputTokens: number; requestCount: number; sessionCount: number }\n >;\n byProvider: Array<{\n provider: string;\n tokens: number;\n sessions: number;\n cost?: number;\n inputTokens: number;\n outputTokens: number;\n requestCount: number;\n }>;\n byStatus: Array<{ status: string; count: number }>;\n}\n\nexport interface AIProvider {\n pluginName: string;\n name?: string;\n displayName?: string;\n isDefault: boolean;\n currentMode?: ProviderMode;\n defaultMode?: ProviderMode;\n supportedModes?: ProviderMode[];\n unsupportedModes?: ProviderMode[];\n modes?: Record<ProviderMode, boolean>;\n prereqApiPrefix?: string;\n}\n\nexport interface AIApiParameter {\n name: string;\n type: string;\n required: boolean;\n description: string;\n}\n\nexport interface AIApiEndpoint {\n id: string;\n method: string;\n path: string;\n description: string;\n parameters?: AIApiParameter[];\n body?: AIApiParameter[];\n}\n\nexport interface AITool {\n name: string;\n displayName: string;\n description: string;\n installed: boolean;\n version?: string;\n configFiles?: string[];\n foundConfigs?: string[];\n installCommand?: string;\n}\n\n// ── Paginated response wrapper ───────────────────────────────────────\n\nexport interface PaginatedResponse<T> {\n items: T[];\n total: number;\n hasMore: boolean;\n}\n\ninterface RawAITemplate extends Omit<AITemplate, 'variables' | 'tags' | 'prompt'> {\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[] | null;\n}\n\ninterface RawAISessionLog extends Omit<AISessionLog, 'message' | 'timestamp'> {\n message?: string;\n timestamp?: string;\n}\n\ninterface RawAISessionStats {\n session?: AISessionStats['session'];\n stats?: Partial<AISessionStats['stats']>;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n promptCount?: number;\n averageLatency?: number;\n}\n\ninterface RawAIDispatch extends Omit<AIDispatch, 'prompt'> {\n prompt?: string;\n}\n\ninterface RawAIStatsOverview {\n totalSessions?: number;\n activeSessions?: number;\n totalInputTokens?: number;\n totalOutputTokens?: number;\n totalTokens?: number;\n inputTokens?: number;\n outputTokens?: number;\n totalRequests?: number;\n sessionsByAgentType?: Record<string, number>;\n queuedPrompts?: number;\n}\n\ninterface RawAIUsageStats {\n usage?: AIUsageStats['usage'];\n byProvider?: AIUsageStats['byProvider'];\n byStatus?: AIUsageStats['byStatus'];\n}\n\ninterface RawAIApiEndpoint {\n method: string;\n path: string;\n description: string;\n parameters?: Record<string, { type: string; required: boolean; description: string }>;\n body?: Record<string, { type: string; required: boolean; description: string }>;\n}\n\nfunction normalizeTemplate(template: RawAITemplate): AITemplate {\n const variables = Array.isArray(template.variables)\n ? Object.fromEntries(template.variables.map((name) => [String(name), '']))\n : (template.variables ?? {});\n\n return {\n ...template,\n description: template.description ?? null,\n variables,\n tags: template.tags ?? [],\n };\n}\n\nfunction normalizeSessionLog(log: RawAISessionLog): AISessionLog {\n return {\n ...log,\n message: log.message ?? log.content,\n timestamp: log.timestamp ?? log.createdAt,\n };\n}\n\nfunction normalizeSessionStats(stats: RawAISessionStats): AISessionStats {\n const statBlock = stats.stats ?? {};\n const inputTokens = stats.inputTokens ?? statBlock.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? statBlock.totalOutputTokens ?? 0;\n const promptCount = stats.promptCount ?? statBlock.totalLogs ?? 0;\n const totalDurationMs = statBlock.totalDurationMs ?? 0;\n\n return {\n session: stats.session ?? {\n id: '',\n name: '',\n agentType: '',\n status: '',\n createdAt: '',\n },\n stats: {\n totalLogs: statBlock.totalLogs ?? 0,\n totalInputTokens: statBlock.totalInputTokens ?? inputTokens,\n totalOutputTokens: statBlock.totalOutputTokens ?? outputTokens,\n totalDurationMs,\n logsByType: statBlock.logsByType ?? {},\n },\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n promptCount,\n averageLatency:\n stats.averageLatency ?? (promptCount > 0 ? totalDurationMs / promptCount : undefined),\n };\n}\n\nfunction normalizeDispatch(dispatch: RawAIDispatch): AIDispatch {\n return {\n ...dispatch,\n prompt: dispatch.prompt ?? dispatch.content,\n };\n}\n\nfunction normalizeStatsOverview(stats: RawAIStatsOverview): AIStatsOverview {\n const inputTokens = stats.inputTokens ?? stats.totalInputTokens ?? 0;\n const outputTokens = stats.outputTokens ?? stats.totalOutputTokens ?? 0;\n\n return {\n totalSessions: stats.totalSessions ?? 0,\n activeSessions: stats.activeSessions ?? 0,\n totalInputTokens: stats.totalInputTokens ?? inputTokens,\n totalOutputTokens: stats.totalOutputTokens ?? outputTokens,\n totalTokens: stats.totalTokens ?? inputTokens + outputTokens,\n inputTokens,\n outputTokens,\n totalRequests: stats.totalRequests ?? 0,\n sessionsByAgentType: stats.sessionsByAgentType ?? {},\n queuedPrompts: stats.queuedPrompts ?? 0,\n };\n}\n\nfunction normalizeUsageStats(usageStats: RawAIUsageStats): AIUsageStats {\n const usage = usageStats.usage ?? {};\n const byProvider =\n usageStats.byProvider ??\n Object.entries(usage).map(([provider, stats]) => ({\n provider,\n tokens: stats.inputTokens + stats.outputTokens,\n sessions: stats.sessionCount,\n inputTokens: stats.inputTokens,\n outputTokens: stats.outputTokens,\n requestCount: stats.requestCount,\n }));\n\n return {\n usage,\n byProvider,\n byStatus: usageStats.byStatus ?? [],\n };\n}\n\nfunction normalizeApiEndpoint(endpoint: RawAIApiEndpoint): AIApiEndpoint {\n const normalizeFields = (\n fields?: Record<string, { type: string; required: boolean; description: string }>\n ): AIApiParameter[] =>\n Object.entries(fields ?? {}).map(([name, config]) => ({\n name,\n type: config.type,\n required: config.required,\n description: config.description,\n }));\n\n return {\n id: `${endpoint.method.toUpperCase()} ${endpoint.path}`,\n method: endpoint.method,\n path: endpoint.path,\n description: endpoint.description,\n parameters: normalizeFields(endpoint.parameters),\n body: normalizeFields(endpoint.body),\n };\n}\n\n// ── Tools ─────────────────────────────────────────────────────────────\n\nexport async function fetchTools(agent: AgentRef): Promise<AITool[]> {\n const data = await agentAiGet<{ tools: AITool[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/tools')\n );\n return data.tools;\n}\n\n// ── Templates (workspace-owned AI prompts) ───────────────────────────\n\nconst AI_PROMPT_FIELDS = `\n id\n name\n description\n content\n category\n variables\n isShared\n usageCount\n tags\n metadata\n createdAt\n updatedAt\n`;\n\nconst LIST_AI_PROMPTS_QUERY = `query ListAIPrompts($filter: AIPromptFilterInput, $pagination: PaginationInput) {\n aiPrompts(filter: $filter, pagination: $pagination) {\n items {\n ${AI_PROMPT_FIELDS}\n }\n total\n hasMore\n }\n}`;\n\nconst CREATE_AI_PROMPT_MUTATION = `mutation CreateAIPrompt($input: CreateAIPromptInput!) {\n createAIPrompt(input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst UPDATE_AI_PROMPT_MUTATION = `mutation UpdateAIPrompt($id: ID!, $input: UpdateAIPromptInput!) {\n updateAIPrompt(id: $id, input: $input) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\nconst DELETE_AI_PROMPT_MUTATION = `mutation DeleteAIPrompt($id: ID!) {\n deleteAIPrompt(id: $id)\n}`;\n\nconst RENDER_AI_PROMPT_MUTATION = `mutation RenderAIPrompt($id: ID!, $variables: JSON) {\n renderAIPrompt(id: $id, variables: $variables) {\n promptId\n rendered\n }\n}`;\n\nconst RECORD_AI_PROMPT_USAGE_MUTATION = `mutation RecordAIPromptUsage($id: ID!) {\n recordAIPromptUsage(id: $id) {\n ${AI_PROMPT_FIELDS}\n }\n}`;\n\ntype AITemplateInput = {\n name: string;\n content: string;\n description?: string | null;\n category?: string | null;\n status?: string | null;\n variables?: Record<string, unknown> | string[] | null;\n tags?: string[];\n isShared?: boolean;\n metadata?: Record<string, unknown>;\n};\n\ntype AITemplateUpdateInput = Partial<AITemplateInput>;\n\nfunction enumInput(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value.trim().toUpperCase() : undefined;\n}\n\nfunction numberInput(value: unknown, fallback: number): number {\n if (typeof value === 'number' && Number.isFinite(value)) return value;\n if (typeof value === 'string' && value.trim()) {\n const parsed = Number(value);\n if (Number.isFinite(parsed)) return parsed;\n }\n return fallback;\n}\n\nfunction buildAIPromptListVariables(params?: Record<string, unknown>): {\n filter: Record<string, unknown> | null;\n pagination: { limit: number; offset: number };\n} {\n const filter: Record<string, unknown> = {};\n const search = typeof params?.search === 'string' ? params.search.trim() : '';\n if (search) filter.search = search;\n\n const category = enumInput(params?.category);\n if (category) filter.category = category;\n\n const status = enumInput(params?.status);\n if (status) filter.status = status;\n\n if (typeof params?.isShared === 'boolean') filter.isShared = params.isShared;\n if (Array.isArray(params?.tags)) filter.tags = params.tags.map(String).filter(Boolean);\n if (typeof params?.createdBy === 'string' && params.createdBy.trim()) {\n filter.createdBy = params.createdBy.trim();\n }\n\n return {\n filter: Object.keys(filter).length > 0 ? filter : null,\n pagination: {\n limit: Math.min(Math.max(numberInput(params?.limit, 50), 1), 500),\n offset: Math.max(numberInput(params?.offset, 0), 0),\n },\n };\n}\n\nfunction toAIPromptInput(input: AITemplateInput | AITemplateUpdateInput): Record<string, unknown> {\n const promptInput: Record<string, unknown> = {};\n\n if (input.name !== undefined) promptInput.name = input.name;\n if (input.content !== undefined) promptInput.content = input.content;\n if (input.description !== undefined) promptInput.description = input.description;\n if (input.variables !== undefined) promptInput.variables = input.variables;\n if (input.tags !== undefined) promptInput.tags = input.tags;\n if (input.isShared !== undefined) promptInput.isShared = input.isShared;\n if (input.metadata !== undefined) promptInput.metadata = input.metadata;\n\n const category = enumInput(input.category);\n if (category) promptInput.category = category;\n\n const status = enumInput(input.status);\n if (status) promptInput.status = status;\n\n return promptInput;\n}\n\nexport async function fetchTemplates(\n _agentId: string,\n params?: Record<string, unknown>\n): Promise<AITemplate[]> {\n const data = await gatewayFetch<{ aiPrompts: PaginatedResponse<RawAITemplate> }>(\n LIST_AI_PROMPTS_QUERY,\n buildAIPromptListVariables(params)\n );\n return data.aiPrompts.items.map(normalizeTemplate);\n}\n\nexport async function createTemplate(\n _agentId: string,\n input: AITemplateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ createAIPrompt: RawAITemplate }>(CREATE_AI_PROMPT_MUTATION, {\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.createAIPrompt);\n}\n\nexport async function recordTemplateUsage(_agentId: string, id: string): Promise<AITemplate> {\n const data = await gatewayFetch<{ recordAIPromptUsage: RawAITemplate }>(\n RECORD_AI_PROMPT_USAGE_MUTATION,\n { id }\n );\n return normalizeTemplate(data.recordAIPromptUsage);\n}\n\nexport async function updateTemplate(\n _agentId: string,\n id: string,\n input: AITemplateUpdateInput\n): Promise<AITemplate> {\n const data = await gatewayFetch<{ updateAIPrompt: RawAITemplate }>(UPDATE_AI_PROMPT_MUTATION, {\n id,\n input: toAIPromptInput(input),\n });\n return normalizeTemplate(data.updateAIPrompt);\n}\n\nexport async function deleteTemplate(_agentId: string, id: string): Promise<void> {\n await gatewayFetch<{ deleteAIPrompt: boolean }>(DELETE_AI_PROMPT_MUTATION, { id });\n}\n\nexport async function renderTemplate(\n _agentId: string,\n id: string,\n variables: Record<string, unknown>\n): Promise<{ rendered: string }> {\n const data = await gatewayFetch<{ renderAIPrompt: { promptId: string; rendered: string } }>(\n RENDER_AI_PROMPT_MUTATION,\n { id, variables }\n );\n return { rendered: data.renderAIPrompt.rendered };\n}\n\n// ── Contexts (backend route: /ai/contexts under canonical profile prefix) ──\n\nexport async function fetchContexts(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIContext[]> {\n const data = await agentAiGet<PaginatedResponse<AIContext>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n params\n );\n return data.items;\n}\n\nexport async function createContext(\n agent: AgentRef,\n input: {\n name: string;\n type: string;\n content: string;\n tags?: string[];\n metadata?: Record<string, unknown>;\n }\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, '/ai/contexts'),\n 'POST',\n input\n );\n}\n\nexport async function updateContext(\n agent: AgentRef,\n id: string,\n input: Partial<{\n name: string;\n type: string;\n content: string;\n tags: string[];\n metadata: Record<string, unknown>;\n }>\n): Promise<AIContext> {\n return agentAiMutate<AIContext>(\n agent.id,\n agentApiPath(agent.profile, `/ai/contexts/${id}`),\n 'PUT',\n input\n );\n}\n\nexport async function deleteContext(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/contexts/${id}`), 'DELETE');\n}\n\n// ── Sessions (backend route: /ai/sessions) ──────────────────────────\n\nexport async function fetchSessions(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AISession[]> {\n const data = await agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params\n );\n return data.items;\n}\n\nexport async function fetchSessionsPaginated(\n agent: AgentRef,\n params?: { limit?: number; offset?: number; search?: string; agentType?: string; status?: string }\n): Promise<PaginatedResponse<AISession>> {\n return agentAiGet<PaginatedResponse<AISession>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n params as Record<string, unknown>\n );\n}\n\nexport async function createSession(\n agent: AgentRef,\n input: { name: string; agentType: string; config?: Record<string, unknown> }\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions'),\n 'POST',\n input\n );\n}\n\nexport async function sendPromptToSession(\n agent: AgentRef,\n sessionId: string,\n prompt: string,\n contexts?: string[],\n options?: {\n agentType?: string;\n model?: string;\n mode?: string;\n conversationHistory?: Array<{ role: string; content: string }>;\n }\n): Promise<{\n response: string;\n tokens?: { input: number; output: number };\n [key: string]: unknown;\n}> {\n const data = await agentAiMutate<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/send`),\n 'POST',\n {\n prompt,\n contexts,\n ...(options?.agentType ? { agentType: options.agentType } : {}),\n ...(options?.model ? { model: options.model } : {}),\n ...(options?.mode ? { mode: options.mode } : {}),\n ...(options?.conversationHistory?.length\n ? { conversationHistory: options.conversationHistory }\n : {}),\n }\n );\n const rawTokens = data.tokens as Record<string, unknown> | undefined;\n\n return {\n ...data,\n response: String(data.response ?? data.content ?? data.message ?? ''),\n tokens:\n rawTokens && (typeof rawTokens.input === 'number' || typeof rawTokens.output === 'number')\n ? {\n input: Number(rawTokens.input ?? 0),\n output: Number(rawTokens.output ?? 0),\n }\n : undefined,\n };\n}\n\nexport async function fetchSessionLogs(\n agent: AgentRef,\n sessionId: string,\n params?: Record<string, unknown>\n): Promise<AISessionLog[]> {\n const data = await agentAiGet<PaginatedResponse<RawAISessionLog>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/logs`),\n params\n );\n return data.items.map(normalizeSessionLog);\n}\n\nexport async function fetchSessionStats(\n agent: AgentRef,\n sessionId: string\n): Promise<AISessionStats> {\n return normalizeSessionStats(\n await agentAiGet<RawAISessionStats>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/stats`)\n )\n );\n}\n\nexport async function updateSessionConfig(\n agent: AgentRef,\n sessionId: string,\n config: Record<string, unknown>\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/config`),\n 'PUT',\n config\n );\n}\n\nexport async function terminateSession(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/sessions/${sessionId}`), 'DELETE');\n}\n\n// ── Prompt Composition & Dispatch (backend route: /ai/dispatch) ──\n\nexport async function composePrompt(\n agent: AgentRef,\n input: {\n templateId?: string;\n variables?: Record<string, unknown>;\n content?: string;\n contextIds?: string[];\n sessionId?: string;\n }\n): Promise<{ dispatch: AIDispatch; preview: string }> {\n return agentAiMutate<{ dispatch: AIDispatch; preview: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/compose'),\n 'POST',\n input\n );\n}\n\nexport async function sendDispatch(\n agent: AgentRef,\n input: {\n dispatchId?: string;\n prompt?: string;\n content?: string;\n sessionId?: string;\n contextIds?: string[];\n scheduledAt?: string;\n }\n): Promise<{ status: string; dispatchId: string; sessionId: string }> {\n return agentAiMutate<{ status: string; dispatchId: string; sessionId: string }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/send'),\n 'POST',\n {\n ...input,\n content: input.content ?? input.prompt,\n }\n );\n}\n\nexport async function fetchDispatchHistory(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIDispatch[]> {\n const data = await agentAiGet<PaginatedResponse<RawAIDispatch>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/dispatch/history'),\n params\n );\n return data.items.map(normalizeDispatch);\n}\n\n// ── Queue (backend route: /ai/queue) ─────────────────────────────\n\nexport async function fetchQueueItems(\n agent: AgentRef,\n params?: Record<string, unknown>\n): Promise<AIQueueItem[]> {\n const data = await agentAiGet<PaginatedResponse<AIQueueItem>>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue'),\n params\n );\n return data.items;\n}\n\nexport async function enqueueItem(\n agent: AgentRef,\n input: {\n dispatchedPromptId: string;\n sessionId: string;\n priority?: number;\n scheduledAt?: string;\n maxAttempts?: number;\n }\n): Promise<AIQueueItem> {\n return agentAiMutate<AIQueueItem>(\n agent.id,\n agentApiPath(agent.profile, '/ai/queue/enqueue'),\n 'POST',\n input\n );\n}\n\nexport async function cancelQueueItem(agent: AgentRef, id: string): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, `/ai/queue/${id}/cancel`), 'POST');\n}\n\n// ── Stats (backend route: /ai/stats) ─────────────────────────────\n\nexport async function fetchStatsOverview(agent: AgentRef): Promise<AIStatsOverview> {\n return normalizeStatsOverview(\n await agentAiGet<RawAIStatsOverview>(\n agent.id,\n agentApiPath(agent.profile, '/ai/stats/overview')\n )\n );\n}\n\nexport async function fetchUsageStats(agent: AgentRef): Promise<AIUsageStats> {\n return normalizeUsageStats(\n await agentAiGet<RawAIUsageStats>(agent.id, agentApiPath(agent.profile, '/ai/stats/usage'))\n );\n}\n\n// ── Providers (backend route: /ai/sessions/providers) ────────────\n\nexport async function fetchProviders(agent: AgentRef): Promise<AIProvider[]> {\n const data = await agentAiGet<{ providers: AIProvider[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/providers')\n );\n return data.providers;\n}\n\n// ── API Descriptor (backend route: /ai/descriptor) ───────────────\n\nexport async function fetchApiDescriptor(agent: AgentRef): Promise<AIApiEndpoint[]> {\n const data = await agentAiGet<{ name: string; version: string; endpoints: RawAIApiEndpoint[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/descriptor')\n );\n return data.endpoints.map(normalizeApiEndpoint);\n}\n\n// ── Agent Manager extensions ────────────────────────────────────────\n\nexport interface AIModelInfo {\n id: string;\n name: string;\n provider: string;\n contextWindow?: number;\n maxOutputTokens?: number;\n}\n\nexport interface SearchResult {\n sessionId: string;\n sessionName: string;\n messageId?: string;\n content: string;\n timestamp: string;\n score?: number;\n}\n\nexport async function renameSession(\n agent: AgentRef,\n sessionId: string,\n name: string\n): Promise<AISession> {\n return agentAiMutate<AISession>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}`),\n 'PATCH',\n { name }\n );\n}\n\nexport async function bulkDeleteSessions(agent: AgentRef, ids: string[]): Promise<void> {\n await agentAiMutate(agent.id, agentApiPath(agent.profile, '/ai/sessions/bulk-delete'), 'POST', {\n ids,\n });\n}\n\nexport async function fetchSessionMessages(\n agent: AgentRef,\n sessionId: string\n): Promise<AgentManagerMessage[]> {\n const data = await agentAiGet<{ messages: AgentManagerMessage[] }>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/messages`)\n );\n return data.messages;\n}\n\nexport async function searchSessions(\n agent: AgentRef,\n query: string,\n params?: Record<string, unknown>\n): Promise<SearchResult[]> {\n const data = await agentAiGet<{ results: SearchResult[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/sessions/search'),\n { query, ...params }\n );\n return data.results;\n}\n\nexport async function fetchMcpServers(agent: AgentRef): Promise<McpServer[]> {\n const data = await agentAiGet<{ servers: McpServer[] }>(\n agent.id,\n agentApiPath(agent.profile, '/ai/mcp/servers')\n );\n return data.servers;\n}\n\nexport async function switchSessionModel(\n agent: AgentRef,\n sessionId: string,\n model: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/model`),\n 'PUT',\n { model }\n );\n}\n\nexport async function switchSessionSdk(\n agent: AgentRef,\n sessionId: string,\n sdk: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/sdk`),\n 'PUT',\n { sdk }\n );\n}\n\nexport async function switchSessionMode(\n agent: AgentRef,\n sessionId: string,\n mode: string\n): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/mode`),\n 'PUT',\n { mode }\n );\n}\n\nexport async function cancelSessionRequest(agent: AgentRef, sessionId: string): Promise<void> {\n await agentAiMutate(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/cancel`),\n 'POST'\n );\n}\n\nexport async function fetchModels(agent: AgentRef, provider?: string): Promise<AIModelInfo[]> {\n const data = await agentAiGet<{ models: AIModelInfo[] }>(\n agent.id,\n agentApiPath(\n agent.profile,\n provider ? `/ai/models/${encodeURIComponent(provider)}` : '/ai/models'\n )\n );\n return data.models;\n}\n\nexport async function exportSession(\n agent: AgentRef,\n sessionId: string,\n format: 'json' | 'markdown'\n): Promise<Blob> {\n const data = await agentAiGet<Record<string, unknown>>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/export`),\n { format }\n );\n const content = format === 'json' ? JSON.stringify(data, null, 2) : String(data.content ?? '');\n const mimeType = format === 'json' ? 'application/json' : 'text/markdown';\n return new Blob([content], { type: mimeType });\n}\n\nexport async function uploadSessionFile(\n agent: AgentRef,\n sessionId: string,\n file: File\n): Promise<FileAttachment> {\n const arrayBuffer = await file.arrayBuffer();\n const base64 = btoa(\n new Uint8Array(arrayBuffer).reduce((acc, byte) => acc + String.fromCharCode(byte), '')\n );\n return agentAiMutate<FileAttachment>(\n agent.id,\n agentApiPath(agent.profile, `/ai/sessions/${sessionId}/files`),\n 'POST',\n {\n name: file.name,\n mimeType: file.type,\n size: file.size,\n content: base64,\n }\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-vibecontrols",
3
- "version": "2026.522.6",
3
+ "version": "2026.522.8",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [