@burdenoff/microfe-bigconsole 2026.713.3 → 2026.713.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.
@@ -231,7 +231,14 @@ async function D(e, t, n, r, i = 9e4) {
231
231
  async function O(e, t, n, r) {
232
232
  return { sessionId: (await E(e, t, "/sessions", "POST", { mode: n }, r)).session.id };
233
233
  }
234
- async function k(e, t, n, r, i, a, o) {
234
+ async function k(e, t, n) {
235
+ let r = (await E(e, t, "/sessions", "GET", void 0, n)).sessions;
236
+ return Array.isArray(r) ? r : [];
237
+ }
238
+ async function A(e, t, n, r) {
239
+ await E(e, t, `/sessions/${n}`, "DELETE", void 0, r);
240
+ }
241
+ async function j(e, t, n, r, i, a, o) {
235
242
  let s = {
236
243
  mode: i,
237
244
  prompt: r,
@@ -239,10 +246,10 @@ async function k(e, t, n, r, i, a, o) {
239
246
  };
240
247
  await E(e, t, `/sessions/${n}/prompt-async`, "POST", s, o);
241
248
  }
242
- async function A(e, t, n, r, i = 50) {
249
+ async function M(e, t, n, r, i = 50) {
243
250
  return (await E(e, t, `/sessions/${n}/messages?limit=${i}`, "GET", void 0, r)).messages ?? [];
244
251
  }
245
- async function j(e, n, r = 600, i) {
252
+ async function N(e, n, r = 600, i) {
246
253
  await x({
247
254
  document: t,
248
255
  variables: {
@@ -255,6 +262,6 @@ async function j(e, n, r = 600, i) {
255
262
  });
256
263
  }
257
264
  //#endregion
258
- export { w as createAssistantSandbox, O as createAssistantSession, j as extendAssistantSandboxTTL, C as findExistingSandbox, A as getAssistantMessages, k as sendAssistantPromptAsync, D as waitForAssistantServiceReady, T as waitForSandboxReady };
265
+ export { w as createAssistantSandbox, O as createAssistantSession, A as deleteAssistantSession, N as extendAssistantSandboxTTL, C as findExistingSandbox, M as getAssistantMessages, k as listAssistantSessions, j as sendAssistantPromptAsync, D as waitForAssistantServiceReady, T as waitForSandboxReady };
259
266
 
260
267
  //# sourceMappingURL=assistantApi.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"assistantApi.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantApi.ts"],"sourcesContent":["/**\n * AI Assistant API service for BigConsole.\n *\n * All requests go through the wspace-public-gateway (GraphQL Federation):\n * Frontend → wspace-public-gateway → wspace-sandbox-svc → ai-assistant (sandbox)\n *\n * This is a faithful port of microfe-vibecontrols' `services/assistantApi.ts`,\n * adapted for BigConsole: the only product-specific difference is\n * `AI_ASSISTANT_PRODUCT=bigconsole` in the sandbox container env (drives the\n * agent's per-product docs fetch + system prompt).\n */\n\nimport { print, type DocumentNode, Kind, type OperationDefinitionNode } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type { AssistantMode, AssistantRawMessage, AssistantSandboxAuthContext } from './types';\nimport type { PageContext } from './pageContext';\nimport {\n CreateSandboxDocument,\n GetSandboxDocument,\n GetActiveSandboxesDocument,\n ProxySandboxRequestDocument,\n ExtendSandboxTtlDocument,\n} from '../../generated/wspace-operations';\n\n// ── Image registry ─────────────────────────────────────────────────\n\nconst ACR_REGISTRY = 'burdenoffregistry.azurecr.io';\nfunction isProdRuntime(): boolean {\n if (typeof window === 'undefined') {\n return import.meta.env.VITE_DEPLOY_ENV === 'prod' || import.meta.env.VITE_APP_ENV === 'prod';\n }\n const host = window.location.hostname.toLowerCase();\n return host === 'app.bigconsole.com';\n}\n\nconst GENERAL_IMAGE_TAG = isProdRuntime() ? 'prod' : 'alpha-proxy-auth-v1';\nconst VIBE_PLUGINS_IMAGE_TAG = isProdRuntime() ? 'prod' : 'alpha-proxy-auth-v1';\nconst API_CALLS_IMAGE_TAG = isProdRuntime() ? 'prod' : 'alpha-delegated-auth-v11';\nconst ASSISTANT_API_AUTH_STRATEGY = 'sandbox-app-client-headers-v1';\n\nfunction getImageTagForMode(mode: AssistantMode): string {\n switch (mode) {\n case 'general':\n return GENERAL_IMAGE_TAG;\n case 'vibe-plugins':\n return VIBE_PLUGINS_IMAGE_TAG;\n // The combined default mode runs in the api-calls image (it can make calls).\n case 'assistant':\n case 'api-calls':\n return API_CALLS_IMAGE_TAG;\n }\n}\n\nfunction getImageForMode(mode: AssistantMode): string {\n // `assistant` reuses the api-calls image — one image, mode selected at runtime\n // via AI_ASSISTANT_MODE. So the image segment is api-calls for both.\n const imageMode = mode === 'assistant' ? 'api-calls' : mode;\n return `${ACR_REGISTRY}/wspace/ai-assistant-${imageMode}:${getImageTagForMode(mode)}`;\n}\n\nfunction getResourcesForMode(mode: AssistantMode): {\n cpuRequest: string;\n cpuLimit: string;\n memoryRequest: string;\n memoryLimit: string;\n} {\n switch (mode) {\n case 'general':\n return {\n cpuRequest: '100m',\n cpuLimit: '500m',\n memoryRequest: '256Mi',\n memoryLimit: '1Gi',\n };\n case 'vibe-plugins':\n case 'assistant':\n case 'api-calls':\n return {\n cpuRequest: '150m',\n cpuLimit: '750m',\n memoryRequest: '384Mi',\n memoryLimit: '1536Mi',\n };\n }\n}\n\nfunction resolveSandboxAssistantMode(sandbox: {\n metadata: Record<string, unknown> | null;\n name?: string | null;\n}): AssistantMode | null {\n const metadataMode = sandbox.metadata?.assistantMode;\n if (\n metadataMode === 'assistant' ||\n metadataMode === 'general' ||\n metadataMode === 'api-calls' ||\n metadataMode === 'vibe-plugins'\n ) {\n return metadataMode;\n }\n\n const name = sandbox.name ?? '';\n if (name.startsWith('ai-assistant-vibe-plugins-')) return 'vibe-plugins';\n if (name.startsWith('ai-assistant-api-calls-')) return 'api-calls';\n if (name.startsWith('ai-assistant-general-')) return 'general';\n if (name.startsWith('ai-assistant-assistant-')) return 'assistant';\n return null;\n}\n\n// ── Types ───────────────────────────────────────────────────────────\n\ninterface ProxyResponse {\n proxySandboxRequest: {\n status: number;\n body: Record<string, unknown>;\n };\n}\n\nfunction getOperationName(document: DocumentNode): string | undefined {\n const operation = document.definitions.find(\n (definition): definition is OperationDefinitionNode => definition.kind === Kind.OPERATION_DEFINITION\n );\n return operation?.name?.value;\n}\n\nfunction buildAssistantRequestId(): string {\n try {\n return `bc-assistant-${crypto.randomUUID()}`;\n } catch {\n return `bc-assistant-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n }\n}\n\nfunction buildSandboxContextInput(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Record<string, unknown> {\n return {\n workspaceId,\n ...(authContext?.organizationId\n ? {\n custom: {\n organizationId: authContext.organizationId,\n },\n }\n : {}),\n };\n}\n\nasync function executeSandboxGraphQL<TData>(params: {\n document: DocumentNode;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n // Retry transient failures (network drops, CORS preflight rejections,\n // gateway 524s, etc.) with bounded exponential backoff. The browser\n // surfaces these as \"Failed to fetch\" via fetch() throwing TypeError\n // before the runtime can inspect the response — so the only signal we\n // have is the throw itself. Bounded retries mask the gateway's\n // occasional CORS preflight races without papering over hard failures\n // (unauth, validation) which the inner block below raises as typed\n // errors that bubble up to the assistant's existing `isRecoverable`\n // retry path.\n const maxRetries = 3;\n let lastError: unknown;\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await executeSandboxGraphQLOnce<TData>(params);\n } catch (error) {\n lastError = error;\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n // Only retry on transient network-level failures. Let domain errors\n // (auth, validation, server 5xx with a body) bubble immediately so\n // the outer `isRecoverable` logic can decide whether to drop the\n // sandbox and try a clean runtime instead.\n const isTransient =\n message.includes('failed to fetch') ||\n message.includes('networkerror') ||\n message.includes('network request failed') ||\n message.includes('aborted') ||\n message.includes('load failed');\n if (!isTransient || attempt === maxRetries - 1) {\n throw error;\n }\n // 250ms, 500ms, 1000ms — bounded so we don't blow the 90s gateway\n // execution timeout on a stuck connection.\n const backoff = 250 * Math.pow(2, attempt);\n console.warn('[BigConsole-Assistant] executeSandboxGraphQL transient failure, retrying', {\n attempt: attempt + 1,\n maxRetries,\n backoffMs: backoff,\n error: error instanceof Error ? error.message : String(error),\n });\n await new Promise((resolve) => setTimeout(resolve, backoff));\n }\n }\n // Unreachable — the loop either returns or throws.\n throw lastError;\n}\n\nasync function executeSandboxGraphQLOnce<TData>(params: {\n document: DocumentNode;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n const operationName = getOperationName(params.document);\n const result = await graphqlFetch<TData>({\n gateway: 'workspace',\n query: print(params.document),\n variables: params.variables,\n operationName,\n authToken: params.authContext?.accessToken ?? undefined,\n // Critical: always opt into workspace-token auto-minting here instead of\n // passing through a cached token string from the shell. If we pass a stale\n // string, graphqlFetch validates it for the current workspace/project and\n // nulls it out, but it WILL NOT mint a replacement unless the caller used\n // `workspaceToken: true`. That exact behavior caused proxySandboxRequest\n // calls to fail with `Dual-token authentication failed` even though the\n // browser had a login session and the workspace context was known.\n workspaceToken: true,\n workspaceId: params.workspaceId,\n organizationId: params.authContext?.organizationId ?? true,\n suppressGlobalErrorEvent: true,\n // Give assistant calls a unique queue key so they never batch with the\n // dashboard's burst of background widget requests. This avoids mixing\n // long-lived assistant polls with unrelated queries in the shared\n // graphqlFetch batching queue.\n extraHeaders: {\n 'x-request-id': buildAssistantRequestId(),\n },\n });\n\n if (!result) {\n throw new Error('Sandbox GraphQL returned empty response');\n }\n\n if (result.errors?.length) {\n console.error('[BigConsole-Assistant] executeSandboxGraphQL errors', {\n operationName,\n errors: result.errors,\n });\n throw new Error(result.errors.map((error: { message: string }) => error.message).join(', '));\n }\n\n if (!result.data) {\n throw new Error('Sandbox GraphQL returned no data');\n }\n\n return result.data;\n}\n\n// ── Find existing sandbox for a mode ───────────────────────────────\n\nexport async function findExistingSandbox(\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<string | null> {\n const data = await executeSandboxGraphQL<{\n getActiveSandboxes: Array<{\n id: string;\n name: string;\n status: string;\n metadata: Record<string, unknown> | null;\n template: string;\n createdBy: string;\n }>;\n }>({\n document: GetActiveSandboxesDocument,\n variables: { context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandboxes = data.getActiveSandboxes ?? [];\n\n return (\n sandboxes.find(\n (s) =>\n (s.status === 'RUNNING' || s.status === 'PROVISIONING' || s.status === 'PENDING') &&\n s.template === 'AI_BUILDER' &&\n resolveSandboxAssistantMode(s) === mode &&\n (!authContext?.userId || s.createdBy === authContext.userId) &&\n s.metadata?.authStrategy === ASSISTANT_API_AUTH_STRATEGY &&\n s.metadata?.imageTag === getImageTagForMode(mode)\n )?.id ?? null\n );\n}\n\n// ── Sandbox lifecycle ──────────────────────────────────────────────\n\nexport async function createAssistantSandbox(\n mode: AssistantMode,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<string> {\n const resources = getResourcesForMode(mode);\n const env = [\n { name: 'AI_ASSISTANT_MODE', value: mode },\n { name: 'HOSTNAME', value: '0.0.0.0' },\n { name: 'WORKSPACE_ID', value: workspaceId },\n // Product the user is in — drives the agent's dynamic per-product docs fetch\n // (the docs repo is `<product>-docs`) and personalizes its system prompt.\n // This package is the BigConsole MFE, so the product is bigconsole.\n { name: 'AI_ASSISTANT_PRODUCT', value: 'bigconsole' },\n ];\n\n const data = await executeSandboxGraphQL<{ createSandbox: { id: string } }>({\n document: CreateSandboxDocument,\n variables: {\n input: {\n name: `ai-assistant-${mode}-${Date.now()}`,\n description: `AI Assistant sandbox (${mode} mode)`,\n template: 'AI_BUILDER',\n mode: 'DEPLOYMENT',\n containers: [\n {\n name: 'ai-assistant',\n image: getImageForMode(mode),\n // The agent (start-assistant.sh) binds :8080; the sandbox svc proxies\n // to this declared containerPort, so it MUST be 8080.\n ports: [{ containerPort: 8080, name: 'http' }],\n env,\n },\n ],\n resources: {\n cpuRequest: resources.cpuRequest,\n cpuLimit: resources.cpuLimit,\n memoryRequest: resources.memoryRequest,\n memoryLimit: resources.memoryLimit,\n },\n ttlSeconds: 600,\n metadata: {\n assistantMode: mode,\n assistantOwnerUserId: authContext?.userId ?? null,\n assistantOrganizationId: authContext?.organizationId ?? null,\n authStrategy: ASSISTANT_API_AUTH_STRATEGY,\n imageTag: getImageTagForMode(mode),\n },\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n if (!data?.createSandbox?.id) {\n throw new Error('createSandbox returned empty response');\n }\n return data.createSandbox.id;\n}\n\nexport async function waitForSandboxReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 300_000\n): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < maxWaitMs) {\n const data = await executeSandboxGraphQL<{\n getSandbox: { status?: string; errorMessage?: string } | null;\n }>({\n document: GetSandboxDocument,\n variables: { id: sandboxId, context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const status = data?.getSandbox?.status;\n const errorMessage = data?.getSandbox?.errorMessage as string | undefined;\n if (status === 'RUNNING') return;\n if (status === 'FAILED' || status === 'STOPPED' || status === 'EXPIRED') {\n throw new Error(\n errorMessage ? `Sandbox ${status.toLowerCase()}: ${errorMessage}` : `Sandbox ${status.toLowerCase()}`\n );\n }\n await new Promise((r) => setTimeout(r, 2000));\n }\n throw new Error('Sandbox startup timed out');\n}\n\n// ── Proxy calls to ai-assistant (via gateway → sandbox-svc) ───────\n\nasync function proxyToSandbox(\n sandboxId: string,\n workspaceId: string,\n path: string,\n method: string,\n body?: unknown,\n authContext?: AssistantSandboxAuthContext\n): Promise<Record<string, unknown>> {\n console.log('[BigConsole-Assistant] proxyToSandbox REQUEST', {\n sandboxId,\n workspaceId,\n path,\n method,\n hasBody: !!body,\n });\n const result = await executeSandboxGraphQL<ProxyResponse>({\n document: ProxySandboxRequestDocument,\n variables: {\n input: {\n sandboxId,\n path,\n method,\n body,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n const proxyResult = result.proxySandboxRequest;\n console.log('[BigConsole-Assistant] proxyToSandbox RESPONSE', { sandboxId, path, status: proxyResult?.status });\n if (!proxyResult || proxyResult.status >= 400) {\n const errBody = proxyResult?.body as Record<string, unknown> | undefined;\n console.error('[BigConsole-Assistant] proxyToSandbox ERROR', {\n sandboxId,\n path,\n status: proxyResult?.status,\n errBody,\n });\n throw new Error((errBody?.error as string) ?? `Proxy error: ${proxyResult?.status ?? 'unknown'}`);\n }\n\n return proxyResult.body;\n}\n\nexport async function waitForAssistantServiceReady(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 90_000\n): Promise<void> {\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < maxWaitMs) {\n try {\n // Use the documented assistant API contract itself as the readiness\n // signal. The assistant image definitively supports POST /sessions,\n // whereas /health is an implementation assumption and has proven to hang\n // behind the Cloudflare sandbox proxy.\n await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, authContext);\n return;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!/proxy error: 502|proxy error: 503|proxy error: 404|service not available|timeout/i.test(message)) {\n throw error;\n }\n }\n\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n throw new Error('Assistant service did not become ready before timeout');\n}\n\n// ── Session management ─────────────────────────────────────────────\n\nexport async function createAssistantSession(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<{ sessionId: string }> {\n const body = await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, authContext);\n const session = body.session as Record<string, unknown>;\n return { sessionId: session.id as string };\n}\n\n// ── Send prompt ────────────────────────────────────────────────────\n\nexport async function sendAssistantPromptAsync(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n prompt: string,\n mode: AssistantMode,\n pageContext?: PageContext,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n const requestBody = {\n mode,\n prompt,\n pageContext: pageContext ?? undefined,\n };\n\n await proxyToSandbox(sandboxId, workspaceId, `/sessions/${sessionId}/prompt-async`, 'POST', requestBody, authContext);\n}\n\nexport async function getAssistantMessages(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext,\n limit = 50\n): Promise<AssistantRawMessage[]> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/messages?limit=${limit}`,\n 'GET',\n undefined,\n authContext\n );\n\n return (body.messages as AssistantRawMessage[] | undefined) ?? [];\n}\n\n// ── TTL extension (keep the sandbox warm across the turn) ──────────\n\nexport async function extendAssistantSandboxTTL(\n sandboxId: string,\n workspaceId: string,\n additionalSeconds = 600,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await executeSandboxGraphQL<{ extendSandboxTTL: { id: string } }>({\n document: ExtendSandboxTtlDocument,\n variables: {\n id: sandboxId,\n additionalSeconds,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n workspaceId,\n authContext,\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"assistantApi.js","names":[],"sources":["../../../src/bigconsole/assistant/assistantApi.ts"],"sourcesContent":["/**\n * AI Assistant API service for BigConsole.\n *\n * All requests go through the wspace-public-gateway (GraphQL Federation):\n * Frontend → wspace-public-gateway → wspace-sandbox-svc → ai-assistant (sandbox)\n *\n * This is a faithful port of microfe-vibecontrols' `services/assistantApi.ts`,\n * adapted for BigConsole: the only product-specific difference is\n * `AI_ASSISTANT_PRODUCT=bigconsole` in the sandbox container env (drives the\n * agent's per-product docs fetch + system prompt).\n */\n\nimport { print, type DocumentNode, Kind, type OperationDefinitionNode } from 'graphql';\nimport { graphqlFetch } from '@burdenoff/fe-libs/shared/graphql';\nimport type { AssistantMode, AssistantRawMessage, AssistantRawSession, AssistantSandboxAuthContext } from './types';\nimport type { PageContext } from './pageContext';\nimport {\n CreateSandboxDocument,\n GetSandboxDocument,\n GetActiveSandboxesDocument,\n ProxySandboxRequestDocument,\n ExtendSandboxTtlDocument,\n} from '../../generated/wspace-operations';\n\n// ── Image registry ─────────────────────────────────────────────────\n\nconst ACR_REGISTRY = 'burdenoffregistry.azurecr.io';\nfunction isProdRuntime(): boolean {\n if (typeof window === 'undefined') {\n return import.meta.env.VITE_DEPLOY_ENV === 'prod' || import.meta.env.VITE_APP_ENV === 'prod';\n }\n const host = window.location.hostname.toLowerCase();\n return host === 'app.bigconsole.com';\n}\n\nconst GENERAL_IMAGE_TAG = isProdRuntime() ? 'prod' : 'alpha-proxy-auth-v1';\nconst VIBE_PLUGINS_IMAGE_TAG = isProdRuntime() ? 'prod' : 'alpha-proxy-auth-v1';\nconst API_CALLS_IMAGE_TAG = isProdRuntime() ? 'prod' : 'alpha-delegated-auth-v11';\nconst ASSISTANT_API_AUTH_STRATEGY = 'sandbox-app-client-headers-v1';\n\nfunction getImageTagForMode(mode: AssistantMode): string {\n switch (mode) {\n case 'general':\n return GENERAL_IMAGE_TAG;\n case 'vibe-plugins':\n return VIBE_PLUGINS_IMAGE_TAG;\n // The combined default mode runs in the api-calls image (it can make calls).\n case 'assistant':\n case 'api-calls':\n return API_CALLS_IMAGE_TAG;\n }\n}\n\nfunction getImageForMode(mode: AssistantMode): string {\n // `assistant` reuses the api-calls image — one image, mode selected at runtime\n // via AI_ASSISTANT_MODE. So the image segment is api-calls for both.\n const imageMode = mode === 'assistant' ? 'api-calls' : mode;\n return `${ACR_REGISTRY}/wspace/ai-assistant-${imageMode}:${getImageTagForMode(mode)}`;\n}\n\nfunction getResourcesForMode(mode: AssistantMode): {\n cpuRequest: string;\n cpuLimit: string;\n memoryRequest: string;\n memoryLimit: string;\n} {\n switch (mode) {\n case 'general':\n return {\n cpuRequest: '100m',\n cpuLimit: '500m',\n memoryRequest: '256Mi',\n memoryLimit: '1Gi',\n };\n case 'vibe-plugins':\n case 'assistant':\n case 'api-calls':\n return {\n cpuRequest: '150m',\n cpuLimit: '750m',\n memoryRequest: '384Mi',\n memoryLimit: '1536Mi',\n };\n }\n}\n\nfunction resolveSandboxAssistantMode(sandbox: {\n metadata: Record<string, unknown> | null;\n name?: string | null;\n}): AssistantMode | null {\n const metadataMode = sandbox.metadata?.assistantMode;\n if (\n metadataMode === 'assistant' ||\n metadataMode === 'general' ||\n metadataMode === 'api-calls' ||\n metadataMode === 'vibe-plugins'\n ) {\n return metadataMode;\n }\n\n const name = sandbox.name ?? '';\n if (name.startsWith('ai-assistant-vibe-plugins-')) return 'vibe-plugins';\n if (name.startsWith('ai-assistant-api-calls-')) return 'api-calls';\n if (name.startsWith('ai-assistant-general-')) return 'general';\n if (name.startsWith('ai-assistant-assistant-')) return 'assistant';\n return null;\n}\n\n// ── Types ───────────────────────────────────────────────────────────\n\ninterface ProxyResponse {\n proxySandboxRequest: {\n status: number;\n body: Record<string, unknown>;\n };\n}\n\nfunction getOperationName(document: DocumentNode): string | undefined {\n const operation = document.definitions.find(\n (definition): definition is OperationDefinitionNode => definition.kind === Kind.OPERATION_DEFINITION\n );\n return operation?.name?.value;\n}\n\nfunction buildAssistantRequestId(): string {\n try {\n return `bc-assistant-${crypto.randomUUID()}`;\n } catch {\n return `bc-assistant-${Date.now()}-${Math.random().toString(36).slice(2)}`;\n }\n}\n\nfunction buildSandboxContextInput(\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Record<string, unknown> {\n return {\n workspaceId,\n ...(authContext?.organizationId\n ? {\n custom: {\n organizationId: authContext.organizationId,\n },\n }\n : {}),\n };\n}\n\nasync function executeSandboxGraphQL<TData>(params: {\n document: DocumentNode;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n // Retry transient failures (network drops, CORS preflight rejections,\n // gateway 524s, etc.) with bounded exponential backoff. The browser\n // surfaces these as \"Failed to fetch\" via fetch() throwing TypeError\n // before the runtime can inspect the response — so the only signal we\n // have is the throw itself. Bounded retries mask the gateway's\n // occasional CORS preflight races without papering over hard failures\n // (unauth, validation) which the inner block below raises as typed\n // errors that bubble up to the assistant's existing `isRecoverable`\n // retry path.\n const maxRetries = 3;\n let lastError: unknown;\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n return await executeSandboxGraphQLOnce<TData>(params);\n } catch (error) {\n lastError = error;\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\n // Only retry on transient network-level failures. Let domain errors\n // (auth, validation, server 5xx with a body) bubble immediately so\n // the outer `isRecoverable` logic can decide whether to drop the\n // sandbox and try a clean runtime instead.\n const isTransient =\n message.includes('failed to fetch') ||\n message.includes('networkerror') ||\n message.includes('network request failed') ||\n message.includes('aborted') ||\n message.includes('load failed');\n if (!isTransient || attempt === maxRetries - 1) {\n throw error;\n }\n // 250ms, 500ms, 1000ms — bounded so we don't blow the 90s gateway\n // execution timeout on a stuck connection.\n const backoff = 250 * Math.pow(2, attempt);\n console.warn('[BigConsole-Assistant] executeSandboxGraphQL transient failure, retrying', {\n attempt: attempt + 1,\n maxRetries,\n backoffMs: backoff,\n error: error instanceof Error ? error.message : String(error),\n });\n await new Promise((resolve) => setTimeout(resolve, backoff));\n }\n }\n // Unreachable — the loop either returns or throws.\n throw lastError;\n}\n\nasync function executeSandboxGraphQLOnce<TData>(params: {\n document: DocumentNode;\n variables: Record<string, unknown>;\n workspaceId: string;\n authContext?: AssistantSandboxAuthContext;\n}): Promise<TData> {\n const operationName = getOperationName(params.document);\n const result = await graphqlFetch<TData>({\n gateway: 'workspace',\n query: print(params.document),\n variables: params.variables,\n operationName,\n authToken: params.authContext?.accessToken ?? undefined,\n // Critical: always opt into workspace-token auto-minting here instead of\n // passing through a cached token string from the shell. If we pass a stale\n // string, graphqlFetch validates it for the current workspace/project and\n // nulls it out, but it WILL NOT mint a replacement unless the caller used\n // `workspaceToken: true`. That exact behavior caused proxySandboxRequest\n // calls to fail with `Dual-token authentication failed` even though the\n // browser had a login session and the workspace context was known.\n workspaceToken: true,\n workspaceId: params.workspaceId,\n organizationId: params.authContext?.organizationId ?? true,\n suppressGlobalErrorEvent: true,\n // Give assistant calls a unique queue key so they never batch with the\n // dashboard's burst of background widget requests. This avoids mixing\n // long-lived assistant polls with unrelated queries in the shared\n // graphqlFetch batching queue.\n extraHeaders: {\n 'x-request-id': buildAssistantRequestId(),\n },\n });\n\n if (!result) {\n throw new Error('Sandbox GraphQL returned empty response');\n }\n\n if (result.errors?.length) {\n console.error('[BigConsole-Assistant] executeSandboxGraphQL errors', {\n operationName,\n errors: result.errors,\n });\n throw new Error(result.errors.map((error: { message: string }) => error.message).join(', '));\n }\n\n if (!result.data) {\n throw new Error('Sandbox GraphQL returned no data');\n }\n\n return result.data;\n}\n\n// ── Find existing sandbox for a mode ───────────────────────────────\n\nexport async function findExistingSandbox(\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<string | null> {\n const data = await executeSandboxGraphQL<{\n getActiveSandboxes: Array<{\n id: string;\n name: string;\n status: string;\n metadata: Record<string, unknown> | null;\n template: string;\n createdBy: string;\n }>;\n }>({\n document: GetActiveSandboxesDocument,\n variables: { context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const sandboxes = data.getActiveSandboxes ?? [];\n\n return (\n sandboxes.find(\n (s) =>\n (s.status === 'RUNNING' || s.status === 'PROVISIONING' || s.status === 'PENDING') &&\n s.template === 'AI_BUILDER' &&\n resolveSandboxAssistantMode(s) === mode &&\n (!authContext?.userId || s.createdBy === authContext.userId) &&\n s.metadata?.authStrategy === ASSISTANT_API_AUTH_STRATEGY &&\n s.metadata?.imageTag === getImageTagForMode(mode)\n )?.id ?? null\n );\n}\n\n// ── Sandbox lifecycle ──────────────────────────────────────────────\n\nexport async function createAssistantSandbox(\n mode: AssistantMode,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<string> {\n const resources = getResourcesForMode(mode);\n const env = [\n { name: 'AI_ASSISTANT_MODE', value: mode },\n { name: 'HOSTNAME', value: '0.0.0.0' },\n { name: 'WORKSPACE_ID', value: workspaceId },\n // Product the user is in — drives the agent's dynamic per-product docs fetch\n // (the docs repo is `<product>-docs`) and personalizes its system prompt.\n // This package is the BigConsole MFE, so the product is bigconsole.\n { name: 'AI_ASSISTANT_PRODUCT', value: 'bigconsole' },\n ];\n\n const data = await executeSandboxGraphQL<{ createSandbox: { id: string } }>({\n document: CreateSandboxDocument,\n variables: {\n input: {\n name: `ai-assistant-${mode}-${Date.now()}`,\n description: `AI Assistant sandbox (${mode} mode)`,\n template: 'AI_BUILDER',\n mode: 'DEPLOYMENT',\n containers: [\n {\n name: 'ai-assistant',\n image: getImageForMode(mode),\n // The agent (start-assistant.sh) binds :8080; the sandbox svc proxies\n // to this declared containerPort, so it MUST be 8080.\n ports: [{ containerPort: 8080, name: 'http' }],\n env,\n },\n ],\n resources: {\n cpuRequest: resources.cpuRequest,\n cpuLimit: resources.cpuLimit,\n memoryRequest: resources.memoryRequest,\n memoryLimit: resources.memoryLimit,\n },\n ttlSeconds: 600,\n metadata: {\n assistantMode: mode,\n assistantOwnerUserId: authContext?.userId ?? null,\n assistantOrganizationId: authContext?.organizationId ?? null,\n authStrategy: ASSISTANT_API_AUTH_STRATEGY,\n imageTag: getImageTagForMode(mode),\n },\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n if (!data?.createSandbox?.id) {\n throw new Error('createSandbox returned empty response');\n }\n return data.createSandbox.id;\n}\n\nexport async function waitForSandboxReady(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 300_000\n): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < maxWaitMs) {\n const data = await executeSandboxGraphQL<{\n getSandbox: { status?: string; errorMessage?: string } | null;\n }>({\n document: GetSandboxDocument,\n variables: { id: sandboxId, context: buildSandboxContextInput(workspaceId, authContext) },\n workspaceId,\n authContext,\n });\n const status = data?.getSandbox?.status;\n const errorMessage = data?.getSandbox?.errorMessage as string | undefined;\n if (status === 'RUNNING') return;\n if (status === 'FAILED' || status === 'STOPPED' || status === 'EXPIRED') {\n throw new Error(\n errorMessage ? `Sandbox ${status.toLowerCase()}: ${errorMessage}` : `Sandbox ${status.toLowerCase()}`\n );\n }\n await new Promise((r) => setTimeout(r, 2000));\n }\n throw new Error('Sandbox startup timed out');\n}\n\n// ── Proxy calls to ai-assistant (via gateway → sandbox-svc) ───────\n\nasync function proxyToSandbox(\n sandboxId: string,\n workspaceId: string,\n path: string,\n method: string,\n body?: unknown,\n authContext?: AssistantSandboxAuthContext\n): Promise<Record<string, unknown>> {\n console.log('[BigConsole-Assistant] proxyToSandbox REQUEST', {\n sandboxId,\n workspaceId,\n path,\n method,\n hasBody: !!body,\n });\n const result = await executeSandboxGraphQL<ProxyResponse>({\n document: ProxySandboxRequestDocument,\n variables: {\n input: {\n sandboxId,\n path,\n method,\n body,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n },\n workspaceId,\n authContext,\n });\n\n const proxyResult = result.proxySandboxRequest;\n console.log('[BigConsole-Assistant] proxyToSandbox RESPONSE', { sandboxId, path, status: proxyResult?.status });\n if (!proxyResult || proxyResult.status >= 400) {\n const errBody = proxyResult?.body as Record<string, unknown> | undefined;\n console.error('[BigConsole-Assistant] proxyToSandbox ERROR', {\n sandboxId,\n path,\n status: proxyResult?.status,\n errBody,\n });\n throw new Error((errBody?.error as string) ?? `Proxy error: ${proxyResult?.status ?? 'unknown'}`);\n }\n\n return proxyResult.body;\n}\n\nexport async function waitForAssistantServiceReady(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext,\n maxWaitMs = 90_000\n): Promise<void> {\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < maxWaitMs) {\n try {\n // Use the documented assistant API contract itself as the readiness\n // signal. The assistant image definitively supports POST /sessions,\n // whereas /health is an implementation assumption and has proven to hang\n // behind the Cloudflare sandbox proxy.\n await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, authContext);\n return;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n if (!/proxy error: 502|proxy error: 503|proxy error: 404|service not available|timeout/i.test(message)) {\n throw error;\n }\n }\n\n await new Promise((resolve) => setTimeout(resolve, 2000));\n }\n\n throw new Error('Assistant service did not become ready before timeout');\n}\n\n// ── Session management ─────────────────────────────────────────────\n\nexport async function createAssistantSession(\n sandboxId: string,\n workspaceId: string,\n mode: AssistantMode,\n authContext?: AssistantSandboxAuthContext\n): Promise<{ sessionId: string }> {\n const body = await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'POST', { mode }, authContext);\n const session = body.session as Record<string, unknown>;\n return { sessionId: session.id as string };\n}\n\n/**\n * All conversations held by this sandbox, newest first.\n *\n * The sandbox is the source of truth for the transcript, so the History list is\n * built from it rather than from anything we cache client-side — what the user\n * can reopen is exactly what the agent still has.\n */\nexport async function listAssistantSessions(\n sandboxId: string,\n workspaceId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<AssistantRawSession[]> {\n const body = await proxyToSandbox(sandboxId, workspaceId, '/sessions', 'GET', undefined, authContext);\n const sessions = body.sessions;\n return Array.isArray(sessions) ? (sessions as AssistantRawSession[]) : [];\n}\n\nexport async function deleteAssistantSession(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await proxyToSandbox(sandboxId, workspaceId, `/sessions/${sessionId}`, 'DELETE', undefined, authContext);\n}\n\n// ── Send prompt ────────────────────────────────────────────────────\n\nexport async function sendAssistantPromptAsync(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n prompt: string,\n mode: AssistantMode,\n pageContext?: PageContext,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n const requestBody = {\n mode,\n prompt,\n pageContext: pageContext ?? undefined,\n };\n\n await proxyToSandbox(sandboxId, workspaceId, `/sessions/${sessionId}/prompt-async`, 'POST', requestBody, authContext);\n}\n\nexport async function getAssistantMessages(\n sandboxId: string,\n workspaceId: string,\n sessionId: string,\n authContext?: AssistantSandboxAuthContext,\n limit = 50\n): Promise<AssistantRawMessage[]> {\n const body = await proxyToSandbox(\n sandboxId,\n workspaceId,\n `/sessions/${sessionId}/messages?limit=${limit}`,\n 'GET',\n undefined,\n authContext\n );\n\n return (body.messages as AssistantRawMessage[] | undefined) ?? [];\n}\n\n// ── TTL extension (keep the sandbox warm across the turn) ──────────\n\nexport async function extendAssistantSandboxTTL(\n sandboxId: string,\n workspaceId: string,\n additionalSeconds = 600,\n authContext?: AssistantSandboxAuthContext\n): Promise<void> {\n await executeSandboxGraphQL<{ extendSandboxTTL: { id: string } }>({\n document: ExtendSandboxTtlDocument,\n variables: {\n id: sandboxId,\n additionalSeconds,\n context: buildSandboxContextInput(workspaceId, authContext),\n },\n workspaceId,\n authContext,\n });\n}\n"],"mappings":"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"}
@@ -1,22 +1,22 @@
1
- import { createAssistantSandbox as e, createAssistantSession as t, extendAssistantSandboxTTL as n, findExistingSandbox as r, getAssistantMessages as i, sendAssistantPromptAsync as a, waitForAssistantServiceReady as o, waitForSandboxReady as s } from "./assistantApi.js";
2
- import { gatherPageContext as c } from "./pageContext.js";
3
- import { useCallback as l, useMemo as u, useRef as d } from "react";
4
- import { useAuthToken as f } from "@burdenoff/fe-libs/shared/providers/shell";
1
+ import { createAssistantSandbox as e, createAssistantSession as t, deleteAssistantSession as n, extendAssistantSandboxTTL as r, findExistingSandbox as i, getAssistantMessages as a, listAssistantSessions as o, sendAssistantPromptAsync as s, waitForAssistantServiceReady as c, waitForSandboxReady as l } from "./assistantApi.js";
2
+ import { gatherPageContext as u } from "./pageContext.js";
3
+ import { useCallback as d, useMemo as f, useRef as p } from "react";
4
+ import { useAuthToken as m } from "@burdenoff/fe-libs/shared/providers/shell";
5
5
  //#region src/bigconsole/assistant/createSandboxAssistantTransport.ts
6
- var p = "api-calls", m = 42e4, h = 1500, g = 3e4, _ = 200, v = "bc-assistant-sandbox-id", y = "bc-assistant-session-id";
7
- function b(e) {
6
+ var h = "api-calls", g = 42e4, _ = 1500, v = 3e4, y = 200, b = "bc-assistant-sandbox-id", x = "bc-assistant-session-id";
7
+ function S(e) {
8
8
  try {
9
9
  return window.sessionStorage.getItem(e);
10
10
  } catch {
11
11
  return null;
12
12
  }
13
13
  }
14
- function x(e, t) {
14
+ function C(e, t) {
15
15
  try {
16
16
  t ? window.sessionStorage.setItem(e, t) : window.sessionStorage.removeItem(e);
17
17
  } catch {}
18
18
  }
19
- function S(e) {
19
+ function w(e) {
20
20
  try {
21
21
  let t = e === "workspaceId" ? "burdenoff-active-context-workspace" : "burdenoff-active-context-organization", n = localStorage.getItem(t);
22
22
  if (n) return n;
@@ -28,16 +28,16 @@ function S(e) {
28
28
  return "";
29
29
  }
30
30
  }
31
- function C(e) {
32
- return new URLSearchParams(window.location.search).get("workspace") ?? S("workspaceId") ?? e ?? "";
31
+ function T(e) {
32
+ return new URLSearchParams(window.location.search).get("workspace") ?? w("workspaceId") ?? e ?? "";
33
33
  }
34
- function w() {
35
- return new URLSearchParams(window.location.search).get("org") ?? S("organizationId");
34
+ function E() {
35
+ return new URLSearchParams(window.location.search).get("org") ?? w("organizationId");
36
36
  }
37
- function T(e) {
37
+ function D(e) {
38
38
  return e.info?.role ?? e.role;
39
39
  }
40
- function E(e) {
40
+ function O(e) {
41
41
  let t = e.info?.time?.created;
42
42
  if (t !== void 0) return t;
43
43
  let n = e.createdAt;
@@ -48,7 +48,7 @@ function E(e) {
48
48
  }
49
49
  return n;
50
50
  }
51
- function D(e) {
51
+ function k(e) {
52
52
  let t = e.info?.time?.completed;
53
53
  if (t !== void 0) return t;
54
54
  let n = e.completedAt;
@@ -60,10 +60,10 @@ function D(e) {
60
60
  return n;
61
61
  }
62
62
  }
63
- function O(e) {
63
+ function A(e) {
64
64
  return (e.parts ?? [] ?? []).filter((e) => e.type === "text" && typeof e.text == "string").map((e) => e.text?.trim() ?? "").filter(Boolean).join("\n") || (typeof e.content == "string" ? e.content.trim() : "");
65
65
  }
66
- var k = {
66
+ var j = {
67
67
  bash: "Running a command",
68
68
  webfetch: "Fetching a page",
69
69
  "file.read": "Reading files",
@@ -74,83 +74,83 @@ var k = {
74
74
  todowrite: "Planning the steps",
75
75
  todoread: "Reviewing the plan"
76
76
  };
77
- function A(e) {
77
+ function M(e) {
78
78
  return typeof e == "object" && e ? e : void 0;
79
79
  }
80
- function j(e) {
81
- let t = e.tool ?? "tool", n = A(e.state?.input), r = n?.description;
80
+ function N(e) {
81
+ let t = e.tool ?? "tool", n = M(e.state?.input), r = n?.description;
82
82
  if (typeof r == "string" && r.trim()) return r.trim();
83
83
  let i = n?.todos;
84
84
  if (Array.isArray(i)) {
85
- let e = A(i.find((e) => A(e)?.status === "in_progress") ?? i[0])?.content;
85
+ let e = M(i.find((e) => M(e)?.status === "in_progress") ?? i[0])?.content;
86
86
  if (typeof e == "string" && e.trim()) return e.trim();
87
87
  }
88
- return k[t] ?? `Running ${t}`;
88
+ return j[t] ?? `Running ${t}`;
89
89
  }
90
- function M(e) {
90
+ function P(e) {
91
91
  return (e.parts ?? []).filter((e) => e.type === "tool" && e.tool).map((e) => {
92
- let t = e.state?.status ?? "running", n = j(e);
92
+ let t = e.state?.status ?? "running", n = N(e);
93
93
  return t === "completed" ? `✓ ${n}` : t === "failed" ? `⚠ ${n}` : `⏳ ${n}…`;
94
94
  });
95
95
  }
96
- function N(e, t, n, r) {
97
- let i = e.filter((e) => T(e) === "assistant" && E(e) >= t).sort((e, t) => E(e) - E(t));
96
+ function F(e, t, n, r) {
97
+ let i = e.filter((e) => D(e) === "assistant" && O(e) >= t).sort((e, t) => O(e) - O(t));
98
98
  i.length === 0 && e.length > 0 && console.log("[BigConsole-Assistant] buildProgress: no relevant messages", {
99
99
  totalMessages: e.length,
100
100
  sinceMs: t,
101
- messageRoles: e.map((e) => T(e)),
102
- messageTimestamps: e.map((e) => E(e))
101
+ messageRoles: e.map((e) => D(e)),
102
+ messageTimestamps: e.map((e) => O(e))
103
103
  });
104
104
  let a, o;
105
- if (i.length === 0) a = "Thinking…", o = n === a && r !== void 0 && Date.now() - r >= 15e3;
105
+ if (i.length === 0) a = "", o = n === a && r !== void 0 && Date.now() - r >= 15e3;
106
106
  else {
107
- let e = i[i.length - 1], t = O(e), s = M(e);
108
- a = [t, ...s].filter(Boolean).join("\n\n") || "Thinking…";
109
- let c = !!D(e) && (!!t || s.length > 0), l = !c && n === a && r !== void 0 && Date.now() - r >= 15e3;
110
- o = c || l;
107
+ let e = i[i.length - 1], t = i.map(A).filter(Boolean), s = P(e);
108
+ a = [...t, ...s].join("\n\n");
109
+ let c = !!k(e) && a.length > 0, l = (e.parts ?? []).some((e) => e.type === "tool" && e.state?.status !== "completed" && e.state?.status !== "failed"), u = !c && !l && n === a && r !== void 0 && Date.now() - r >= 45e3;
110
+ o = c || u;
111
111
  }
112
112
  return {
113
113
  content: a,
114
114
  done: o
115
115
  };
116
116
  }
117
- function P(e) {
117
+ function I(e) {
118
118
  return e.info?.id ?? e.id;
119
119
  }
120
- var F = ["[Context file:", "User's current context:"];
121
- function I(e) {
120
+ var L = ["[Context file:", "User's current context:"];
121
+ function R(e) {
122
122
  let t = e.trimStart();
123
- return F.some((e) => t.startsWith(e));
123
+ return L.some((e) => t.startsWith(e));
124
124
  }
125
- function L(e) {
126
- let t = [...e].sort((e, t) => E(e) - E(t)), n = [], r = null, i = "", a = () => {
125
+ function z(e) {
126
+ let t = [...e].sort((e, t) => O(e) - O(t)), n = [], r = null, i = [], a = () => {
127
127
  let e = r;
128
128
  e && (n.push({
129
129
  id: e.id,
130
130
  role: "user",
131
131
  content: e.content
132
- }), i && n.push({
132
+ }), i.length > 0 && n.push({
133
133
  id: `${e.id}:reply`,
134
134
  role: "assistant",
135
- content: R(i)
136
- }), r = null, i = "");
135
+ content: B(i.join("\n\n"))
136
+ }), r = null, i = []);
137
137
  };
138
138
  for (let e of t) {
139
- let t = T(e), o = O(e);
139
+ let t = D(e), o = A(e);
140
140
  if (t === "user") {
141
- if (!o || I(o)) continue;
141
+ if (!o || R(o)) continue;
142
142
  a(), r = {
143
- id: P(e) ?? `user-${String(n.length)}`,
143
+ id: I(e) ?? `user-${String(n.length)}`,
144
144
  content: o
145
145
  };
146
- } else t === "assistant" && r && o && (i = o);
146
+ } else t === "assistant" && r && o && i.push(o);
147
147
  }
148
148
  return a(), n;
149
149
  }
150
- function R(e) {
150
+ function B(e) {
151
151
  return e.replace(/(Authorization\s*:\s*Bearer\s+)[^\s\n]+/gi, "$1[REDACTED]").replace(/(X-Workspace-Authorization\s*:\s*Bearer\s+)[^\s\n]+/gi, "$1[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]+\.[A-Za-z0-9._-]+\.[A-Za-z0-9._-]+\b/g, "[REDACTED_JWT]").replace(/\bsk-ant-[A-Za-z0-9-]+\b/g, "[REDACTED_API_KEY]");
152
152
  }
153
- function z(e) {
153
+ function V(e) {
154
154
  let t = e instanceof Error ? e.message.toLowerCase() : String(e).toLowerCase();
155
155
  return t.includes("rate limit exceeded") || t.includes("unauthorized") || t.includes("k8s api error 401") ? !1 : [
156
156
  "sandbox not found",
@@ -171,154 +171,216 @@ function z(e) {
171
171
  "unexpected error"
172
172
  ].some((e) => t.includes(e));
173
173
  }
174
- function B() {
175
- let { getAccessToken: S, getWorkspaceToken: D, userId: O, workspaceId: k } = f(), [A, j] = u(() => [b(v), b(y)], []), M = d(A), P = d(j), F = l((e) => {
176
- M.current = e, x(v, e);
177
- }, []), I = l((e) => {
178
- P.current = e, x(y, e);
179
- }, []), B = l(async (n, i) => {
180
- let a = M.current;
174
+ function H() {
175
+ let { getAccessToken: w, getWorkspaceToken: k, userId: A, workspaceId: j } = m(), [M, N] = f(() => [S(b), S(x)], []), P = p(M), I = p(N), L = d((e) => {
176
+ P.current = e, C(b, e);
177
+ }, []), R = d((e) => {
178
+ I.current = e, C(x, e);
179
+ }, []), H = d(async (n, r) => {
180
+ let a = P.current;
181
181
  if (console.log("[BigConsole-Assistant] ensureRuntime start", {
182
182
  sandboxId: a,
183
183
  workspaceId: n
184
184
  }), !a) {
185
- if (!S()) throw Error("The assistant requires an authenticated session. Please sign in again.");
186
- if (console.log("[BigConsole-Assistant] findExistingSandbox called"), a = await r(n, p, i), console.log("[BigConsole-Assistant] findExistingSandbox result", { sandboxId: a }), a) try {
187
- console.log("[BigConsole-Assistant] waitForSandboxReady called (reused)", { sandboxId: a }), await s(a, n, i), console.log("[BigConsole-Assistant] waitForSandboxReady done (reused)", { sandboxId: a }), console.log("[BigConsole-Assistant] waitForAssistantServiceReady called (reused)", { sandboxId: a }), await o(a, n, p, i), console.log("[BigConsole-Assistant] waitForAssistantServiceReady done (reused)", { sandboxId: a });
185
+ if (!w()) throw Error("The assistant requires an authenticated session. Please sign in again.");
186
+ if (console.log("[BigConsole-Assistant] findExistingSandbox called"), a = await i(n, h, r), console.log("[BigConsole-Assistant] findExistingSandbox result", { sandboxId: a }), a) try {
187
+ console.log("[BigConsole-Assistant] waitForSandboxReady called (reused)", { sandboxId: a }), await l(a, n, r), console.log("[BigConsole-Assistant] waitForSandboxReady done (reused)", { sandboxId: a }), console.log("[BigConsole-Assistant] waitForAssistantServiceReady called (reused)", { sandboxId: a }), await c(a, n, h, r), console.log("[BigConsole-Assistant] waitForAssistantServiceReady done (reused)", { sandboxId: a });
188
188
  } catch (e) {
189
189
  console.warn("[BigConsole-Assistant] existing sandbox unusable, falling back to fresh sandbox", {
190
190
  sandboxId: a,
191
191
  error: e instanceof Error ? e.message : String(e)
192
192
  }), a = null;
193
193
  }
194
- a || (console.log("[BigConsole-Assistant] createAssistantSandbox called"), a = await e(p, n, i), console.log("[BigConsole-Assistant] createAssistantSandbox result", { sandboxId: a }), console.log("[BigConsole-Assistant] waitForSandboxReady called (fresh)", { sandboxId: a }), await s(a, n, i), console.log("[BigConsole-Assistant] waitForSandboxReady done (fresh)", { sandboxId: a }), console.log("[BigConsole-Assistant] waitForAssistantServiceReady called (fresh)", { sandboxId: a }), await o(a, n, p, i), console.log("[BigConsole-Assistant] waitForAssistantServiceReady done (fresh)", { sandboxId: a })), F(a);
194
+ a || (console.log("[BigConsole-Assistant] createAssistantSandbox called"), a = await e(h, n, r), console.log("[BigConsole-Assistant] createAssistantSandbox result", { sandboxId: a }), console.log("[BigConsole-Assistant] waitForSandboxReady called (fresh)", { sandboxId: a }), await l(a, n, r), console.log("[BigConsole-Assistant] waitForSandboxReady done (fresh)", { sandboxId: a }), console.log("[BigConsole-Assistant] waitForAssistantServiceReady called (fresh)", { sandboxId: a }), await c(a, n, h, r), console.log("[BigConsole-Assistant] waitForAssistantServiceReady done (fresh)", { sandboxId: a })), L(a);
195
195
  }
196
- let c = P.current;
196
+ let o = I.current;
197
197
  return console.log("[BigConsole-Assistant] session check", {
198
- sessionId: c,
198
+ sessionId: o,
199
199
  sandboxId: a
200
- }), c || (console.log("[BigConsole-Assistant] createAssistantSession called", {
200
+ }), o || (console.log("[BigConsole-Assistant] createAssistantSession called", {
201
201
  sandboxId: a,
202
202
  workspaceId: n
203
- }), c = (await t(a, n, p, i)).sessionId, I(c)), {
203
+ }), o = (await t(a, n, h, r)).sessionId, R(o)), {
204
204
  sandboxId: a,
205
- sessionId: c
205
+ sessionId: o
206
206
  };
207
- }, [S]), V = l(async ({ prompt: e, onProgress: t, signal: r }) => {
208
- let o = C(k);
207
+ }, [w]), U = d(async ({ prompt: e, onProgress: t, signal: n }) => {
208
+ let i = T(j);
209
209
  if (console.log("[BigConsole-Assistant] sendPrompt called", {
210
210
  promptLength: e.length,
211
- workspaceId: o,
212
- hasCtxWorkspaceId: !!k,
211
+ workspaceId: i,
212
+ hasCtxWorkspaceId: !!j,
213
213
  authContextKeys: {
214
- hasAccessToken: !!S(),
215
- hasWorkspaceToken: !!D(),
216
- hasUserId: !!O
214
+ hasAccessToken: !!w(),
215
+ hasWorkspaceToken: !!k(),
216
+ hasUserId: !!A
217
217
  },
218
218
  locationSearch: window.location.search
219
- }), !o) throw Error("The assistant needs an active workspace. Open a workspace and try again.");
220
- let s = {
221
- accessToken: S(),
222
- workspaceToken: D(),
223
- userId: O,
224
- organizationId: w()
219
+ }), !i) throw Error("The assistant needs an active workspace. Open a workspace and try again.");
220
+ let o = {
221
+ accessToken: w(),
222
+ workspaceToken: k(),
223
+ userId: A,
224
+ organizationId: E()
225
225
  };
226
226
  console.log("[BigConsole-Assistant] authContext prepared", {
227
- hasAccessToken: !!s.accessToken,
228
- hasWorkspaceToken: !!s.workspaceToken,
229
- hasUserId: !!s.userId,
230
- hasOrgId: !!s.organizationId
227
+ hasAccessToken: !!o.accessToken,
228
+ hasWorkspaceToken: !!o.workspaceToken,
229
+ hasUserId: !!o.userId,
230
+ hasOrgId: !!o.organizationId
231
231
  });
232
- let l = async (u) => {
232
+ let c = async (l) => {
233
233
  try {
234
- console.log("[BigConsole-Assistant] run attempt", u);
235
- let { sandboxId: l, sessionId: d } = await B(o, s);
234
+ console.log("[BigConsole-Assistant] run attempt", l);
235
+ let { sandboxId: c, sessionId: d } = await H(i, o);
236
236
  console.log("[BigConsole-Assistant] ensureRuntime resolved", {
237
- sandboxId: l,
237
+ sandboxId: c,
238
238
  sessionId: d
239
239
  });
240
240
  let f = Date.now();
241
241
  console.log("[BigConsole-Assistant] calling sendAssistantPromptAsync", {
242
- sandboxId: l,
243
- workspaceId: o,
242
+ sandboxId: c,
243
+ workspaceId: i,
244
244
  sessionId: d,
245
245
  promptLength: e.length
246
- }), await a(l, o, d, e, p, c(), s);
247
- let _ = Date.now() + m, v = Date.now(), y = "", b = Date.now(), x = N([], f);
248
- for (; Date.now() < _;) {
249
- if (r.aborted) throw Error("Cancelled");
250
- let e = await i(l, o, d, s, 50);
246
+ }), await s(c, i, d, e, h, u(), o);
247
+ let p = Date.now() + g, m = Date.now(), y = "", b = Date.now(), x = F([], f);
248
+ for (; Date.now() < p;) {
249
+ if (n.aborted) throw Error("Cancelled");
250
+ let e = await a(c, i, d, o, 50);
251
251
  if (console.log("[BigConsole-Assistant] poll", {
252
252
  elapsedMs: Date.now() - f,
253
253
  messageCount: e.length,
254
- firstFewRoles: e.slice(0, 3).map((e) => T(e)),
255
- firstFewTimestamps: e.slice(0, 3).map((e) => E(e))
256
- }), x = N(e, f, y, b), console.log("[BigConsole-Assistant] progress", {
254
+ firstFewRoles: e.slice(0, 3).map((e) => D(e)),
255
+ firstFewTimestamps: e.slice(0, 3).map((e) => O(e))
256
+ }), x = F(e, f, y, b), console.log("[BigConsole-Assistant] progress", {
257
257
  contentPreview: x.content.slice(0, 100),
258
258
  done: x.done,
259
259
  lastContentChangeAt: Date.now() - b
260
- }), x.content !== y && (y = x.content, b = Date.now()), t(R(x.content)), x.done) {
260
+ }), x.content !== y && (y = x.content, b = Date.now()), t(B(x.content)), x.done) {
261
261
  console.log("[BigConsole-Assistant] progress.done=true, breaking poll loop");
262
262
  break;
263
263
  }
264
- Date.now() - v > g && (await n(l, o, 600, s).catch(() => void 0), v = Date.now()), await new Promise((e) => setTimeout(e, h));
264
+ Date.now() - m > v && (await r(c, i, 600, o).catch(() => void 0), m = Date.now()), await new Promise((e) => setTimeout(e, _));
265
265
  }
266
266
  if (!x.done) throw Error("I stopped waiting for a reply, but I may still be working — anything I already created will be there. Check your dashboards and data sinks before asking again, so you do not end up with duplicates.");
267
- return { text: R(x.content) };
267
+ return { text: B(x.content) };
268
268
  } catch (e) {
269
269
  if (console.error("[BigConsole-Assistant] run error", {
270
- attempt: u,
270
+ attempt: l,
271
271
  error: e instanceof Error ? e.message : String(e),
272
272
  stack: e instanceof Error ? e.stack : void 0,
273
- sandboxId: M.current,
274
- sessionId: P.current
275
- }), u === 1 && z(e)) return F(null), I(null), l(2);
273
+ sandboxId: P.current,
274
+ sessionId: I.current
275
+ }), l === 1 && V(e)) return L(null), R(null), c(2);
276
276
  throw e;
277
277
  }
278
278
  };
279
- return l(1);
279
+ return c(1);
280
280
  }, [
281
+ j,
282
+ H,
283
+ w,
281
284
  k,
282
- B,
283
- S,
284
- D,
285
- O,
286
- F,
287
- I
288
- ]), H = l(async () => {
289
- let e = M.current, t = P.current;
285
+ A,
286
+ L,
287
+ R
288
+ ]), W = d(async () => {
289
+ let e = P.current, t = I.current;
290
290
  if (!e || !t) return [];
291
- let n = C(k);
292
- if (!n || !S()) return [];
291
+ let n = T(j);
292
+ if (!n || !w()) return [];
293
293
  let r = {
294
- accessToken: S(),
295
- workspaceToken: D(),
296
- userId: O,
297
- organizationId: w()
294
+ accessToken: w(),
295
+ workspaceToken: k(),
296
+ userId: A,
297
+ organizationId: E()
298
298
  };
299
299
  try {
300
- return L(await i(e, n, t, r, _));
300
+ return z(await a(e, n, t, r, y));
301
301
  } catch (n) {
302
302
  return console.warn("[BigConsole-Assistant] could not restore history; starting fresh", {
303
303
  sandboxId: e,
304
304
  sessionId: t,
305
305
  error: n instanceof Error ? n.message : String(n)
306
- }), F(null), I(null), [];
306
+ }), L(null), R(null), [];
307
307
  }
308
308
  }, [
309
+ j,
310
+ w,
311
+ k,
312
+ A,
313
+ L,
314
+ R
315
+ ]), G = d(() => ({
316
+ accessToken: w(),
317
+ workspaceToken: k(),
318
+ userId: A,
319
+ organizationId: E()
320
+ }), [
321
+ w,
309
322
  k,
310
- S,
311
- D,
312
- O,
313
- F,
314
- I
323
+ A
324
+ ]), K = d(async () => {
325
+ let e = P.current, t = T(j);
326
+ if (!e || !t) return [];
327
+ try {
328
+ return (await o(e, t, G())).map((e) => ({
329
+ id: e.id,
330
+ title: e.title?.trim() || "Untitled chat",
331
+ updatedAt: e.time?.updated ?? e.time?.created,
332
+ active: e.id === I.current
333
+ })).sort((e, t) => (t.updatedAt ?? 0) - (e.updatedAt ?? 0));
334
+ } catch {
335
+ return [];
336
+ }
337
+ }, [j, G]), q = d(async () => {
338
+ let e = P.current, n = T(j);
339
+ if (!e || !n) {
340
+ R(null);
341
+ return;
342
+ }
343
+ let { sessionId: r } = await t(e, n, h, G());
344
+ R(r);
345
+ }, [
346
+ j,
347
+ G,
348
+ R
349
+ ]), J = d(async () => {
350
+ let e = P.current, t = I.current, r = T(j);
351
+ e && t && r && await n(e, r, t, G()).catch(() => void 0), R(null), await q();
352
+ }, [
353
+ j,
354
+ G,
355
+ R,
356
+ q
357
+ ]), Y = d(async (e) => {
358
+ let t = P.current, n = T(j);
359
+ if (!t || !n) return [];
360
+ let r = await a(t, n, e, G(), y);
361
+ return R(e), z(r);
362
+ }, [
363
+ j,
364
+ G,
365
+ R
366
+ ]);
367
+ return f(() => ({
368
+ sendPrompt: U,
369
+ loadHistory: W,
370
+ listSessions: K,
371
+ newSession: q,
372
+ clearSession: J,
373
+ selectSession: Y
374
+ }), [
375
+ U,
376
+ W,
377
+ K,
378
+ q,
379
+ J,
380
+ Y
315
381
  ]);
316
- return u(() => ({
317
- sendPrompt: V,
318
- loadHistory: H
319
- }), [V, H]);
320
382
  }
321
383
  //#endregion
322
- export { B as useSandboxAssistantTransport };
384
+ export { H as useSandboxAssistantTransport };
323
385
 
324
386
  //# sourceMappingURL=createSandboxAssistantTransport.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../../src/bigconsole/assistant/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * BigConsole adapter for the shared fe-libs AssistantWidget.\n *\n * The floating widget (fe-libs, Layer 1) is backend-agnostic — it calls an\n * injected `AssistantTransport`. This hook builds a transport that drives the\n * sandbox AI assistant (combined `assistant` mode = docs Q&A + api-calls): it\n * provisions/reuses a sandbox, opens a session, dispatches the prompt async,\n * then polls for the streamed answer. The agent introspects the GraphQL schema\n * and performs API calls on the user's behalf using their workspace token,\n * contextual to the current screen (via `gatherPageContext`).\n *\n * This is a faithful port of microfe-vibecontrols'\n * `services/createSandboxAssistantTransport.ts`; the only product-specific\n * difference lives in `assistantApi.createAssistantSandbox`\n * (`AI_ASSISTANT_PRODUCT=bigconsole`).\n */\n\nimport { useCallback, useMemo, useRef } from 'react';\nimport { useAuthToken } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n createAssistantSandbox,\n createAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantMessages,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n} from './assistantApi';\nimport { gatherPageContext } from './pageContext';\nimport type { AssistantMode, AssistantRawMessage, AssistantRawMessagePart, AssistantSandboxAuthContext } from './types';\n\n/**\n * Locally-defined mirror of the fe-libs `AssistantTransport` contract.\n *\n * Intentionally NOT imported from `@burdenoff/fe-libs`: microfe's tsconfig maps\n * `@burdenoff/fe-libs/*` to fe-libs *source*, so vite-plugin-dts would rewrite a\n * cross-package type used in this hook's public signature to a broken\n * source-relative path in the emitted `.d.ts`. Structural typing makes this\n * shape assignable to fe-libs' `AssistantTransport` at the call site\n * (bigconsole-app's AppShell), which is where compatibility is enforced.\n */\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\n/** Mirror of fe-libs' `AssistantWidgetMessage` (see note above on why). */\ninterface AssistantHistoryMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n pending?: boolean;\n error?: boolean;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n loadHistory: () => Promise<AssistantHistoryMessage[]>;\n}\n\n// BigConsole still boots the manually-tagged ACA image\n// `alpha-delegated-auth-v11` for the assistant sandbox. The historical\n// platform notes show that this image line reliably supports `api-calls`, while\n// the combined `assistant` mode depends on newer image contracts that are not\n// yet guaranteed on this tag. Use `api-calls` here so the assistant can execute\n// workspace GraphQL operations end-to-end right now. Once the underlying image\n// line is rebuilt and verified for combined mode, this can be switched back.\nconst MODE: AssistantMode = 'api-calls';\n// How long the UI will follow a single turn.\n//\n// This was 180s, which was SHORTER THAN THE WORK. A full \"create a school\n// attendance dashboard\" build — datasink → dashboard → parser → widget, each a\n// separate gateway call preceded by a model round-trip — measured 229s in prod.\n// So the agent finished, the dashboard genuinely existed, and the user was still\n// shown \"the assistant timed out\". That is worse than cosmetic: people retry and\n// end up with duplicate dashboards.\n//\n// 7 minutes covers the observed worst case with headroom. It costs nothing on\n// fast turns (we stop the moment the turn reports done), and the backend keeps\n// pace — the sandbox TTL is extended every TTL_EXTEND_INTERVAL_MS.\nconst STREAM_BUDGET_MS = 420_000;\nconst POLL_INTERVAL_MS = 1500;\nconst TTL_EXTEND_INTERVAL_MS = 30_000;\n// Raw agent messages per restore. The agent emits one message per internal\n// step, so a handful of turns is already dozens of messages — this is a cap on\n// the RAW fetch, not on the number of restored turns.\nconst HISTORY_MESSAGE_LIMIT = 200;\n\nconst SANDBOX_ID_KEY = 'bc-assistant-sandbox-id';\nconst SESSION_ID_KEY = 'bc-assistant-session-id';\n\nfunction readStoredId(key: string): string | null {\n try {\n return window.sessionStorage.getItem(key);\n } catch {\n // sessionStorage unavailable (private mode) — degrade to a fresh session.\n return null;\n }\n}\n\nfunction writeStoredId(key: string, id: string | null): void {\n try {\n if (id) window.sessionStorage.setItem(key, id);\n else window.sessionStorage.removeItem(key);\n } catch {\n // Non-fatal: we simply lose cross-reload continuity.\n }\n}\n\n// ── Context helpers (mirror vibecontrols' resolution) ────────────────\n\nfunction getProfileContextValue(key: 'workspaceId' | 'organizationId'): string {\n try {\n const activeContextKey =\n key === 'workspaceId' ? 'burdenoff-active-context-workspace' : '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 { workspaceId?: string; organizationId?: string };\n return context[key] ?? '';\n } catch {\n return '';\n }\n}\n\nfunction getWorkspaceId(fallback: string | null): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('workspace') ?? getProfileContextValue('workspaceId') ?? fallback ?? '';\n}\n\nfunction getOrganizationId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('org') ?? getProfileContextValue('organizationId');\n}\n\n// ── Message-progress helpers (pure; mirror vibecontrols) ─────────────\n\nfunction getMessageRole(message: AssistantRawMessage): string | undefined {\n // Check nested format first, then flat format\n return message.info?.role ?? message.role;\n}\n\nfunction getRawMessageCreatedAt(message: AssistantRawMessage): number {\n // Check nested format first (epoch ms), then flat format (ISO string or epoch ms)\n const nested = message.info?.time?.created;\n if (nested !== undefined) return nested;\n const flat = message.createdAt;\n if (flat === undefined) return 0;\n // If it's a string (ISO), parse it; otherwise treat as epoch ms\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? 0 : parsed;\n }\n return flat;\n}\n\nfunction getMessageCompleted(message: AssistantRawMessage): number | undefined {\n // Check nested format first, then flat format\n const nested = message.info?.time?.completed;\n if (nested !== undefined) return nested;\n const flat = message.completedAt;\n if (flat === undefined) return undefined;\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? undefined : parsed;\n }\n return flat;\n}\n\nfunction getAssistantText(message: AssistantRawMessage): string {\n // Check parts format first (nested), then flat content\n const parts = message.parts ?? [];\n const textFromParts = (parts ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text?.trim() ?? '')\n .filter(Boolean)\n .join('\\n');\n if (textFromParts) return textFromParts;\n // Fallback to flat content field\n return typeof message.content === 'string' ? message.content.trim() : '';\n}\n\n// Friendly, human-readable labels for the agent's tools so the progress line\n// reads like \"Searching the schema…\" instead of \"Running: bash\". The agent sets\n// a `description` on every bash call (e.g. \"Search for sales-related types in\n// workspace schema\") and a todo list on todowrite — surface those directly.\nconst TOOL_LABELS: Record<string, string> = {\n bash: 'Running a command',\n webfetch: 'Fetching a page',\n 'file.read': 'Reading files',\n 'file.write': 'Writing files',\n 'file.edit': 'Editing files',\n 'file.find.text': 'Searching the code',\n 'file.find.file': 'Looking for files',\n todowrite: 'Planning the steps',\n todoread: 'Reviewing the plan',\n};\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined;\n}\n\n/** Best-effort human summary of what a single tool part is doing right now. */\nfunction describeToolPart(part: AssistantRawMessagePart): string {\n const tool = part.tool ?? 'tool';\n const input = asRecord(part.state?.input);\n\n // bash carries a plain-English `description` of the step — the best signal.\n const description = input?.description;\n if (typeof description === 'string' && description.trim()) return description.trim();\n\n // todowrite carries the todo list — surface the item being worked on.\n const todos = input?.todos;\n if (Array.isArray(todos)) {\n const active = todos.find((todo) => asRecord(todo)?.status === 'in_progress') ?? todos[0];\n const content = asRecord(active)?.content;\n if (typeof content === 'string' && content.trim()) return content.trim();\n }\n\n return TOOL_LABELS[tool] ?? `Running ${tool}`;\n}\n\nfunction getToolProgress(message: AssistantRawMessage): string[] {\n const parts = message.parts ?? [];\n return parts\n .filter((part) => part.type === 'tool' && part.tool)\n .map((part) => {\n const status = part.state?.status ?? 'running';\n const label = describeToolPart(part);\n if (status === 'completed') return `✓ ${label}`;\n if (status === 'failed') return `⚠ ${label}`;\n return `⏳ ${label}…`;\n });\n}\n\nfunction buildProgress(\n messages: AssistantRawMessage[],\n sinceMs: number,\n previousContent?: string,\n previousContentAtMs?: number\n): { content: string; done: boolean } {\n const relevant = messages\n .filter((message) => getMessageRole(message) === 'assistant' && getRawMessageCreatedAt(message) >= sinceMs)\n .sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n // Debug: log message filtering when no relevant messages found\n if (relevant.length === 0 && messages.length > 0) {\n console.log('[BigConsole-Assistant] buildProgress: no relevant messages', {\n totalMessages: messages.length,\n sinceMs,\n messageRoles: messages.map((m) => getMessageRole(m)),\n messageTimestamps: messages.map((m) => getRawMessageCreatedAt(m)),\n });\n }\n\n let content: string;\n let done: boolean;\n\n if (relevant.length === 0) {\n content = 'Thinking…';\n // Staleness fallback: if no messages have appeared after 15+ seconds of polling,\n // treat the response as complete even when messages get filtered out\n // (e.g., due to timestamp skew or role mismatch).\n done =\n previousContent === content && previousContentAtMs !== undefined && Date.now() - previousContentAtMs >= 15_000;\n } else {\n const latest = relevant[relevant.length - 1]!;\n const text = getAssistantText(latest);\n const toolProgress = getToolProgress(latest);\n content = [text, ...toolProgress].filter(Boolean).join('\\n\\n') || 'Thinking…';\n\n const officiallyDone = Boolean(getMessageCompleted(latest)) && (Boolean(text) || toolProgress.length > 0);\n\n // Staleness fallback: if content hasn't changed in 15 s, treat the build as\n // complete even when the assistant forgot to set `completed`.\n const staleDone =\n !officiallyDone &&\n previousContent === content &&\n previousContentAtMs !== undefined &&\n Date.now() - previousContentAtMs >= 15_000;\n\n done = officiallyDone || staleDone;\n }\n\n return { content, done };\n}\n\nfunction getRawMessageId(message: AssistantRawMessage): string | undefined {\n return message.info?.id ?? message.id;\n}\n\n/**\n * The host injects synthetic \"user\" messages into the session (the auth-context\n * file, and a `User's current context:` block describing the open screen). They\n * are plumbing, not something the human typed, so they must never be replayed\n * into the visible transcript.\n */\nconst INJECTED_USER_PREFIXES = ['[Context file:', \"User's current context:\"];\n\nfunction isInjectedContextMessage(text: string): boolean {\n const head = text.trimStart();\n return INJECTED_USER_PREFIXES.some((prefix) => head.startsWith(prefix));\n}\n\n/**\n * Collapse a raw agent session into the user-visible conversation.\n *\n * The agent emits a message per internal step — narration (\"Let me grep the\n * schema…\"), tool calls, step markers — so replaying every assistant message\n * would dump its scratchpad into the chat. For each user turn we keep only the\n * LAST assistant message that carried text, which is its final answer. That is\n * exactly what the widget rendered live, so a restored conversation is\n * indistinguishable from the one the user was looking at before the reload.\n *\n * Turn boundaries are derived from ordering rather than `parentID` so this also\n * works for backends that return the flat message shape.\n */\nfunction mapSessionToHistory(raw: AssistantRawMessage[]): AssistantHistoryMessage[] {\n const ordered = [...raw].sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n const history: AssistantHistoryMessage[] = [];\n let pendingUser: { id: string; content: string } | null = null;\n let finalAnswer = '';\n\n const flushTurn = (): void => {\n const turn = pendingUser;\n if (!turn) return;\n history.push({ id: turn.id, role: 'user', content: turn.content });\n if (finalAnswer) {\n history.push({ id: `${turn.id}:reply`, role: 'assistant', content: sanitize(finalAnswer) });\n }\n pendingUser = null;\n finalAnswer = '';\n };\n\n for (const message of ordered) {\n const role = getMessageRole(message);\n const text = getAssistantText(message);\n\n if (role === 'user') {\n if (!text || isInjectedContextMessage(text)) continue;\n flushTurn();\n pendingUser = {\n id: getRawMessageId(message) ?? `user-${String(history.length)}`,\n content: text,\n };\n } else if (role === 'assistant' && pendingUser && text) {\n // Overwrite as the turn progresses — we end up holding the final answer.\n finalAnswer = text;\n }\n }\n flushTurn();\n\n return history;\n}\n\nfunction sanitize(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 isRecoverable(error: unknown): boolean {\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\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 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 'unable to connect',\n 'image pull',\n 'container failed',\n 'bootstrap failed',\n // Transient network errors from the browser fetch — gateway CORS preflight\n // failures, mid-stream resets, and Cloudflare 524s all surface as\n // \"Failed to fetch\" via TypeError. They're worth retrying on a clean\n // runtime since the underlying sandbox state is unaffected.\n 'failed to fetch',\n 'cf-proxy timeout',\n // Subgraph returned 500 with a generic message — gateway returns this as\n // a GraphQL error rather than an HTTP error. The actual underlying cause\n // (e.g., transient Prisma timeout) is recoverable, but a fresh sandbox\n // may be needed.\n 'unexpected error',\n ].some((fragment) => message.includes(fragment));\n}\n\n/**\n * Returns a memoized `AssistantTransport` wired to the BigConsole sandbox\n * assistant agent. The sandbox + session are cached in refs so follow-up turns\n * reuse the warm environment for the lifetime of the host shell.\n */\nexport function useSandboxAssistantTransport(): AssistantTransport {\n const { getAccessToken, getWorkspaceToken, userId, workspaceId: ctxWorkspaceId } = useAuthToken();\n\n // Rehydrate the sandbox + session ids persisted by the previous page\n // lifecycle. These were being WRITTEN to sessionStorage but never read back,\n // so every reload silently opened a brand-new agent session: the chat looked\n // empty AND the agent genuinely lost the conversation (it could no longer\n // resolve \"that datasink\" / \"the dashboard you just made\").\n //\n // Restoring both together is what makes history real rather than cosmetic —\n // the transcript we replay into the UI is the same session the agent will\n // keep reasoning over. A stale/expired sandbox is not a problem: `sendPrompt`\n // already treats that as recoverable, drops the refs, and retries clean.\n const [initialSandboxId, initialSessionId] = useMemo(\n () => [readStoredId(SANDBOX_ID_KEY), readStoredId(SESSION_ID_KEY)] as const,\n []\n );\n\n const sandboxIdRef = useRef<string | null>(initialSandboxId);\n const sessionIdRef = useRef<string | null>(initialSessionId);\n\n const persistSandboxId = useCallback((id: string | null) => {\n sandboxIdRef.current = id;\n writeStoredId(SANDBOX_ID_KEY, id);\n }, []);\n\n const persistSessionId = useCallback((id: string | null) => {\n sessionIdRef.current = id;\n writeStoredId(SESSION_ID_KEY, id);\n }, []);\n\n const ensureRuntime = useCallback(\n async (\n workspaceId: string,\n authContext: AssistantSandboxAuthContext\n ): Promise<{ sandboxId: string; sessionId: string }> => {\n let sandboxId = sandboxIdRef.current;\n console.log('[BigConsole-Assistant] ensureRuntime start', { sandboxId, workspaceId });\n if (!sandboxId) {\n if (!getAccessToken()) {\n throw new Error('The assistant requires an authenticated session. Please sign in again.');\n }\n console.log('[BigConsole-Assistant] findExistingSandbox called');\n sandboxId = await findExistingSandbox(workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] findExistingSandbox result', { sandboxId });\n\n if (sandboxId) {\n try {\n console.log('[BigConsole-Assistant] waitForSandboxReady called (reused)', { sandboxId });\n await waitForSandboxReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForSandboxReady done (reused)', { sandboxId });\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady called (reused)', { sandboxId });\n await waitForAssistantServiceReady(sandboxId, workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady done (reused)', { sandboxId });\n } catch (error) {\n console.warn('[BigConsole-Assistant] existing sandbox unusable, falling back to fresh sandbox', {\n sandboxId,\n error: error instanceof Error ? error.message : String(error),\n });\n sandboxId = null;\n }\n }\n\n if (!sandboxId) {\n console.log('[BigConsole-Assistant] createAssistantSandbox called');\n sandboxId = await createAssistantSandbox(MODE, workspaceId, authContext);\n console.log('[BigConsole-Assistant] createAssistantSandbox result', { sandboxId });\n console.log('[BigConsole-Assistant] waitForSandboxReady called (fresh)', { sandboxId });\n await waitForSandboxReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForSandboxReady done (fresh)', { sandboxId });\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady called (fresh)', { sandboxId });\n await waitForAssistantServiceReady(sandboxId, workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady done (fresh)', { sandboxId });\n }\n\n persistSandboxId(sandboxId);\n }\n\n let sessionId = sessionIdRef.current;\n console.log('[BigConsole-Assistant] session check', { sessionId, sandboxId });\n if (!sessionId) {\n console.log('[BigConsole-Assistant] createAssistantSession called', { sandboxId, workspaceId });\n const result = await createAssistantSession(sandboxId, workspaceId, MODE, authContext);\n sessionId = result.sessionId;\n persistSessionId(sessionId);\n }\n\n return { sandboxId, sessionId };\n },\n [getAccessToken]\n );\n\n const sendPrompt = useCallback(\n async ({ prompt, onProgress, signal }: AssistantSendArgs): Promise<{ text: string }> => {\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n console.log('[BigConsole-Assistant] sendPrompt called', {\n promptLength: prompt.length,\n workspaceId,\n hasCtxWorkspaceId: !!ctxWorkspaceId,\n authContextKeys: {\n hasAccessToken: !!getAccessToken(),\n hasWorkspaceToken: !!getWorkspaceToken(),\n hasUserId: !!userId,\n },\n locationSearch: window.location.search,\n });\n if (!workspaceId) {\n throw new Error('The assistant needs an active workspace. Open a workspace and try again.');\n }\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n console.log('[BigConsole-Assistant] authContext prepared', {\n hasAccessToken: !!authContext.accessToken,\n hasWorkspaceToken: !!authContext.workspaceToken,\n hasUserId: !!authContext.userId,\n hasOrgId: !!authContext.organizationId,\n });\n\n const run = async (attempt: 1 | 2): Promise<{ text: string }> => {\n try {\n console.log('[BigConsole-Assistant] run attempt', attempt);\n const { sandboxId, sessionId } = await ensureRuntime(workspaceId, authContext);\n console.log('[BigConsole-Assistant] ensureRuntime resolved', { sandboxId, sessionId });\n\n const startedAt = Date.now();\n console.log('[BigConsole-Assistant] calling sendAssistantPromptAsync', {\n sandboxId,\n workspaceId,\n sessionId,\n promptLength: prompt.length,\n });\n await sendAssistantPromptAsync(\n sandboxId,\n workspaceId,\n sessionId,\n prompt,\n MODE,\n gatherPageContext(),\n authContext\n );\n\n const timeoutAt = Date.now() + STREAM_BUDGET_MS;\n let lastTtlExtensionAt = Date.now();\n let lastContent = '';\n let lastContentChangeAt = Date.now();\n let progress = buildProgress([], startedAt);\n\n while (Date.now() < timeoutAt) {\n if (signal.aborted) throw new Error('Cancelled');\n\n const messages = await getAssistantMessages(sandboxId, workspaceId, sessionId, authContext, 50);\n console.log('[BigConsole-Assistant] poll', {\n elapsedMs: Date.now() - startedAt,\n messageCount: messages.length,\n firstFewRoles: messages.slice(0, 3).map((m) => getMessageRole(m)),\n firstFewTimestamps: messages.slice(0, 3).map((m) => getRawMessageCreatedAt(m)),\n });\n progress = buildProgress(messages, startedAt, lastContent, lastContentChangeAt);\n console.log('[BigConsole-Assistant] progress', {\n contentPreview: progress.content.slice(0, 100),\n done: progress.done,\n lastContentChangeAt: Date.now() - lastContentChangeAt,\n });\n if (progress.content !== lastContent) {\n lastContent = progress.content;\n lastContentChangeAt = Date.now();\n }\n onProgress(sanitize(progress.content));\n if (progress.done) {\n console.log('[BigConsole-Assistant] progress.done=true, breaking poll loop');\n break;\n }\n\n if (Date.now() - lastTtlExtensionAt > TTL_EXTEND_INTERVAL_MS) {\n await extendAssistantSandboxTTL(sandboxId, workspaceId, 600, authContext).catch(() => undefined);\n lastTtlExtensionAt = Date.now();\n }\n\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n\n if (!progress.done) {\n // Do NOT say \"please try again\". We stopped watching; the agent did\n // not stop working, and anything it already created is real. Telling\n // people to retry is how you get duplicate dashboards.\n throw new Error(\n 'I stopped waiting for a reply, but I may still be working — anything I already created will be there. Check your dashboards and data sinks before asking again, so you do not end up with duplicates.'\n );\n }\n\n return { text: sanitize(progress.content) };\n } catch (error) {\n console.error('[BigConsole-Assistant] run error', {\n attempt,\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n sandboxId: sandboxIdRef.current,\n sessionId: sessionIdRef.current,\n });\n // A stale/expired sandbox or session is recoverable — drop the warm\n // refs and retry once from a clean runtime.\n if (attempt === 1 && isRecoverable(error)) {\n persistSandboxId(null);\n persistSessionId(null);\n return run(2);\n }\n throw error;\n }\n };\n\n return run(1);\n },\n [ctxWorkspaceId, ensureRuntime, getAccessToken, getWorkspaceToken, userId, persistSandboxId, persistSessionId]\n );\n\n /**\n * Rebuild the visible conversation from the agent's own session.\n *\n * The sandbox is the source of truth for the transcript, so we replay from it\n * rather than mirroring messages into localStorage: the UI can then never\n * show a history the agent doesn't actually have. Called by the widget the\n * first time it opens; a no-op on a first-ever visit.\n */\n const loadHistory = useCallback(async (): Promise<AssistantHistoryMessage[]> => {\n const sandboxId = sandboxIdRef.current;\n const sessionId = sessionIdRef.current;\n if (!sandboxId || !sessionId) return [];\n\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n if (!workspaceId || !getAccessToken()) return [];\n\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n\n try {\n const raw = await getAssistantMessages(sandboxId, workspaceId, sessionId, authContext, HISTORY_MESSAGE_LIMIT);\n return mapSessionToHistory(raw);\n } catch (error) {\n // The session is gone — sandbox TTL expired, or the workspace was reset.\n // Drop the stale ids so the next prompt provisions a clean runtime instead\n // of repeatedly failing against a dead session.\n console.warn('[BigConsole-Assistant] could not restore history; starting fresh', {\n sandboxId,\n sessionId,\n error: error instanceof Error ? error.message : String(error),\n });\n persistSandboxId(null);\n persistSessionId(null);\n return [];\n }\n }, [ctxWorkspaceId, getAccessToken, getWorkspaceToken, userId, persistSandboxId, persistSessionId]);\n\n return useMemo<AssistantTransport>(() => ({ sendPrompt, loadHistory }), [sendPrompt, loadHistory]);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"createSandboxAssistantTransport.js","names":[],"sources":["../../../src/bigconsole/assistant/createSandboxAssistantTransport.ts"],"sourcesContent":["/**\n * BigConsole adapter for the shared fe-libs AssistantWidget.\n *\n * The floating widget (fe-libs, Layer 1) is backend-agnostic — it calls an\n * injected `AssistantTransport`. This hook builds a transport that drives the\n * sandbox AI assistant (combined `assistant` mode = docs Q&A + api-calls): it\n * provisions/reuses a sandbox, opens a session, dispatches the prompt async,\n * then polls for the streamed answer. The agent introspects the GraphQL schema\n * and performs API calls on the user's behalf using their workspace token,\n * contextual to the current screen (via `gatherPageContext`).\n *\n * This is a faithful port of microfe-vibecontrols'\n * `services/createSandboxAssistantTransport.ts`; the only product-specific\n * difference lives in `assistantApi.createAssistantSandbox`\n * (`AI_ASSISTANT_PRODUCT=bigconsole`).\n */\n\nimport { useCallback, useMemo, useRef } from 'react';\nimport { useAuthToken } from '@burdenoff/fe-libs/shared/providers/shell';\nimport {\n createAssistantSandbox,\n createAssistantSession,\n deleteAssistantSession,\n extendAssistantSandboxTTL,\n findExistingSandbox,\n getAssistantMessages,\n listAssistantSessions,\n sendAssistantPromptAsync,\n waitForAssistantServiceReady,\n waitForSandboxReady,\n} from './assistantApi';\nimport { gatherPageContext } from './pageContext';\nimport type { AssistantMode, AssistantRawMessage, AssistantRawMessagePart, AssistantSandboxAuthContext } from './types';\n\n/**\n * Locally-defined mirror of the fe-libs `AssistantTransport` contract.\n *\n * Intentionally NOT imported from `@burdenoff/fe-libs`: microfe's tsconfig maps\n * `@burdenoff/fe-libs/*` to fe-libs *source*, so vite-plugin-dts would rewrite a\n * cross-package type used in this hook's public signature to a broken\n * source-relative path in the emitted `.d.ts`. Structural typing makes this\n * shape assignable to fe-libs' `AssistantTransport` at the call site\n * (bigconsole-app's AppShell), which is where compatibility is enforced.\n */\ninterface AssistantSendArgs {\n prompt: string;\n onProgress: (partialText: string) => void;\n signal: AbortSignal;\n}\n\n/** Mirror of fe-libs' `AssistantWidgetMessage` (see note above on why). */\ninterface AssistantHistoryMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n pending?: boolean;\n error?: boolean;\n}\n\n/** Mirror of fe-libs' `AssistantSessionSummary` (see note above on why). */\ninterface AssistantSessionSummaryLocal {\n id: string;\n title: string;\n updatedAt?: number;\n active?: boolean;\n}\n\nexport interface AssistantTransport {\n sendPrompt: (args: AssistantSendArgs) => Promise<{ text: string }>;\n loadHistory: () => Promise<AssistantHistoryMessage[]>;\n listSessions: () => Promise<AssistantSessionSummaryLocal[]>;\n newSession: () => Promise<void>;\n clearSession: () => Promise<void>;\n selectSession: (sessionId: string) => Promise<AssistantHistoryMessage[]>;\n}\n\n// BigConsole still boots the manually-tagged ACA image\n// `alpha-delegated-auth-v11` for the assistant sandbox. The historical\n// platform notes show that this image line reliably supports `api-calls`, while\n// the combined `assistant` mode depends on newer image contracts that are not\n// yet guaranteed on this tag. Use `api-calls` here so the assistant can execute\n// workspace GraphQL operations end-to-end right now. Once the underlying image\n// line is rebuilt and verified for combined mode, this can be switched back.\nconst MODE: AssistantMode = 'api-calls';\n// How long the UI will follow a single turn.\n//\n// This was 180s, which was SHORTER THAN THE WORK. A full \"create a school\n// attendance dashboard\" build — datasink → dashboard → parser → widget, each a\n// separate gateway call preceded by a model round-trip — measured 229s in prod.\n// So the agent finished, the dashboard genuinely existed, and the user was still\n// shown \"the assistant timed out\". That is worse than cosmetic: people retry and\n// end up with duplicate dashboards.\n//\n// 7 minutes covers the observed worst case with headroom. It costs nothing on\n// fast turns (we stop the moment the turn reports done), and the backend keeps\n// pace — the sandbox TTL is extended every TTL_EXTEND_INTERVAL_MS.\nconst STREAM_BUDGET_MS = 420_000;\nconst POLL_INTERVAL_MS = 1500;\nconst TTL_EXTEND_INTERVAL_MS = 30_000;\n// Raw agent messages per restore. The agent emits one message per internal\n// step, so a handful of turns is already dozens of messages — this is a cap on\n// the RAW fetch, not on the number of restored turns.\nconst HISTORY_MESSAGE_LIMIT = 200;\n\nconst SANDBOX_ID_KEY = 'bc-assistant-sandbox-id';\nconst SESSION_ID_KEY = 'bc-assistant-session-id';\n\nfunction readStoredId(key: string): string | null {\n try {\n return window.sessionStorage.getItem(key);\n } catch {\n // sessionStorage unavailable (private mode) — degrade to a fresh session.\n return null;\n }\n}\n\nfunction writeStoredId(key: string, id: string | null): void {\n try {\n if (id) window.sessionStorage.setItem(key, id);\n else window.sessionStorage.removeItem(key);\n } catch {\n // Non-fatal: we simply lose cross-reload continuity.\n }\n}\n\n// ── Context helpers (mirror vibecontrols' resolution) ────────────────\n\nfunction getProfileContextValue(key: 'workspaceId' | 'organizationId'): string {\n try {\n const activeContextKey =\n key === 'workspaceId' ? 'burdenoff-active-context-workspace' : '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 { workspaceId?: string; organizationId?: string };\n return context[key] ?? '';\n } catch {\n return '';\n }\n}\n\nfunction getWorkspaceId(fallback: string | null): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('workspace') ?? getProfileContextValue('workspaceId') ?? fallback ?? '';\n}\n\nfunction getOrganizationId(): string {\n const params = new URLSearchParams(window.location.search);\n return params.get('org') ?? getProfileContextValue('organizationId');\n}\n\n// ── Message-progress helpers (pure; mirror vibecontrols) ─────────────\n\nfunction getMessageRole(message: AssistantRawMessage): string | undefined {\n // Check nested format first, then flat format\n return message.info?.role ?? message.role;\n}\n\nfunction getRawMessageCreatedAt(message: AssistantRawMessage): number {\n // Check nested format first (epoch ms), then flat format (ISO string or epoch ms)\n const nested = message.info?.time?.created;\n if (nested !== undefined) return nested;\n const flat = message.createdAt;\n if (flat === undefined) return 0;\n // If it's a string (ISO), parse it; otherwise treat as epoch ms\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? 0 : parsed;\n }\n return flat;\n}\n\nfunction getMessageCompleted(message: AssistantRawMessage): number | undefined {\n // Check nested format first, then flat format\n const nested = message.info?.time?.completed;\n if (nested !== undefined) return nested;\n const flat = message.completedAt;\n if (flat === undefined) return undefined;\n if (typeof flat === 'string') {\n const parsed = Date.parse(flat);\n return isNaN(parsed) ? undefined : parsed;\n }\n return flat;\n}\n\nfunction getAssistantText(message: AssistantRawMessage): string {\n // Check parts format first (nested), then flat content\n const parts = message.parts ?? [];\n const textFromParts = (parts ?? [])\n .filter((part) => part.type === 'text' && typeof part.text === 'string')\n .map((part) => part.text?.trim() ?? '')\n .filter(Boolean)\n .join('\\n');\n if (textFromParts) return textFromParts;\n // Fallback to flat content field\n return typeof message.content === 'string' ? message.content.trim() : '';\n}\n\n// Friendly, human-readable labels for the agent's tools so the progress line\n// reads like \"Searching the schema…\" instead of \"Running: bash\". The agent sets\n// a `description` on every bash call (e.g. \"Search for sales-related types in\n// workspace schema\") and a todo list on todowrite — surface those directly.\nconst TOOL_LABELS: Record<string, string> = {\n bash: 'Running a command',\n webfetch: 'Fetching a page',\n 'file.read': 'Reading files',\n 'file.write': 'Writing files',\n 'file.edit': 'Editing files',\n 'file.find.text': 'Searching the code',\n 'file.find.file': 'Looking for files',\n todowrite: 'Planning the steps',\n todoread: 'Reviewing the plan',\n};\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : undefined;\n}\n\n/** Best-effort human summary of what a single tool part is doing right now. */\nfunction describeToolPart(part: AssistantRawMessagePart): string {\n const tool = part.tool ?? 'tool';\n const input = asRecord(part.state?.input);\n\n // bash carries a plain-English `description` of the step — the best signal.\n const description = input?.description;\n if (typeof description === 'string' && description.trim()) return description.trim();\n\n // todowrite carries the todo list — surface the item being worked on.\n const todos = input?.todos;\n if (Array.isArray(todos)) {\n const active = todos.find((todo) => asRecord(todo)?.status === 'in_progress') ?? todos[0];\n const content = asRecord(active)?.content;\n if (typeof content === 'string' && content.trim()) return content.trim();\n }\n\n return TOOL_LABELS[tool] ?? `Running ${tool}`;\n}\n\nfunction getToolProgress(message: AssistantRawMessage): string[] {\n const parts = message.parts ?? [];\n return parts\n .filter((part) => part.type === 'tool' && part.tool)\n .map((part) => {\n const status = part.state?.status ?? 'running';\n const label = describeToolPart(part);\n if (status === 'completed') return `✓ ${label}`;\n if (status === 'failed') return `⚠ ${label}`;\n return `⏳ ${label}…`;\n });\n}\n\nfunction buildProgress(\n messages: AssistantRawMessage[],\n sinceMs: number,\n previousContent?: string,\n previousContentAtMs?: number\n): { content: string; done: boolean } {\n const relevant = messages\n .filter((message) => getMessageRole(message) === 'assistant' && getRawMessageCreatedAt(message) >= sinceMs)\n .sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n // Debug: log message filtering when no relevant messages found\n if (relevant.length === 0 && messages.length > 0) {\n console.log('[BigConsole-Assistant] buildProgress: no relevant messages', {\n totalMessages: messages.length,\n sinceMs,\n messageRoles: messages.map((m) => getMessageRole(m)),\n messageTimestamps: messages.map((m) => getRawMessageCreatedAt(m)),\n });\n }\n\n let content: string;\n let done: boolean;\n\n if (relevant.length === 0) {\n content = '';\n // Staleness fallback: if no messages have appeared after 15+ seconds of polling,\n // treat the response as complete even when messages get filtered out\n // (e.g., due to timestamp skew or role mismatch).\n done =\n previousContent === content && previousContentAtMs !== undefined && Date.now() - previousContentAtMs >= 15_000;\n } else {\n const latest = relevant[relevant.length - 1]!;\n\n // ACCUMULATE the run, don't just show its last line.\n //\n // The agent emits a message per step, and it now narrates each one and prints\n // a link the moment a create lands (\"✅ Data sink created — [Open …](/…)\").\n // Showing only the newest message threw all of that away a second later: the\n // user saw a lone \"Thinking…\" and none of the links they were promised. Join\n // the whole run instead, so the panel reads as a live account of what is\n // happening and every link stays on screen.\n const narration = relevant.map(getAssistantText).filter(Boolean);\n const toolProgress = getToolProgress(latest);\n content = [...narration, ...toolProgress].join('\\n\\n');\n\n const officiallyDone = Boolean(getMessageCompleted(latest)) && content.length > 0;\n\n // A tool that is still running is proof the turn is alive, so never let the\n // staleness fallback fire underneath it. A single gateway call can sit on the\n // same \"⏳ Creating the data sink…\" line for far longer than the old 15s\n // window, which would have declared the turn finished mid-build.\n const hasRunningTool = (latest.parts ?? []).some(\n (part) => part.type === 'tool' && part.state?.status !== 'completed' && part.state?.status !== 'failed'\n );\n\n const staleDone =\n !officiallyDone &&\n !hasRunningTool &&\n previousContent === content &&\n previousContentAtMs !== undefined &&\n Date.now() - previousContentAtMs >= 45_000;\n\n done = officiallyDone || staleDone;\n }\n\n return { content, done };\n}\n\nfunction getRawMessageId(message: AssistantRawMessage): string | undefined {\n return message.info?.id ?? message.id;\n}\n\n/**\n * The host injects synthetic \"user\" messages into the session (the auth-context\n * file, and a `User's current context:` block describing the open screen). They\n * are plumbing, not something the human typed, so they must never be replayed\n * into the visible transcript.\n */\nconst INJECTED_USER_PREFIXES = ['[Context file:', \"User's current context:\"];\n\nfunction isInjectedContextMessage(text: string): boolean {\n const head = text.trimStart();\n return INJECTED_USER_PREFIXES.some((prefix) => head.startsWith(prefix));\n}\n\n/**\n * Collapse a raw agent session into the user-visible conversation.\n *\n * For each user turn we join every assistant message that carried text — the\n * same accumulation `buildProgress` renders live. That is deliberate: a restored\n * conversation must look like the one the user was actually looking at, and it\n * keeps the per-step \"✅ … [Open the data sink](/…)\" links, which are the whole\n * point of the run. Keeping only the final answer would silently drop them.\n *\n * Tool/step parts carry no text and so fall out on their own — the agent's\n * scratchpad never reaches the chat.\n *\n * Turn boundaries are derived from ordering rather than `parentID` so this also\n * works for backends that return the flat message shape.\n */\nfunction mapSessionToHistory(raw: AssistantRawMessage[]): AssistantHistoryMessage[] {\n const ordered = [...raw].sort((left, right) => getRawMessageCreatedAt(left) - getRawMessageCreatedAt(right));\n\n const history: AssistantHistoryMessage[] = [];\n let pendingUser: { id: string; content: string } | null = null;\n let reply: string[] = [];\n\n const flushTurn = (): void => {\n const turn = pendingUser;\n if (!turn) return;\n history.push({ id: turn.id, role: 'user', content: turn.content });\n if (reply.length > 0) {\n history.push({\n id: `${turn.id}:reply`,\n role: 'assistant',\n content: sanitize(reply.join('\\n\\n')),\n });\n }\n pendingUser = null;\n reply = [];\n };\n\n for (const message of ordered) {\n const role = getMessageRole(message);\n const text = getAssistantText(message);\n\n if (role === 'user') {\n if (!text || isInjectedContextMessage(text)) continue;\n flushTurn();\n pendingUser = {\n id: getRawMessageId(message) ?? `user-${String(history.length)}`,\n content: text,\n };\n } else if (role === 'assistant' && pendingUser && text) {\n // Accumulate every narrated step — mirrors what buildProgress showed live,\n // and preserves the per-step links.\n reply.push(text);\n }\n }\n flushTurn();\n\n return history;\n}\n\nfunction sanitize(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 isRecoverable(error: unknown): boolean {\n const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();\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 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 'unable to connect',\n 'image pull',\n 'container failed',\n 'bootstrap failed',\n // Transient network errors from the browser fetch — gateway CORS preflight\n // failures, mid-stream resets, and Cloudflare 524s all surface as\n // \"Failed to fetch\" via TypeError. They're worth retrying on a clean\n // runtime since the underlying sandbox state is unaffected.\n 'failed to fetch',\n 'cf-proxy timeout',\n // Subgraph returned 500 with a generic message — gateway returns this as\n // a GraphQL error rather than an HTTP error. The actual underlying cause\n // (e.g., transient Prisma timeout) is recoverable, but a fresh sandbox\n // may be needed.\n 'unexpected error',\n ].some((fragment) => message.includes(fragment));\n}\n\n/**\n * Returns a memoized `AssistantTransport` wired to the BigConsole sandbox\n * assistant agent. The sandbox + session are cached in refs so follow-up turns\n * reuse the warm environment for the lifetime of the host shell.\n */\nexport function useSandboxAssistantTransport(): AssistantTransport {\n const { getAccessToken, getWorkspaceToken, userId, workspaceId: ctxWorkspaceId } = useAuthToken();\n\n // Rehydrate the sandbox + session ids persisted by the previous page\n // lifecycle. These were being WRITTEN to sessionStorage but never read back,\n // so every reload silently opened a brand-new agent session: the chat looked\n // empty AND the agent genuinely lost the conversation (it could no longer\n // resolve \"that datasink\" / \"the dashboard you just made\").\n //\n // Restoring both together is what makes history real rather than cosmetic —\n // the transcript we replay into the UI is the same session the agent will\n // keep reasoning over. A stale/expired sandbox is not a problem: `sendPrompt`\n // already treats that as recoverable, drops the refs, and retries clean.\n const [initialSandboxId, initialSessionId] = useMemo(\n () => [readStoredId(SANDBOX_ID_KEY), readStoredId(SESSION_ID_KEY)] as const,\n []\n );\n\n const sandboxIdRef = useRef<string | null>(initialSandboxId);\n const sessionIdRef = useRef<string | null>(initialSessionId);\n\n const persistSandboxId = useCallback((id: string | null) => {\n sandboxIdRef.current = id;\n writeStoredId(SANDBOX_ID_KEY, id);\n }, []);\n\n const persistSessionId = useCallback((id: string | null) => {\n sessionIdRef.current = id;\n writeStoredId(SESSION_ID_KEY, id);\n }, []);\n\n const ensureRuntime = useCallback(\n async (\n workspaceId: string,\n authContext: AssistantSandboxAuthContext\n ): Promise<{ sandboxId: string; sessionId: string }> => {\n let sandboxId = sandboxIdRef.current;\n console.log('[BigConsole-Assistant] ensureRuntime start', { sandboxId, workspaceId });\n if (!sandboxId) {\n if (!getAccessToken()) {\n throw new Error('The assistant requires an authenticated session. Please sign in again.');\n }\n console.log('[BigConsole-Assistant] findExistingSandbox called');\n sandboxId = await findExistingSandbox(workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] findExistingSandbox result', { sandboxId });\n\n if (sandboxId) {\n try {\n console.log('[BigConsole-Assistant] waitForSandboxReady called (reused)', { sandboxId });\n await waitForSandboxReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForSandboxReady done (reused)', { sandboxId });\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady called (reused)', { sandboxId });\n await waitForAssistantServiceReady(sandboxId, workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady done (reused)', { sandboxId });\n } catch (error) {\n console.warn('[BigConsole-Assistant] existing sandbox unusable, falling back to fresh sandbox', {\n sandboxId,\n error: error instanceof Error ? error.message : String(error),\n });\n sandboxId = null;\n }\n }\n\n if (!sandboxId) {\n console.log('[BigConsole-Assistant] createAssistantSandbox called');\n sandboxId = await createAssistantSandbox(MODE, workspaceId, authContext);\n console.log('[BigConsole-Assistant] createAssistantSandbox result', { sandboxId });\n console.log('[BigConsole-Assistant] waitForSandboxReady called (fresh)', { sandboxId });\n await waitForSandboxReady(sandboxId, workspaceId, authContext);\n console.log('[BigConsole-Assistant] waitForSandboxReady done (fresh)', { sandboxId });\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady called (fresh)', { sandboxId });\n await waitForAssistantServiceReady(sandboxId, workspaceId, MODE, authContext);\n console.log('[BigConsole-Assistant] waitForAssistantServiceReady done (fresh)', { sandboxId });\n }\n\n persistSandboxId(sandboxId);\n }\n\n let sessionId = sessionIdRef.current;\n console.log('[BigConsole-Assistant] session check', { sessionId, sandboxId });\n if (!sessionId) {\n console.log('[BigConsole-Assistant] createAssistantSession called', { sandboxId, workspaceId });\n const result = await createAssistantSession(sandboxId, workspaceId, MODE, authContext);\n sessionId = result.sessionId;\n persistSessionId(sessionId);\n }\n\n return { sandboxId, sessionId };\n },\n [getAccessToken]\n );\n\n const sendPrompt = useCallback(\n async ({ prompt, onProgress, signal }: AssistantSendArgs): Promise<{ text: string }> => {\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n console.log('[BigConsole-Assistant] sendPrompt called', {\n promptLength: prompt.length,\n workspaceId,\n hasCtxWorkspaceId: !!ctxWorkspaceId,\n authContextKeys: {\n hasAccessToken: !!getAccessToken(),\n hasWorkspaceToken: !!getWorkspaceToken(),\n hasUserId: !!userId,\n },\n locationSearch: window.location.search,\n });\n if (!workspaceId) {\n throw new Error('The assistant needs an active workspace. Open a workspace and try again.');\n }\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n console.log('[BigConsole-Assistant] authContext prepared', {\n hasAccessToken: !!authContext.accessToken,\n hasWorkspaceToken: !!authContext.workspaceToken,\n hasUserId: !!authContext.userId,\n hasOrgId: !!authContext.organizationId,\n });\n\n const run = async (attempt: 1 | 2): Promise<{ text: string }> => {\n try {\n console.log('[BigConsole-Assistant] run attempt', attempt);\n const { sandboxId, sessionId } = await ensureRuntime(workspaceId, authContext);\n console.log('[BigConsole-Assistant] ensureRuntime resolved', { sandboxId, sessionId });\n\n const startedAt = Date.now();\n console.log('[BigConsole-Assistant] calling sendAssistantPromptAsync', {\n sandboxId,\n workspaceId,\n sessionId,\n promptLength: prompt.length,\n });\n await sendAssistantPromptAsync(\n sandboxId,\n workspaceId,\n sessionId,\n prompt,\n MODE,\n gatherPageContext(),\n authContext\n );\n\n const timeoutAt = Date.now() + STREAM_BUDGET_MS;\n let lastTtlExtensionAt = Date.now();\n let lastContent = '';\n let lastContentChangeAt = Date.now();\n let progress = buildProgress([], startedAt);\n\n while (Date.now() < timeoutAt) {\n if (signal.aborted) throw new Error('Cancelled');\n\n const messages = await getAssistantMessages(sandboxId, workspaceId, sessionId, authContext, 50);\n console.log('[BigConsole-Assistant] poll', {\n elapsedMs: Date.now() - startedAt,\n messageCount: messages.length,\n firstFewRoles: messages.slice(0, 3).map((m) => getMessageRole(m)),\n firstFewTimestamps: messages.slice(0, 3).map((m) => getRawMessageCreatedAt(m)),\n });\n progress = buildProgress(messages, startedAt, lastContent, lastContentChangeAt);\n console.log('[BigConsole-Assistant] progress', {\n contentPreview: progress.content.slice(0, 100),\n done: progress.done,\n lastContentChangeAt: Date.now() - lastContentChangeAt,\n });\n if (progress.content !== lastContent) {\n lastContent = progress.content;\n lastContentChangeAt = Date.now();\n }\n onProgress(sanitize(progress.content));\n if (progress.done) {\n console.log('[BigConsole-Assistant] progress.done=true, breaking poll loop');\n break;\n }\n\n if (Date.now() - lastTtlExtensionAt > TTL_EXTEND_INTERVAL_MS) {\n await extendAssistantSandboxTTL(sandboxId, workspaceId, 600, authContext).catch(() => undefined);\n lastTtlExtensionAt = Date.now();\n }\n\n await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));\n }\n\n if (!progress.done) {\n // Do NOT say \"please try again\". We stopped watching; the agent did\n // not stop working, and anything it already created is real. Telling\n // people to retry is how you get duplicate dashboards.\n throw new Error(\n 'I stopped waiting for a reply, but I may still be working — anything I already created will be there. Check your dashboards and data sinks before asking again, so you do not end up with duplicates.'\n );\n }\n\n return { text: sanitize(progress.content) };\n } catch (error) {\n console.error('[BigConsole-Assistant] run error', {\n attempt,\n error: error instanceof Error ? error.message : String(error),\n stack: error instanceof Error ? error.stack : undefined,\n sandboxId: sandboxIdRef.current,\n sessionId: sessionIdRef.current,\n });\n // A stale/expired sandbox or session is recoverable — drop the warm\n // refs and retry once from a clean runtime.\n if (attempt === 1 && isRecoverable(error)) {\n persistSandboxId(null);\n persistSessionId(null);\n return run(2);\n }\n throw error;\n }\n };\n\n return run(1);\n },\n [ctxWorkspaceId, ensureRuntime, getAccessToken, getWorkspaceToken, userId, persistSandboxId, persistSessionId]\n );\n\n /**\n * Rebuild the visible conversation from the agent's own session.\n *\n * The sandbox is the source of truth for the transcript, so we replay from it\n * rather than mirroring messages into localStorage: the UI can then never\n * show a history the agent doesn't actually have. Called by the widget the\n * first time it opens; a no-op on a first-ever visit.\n */\n const loadHistory = useCallback(async (): Promise<AssistantHistoryMessage[]> => {\n const sandboxId = sandboxIdRef.current;\n const sessionId = sessionIdRef.current;\n if (!sandboxId || !sessionId) return [];\n\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n if (!workspaceId || !getAccessToken()) return [];\n\n const authContext: AssistantSandboxAuthContext = {\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n };\n\n try {\n const raw = await getAssistantMessages(sandboxId, workspaceId, sessionId, authContext, HISTORY_MESSAGE_LIMIT);\n return mapSessionToHistory(raw);\n } catch (error) {\n // The session is gone — sandbox TTL expired, or the workspace was reset.\n // Drop the stale ids so the next prompt provisions a clean runtime instead\n // of repeatedly failing against a dead session.\n console.warn('[BigConsole-Assistant] could not restore history; starting fresh', {\n sandboxId,\n sessionId,\n error: error instanceof Error ? error.message : String(error),\n });\n persistSandboxId(null);\n persistSessionId(null);\n return [];\n }\n }, [ctxWorkspaceId, getAccessToken, getWorkspaceToken, userId, persistSandboxId, persistSessionId]);\n\n // ── Session management (powers the New chat / Clear chat / History rail) ──\n //\n // The sandbox owns the conversations, so every one of these is a thin call\n // onto it rather than client-side bookkeeping — the rail can never show a\n // chat the agent has actually forgotten.\n\n const authFor = useCallback(\n (): AssistantSandboxAuthContext => ({\n accessToken: getAccessToken(),\n workspaceToken: getWorkspaceToken(),\n userId,\n organizationId: getOrganizationId(),\n }),\n [getAccessToken, getWorkspaceToken, userId]\n );\n\n const listSessions = useCallback(async (): Promise<AssistantSessionSummaryLocal[]> => {\n const sandboxId = sandboxIdRef.current;\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n if (!sandboxId || !workspaceId) return [];\n\n try {\n const raw = await listAssistantSessions(sandboxId, workspaceId, authFor());\n return raw\n .map((session) => ({\n id: session.id,\n title: session.title?.trim() || 'Untitled chat',\n updatedAt: session.time?.updated ?? session.time?.created,\n active: session.id === sessionIdRef.current,\n }))\n .sort((left, right) => (right.updatedAt ?? 0) - (left.updatedAt ?? 0));\n } catch {\n return [];\n }\n }, [ctxWorkspaceId, authFor]);\n\n const newSession = useCallback(async (): Promise<void> => {\n const sandboxId = sandboxIdRef.current;\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n\n // No sandbox yet → nothing to detach from; the next prompt provisions one.\n if (!sandboxId || !workspaceId) {\n persistSessionId(null);\n return;\n }\n\n const { sessionId } = await createAssistantSession(sandboxId, workspaceId, MODE, authFor());\n persistSessionId(sessionId);\n }, [ctxWorkspaceId, authFor, persistSessionId]);\n\n const clearSession = useCallback(async (): Promise<void> => {\n const sandboxId = sandboxIdRef.current;\n const sessionId = sessionIdRef.current;\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n\n if (sandboxId && sessionId && workspaceId) {\n // Best-effort: if the delete fails the session is still detached below, so\n // the user gets the empty chat they asked for either way.\n await deleteAssistantSession(sandboxId, workspaceId, sessionId, authFor()).catch(() => undefined);\n }\n persistSessionId(null);\n await newSession();\n }, [ctxWorkspaceId, authFor, persistSessionId, newSession]);\n\n const selectSession = useCallback(\n async (sessionId: string): Promise<AssistantHistoryMessage[]> => {\n const sandboxId = sandboxIdRef.current;\n const workspaceId = getWorkspaceId(ctxWorkspaceId);\n if (!sandboxId || !workspaceId) return [];\n\n const raw = await getAssistantMessages(sandboxId, workspaceId, sessionId, authFor(), HISTORY_MESSAGE_LIMIT);\n // Only adopt the session once we know we can actually read it.\n persistSessionId(sessionId);\n return mapSessionToHistory(raw);\n },\n [ctxWorkspaceId, authFor, persistSessionId]\n );\n\n return useMemo<AssistantTransport>(\n () => ({ sendPrompt, loadHistory, listSessions, newSession, clearSession, selectSession }),\n [sendPrompt, loadHistory, listSessions, newSession, clearSession, selectSession]\n );\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-bigconsole",
3
- "version": "2026.713.3",
3
+ "version": "2026.713.4",
4
4
  "description": "BigConsole - AI-powered analytics and dashboard platform",
5
5
  "type": "module",
6
6
  "files": [