@burdenoff/microfe-vibecontrols 2026.602.6 → 2026.602.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,7 +79,8 @@ function A(e, t) {
79
79
  return {
80
80
  content: [i, ...a].filter(Boolean).join("\n\n") || "Thinking...",
81
81
  done: !!r.info?.time?.completed && (!!i || a.length > 0),
82
- tokens: r.info?.tokens
82
+ tokens: r.info?.tokens,
83
+ model: r.info?.providerID && r.info?.modelID ? `${r.info.providerID}/${r.info.modelID}` : void 0
83
84
  };
84
85
  }
85
86
  function ue(e) {
@@ -183,7 +184,7 @@ var j = () => {
183
184
  }), h(s.content, _, n, t), s.tokens) {
184
185
  H(a, m, { tokens: s.tokens });
185
186
  try {
186
- await d(D, u, n, s.tokens, f), ne();
187
+ await d(D, u, n, m, s.model, s.tokens, f), ne();
187
188
  } catch {}
188
189
  }
189
190
  U(a, "complete", m, null);
@@ -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 checkAssistantAiCredits,\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantArtifacts,\n getAssistantMessages,\n getAssistantSession,\n reportAssistantAiTokenUsage,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n type AssistantRawMessage,\n type AssistantSandboxAuthContext,\n} from '@/services/assistantApi';\nimport { gatherPageContext } from '@/utils/pageContext';\nimport { dispatchAssistantQuotaExhausted, notifyAiCreditsUpdated } from '@/utils/aiCredits';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type {\n AssistantArtifact,\n AssistantConversationStatus,\n AssistantMode,\n Message,\n} from '@/types/assistant';\n\n// How long the client streams progress before giving up on a turn. Chat replies\n// (general / api-calls) finish in seconds; a vibe-plugins build is a multi-step\n// pipeline (read docs → scaffold → bun install → bun build → checklist) that runs\n// for minutes, so it gets a much larger budget. The sandbox TTL is auto-extended\n// every 30s during the poll loop, so a longer budget never outlives the sandbox.\nconst STREAM_BUDGET_MS: Record<AssistantMode, number> = {\n general: 180_000,\n 'api-calls': 180_000,\n 'vibe-plugins': 600_000,\n};\nconst DEFAULT_STREAM_BUDGET_MS = 180_000;\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\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\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 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 // Only block re-entry while a turn is genuinely in-flight. Earlier code\n // checked for *any* entry in the map, which stuck around as `complete`\n // / `error` after the first turn — making every subsequent message\n // silently disappear (the input cleared but handleSend bailed before\n // adding the user message to the conversation).\n if (activeConversationId) {\n const inflightStatus = processingByConversationId[activeConversationId]?.status;\n if (\n inflightStatus === 'provisioning' ||\n inflightStatus === 'connecting' ||\n inflightStatus === 'streaming'\n ) {\n return;\n }\n }\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 // Pre-flight AI-credit gate. The wspace gateway's @usage(checkQuota)\n // rejects with 429 QUOTA_EXHAUSTED when the monthly token budget is\n // depleted — block the send (don't provision a sandbox or dispatch the\n // prompt) and surface the upgrade dialog instead of a generic error.\n try {\n await checkAssistantAiCredits(apollo, workspaceId, authContext);\n } catch (creditError) {\n if (isQuotaExhaustedError(creditError)) {\n dispatchAssistantQuotaExhausted('ai-credits');\n applyAssistantResponse(\n \"You've run out of AI credits for this billing cycle. Upgrade your plan to keep chatting.\"\n );\n setConversationProcessing(conversationId, 'error', assistantMessageId, null);\n return;\n }\n // The pre-flight gate exists ONLY to catch a genuine QUOTA_EXHAUSTED\n // rejection. Any other failure here is a transient fault in the\n // credit check itself — e.g. the usage subgraph's getUsageSummary /\n // checkAiCredits intermittently 500s (Postgres/Prisma blip) and the\n // gateway masks it as the opaque \"Unexpected error.\". That is NOT\n // proof the user is out of budget, so it must not abort an otherwise\n // valid send. Fail open and proceed: the wspace gateway's\n // @usage(checkQuota) on createSandbox still enforces the real budget\n // downstream (a truly over-quota caller is stopped there with a 429\n // that surfaces the upgrade dialog via the outer catch).\n console.warn(\n '[assistant] AI-credit pre-flight check failed with a non-quota error; proceeding — gateway @usage(checkQuota) still enforces the real budget downstream',\n creditError\n );\n }\n\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 =\n Date.now() + (STREAM_BUDGET_MS[selectedMode] ?? DEFAULT_STREAM_BUDGET_MS);\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 // For non-build modes a timeout is a hard failure. For vibe-plugins the\n // prompt runs async server-side (/sessions/:id/prompt-async) and keeps\n // writing artifacts after the poll budget elapses, so we surface whatever\n // it has produced instead of discarding the turn.\n if (!result.done && selectedMode !== 'vibe-plugins') {\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 if (!result.done) {\n // vibe-plugins build still running in the background. Append a note so\n // the user knows the answer is partial and the artifacts will finish.\n result = {\n ...result,\n content: `${result.content}\\n\\n_Still building in the background — any artifacts below will finish shortly. Re-open this conversation to pick up the completed plugin._`,\n };\n }\n\n applyAssistantResponse(result.content, artifacts, sessionId, sandboxId);\n\n if (result.tokens) {\n patchMessage(conversationId, assistantMessageId, { tokens: result.tokens });\n // Charge the real token usage against the AI-credits quota (the\n // gateway records it via @usage(trackUsage)). We AWAIT it before\n // marking the turn complete / re-enabling input: otherwise a rapid\n // follow-up send would run its pre-flight checkAiCredits against a\n // stale balance (this turn's charge still in flight) and could\n // dispatch a prompt that should have been blocked, letting fast\n // sends bypass the credit cap. Still best-effort — a charge failure\n // never blocks the displayed answer.\n try {\n await reportAssistantAiTokenUsage(\n apollo,\n workspaceId,\n sessionId,\n result.tokens,\n authContext\n );\n notifyAiCreditsUpdated();\n } catch {\n // ignore — server-side @usage tracking is the source of truth\n }\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 await sendWithRealBackend();\n } catch (error) {\n // A quota error reaching here is the sandbox-environment wall hit\n // during runtime provisioning (createSandbox @usage). Surface the\n // upgrade dialog instead of a generic failure (the AI-credit wall is\n // already handled by the pre-flight gate above).\n if (isQuotaExhaustedError(error)) {\n dispatchAssistantQuotaExhausted('sandbox');\n applyAssistantResponse(\n \"You've reached your sandbox limit on the current plan. Upgrade to start the AI assistant.\"\n );\n setConversationProcessing(conversationId, 'error', assistantMessageId, null);\n return;\n }\n const errorMessage = error instanceof Error ? error.message : 'Something went wrong';\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 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":"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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 checkAssistantAiCredits,\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantArtifacts,\n getAssistantMessages,\n getAssistantSession,\n reportAssistantAiTokenUsage,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n type AssistantRawMessage,\n type AssistantSandboxAuthContext,\n} from '@/services/assistantApi';\nimport { gatherPageContext } from '@/utils/pageContext';\nimport { dispatchAssistantQuotaExhausted, notifyAiCreditsUpdated } from '@/utils/aiCredits';\nimport { isQuotaExhaustedError } from '@/utils/quotaUtils';\nimport { useTr } from '../../shared/hooks/useTr';\nimport type {\n AssistantArtifact,\n AssistantConversationStatus,\n AssistantMode,\n Message,\n} from '@/types/assistant';\n\n// How long the client streams progress before giving up on a turn. Chat replies\n// (general / api-calls) finish in seconds; a vibe-plugins build is a multi-step\n// pipeline (read docs → scaffold → bun install → bun build → checklist) that runs\n// for minutes, so it gets a much larger budget. The sandbox TTL is auto-extended\n// every 30s during the poll loop, so a longer budget never outlives the sandbox.\nconst STREAM_BUDGET_MS: Record<AssistantMode, number> = {\n general: 180_000,\n 'api-calls': 180_000,\n 'vibe-plugins': 600_000,\n};\nconst DEFAULT_STREAM_BUDGET_MS = 180_000;\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\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 model?: string;\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 // \"provider/model\" id for this turn — drives the cost-weighted credit charge.\n model:\n latest.info?.providerID && latest.info?.modelID\n ? `${latest.info.providerID}/${latest.info.modelID}`\n : undefined,\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\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 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 // Only block re-entry while a turn is genuinely in-flight. Earlier code\n // checked for *any* entry in the map, which stuck around as `complete`\n // / `error` after the first turn — making every subsequent message\n // silently disappear (the input cleared but handleSend bailed before\n // adding the user message to the conversation).\n if (activeConversationId) {\n const inflightStatus = processingByConversationId[activeConversationId]?.status;\n if (\n inflightStatus === 'provisioning' ||\n inflightStatus === 'connecting' ||\n inflightStatus === 'streaming'\n ) {\n return;\n }\n }\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 // Pre-flight AI-credit gate. The wspace gateway's @usage(checkQuota)\n // rejects with 429 QUOTA_EXHAUSTED when the monthly token budget is\n // depleted — block the send (don't provision a sandbox or dispatch the\n // prompt) and surface the upgrade dialog instead of a generic error.\n try {\n await checkAssistantAiCredits(apollo, workspaceId, authContext);\n } catch (creditError) {\n if (isQuotaExhaustedError(creditError)) {\n dispatchAssistantQuotaExhausted('ai-credits');\n applyAssistantResponse(\n \"You've run out of AI credits for this billing cycle. Upgrade your plan to keep chatting.\"\n );\n setConversationProcessing(conversationId, 'error', assistantMessageId, null);\n return;\n }\n // The pre-flight gate exists ONLY to catch a genuine QUOTA_EXHAUSTED\n // rejection. Any other failure here is a transient fault in the\n // credit check itself — e.g. the usage subgraph's getUsageSummary /\n // checkAiCredits intermittently 500s (Postgres/Prisma blip) and the\n // gateway masks it as the opaque \"Unexpected error.\". That is NOT\n // proof the user is out of budget, so it must not abort an otherwise\n // valid send. Fail open and proceed: the wspace gateway's\n // @usage(checkQuota) on createSandbox still enforces the real budget\n // downstream (a truly over-quota caller is stopped there with a 429\n // that surfaces the upgrade dialog via the outer catch).\n console.warn(\n '[assistant] AI-credit pre-flight check failed with a non-quota error; proceeding — gateway @usage(checkQuota) still enforces the real budget downstream',\n creditError\n );\n }\n\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 =\n Date.now() + (STREAM_BUDGET_MS[selectedMode] ?? DEFAULT_STREAM_BUDGET_MS);\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 // For non-build modes a timeout is a hard failure. For vibe-plugins the\n // prompt runs async server-side (/sessions/:id/prompt-async) and keeps\n // writing artifacts after the poll budget elapses, so we surface whatever\n // it has produced instead of discarding the turn.\n if (!result.done && selectedMode !== 'vibe-plugins') {\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 if (!result.done) {\n // vibe-plugins build still running in the background. Append a note so\n // the user knows the answer is partial and the artifacts will finish.\n result = {\n ...result,\n content: `${result.content}\\n\\n_Still building in the background — any artifacts below will finish shortly. Re-open this conversation to pick up the completed plugin._`,\n };\n }\n\n applyAssistantResponse(result.content, artifacts, sessionId, sandboxId);\n\n if (result.tokens) {\n patchMessage(conversationId, assistantMessageId, { tokens: result.tokens });\n // Charge the real token usage against the AI-credits quota (the\n // gateway records it via @usage(trackUsage)). We AWAIT it before\n // marking the turn complete / re-enabling input: otherwise a rapid\n // follow-up send would run its pre-flight checkAiCredits against a\n // stale balance (this turn's charge still in flight) and could\n // dispatch a prompt that should have been blocked, letting fast\n // sends bypass the credit cap. Still best-effort — a charge failure\n // never blocks the displayed answer.\n try {\n await reportAssistantAiTokenUsage(\n apollo,\n workspaceId,\n sessionId,\n assistantMessageId,\n result.model,\n result.tokens,\n authContext\n );\n notifyAiCreditsUpdated();\n } catch {\n // ignore — server-side @usage tracking is the source of truth\n }\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 await sendWithRealBackend();\n } catch (error) {\n // A quota error reaching here is the sandbox-environment wall hit\n // during runtime provisioning (createSandbox @usage). Surface the\n // upgrade dialog instead of a generic failure (the AI-credit wall is\n // already handled by the pre-flight gate above).\n if (isQuotaExhaustedError(error)) {\n dispatchAssistantQuotaExhausted('sandbox');\n applyAssistantResponse(\n \"You've reached your sandbox limit on the current plan. Upgrade to start the AI assistant.\"\n );\n setConversationProcessing(conversationId, 'error', assistantMessageId, null);\n return;\n }\n const errorMessage = error instanceof Error ? error.message : 'Something went wrong';\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 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":"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"}
@@ -7436,6 +7436,13 @@ var rm = e`
7436
7436
  query GetSession($id: ID!) {
7437
7437
  session(id: $id) {
7438
7438
  ...SessionFields
7439
+ isShared
7440
+ context {
7441
+ userId
7442
+ }
7443
+ collaborators {
7444
+ ...SessionCollaboratorFields
7445
+ }
7439
7446
  agent {
7440
7447
  id
7441
7448
  name
@@ -7462,7 +7469,8 @@ var rm = e`
7462
7469
  }
7463
7470
  }
7464
7471
  }
7465
- ${_}`;
7472
+ ${_}
7473
+ ${g}`;
7466
7474
  function im(e) {
7467
7475
  let r = {
7468
7476
  ...n,