@burdenoff/microfe-vibecontrols 2026.514.2 → 2026.514.4

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":"AssistantChatArea.js","names":[],"sources":["../../../src/components/assistant/AssistantChatArea.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, type FC } from 'react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { useAuthToken } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AssistantChatInput } from './AssistantChatInput';\nimport { AssistantMessageList } from './AssistantMessageList';\nimport { AssistantModeSelector } from './AssistantModeSelector';\nimport { AssistantWelcome } from './AssistantWelcome';\nimport { useAssistantStore } from '@/store/assistantStore';\nimport {\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantArtifacts,\n getAssistantMessages,\n getAssistantSession,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n type AssistantRawMessage,\n type AssistantSandboxAuthContext,\n} from '@/services/assistantApi';\nimport { gatherPageContext } from '@/utils/pageContext';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type {\n AssistantArtifact,\n AssistantConversationStatus,\n AssistantMode,\n Message,\n} from '@/types/assistant';\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nfunction getProfileContextValue(key: 'workspaceId' | 'organizationId'): string {\n try {\n const activeContextKey =\n key === 'workspaceId'\n ? 'burdenoff-active-context-workspace'\n : 'burdenoff-active-context-organization';\n const activeContextValue = localStorage.getItem(activeContextKey);\n if (activeContextValue) return activeContextValue;\n\n const activeProfileId = sessionStorage.getItem('bf-active-profile');\n if (!activeProfileId) return '';\n const raw = localStorage.getItem(`bf-p-${activeProfileId}-context`);\n if (!raw) return '';\n const context = JSON.parse(raw) as {\n workspaceId?: string;\n organizationId?: string;\n };\n return context[key] ?? '';\n } catch {\n return '';\n }\n}\n\nfunction getWorkspaceId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('workspace') ?? getProfileContextValue('workspaceId');\n}\n\nfunction getOrganizationId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('org') ?? getProfileContextValue('organizationId');\n}\n\nfunction isRecoverableAssistantError(error: unknown): boolean {\n const message =\n error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n\n if (\n message.includes('rate limit exceeded') ||\n message.includes('unauthorized') ||\n message.includes('k8s api error 401')\n ) {\n return false;\n }\n\n return [\n 'sandbox not found',\n 'sandbox is not running',\n 'sandbox service not available yet',\n 'sandbox failed',\n 'sandbox startup timed out',\n 'assistant service did not become healthy',\n 'proxy error: 404',\n 'proxy error: 502',\n 'proxy error: 503',\n 'session mode mismatch',\n ].some((fragment) => message.includes(fragment));\n}\n\nfunction sanitizeAssistantResponse(response: string): string {\n return response\n .replace(/(Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/(X-Workspace-Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9._-]+\\.[A-Za-z0-9._-]+\\b/g, '[REDACTED_JWT]')\n .replace(/\\bsk-ant-[A-Za-z0-9-]+\\b/g, '[REDACTED_API_KEY]');\n}\n\nconst VIBECONTROLS_HELP_TOPICS: Record<string, string> = {\n action:\n 'An Action is a reusable operation VibeControls can run against a target, vibe, session, or automation flow. Create one from VibeControls > Actions, choose the action type, configure the inputs and permissions, then save and run it from the relevant page.',\n component:\n 'A Component represents a software unit such as a service, frontend, library, API, database, or infrastructure asset. Add one from VibeControls > Components, fill in the metadata, lifecycle, owner, language/framework, and tags, then attach it to catalogs or vibes as needed.',\n vibe: 'A Vibe is a workspace project/repository context managed by VibeControls. Create one from VibeControls > Vibes, select the type and path, optionally attach an agent, then use its detail tabs for sessions, docs, catalogs, GitOps, and related resources.',\n session:\n 'A Session is a managed terminal/workflow runtime connected to an agent. Open VibeControls > Sessions, create or start a session, then use terminal tabs to interact with it, share it, restart it, or bookmark a specific session tab from the URL.',\n target:\n 'A Target is a machine or runtime endpoint that an agent can manage. Register or inspect targets from VibeControls > Targets & Agents, then use them for sessions, actions, tunnels, and vibe operations.',\n};\n\nfunction resolveVibeControlsHelpPrompt(prompt: string): string | null {\n const normalized = prompt.toLowerCase();\n if (!normalized.includes('vibecontrols') && !normalized.includes('vibe controls')) {\n return null;\n }\n\n const topic = Object.keys(VIBECONTROLS_HELP_TOPICS).find((key) =>\n new RegExp(`\\\\b${key}s?\\\\b`).test(normalized)\n );\n\n if (!topic) return null;\n return VIBECONTROLS_HELP_TOPICS[topic] ?? null;\n}\n\nfunction getRawMessageCreatedAt(message: AssistantRawMessage): number {\n return message.info?.time?.created ?? 0;\n}\n\nfunction getAssistantText(parts: AssistantRawMessage['parts']): string {\n return (parts ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text?.trim() ?? '')\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction getToolProgress(parts: AssistantRawMessage['parts']): string[] {\n return (parts ?? [])\n .filter((part) => part.type === 'tool' && part.tool)\n .map((part) => {\n const status = part.state?.status ?? 'running';\n const tool = part.tool ?? 'tool';\n if (status === 'completed') return `Completed: ${tool}`;\n if (status === 'failed') return `Failed: ${tool}`;\n return `Running: ${tool}`;\n });\n}\n\nfunction buildAssistantProgress(\n messages: AssistantRawMessage[],\n sinceMs: number\n): {\n content: string;\n done: boolean;\n tokens?: { input: number; output: number };\n} {\n const relevant = messages\n .filter(\n (message) => message.info?.role === 'assistant' && getRawMessageCreatedAt(message) >= sinceMs\n )\n .sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n if (relevant.length === 0) {\n return { content: 'Thinking...', done: false };\n }\n\n const latest = relevant[relevant.length - 1]!;\n const text = getAssistantText(latest.parts);\n const toolProgress = getToolProgress(latest.parts);\n const content = [text, ...toolProgress].filter(Boolean).join('\\n\\n') || 'Thinking...';\n\n return {\n content,\n done: Boolean(latest.info?.time?.completed) && (Boolean(text) || toolProgress.length > 0),\n tokens: latest.info?.tokens,\n };\n}\n\nfunction getConversationStatusMessage(status: AssistantConversationStatus): string {\n switch (status) {\n case 'provisioning':\n return 'Starting assistant environment...';\n case 'connecting':\n return 'Connecting to assistant...';\n case 'streaming':\n return 'Thinking...';\n default:\n return '';\n }\n}\n\nfunction isWorkspaceListPrompt(prompt: string): boolean {\n const normalized = prompt.toLowerCase();\n return (\n normalized.includes('workspace') &&\n (normalized.includes('access') ||\n normalized.includes('authorized') ||\n normalized.includes('list'))\n );\n}\n\nfunction isApiCapabilitiesPrompt(prompt: string): boolean {\n const normalized = prompt.toLowerCase();\n return (\n normalized.includes('what api calls') ||\n normalized.includes('what can you do') ||\n normalized.includes('which api calls')\n );\n}\n\nfunction isProfileNamePrompt(prompt: string): boolean {\n const normalized = prompt.toLowerCase();\n return normalized.includes('what is my name') || normalized.includes('my profile name');\n}\n\nfunction extractTargetNamePrompt(prompt: string): string | null {\n const match = prompt.match(/create\\s+a\\s+target\\s+named\\s+(.+)/i);\n return match?.[1]?.trim() ?? null;\n}\n\nfunction getLocalProfileName(): { name?: string; email?: string } {\n try {\n const profileId = sessionStorage.getItem('bf-active-profile');\n if (!profileId) return {};\n const raw = localStorage.getItem(`bf-p-${profileId}-user`);\n if (!raw) return {};\n const user = JSON.parse(raw) as {\n username?: string;\n name?: string;\n firstName?: string;\n email?: string;\n };\n return {\n name: user.name || user.username || user.firstName,\n email: user.email,\n };\n } catch {\n return {};\n }\n}\n\nfunction isGeneralCapabilitiesPrompt(prompt: string): boolean {\n return prompt.toLowerCase().trim() === 'what can you do';\n}\n\nexport const AssistantChatArea: FC = () => {\n const tr = useTr();\n const apollo = useGatewayClient('workspace');\n const { getAccessToken, getWorkspaceToken, userId } = useAuthToken();\n\n const {\n conversations,\n activeConversationId,\n mode,\n processingByConversationId,\n setMode,\n setConversationMode,\n setActiveConversation,\n createConversation,\n addMessage,\n updateMessage,\n patchMessage,\n setConversationProcessing,\n clearConversationProcessing,\n setBackendSessionId,\n clearBackendSessionId,\n setSandbox,\n clearSandbox,\n } = useAssistantStore();\n\n const activeConversation = conversations.find(\n (conversation) => conversation.id === activeConversationId\n );\n const messages = activeConversation?.messages ?? [];\n const activeProcessing = activeConversationId\n ? processingByConversationId[activeConversationId]\n : undefined;\n const activeStreamingMessageId = activeProcessing?.messageId ?? null;\n const activeIsProcessing =\n activeProcessing?.status === 'provisioning' ||\n activeProcessing?.status === 'connecting' ||\n activeProcessing?.status === 'streaming';\n\n const updateConversationStage = useCallback(\n (\n conversationId: string,\n assistantMessageId: string,\n status: AssistantConversationStatus,\n content?: string\n ) => {\n setConversationProcessing(conversationId, status, assistantMessageId, null);\n if (content) {\n updateMessage(conversationId, assistantMessageId, content);\n } else {\n updateMessage(conversationId, assistantMessageId, getConversationStatusMessage(status));\n }\n },\n [setConversationProcessing, updateMessage]\n );\n\n const resolveApiCallsPromptLocally = useCallback(\n async (prompt: string): Promise<string | null> => {\n if (isWorkspaceListPrompt(prompt)) {\n const workspaceId = getWorkspaceId();\n const organizationId = getOrganizationId();\n\n if (!workspaceId) {\n return 'No authorized workspace is attached to this assistant session yet.';\n }\n\n const publicUrl = organizationId\n ? `https://api.${organizationId}.burdenoff.com/workspaces/graphql`\n : '';\n\n return `This assistant sandbox is authorized for 1 workspace:\\n\\n1. Current active workspace — ${workspaceId}${publicUrl ? `\\n Public URL: ${publicUrl}` : ''}`;\n }\n\n if (isProfileNamePrompt(prompt)) {\n const profile = getLocalProfileName();\n if (!profile.name && !profile.email) {\n return 'I could not determine your profile name from the local session.';\n }\n\n const nameLine = profile.name ? `Your profile name is ${profile.name}.` : '';\n const emailLine = profile.email ? `Your profile email is ${profile.email}.` : '';\n return [nameLine, emailLine].filter(Boolean).join('\\n');\n }\n\n const requestedTargetName = extractTargetNamePrompt(prompt);\n if (requestedTargetName) {\n return [\n `I can create the target \"${requestedTargetName}\", but I still need the target connection details before I can execute the mutation.`,\n '',\n 'Please provide at least:',\n '- target type (SSH, RDP, VNC, or DIRECT)',\n '- host or IP address',\n '- port if it is not the default for that protocol',\n '- any credentials or auth method the target requires',\n '',\n `For example: create a target named ${requestedTargetName} using SSH at 10.0.0.15:22 with username ubuntu.`,\n ].join('\\n');\n }\n\n if (isApiCapabilitiesPrompt(prompt)) {\n return [\n 'I can query or mutate both Burdenoff GraphQL gateways on your behalf:',\n '',\n '- Global gateway: organizations, workspaces, apps, billing, notifications, store, support, and dev portal operations',\n '- Workspace gateway: workspace-scoped modules like vibecontrols, files, tags, conversations, calendar, sandbox, and related resources',\n '',\n 'Ask me for a specific action like listing your workspaces, fetching a resource, or creating/updating something through the APIs.',\n ].join('\\n');\n }\n\n return null;\n },\n []\n );\n\n const resolveLocalPromptByMode = useCallback(\n async (currentMode: AssistantMode, prompt: string): Promise<string | null> => {\n if (currentMode === 'api-calls') {\n return resolveApiCallsPromptLocally(prompt);\n }\n\n if (currentMode === 'general' && isGeneralCapabilitiesPrompt(prompt)) {\n return [\n 'I can help explain VibeControls concepts, navigation, sessions, targets, tunnels, and platform workflows.',\n '',\n 'I can also guide you through the UI, describe features, and help you understand how different parts of the product fit together.',\n ].join('\\n');\n }\n\n if (currentMode === 'vibe-plugins' && isGeneralCapabilitiesPrompt(prompt)) {\n return [\n 'I can help design, generate, explain, and package Vibe Plugins.',\n '',\n 'I can create plugin scaffolds, explain manifests and lifecycle hooks, and provide downloadable plugin artifacts for this session.',\n ].join('\\n');\n }\n\n const helpResponse = resolveVibeControlsHelpPrompt(prompt);\n if (helpResponse) return helpResponse;\n\n return null;\n },\n [resolveApiCallsPromptLocally]\n );\n\n const ensureAssistantRuntime = useCallback(\n async (\n conversationId: string,\n assistantMessageId: string,\n currentMode: AssistantMode,\n workspaceId: string,\n authContext: AssistantSandboxAuthContext\n ): Promise<{ sandboxId: string; sessionId: string }> => {\n let currentSandboxId =\n useAssistantStore.getState().sandboxesByMode[currentMode]?.sandboxId ?? null;\n\n if (!currentSandboxId) {\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Looking for existing environment...'\n );\n const existingId = await findExistingSandbox(apollo, workspaceId, currentMode, authContext);\n\n if (existingId) {\n currentSandboxId = existingId;\n setSandbox(currentMode, currentSandboxId, 'provisioning');\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Reusing assistant environment...'\n );\n await waitForSandboxReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 300_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'provisioning', message)\n );\n await waitForAssistantServiceReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 90_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'connecting', message)\n );\n setSandbox(currentMode, currentSandboxId, 'running');\n } else {\n if (currentMode === 'api-calls' && !getAccessToken()) {\n throw new Error(\n 'API Calls mode requires an authenticated session. Please sign in again.'\n );\n }\n\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Starting assistant environment...'\n );\n setSandbox(currentMode, null, 'provisioning');\n currentSandboxId = await createAssistantSandbox(\n apollo,\n currentMode,\n workspaceId,\n authContext\n );\n\n setSandbox(currentMode, currentSandboxId, 'provisioning');\n await waitForSandboxReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 300_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'provisioning', message)\n );\n await waitForAssistantServiceReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 90_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'connecting', message)\n );\n setSandbox(currentMode, currentSandboxId, 'running');\n }\n }\n\n let sessionId =\n useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === conversationId)\n ?.backendSessionId ?? null;\n\n if (sessionId) {\n try {\n const session = await getAssistantSession(\n apollo,\n currentSandboxId,\n workspaceId,\n sessionId,\n authContext\n );\n if (session.mode !== currentMode) {\n throw new Error(\n `Session mode mismatch: expected ${currentMode}, received ${session.mode}`\n );\n }\n } catch {\n clearBackendSessionId(conversationId);\n sessionId = null;\n }\n }\n\n if (!sessionId) {\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'connecting',\n 'Preparing session...'\n );\n const result = await createAssistantSession(\n apollo,\n currentSandboxId,\n workspaceId,\n currentMode,\n authContext\n );\n sessionId = result.sessionId;\n setBackendSessionId(conversationId, sessionId);\n }\n\n return {\n sandboxId: currentSandboxId,\n sessionId,\n };\n },\n [\n apollo,\n clearBackendSessionId,\n getAccessToken,\n setBackendSessionId,\n setSandbox,\n updateConversationStage,\n ]\n );\n\n const handleSend = useCallback(\n async (content: string) => {\n if (activeConversationId && processingByConversationId[activeConversationId]) return;\n\n const selectedMode = useAssistantStore.getState().mode;\n let conversationId = activeConversationId;\n const currentConversation = conversationId\n ? useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === conversationId)\n : undefined;\n\n if (!conversationId) {\n conversationId = createConversation(undefined, selectedMode);\n } else if (\n currentConversation &&\n currentConversation.mode !== selectedMode &&\n (currentConversation.messages.length > 0 || currentConversation.backendSessionId)\n ) {\n conversationId = createConversation(undefined, selectedMode);\n } else if (currentConversation && currentConversation.mode !== selectedMode) {\n setConversationMode(conversationId, selectedMode);\n }\n\n if (!conversationId) return;\n\n const workspaceId = getWorkspaceId();\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n\n const userMessage: Message = {\n id: generateId(),\n role: 'user',\n content,\n timestamp: new Date().toISOString(),\n };\n addMessage(conversationId, userMessage);\n\n const assistantMessageId = generateId();\n addMessage(conversationId, {\n id: assistantMessageId,\n role: 'assistant',\n content: '',\n timestamp: new Date().toISOString(),\n });\n\n const applyAssistantResponse = (\n response: string,\n artifacts?: AssistantArtifact[],\n sessionId?: string,\n sandboxId?: string\n ) => {\n patchMessage(conversationId, assistantMessageId, {\n content: sanitizeAssistantResponse(response),\n metadata:\n artifacts?.length || sessionId || sandboxId\n ? {\n artifacts,\n mode: selectedMode,\n sessionId,\n sandboxId,\n }\n : undefined,\n });\n };\n\n const sendWithRealBackend = async (attempt: 1 | 2 = 1): Promise<void> => {\n try {\n const { sandboxId, sessionId } = await ensureAssistantRuntime(\n conversationId,\n assistantMessageId,\n selectedMode,\n workspaceId,\n authContext\n );\n\n updateConversationStage(conversationId, assistantMessageId, 'streaming', 'Thinking...');\n const promptStartedAt = Date.now();\n await sendAssistantPromptAsync(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n content,\n selectedMode,\n gatherPageContext(),\n authContext\n );\n\n let result = buildAssistantProgress([], promptStartedAt);\n const timeoutAt = Date.now() + 180_000;\n let lastTtlExtensionAt = Date.now();\n\n while (Date.now() < timeoutAt) {\n const sessionMessages = await getAssistantMessages(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n authContext,\n 50\n );\n\n result = buildAssistantProgress(sessionMessages, promptStartedAt);\n patchMessage(conversationId, assistantMessageId, {\n content: sanitizeAssistantResponse(result.content),\n });\n\n if (result.done) break;\n\n if (Date.now() - lastTtlExtensionAt > 30_000) {\n await extendAssistantSandboxTTL(\n apollo,\n sandboxId,\n workspaceId,\n 600,\n authContext\n ).catch(() => undefined);\n lastTtlExtensionAt = Date.now();\n }\n\n await new Promise((resolve) => setTimeout(resolve, 1500));\n }\n\n if (!result.done) {\n throw new Error('Assistant response timed out while streaming progress');\n }\n\n const artifacts =\n selectedMode === 'vibe-plugins'\n ? await getAssistantArtifacts(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n authContext\n ).catch(() => [])\n : undefined;\n\n applyAssistantResponse(result.content, artifacts, sessionId, sandboxId);\n\n if (result.tokens) {\n patchMessage(conversationId, assistantMessageId, { tokens: result.tokens });\n }\n setConversationProcessing(conversationId, 'complete', assistantMessageId, null);\n } catch (error) {\n if (attempt === 1 && isRecoverableAssistantError(error)) {\n clearBackendSessionId(conversationId);\n clearSandbox(selectedMode);\n return sendWithRealBackend(2);\n }\n\n throw error;\n }\n };\n\n try {\n if (!workspaceId) {\n throw new Error('Assistant backend is unavailable without an active workspace context.');\n }\n\n {\n const localResponse = await resolveLocalPromptByMode(selectedMode, content);\n if (localResponse) {\n applyAssistantResponse(localResponse);\n setConversationProcessing(conversationId, 'complete', assistantMessageId, null);\n return;\n }\n }\n\n await sendWithRealBackend();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Something went wrong';\n\n {\n const localResponse = await resolveLocalPromptByMode(selectedMode, content).catch(\n () => null\n );\n if (localResponse) {\n applyAssistantResponse(localResponse);\n setConversationProcessing(conversationId, 'complete', assistantMessageId, null);\n return;\n }\n }\n\n setConversationProcessing(conversationId, 'error', assistantMessageId, errorMessage);\n updateMessage(\n conversationId,\n assistantMessageId,\n `Sorry, an error occurred: ${errorMessage}. Please try again.`\n );\n }\n },\n [\n activeConversationId,\n addMessage,\n apollo,\n clearBackendSessionId,\n clearSandbox,\n conversations,\n createConversation,\n ensureAssistantRuntime,\n getAccessToken,\n getWorkspaceToken,\n patchMessage,\n processingByConversationId,\n setBackendSessionId,\n setConversationMode,\n setConversationProcessing,\n resolveLocalPromptByMode,\n updateConversationStage,\n updateMessage,\n userId,\n ]\n );\n\n const handleSuggestionClick = useCallback(\n (suggestion: string) => {\n void handleSend(suggestion);\n },\n [handleSend]\n );\n\n // Auto-send any prompt that was queued by `useOpenAssistantHelp` (from\n // the shared GetHelpButton). We keep the latest handleSend in a ref so\n // the subscription effect below doesn't need to tear down on every\n // render — the store subscription lives for the component lifetime and\n // always calls the freshest handler.\n const handleSendRef = useRef(handleSend);\n useEffect(() => {\n handleSendRef.current = handleSend;\n }, [handleSend]);\n\n useEffect(() => {\n // Flush any prompt that was queued before this component mounted.\n const initial = useAssistantStore.getState().consumePendingPrompt();\n if (initial) {\n void handleSendRef.current(initial);\n }\n\n // Watch for new prompts queued while we're already mounted.\n const unsubscribe = useAssistantStore.subscribe((state, prev) => {\n if (state.pendingPrompt && state.pendingPrompt !== prev.pendingPrompt) {\n const queued = useAssistantStore.getState().consumePendingPrompt();\n if (queued) {\n void handleSendRef.current(queued);\n }\n }\n });\n return unsubscribe;\n }, []);\n\n const handleModeChange = useCallback(\n (newMode: AssistantMode) => {\n const currentConversation = activeConversationId\n ? useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === activeConversationId)\n : undefined;\n\n if (\n currentConversation &&\n (currentConversation.messages.length > 0 || currentConversation.backendSessionId)\n ) {\n if (currentConversation.mode !== newMode) {\n const reusableDraft = useAssistantStore\n .getState()\n .conversations.find(\n (conversation) =>\n conversation.mode === newMode &&\n conversation.messages.length === 0 &&\n !conversation.backendSessionId &&\n !useAssistantStore.getState().processingByConversationId[conversation.id]\n );\n\n if (reusableDraft) {\n setActiveConversation(reusableDraft.id);\n } else {\n createConversation(undefined, newMode);\n }\n }\n } else if (activeConversationId) {\n setConversationMode(activeConversationId, newMode);\n }\n\n setMode(newMode);\n },\n [activeConversationId, createConversation, setActiveConversation, setConversationMode, setMode]\n );\n\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n {messages.length === 0 ? (\n <AssistantWelcome mode={mode} onSuggestionClick={handleSuggestionClick} />\n ) : (\n <AssistantMessageList messages={messages} streamingMessageId={activeStreamingMessageId} />\n )}\n\n <div>\n <div className=\"px-3 pb-1\">\n <AssistantModeSelector mode={mode} onModeChange={handleModeChange} />\n </div>\n <AssistantChatInput\n onSend={(value) => void handleSend(value)}\n disabled={activeIsProcessing}\n placeholder={tr(\n 'vibecontrols.assistant.chatArea.placeholder',\n `Ask about ${mode.replace(/-/g, ' ')}...`\n )}\n />\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"AssistantChatArea.js","names":[],"sources":["../../../src/components/assistant/AssistantChatArea.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, type FC } from 'react';\nimport { useGatewayClient } from '@burdenoff/fe-libs/shared/graphql';\nimport { useAuthToken } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { AssistantChatInput } from './AssistantChatInput';\nimport { AssistantMessageList } from './AssistantMessageList';\nimport { AssistantModeSelector } from './AssistantModeSelector';\nimport { AssistantWelcome } from './AssistantWelcome';\nimport { useAssistantStore } from '@/store/assistantStore';\nimport {\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantArtifacts,\n getAssistantMessages,\n getAssistantSession,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n type AssistantRawMessage,\n type AssistantSandboxAuthContext,\n} from '@/services/assistantApi';\nimport { gatherPageContext } from '@/utils/pageContext';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type {\n AssistantArtifact,\n AssistantConversationStatus,\n AssistantMode,\n Message,\n} from '@/types/assistant';\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nfunction getProfileContextValue(key: 'workspaceId' | 'organizationId'): string {\n try {\n const activeContextKey =\n key === 'workspaceId'\n ? 'burdenoff-active-context-workspace'\n : 'burdenoff-active-context-organization';\n const activeContextValue = localStorage.getItem(activeContextKey);\n if (activeContextValue) return activeContextValue;\n\n const activeProfileId = sessionStorage.getItem('bf-active-profile');\n if (!activeProfileId) return '';\n const raw = localStorage.getItem(`bf-p-${activeProfileId}-context`);\n if (!raw) return '';\n const context = JSON.parse(raw) as {\n workspaceId?: string;\n organizationId?: string;\n };\n return context[key] ?? '';\n } catch {\n return '';\n }\n}\n\nfunction getWorkspaceId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('workspace') ?? getProfileContextValue('workspaceId');\n}\n\nfunction getOrganizationId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('org') ?? getProfileContextValue('organizationId');\n}\n\nfunction isRecoverableAssistantError(error: unknown): boolean {\n const message =\n error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n\n if (\n message.includes('rate limit exceeded') ||\n message.includes('unauthorized') ||\n message.includes('k8s api error 401')\n ) {\n return false;\n }\n\n return [\n 'sandbox not found',\n 'sandbox is not running',\n 'sandbox service not available yet',\n 'sandbox failed',\n 'sandbox startup timed out',\n 'assistant service did not become healthy',\n 'proxy error: 404',\n 'proxy error: 502',\n 'proxy error: 503',\n 'session mode mismatch',\n ].some((fragment) => message.includes(fragment));\n}\n\nfunction sanitizeAssistantResponse(response: string): string {\n return response\n .replace(/(Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/(X-Workspace-Authorization\\s*:\\s*Bearer\\s+)[^\\s\\n]+/gi, '$1[REDACTED]')\n .replace(/\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9._-]+\\.[A-Za-z0-9._-]+\\b/g, '[REDACTED_JWT]')\n .replace(/\\bsk-ant-[A-Za-z0-9-]+\\b/g, '[REDACTED_API_KEY]');\n}\n\nconst VIBECONTROLS_HELP_TOPICS: Record<string, string> = {\n action:\n 'An Action is a reusable operation VibeControls can run against a target, vibe, session, or automation flow. Create one from VibeControls > Actions, choose the action type, configure the inputs and permissions, then save and run it from the relevant page.',\n component:\n 'A Component represents a software unit such as a service, frontend, library, API, database, or infrastructure asset. Add one from VibeControls > Components, fill in the metadata, lifecycle, owner, language/framework, and tags, then attach it to catalogs or vibes as needed.',\n vibe: 'A Vibe is a workspace project/repository context managed by VibeControls. Create one from VibeControls > Vibes, select the type and path, optionally attach an agent, then use its detail tabs for sessions, docs, catalogs, GitOps, and related resources.',\n session:\n 'A Session is a managed terminal/workflow runtime connected to an agent. Open VibeControls > Sessions, create or start a session, then use terminal tabs to interact with it, share it, restart it, or bookmark a specific session tab from the URL.',\n target:\n 'A Target is a machine or runtime endpoint that an agent can manage. Register or inspect targets from VibeControls > Targets & Agents, then use them for sessions, actions, tunnels, and vibe operations.',\n};\n\nfunction resolveVibeControlsHelpPrompt(prompt: string): string | null {\n const normalized = prompt.toLowerCase();\n if (!normalized.includes('vibecontrols') && !normalized.includes('vibe controls')) {\n return null;\n }\n\n const topic = Object.keys(VIBECONTROLS_HELP_TOPICS).find((key) =>\n new RegExp(`\\\\b${key}s?\\\\b`).test(normalized)\n );\n\n if (!topic) return null;\n return VIBECONTROLS_HELP_TOPICS[topic] ?? null;\n}\n\nfunction getRawMessageCreatedAt(message: AssistantRawMessage): number {\n return message.info?.time?.created ?? 0;\n}\n\nfunction getAssistantText(parts: AssistantRawMessage['parts']): string {\n return (parts ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text?.trim() ?? '')\n .filter(Boolean)\n .join('\\n');\n}\n\nfunction getToolProgress(parts: AssistantRawMessage['parts']): string[] {\n return (parts ?? [])\n .filter((part) => part.type === 'tool' && part.tool)\n .map((part) => {\n const status = part.state?.status ?? 'running';\n const tool = part.tool ?? 'tool';\n if (status === 'completed') return `Completed: ${tool}`;\n if (status === 'failed') return `Failed: ${tool}`;\n return `Running: ${tool}`;\n });\n}\n\nfunction buildAssistantProgress(\n messages: AssistantRawMessage[],\n sinceMs: number\n): {\n content: string;\n done: boolean;\n tokens?: { input: number; output: number };\n} {\n const relevant = messages\n .filter(\n (message) => message.info?.role === 'assistant' && getRawMessageCreatedAt(message) >= sinceMs\n )\n .sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n if (relevant.length === 0) {\n return { content: 'Thinking...', done: false };\n }\n\n const latest = relevant[relevant.length - 1]!;\n const text = getAssistantText(latest.parts);\n const toolProgress = getToolProgress(latest.parts);\n const content = [text, ...toolProgress].filter(Boolean).join('\\n\\n') || 'Thinking...';\n\n return {\n content,\n done: Boolean(latest.info?.time?.completed) && (Boolean(text) || toolProgress.length > 0),\n tokens: latest.info?.tokens,\n };\n}\n\nfunction getConversationStatusMessage(status: AssistantConversationStatus): string {\n switch (status) {\n case 'provisioning':\n return 'Starting assistant environment...';\n case 'connecting':\n return 'Connecting to assistant...';\n case 'streaming':\n return 'Thinking...';\n default:\n return '';\n }\n}\n\nfunction isWorkspaceListPrompt(prompt: string): boolean {\n const normalized = prompt.toLowerCase();\n return (\n normalized.includes('workspace') &&\n (normalized.includes('access') ||\n normalized.includes('authorized') ||\n normalized.includes('list'))\n );\n}\n\nfunction isApiCapabilitiesPrompt(prompt: string): boolean {\n const normalized = prompt.toLowerCase();\n return (\n normalized.includes('what api calls') ||\n normalized.includes('what can you do') ||\n normalized.includes('which api calls')\n );\n}\n\nfunction isProfileNamePrompt(prompt: string): boolean {\n const normalized = prompt.toLowerCase();\n return normalized.includes('what is my name') || normalized.includes('my profile name');\n}\n\nfunction extractTargetNamePrompt(prompt: string): string | null {\n const match = prompt.match(/create\\s+a\\s+target\\s+named\\s+(.+)/i);\n return match?.[1]?.trim() ?? null;\n}\n\nfunction getLocalProfileName(): { name?: string; email?: string } {\n try {\n const profileId = sessionStorage.getItem('bf-active-profile');\n if (!profileId) return {};\n const raw = localStorage.getItem(`bf-p-${profileId}-user`);\n if (!raw) return {};\n const user = JSON.parse(raw) as {\n username?: string;\n name?: string;\n firstName?: string;\n email?: string;\n };\n return {\n name: user.name || user.username || user.firstName,\n email: user.email,\n };\n } catch {\n return {};\n }\n}\n\nfunction isGeneralCapabilitiesPrompt(prompt: string): boolean {\n return prompt.toLowerCase().trim() === 'what can you do';\n}\n\nexport const AssistantChatArea: FC = () => {\n const tr = useTr();\n const apollo = useGatewayClient('workspace');\n const { getAccessToken, getWorkspaceToken, userId } = useAuthToken();\n\n const {\n conversations,\n activeConversationId,\n mode,\n processingByConversationId,\n setMode,\n setConversationMode,\n setActiveConversation,\n createConversation,\n addMessage,\n updateMessage,\n patchMessage,\n setConversationProcessing,\n clearConversationProcessing,\n setBackendSessionId,\n clearBackendSessionId,\n setSandbox,\n clearSandbox,\n } = useAssistantStore();\n\n const activeConversation = conversations.find(\n (conversation) => conversation.id === activeConversationId\n );\n const messages = activeConversation?.messages ?? [];\n const activeProcessing = activeConversationId\n ? processingByConversationId[activeConversationId]\n : undefined;\n const activeStreamingMessageId = activeProcessing?.messageId ?? null;\n const activeIsProcessing =\n activeProcessing?.status === 'provisioning' ||\n activeProcessing?.status === 'connecting' ||\n activeProcessing?.status === 'streaming';\n\n const updateConversationStage = useCallback(\n (\n conversationId: string,\n assistantMessageId: string,\n status: AssistantConversationStatus,\n content?: string\n ) => {\n setConversationProcessing(conversationId, status, assistantMessageId, null);\n if (content) {\n updateMessage(conversationId, assistantMessageId, content);\n } else {\n updateMessage(conversationId, assistantMessageId, getConversationStatusMessage(status));\n }\n },\n [setConversationProcessing, updateMessage]\n );\n\n const resolveApiCallsPromptLocally = useCallback(\n async (prompt: string): Promise<string | null> => {\n if (isWorkspaceListPrompt(prompt)) {\n const workspaceId = getWorkspaceId();\n const organizationId = getOrganizationId();\n\n if (!workspaceId) {\n return 'No authorized workspace is attached to this assistant session yet.';\n }\n\n const publicUrl = organizationId\n ? `https://api.${organizationId}.burdenoff.com/workspaces/graphql`\n : '';\n\n return `This assistant sandbox is authorized for 1 workspace:\\n\\n1. Current active workspace — ${workspaceId}${publicUrl ? `\\n Public URL: ${publicUrl}` : ''}`;\n }\n\n if (isProfileNamePrompt(prompt)) {\n const profile = getLocalProfileName();\n if (!profile.name && !profile.email) {\n return 'I could not determine your profile name from the local session.';\n }\n\n const nameLine = profile.name ? `Your profile name is ${profile.name}.` : '';\n const emailLine = profile.email ? `Your profile email is ${profile.email}.` : '';\n return [nameLine, emailLine].filter(Boolean).join('\\n');\n }\n\n const requestedTargetName = extractTargetNamePrompt(prompt);\n if (requestedTargetName) {\n return [\n `I can create the target \"${requestedTargetName}\", but I still need the target connection details before I can execute the mutation.`,\n '',\n 'Please provide at least:',\n '- target type (SSH, RDP, VNC, or DIRECT)',\n '- host or IP address',\n '- port if it is not the default for that protocol',\n '- any credentials or auth method the target requires',\n '',\n `For example: create a target named ${requestedTargetName} using SSH at 10.0.0.15:22 with username ubuntu.`,\n ].join('\\n');\n }\n\n if (isApiCapabilitiesPrompt(prompt)) {\n return [\n 'I can query or mutate both Burdenoff GraphQL gateways on your behalf:',\n '',\n '- Global gateway: organizations, workspaces, apps, billing, notifications, store, support, and dev portal operations',\n '- Workspace gateway: workspace-scoped modules like vibecontrols, files, tags, conversations, calendar, sandbox, and related resources',\n '',\n 'Ask me for a specific action like listing your workspaces, fetching a resource, or creating/updating something through the APIs.',\n ].join('\\n');\n }\n\n return null;\n },\n []\n );\n\n const resolveLocalPromptByMode = useCallback(\n async (currentMode: AssistantMode, prompt: string): Promise<string | null> => {\n if (currentMode === 'api-calls') {\n return resolveApiCallsPromptLocally(prompt);\n }\n\n if (currentMode === 'general' && isGeneralCapabilitiesPrompt(prompt)) {\n return [\n 'I can help explain VibeControls concepts, navigation, sessions, targets, tunnels, and platform workflows.',\n '',\n 'I can also guide you through the UI, describe features, and help you understand how different parts of the product fit together.',\n ].join('\\n');\n }\n\n if (currentMode === 'vibe-plugins' && isGeneralCapabilitiesPrompt(prompt)) {\n return [\n 'I can help design, generate, explain, and package Vibe Plugins.',\n '',\n 'I can create plugin scaffolds, explain manifests and lifecycle hooks, and provide downloadable plugin artifacts for this session.',\n ].join('\\n');\n }\n\n const helpResponse = resolveVibeControlsHelpPrompt(prompt);\n if (helpResponse) return helpResponse;\n\n return null;\n },\n [resolveApiCallsPromptLocally]\n );\n\n const ensureAssistantRuntime = useCallback(\n async (\n conversationId: string,\n assistantMessageId: string,\n currentMode: AssistantMode,\n workspaceId: string,\n authContext: AssistantSandboxAuthContext\n ): Promise<{ sandboxId: string; sessionId: string }> => {\n let currentSandboxId =\n useAssistantStore.getState().sandboxesByMode[currentMode]?.sandboxId ?? null;\n\n if (!currentSandboxId) {\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Looking for existing environment...'\n );\n const existingId = await findExistingSandbox(apollo, workspaceId, currentMode, authContext);\n\n if (existingId) {\n currentSandboxId = existingId;\n setSandbox(currentMode, currentSandboxId, 'provisioning');\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Reusing assistant environment...'\n );\n await waitForSandboxReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 300_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'provisioning', message)\n );\n await waitForAssistantServiceReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 90_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'connecting', message)\n );\n setSandbox(currentMode, currentSandboxId, 'running');\n } else {\n if (currentMode === 'api-calls' && !getAccessToken()) {\n throw new Error(\n 'API Calls mode requires an authenticated session. Please sign in again.'\n );\n }\n\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'provisioning',\n 'Starting assistant environment...'\n );\n setSandbox(currentMode, null, 'provisioning');\n currentSandboxId = await createAssistantSandbox(\n apollo,\n currentMode,\n workspaceId,\n authContext\n );\n\n setSandbox(currentMode, currentSandboxId, 'provisioning');\n await waitForSandboxReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 300_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'provisioning', message)\n );\n await waitForAssistantServiceReady(\n apollo,\n currentSandboxId,\n workspaceId,\n authContext,\n 90_000,\n (message) =>\n updateConversationStage(conversationId, assistantMessageId, 'connecting', message)\n );\n setSandbox(currentMode, currentSandboxId, 'running');\n }\n }\n\n let sessionId =\n useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === conversationId)\n ?.backendSessionId ?? null;\n\n if (sessionId) {\n try {\n const session = await getAssistantSession(\n apollo,\n currentSandboxId,\n workspaceId,\n sessionId,\n authContext\n );\n if (session.mode !== currentMode) {\n throw new Error(\n `Session mode mismatch: expected ${currentMode}, received ${session.mode}`\n );\n }\n } catch {\n clearBackendSessionId(conversationId);\n sessionId = null;\n }\n }\n\n if (!sessionId) {\n updateConversationStage(\n conversationId,\n assistantMessageId,\n 'connecting',\n 'Preparing session...'\n );\n const result = await createAssistantSession(\n apollo,\n currentSandboxId,\n workspaceId,\n currentMode,\n authContext\n );\n sessionId = result.sessionId;\n setBackendSessionId(conversationId, sessionId);\n }\n\n return {\n sandboxId: currentSandboxId,\n sessionId,\n };\n },\n [\n apollo,\n clearBackendSessionId,\n getAccessToken,\n setBackendSessionId,\n setSandbox,\n updateConversationStage,\n ]\n );\n\n const handleSend = useCallback(\n async (content: string) => {\n if (activeConversationId && processingByConversationId[activeConversationId]) return;\n\n const selectedMode = useAssistantStore.getState().mode;\n let conversationId = activeConversationId;\n const currentConversation = conversationId\n ? useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === conversationId)\n : undefined;\n\n if (!conversationId) {\n conversationId = createConversation(undefined, selectedMode);\n } else if (\n currentConversation &&\n currentConversation.mode !== selectedMode &&\n (currentConversation.messages.length > 0 || currentConversation.backendSessionId)\n ) {\n conversationId = createConversation(undefined, selectedMode);\n } else if (currentConversation && currentConversation.mode !== selectedMode) {\n setConversationMode(conversationId, selectedMode);\n }\n\n if (!conversationId) return;\n\n const workspaceId = getWorkspaceId();\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n\n const userMessage: Message = {\n id: generateId(),\n role: 'user',\n content,\n timestamp: new Date().toISOString(),\n };\n addMessage(conversationId, userMessage);\n\n const assistantMessageId = generateId();\n addMessage(conversationId, {\n id: assistantMessageId,\n role: 'assistant',\n content: '',\n timestamp: new Date().toISOString(),\n });\n\n const applyAssistantResponse = (\n response: string,\n artifacts?: AssistantArtifact[],\n sessionId?: string,\n sandboxId?: string\n ) => {\n patchMessage(conversationId, assistantMessageId, {\n content: sanitizeAssistantResponse(response),\n metadata:\n artifacts?.length || sessionId || sandboxId\n ? {\n artifacts,\n mode: selectedMode,\n sessionId,\n sandboxId,\n }\n : undefined,\n });\n };\n\n const sendWithRealBackend = async (attempt: 1 | 2 = 1): Promise<void> => {\n try {\n const { sandboxId, sessionId } = await ensureAssistantRuntime(\n conversationId,\n assistantMessageId,\n selectedMode,\n workspaceId,\n authContext\n );\n\n updateConversationStage(conversationId, assistantMessageId, 'streaming', 'Thinking...');\n const promptStartedAt = Date.now();\n await sendAssistantPromptAsync(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n content,\n selectedMode,\n gatherPageContext(),\n authContext\n );\n\n let result = buildAssistantProgress([], promptStartedAt);\n const timeoutAt = Date.now() + 180_000;\n let lastTtlExtensionAt = Date.now();\n\n while (Date.now() < timeoutAt) {\n const sessionMessages = await getAssistantMessages(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n authContext,\n 50\n );\n\n result = buildAssistantProgress(sessionMessages, promptStartedAt);\n patchMessage(conversationId, assistantMessageId, {\n content: sanitizeAssistantResponse(result.content),\n });\n\n if (result.done) break;\n\n if (Date.now() - lastTtlExtensionAt > 30_000) {\n await extendAssistantSandboxTTL(\n apollo,\n sandboxId,\n workspaceId,\n 600,\n authContext\n ).catch(() => undefined);\n lastTtlExtensionAt = Date.now();\n }\n\n await new Promise((resolve) => setTimeout(resolve, 1500));\n }\n\n if (!result.done) {\n throw new Error('Assistant response timed out while streaming progress');\n }\n\n const artifacts =\n selectedMode === 'vibe-plugins'\n ? await getAssistantArtifacts(\n apollo,\n sandboxId,\n workspaceId,\n sessionId,\n authContext\n ).catch(() => [])\n : undefined;\n\n applyAssistantResponse(result.content, artifacts, sessionId, sandboxId);\n\n if (result.tokens) {\n patchMessage(conversationId, assistantMessageId, { tokens: result.tokens });\n }\n setConversationProcessing(conversationId, 'complete', assistantMessageId, null);\n } catch (error) {\n if (attempt === 1 && isRecoverableAssistantError(error)) {\n clearBackendSessionId(conversationId);\n clearSandbox(selectedMode);\n return sendWithRealBackend(2);\n }\n\n throw error;\n }\n };\n\n try {\n if (!workspaceId) {\n throw new Error('Assistant backend is unavailable without an active workspace context.');\n }\n\n {\n const localResponse = await resolveLocalPromptByMode(selectedMode, content);\n if (localResponse) {\n applyAssistantResponse(localResponse);\n setConversationProcessing(conversationId, 'complete', assistantMessageId, null);\n return;\n }\n }\n\n await sendWithRealBackend();\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Something went wrong';\n\n {\n const localResponse = await resolveLocalPromptByMode(selectedMode, content).catch(\n () => null\n );\n if (localResponse) {\n applyAssistantResponse(localResponse);\n setConversationProcessing(conversationId, 'complete', assistantMessageId, null);\n return;\n }\n }\n\n setConversationProcessing(conversationId, 'error', assistantMessageId, errorMessage);\n updateMessage(\n conversationId,\n assistantMessageId,\n `Sorry, an error occurred: ${errorMessage}. Please try again.`\n );\n }\n },\n [\n activeConversationId,\n addMessage,\n apollo,\n clearBackendSessionId,\n clearSandbox,\n conversations,\n createConversation,\n ensureAssistantRuntime,\n getAccessToken,\n getWorkspaceToken,\n patchMessage,\n processingByConversationId,\n setBackendSessionId,\n setConversationMode,\n setConversationProcessing,\n resolveLocalPromptByMode,\n updateConversationStage,\n updateMessage,\n userId,\n ]\n );\n\n const handleSuggestionClick = useCallback(\n (suggestion: string) => {\n void handleSend(suggestion);\n },\n [handleSend]\n );\n\n // Auto-send any prompt that was queued by `useOpenAssistantHelp` (from\n // the shared GetHelpButton). We keep the latest handleSend in a ref so\n // the subscription effect below doesn't need to tear down on every\n // render — the store subscription lives for the component lifetime and\n // always calls the freshest handler.\n const handleSendRef = useRef(handleSend);\n useEffect(() => {\n handleSendRef.current = handleSend;\n }, [handleSend]);\n\n useEffect(() => {\n // Flush any prompt that was queued before this component mounted.\n const initial = useAssistantStore.getState().consumePendingPrompt();\n if (initial) {\n void handleSendRef.current(initial);\n }\n\n // Watch for new prompts queued while we're already mounted.\n const unsubscribe = useAssistantStore.subscribe((state, prev) => {\n if (state.pendingPrompt && state.pendingPrompt !== prev.pendingPrompt) {\n const queued = useAssistantStore.getState().consumePendingPrompt();\n if (queued) {\n void handleSendRef.current(queued);\n }\n }\n });\n return unsubscribe;\n }, []);\n\n const handleModeChange = useCallback(\n (newMode: AssistantMode) => {\n const currentConversation = activeConversationId\n ? useAssistantStore\n .getState()\n .conversations.find((conversation) => conversation.id === activeConversationId)\n : undefined;\n\n if (\n currentConversation &&\n (currentConversation.messages.length > 0 || currentConversation.backendSessionId)\n ) {\n if (currentConversation.mode !== newMode) {\n const reusableDraft = useAssistantStore\n .getState()\n .conversations.find(\n (conversation) =>\n conversation.mode === newMode &&\n conversation.messages.length === 0 &&\n !conversation.backendSessionId &&\n !useAssistantStore.getState().processingByConversationId[conversation.id]\n );\n\n if (reusableDraft) {\n setActiveConversation(reusableDraft.id);\n } else {\n createConversation(undefined, newMode);\n }\n }\n } else if (activeConversationId) {\n setConversationMode(activeConversationId, newMode);\n }\n\n setMode(newMode);\n },\n [activeConversationId, createConversation, setActiveConversation, setConversationMode, setMode]\n );\n\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden pt-3\">\n {messages.length === 0 ? (\n <AssistantWelcome mode={mode} onSuggestionClick={handleSuggestionClick} />\n ) : (\n <AssistantMessageList messages={messages} streamingMessageId={activeStreamingMessageId} />\n )}\n\n <div>\n <div className=\"px-3 pb-1\">\n <AssistantModeSelector mode={mode} onModeChange={handleModeChange} />\n </div>\n <AssistantChatInput\n onSend={(value) => void handleSend(value)}\n disabled={activeIsProcessing}\n placeholder={tr(\n 'vibecontrols.assistant.chatArea.placeholder',\n `Ask about ${mode.replace(/-/g, ' ')}...`\n )}\n />\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;;;;;;;;AA+BA,SAAS,IAAqB;AAC5B,QAAO,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;;AAGhE,SAAS,EAAuB,GAA+C;AAC7E,KAAI;EACF,IAAM,IACJ,MAAQ,gBACJ,uCACA,yCACA,IAAqB,aAAa,QAAQ,EAAiB;AACjE,MAAI,EAAoB,QAAO;EAE/B,IAAM,IAAkB,eAAe,QAAQ,oBAAoB;AACnE,MAAI,CAAC,EAAiB,QAAO;EAC7B,IAAM,IAAM,aAAa,QAAQ,QAAQ,EAAgB,UAAU;AAMnE,SALK,IACW,KAAK,MAAM,EAAI,CAIhB,MAAQ,KALN;SAMX;AACN,SAAO;;;AAIX,SAAS,IAAyB;AAEhC,QADe,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC5C,IAAI,YAAY,IAAI,EAAuB,cAAc;;AAGzE,SAAS,IAA4B;AAEnC,QADe,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC5C,IAAI,MAAM,IAAI,EAAuB,iBAAiB;;AAGtE,SAAS,GAA4B,GAAyB;CAC5D,IAAM,IACJ,aAAiB,QAAQ,EAAM,QAAQ,aAAa,GAAG,OAAO,EAAM,CAAC,aAAa;AAUpF,QAPE,EAAQ,SAAS,sBAAsB,IACvC,EAAQ,SAAS,eAAe,IAChC,EAAQ,SAAS,oBAAoB,GAE9B,KAGF;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,MAAM,MAAa,EAAQ,SAAS,EAAS,CAAC;;AAGlD,SAAS,EAA0B,GAA0B;AAC3D,QAAO,EACJ,QAAQ,6CAA6C,eAAe,CACpE,QAAQ,yDAAyD,eAAe,CAChF,QAAQ,4DAA4D,iBAAiB,CACrF,QAAQ,6BAA6B,qBAAqB;;AAG/D,IAAM,IAAmD;CACvD,QACE;CACF,WACE;CACF,MAAM;CACN,SACE;CACF,QACE;CACH;AAED,SAAS,GAA8B,GAA+B;CACpE,IAAM,IAAa,EAAO,aAAa;AACvC,KAAI,CAAC,EAAW,SAAS,eAAe,IAAI,CAAC,EAAW,SAAS,gBAAgB,CAC/E,QAAO;CAGT,IAAM,IAAQ,OAAO,KAAK,EAAyB,CAAC,MAAM,MACpD,OAAO,MAAM,EAAI,OAAO,CAAC,KAAK,EAAW,CAC9C;AAGD,QADK,IACE,EAAyB,MAAU,OADvB;;AAIrB,SAAS,EAAuB,GAAsC;AACpE,QAAO,EAAQ,MAAM,MAAM,WAAW;;AAGxC,SAAS,EAAiB,GAA6C;AACrE,SAAQ,KAAS,EAAE,EAChB,QAAQ,MAAS,EAAK,SAAS,UAAU,OAAO,EAAK,QAAS,SAAS,CACvE,KAAK,MAAS,EAAK,MAAM,MAAM,IAAI,GAAG,CACtC,OAAO,QAAQ,CACf,KAAK,KAAK;;AAGf,SAAS,EAAgB,GAA+C;AACtE,SAAQ,KAAS,EAAE,EAChB,QAAQ,MAAS,EAAK,SAAS,UAAU,EAAK,KAAK,CACnD,KAAK,MAAS;EACb,IAAM,IAAS,EAAK,OAAO,UAAU,WAC/B,IAAO,EAAK,QAAQ;AAG1B,SAFI,MAAW,cAAoB,cAAc,MAC7C,MAAW,WAAiB,WAAW,MACpC,YAAY;GACnB;;AAGN,SAAS,EACP,GACA,GAKA;CACA,IAAM,IAAW,EACd,QACE,MAAY,EAAQ,MAAM,SAAS,eAAe,EAAuB,EAAQ,IAAI,EACvF,CACA,MAAM,GAAM,MAAU,EAAuB,EAAK,GAAG,EAAuB,EAAM,CAAC;AAEtF,KAAI,EAAS,WAAW,EACtB,QAAO;EAAE,SAAS;EAAe,MAAM;EAAO;CAGhD,IAAM,IAAS,EAAS,EAAS,SAAS,IACpC,IAAO,EAAiB,EAAO,MAAM,EACrC,IAAe,EAAgB,EAAO,MAAM;AAGlD,QAAO;EACL,SAHc,CAAC,GAAM,GAAG,EAAa,CAAC,OAAO,QAAQ,CAAC,KAAK,OAAO,IAAI;EAItE,MAAM,EAAQ,EAAO,MAAM,MAAM,cAAe,EAAQ,KAAS,EAAa,SAAS;EACvF,QAAQ,EAAO,MAAM;EACtB;;AAGH,SAAS,GAA6B,GAA6C;AACjF,SAAQ,GAAR;EACE,KAAK,eACH,QAAO;EACT,KAAK,aACH,QAAO;EACT,KAAK,YACH,QAAO;EACT,QACE,QAAO;;;AAIb,SAAS,GAAsB,GAAyB;CACtD,IAAM,IAAa,EAAO,aAAa;AACvC,QACE,EAAW,SAAS,YAAY,KAC/B,EAAW,SAAS,SAAS,IAC5B,EAAW,SAAS,aAAa,IACjC,EAAW,SAAS,OAAO;;AAIjC,SAAS,GAAwB,GAAyB;CACxD,IAAM,IAAa,EAAO,aAAa;AACvC,QACE,EAAW,SAAS,iBAAiB,IACrC,EAAW,SAAS,kBAAkB,IACtC,EAAW,SAAS,kBAAkB;;AAI1C,SAAS,GAAoB,GAAyB;CACpD,IAAM,IAAa,EAAO,aAAa;AACvC,QAAO,EAAW,SAAS,kBAAkB,IAAI,EAAW,SAAS,kBAAkB;;AAGzF,SAAS,GAAwB,GAA+B;AAE9D,QADc,EAAO,MAAM,sCAAsC,GAClD,IAAI,MAAM,IAAI;;AAG/B,SAAS,KAAyD;AAChE,KAAI;EACF,IAAM,IAAY,eAAe,QAAQ,oBAAoB;AAC7D,MAAI,CAAC,EAAW,QAAO,EAAE;EACzB,IAAM,IAAM,aAAa,QAAQ,QAAQ,EAAU,OAAO;AAC1D,MAAI,CAAC,EAAK,QAAO,EAAE;EACnB,IAAM,IAAO,KAAK,MAAM,EAAI;AAM5B,SAAO;GACL,MAAM,EAAK,QAAQ,EAAK,YAAY,EAAK;GACzC,OAAO,EAAK;GACb;SACK;AACN,SAAO,EAAE;;;AAIb,SAAS,EAA4B,GAAyB;AAC5D,QAAO,EAAO,aAAa,CAAC,MAAM,KAAK;;AAGzC,IAAa,UAA8B;CACzC,IAAM,IAAK,GAAO,EACZ,IAAS,GAAiB,YAAY,EACtC,EAAE,mBAAgB,sBAAmB,cAAW,IAAc,EAE9D,EACJ,kBACA,yBACA,SACA,+BACA,YACA,wBACA,0BACA,uBACA,eACA,kBACA,iBACA,8BACA,iCACA,wBACA,0BACA,eACA,oBACE,GAAmB,EAKjB,IAHqB,EAAc,MACtC,MAAiB,EAAa,OAAO,EACvC,EACoC,YAAY,EAAE,EAC7C,IAAmB,IACrB,EAA2B,KAC3B,KAAA,GACE,KAA2B,GAAkB,aAAa,MAC1D,KACJ,GAAkB,WAAW,kBAC7B,GAAkB,WAAW,gBAC7B,GAAkB,WAAW,aAEzB,IAA0B,GAE5B,GACA,GACA,GACA,MACG;AAEH,EADA,EAA0B,GAAgB,GAAQ,GAAoB,KAAK,EACvE,IACF,EAAc,GAAgB,GAAoB,EAAQ,GAE1D,EAAc,GAAgB,GAAoB,GAA6B,EAAO,CAAC;IAG3F,CAAC,GAA2B,EAAc,CAC3C,EAEK,KAA+B,EACnC,OAAO,MAA2C;AAChD,MAAI,GAAsB,EAAO,EAAE;GACjC,IAAM,IAAc,GAAgB,EAC9B,IAAiB,GAAmB;AAE1C,OAAI,CAAC,EACH,QAAO;GAGT,IAAM,IAAY,IACd,eAAe,EAAe,qCAC9B;AAEJ,UAAO,0FAA0F,IAAc,IAAY,oBAAoB,MAAc;;AAG/J,MAAI,GAAoB,EAAO,EAAE;GAC/B,IAAM,IAAU,IAAqB;AAOrC,UANI,CAAC,EAAQ,QAAQ,CAAC,EAAQ,QACrB,oEAKF,CAFU,EAAQ,OAAO,wBAAwB,EAAQ,KAAK,KAAK,IACxD,EAAQ,QAAQ,yBAAyB,EAAQ,MAAM,KAAK,GAClD,CAAC,OAAO,QAAQ,CAAC,KAAK,KAAK;;EAGzD,IAAM,IAAsB,GAAwB,EAAO;AA0B3D,SAzBI,IACK;GACL,4BAA4B,EAAoB;GAChD;GACA;GACA;GACA;GACA;GACA;GACA;GACA,sCAAsC,EAAoB;GAC3D,CAAC,KAAK,KAAK,GAGV,GAAwB,EAAO,GAC1B;GACL;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK,GAGP;IAET,EAAE,CACH,EAEK,IAA2B,EAC/B,OAAO,GAA4B,MAC7B,MAAgB,cACX,GAA6B,EAAO,GAGzC,MAAgB,aAAa,EAA4B,EAAO,GAC3D;EACL;EACA;EACA;EACD,CAAC,KAAK,KAAK,GAGV,MAAgB,kBAAkB,EAA4B,EAAO,GAChE;EACL;EACA;EACA;EACD,CAAC,KAAK,KAAK,GAGO,GAA8B,EAAO,IAGnD,MAET,CAAC,GAA6B,CAC/B,EAEK,IAAyB,EAC7B,OACE,GACA,GACA,GACA,GACA,MACsD;EACtD,IAAI,IACF,EAAkB,UAAU,CAAC,gBAAgB,IAAc,aAAa;AAE1E,MAAI,CAAC,GAAkB;AACrB,KACE,GACA,GACA,gBACA,sCACD;GACD,IAAM,IAAa,MAAM,EAAoB,GAAQ,GAAa,GAAa,EAAY;AAE3F,OAAI,EA2BF,CA1BA,IAAmB,GACnB,EAAW,GAAa,GAAkB,eAAe,EACzD,EACE,GACA,GACA,gBACA,mCACD,EACD,MAAM,EACJ,GACA,GACA,GACA,GACA,MACC,MACC,EAAwB,GAAgB,GAAoB,gBAAgB,EAAQ,CACvF,EACD,MAAM,EACJ,GACA,GACA,GACA,GACA,MACC,MACC,EAAwB,GAAgB,GAAoB,cAAc,EAAQ,CACrF,EACD,EAAW,GAAa,GAAkB,UAAU;QAC/C;AACL,QAAI,MAAgB,eAAe,CAAC,GAAgB,CAClD,OAAU,MACR,0EACD;AAoCH,IAjCA,EACE,GACA,GACA,gBACA,oCACD,EACD,EAAW,GAAa,MAAM,eAAe,EAC7C,IAAmB,MAAM,EACvB,GACA,GACA,GACA,EACD,EAED,EAAW,GAAa,GAAkB,eAAe,EACzD,MAAM,EACJ,GACA,GACA,GACA,GACA,MACC,MACC,EAAwB,GAAgB,GAAoB,gBAAgB,EAAQ,CACvF,EACD,MAAM,EACJ,GACA,GACA,GACA,GACA,MACC,MACC,EAAwB,GAAgB,GAAoB,cAAc,EAAQ,CACrF,EACD,EAAW,GAAa,GAAkB,UAAU;;;EAIxD,IAAI,IACF,EACG,UAAU,CACV,cAAc,MAAM,MAAiB,EAAa,OAAO,EAAe,EACvE,oBAAoB;AAE1B,MAAI,EACF,KAAI;GACF,IAAM,IAAU,MAAM,EACpB,GACA,GACA,GACA,GACA,EACD;AACD,OAAI,EAAQ,SAAS,EACnB,OAAU,MACR,mCAAmC,EAAY,aAAa,EAAQ,OACrE;UAEG;AAEN,GADA,EAAsB,EAAe,EACrC,IAAY;;AAsBhB,SAlBK,MACH,EACE,GACA,GACA,cACA,uBACD,EAQD,KAPe,MAAM,EACnB,GACA,GACA,GACA,GACA,EACD,EACkB,WACnB,EAAoB,GAAgB,EAAU,GAGzC;GACL,WAAW;GACX;GACD;IAEH;EACE;EACA;EACA;EACA;EACA;EACA;EACD,CACF,EAEK,IAAa,EACjB,OAAO,MAAoB;AACzB,MAAI,KAAwB,EAA2B,GAAuB;EAE9E,IAAM,IAAe,EAAkB,UAAU,CAAC,MAC9C,IAAiB,GACf,IAAsB,IACxB,EACG,UAAU,CACV,cAAc,MAAM,MAAiB,EAAa,OAAO,EAAe,GAC3E,KAAA;AAcJ,MAZK,IAGH,KACA,EAAoB,SAAS,MAC5B,EAAoB,SAAS,SAAS,KAAK,EAAoB,oBAEhE,IAAiB,EAAmB,KAAA,GAAW,EAAa,GACnD,KAAuB,EAAoB,SAAS,KAC7D,EAAoB,GAAgB,EAAa,GARjD,IAAiB,EAAmB,KAAA,GAAW,EAAa,EAW1D,CAAC,EAAgB;EAErB,IAAM,IAAc,GAAgB,EAC9B,IAA2C;GAC/C,aAAa,GAAgB;GAC7B,gBAAgB,GAAmB;GACnC;GACA,gBAAgB,GAAmB;GACpC,EAEK,IAAuB;GAC3B,IAAI,GAAY;GAChB,MAAM;GACN;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACpC;AACD,IAAW,GAAgB,EAAY;EAEvC,IAAM,IAAqB,GAAY;AACvC,IAAW,GAAgB;GACzB,IAAI;GACJ,MAAM;GACN,SAAS;GACT,4BAAW,IAAI,MAAM,EAAC,aAAa;GACpC,CAAC;EAEF,IAAM,KACJ,GACA,GACA,GACA,MACG;AACH,KAAa,GAAgB,GAAoB;IAC/C,SAAS,EAA0B,EAAS;IAC5C,UACE,GAAW,UAAU,KAAa,IAC9B;KACE;KACA,MAAM;KACN;KACA;KACD,GACD,KAAA;IACP,CAAC;KAGE,IAAsB,OAAO,IAAiB,MAAqB;AACvE,OAAI;IACF,IAAM,EAAE,cAAW,iBAAc,MAAM,EACrC,GACA,GACA,GACA,GACA,EACD;AAED,MAAwB,GAAgB,GAAoB,aAAa,cAAc;IACvF,IAAM,IAAkB,KAAK,KAAK;AAClC,UAAM,EACJ,GACA,GACA,GACA,GACA,GACA,GACA,IAAmB,EACnB,EACD;IAED,IAAI,IAAS,EAAuB,EAAE,EAAE,EAAgB,EAClD,IAAY,KAAK,KAAK,GAAG,MAC3B,IAAqB,KAAK,KAAK;AAEnC,WAAO,KAAK,KAAK,GAAG,MAUlB,IAAS,EATe,MAAM,EAC5B,GACA,GACA,GACA,GACA,GACA,GACD,EAEgD,EAAgB,EACjE,EAAa,GAAgB,GAAoB,EAC/C,SAAS,EAA0B,EAAO,QAAQ,EACnD,CAAC,EAEE,GAAO,OAaX,CAXI,KAAK,KAAK,GAAG,IAAqB,QACpC,MAAM,EACJ,GACA,GACA,GACA,KACA,EACD,CAAC,YAAY,KAAA,EAAU,EACxB,IAAqB,KAAK,KAAK,GAGjC,MAAM,IAAI,SAAS,MAAY,WAAW,GAAS,KAAK,CAAC;AAG3D,QAAI,CAAC,EAAO,KACV,OAAU,MAAM,wDAAwD;IAG1E,IAAM,IACJ,MAAiB,iBACb,MAAM,EACJ,GACA,GACA,GACA,GACA,EACD,CAAC,YAAY,EAAE,CAAC,GACjB,KAAA;AAON,IALA,EAAuB,EAAO,SAAS,GAAW,GAAW,EAAU,EAEnE,EAAO,UACT,EAAa,GAAgB,GAAoB,EAAE,QAAQ,EAAO,QAAQ,CAAC,EAE7E,EAA0B,GAAgB,YAAY,GAAoB,KAAK;YACxE,GAAO;AACd,QAAI,MAAY,KAAK,GAA4B,EAAM,CAGrD,QAFA,EAAsB,EAAe,EACrC,EAAa,EAAa,EACnB,EAAoB,EAAE;AAG/B,UAAM;;;AAIV,MAAI;AACF,OAAI,CAAC,EACH,OAAU,MAAM,wEAAwE;GAG1F;IACE,IAAM,IAAgB,MAAM,EAAyB,GAAc,EAAQ;AAC3E,QAAI,GAAe;AAEjB,KADA,EAAuB,EAAc,EACrC,EAA0B,GAAgB,YAAY,GAAoB,KAAK;AAC/E;;;AAIJ,SAAM,GAAqB;WACpB,GAAO;GACd,IAAM,IAAe,aAAiB,QAAQ,EAAM,UAAU;GAE9D;IACE,IAAM,IAAgB,MAAM,EAAyB,GAAc,EAAQ,CAAC,YACpE,KACP;AACD,QAAI,GAAe;AAEjB,KADA,EAAuB,EAAc,EACrC,EAA0B,GAAgB,YAAY,GAAoB,KAAK;AAC/E;;;AAKJ,GADA,EAA0B,GAAgB,SAAS,GAAoB,EAAa,EACpF,EACE,GACA,GACA,6BAA6B,EAAa,qBAC3C;;IAGL;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACF,EAEK,KAAwB,GAC3B,MAAuB;AACjB,IAAW,EAAW;IAE7B,CAAC,EAAW,CACb,EAOK,IAAgB,GAAO,EAAW;AAKxC,CAJA,QAAgB;AACd,IAAc,UAAU;IACvB,CAAC,EAAW,CAAC,EAEhB,QAAgB;EAEd,IAAM,IAAU,EAAkB,UAAU,CAAC,sBAAsB;AAcnE,SAbI,KACG,EAAc,QAAQ,EAAQ,EAIjB,EAAkB,WAAW,GAAO,MAAS;AAC/D,OAAI,EAAM,iBAAiB,EAAM,kBAAkB,EAAK,eAAe;IACrE,IAAM,IAAS,EAAkB,UAAU,CAAC,sBAAsB;AAClE,IAAI,KACG,EAAc,QAAQ,EAAO;;IAGtC;IAED,EAAE,CAAC;CAEN,IAAM,KAAmB,GACtB,MAA2B;EAC1B,IAAM,IAAsB,IACxB,EACG,UAAU,CACV,cAAc,MAAM,MAAiB,EAAa,OAAO,EAAqB,GACjF,KAAA;AAEJ,MACE,MACC,EAAoB,SAAS,SAAS,KAAK,EAAoB;OAE5D,EAAoB,SAAS,GAAS;IACxC,IAAM,IAAgB,EACnB,UAAU,CACV,cAAc,MACZ,MACC,EAAa,SAAS,KACtB,EAAa,SAAS,WAAW,KACjC,CAAC,EAAa,oBACd,CAAC,EAAkB,UAAU,CAAC,2BAA2B,EAAa,IACzE;AAEH,IAAI,IACF,EAAsB,EAAc,GAAG,GAEvC,EAAmB,KAAA,GAAW,EAAQ;;SAGjC,KACT,EAAoB,GAAsB,EAAQ;AAGpD,IAAQ,EAAQ;IAElB;EAAC;EAAsB;EAAoB;EAAuB;EAAqB;EAAQ,CAChG;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACG,EAAS,WAAW,IACnB,kBAAC,GAAD;GAAwB;GAAM,mBAAmB;GAAyB,CAAA,GAE1E,kBAAC,GAAD;GAAgC;GAAU,oBAAoB;GAA4B,CAAA,EAG5F,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;GAAK,WAAU;aACb,kBAAC,GAAD;IAA6B;IAAM,cAAc;IAAoB,CAAA;GACjE,CAAA,EACN,kBAAC,GAAD;GACE,SAAS,MAAU,KAAK,EAAW,EAAM;GACzC,UAAU;GACV,aAAa,EACX,+CACA,aAAa,EAAK,QAAQ,MAAM,IAAI,CAAC,KACtC;GACD,CAAA,CACE,EAAA,CAAA,CACF"}
@@ -61,9 +61,13 @@ function a({ agent: a, sessionId: o }) {
61
61
  u(o, "idle");
62
62
  } catch (e) {
63
63
  if (_.signal.aborted) return;
64
- l(o, g, `Error: ${e instanceof Error ? e.message : "An error occurred"}`), u(o, "error"), setTimeout(() => {
64
+ let n = e instanceof Error ? e.message : "An error occurred";
65
+ n.includes("Claude SDK auth is not configured") || n.includes("ANTHROPIC_API_KEY") || n.includes("ANTHROPIC_AUTH_TOKEN") ? (t.getState().setSdkAuthPrompt({
66
+ agentId: a.id,
67
+ reason: n
68
+ }), l(o, g, "Set your Anthropic API key or auth token to continue. A prompt has been opened below."), u(o, "idle")) : (l(o, g, `Error: ${n}`), u(o, "error"), setTimeout(() => {
65
69
  u(o, "idle");
66
- }, 3e3);
70
+ }, 3e3));
67
71
  } finally {
68
72
  s.current = null;
69
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useAgentManagerStreaming.js","names":[],"sources":["../../src/hooks/useAgentManagerStreaming.ts"],"sourcesContent":["/**\n * Hook for streaming management in Agent Manager sessions.\n *\n * Manages the send/receive lifecycle: adds user message to store,\n * creates a placeholder assistant message, calls sendPromptToSession,\n * updates the placeholder with the response, and handles cancellation.\n */\n\nimport { useRef, useCallback } from 'react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { sendPromptToSession, type AgentRef } from '@/services/aiApi';\nimport type { AgentManagerMessage } from '@/types/agentManager';\n\ninterface UseAgentManagerStreamingParams {\n agent: AgentRef;\n sessionId: string;\n}\n\ninterface UseAgentManagerStreamingReturn {\n sendMessage: (prompt: string) => Promise<void>;\n cancelStream: () => void;\n isStreaming: boolean;\n}\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nexport function useAgentManagerStreaming({\n agent,\n sessionId,\n}: UseAgentManagerStreamingParams): UseAgentManagerStreamingReturn {\n const abortRef = useRef<AbortController | null>(null);\n\n const addMessage = useAgentManagerStore((s) => s.addMessage);\n const updateMessage = useAgentManagerStore((s) => s.updateMessage);\n const setSessionStatus = useAgentManagerStore((s) => s.setSessionStatus);\n const sessionState = useAgentManagerStore((s) => s.sessionStates[sessionId]);\n\n const isStreaming = sessionState?.status === 'streaming' || sessionState?.status === 'processing';\n\n const sendMessage = useCallback(\n async (prompt: string) => {\n // Get current session state for SDK, model, and history limit\n const currentState = useAgentManagerStore.getState().sessionStates[sessionId];\n const currentSdk = currentState?.sdk;\n const currentModel = currentState?.model;\n const currentMode = currentState?.mode;\n const historyLimit = currentState?.contextHistoryLimit ?? 20;\n\n // Add user message with SDK info\n const userMsg: AgentManagerMessage = {\n id: generateId(),\n role: 'user',\n content: prompt,\n timestamp: new Date().toISOString(),\n sdk: currentSdk,\n };\n addMessage(sessionId, userMsg);\n\n // Build conversation history from last N messages (excluding the one just added)\n const messages = currentState?.messages ?? [];\n const historyMessages = messages\n .filter((m) => m.role === 'user' || m.role === 'assistant')\n .slice(-historyLimit)\n .map((m) => ({ role: m.role, content: m.content }));\n\n // Add placeholder assistant message\n const assistantMsgId = generateId();\n const placeholderMsg: AgentManagerMessage = {\n id: assistantMsgId,\n role: 'assistant',\n content: '',\n timestamp: new Date().toISOString(),\n sdk: currentSdk,\n };\n addMessage(sessionId, placeholderMsg);\n\n // Set status to streaming\n setSessionStatus(sessionId, 'streaming');\n\n // Create abort controller\n const controller = new AbortController();\n abortRef.current = controller;\n\n try {\n const result = await sendPromptToSession(agent, sessionId, prompt, undefined, {\n agentType: currentSdk,\n model: currentModel,\n mode: currentMode,\n conversationHistory: historyMessages.length > 0 ? historyMessages : undefined,\n });\n\n // Check if aborted\n if (controller.signal.aborted) return;\n\n // Update the placeholder message with the response\n updateMessage(sessionId, assistantMsgId, result.response);\n\n // Patch the assistant message with tokens, model, and SDK used\n {\n const store = useAgentManagerStore.getState();\n const session = store.sessionStates[sessionId];\n if (session) {\n const responseModel = (result as Record<string, unknown>).model as string | undefined;\n const responseSdk = (\n (result as Record<string, unknown>).metadata as Record<string, unknown>\n )?.provider as string | undefined;\n const updatedMessages = session.messages.map((m) =>\n m.id === assistantMsgId\n ? {\n ...m,\n content: result.response,\n tokens: result.tokens,\n model: responseModel || m.model,\n sdk: (responseSdk as AgentManagerMessage['sdk']) || currentSdk,\n }\n : m\n );\n useAgentManagerStore.setState((prev) => ({\n sessionStates: {\n ...prev.sessionStates,\n [sessionId]: { ...session, messages: updatedMessages },\n },\n }));\n }\n }\n\n setSessionStatus(sessionId, 'idle');\n } catch (error: unknown) {\n if (controller.signal.aborted) return;\n\n const errorMessage = error instanceof Error ? error.message : 'An error occurred';\n\n updateMessage(sessionId, assistantMsgId, `Error: ${errorMessage}`);\n setSessionStatus(sessionId, 'error');\n\n // Reset to idle after a brief period\n setTimeout(() => {\n setSessionStatus(sessionId, 'idle');\n }, 3000);\n } finally {\n abortRef.current = null;\n }\n },\n [agent, sessionId, addMessage, updateMessage, setSessionStatus]\n );\n\n const cancelStream = useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort();\n abortRef.current = null;\n }\n setSessionStatus(sessionId, 'idle');\n }, [sessionId, setSessionStatus]);\n\n return { sendMessage, cancelStream, isStreaming };\n}\n"],"mappings":";;;;AAwBA,SAAS,IAAqB;AAC5B,QAAO,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,GAAG,EAAE;;AAGhE,SAAgB,EAAyB,EACvC,UACA,gBACiE;CACjE,IAAM,IAAW,EAA+B,KAAK,EAE/C,IAAa,GAAsB,MAAM,EAAE,WAAW,EACtD,IAAgB,GAAsB,MAAM,EAAE,cAAc,EAC5D,IAAmB,GAAsB,MAAM,EAAE,iBAAiB,EAClE,IAAe,GAAsB,MAAM,EAAE,cAAc,GAAW,EAEtE,IAAc,GAAc,WAAW,eAAe,GAAc,WAAW;AAqHrF,QAAO;EAAE,aAnHW,EAClB,OAAO,MAAmB;GAExB,IAAM,IAAe,EAAqB,UAAU,CAAC,cAAc,IAC7D,IAAa,GAAc,KAC3B,IAAe,GAAc,OAC7B,IAAc,GAAc,MAC5B,IAAe,GAAc,uBAAuB;AAU1D,KAAW,GAP0B;IACnC,IAAI,GAAY;IAChB,MAAM;IACN,SAAS;IACT,4BAAW,IAAI,MAAM,EAAC,aAAa;IACnC,KAAK;IACN,CAC6B;GAI9B,IAAM,KADW,GAAc,YAAY,EAAE,EAE1C,QAAQ,MAAM,EAAE,SAAS,UAAU,EAAE,SAAS,YAAY,CAC1D,MAAM,CAAC,EAAa,CACpB,KAAK,OAAO;IAAE,MAAM,EAAE;IAAM,SAAS,EAAE;IAAS,EAAE,EAG/C,IAAiB,GAAY;AAWnC,GAHA,EAAW,GAPiC;IAC1C,IAAI;IACJ,MAAM;IACN,SAAS;IACT,4BAAW,IAAI,MAAM,EAAC,aAAa;IACnC,KAAK;IACN,CACoC,EAGrC,EAAiB,GAAW,YAAY;GAGxC,IAAM,IAAa,IAAI,iBAAiB;AACxC,KAAS,UAAU;AAEnB,OAAI;IACF,IAAM,IAAS,MAAM,EAAoB,GAAO,GAAW,GAAQ,KAAA,GAAW;KAC5E,WAAW;KACX,OAAO;KACP,MAAM;KACN,qBAAqB,EAAgB,SAAS,IAAI,IAAkB,KAAA;KACrE,CAAC;AAGF,QAAI,EAAW,OAAO,QAAS;AAG/B,MAAc,GAAW,GAAgB,EAAO,SAAS;IAGzD;KAEE,IAAM,IADQ,EAAqB,UAAU,CACvB,cAAc;AACpC,SAAI,GAAS;MACX,IAAM,IAAiB,EAAmC,OACpD,IACH,EAAmC,UACnC,UACG,IAAkB,EAAQ,SAAS,KAAK,MAC5C,EAAE,OAAO,IACL;OACE,GAAG;OACH,SAAS,EAAO;OAChB,QAAQ,EAAO;OACf,OAAO,KAAiB,EAAE;OAC1B,KAAM,KAA8C;OACrD,GACD,EACL;AACD,QAAqB,UAAU,OAAU,EACvC,eAAe;OACb,GAAG,EAAK;QACP,IAAY;QAAE,GAAG;QAAS,UAAU;QAAiB;OACvD,EACF,EAAE;;;AAIP,MAAiB,GAAW,OAAO;YAC5B,GAAgB;AACvB,QAAI,EAAW,OAAO,QAAS;AAQ/B,IAJA,EAAc,GAAW,GAAgB,UAFpB,aAAiB,QAAQ,EAAM,UAAU,sBAEI,EAClE,EAAiB,GAAW,QAAQ,EAGpC,iBAAiB;AACf,OAAiB,GAAW,OAAO;OAClC,IAAK;aACA;AACR,MAAS,UAAU;;KAGvB;GAAC;GAAO;GAAW;GAAY;GAAe;GAAiB,CAChE;EAUqB,cARD,QAAkB;AAKrC,GAJA,AAEE,EAAS,aADT,EAAS,QAAQ,OAAO,EACL,OAErB,EAAiB,GAAW,OAAO;KAClC,CAAC,GAAW,EAAiB,CAAC;EAEG;EAAa"}
1
+ {"version":3,"file":"useAgentManagerStreaming.js","names":[],"sources":["../../src/hooks/useAgentManagerStreaming.ts"],"sourcesContent":["/**\n * Hook for streaming management in Agent Manager sessions.\n *\n * Manages the send/receive lifecycle: adds user message to store,\n * creates a placeholder assistant message, calls sendPromptToSession,\n * updates the placeholder with the response, and handles cancellation.\n */\n\nimport { useRef, useCallback } from 'react';\nimport { useAgentManagerStore } from '@/store/agentManagerStore';\nimport { sendPromptToSession, type AgentRef } from '@/services/aiApi';\nimport type { AgentManagerMessage } from '@/types/agentManager';\n\ninterface UseAgentManagerStreamingParams {\n agent: AgentRef;\n sessionId: string;\n}\n\ninterface UseAgentManagerStreamingReturn {\n sendMessage: (prompt: string) => Promise<void>;\n cancelStream: () => void;\n isStreaming: boolean;\n}\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\nexport function useAgentManagerStreaming({\n agent,\n sessionId,\n}: UseAgentManagerStreamingParams): UseAgentManagerStreamingReturn {\n const abortRef = useRef<AbortController | null>(null);\n\n const addMessage = useAgentManagerStore((s) => s.addMessage);\n const updateMessage = useAgentManagerStore((s) => s.updateMessage);\n const setSessionStatus = useAgentManagerStore((s) => s.setSessionStatus);\n const sessionState = useAgentManagerStore((s) => s.sessionStates[sessionId]);\n\n const isStreaming = sessionState?.status === 'streaming' || sessionState?.status === 'processing';\n\n const sendMessage = useCallback(\n async (prompt: string) => {\n // Get current session state for SDK, model, and history limit\n const currentState = useAgentManagerStore.getState().sessionStates[sessionId];\n const currentSdk = currentState?.sdk;\n const currentModel = currentState?.model;\n const currentMode = currentState?.mode;\n const historyLimit = currentState?.contextHistoryLimit ?? 20;\n\n // Add user message with SDK info\n const userMsg: AgentManagerMessage = {\n id: generateId(),\n role: 'user',\n content: prompt,\n timestamp: new Date().toISOString(),\n sdk: currentSdk,\n };\n addMessage(sessionId, userMsg);\n\n // Build conversation history from last N messages (excluding the one just added)\n const messages = currentState?.messages ?? [];\n const historyMessages = messages\n .filter((m) => m.role === 'user' || m.role === 'assistant')\n .slice(-historyLimit)\n .map((m) => ({ role: m.role, content: m.content }));\n\n // Add placeholder assistant message\n const assistantMsgId = generateId();\n const placeholderMsg: AgentManagerMessage = {\n id: assistantMsgId,\n role: 'assistant',\n content: '',\n timestamp: new Date().toISOString(),\n sdk: currentSdk,\n };\n addMessage(sessionId, placeholderMsg);\n\n // Set status to streaming\n setSessionStatus(sessionId, 'streaming');\n\n // Create abort controller\n const controller = new AbortController();\n abortRef.current = controller;\n\n try {\n const result = await sendPromptToSession(agent, sessionId, prompt, undefined, {\n agentType: currentSdk,\n model: currentModel,\n mode: currentMode,\n conversationHistory: historyMessages.length > 0 ? historyMessages : undefined,\n });\n\n // Check if aborted\n if (controller.signal.aborted) return;\n\n // Update the placeholder message with the response\n updateMessage(sessionId, assistantMsgId, result.response);\n\n // Patch the assistant message with tokens, model, and SDK used\n {\n const store = useAgentManagerStore.getState();\n const session = store.sessionStates[sessionId];\n if (session) {\n const responseModel = (result as Record<string, unknown>).model as string | undefined;\n const responseSdk = (\n (result as Record<string, unknown>).metadata as Record<string, unknown>\n )?.provider as string | undefined;\n const updatedMessages = session.messages.map((m) =>\n m.id === assistantMsgId\n ? {\n ...m,\n content: result.response,\n tokens: result.tokens,\n model: responseModel || m.model,\n sdk: (responseSdk as AgentManagerMessage['sdk']) || currentSdk,\n }\n : m\n );\n useAgentManagerStore.setState((prev) => ({\n sessionStates: {\n ...prev.sessionStates,\n [sessionId]: { ...session, messages: updatedMessages },\n },\n }));\n }\n }\n\n setSessionStatus(sessionId, 'idle');\n } catch (error: unknown) {\n if (controller.signal.aborted) return;\n\n const errorMessage = error instanceof Error ? error.message : 'An error occurred';\n\n // SDK auth not configured → surface a credentials prompt instead of\n // a dead-end error message. The chat surface watches the store's\n // sdkAuthPrompt field and opens AIProviderCredentialsCard inline.\n if (\n errorMessage.includes('Claude SDK auth is not configured') ||\n errorMessage.includes('ANTHROPIC_API_KEY') ||\n errorMessage.includes('ANTHROPIC_AUTH_TOKEN')\n ) {\n useAgentManagerStore.getState().setSdkAuthPrompt({\n agentId: agent.id,\n reason: errorMessage,\n });\n updateMessage(\n sessionId,\n assistantMsgId,\n 'Set your Anthropic API key or auth token to continue. A prompt has been opened below.'\n );\n setSessionStatus(sessionId, 'idle');\n } else {\n updateMessage(sessionId, assistantMsgId, `Error: ${errorMessage}`);\n setSessionStatus(sessionId, 'error');\n\n // Reset to idle after a brief period\n setTimeout(() => {\n setSessionStatus(sessionId, 'idle');\n }, 3000);\n }\n } finally {\n abortRef.current = null;\n }\n },\n [agent, sessionId, addMessage, updateMessage, setSessionStatus]\n );\n\n const cancelStream = useCallback(() => {\n if (abortRef.current) {\n abortRef.current.abort();\n abortRef.current = null;\n }\n setSessionStatus(sessionId, 'idle');\n }, [sessionId, setSessionStatus]);\n\n return { sendMessage, cancelStream, isStreaming };\n}\n"],"mappings":"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"}
@@ -11,15 +11,15 @@ function n({ panelRef: n, inputRef: r, onClose: i }) {
11
11
  t.preventDefault(), i();
12
12
  return;
13
13
  }
14
- if (o && t.key === "n") {
14
+ if (o && t.shiftKey && (t.key === "N" || t.key === "n")) {
15
15
  t.preventDefault(), e.getState().createConversation();
16
16
  return;
17
17
  }
18
- if (o && t.key === "e") {
18
+ if (o && t.shiftKey && (t.key === "E" || t.key === "e")) {
19
19
  t.preventDefault(), a.querySelector("[data-testid=\"assistant-export\"]")?.click();
20
20
  return;
21
21
  }
22
- if (t.key === "/" && !o) {
22
+ if (t.key === "/" && !o && !t.shiftKey) {
23
23
  let e = document.activeElement?.tagName;
24
24
  e !== "INPUT" && e !== "TEXTAREA" && (t.preventDefault(), r?.current?.focus());
25
25
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useAssistantKeyboardShortcuts.js","names":[],"sources":["../../src/hooks/useAssistantKeyboardShortcuts.ts"],"sourcesContent":["import { useEffect, type RefObject } from 'react';\nimport { useAssistantStore } from '@/store/assistantStore';\n\ninterface Options {\n panelRef: RefObject<HTMLDivElement | null>;\n inputRef?: RefObject<HTMLTextAreaElement | null>;\n onClose: () => void;\n}\n\n/**\n * Panel-scoped keyboard shortcuts for the AI assistant.\n *\n * - Escape: Close panel\n * - Ctrl+N / Cmd+N: New conversation\n * - Ctrl+E / Cmd+E: Export (triggers click on export button)\n * - /: Focus input (when not already focused)\n */\nexport function useAssistantKeyboardShortcuts({ panelRef, inputRef, onClose }: Options) {\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n const panel = panelRef.current;\n if (!panel) return;\n\n // Only handle shortcuts when the panel or its children are focused\n if (!panel.contains(document.activeElement) && document.activeElement !== panel) {\n return;\n }\n\n const isModKey = e.ctrlKey || e.metaKey;\n\n // Escape: close panel\n if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n return;\n }\n\n // Ctrl+N: new conversation\n if (isModKey && e.key === 'n') {\n e.preventDefault();\n useAssistantStore.getState().createConversation();\n return;\n }\n\n // Ctrl+E: export\n if (isModKey && e.key === 'e') {\n e.preventDefault();\n const exportBtn = panel.querySelector(\n '[data-testid=\"assistant-export\"]'\n ) as HTMLButtonElement;\n exportBtn?.click();\n return;\n }\n\n // /: focus input (when not in an input/textarea)\n if (e.key === '/' && !isModKey) {\n const tag = (document.activeElement as HTMLElement)?.tagName;\n if (tag !== 'INPUT' && tag !== 'TEXTAREA') {\n e.preventDefault();\n inputRef?.current?.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [panelRef, inputRef, onClose]);\n}\n"],"mappings":";;;AAiBA,SAAgB,EAA8B,EAAE,aAAU,aAAU,cAAoB;AACtF,SAAgB;EACd,IAAM,KAAiB,MAAqB;GAC1C,IAAM,IAAQ,EAAS;AAIvB,OAHI,CAAC,KAGD,CAAC,EAAM,SAAS,SAAS,cAAc,IAAI,SAAS,kBAAkB,EACxE;GAGF,IAAM,IAAW,EAAE,WAAW,EAAE;AAGhC,OAAI,EAAE,QAAQ,UAAU;AAEtB,IADA,EAAE,gBAAgB,EAClB,GAAS;AACT;;AAIF,OAAI,KAAY,EAAE,QAAQ,KAAK;AAE7B,IADA,EAAE,gBAAgB,EAClB,EAAkB,UAAU,CAAC,oBAAoB;AACjD;;AAIF,OAAI,KAAY,EAAE,QAAQ,KAAK;AAEX,IADlB,EAAE,gBAAgB,EACA,EAAM,cACtB,qCACD,EACU,OAAO;AAClB;;AAIF,OAAI,EAAE,QAAQ,OAAO,CAAC,GAAU;IAC9B,IAAM,IAAO,SAAS,eAA+B;AACrD,IAAI,MAAQ,WAAW,MAAQ,eAC7B,EAAE,gBAAgB,EAClB,GAAU,SAAS,OAAO;;;AAMhC,SADA,SAAS,iBAAiB,WAAW,EAAc,QACtC,SAAS,oBAAoB,WAAW,EAAc;IAClE;EAAC;EAAU;EAAU;EAAQ,CAAC"}
1
+ {"version":3,"file":"useAssistantKeyboardShortcuts.js","names":[],"sources":["../../src/hooks/useAssistantKeyboardShortcuts.ts"],"sourcesContent":["import { useEffect, type RefObject } from 'react';\nimport { useAssistantStore } from '@/store/assistantStore';\n\ninterface Options {\n panelRef: RefObject<HTMLDivElement | null>;\n inputRef?: RefObject<HTMLTextAreaElement | null>;\n onClose: () => void;\n}\n\n/**\n * Panel-scoped keyboard shortcuts for the AI assistant.\n *\n * All shortcuts only fire when the assistant panel (or one of its children)\n * has focus, so they never override the host page's own shortcuts. We also\n * deliberately avoid the bare `Ctrl+N`/`Cmd+N`/`Ctrl+E`/`Ctrl+W` set the\n * browser owns at the OS level — those couldn't be cancelled reliably\n * anyway and were just adding visible \"shortcut listed, doesn't work\"\n * inconsistencies. The shortcuts here match the in-app shortcuts modal.\n *\n * - Escape: Close panel\n * - Ctrl/Cmd+Shift+N: New conversation\n * - Ctrl/Cmd+Shift+E: Export (triggers click on export button)\n * - /: Focus input (only when no input/textarea is already focused)\n */\nexport function useAssistantKeyboardShortcuts({ panelRef, inputRef, onClose }: Options) {\n useEffect(() => {\n const handleKeyDown = (e: KeyboardEvent) => {\n const panel = panelRef.current;\n if (!panel) return;\n\n // Only handle shortcuts when the panel or its children are focused.\n if (!panel.contains(document.activeElement) && document.activeElement !== panel) {\n return;\n }\n\n const isModKey = e.ctrlKey || e.metaKey;\n\n // Escape: close panel\n if (e.key === 'Escape') {\n e.preventDefault();\n onClose();\n return;\n }\n\n // Ctrl/Cmd+Shift+N: new conversation. Shift is required so we don't\n // fight the browser's `Ctrl+N` (new window).\n if (isModKey && e.shiftKey && (e.key === 'N' || e.key === 'n')) {\n e.preventDefault();\n useAssistantStore.getState().createConversation();\n return;\n }\n\n // Ctrl/Cmd+Shift+E: export. Shift required to avoid the browser's\n // `Ctrl+E` (search address bar in Chrome/Edge).\n if (isModKey && e.shiftKey && (e.key === 'E' || e.key === 'e')) {\n e.preventDefault();\n const exportBtn = panel.querySelector(\n '[data-testid=\"assistant-export\"]'\n ) as HTMLButtonElement | null;\n exportBtn?.click();\n return;\n }\n\n // /: focus input (when not in an input/textarea so single-key search\n // typing inside the page doesn't get hijacked).\n if (e.key === '/' && !isModKey && !e.shiftKey) {\n const tag = (document.activeElement as HTMLElement | null)?.tagName;\n if (tag !== 'INPUT' && tag !== 'TEXTAREA') {\n e.preventDefault();\n inputRef?.current?.focus();\n }\n }\n };\n\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [panelRef, inputRef, onClose]);\n}\n"],"mappings":";;;AAwBA,SAAgB,EAA8B,EAAE,aAAU,aAAU,cAAoB;AACtF,SAAgB;EACd,IAAM,KAAiB,MAAqB;GAC1C,IAAM,IAAQ,EAAS;AAIvB,OAHI,CAAC,KAGD,CAAC,EAAM,SAAS,SAAS,cAAc,IAAI,SAAS,kBAAkB,EACxE;GAGF,IAAM,IAAW,EAAE,WAAW,EAAE;AAGhC,OAAI,EAAE,QAAQ,UAAU;AAEtB,IADA,EAAE,gBAAgB,EAClB,GAAS;AACT;;AAKF,OAAI,KAAY,EAAE,aAAa,EAAE,QAAQ,OAAO,EAAE,QAAQ,MAAM;AAE9D,IADA,EAAE,gBAAgB,EAClB,EAAkB,UAAU,CAAC,oBAAoB;AACjD;;AAKF,OAAI,KAAY,EAAE,aAAa,EAAE,QAAQ,OAAO,EAAE,QAAQ,MAAM;AAE5C,IADlB,EAAE,gBAAgB,EACA,EAAM,cACtB,qCACD,EACU,OAAO;AAClB;;AAKF,OAAI,EAAE,QAAQ,OAAO,CAAC,KAAY,CAAC,EAAE,UAAU;IAC7C,IAAM,IAAO,SAAS,eAAsC;AAC5D,IAAI,MAAQ,WAAW,MAAQ,eAC7B,EAAE,gBAAgB,EAClB,GAAU,SAAS,OAAO;;;AAMhC,SADA,SAAS,iBAAiB,WAAW,EAAc,QACtC,SAAS,oBAAoB,WAAW,EAAc;IAClE;EAAC;EAAU;EAAU;EAAQ,CAAC"}
@@ -61,6 +61,10 @@ var s = {
61
61
  adminTab: "templates",
62
62
  selectedSessionIds: [],
63
63
  isFullscreen: !1,
64
+ sessionsRefreshTrigger: 0,
65
+ sdkAuthPrompt: null,
66
+ bumpSessionsRefresh: () => e((e) => ({ sessionsRefreshTrigger: e.sessionsRefreshTrigger + 1 })),
67
+ setSdkAuthPrompt: (t) => e({ sdkAuthPrompt: t }),
64
68
  openSession: (n, i) => {
65
69
  let a = t();
66
70
  if (a.tabs.find((e) => e.sessionId === n)) {
@@ -1 +1 @@
1
- {"version":3,"file":"agentManagerStore.js","names":[],"sources":["../../src/store/agentManagerStore.ts"],"sourcesContent":["/**\n * Agent Manager state management.\n *\n * Zustand store with profile-scoped persistence for multi-session\n * tabbed agent management. Follows the same pattern as assistantStore.ts.\n */\n\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { createProfileScopedStorage } from '@burdenoff/fe-libs/shared/utils';\nimport type {\n AdminTab,\n AgentManagerMessage,\n AgentManagerSessionState,\n AgentManagerTab,\n AgentSdkType,\n DetailPanelTab,\n ProviderMode,\n SessionFilters,\n SidebarGroupBy,\n} from '@/types/agentManager';\n\n/** Default state for a new session */\nfunction createDefaultSessionState(): AgentManagerSessionState {\n return {\n messages: [],\n model: 'claude-sonnet-4-20250514',\n sdk: 'claude',\n mode: 'sdk',\n mcpServers: [],\n voiceEnabled: false,\n showThinking: true,\n contextHistoryLimit: 20,\n status: 'idle',\n rootPath: null,\n contextLevel: 'agent',\n vibeId: null,\n };\n}\n\nconst VALID_SDKS = new Set<AgentSdkType>([\n 'claude',\n 'codex',\n 'opencode',\n 'gemini',\n 'openai-compat',\n 'goose',\n 'amp',\n 'copilot',\n 'crush',\n 'pi',\n 'aider',\n 'plandex',\n]);\n\nfunction asSdk(value: unknown): AgentSdkType | null {\n return typeof value === 'string' && VALID_SDKS.has(value as AgentSdkType)\n ? (value as AgentSdkType)\n : null;\n}\n\nfunction asMode(value: unknown): ProviderMode | null {\n return value === 'sdk' || value === 'cli' ? value : null;\n}\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\ninterface AgentManagerState {\n // ── Tab management ──\n tabs: AgentManagerTab[];\n activeTabSessionId: string | null;\n tabLimit: number; // Max visible tabs before scrolling (default 10)\n\n // ── Per-session state ──\n sessionStates: Record<string, AgentManagerSessionState>;\n\n // ── Sidebar ──\n sidebarOpen: boolean;\n sidebarSearchQuery: string;\n sidebarFilters: SessionFilters;\n sidebarGroupBy: SidebarGroupBy;\n\n // ── Detail panel ──\n detailPanelOpen: boolean;\n detailPanelTab: DetailPanelTab;\n\n // ── Admin tab ──\n adminTab: AdminTab;\n\n // ── Bulk selection ──\n selectedSessionIds: string[];\n\n // ── Fullscreen ──\n isFullscreen: boolean;\n\n // ── Tab actions ──\n openSession: (sessionId: string, name?: string) => void;\n closeTab: (sessionId: string) => void;\n pinTab: (sessionId: string) => void;\n renameTab: (sessionId: string, name: string) => void;\n setTabLimit: (limit: number) => void;\n reorderTabs: (tabs: AgentManagerTab[]) => void;\n setActiveTab: (sessionId: string) => void;\n\n // ── Session state actions ──\n addMessage: (sessionId: string, message: AgentManagerMessage) => void;\n updateMessage: (sessionId: string, messageId: string, content: string) => void;\n setSessionModel: (sessionId: string, model: string) => void;\n setSessionSdk: (sessionId: string, sdk: AgentSdkType) => void;\n setSessionMode: (sessionId: string, mode: ProviderMode) => void;\n toggleVoice: (sessionId: string) => void;\n toggleThinking: (sessionId: string) => void;\n setSessionStatus: (sessionId: string, status: AgentManagerSessionState['status']) => void;\n setSessionRootPath: (sessionId: string, rootPath: string | null) => void;\n setSessionContextLevel: (\n sessionId: string,\n level: AgentManagerSessionState['contextLevel']\n ) => void;\n setSessionVibe: (sessionId: string, vibeId: string | null, rootPath: string | null) => void;\n hydrateSessionConfig: (\n sessionId: string,\n metadata: { agentType?: string | null; config?: Record<string, unknown> | null }\n ) => void;\n\n // ── Sidebar actions ──\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n setSidebarSearchQuery: (query: string) => void;\n setSidebarFilters: (filters: Partial<SessionFilters>) => void;\n resetSidebarFilters: () => void;\n setSidebarGroupBy: (groupBy: SidebarGroupBy) => void;\n\n // ── Detail panel actions ──\n toggleDetailPanel: () => void;\n setDetailPanelOpen: (open: boolean) => void;\n setDetailPanelTab: (tab: DetailPanelTab) => void;\n\n // ── Admin tab actions ──\n setAdminTab: (tab: AdminTab) => void;\n\n // ── Bulk selection actions ──\n toggleSessionSelection: (sessionId: string) => void;\n selectAllSessions: (sessionIds: string[]) => void;\n clearSelection: () => void;\n\n // ── Fullscreen ──\n toggleFullscreen: () => void;\n\n // ── Reset ──\n resetStore: () => void;\n}\n\nconst defaultFilters: SessionFilters = {\n status: [],\n model: [],\n sdk: [],\n dateRange: { from: null, to: null },\n searchQuery: '',\n};\n\nexport const useAgentManagerStore = create<AgentManagerState>()(\n persist(\n (set, get) => ({\n tabs: [],\n activeTabSessionId: null,\n tabLimit: 10,\n sessionStates: {},\n sidebarOpen: true,\n sidebarSearchQuery: '',\n sidebarFilters: { ...defaultFilters },\n sidebarGroupBy: 'date',\n detailPanelOpen: false,\n detailPanelTab: 'info',\n adminTab: 'templates',\n selectedSessionIds: [],\n isFullscreen: false,\n\n // ── Tab actions ──\n\n openSession: (sessionId: string, name?: string) => {\n const state = get();\n const existingTab = state.tabs.find((t) => t.sessionId === sessionId);\n\n if (existingTab) {\n set((prev) => ({\n activeTabSessionId: sessionId,\n sessionStates: {\n ...prev.sessionStates,\n [sessionId]: prev.sessionStates[sessionId] ?? createDefaultSessionState(),\n },\n }));\n return;\n }\n\n const newTab: AgentManagerTab = {\n sessionId,\n name: name ?? `Session ${state.tabs.length + 1}`,\n pinned: false,\n order: state.tabs.length,\n };\n\n set((prev) => ({\n tabs: [...prev.tabs, newTab],\n activeTabSessionId: sessionId,\n sessionStates: {\n ...prev.sessionStates,\n [sessionId]: prev.sessionStates[sessionId] ?? createDefaultSessionState(),\n },\n }));\n },\n\n closeTab: (sessionId: string) =>\n set((state) => {\n const filtered = state.tabs.filter((t) => t.sessionId !== sessionId);\n const nextSessionStates = { ...state.sessionStates };\n delete nextSessionStates[sessionId];\n\n let nextActive = state.activeTabSessionId;\n if (nextActive === sessionId) {\n nextActive = filtered.length > 0 ? filtered[filtered.length - 1].sessionId : null;\n }\n\n return {\n tabs: filtered,\n activeTabSessionId: nextActive,\n sessionStates: nextSessionStates,\n };\n }),\n\n pinTab: (sessionId: string) =>\n set((state) => ({\n tabs: state.tabs.map((t) =>\n t.sessionId === sessionId ? { ...t, pinned: !t.pinned } : t\n ),\n })),\n\n setTabLimit: (limit: number) => set({ tabLimit: Math.max(1, limit) }),\n\n renameTab: (sessionId: string, name: string) =>\n set((state) => ({\n tabs: state.tabs.map((t) => (t.sessionId === sessionId ? { ...t, name } : t)),\n })),\n\n reorderTabs: (tabs: AgentManagerTab[]) => set({ tabs }),\n\n setActiveTab: (sessionId: string) =>\n set((state) => ({\n activeTabSessionId: sessionId,\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: state.sessionStates[sessionId] ?? createDefaultSessionState(),\n },\n })),\n\n // ── Session state actions ──\n\n addMessage: (sessionId: string, message: AgentManagerMessage) =>\n set((state) => {\n const current = state.sessionStates[sessionId] ?? createDefaultSessionState();\n if (current.messages.some((existing) => existing.id === message.id)) return state;\n\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...current,\n messages: [...current.messages, message],\n },\n },\n };\n }),\n\n updateMessage: (sessionId: string, messageId: string, content: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...sessionState,\n messages: sessionState.messages.map((m) =>\n m.id === messageId ? { ...m, content } : m\n ),\n },\n },\n };\n }),\n\n setSessionModel: (sessionId: string, model: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, model },\n },\n };\n }),\n\n setSessionSdk: (sessionId: string, sdk: AgentSdkType) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, sdk },\n },\n };\n }),\n\n setSessionMode: (sessionId: string, mode: ProviderMode) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, mode },\n },\n };\n }),\n\n toggleVoice: (sessionId: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...sessionState,\n voiceEnabled: !sessionState.voiceEnabled,\n },\n },\n };\n }),\n\n toggleThinking: (sessionId: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...sessionState,\n showThinking: !sessionState.showThinking,\n },\n },\n };\n }),\n\n setSessionStatus: (sessionId: string, status: AgentManagerSessionState['status']) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, status },\n },\n };\n }),\n\n setSessionRootPath: (sessionId: string, rootPath: string | null) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, rootPath },\n },\n };\n }),\n\n setSessionContextLevel: (\n sessionId: string,\n level: AgentManagerSessionState['contextLevel']\n ) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, contextLevel: level },\n },\n };\n }),\n\n setSessionVibe: (sessionId: string, vibeId: string | null, rootPath: string | null) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, vibeId, rootPath },\n },\n };\n }),\n\n hydrateSessionConfig: (sessionId, metadata) =>\n set((state) => {\n const current = state.sessionStates[sessionId] ?? createDefaultSessionState();\n const config = metadata.config ?? {};\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...current,\n sdk: asSdk(metadata.agentType) ?? current.sdk,\n mode: asMode(config.mode) ?? current.mode,\n model: typeof config.model === 'string' ? config.model : current.model,\n },\n },\n };\n }),\n\n // ── Sidebar actions ──\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n\n setSidebarOpen: (open: boolean) => set({ sidebarOpen: open }),\n\n setSidebarSearchQuery: (query: string) => set({ sidebarSearchQuery: query }),\n\n setSidebarFilters: (filters: Partial<SessionFilters>) =>\n set((state) => ({\n sidebarFilters: { ...state.sidebarFilters, ...filters },\n })),\n\n resetSidebarFilters: () => set({ sidebarFilters: { ...defaultFilters } }),\n\n setSidebarGroupBy: (groupBy: SidebarGroupBy) => set({ sidebarGroupBy: groupBy }),\n\n // ── Detail panel actions ──\n\n toggleDetailPanel: () => set((state) => ({ detailPanelOpen: !state.detailPanelOpen })),\n\n setDetailPanelOpen: (open: boolean) => set({ detailPanelOpen: open }),\n\n setDetailPanelTab: (tab: DetailPanelTab) => set({ detailPanelTab: tab }),\n\n // ── Admin tab actions ──\n\n setAdminTab: (tab: AdminTab) => set({ adminTab: tab }),\n\n // ── Bulk selection actions ──\n\n toggleSessionSelection: (sessionId: string) =>\n set((state) => {\n const isSelected = state.selectedSessionIds.includes(sessionId);\n return {\n selectedSessionIds: isSelected\n ? state.selectedSessionIds.filter((id) => id !== sessionId)\n : [...state.selectedSessionIds, sessionId],\n };\n }),\n\n selectAllSessions: (sessionIds: string[]) => set({ selectedSessionIds: sessionIds }),\n\n clearSelection: () => set({ selectedSessionIds: [] }),\n\n // ── Fullscreen ──\n\n toggleFullscreen: () => set((state) => ({ isFullscreen: !state.isFullscreen })),\n\n // ── Reset ──\n\n resetStore: () =>\n set({\n tabs: [],\n activeTabSessionId: null,\n sessionStates: {},\n sidebarOpen: true,\n sidebarSearchQuery: '',\n sidebarFilters: { ...defaultFilters },\n sidebarGroupBy: 'date',\n detailPanelOpen: false,\n detailPanelTab: 'info',\n adminTab: 'templates',\n selectedSessionIds: [],\n }),\n }),\n {\n name: 'vibecontrols-agent-manager',\n storage: createProfileScopedStorage(),\n partialize: (state) => ({\n tabs: state.tabs,\n activeTabSessionId: state.activeTabSessionId,\n sidebarOpen: state.sidebarOpen,\n sidebarGroupBy: state.sidebarGroupBy,\n detailPanelOpen: state.detailPanelOpen,\n detailPanelTab: state.detailPanelTab,\n adminTab: state.adminTab,\n isFullscreen: state.isFullscreen,\n }),\n }\n )\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"agentManagerStore.js","names":[],"sources":["../../src/store/agentManagerStore.ts"],"sourcesContent":["/**\n * Agent Manager state management.\n *\n * Zustand store with profile-scoped persistence for multi-session\n * tabbed agent management. Follows the same pattern as assistantStore.ts.\n */\n\nimport { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport { createProfileScopedStorage } from '@burdenoff/fe-libs/shared/utils';\nimport type {\n AdminTab,\n AgentManagerMessage,\n AgentManagerSessionState,\n AgentManagerTab,\n AgentSdkType,\n DetailPanelTab,\n ProviderMode,\n SessionFilters,\n SidebarGroupBy,\n} from '@/types/agentManager';\n\n/** Default state for a new session */\nfunction createDefaultSessionState(): AgentManagerSessionState {\n return {\n messages: [],\n model: 'claude-sonnet-4-20250514',\n sdk: 'claude',\n mode: 'sdk',\n mcpServers: [],\n voiceEnabled: false,\n showThinking: true,\n contextHistoryLimit: 20,\n status: 'idle',\n rootPath: null,\n contextLevel: 'agent',\n vibeId: null,\n };\n}\n\nconst VALID_SDKS = new Set<AgentSdkType>([\n 'claude',\n 'codex',\n 'opencode',\n 'gemini',\n 'openai-compat',\n 'goose',\n 'amp',\n 'copilot',\n 'crush',\n 'pi',\n 'aider',\n 'plandex',\n]);\n\nfunction asSdk(value: unknown): AgentSdkType | null {\n return typeof value === 'string' && VALID_SDKS.has(value as AgentSdkType)\n ? (value as AgentSdkType)\n : null;\n}\n\nfunction asMode(value: unknown): ProviderMode | null {\n return value === 'sdk' || value === 'cli' ? value : null;\n}\n\nfunction generateId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;\n}\n\ninterface AgentManagerState {\n // ── Tab management ──\n tabs: AgentManagerTab[];\n activeTabSessionId: string | null;\n tabLimit: number; // Max visible tabs before scrolling (default 10)\n\n // ── Per-session state ──\n sessionStates: Record<string, AgentManagerSessionState>;\n\n // ── Sidebar ──\n sidebarOpen: boolean;\n sidebarSearchQuery: string;\n sidebarFilters: SessionFilters;\n sidebarGroupBy: SidebarGroupBy;\n\n // ── Detail panel ──\n detailPanelOpen: boolean;\n detailPanelTab: DetailPanelTab;\n\n // ── Admin tab ──\n adminTab: AdminTab;\n\n // ── Bulk selection ──\n selectedSessionIds: string[];\n\n // ── Fullscreen ──\n isFullscreen: boolean;\n\n // ── Cross-component refresh signal (incremented after backend mutations\n // that affect the sessions list). Components watch this to re-fetch. ──\n sessionsRefreshTrigger: number;\n\n // ── SDK auth prompt request. Set when a streaming call returns an\n // SDK-auth-missing error so the chat surface can open the credentials\n // modal instead of erroring out. Null when no prompt is requested. ──\n sdkAuthPrompt: { agentId: string; reason: string } | null;\n\n // ── Tab actions ──\n openSession: (sessionId: string, name?: string) => void;\n closeTab: (sessionId: string) => void;\n pinTab: (sessionId: string) => void;\n renameTab: (sessionId: string, name: string) => void;\n setTabLimit: (limit: number) => void;\n reorderTabs: (tabs: AgentManagerTab[]) => void;\n setActiveTab: (sessionId: string) => void;\n\n // ── Session state actions ──\n addMessage: (sessionId: string, message: AgentManagerMessage) => void;\n updateMessage: (sessionId: string, messageId: string, content: string) => void;\n setSessionModel: (sessionId: string, model: string) => void;\n setSessionSdk: (sessionId: string, sdk: AgentSdkType) => void;\n setSessionMode: (sessionId: string, mode: ProviderMode) => void;\n toggleVoice: (sessionId: string) => void;\n toggleThinking: (sessionId: string) => void;\n setSessionStatus: (sessionId: string, status: AgentManagerSessionState['status']) => void;\n setSessionRootPath: (sessionId: string, rootPath: string | null) => void;\n setSessionContextLevel: (\n sessionId: string,\n level: AgentManagerSessionState['contextLevel']\n ) => void;\n setSessionVibe: (sessionId: string, vibeId: string | null, rootPath: string | null) => void;\n hydrateSessionConfig: (\n sessionId: string,\n metadata: { agentType?: string | null; config?: Record<string, unknown> | null }\n ) => void;\n\n // ── Sidebar actions ──\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n setSidebarSearchQuery: (query: string) => void;\n setSidebarFilters: (filters: Partial<SessionFilters>) => void;\n resetSidebarFilters: () => void;\n setSidebarGroupBy: (groupBy: SidebarGroupBy) => void;\n\n // ── Detail panel actions ──\n toggleDetailPanel: () => void;\n setDetailPanelOpen: (open: boolean) => void;\n setDetailPanelTab: (tab: DetailPanelTab) => void;\n\n // ── Admin tab actions ──\n setAdminTab: (tab: AdminTab) => void;\n\n // ── Bulk selection actions ──\n toggleSessionSelection: (sessionId: string) => void;\n selectAllSessions: (sessionIds: string[]) => void;\n clearSelection: () => void;\n\n // ── Fullscreen ──\n toggleFullscreen: () => void;\n\n // ── Refresh + SDK-auth-prompt actions ──\n bumpSessionsRefresh: () => void;\n setSdkAuthPrompt: (prompt: { agentId: string; reason: string } | null) => void;\n\n // ── Reset ──\n resetStore: () => void;\n}\n\nconst defaultFilters: SessionFilters = {\n status: [],\n model: [],\n sdk: [],\n dateRange: { from: null, to: null },\n searchQuery: '',\n};\n\nexport const useAgentManagerStore = create<AgentManagerState>()(\n persist(\n (set, get) => ({\n tabs: [],\n activeTabSessionId: null,\n tabLimit: 10,\n sessionStates: {},\n sidebarOpen: true,\n sidebarSearchQuery: '',\n sidebarFilters: { ...defaultFilters },\n sidebarGroupBy: 'date',\n detailPanelOpen: false,\n detailPanelTab: 'info',\n adminTab: 'templates',\n selectedSessionIds: [],\n isFullscreen: false,\n sessionsRefreshTrigger: 0,\n sdkAuthPrompt: null,\n\n bumpSessionsRefresh: () =>\n set((state) => ({ sessionsRefreshTrigger: state.sessionsRefreshTrigger + 1 })),\n setSdkAuthPrompt: (prompt) => set({ sdkAuthPrompt: prompt }),\n\n // ── Tab actions ──\n\n openSession: (sessionId: string, name?: string) => {\n const state = get();\n const existingTab = state.tabs.find((t) => t.sessionId === sessionId);\n\n if (existingTab) {\n set((prev) => ({\n activeTabSessionId: sessionId,\n sessionStates: {\n ...prev.sessionStates,\n [sessionId]: prev.sessionStates[sessionId] ?? createDefaultSessionState(),\n },\n }));\n return;\n }\n\n const newTab: AgentManagerTab = {\n sessionId,\n name: name ?? `Session ${state.tabs.length + 1}`,\n pinned: false,\n order: state.tabs.length,\n };\n\n set((prev) => ({\n tabs: [...prev.tabs, newTab],\n activeTabSessionId: sessionId,\n sessionStates: {\n ...prev.sessionStates,\n [sessionId]: prev.sessionStates[sessionId] ?? createDefaultSessionState(),\n },\n }));\n },\n\n closeTab: (sessionId: string) =>\n set((state) => {\n const filtered = state.tabs.filter((t) => t.sessionId !== sessionId);\n const nextSessionStates = { ...state.sessionStates };\n delete nextSessionStates[sessionId];\n\n let nextActive = state.activeTabSessionId;\n if (nextActive === sessionId) {\n nextActive = filtered.length > 0 ? filtered[filtered.length - 1].sessionId : null;\n }\n\n return {\n tabs: filtered,\n activeTabSessionId: nextActive,\n sessionStates: nextSessionStates,\n };\n }),\n\n pinTab: (sessionId: string) =>\n set((state) => ({\n tabs: state.tabs.map((t) =>\n t.sessionId === sessionId ? { ...t, pinned: !t.pinned } : t\n ),\n })),\n\n setTabLimit: (limit: number) => set({ tabLimit: Math.max(1, limit) }),\n\n renameTab: (sessionId: string, name: string) =>\n set((state) => ({\n tabs: state.tabs.map((t) => (t.sessionId === sessionId ? { ...t, name } : t)),\n })),\n\n reorderTabs: (tabs: AgentManagerTab[]) => set({ tabs }),\n\n setActiveTab: (sessionId: string) =>\n set((state) => ({\n activeTabSessionId: sessionId,\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: state.sessionStates[sessionId] ?? createDefaultSessionState(),\n },\n })),\n\n // ── Session state actions ──\n\n addMessage: (sessionId: string, message: AgentManagerMessage) =>\n set((state) => {\n const current = state.sessionStates[sessionId] ?? createDefaultSessionState();\n if (current.messages.some((existing) => existing.id === message.id)) return state;\n\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...current,\n messages: [...current.messages, message],\n },\n },\n };\n }),\n\n updateMessage: (sessionId: string, messageId: string, content: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...sessionState,\n messages: sessionState.messages.map((m) =>\n m.id === messageId ? { ...m, content } : m\n ),\n },\n },\n };\n }),\n\n setSessionModel: (sessionId: string, model: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, model },\n },\n };\n }),\n\n setSessionSdk: (sessionId: string, sdk: AgentSdkType) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, sdk },\n },\n };\n }),\n\n setSessionMode: (sessionId: string, mode: ProviderMode) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, mode },\n },\n };\n }),\n\n toggleVoice: (sessionId: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...sessionState,\n voiceEnabled: !sessionState.voiceEnabled,\n },\n },\n };\n }),\n\n toggleThinking: (sessionId: string) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...sessionState,\n showThinking: !sessionState.showThinking,\n },\n },\n };\n }),\n\n setSessionStatus: (sessionId: string, status: AgentManagerSessionState['status']) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, status },\n },\n };\n }),\n\n setSessionRootPath: (sessionId: string, rootPath: string | null) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, rootPath },\n },\n };\n }),\n\n setSessionContextLevel: (\n sessionId: string,\n level: AgentManagerSessionState['contextLevel']\n ) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, contextLevel: level },\n },\n };\n }),\n\n setSessionVibe: (sessionId: string, vibeId: string | null, rootPath: string | null) =>\n set((state) => {\n const sessionState = state.sessionStates[sessionId];\n if (!sessionState) return state;\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: { ...sessionState, vibeId, rootPath },\n },\n };\n }),\n\n hydrateSessionConfig: (sessionId, metadata) =>\n set((state) => {\n const current = state.sessionStates[sessionId] ?? createDefaultSessionState();\n const config = metadata.config ?? {};\n return {\n sessionStates: {\n ...state.sessionStates,\n [sessionId]: {\n ...current,\n sdk: asSdk(metadata.agentType) ?? current.sdk,\n mode: asMode(config.mode) ?? current.mode,\n model: typeof config.model === 'string' ? config.model : current.model,\n },\n },\n };\n }),\n\n // ── Sidebar actions ──\n\n toggleSidebar: () => set((state) => ({ sidebarOpen: !state.sidebarOpen })),\n\n setSidebarOpen: (open: boolean) => set({ sidebarOpen: open }),\n\n setSidebarSearchQuery: (query: string) => set({ sidebarSearchQuery: query }),\n\n setSidebarFilters: (filters: Partial<SessionFilters>) =>\n set((state) => ({\n sidebarFilters: { ...state.sidebarFilters, ...filters },\n })),\n\n resetSidebarFilters: () => set({ sidebarFilters: { ...defaultFilters } }),\n\n setSidebarGroupBy: (groupBy: SidebarGroupBy) => set({ sidebarGroupBy: groupBy }),\n\n // ── Detail panel actions ──\n\n toggleDetailPanel: () => set((state) => ({ detailPanelOpen: !state.detailPanelOpen })),\n\n setDetailPanelOpen: (open: boolean) => set({ detailPanelOpen: open }),\n\n setDetailPanelTab: (tab: DetailPanelTab) => set({ detailPanelTab: tab }),\n\n // ── Admin tab actions ──\n\n setAdminTab: (tab: AdminTab) => set({ adminTab: tab }),\n\n // ── Bulk selection actions ──\n\n toggleSessionSelection: (sessionId: string) =>\n set((state) => {\n const isSelected = state.selectedSessionIds.includes(sessionId);\n return {\n selectedSessionIds: isSelected\n ? state.selectedSessionIds.filter((id) => id !== sessionId)\n : [...state.selectedSessionIds, sessionId],\n };\n }),\n\n selectAllSessions: (sessionIds: string[]) => set({ selectedSessionIds: sessionIds }),\n\n clearSelection: () => set({ selectedSessionIds: [] }),\n\n // ── Fullscreen ──\n\n toggleFullscreen: () => set((state) => ({ isFullscreen: !state.isFullscreen })),\n\n // ── Reset ──\n\n resetStore: () =>\n set({\n tabs: [],\n activeTabSessionId: null,\n sessionStates: {},\n sidebarOpen: true,\n sidebarSearchQuery: '',\n sidebarFilters: { ...defaultFilters },\n sidebarGroupBy: 'date',\n detailPanelOpen: false,\n detailPanelTab: 'info',\n adminTab: 'templates',\n selectedSessionIds: [],\n }),\n }),\n {\n name: 'vibecontrols-agent-manager',\n storage: createProfileScopedStorage(),\n partialize: (state) => ({\n tabs: state.tabs,\n activeTabSessionId: state.activeTabSessionId,\n sidebarOpen: state.sidebarOpen,\n sidebarGroupBy: state.sidebarGroupBy,\n detailPanelOpen: state.detailPanelOpen,\n detailPanelTab: state.detailPanelTab,\n adminTab: state.adminTab,\n isFullscreen: state.isFullscreen,\n }),\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.514.2",
3
+ "version": "2026.514.4",
4
4
  "description": "VibeControls microfrontend for Burdenoff products",
5
5
  "type": "module",
6
6
  "files": [