@adminide-stack/yantra-mobile 12.0.124-alpha.0 → 12.0.124-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"useChatStream.js","sources":["../../src/hooks/useChatStream.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport { Platform } from 'react-native';\nimport {\n useCreateAccountChatFileDownloadLinksMutation,\n useCreateAccountChatFileUploadLinksMutation,\n} from 'common/graphql';\nimport {\n clearChannelHistoryRunning,\n markChannelHistoryRunning,\n patchChannelHistoryPreview,\n useChatMutations,\n useChatMessages,\n} from './useChatApi';\nimport { useCdecliChannel, type CdecliTurnOptions } from './useCdecliChannel';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\nimport { isAgentJwtRejected, shouldInlineGatewayMediaForHostedAgent } from './systemTokenJwt';\nimport { config } from '../config';\nimport {\n MAX_QUEUED_MESSAGES,\n clearQueuedGatewayMessages,\n dropQueuedGatewayMessage,\n queueGatewayMessage,\n queuedGatewayMessages,\n restoreQueuedGatewayMessages,\n subscribeQueuedGatewayMessages,\n} from '../services/queuedGatewayMessages';\nimport { isImageAttachment } from '../features/attachments/isImageAttachment';\nimport { buildGatewayMedia, buildGatewayMediaPreferUrls } from '../features/attachments/buildGatewayMedia';\nimport { preserveAttachmentPreviews } from '../features/attachments/preserveAttachmentPreviews';\nimport { releasePreviewBytes } from '../features/attachments/releasePreviewBytes';\nimport { resolveAssistantReply } from '../features/chat/emptyChatResponse';\nimport { splitPreResponse } from '../features/chat/preResponse';\nimport {\n appendFollowUpAssistant,\n appendOrReplaceAssistant,\n collapseConsecutiveAssistants,\n mergeOptimisticGatewayMessages,\n} from '../state/chatThreadMerge';\n\n// Each flush re-parses the whole reply as markdown. Mid-range Android CPUs\n// drop frames at 20 flushes/s; 10/s still reads as live typing.\nconst STREAM_FLUSH_MS = Platform.OS === 'android' ? 100 : 50;\n\n/** Agent returned an error while the cdecli-serve channel stayed connected (e.g. bad LLM API key). */\nfunction isCdecliAgentFailureOutput(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n return (\n /⚠\\s*Error:/.test(t) ||\n /LLM stream error/i.test(t) ||\n /Incorrect API key/i.test(t) ||\n /invalid_api_key/i.test(t) ||\n /authentication_error/i.test(t) ||\n isAgentJwtRejected(t)\n );\n}\n\nexport interface MessageAttachment {\n id: string;\n name: string;\n type: 'file' | 'screenshot';\n dataUrl?: string;\n mimeType?: string;\n size?: number;\n url?: string;\n}\n\nexport interface ChatMessage {\n role: 'user' | 'assistant';\n content: string;\n attachments?: MessageAttachment[];\n metadata?: {\n tokenUsage?: { inputTokens: number; outputTokens: number; totalTokens: number };\n model?: string;\n };\n}\n\n/** The queued row renders this turn now, so the optimistic bubble must not stay behind it. */\nfunction withoutTrailingUserMessage(prev: ChatMessage[], text: string): ChatMessage[] {\n const last = prev[prev.length - 1];\n return last?.role === 'user' && last.content === text ? prev.slice(0, -1) : prev;\n}\n\n/** A message the gateway queued for the agent while a turn was running. */\nexport interface QueuedFollowUp {\n /** The gateway's id for it - what cancels it. */\n id: string;\n text: string;\n attachments?: MessageAttachment[];\n}\n\n/** What became of a send: delivered to the gateway, parked in the outbox, or refused outright. */\nexport type SendOutcome = 'sent' | 'queued' | 'failed';\n\nfunction formatBackendThread(\n backendMessages: Array<{\n role: string;\n content?: string;\n // The generated `ChatMessageUI` types `type` as a plain string; it is narrowed\n // to the UI union in the map below, so take it wide rather than cast at the call.\n attachments?: Array<{ id: string; name: string; type: string; mimeType?: string; size?: number; url?: string }>;\n tokenCount?: number;\n model?: string;\n }>,\n): ChatMessage[] {\n return collapseConsecutiveAssistants(\n backendMessages.map((msg) => ({\n role: msg.role as 'user' | 'assistant',\n content: msg.content ?? '',\n attachments: msg.attachments?.map((a) => ({\n id: a.id,\n name: a.name,\n type: a.type === 'screenshot' ? 'screenshot' : 'file',\n mimeType: a.mimeType,\n size: a.size,\n url: a.url,\n })),\n metadata: msg.tokenCount\n ? {\n tokenUsage: {\n inputTokens: 0,\n outputTokens: msg.tokenCount,\n totalTokens: msg.tokenCount,\n },\n model: msg.model,\n }\n : undefined,\n })),\n );\n}\n\n/** Mobile chat routes exclusively through CDeCLI (messenger-gateway). */\nexport interface UseChatStreamRouting {\n channelConnected: boolean;\n accountId: string;\n /** When `'backend'`, the gateway persists posts — skip client `saveMessages`. */\n persistenceMode?: 'backend' | 'frontend';\n /** Remint the system JWT and reconnect after cdecli-agent 401s the session. */\n recoverAgentAuth?: () => Promise<boolean>;\n /** Agent skill every turn on this stream carries (`metadata.skill`), e.g. Web Builder's. */\n skill?: string;\n}\n\nexport function useChatStream(\n sessionId: string | null,\n routing: UseChatStreamRouting = {\n channelConnected: false,\n accountId: 'default',\n persistenceMode: 'backend',\n },\n) {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [response, setResponse] = useState('');\n /** Brain assist — never the published agent stream. Shown as a Quick answer strip. */\n const [quickReply, setQuickReply] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const isSendingRef = useRef(false);\n /**\n * Messages sent while a turn was running, which the gateway queued for the\n * agent. Shown above the composer until the agent starts on them (the way\n * Claude Code shows a message typed mid-turn), then moved into the thread.\n * Kept out of `messages` so a backend hydration cannot drop or double them.\n */\n const [queuedFollowUps, setQueuedFollowUps] = useState<QueuedFollowUp[]>([]);\n const queuedFollowUpsRef = useRef<QueuedFollowUp[]>([]);\n const sendMessageRef = useRef<\n (\n content: string,\n attachments?: MessageAttachment[],\n sessionIdOverride?: string | null,\n alreadyVisible?: boolean,\n queuedId?: string,\n turnOptions?: CdecliTurnOptions,\n ) => Promise<SendOutcome>\n >(async () => 'failed');\n\n /** Ensures GraphQL stream subscriptions use the id from createChannel+send before the next parent render. */\n const [cdecliStreamOverrideId, setCdecliStreamOverrideId] = useState<string | undefined>(undefined);\n const userMsgForSaveRef = useRef<ChatMessage | null>(null);\n const saveSessionIdRef = useRef<string | null>(null);\n const sessionIdRef = useRef<string | null>(sessionId);\n sessionIdRef.current = sessionId || cdecliStreamOverrideId || null;\n /** Blocks duplicate cdecli `onComplete` from appending assistant twice / saving twice per send. */\n const cdecliRoundHandledRef = useRef(false);\n /**\n * The round in flight was opened by the agent, not by a send: a follow-up,\n * e.g. the answer to a request the gateway queued while the agent was busy.\n * It completes as a new bubble instead of replacing the reply before it.\n */\n const followUpRoundRef = useRef(false);\n /** One remint+resend per turn when cdecli-agent 401s the JWT. */\n const authRetryUsedRef = useRef(false);\n const recoveringAuthRef = useRef(false);\n const routingRef = useRef(routing);\n routingRef.current = routing;\n\n const tryRecoverAndResendRef = useRef<(reason: string) => Promise<boolean>>(async () => false);\n\n /** Coalesce token deltas so Fabric is not committing a native tree every chunk. */\n const pendingStreamRef = useRef('');\n const streamFlushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearPendingStream = useCallback(() => {\n if (streamFlushTimerRef.current != null) {\n clearTimeout(streamFlushTimerRef.current);\n streamFlushTimerRef.current = null;\n }\n pendingStreamRef.current = '';\n }, []);\n\n const flushPendingStream = useCallback(() => {\n streamFlushTimerRef.current = null;\n const pending = pendingStreamRef.current;\n if (!pending) return;\n pendingStreamRef.current = '';\n setResponse((r) => {\n const next = r + pending;\n const sid = sessionIdRef.current;\n if (sid) markChannelHistoryRunning(sid, next);\n return next;\n });\n }, []);\n\n useEffect(() => () => clearPendingStream(), [clearPendingStream]);\n\n const { saveMessages, patchChannelTitle } = useChatMutations();\n const titlePatchedChannelsRef = useRef(new Set<string>());\n const { accountUserId } = usePrerequisiteIds();\n const [createAccountChatFileUploadLinksMutation] = useCreateAccountChatFileUploadLinksMutation();\n const [createAccountChatFileDownloadLinksMutation] = useCreateAccountChatFileDownloadLinksMutation();\n const {\n messages: backendMessages,\n loading: backendMessagesLoading,\n messagesLoaded: backendMessagesLoaded,\n } = useChatMessages(sessionId);\n const lastHydratedSessionIdRef = useRef<string | null>(null);\n /** Trash-clear: do not re-hydrate gateway history until the next send. */\n const ignoreHydrationRef = useRef(false);\n\n const streamChannelId = sessionId || cdecliStreamOverrideId;\n\n const finalizeCdecliRound = useCallback(\n (assistantContent?: string) => {\n const previewSid = saveSessionIdRef.current || sessionIdRef.current;\n clearPendingStream();\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n followUpRoundRef.current = false;\n setResponse('');\n setQuickReply('');\n setIsLoading(false);\n setIsStreaming(false);\n isSendingRef.current = false;\n if (previewSid) clearChannelHistoryRunning(previewSid);\n if (assistantContent !== undefined) {\n setMessages((prev) =>\n appendOrReplaceAssistant(prev, { role: 'assistant' as const, content: assistantContent }),\n );\n if (previewSid) patchChannelHistoryPreview(previewSid, assistantContent);\n }\n },\n [clearPendingStream],\n );\n\n const cdecliCallbacks = useMemo(\n () => ({\n onChunk: (text: string) => {\n if (!isSendingRef.current) {\n // No send opened this round: the agent is speaking on its own.\n isSendingRef.current = true;\n followUpRoundRef.current = true;\n cdecliRoundHandledRef.current = false;\n saveSessionIdRef.current = sessionIdRef.current;\n setIsLoading(true);\n setIsStreaming(true);\n setError(null);\n const sid = sessionIdRef.current;\n if (sid) markChannelHistoryRunning(sid, text);\n // The agent has started on what was queued: those messages\n // join the thread now, above the answer they are getting.\n const started = queuedFollowUpsRef.current;\n if (started.length > 0) {\n queuedFollowUpsRef.current = [];\n setQueuedFollowUps([]);\n setMessages((prev) => [\n ...prev,\n ...started.map((q) => ({\n role: 'user' as const,\n content: q.text,\n attachments: q.attachments,\n })),\n ]);\n }\n }\n pendingStreamRef.current += text;\n if (streamFlushTimerRef.current == null) {\n streamFlushTimerRef.current = setTimeout(flushPendingStream, STREAM_FLUSH_MS);\n }\n },\n // The brain's provisional answer is the WHOLE reply-so-far, not a chunk:\n // it is set outright, and set to '' when the real stream supersedes it.\n onQuickReply: (text: string) => {\n setQuickReply(text);\n },\n // The gateway's own words on the running work (a status answer, a\n // long-wait report) arrive as thread posts through hydration; the\n // callback only keeps them from ending the reply in progress.\n onNotice: () => undefined,\n onStreamRestart: () => {\n clearPendingStream();\n pendingStreamRef.current = '';\n setResponse('');\n },\n onComplete: (text: string) => {\n clearPendingStream();\n if (cdecliRoundHandledRef.current) {\n return;\n }\n\n // Judge the agent's own words: a quick answer in front of them is\n // not the agent failing, and never belongs in the error banner.\n const agentText = splitPreResponse(text).text;\n if (isCdecliAgentFailureOutput(agentText)) {\n void (async () => {\n if (await tryRecoverAndResendRef.current(agentText)) return;\n if (cdecliRoundHandledRef.current) return;\n cdecliRoundHandledRef.current = true;\n setError(\n isAgentJwtRejected(agentText)\n ? agentText\n : 'CDeCLI agent error. Check gateway status and server-side LLM configuration.',\n );\n finalizeCdecliRound(isAgentJwtRejected(agentText) ? undefined : text);\n })();\n return;\n }\n\n cdecliRoundHandledRef.current = true;\n authRetryUsedRef.current = false;\n\n const reply = resolveAssistantReply(text);\n const sid = saveSessionIdRef.current;\n const um = userMsgForSaveRef.current;\n const followUp = followUpRoundRef.current;\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n followUpRoundRef.current = false;\n\n const assistant = { role: 'assistant' as const, content: reply };\n setMessages((prev) =>\n followUp ? appendFollowUpAssistant(prev, assistant) : appendOrReplaceAssistant(prev, assistant),\n );\n const previewSid = sid || sessionIdRef.current;\n if (previewSid) {\n clearChannelHistoryRunning(previewSid);\n patchChannelHistoryPreview(previewSid, reply);\n }\n setResponse('');\n setQuickReply('');\n setIsLoading(false);\n setIsStreaming(false);\n isSendingRef.current = false;\n\n const rt = routingRef.current;\n const persistClientSide = rt.persistenceMode !== 'backend';\n\n if (sid && um && persistClientSide) {\n saveMessages({\n sessionId: sid,\n ...(accountUserId ? { createdBy: accountUserId } : {}),\n userMessage: {\n content: um.content,\n attachments: um.attachments?.map((a) => ({\n id: a.id,\n name: a.name,\n type: a.type,\n mimeType: a.mimeType,\n size: a.size,\n url: a.url,\n })),\n },\n assistantMessage: {\n content: reply,\n tokenCount: undefined,\n model: 'cdecli-serve',\n },\n }).catch((e) => {\n console.error('[useChatStream] saveMessages (cdecli):', e);\n });\n }\n },\n onError: (err: string) => {\n if (cdecliRoundHandledRef.current) return;\n void (async () => {\n if (await tryRecoverAndResendRef.current(err)) return;\n if (cdecliRoundHandledRef.current) return;\n cdecliRoundHandledRef.current = true;\n setError(err || 'CDeCLI stream error.');\n finalizeCdecliRound(resolveAssistantReply(''));\n })();\n },\n }),\n [saveMessages, accountUserId, finalizeCdecliRound, flushPendingStream, clearPendingStream],\n );\n\n const {\n sendMessage: sendCdecliMessage,\n sendQueued: sendQueuedCdecli,\n cancelQueued: cancelQueuedCdecli,\n cancel: cancelCdecliTurn,\n } = useCdecliChannel(\n routing.channelConnected,\n routing.accountId,\n streamChannelId,\n cdecliCallbacks,\n undefined,\n routing.skill,\n );\n\n const setQueue = useCallback((next: QueuedFollowUp[]) => {\n queuedFollowUpsRef.current = next;\n setQueuedFollowUps(next);\n }, []);\n\n useEffect(() => {\n if (sessionId && cdecliStreamOverrideId && sessionId === cdecliStreamOverrideId) {\n setCdecliStreamOverrideId(undefined);\n }\n }, [sessionId, cdecliStreamOverrideId]);\n\n useEffect(() => {\n const prevSid = lastHydratedSessionIdRef.current;\n if (prevSid && sessionId && prevSid !== sessionId && isSendingRef.current) {\n // Switching threads: drop local send flags so this view can hydrate.\n // The previous channel's agent keeps running server-side; history still\n // shows it as running until that turn completes or we reopen it.\n clearPendingStream();\n setQueue([]);\n isSendingRef.current = false;\n followUpRoundRef.current = false;\n cdecliRoundHandledRef.current = false;\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n setIsLoading(false);\n setIsStreaming(false);\n setResponse('');\n setQuickReply('');\n }\n\n if (!sessionId) {\n setMessages([]);\n setResponse('');\n setQuickReply('');\n lastHydratedSessionIdRef.current = null;\n return;\n }\n\n const stampHistoryPreview = (msgs: ChatMessage[]) => {\n const last = msgs[msgs.length - 1];\n const lastAssistant = [...msgs].reverse().find((msg) => msg.role === 'assistant' && msg.content.trim());\n if (lastAssistant && !isSendingRef.current) {\n patchChannelHistoryPreview(sessionId, lastAssistant.content);\n }\n // Finished thread (last post is assistant) — drop the history \"Running\" badge.\n // A live turn ends with a user post until the assistant lands.\n if (!isSendingRef.current && last?.role === 'assistant') {\n clearChannelHistoryRunning(sessionId);\n }\n };\n\n if (ignoreHydrationRef.current) {\n return;\n }\n\n const keepStream = isSendingRef.current;\n const sessionChanged = lastHydratedSessionIdRef.current !== sessionId;\n if (sessionChanged) {\n lastHydratedSessionIdRef.current = sessionId;\n // Do not merge the previous channel's in-memory thread into this one.\n if (!backendMessagesLoaded) {\n if (!keepStream) {\n setMessages([]);\n setResponse('');\n setQuickReply('');\n }\n return;\n }\n const formatted = formatBackendThread(backendMessages ?? []);\n stampHistoryPreview(formatted);\n setMessages((prev) => (keepStream ? mergeOptimisticGatewayMessages(prev, formatted) : formatted));\n if (!keepStream) {\n setResponse('');\n setQuickReply('');\n }\n return;\n }\n\n if (backendMessagesLoaded) {\n const formatted = formatBackendThread(backendMessages ?? []);\n stampHistoryPreview(formatted);\n // Positional merge first so assistant replies the gateway never persisted\n // (cdecli skill turns emit no chatMessageAdded) survive this snapshot instead\n // of being dropped, then preserve in-memory attachment previews on the result.\n setMessages((prev) => preserveAttachmentPreviews(prev, mergeOptimisticGatewayMessages(prev, formatted)));\n if (!keepStream) {\n setResponse('');\n setQuickReply('');\n }\n }\n }, [sessionId, backendMessages, backendMessagesLoaded, clearPendingStream, setQueue]);\n\n const sendMessage = useCallback(\n async (\n content: string,\n attachments?: MessageAttachment[],\n sessionIdOverride?: string | null,\n alreadyVisible = false,\n /** Set when this send is the release of an outbox entry; it owns that entry's fate. */\n queuedId: string | undefined = undefined,\n /**\n * Extras for this turn (a surface job the agent settles). Only a turn that\n * starts now carries them: a queued or parked message goes out later without.\n */\n turnOptions: CdecliTurnOptions = {},\n ): Promise<SendOutcome> => {\n if (!content.trim() && !attachments?.length) return 'failed';\n\n const effectiveSessionId = sessionIdOverride !== undefined ? sessionIdOverride : sessionId;\n const rt = routingRef.current;\n const trimmed = content.trim();\n const sendText =\n trimmed ||\n (attachments?.length\n ? `Attached: ${attachments\n .map((a) => a.name)\n .filter(Boolean)\n .join(', ')}`\n : '');\n\n const userMessage: ChatMessage = {\n role: 'user',\n content: sendText,\n attachments,\n };\n\n /**\n * The gateway is down. The turn is remembered, not erased: it stays in the\n * thread as a queued bubble and the outbox releases it in order the moment\n * the channel reports connected. Losing what the user typed here is the\n * whole reason this lane exists.\n */\n if (!rt.channelConnected) {\n if (!effectiveSessionId) {\n setError('Missing chat session id.');\n return 'failed';\n }\n // A release that raced the connection dropping stays where it is.\n if (queuedId) return 'queued';\n const result = queueGatewayMessage({\n channelId: effectiveSessionId,\n accountId: rt.accountId,\n text: sendText,\n attachments,\n });\n if (!result.ok) {\n setError(\n result.reason === 'full'\n ? `${MAX_QUEUED_MESSAGES} messages are already waiting for the gateway. They send as soon as it connects.`\n : 'Nothing to send.',\n );\n return 'failed';\n }\n if (alreadyVisible) setMessages((prev) => withoutTrailingUserMessage(prev, sendText));\n setError(null);\n return 'queued';\n }\n\n /**\n * A turn is already in flight. The gateway queues the message for the\n * agent, which answers it when the running turn ends; the running reply\n * is left alone. It waits above the composer (cancellable) until the\n * agent starts on it. A status question is answered on the spot, and a\n * turn that had already ended takes the message straight away.\n */\n if (isSendingRef.current) {\n if (!effectiveSessionId) {\n setError('Missing chat session id.');\n return 'failed';\n }\n const media = attachments?.length ? buildGatewayMedia(attachments, { includeData: true }) : [];\n const outcome = await sendQueuedCdecli(sendText, effectiveSessionId, media);\n if (outcome.status === 'failed') {\n setError(outcome.error || 'Could not send the message. Try again.');\n return 'failed';\n }\n if (queuedId) dropQueuedGatewayMessage(queuedId);\n setError(null);\n if (outcome.status === 'queued') {\n setQueue([\n ...queuedFollowUpsRef.current,\n { id: outcome.id, text: sendText, attachments: releasePreviewBytes(attachments) },\n ]);\n if (alreadyVisible) setMessages((prev) => withoutTrailingUserMessage(prev, sendText));\n } else if (!alreadyVisible) {\n setMessages((prev) => [...prev, userMessage]);\n }\n return 'sent';\n }\n\n isSendingRef.current = true;\n followUpRoundRef.current = false;\n ignoreHydrationRef.current = false;\n if (effectiveSessionId && sendText) {\n titlePatchedChannelsRef.current.add(effectiveSessionId);\n patchChannelTitle(effectiveSessionId, sendText, true);\n markChannelHistoryRunning(effectiveSessionId);\n }\n if (!alreadyVisible) {\n // The transcript keeps this turn for the rest of the session, so it\n // keeps the preview URI and not the base64 behind it. The bytes stay\n // on `userMsgForSaveRef` below, which is nulled when the turn ends -\n // a retry still has them, a backgrounded app no longer carries them.\n const retained: ChatMessage = {\n ...userMessage,\n attachments: releasePreviewBytes(userMessage.attachments),\n };\n setMessages((prev) => [...prev, retained]);\n authRetryUsedRef.current = false;\n }\n setResponse('');\n setQuickReply('');\n setIsLoading(true);\n setIsStreaming(true);\n setError(null);\n userMsgForSaveRef.current = userMessage;\n saveSessionIdRef.current = effectiveSessionId ?? null;\n cdecliRoundHandledRef.current = false;\n\n if (!effectiveSessionId) {\n setError('Missing chat session id.');\n if (!alreadyVisible) setMessages((p) => p.slice(0, -1));\n isSendingRef.current = false;\n setIsLoading(false);\n setIsStreaming(false);\n userMsgForSaveRef.current = null;\n saveSessionIdRef.current = null;\n return 'failed';\n }\n\n if (sessionIdOverride) {\n setCdecliStreamOverrideId(sessionIdOverride);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n }\n\n const inlineForHostedAgent = shouldInlineGatewayMediaForHostedAgent(\n config.GRAPHQL_URL,\n config.CDECLI_AGENT_ENDPOINT,\n );\n // Vision turns must carry inline bytes. URL-only media is what made\n // \"what is this\" + a photo look like a text-only question.\n const inlineImages = (attachments ?? []).some(isImageAttachment) || inlineForHostedAgent;\n let media = inlineImages\n ? buildGatewayMedia(attachments, { includeData: true })\n : await buildGatewayMediaPreferUrls(\n attachments,\n async (filenames) => {\n const result = await createAccountChatFileUploadLinksMutation({\n variables: { filenames },\n });\n return result.data?.createAccountChatFileUploadLinks || [];\n },\n async (urls) => {\n const result = await createAccountChatFileDownloadLinksMutation({\n variables: { urls },\n });\n return result.data?.createAccountChatFileDownloadLinks || [];\n },\n );\n if ((attachments?.length ?? 0) > 0 && media.length === 0) {\n media = buildGatewayMedia(attachments, { includeData: true });\n }\n const ok = await sendCdecliMessage(sendText, effectiveSessionId, media, turnOptions);\n if (!ok && !cdecliRoundHandledRef.current && !recoveringAuthRef.current) {\n cdecliRoundHandledRef.current = true;\n // The send never reached the agent. Park it rather than drop it - a\n // release keeps its place at the head of its own queue.\n const parked = queuedId\n ? { ok: true as const }\n : queueGatewayMessage({\n channelId: effectiveSessionId,\n accountId: rt.accountId,\n text: sendText,\n attachments,\n });\n setError(\n parked.ok\n ? 'Gateway unreachable - message queued. It sends as soon as the connection is back.'\n : 'Failed to send message via CDeCLI. Check gateway connection.',\n );\n // Queued: the queued row renders the turn, so the optimistic bubble goes.\n // Not queued: only drop a bubble this call put there.\n setMessages((p) => {\n if (parked.ok) return withoutTrailingUserMessage(p, sendText);\n return alreadyVisible ? p : p.slice(0, -1);\n });\n isSendingRef.current = false;\n setIsLoading(false);\n setIsStreaming(false);\n userMsgForSaveRef.current = null;\n saveSessionIdRef.current = null;\n clearChannelHistoryRunning(effectiveSessionId);\n return parked.ok ? 'queued' : 'failed';\n }\n if (ok && queuedId) dropQueuedGatewayMessage(queuedId);\n return ok ? 'sent' : 'failed';\n },\n [\n sessionId,\n sendCdecliMessage,\n patchChannelTitle,\n sendQueuedCdecli,\n setQueue,\n createAccountChatFileUploadLinksMutation,\n createAccountChatFileDownloadLinksMutation,\n ],\n );\n\n sendMessageRef.current = sendMessage;\n\n /* ---------------------------------------------------------------------- *\n * Outbox: turns typed while the gateway was down.\n * ---------------------------------------------------------------------- */\n\n const queuedChannelId = sessionId || cdecliStreamOverrideId || null;\n const queuedSnapshot = useCallback(\n () => queuedGatewayMessages(queuedChannelId, routing.accountId),\n [queuedChannelId, routing.accountId],\n );\n const queuedMessages = useSyncExternalStore(subscribeQueuedGatewayMessages, queuedSnapshot, queuedSnapshot);\n\n useEffect(() => {\n void restoreQueuedGatewayMessages();\n }, []);\n\n /** One release at a time; a send that fails leaves its entry at the head for the next reconnect. */\n const flushingRef = useRef(false);\n const flushQueuedMessages = useCallback(async () => {\n if (flushingRef.current) return;\n flushingRef.current = true;\n try {\n // Bounded: the outbox can never hand out more than its own depth.\n for (let i = 0; i <= MAX_QUEUED_MESSAGES; i += 1) {\n const rt = routingRef.current;\n const sid = sessionIdRef.current;\n if (!rt.channelConnected || !sid) return;\n const next = queuedGatewayMessages(sid, rt.accountId)[0];\n if (!next) return;\n const outcome = await sendMessageRef.current(\n next.text,\n next.attachments,\n next.channelId,\n false,\n next.id,\n );\n if (outcome !== 'sent') return;\n }\n } finally {\n flushingRef.current = false;\n }\n }, []);\n\n /**\n * Release on the disconnected→connected edge, and when a channel with a queue\n * is opened on an already-live gateway. Deliberately not keyed on queue depth:\n * a send that fails puts its turn back, and depth would spin that into a loop.\n */\n const wasConnectedRef = useRef(routing.channelConnected);\n useEffect(() => {\n const connected = routing.channelConnected;\n const reconnected = connected && !wasConnectedRef.current;\n wasConnectedRef.current = connected;\n if (!connected || !queuedChannelId) return;\n if (!reconnected && queuedGatewayMessages(queuedChannelId, routing.accountId).length === 0) return;\n void flushQueuedMessages();\n }, [routing.channelConnected, routing.accountId, queuedChannelId, flushQueuedMessages]);\n\n const discardQueuedMessages = useCallback(() => {\n if (!queuedChannelId) return;\n clearQueuedGatewayMessages(queuedChannelId);\n setError(null);\n }, [queuedChannelId]);\n\n tryRecoverAndResendRef.current = async (reason: string) => {\n if (authRetryUsedRef.current || cdecliRoundHandledRef.current) return false;\n if (!isAgentJwtRejected(reason)) return false;\n const recover = routingRef.current.recoverAgentAuth;\n if (!recover) return false;\n authRetryUsedRef.current = true;\n recoveringAuthRef.current = true;\n try {\n setError('Refreshing CDeCLI credentials…');\n const recovered = await recover();\n if (!recovered) return false;\n const um = userMsgForSaveRef.current;\n const sid = saveSessionIdRef.current;\n cdecliRoundHandledRef.current = false;\n isSendingRef.current = false;\n setIsLoading(false);\n setIsStreaming(false);\n setError(null);\n if (um && sid) {\n queueMicrotask(() => {\n void sendMessageRef.current(um.content, um.attachments, sid, true);\n });\n }\n return true;\n } catch (err) {\n console.error('[useChatStream] recoverAgentAuth failed:', err);\n return false;\n } finally {\n recoveringAuthRef.current = false;\n }\n };\n\n const cancel = useCallback(() => {\n clearPendingStream();\n const sid = saveSessionIdRef.current ?? sessionId;\n void cancelCdecliTurn(sid ?? 'messenger');\n if (!cdecliRoundHandledRef.current) {\n cdecliRoundHandledRef.current = true;\n finalizeCdecliRound(resolveAssistantReply(''));\n }\n }, [cancelCdecliTurn, finalizeCdecliRound, sessionId, clearPendingStream]);\n\n const clearMessages = useCallback(() => {\n clearPendingStream();\n const hadActiveTurn = isSendingRef.current;\n const sid = saveSessionIdRef.current ?? sessionId;\n isSendingRef.current = false;\n cdecliRoundHandledRef.current = true;\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n // Only hit /cancel when a turn was live — otherwise cdecli logs\n // \"Cannot cancel: no active session\" (Research greet + trash).\n if (hadActiveTurn) {\n void cancelCdecliTurn(sid ?? 'messenger');\n }\n ignoreHydrationRef.current = true;\n // Emptying the thread empties its outbox too: a queued turn released after\n // the user cleared the chat would answer a conversation they threw away.\n if (sid) clearQueuedGatewayMessages(sid);\n // ...and what the gateway queued for the agent.\n for (const queued of queuedFollowUpsRef.current) void cancelQueuedCdecli(queued.id, sid ?? 'messenger');\n setQueue([]);\n setMessages([]);\n setResponse('');\n setQuickReply('');\n setError(null);\n setIsLoading(false);\n setIsStreaming(false);\n if (sid) clearChannelHistoryRunning(sid);\n }, [cancelCdecliTurn, cancelQueuedCdecli, clearPendingStream, sessionId, setQueue]);\n\n /** Take one queued message back before the agent gets to it. Too late once\n * the agent started it: it then joins the thread with its answer. */\n const cancelQueuedFollowUp = useCallback(\n async (id: string) => {\n const sid = sessionIdRef.current;\n if (!sid) return;\n const result = await cancelQueuedCdecli(id, sid);\n if (result.ok) {\n setQueue(queuedFollowUpsRef.current.filter((q) => q.id !== id));\n } else {\n setError(result.error || 'Could not cancel that message.');\n }\n },\n [cancelQueuedCdecli, setQueue],\n );\n\n /** Stop the running reply so the agent takes the queued messages now. */\n const sendQueuedNow = useCallback(() => {\n if (isSendingRef.current) cancel();\n }, [cancel]);\n\n const messagesLoading = !!sessionId && backendMessagesLoading && !backendMessagesLoaded;\n\n return {\n messages,\n response,\n quickReply,\n error,\n isLoading,\n isStreaming,\n messagesLoading,\n hasMessages:\n messages.length > 0 ||\n !!response ||\n !!quickReply ||\n queuedMessages.length > 0 ||\n queuedFollowUps.length > 0,\n /** Turns waiting on the gateway, oldest first. Rendered as queued bubbles. */\n queuedMessages,\n /** Send the queue now (the notice's retry), independent of the reconnect edge. */\n flushQueuedMessages,\n discardQueuedMessages,\n /** Sent while the agent was busy and queued for it, oldest first. */\n queuedFollowUps,\n cancelQueuedFollowUp,\n sendQueuedNow,\n sendMessage,\n cancel,\n clearMessages,\n };\n}\n"],"names":["media","_a"],"mappings":"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"}
1
+ {"version":3,"file":"useChatStream.js","sources":["../../src/hooks/useChatStream.ts"],"sourcesContent":["import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from 'react';\nimport { Platform } from 'react-native';\nimport {\n useCreateAccountChatFileDownloadLinksMutation,\n useCreateAccountChatFileUploadLinksMutation,\n} from 'common/graphql';\nimport {\n clearChannelHistoryRunning,\n markChannelHistoryRunning,\n patchChannelHistoryPreview,\n useChatMutations,\n useChatMessages,\n} from './useChatApi';\nimport { useCdecliChannel, type CdecliTurnOptions } from './useCdecliChannel';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\nimport { isAgentJwtRejected, shouldInlineGatewayMediaForHostedAgent } from './systemTokenJwt';\nimport { config } from '../config';\nimport {\n MAX_QUEUED_MESSAGES,\n clearQueuedGatewayMessages,\n dropQueuedGatewayMessage,\n isQueuedMessageDelivered,\n queueGatewayMessage,\n queuedGatewayMessages,\n restoreQueuedGatewayMessages,\n subscribeQueuedGatewayMessages,\n} from '../services/queuedGatewayMessages';\nimport { isImageAttachment } from '../features/attachments/isImageAttachment';\nimport { buildGatewayMedia, buildGatewayMediaPreferUrls } from '../features/attachments/buildGatewayMedia';\nimport { preserveAttachmentPreviews } from '../features/attachments/preserveAttachmentPreviews';\nimport { releasePreviewBytes } from '../features/attachments/releasePreviewBytes';\nimport { resolveAssistantReply } from '../features/chat/emptyChatResponse';\nimport { splitPreResponse } from '../features/chat/preResponse';\nimport {\n appendFollowUpAssistant,\n appendOrReplaceAssistant,\n collapseConsecutiveAssistants,\n mergeOptimisticGatewayMessages,\n} from '../state/chatThreadMerge';\n\n// Each flush re-parses the whole reply as markdown. Mid-range Android CPUs\n// drop frames at 20 flushes/s; 10/s still reads as live typing.\nconst STREAM_FLUSH_MS = Platform.OS === 'android' ? 100 : 50;\n\n/** Agent returned an error while the cdecli-serve channel stayed connected (e.g. bad LLM API key). */\nfunction isCdecliAgentFailureOutput(text: string): boolean {\n const t = text.trim();\n if (!t) return false;\n return (\n /⚠\\s*Error:/.test(t) ||\n /LLM stream error/i.test(t) ||\n /Incorrect API key/i.test(t) ||\n /invalid_api_key/i.test(t) ||\n /authentication_error/i.test(t) ||\n isAgentJwtRejected(t)\n );\n}\n\nexport interface MessageAttachment {\n id: string;\n name: string;\n type: 'file' | 'screenshot';\n dataUrl?: string;\n mimeType?: string;\n size?: number;\n url?: string;\n}\n\nexport interface ChatMessage {\n role: 'user' | 'assistant';\n content: string;\n attachments?: MessageAttachment[];\n metadata?: {\n tokenUsage?: { inputTokens: number; outputTokens: number; totalTokens: number };\n model?: string;\n };\n}\n\n/** The queued row renders this turn now, so the optimistic bubble must not stay behind it. */\nfunction withoutTrailingUserMessage(prev: ChatMessage[], text: string): ChatMessage[] {\n const last = prev[prev.length - 1];\n return last?.role === 'user' && last.content === text ? prev.slice(0, -1) : prev;\n}\n\n/** A message the gateway queued for the agent while a turn was running. */\nexport interface QueuedFollowUp {\n /** The gateway's id for it - what cancels it. */\n id: string;\n text: string;\n attachments?: MessageAttachment[];\n}\n\n/** What became of a send: delivered to the gateway, parked in the outbox, or refused outright. */\nexport type SendOutcome = 'sent' | 'queued' | 'failed';\n\nfunction formatBackendThread(\n backendMessages: Array<{\n role: string;\n content?: string;\n // The generated `ChatMessageUI` types `type` as a plain string; it is narrowed\n // to the UI union in the map below, so take it wide rather than cast at the call.\n attachments?: Array<{ id: string; name: string; type: string; mimeType?: string; size?: number; url?: string }>;\n tokenCount?: number;\n model?: string;\n }>,\n): ChatMessage[] {\n return collapseConsecutiveAssistants(\n backendMessages.map((msg) => ({\n role: msg.role as 'user' | 'assistant',\n content: msg.content ?? '',\n attachments: msg.attachments?.map((a) => ({\n id: a.id,\n name: a.name,\n type: a.type === 'screenshot' ? 'screenshot' : 'file',\n mimeType: a.mimeType,\n size: a.size,\n url: a.url,\n })),\n metadata: msg.tokenCount\n ? {\n tokenUsage: {\n inputTokens: 0,\n outputTokens: msg.tokenCount,\n totalTokens: msg.tokenCount,\n },\n model: msg.model,\n }\n : undefined,\n })),\n );\n}\n\n/** Mobile chat routes exclusively through CDeCLI (messenger-gateway). */\nexport interface UseChatStreamRouting {\n channelConnected: boolean;\n accountId: string;\n /** When `'backend'`, the gateway persists posts — skip client `saveMessages`. */\n persistenceMode?: 'backend' | 'frontend';\n /** Remint the system JWT and reconnect after cdecli-agent 401s the session. */\n recoverAgentAuth?: () => Promise<boolean>;\n /** Agent skill every turn on this stream carries (`metadata.skill`), e.g. Web Builder's. */\n skill?: string;\n}\n\nexport function useChatStream(\n sessionId: string | null,\n routing: UseChatStreamRouting = {\n channelConnected: false,\n accountId: 'default',\n persistenceMode: 'backend',\n },\n) {\n const [messages, setMessages] = useState<ChatMessage[]>([]);\n const [response, setResponse] = useState('');\n /** Brain assist — never the published agent stream. Shown as a Quick answer strip. */\n const [quickReply, setQuickReply] = useState('');\n const [isLoading, setIsLoading] = useState(false);\n const [isStreaming, setIsStreaming] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const isSendingRef = useRef(false);\n /**\n * Messages sent while a turn was running, which the gateway queued for the\n * agent. Shown above the composer until the agent starts on them (the way\n * Claude Code shows a message typed mid-turn), then moved into the thread.\n * Kept out of `messages` so a backend hydration cannot drop or double them.\n */\n const [queuedFollowUps, setQueuedFollowUps] = useState<QueuedFollowUp[]>([]);\n const queuedFollowUpsRef = useRef<QueuedFollowUp[]>([]);\n const sendMessageRef = useRef<\n (\n content: string,\n attachments?: MessageAttachment[],\n sessionIdOverride?: string | null,\n alreadyVisible?: boolean,\n queuedId?: string,\n turnOptions?: CdecliTurnOptions,\n ) => Promise<SendOutcome>\n >(async () => 'failed');\n\n /** Ensures GraphQL stream subscriptions use the id from createChannel+send before the next parent render. */\n const [cdecliStreamOverrideId, setCdecliStreamOverrideId] = useState<string | undefined>(undefined);\n const userMsgForSaveRef = useRef<ChatMessage | null>(null);\n const saveSessionIdRef = useRef<string | null>(null);\n const sessionIdRef = useRef<string | null>(sessionId);\n sessionIdRef.current = sessionId || cdecliStreamOverrideId || null;\n /** Blocks duplicate cdecli `onComplete` from appending assistant twice / saving twice per send. */\n const cdecliRoundHandledRef = useRef(false);\n /**\n * The round in flight was opened by the agent, not by a send: a follow-up,\n * e.g. the answer to a request the gateway queued while the agent was busy.\n * It completes as a new bubble instead of replacing the reply before it.\n */\n const followUpRoundRef = useRef(false);\n /** One remint+resend per turn when cdecli-agent 401s the JWT. */\n const authRetryUsedRef = useRef(false);\n const recoveringAuthRef = useRef(false);\n const routingRef = useRef(routing);\n routingRef.current = routing;\n\n const tryRecoverAndResendRef = useRef<(reason: string) => Promise<boolean>>(async () => false);\n\n /** Coalesce token deltas so Fabric is not committing a native tree every chunk. */\n const pendingStreamRef = useRef('');\n const streamFlushTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const clearPendingStream = useCallback(() => {\n if (streamFlushTimerRef.current != null) {\n clearTimeout(streamFlushTimerRef.current);\n streamFlushTimerRef.current = null;\n }\n pendingStreamRef.current = '';\n }, []);\n\n const flushPendingStream = useCallback(() => {\n streamFlushTimerRef.current = null;\n const pending = pendingStreamRef.current;\n if (!pending) return;\n pendingStreamRef.current = '';\n setResponse((r) => {\n const next = r + pending;\n const sid = sessionIdRef.current;\n if (sid) markChannelHistoryRunning(sid, next);\n return next;\n });\n }, []);\n\n useEffect(() => () => clearPendingStream(), [clearPendingStream]);\n\n const { saveMessages, patchChannelTitle } = useChatMutations();\n const titlePatchedChannelsRef = useRef(new Set<string>());\n const { accountUserId } = usePrerequisiteIds();\n const [createAccountChatFileUploadLinksMutation] = useCreateAccountChatFileUploadLinksMutation();\n const [createAccountChatFileDownloadLinksMutation] = useCreateAccountChatFileDownloadLinksMutation();\n const {\n messages: backendMessages,\n loading: backendMessagesLoading,\n messagesLoaded: backendMessagesLoaded,\n } = useChatMessages(sessionId);\n const lastHydratedSessionIdRef = useRef<string | null>(null);\n /** Trash-clear: do not re-hydrate gateway history until the next send. */\n const ignoreHydrationRef = useRef(false);\n\n const streamChannelId = sessionId || cdecliStreamOverrideId;\n\n const finalizeCdecliRound = useCallback(\n (assistantContent?: string) => {\n const previewSid = saveSessionIdRef.current || sessionIdRef.current;\n clearPendingStream();\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n followUpRoundRef.current = false;\n setResponse('');\n setQuickReply('');\n setIsLoading(false);\n setIsStreaming(false);\n isSendingRef.current = false;\n if (previewSid) clearChannelHistoryRunning(previewSid);\n if (assistantContent !== undefined) {\n setMessages((prev) =>\n appendOrReplaceAssistant(prev, { role: 'assistant' as const, content: assistantContent }),\n );\n if (previewSid) patchChannelHistoryPreview(previewSid, assistantContent);\n }\n },\n [clearPendingStream],\n );\n\n const cdecliCallbacks = useMemo(\n () => ({\n onChunk: (text: string) => {\n if (!isSendingRef.current) {\n // No send opened this round: the agent is speaking on its own.\n isSendingRef.current = true;\n followUpRoundRef.current = true;\n cdecliRoundHandledRef.current = false;\n saveSessionIdRef.current = sessionIdRef.current;\n setIsLoading(true);\n setIsStreaming(true);\n setError(null);\n const sid = sessionIdRef.current;\n if (sid) markChannelHistoryRunning(sid, text);\n // The agent has started on what was queued: those messages\n // join the thread now, above the answer they are getting.\n const started = queuedFollowUpsRef.current;\n if (started.length > 0) {\n queuedFollowUpsRef.current = [];\n setQueuedFollowUps([]);\n setMessages((prev) => [\n ...prev,\n ...started.map((q) => ({\n role: 'user' as const,\n content: q.text,\n attachments: q.attachments,\n })),\n ]);\n }\n }\n pendingStreamRef.current += text;\n if (streamFlushTimerRef.current == null) {\n streamFlushTimerRef.current = setTimeout(flushPendingStream, STREAM_FLUSH_MS);\n }\n },\n // The brain's provisional answer is the WHOLE reply-so-far, not a chunk:\n // it is set outright, and set to '' when the real stream supersedes it.\n onQuickReply: (text: string) => {\n setQuickReply(text);\n },\n // The gateway's own words on the running work (a status answer, a\n // long-wait report) arrive as thread posts through hydration; the\n // callback only keeps them from ending the reply in progress.\n onNotice: () => undefined,\n onStreamRestart: () => {\n clearPendingStream();\n pendingStreamRef.current = '';\n setResponse('');\n },\n onComplete: (text: string) => {\n clearPendingStream();\n if (cdecliRoundHandledRef.current) {\n return;\n }\n\n // Judge the agent's own words: a quick answer in front of them is\n // not the agent failing, and never belongs in the error banner.\n const agentText = splitPreResponse(text).text;\n if (isCdecliAgentFailureOutput(agentText)) {\n void (async () => {\n if (await tryRecoverAndResendRef.current(agentText)) return;\n if (cdecliRoundHandledRef.current) return;\n cdecliRoundHandledRef.current = true;\n setError(\n isAgentJwtRejected(agentText)\n ? agentText\n : 'CDeCLI agent error. Check gateway status and server-side LLM configuration.',\n );\n finalizeCdecliRound(isAgentJwtRejected(agentText) ? undefined : text);\n })();\n return;\n }\n\n cdecliRoundHandledRef.current = true;\n authRetryUsedRef.current = false;\n\n const reply = resolveAssistantReply(text);\n const sid = saveSessionIdRef.current;\n const um = userMsgForSaveRef.current;\n const followUp = followUpRoundRef.current;\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n followUpRoundRef.current = false;\n\n const assistant = { role: 'assistant' as const, content: reply };\n setMessages((prev) =>\n followUp ? appendFollowUpAssistant(prev, assistant) : appendOrReplaceAssistant(prev, assistant),\n );\n const previewSid = sid || sessionIdRef.current;\n if (previewSid) {\n clearChannelHistoryRunning(previewSid);\n patchChannelHistoryPreview(previewSid, reply);\n }\n setResponse('');\n setQuickReply('');\n setIsLoading(false);\n setIsStreaming(false);\n isSendingRef.current = false;\n\n const rt = routingRef.current;\n const persistClientSide = rt.persistenceMode !== 'backend';\n\n if (sid && um && persistClientSide) {\n saveMessages({\n sessionId: sid,\n ...(accountUserId ? { createdBy: accountUserId } : {}),\n userMessage: {\n content: um.content,\n attachments: um.attachments?.map((a) => ({\n id: a.id,\n name: a.name,\n type: a.type,\n mimeType: a.mimeType,\n size: a.size,\n url: a.url,\n })),\n },\n assistantMessage: {\n content: reply,\n tokenCount: undefined,\n model: 'cdecli-serve',\n },\n }).catch((e) => {\n console.error('[useChatStream] saveMessages (cdecli):', e);\n });\n }\n },\n onError: (err: string) => {\n if (cdecliRoundHandledRef.current) return;\n void (async () => {\n if (await tryRecoverAndResendRef.current(err)) return;\n if (cdecliRoundHandledRef.current) return;\n cdecliRoundHandledRef.current = true;\n setError(err || 'CDeCLI stream error.');\n finalizeCdecliRound(resolveAssistantReply(''));\n })();\n },\n }),\n [saveMessages, accountUserId, finalizeCdecliRound, flushPendingStream, clearPendingStream],\n );\n\n const {\n sendMessage: sendCdecliMessage,\n sendQueued: sendQueuedCdecli,\n cancelQueued: cancelQueuedCdecli,\n cancel: cancelCdecliTurn,\n } = useCdecliChannel(\n routing.channelConnected,\n routing.accountId,\n streamChannelId,\n cdecliCallbacks,\n undefined,\n routing.skill,\n );\n\n const setQueue = useCallback((next: QueuedFollowUp[]) => {\n queuedFollowUpsRef.current = next;\n setQueuedFollowUps(next);\n }, []);\n\n useEffect(() => {\n if (sessionId && cdecliStreamOverrideId && sessionId === cdecliStreamOverrideId) {\n setCdecliStreamOverrideId(undefined);\n }\n }, [sessionId, cdecliStreamOverrideId]);\n\n useEffect(() => {\n const prevSid = lastHydratedSessionIdRef.current;\n if (prevSid && sessionId && prevSid !== sessionId && isSendingRef.current) {\n // Switching threads: drop local send flags so this view can hydrate.\n // The previous channel's agent keeps running server-side; history still\n // shows it as running until that turn completes or we reopen it.\n clearPendingStream();\n setQueue([]);\n isSendingRef.current = false;\n followUpRoundRef.current = false;\n cdecliRoundHandledRef.current = false;\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n setIsLoading(false);\n setIsStreaming(false);\n setResponse('');\n setQuickReply('');\n }\n\n if (!sessionId) {\n setMessages([]);\n setResponse('');\n setQuickReply('');\n lastHydratedSessionIdRef.current = null;\n return;\n }\n\n const stampHistoryPreview = (msgs: ChatMessage[]) => {\n const last = msgs[msgs.length - 1];\n const lastAssistant = [...msgs].reverse().find((msg) => msg.role === 'assistant' && msg.content.trim());\n if (lastAssistant && !isSendingRef.current) {\n patchChannelHistoryPreview(sessionId, lastAssistant.content);\n }\n // Finished thread (last post is assistant) — drop the history \"Running\" badge.\n // A live turn ends with a user post until the assistant lands.\n if (!isSendingRef.current && last?.role === 'assistant') {\n clearChannelHistoryRunning(sessionId);\n }\n };\n\n if (ignoreHydrationRef.current) {\n return;\n }\n\n const keepStream = isSendingRef.current;\n const sessionChanged = lastHydratedSessionIdRef.current !== sessionId;\n if (sessionChanged) {\n lastHydratedSessionIdRef.current = sessionId;\n // Do not merge the previous channel's in-memory thread into this one.\n if (!backendMessagesLoaded) {\n if (!keepStream) {\n setMessages([]);\n setResponse('');\n setQuickReply('');\n }\n return;\n }\n const formatted = formatBackendThread(backendMessages ?? []);\n stampHistoryPreview(formatted);\n setMessages((prev) => (keepStream ? mergeOptimisticGatewayMessages(prev, formatted) : formatted));\n if (!keepStream) {\n setResponse('');\n setQuickReply('');\n }\n return;\n }\n\n if (backendMessagesLoaded) {\n const formatted = formatBackendThread(backendMessages ?? []);\n stampHistoryPreview(formatted);\n // Positional merge first so assistant replies the gateway never persisted\n // (cdecli skill turns emit no chatMessageAdded) survive this snapshot instead\n // of being dropped, then preserve in-memory attachment previews on the result.\n setMessages((prev) => preserveAttachmentPreviews(prev, mergeOptimisticGatewayMessages(prev, formatted)));\n if (!keepStream) {\n setResponse('');\n setQuickReply('');\n }\n }\n }, [sessionId, backendMessages, backendMessagesLoaded, clearPendingStream, setQueue]);\n\n const sendMessage = useCallback(\n async (\n content: string,\n attachments?: MessageAttachment[],\n sessionIdOverride?: string | null,\n alreadyVisible = false,\n /** Set when this send is the release of an outbox entry; it owns that entry's fate. */\n queuedId: string | undefined = undefined,\n /**\n * Extras for this turn (a surface job the agent settles). Only a turn that\n * starts now carries them: a queued or parked message goes out later without.\n */\n turnOptions: CdecliTurnOptions = {},\n ): Promise<SendOutcome> => {\n if (!content.trim() && !attachments?.length) return 'failed';\n\n const effectiveSessionId = sessionIdOverride !== undefined ? sessionIdOverride : sessionId;\n const rt = routingRef.current;\n const trimmed = content.trim();\n const sendText =\n trimmed ||\n (attachments?.length\n ? `Attached: ${attachments\n .map((a) => a.name)\n .filter(Boolean)\n .join(', ')}`\n : '');\n\n const userMessage: ChatMessage = {\n role: 'user',\n content: sendText,\n attachments,\n };\n\n /**\n * The gateway is down. The turn is remembered, not erased: it stays in the\n * thread as a queued bubble and the outbox releases it in order the moment\n * the channel reports connected. Losing what the user typed here is the\n * whole reason this lane exists.\n */\n if (!rt.channelConnected) {\n if (!effectiveSessionId) {\n setError('Missing chat session id.');\n return 'failed';\n }\n // A release that raced the connection dropping stays where it is.\n if (queuedId) return 'queued';\n const result = queueGatewayMessage({\n channelId: effectiveSessionId,\n accountId: rt.accountId,\n text: sendText,\n attachments,\n });\n if (!result.ok) {\n setError(\n result.reason === 'full'\n ? `${MAX_QUEUED_MESSAGES} messages are already waiting for the gateway. They send as soon as it connects.`\n : 'Nothing to send.',\n );\n return 'failed';\n }\n if (alreadyVisible) setMessages((prev) => withoutTrailingUserMessage(prev, sendText));\n setError(null);\n return 'queued';\n }\n\n /**\n * A turn is already in flight. The gateway queues the message for the\n * agent, which answers it when the running turn ends; the running reply\n * is left alone. It waits above the composer (cancellable) until the\n * agent starts on it. A status question is answered on the spot, and a\n * turn that had already ended takes the message straight away.\n */\n if (isSendingRef.current) {\n if (!effectiveSessionId) {\n setError('Missing chat session id.');\n return 'failed';\n }\n const media = attachments?.length ? buildGatewayMedia(attachments, { includeData: true }) : [];\n const outcome = await sendQueuedCdecli(sendText, effectiveSessionId, media);\n if (outcome.status === 'failed') {\n setError(outcome.error || 'Could not send the message. Try again.');\n return 'failed';\n }\n if (queuedId) dropQueuedGatewayMessage(queuedId);\n setError(null);\n if (outcome.status === 'queued') {\n setQueue([\n ...queuedFollowUpsRef.current,\n { id: outcome.id, text: sendText, attachments: releasePreviewBytes(attachments) },\n ]);\n if (alreadyVisible) setMessages((prev) => withoutTrailingUserMessage(prev, sendText));\n } else if (!alreadyVisible) {\n setMessages((prev) => [...prev, userMessage]);\n }\n return 'sent';\n }\n\n isSendingRef.current = true;\n followUpRoundRef.current = false;\n ignoreHydrationRef.current = false;\n if (effectiveSessionId && sendText) {\n titlePatchedChannelsRef.current.add(effectiveSessionId);\n patchChannelTitle(effectiveSessionId, sendText, true);\n markChannelHistoryRunning(effectiveSessionId);\n }\n if (!alreadyVisible) {\n // The transcript keeps this turn for the rest of the session, so it\n // keeps the preview URI and not the base64 behind it. The bytes stay\n // on `userMsgForSaveRef` below, which is nulled when the turn ends -\n // a retry still has them, a backgrounded app no longer carries them.\n const retained: ChatMessage = {\n ...userMessage,\n attachments: releasePreviewBytes(userMessage.attachments),\n };\n setMessages((prev) => [...prev, retained]);\n authRetryUsedRef.current = false;\n }\n setResponse('');\n setQuickReply('');\n setIsLoading(true);\n setIsStreaming(true);\n setError(null);\n userMsgForSaveRef.current = userMessage;\n saveSessionIdRef.current = effectiveSessionId ?? null;\n cdecliRoundHandledRef.current = false;\n\n if (!effectiveSessionId) {\n setError('Missing chat session id.');\n if (!alreadyVisible) setMessages((p) => p.slice(0, -1));\n isSendingRef.current = false;\n setIsLoading(false);\n setIsStreaming(false);\n userMsgForSaveRef.current = null;\n saveSessionIdRef.current = null;\n return 'failed';\n }\n\n if (sessionIdOverride) {\n setCdecliStreamOverrideId(sessionIdOverride);\n await new Promise<void>((resolve) => {\n setTimeout(resolve, 0);\n });\n }\n\n const inlineForHostedAgent = shouldInlineGatewayMediaForHostedAgent(\n config.GRAPHQL_URL,\n config.CDECLI_AGENT_ENDPOINT,\n );\n // Vision turns must carry inline bytes. URL-only media is what made\n // \"what is this\" + a photo look like a text-only question.\n const inlineImages = (attachments ?? []).some(isImageAttachment) || inlineForHostedAgent;\n let media = inlineImages\n ? buildGatewayMedia(attachments, { includeData: true })\n : await buildGatewayMediaPreferUrls(\n attachments,\n async (filenames) => {\n const result = await createAccountChatFileUploadLinksMutation({\n variables: { filenames },\n });\n return result.data?.createAccountChatFileUploadLinks || [];\n },\n async (urls) => {\n const result = await createAccountChatFileDownloadLinksMutation({\n variables: { urls },\n });\n return result.data?.createAccountChatFileDownloadLinks || [];\n },\n );\n if ((attachments?.length ?? 0) > 0 && media.length === 0) {\n media = buildGatewayMedia(attachments, { includeData: true });\n }\n const ok = await sendCdecliMessage(sendText, effectiveSessionId, media, turnOptions);\n if (!ok && !cdecliRoundHandledRef.current && !recoveringAuthRef.current) {\n cdecliRoundHandledRef.current = true;\n // The send never reached the agent. Park it rather than drop it - a\n // release keeps its place at the head of its own queue.\n const parked = queuedId\n ? { ok: true as const }\n : queueGatewayMessage({\n channelId: effectiveSessionId,\n accountId: rt.accountId,\n text: sendText,\n attachments,\n });\n setError(\n parked.ok\n ? 'Gateway unreachable - message queued. It sends as soon as the connection is back.'\n : 'Failed to send message via CDeCLI. Check gateway connection.',\n );\n // Queued: the queued row renders the turn, so the optimistic bubble goes.\n // Not queued: only drop a bubble this call put there.\n setMessages((p) => {\n if (parked.ok) return withoutTrailingUserMessage(p, sendText);\n return alreadyVisible ? p : p.slice(0, -1);\n });\n isSendingRef.current = false;\n setIsLoading(false);\n setIsStreaming(false);\n userMsgForSaveRef.current = null;\n saveSessionIdRef.current = null;\n clearChannelHistoryRunning(effectiveSessionId);\n return parked.ok ? 'queued' : 'failed';\n }\n if (ok && queuedId) dropQueuedGatewayMessage(queuedId);\n return ok ? 'sent' : 'failed';\n },\n [\n sessionId,\n sendCdecliMessage,\n patchChannelTitle,\n sendQueuedCdecli,\n setQueue,\n createAccountChatFileUploadLinksMutation,\n createAccountChatFileDownloadLinksMutation,\n ],\n );\n\n sendMessageRef.current = sendMessage;\n\n /* ---------------------------------------------------------------------- *\n * Outbox: turns typed while the gateway was down.\n * ---------------------------------------------------------------------- */\n\n const queuedChannelId = sessionId || cdecliStreamOverrideId || null;\n const queuedSnapshot = useCallback(\n () => queuedGatewayMessages(queuedChannelId, routing.accountId),\n [queuedChannelId, routing.accountId],\n );\n const queuedMessages = useSyncExternalStore(subscribeQueuedGatewayMessages, queuedSnapshot, queuedSnapshot);\n\n useEffect(() => {\n void restoreQueuedGatewayMessages();\n }, []);\n\n /** This chat's saved history, as the flush checks a queued turn against it. */\n const historyRef = useRef({ sessionId, loading: backendMessagesLoading, messages: backendMessages });\n historyRef.current = { sessionId, loading: backendMessagesLoading, messages: backendMessages };\n\n /** One release at a time; a send that fails leaves its entry at the head for the next reconnect. */\n const flushingRef = useRef(false);\n const flushQueuedMessages = useCallback(async () => {\n if (flushingRef.current) return;\n flushingRef.current = true;\n try {\n // Bounded: the outbox can never hand out more than its own depth.\n for (let i = 0; i <= MAX_QUEUED_MESSAGES; i += 1) {\n const rt = routingRef.current;\n const sid = sessionIdRef.current;\n if (!rt.channelConnected || !sid) return;\n const next = queuedGatewayMessages(sid, rt.accountId)[0];\n if (!next) return;\n // Never replay a turn the chat already holds. While its history is still\n // loading, wait: the release runs again once it lands.\n const history = historyRef.current;\n if (history.sessionId !== sid || history.loading) return;\n if (isQueuedMessageDelivered(next, history.messages ?? [])) {\n dropQueuedGatewayMessage(next.id);\n } else {\n const outcome = await sendMessageRef.current(\n next.text,\n next.attachments,\n next.channelId,\n false,\n next.id,\n );\n if (outcome !== 'sent') return;\n }\n }\n } finally {\n flushingRef.current = false;\n }\n }, []);\n\n /**\n * Release on the disconnected→connected edge, and when a channel with a queue\n * is opened on an already-live gateway. Deliberately not keyed on queue depth:\n * a send that fails puts its turn back, and depth would spin that into a loop.\n */\n const wasConnectedRef = useRef(routing.channelConnected);\n useEffect(() => {\n const connected = routing.channelConnected;\n const reconnected = connected && !wasConnectedRef.current;\n wasConnectedRef.current = connected;\n if (!connected || !queuedChannelId) return;\n if (!reconnected && queuedGatewayMessages(queuedChannelId, routing.accountId).length === 0) return;\n void flushQueuedMessages();\n // backendMessagesLoading: a release that waited for the chat's history runs when it lands.\n }, [routing.channelConnected, routing.accountId, queuedChannelId, flushQueuedMessages, backendMessagesLoading]);\n\n const discardQueuedMessages = useCallback(() => {\n if (!queuedChannelId) return;\n clearQueuedGatewayMessages(queuedChannelId);\n setError(null);\n }, [queuedChannelId]);\n\n tryRecoverAndResendRef.current = async (reason: string) => {\n if (authRetryUsedRef.current || cdecliRoundHandledRef.current) return false;\n if (!isAgentJwtRejected(reason)) return false;\n const recover = routingRef.current.recoverAgentAuth;\n if (!recover) return false;\n authRetryUsedRef.current = true;\n recoveringAuthRef.current = true;\n try {\n setError('Refreshing CDeCLI credentials…');\n const recovered = await recover();\n if (!recovered) return false;\n const um = userMsgForSaveRef.current;\n const sid = saveSessionIdRef.current;\n cdecliRoundHandledRef.current = false;\n isSendingRef.current = false;\n setIsLoading(false);\n setIsStreaming(false);\n setError(null);\n if (um && sid) {\n queueMicrotask(() => {\n void sendMessageRef.current(um.content, um.attachments, sid, true);\n });\n }\n return true;\n } catch (err) {\n console.error('[useChatStream] recoverAgentAuth failed:', err);\n return false;\n } finally {\n recoveringAuthRef.current = false;\n }\n };\n\n const cancel = useCallback(() => {\n clearPendingStream();\n const sid = saveSessionIdRef.current ?? sessionId;\n void cancelCdecliTurn(sid ?? 'messenger');\n if (!cdecliRoundHandledRef.current) {\n cdecliRoundHandledRef.current = true;\n finalizeCdecliRound(resolveAssistantReply(''));\n }\n }, [cancelCdecliTurn, finalizeCdecliRound, sessionId, clearPendingStream]);\n\n const clearMessages = useCallback(() => {\n clearPendingStream();\n const hadActiveTurn = isSendingRef.current;\n const sid = saveSessionIdRef.current ?? sessionId;\n isSendingRef.current = false;\n cdecliRoundHandledRef.current = true;\n saveSessionIdRef.current = null;\n userMsgForSaveRef.current = null;\n // Only hit /cancel when a turn was live — otherwise cdecli logs\n // \"Cannot cancel: no active session\" (Research greet + trash).\n if (hadActiveTurn) {\n void cancelCdecliTurn(sid ?? 'messenger');\n }\n ignoreHydrationRef.current = true;\n // Emptying the thread empties its outbox too: a queued turn released after\n // the user cleared the chat would answer a conversation they threw away.\n if (sid) clearQueuedGatewayMessages(sid);\n // ...and what the gateway queued for the agent.\n for (const queued of queuedFollowUpsRef.current) void cancelQueuedCdecli(queued.id, sid ?? 'messenger');\n setQueue([]);\n setMessages([]);\n setResponse('');\n setQuickReply('');\n setError(null);\n setIsLoading(false);\n setIsStreaming(false);\n if (sid) clearChannelHistoryRunning(sid);\n }, [cancelCdecliTurn, cancelQueuedCdecli, clearPendingStream, sessionId, setQueue]);\n\n /** Take one queued message back before the agent gets to it. Too late once\n * the agent started it: it then joins the thread with its answer. */\n const cancelQueuedFollowUp = useCallback(\n async (id: string) => {\n const sid = sessionIdRef.current;\n if (!sid) return;\n const result = await cancelQueuedCdecli(id, sid);\n if (result.ok) {\n setQueue(queuedFollowUpsRef.current.filter((q) => q.id !== id));\n } else {\n setError(result.error || 'Could not cancel that message.');\n }\n },\n [cancelQueuedCdecli, setQueue],\n );\n\n /** Stop the running reply so the agent takes the queued messages now. */\n const sendQueuedNow = useCallback(() => {\n if (isSendingRef.current) cancel();\n }, [cancel]);\n\n const messagesLoading = !!sessionId && backendMessagesLoading && !backendMessagesLoaded;\n\n return {\n messages,\n response,\n quickReply,\n error,\n isLoading,\n isStreaming,\n messagesLoading,\n hasMessages:\n messages.length > 0 ||\n !!response ||\n !!quickReply ||\n queuedMessages.length > 0 ||\n queuedFollowUps.length > 0,\n /** Turns waiting on the gateway, oldest first. Rendered as queued bubbles. */\n queuedMessages,\n /** Send the queue now (the notice's retry), independent of the reconnect edge. */\n flushQueuedMessages,\n discardQueuedMessages,\n /** Sent while the agent was busy and queued for it, oldest first. */\n queuedFollowUps,\n cancelQueuedFollowUp,\n sendQueuedNow,\n sendMessage,\n cancel,\n clearMessages,\n };\n}\n"],"names":["media","_a"],"mappings":"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"}
@@ -10,9 +10,12 @@ function useSurfaceJobs(open) {
10
10
  data,
11
11
  refetch
12
12
  } = useGetSurfaceJobsQuery({
13
+ // No `orgName`: a job is recorded in the request's organization (recordSurfaceJob), and the
14
+ // phone's request organization is not always the workspace it shows - naming that one
15
+ // listed nothing (dev 10/20: three recorded builds, an empty sheet). The name only says the
16
+ // workspace is known, i.e. the person is signed in.
13
17
  variables: {
14
- limit: JOBS_LIMIT,
15
- orgName: orgName || void 0
18
+ limit: JOBS_LIMIT
16
19
  },
17
20
  skip: !orgName || unsupported,
18
21
  fetchPolicy: "cache-and-network",
@@ -1 +1 @@
1
- {"version":3,"file":"useSurfaceJobs.js","sources":["../../src/hooks/useSurfaceJobs.ts"],"sourcesContent":["/**\n * The person's long-running jobs (a Web Builder build, a canvas video, ...) for the\n * Tasks sheet and its badge: the server-owned surface jobs the web chat's Tasks panel\n * reads too (common CENTRAL_TASKS_MODEL), so a job shows wherever it was started and\n * after the app that started it is gone. Polls like the web: often while the sheet is\n * open, rarely while it is closed.\n */\nimport { useCallback, useMemo, useState } from 'react';\nimport { useGetSurfaceJobsQuery, useRemoveSurfaceJobMutation } from 'common/graphql';\nimport {\n centralJobsFrom,\n isJobRunning,\n isSurfaceJobsUnsupported,\n visibleJobs,\n type CentralJob,\n type RawJobTask,\n} from 'common';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\n\nconst POLL_OPEN_MS = 15_000;\nconst POLL_CLOSED_MS = 60_000;\nconst JOBS_LIMIT = 50;\n\nexport function useSurfaceJobs(open: boolean): {\n jobs: CentralJob[];\n runningCount: number;\n dismiss: (id: string) => void;\n refresh: () => void;\n} {\n const { orgName } = usePrerequisiteIds();\n // Once the backend says it has no getSurfaceJobs, stop asking for this session.\n const [unsupported, setUnsupported] = useState(false);\n const { data, refetch } = useGetSurfaceJobsQuery({\n variables: { limit: JOBS_LIMIT, orgName: orgName || undefined },\n skip: !orgName || unsupported,\n fetchPolicy: 'cache-and-network',\n pollInterval: open ? POLL_OPEN_MS : POLL_CLOSED_MS,\n onError: (error) => {\n if (isSurfaceJobsUnsupported(error.message)) setUnsupported(true);\n },\n });\n const [removeJob] = useRemoveSurfaceJobMutation();\n // Dismissed ids stay hidden until the refetch confirms they are gone.\n const [dismissed, setDismissed] = useState<Set<string>>(() => new Set());\n\n const jobs = useMemo(() => {\n const rows = (data?.getSurfaceJobs ?? []) as RawJobTask[];\n return visibleJobs(centralJobsFrom(rows, []).filter((job) => !dismissed.has(job.id)));\n }, [data, dismissed]);\n const runningCount = useMemo(() => jobs.filter(isJobRunning).length, [jobs]);\n\n const refresh = useCallback(() => {\n if (!unsupported) refetch().catch(() => undefined);\n }, [refetch, unsupported]);\n\n const dismiss = useCallback(\n (id: string) => {\n setDismissed((prev) => new Set(prev).add(id));\n removeJob({ variables: { id } })\n .catch((err: unknown) => console.warn('[Tasks] dismiss failed:', err))\n .finally(refresh);\n },\n [removeJob, refresh],\n );\n\n return { jobs, runningCount, dismiss, refresh };\n}\n"],"names":[],"mappings":"iRAWA,MAAM,YAAe,GAAA,IAAA;AACrB,MAAM,cAAiB,GAAA,GAAA;AACvB,MAAM,UAAa,GAAA,EAAA;AACZ,SAAS,eAAe,IAK7B,EAAA;AACA,EAAM,MAAA;AAAA,IACJ;AAAA,MACE,kBAAmB,EAAA;AAEvB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AACpD,EAAM,MAAA;AAAA,IACJ,IAAA;AAAA,IACA;AAAA,MACE,sBAAuB,CAAA;AAAA,IACzB,SAAW,EAAA;AAAA,MACT,KAAO,EAAA,UAAA;AAAA,MACP,SAAS,OAAW,IAAA;AAAA,KACtB;AAAA,IACA,IAAA,EAAM,CAAC,OAAW,IAAA,WAAA;AAAA,IAClB,WAAa,EAAA,mBAAA;AAAA,IACb,YAAA,EAAc,OAAO,YAAe,GAAA,cAAA;AAAA,IACpC,SAAS,CAAS,KAAA,KAAA;AAChB,MAAA,IAAI,wBAAyB,CAAA,KAAA,CAAM,OAAO,CAAA,iBAAkB,IAAI,CAAA;AAAA;AAClE,GACD,CAAA;AACD,EAAM,MAAA,CAAC,SAAS,CAAA,GAAI,2BAA4B,EAAA;AAEhD,EAAM,MAAA,CAAC,WAAW,YAAY,CAAA,GAAI,SAAsB,sBAAM,IAAI,KAAK,CAAA;AACvE,EAAM,MAAA,IAAA,GAAO,QAAQ,MAAM;AA3C7B,IAAA,IAAA,EAAA;AA4CI,IAAA,MAAM,IAAQ,GAAA,CAAA,EAAA,GAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAM,cAAN,KAAA,IAAA,GAAA,EAAA,GAAwB,EAAC;AACvC,IAAA,OAAO,WAAY,CAAA,eAAA,CAAgB,IAAM,EAAA,EAAE,CAAE,CAAA,MAAA,CAAO,CAAO,GAAA,KAAA,CAAC,SAAU,CAAA,GAAA,CAAI,GAAI,CAAA,EAAE,CAAC,CAAC,CAAA;AAAA,GACjF,EAAA,CAAC,IAAM,EAAA,SAAS,CAAC,CAAA;AACpB,EAAM,MAAA,YAAA,GAAe,OAAQ,CAAA,MAAM,IAAK,CAAA,MAAA,CAAO,YAAY,CAAE,CAAA,MAAA,EAAQ,CAAC,IAAI,CAAC,CAAA;AAC3E,EAAM,MAAA,OAAA,GAAU,YAAY,MAAM;AAChC,IAAA,IAAI,CAAC,WAAa,EAAA,OAAA,EAAU,CAAA,KAAA,CAAM,MAAM,MAAS,CAAA;AAAA,GAChD,EAAA,CAAC,OAAS,EAAA,WAAW,CAAC,CAAA;AACzB,EAAM,MAAA,OAAA,GAAU,WAAY,CAAA,CAAC,EAAe,KAAA;AAC1C,IAAA,YAAA,CAAa,UAAQ,IAAI,GAAA,CAAI,IAAI,CAAE,CAAA,GAAA,CAAI,EAAE,CAAC,CAAA;AAC1C,IAAU,SAAA,CAAA;AAAA,MACR,SAAW,EAAA;AAAA,QACT;AAAA;AACF,KACD,CAAA,CAAE,KAAM,CAAA,CAAC,GAAiB,KAAA,OAAA,CAAQ,IAAK,CAAA,yBAAA,EAA2B,GAAG,CAAC,CAAE,CAAA,OAAA,CAAQ,OAAO,CAAA;AAAA,GACvF,EAAA,CAAC,SAAW,EAAA,OAAO,CAAC,CAAA;AACvB,EAAO,OAAA;AAAA,IACL,IAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AACF"}
1
+ {"version":3,"file":"useSurfaceJobs.js","sources":["../../src/hooks/useSurfaceJobs.ts"],"sourcesContent":["/**\n * The person's long-running jobs (a Web Builder build, a canvas video, ...) for the\n * Tasks sheet and its badge: the server-owned surface jobs the web chat's Tasks panel\n * reads too (common CENTRAL_TASKS_MODEL), so a job shows wherever it was started and\n * after the app that started it is gone. Polls like the web: often while the sheet is\n * open, rarely while it is closed.\n */\nimport { useCallback, useMemo, useState } from 'react';\nimport { useGetSurfaceJobsQuery, useRemoveSurfaceJobMutation } from 'common/graphql';\nimport {\n centralJobsFrom,\n isJobRunning,\n isSurfaceJobsUnsupported,\n visibleJobs,\n type CentralJob,\n type RawJobTask,\n} from 'common';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\n\nconst POLL_OPEN_MS = 15_000;\nconst POLL_CLOSED_MS = 60_000;\nconst JOBS_LIMIT = 50;\n\nexport function useSurfaceJobs(open: boolean): {\n jobs: CentralJob[];\n runningCount: number;\n dismiss: (id: string) => void;\n refresh: () => void;\n} {\n const { orgName } = usePrerequisiteIds();\n // Once the backend says it has no getSurfaceJobs, stop asking for this session.\n const [unsupported, setUnsupported] = useState(false);\n const { data, refetch } = useGetSurfaceJobsQuery({\n // No `orgName`: a job is recorded in the request's organization (recordSurfaceJob), and the\n // phone's request organization is not always the workspace it shows - naming that one\n // listed nothing (dev 10/20: three recorded builds, an empty sheet). The name only says the\n // workspace is known, i.e. the person is signed in.\n variables: { limit: JOBS_LIMIT },\n skip: !orgName || unsupported,\n fetchPolicy: 'cache-and-network',\n pollInterval: open ? POLL_OPEN_MS : POLL_CLOSED_MS,\n onError: (error) => {\n if (isSurfaceJobsUnsupported(error.message)) setUnsupported(true);\n },\n });\n const [removeJob] = useRemoveSurfaceJobMutation();\n // Dismissed ids stay hidden until the refetch confirms they are gone.\n const [dismissed, setDismissed] = useState<Set<string>>(() => new Set());\n\n const jobs = useMemo(() => {\n const rows = (data?.getSurfaceJobs ?? []) as RawJobTask[];\n return visibleJobs(centralJobsFrom(rows, []).filter((job) => !dismissed.has(job.id)));\n }, [data, dismissed]);\n const runningCount = useMemo(() => jobs.filter(isJobRunning).length, [jobs]);\n\n const refresh = useCallback(() => {\n if (!unsupported) refetch().catch(() => undefined);\n }, [refetch, unsupported]);\n\n const dismiss = useCallback(\n (id: string) => {\n setDismissed((prev) => new Set(prev).add(id));\n removeJob({ variables: { id } })\n .catch((err: unknown) => console.warn('[Tasks] dismiss failed:', err))\n .finally(refresh);\n },\n [removeJob, refresh],\n );\n\n return { jobs, runningCount, dismiss, refresh };\n}\n"],"names":[],"mappings":"iRAWA,MAAM,YAAe,GAAA,IAAA;AACrB,MAAM,cAAiB,GAAA,GAAA;AACvB,MAAM,UAAa,GAAA,EAAA;AACZ,SAAS,eAAe,IAK7B,EAAA;AACA,EAAM,MAAA;AAAA,IACJ;AAAA,MACE,kBAAmB,EAAA;AAEvB,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA;AACpD,EAAM,MAAA;AAAA,IACJ,IAAA;AAAA,IACA;AAAA,MACE,sBAAuB,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAKzB,SAAW,EAAA;AAAA,MACT,KAAO,EAAA;AAAA,KACT;AAAA,IACA,IAAA,EAAM,CAAC,OAAW,IAAA,WAAA;AAAA,IAClB,WAAa,EAAA,mBAAA;AAAA,IACb,YAAA,EAAc,OAAO,YAAe,GAAA,cAAA;AAAA,IACpC,SAAS,CAAS,KAAA,KAAA;AAChB,MAAA,IAAI,wBAAyB,CAAA,KAAA,CAAM,OAAO,CAAA,iBAAkB,IAAI,CAAA;AAAA;AAClE,GACD,CAAA;AACD,EAAM,MAAA,CAAC,SAAS,CAAA,GAAI,2BAA4B,EAAA;AAEhD,EAAM,MAAA,CAAC,WAAW,YAAY,CAAA,GAAI,SAAsB,sBAAM,IAAI,KAAK,CAAA;AACvE,EAAM,MAAA,IAAA,GAAO,QAAQ,MAAM;AA9C7B,IAAA,IAAA,EAAA;AA+CI,IAAA,MAAM,IAAQ,GAAA,CAAA,EAAA,GAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAM,cAAN,KAAA,IAAA,GAAA,EAAA,GAAwB,EAAC;AACvC,IAAA,OAAO,WAAY,CAAA,eAAA,CAAgB,IAAM,EAAA,EAAE,CAAE,CAAA,MAAA,CAAO,CAAO,GAAA,KAAA,CAAC,SAAU,CAAA,GAAA,CAAI,GAAI,CAAA,EAAE,CAAC,CAAC,CAAA;AAAA,GACjF,EAAA,CAAC,IAAM,EAAA,SAAS,CAAC,CAAA;AACpB,EAAM,MAAA,YAAA,GAAe,OAAQ,CAAA,MAAM,IAAK,CAAA,MAAA,CAAO,YAAY,CAAE,CAAA,MAAA,EAAQ,CAAC,IAAI,CAAC,CAAA;AAC3E,EAAM,MAAA,OAAA,GAAU,YAAY,MAAM;AAChC,IAAA,IAAI,CAAC,WAAa,EAAA,OAAA,EAAU,CAAA,KAAA,CAAM,MAAM,MAAS,CAAA;AAAA,GAChD,EAAA,CAAC,OAAS,EAAA,WAAW,CAAC,CAAA;AACzB,EAAM,MAAA,OAAA,GAAU,WAAY,CAAA,CAAC,EAAe,KAAA;AAC1C,IAAA,YAAA,CAAa,UAAQ,IAAI,GAAA,CAAI,IAAI,CAAE,CAAA,GAAA,CAAI,EAAE,CAAC,CAAA;AAC1C,IAAU,SAAA,CAAA;AAAA,MACR,SAAW,EAAA;AAAA,QACT;AAAA;AACF,KACD,CAAA,CAAE,KAAM,CAAA,CAAC,GAAiB,KAAA,OAAA,CAAQ,IAAK,CAAA,yBAAA,EAA2B,GAAG,CAAC,CAAE,CAAA,OAAA,CAAQ,OAAO,CAAA;AAAA,GACvF,EAAA,CAAC,SAAW,EAAA,OAAO,CAAC,CAAA;AACvB,EAAO,OAAA;AAAA,IACL,IAAA;AAAA,IACA,YAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF;AACF"}
@@ -1,4 +1,4 @@
1
- import {config}from'../config/env-config.js';var __defProp = Object.defineProperty;
1
+ import {config}from'../config/env-config.js';import {isJwtExpired}from'../hooks/systemTokenJwt.js';var __defProp = Object.defineProperty;
2
2
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
3
  var __hasOwnProp = Object.prototype.hasOwnProperty;
4
4
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -28,6 +28,48 @@ var __objRest = (source, exclude) => {
28
28
  };
29
29
  const BRAIN_PATH = "/resources/yantra-brain-classify";
30
30
  const DEFAULT_TIMEOUT_MS = 15e3;
31
+ let tokenProvider = null;
32
+ let cachedToken = null;
33
+ let pendingToken = null;
34
+ function registerBrainTokenProvider(provider) {
35
+ tokenProvider = provider;
36
+ cachedToken = null;
37
+ pendingToken = null;
38
+ return () => {
39
+ if (tokenProvider !== provider) return;
40
+ tokenProvider = null;
41
+ cachedToken = null;
42
+ pendingToken = null;
43
+ };
44
+ }
45
+ async function brainToken() {
46
+ if (cachedToken && !isJwtExpired(cachedToken)) return cachedToken;
47
+ cachedToken = null;
48
+ const provider = tokenProvider;
49
+ if (!provider) return null;
50
+ if (!pendingToken) {
51
+ const request = provider().then((token) => {
52
+ const trimmed = (token == null ? void 0 : token.trim()) || null;
53
+ if (tokenProvider === provider && pendingToken === request) cachedToken = trimmed;
54
+ return trimmed;
55
+ }).catch(() => null).finally(() => {
56
+ if (pendingToken === request) pendingToken = null;
57
+ });
58
+ pendingToken = request;
59
+ }
60
+ return pendingToken;
61
+ }
62
+ async function brainHeaders() {
63
+ const token = await brainToken();
64
+ return __spreadValues({
65
+ "Content-Type": "application/json"
66
+ }, token ? {
67
+ Authorization: `Bearer ${token}`
68
+ } : {});
69
+ }
70
+ function noteRefusal(res) {
71
+ if (res.status === 401) cachedToken = null;
72
+ }
31
73
  function brainEndpoint() {
32
74
  const base = (config.CLIENT_URL || "").trim().replace(/\/+$/, "");
33
75
  return base ? `${base}${BRAIN_PATH}` : "";
@@ -48,15 +90,14 @@ async function quickReplyViaBrain(text, opts = {}) {
48
90
  try {
49
91
  const res = await fetch(endpoint, {
50
92
  method: "POST",
51
- headers: {
52
- "Content-Type": "application/json"
53
- },
93
+ headers: await brainHeaders(),
54
94
  body: JSON.stringify({
55
95
  text: trimmed.slice(0, 4e3),
56
96
  task: "assist"
57
97
  }),
58
98
  signal: controller.signal
59
99
  });
100
+ noteRefusal(res);
60
101
  if (!res.ok) return null;
61
102
  const data = await res.json();
62
103
  const answer = typeof (data == null ? void 0 : data.answer) === "string" ? data.answer.trim() : "";
@@ -83,14 +124,13 @@ async function postBrainJson(text, extra, timeoutMs, signal) {
83
124
  try {
84
125
  const res = await fetch(endpoint, {
85
126
  method: "POST",
86
- headers: {
87
- "Content-Type": "application/json"
88
- },
127
+ headers: await brainHeaders(),
89
128
  body: JSON.stringify(__spreadValues({
90
129
  text: trimmed.slice(0, 4e3)
91
130
  }, extra)),
92
131
  signal: controller.signal
93
132
  });
133
+ noteRefusal(res);
94
134
  if (!res.ok) return null;
95
135
  return await res.json();
96
136
  } catch (e) {
@@ -177,9 +217,7 @@ async function brainCompleteViaBrain(messages, opts = {}) {
177
217
  try {
178
218
  const res = await fetch(endpoint, {
179
219
  method: "POST",
180
- headers: {
181
- "Content-Type": "application/json"
182
- },
220
+ headers: await brainHeaders(),
183
221
  body: JSON.stringify({
184
222
  messages,
185
223
  model: "yantra-brain/fast",
@@ -187,6 +225,7 @@ async function brainCompleteViaBrain(messages, opts = {}) {
187
225
  }),
188
226
  signal: controller.signal
189
227
  });
228
+ noteRefusal(res);
190
229
  if (!res.ok) return null;
191
230
  const data = await res.json();
192
231
  const content = typeof (data == null ? void 0 : data.content) === "string" ? data.content.trim() : "";
@@ -196,4 +235,4 @@ async function brainCompleteViaBrain(messages, opts = {}) {
196
235
  } finally {
197
236
  clearTimeout(timeout);
198
237
  }
199
- }export{brainCompleteViaBrain,brainEndpoint,classifyIntentViaBrain,classifyShoppingViaBrain,parseShoppingViaBrain,quickReplyViaBrain};//# sourceMappingURL=brainQuickReply.js.map
238
+ }export{brainCompleteViaBrain,brainEndpoint,classifyIntentViaBrain,classifyShoppingViaBrain,parseShoppingViaBrain,quickReplyViaBrain,registerBrainTokenProvider};//# sourceMappingURL=brainQuickReply.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"brainQuickReply.js","sources":["../../src/services/brainQuickReply.ts"],"sourcesContent":["/**\n * brainQuickReply - a fast first answer from `yantra-brain` while the real agent works.\n *\n * On mobile the first turn of a session is the slow one: the agent session has to\n * bootstrap (sandbox, connectors, memory) before a single delta arrives, and on a\n * cold pod that can run to minutes. The web client only reaches for the brain after\n * the full 300s stall budget expires; mobile did not reach for it at all, so a user\n * saw nothing, then an error. This asks the brain the SAME question in parallel and\n * paints its answer as a provisional reply the moment it lands. When the agent's\n * stream starts, that provisional text is replaced in place - the brain fills the\n * silence, it never wins over the real answer.\n *\n * The brain proxy is the web app's same-origin Remix resource route\n * (`/resources/yantra-brain-classify`, `task: 'assist'`). Mobile has no same\n * origin, so it targets the web app's public URL (CLIENT_URL) explicitly. The\n * route holds the model, prompt and secret server-side - the phone still never\n * sees a provider key.\n *\n * Best-effort and never throws: null on timeout, network error or empty answer.\n */\nimport { config } from '../config/env-config';\n\nconst BRAIN_PATH = '/resources/yantra-brain-classify';\n\n// Server-side upstream cap is ASSIST_TIMEOUT_MS (12s); give the phone headroom\n// past it so a slow-but-successful answer is not aborted at the same instant.\nconst DEFAULT_TIMEOUT_MS = 15000;\n\n/** The web app origin that serves the brain proxy, or '' when unconfigured. */\nexport function brainEndpoint(): string {\n const base = (config.CLIENT_URL || '').trim().replace(/\\/+$/, '');\n return base ? `${base}${BRAIN_PATH}` : '';\n}\n\nexport async function quickReplyViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<string | null> {\n const trimmed = (text || '').trim();\n const endpoint = brainEndpoint();\n if (!trimmed || !endpoint) return null;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n if (opts.signal) {\n if (opts.signal.aborted) controller.abort();\n else opts.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text: trimmed.slice(0, 4000), task: 'assist' }),\n signal: controller.signal,\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { answer?: string | null };\n const answer = typeof data?.answer === 'string' ? data.answer.trim() : '';\n return answer || null;\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nexport interface BrainIntent {\n intent: string;\n confidence?: number;\n}\n\nconst CLASSIFY_TIMEOUT_MS = 2000;\n\nasync function postBrainJson(\n text: string,\n extra: Record<string, unknown>,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<Record<string, unknown> | null> {\n const trimmed = (text || '').trim();\n const endpoint = brainEndpoint();\n if (!trimmed || !endpoint) return null;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n if (signal) {\n if (signal.aborted) controller.abort();\n else signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ text: trimmed.slice(0, 4000), ...extra }),\n signal: controller.signal,\n });\n if (!res.ok) return null;\n return (await res.json()) as Record<string, unknown>;\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/** cdecli yantra-brain classify — same default task as web intentRouter. */\nexport async function classifyIntentViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<BrainIntent | null> {\n const data = await postBrainJson(text, {}, opts.timeoutMs ?? CLASSIFY_TIMEOUT_MS, opts.signal);\n const intent = typeof data?.intent === 'string' ? data.intent.trim().toLowerCase() : '';\n if (!intent) return null;\n return {\n intent,\n confidence: typeof data?.confidence === 'number' ? data.confidence : undefined,\n };\n}\n\n/** cdecli shopping parse — `task: 'shopping'`. */\nexport type ShoppingBrainVerdict =\n | { status: 'unreachable' }\n | { status: 'declined' }\n | {\n status: 'ok';\n query: string;\n productType?: string;\n synonyms?: string[];\n excludes?: string[];\n monitor?: 'watch' | 'stop';\n targetPrice?: number;\n budget?: number;\n departmentId?: string;\n };\n\nexport async function classifyShoppingViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<ShoppingBrainVerdict> {\n const data = await postBrainJson(text, { task: 'shopping' }, opts.timeoutMs ?? CLASSIFY_TIMEOUT_MS, opts.signal);\n if (!data) return { status: 'unreachable' };\n if (data.intent === null || data.intent === undefined) return { status: 'declined' };\n const payload = data.intent;\n if (!payload || typeof payload !== 'object') return { status: 'declined' };\n const row = payload as Record<string, unknown>;\n const query = typeof row.query === 'string' ? row.query.trim() : '';\n const monitorRaw = typeof row.monitor === 'string' ? row.monitor.trim().toLowerCase() : '';\n const monitor = monitorRaw === 'watch' || monitorRaw === 'stop' ? monitorRaw : undefined;\n if (!query && !monitor) return { status: 'declined' };\n return {\n status: 'ok',\n query,\n ...(typeof row.productType === 'string' && row.productType.trim()\n ? { productType: row.productType.trim().toLowerCase() }\n : {}),\n ...(Array.isArray(row.synonyms)\n ? {\n synonyms: row.synonyms\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim().toLowerCase())\n .filter(Boolean),\n }\n : {}),\n ...(Array.isArray(row.excludes)\n ? {\n excludes: row.excludes\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim().toLowerCase())\n .filter(Boolean),\n }\n : {}),\n ...(monitor ? { monitor } : {}),\n ...(typeof row.targetPrice === 'number' && row.targetPrice > 0 ? { targetPrice: row.targetPrice } : {}),\n ...(typeof row.budget === 'number' && row.budget > 0 ? { budget: Math.round(row.budget) } : {}),\n ...(typeof row.departmentId === 'string' && row.departmentId.trim()\n ? { departmentId: row.departmentId.trim().toLowerCase() }\n : {}),\n };\n}\n\nexport async function parseShoppingViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<{\n query: string;\n productType?: string;\n synonyms?: string[];\n excludes?: string[];\n monitor?: 'watch' | 'stop';\n targetPrice?: number;\n budget?: number;\n departmentId?: string;\n} | null> {\n const verdict = await classifyShoppingViaBrain(text, opts);\n if (verdict.status !== 'ok') return null;\n // Destructured only to leave `status` out of what is returned.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { status: _status, ...rest } = verdict;\n return rest;\n}\n\nconst BRAIN_COMPLETE_PATH = '/resources/yantra-brain-complete';\n\n/** Keyless chat completion via the same web origin as the classify proxy. */\nexport async function brainCompleteViaBrain(\n messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>,\n opts: { maxTokens?: number; timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<string | null> {\n const base = (config.CLIENT_URL || '').trim().replace(/\\/+$/, '');\n const endpoint = base ? `${base}${BRAIN_COMPLETE_PATH}` : '';\n if (!endpoint || messages.length === 0) return null;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 15000);\n if (opts.signal) {\n if (opts.signal.aborted) controller.abort();\n else opts.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n messages,\n model: 'yantra-brain/fast',\n max_tokens: opts.maxTokens ?? 220,\n }),\n signal: controller.signal,\n });\n if (!res.ok) return null;\n const data = (await res.json()) as { content?: string | null };\n const content = typeof data?.content === 'string' ? data.content.trim() : '';\n return content || null;\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"brainQuickReply.js","sources":["../../src/services/brainQuickReply.ts"],"sourcesContent":["/**\n * brainQuickReply - a fast first answer from `yantra-brain` while the real agent works.\n *\n * On mobile the first turn of a session is the slow one: the agent session has to\n * bootstrap (sandbox, connectors, memory) before a single delta arrives, and on a\n * cold pod that can run to minutes. The web client only reaches for the brain after\n * the full 300s stall budget expires; mobile did not reach for it at all, so a user\n * saw nothing, then an error. This asks the brain the SAME question in parallel and\n * paints its answer as a provisional reply the moment it lands. When the agent's\n * stream starts, that provisional text is replaced in place - the brain fills the\n * silence, it never wins over the real answer.\n *\n * The brain proxy is the web app's same-origin Remix resource route\n * (`/resources/yantra-brain-classify`, `task: 'assist'`). Mobile has no same\n * origin, so it targets the web app's public URL (CLIENT_URL) explicitly. The\n * route holds the model and prompt server-side - the phone still never sees a\n * provider key.\n *\n * Every call carries the signed-in user's own system JWT (the token the chat\n * connection hands the agent - see {@link registerBrainTokenProvider}): the route\n * forwards it, and cdecli (2.20.22+) runs no model for an anonymous caller. With\n * no token the route answers 401, read here like any other non-answer.\n *\n * Best-effort and never throws: null on timeout, network error or empty answer.\n */\nimport { config } from '../config/env-config';\nimport { isJwtExpired } from '../hooks/systemTokenJwt';\n\nconst BRAIN_PATH = '/resources/yantra-brain-classify';\n\n// Server-side upstream cap is ASSIST_TIMEOUT_MS (12s); give the phone headroom\n// past it so a slow-but-successful answer is not aborted at the same instant.\nconst DEFAULT_TIMEOUT_MS = 15000;\n\n// ─── Caller token ────────────────────────────────────────────────────────────\n\ntype BrainTokenProvider = () => Promise<string | null>;\n\nlet tokenProvider: BrainTokenProvider | null = null;\nlet cachedToken: string | null = null;\nlet pendingToken: Promise<string | null> | null = null;\n\n/**\n * Wire the source of the signed-in user's token. The app-wide CDeCLI connection\n * registers its `obtainAgentToken` (the same token it connects the chat with), so\n * these module-level helpers can call the brain as that user. Registering again\n * (another account) drops the cached token. Returns the unregister function.\n */\nexport function registerBrainTokenProvider(provider: BrainTokenProvider): () => void {\n tokenProvider = provider;\n cachedToken = null;\n pendingToken = null;\n return () => {\n if (tokenProvider !== provider) return;\n tokenProvider = null;\n cachedToken = null;\n pendingToken = null;\n };\n}\n\n/** The token is obtained once (a reveal round trip) and reused until it expires or is refused. */\nasync function brainToken(): Promise<string | null> {\n if (cachedToken && !isJwtExpired(cachedToken)) return cachedToken;\n cachedToken = null;\n const provider = tokenProvider;\n if (!provider) return null;\n if (!pendingToken) {\n const request = provider()\n .then((token) => {\n const trimmed = token?.trim() || null;\n if (tokenProvider === provider && pendingToken === request) cachedToken = trimmed;\n return trimmed;\n })\n .catch(() => null)\n .finally(() => {\n if (pendingToken === request) pendingToken = null;\n });\n pendingToken = request;\n }\n return pendingToken;\n}\n\n/** The headers every brain call sends: JSON plus the signed-in user's bearer, when there is one. */\nasync function brainHeaders(): Promise<Record<string, string>> {\n const token = await brainToken();\n return { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}) };\n}\n\n/** A 401 means the agent refused the token: drop it so the next call obtains a fresh one. */\nfunction noteRefusal(res: { status?: number }): void {\n if (res.status === 401) cachedToken = null;\n}\n\n/** The web app origin that serves the brain proxy, or '' when unconfigured. */\nexport function brainEndpoint(): string {\n const base = (config.CLIENT_URL || '').trim().replace(/\\/+$/, '');\n return base ? `${base}${BRAIN_PATH}` : '';\n}\n\nexport async function quickReplyViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<string | null> {\n const trimmed = (text || '').trim();\n const endpoint = brainEndpoint();\n if (!trimmed || !endpoint) return null;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n if (opts.signal) {\n if (opts.signal.aborted) controller.abort();\n else opts.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: await brainHeaders(),\n body: JSON.stringify({ text: trimmed.slice(0, 4000), task: 'assist' }),\n signal: controller.signal,\n });\n noteRefusal(res);\n if (!res.ok) return null;\n const data = (await res.json()) as { answer?: string | null };\n const answer = typeof data?.answer === 'string' ? data.answer.trim() : '';\n return answer || null;\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n\nexport interface BrainIntent {\n intent: string;\n confidence?: number;\n}\n\nconst CLASSIFY_TIMEOUT_MS = 2000;\n\nasync function postBrainJson(\n text: string,\n extra: Record<string, unknown>,\n timeoutMs: number,\n signal?: AbortSignal,\n): Promise<Record<string, unknown> | null> {\n const trimmed = (text || '').trim();\n const endpoint = brainEndpoint();\n if (!trimmed || !endpoint) return null;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), timeoutMs);\n if (signal) {\n if (signal.aborted) controller.abort();\n else signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: await brainHeaders(),\n body: JSON.stringify({ text: trimmed.slice(0, 4000), ...extra }),\n signal: controller.signal,\n });\n noteRefusal(res);\n if (!res.ok) return null;\n return (await res.json()) as Record<string, unknown>;\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n\n/** cdecli yantra-brain classify — same default task as web intentRouter. */\nexport async function classifyIntentViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<BrainIntent | null> {\n const data = await postBrainJson(text, {}, opts.timeoutMs ?? CLASSIFY_TIMEOUT_MS, opts.signal);\n const intent = typeof data?.intent === 'string' ? data.intent.trim().toLowerCase() : '';\n if (!intent) return null;\n return {\n intent,\n confidence: typeof data?.confidence === 'number' ? data.confidence : undefined,\n };\n}\n\n/** cdecli shopping parse — `task: 'shopping'`. */\nexport type ShoppingBrainVerdict =\n | { status: 'unreachable' }\n | { status: 'declined' }\n | {\n status: 'ok';\n query: string;\n productType?: string;\n synonyms?: string[];\n excludes?: string[];\n monitor?: 'watch' | 'stop';\n targetPrice?: number;\n budget?: number;\n departmentId?: string;\n };\n\nexport async function classifyShoppingViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<ShoppingBrainVerdict> {\n const data = await postBrainJson(text, { task: 'shopping' }, opts.timeoutMs ?? CLASSIFY_TIMEOUT_MS, opts.signal);\n if (!data) return { status: 'unreachable' };\n if (data.intent === null || data.intent === undefined) return { status: 'declined' };\n const payload = data.intent;\n if (!payload || typeof payload !== 'object') return { status: 'declined' };\n const row = payload as Record<string, unknown>;\n const query = typeof row.query === 'string' ? row.query.trim() : '';\n const monitorRaw = typeof row.monitor === 'string' ? row.monitor.trim().toLowerCase() : '';\n const monitor = monitorRaw === 'watch' || monitorRaw === 'stop' ? monitorRaw : undefined;\n if (!query && !monitor) return { status: 'declined' };\n return {\n status: 'ok',\n query,\n ...(typeof row.productType === 'string' && row.productType.trim()\n ? { productType: row.productType.trim().toLowerCase() }\n : {}),\n ...(Array.isArray(row.synonyms)\n ? {\n synonyms: row.synonyms\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim().toLowerCase())\n .filter(Boolean),\n }\n : {}),\n ...(Array.isArray(row.excludes)\n ? {\n excludes: row.excludes\n .filter((s): s is string => typeof s === 'string')\n .map((s) => s.trim().toLowerCase())\n .filter(Boolean),\n }\n : {}),\n ...(monitor ? { monitor } : {}),\n ...(typeof row.targetPrice === 'number' && row.targetPrice > 0 ? { targetPrice: row.targetPrice } : {}),\n ...(typeof row.budget === 'number' && row.budget > 0 ? { budget: Math.round(row.budget) } : {}),\n ...(typeof row.departmentId === 'string' && row.departmentId.trim()\n ? { departmentId: row.departmentId.trim().toLowerCase() }\n : {}),\n };\n}\n\nexport async function parseShoppingViaBrain(\n text: string,\n opts: { timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<{\n query: string;\n productType?: string;\n synonyms?: string[];\n excludes?: string[];\n monitor?: 'watch' | 'stop';\n targetPrice?: number;\n budget?: number;\n departmentId?: string;\n} | null> {\n const verdict = await classifyShoppingViaBrain(text, opts);\n if (verdict.status !== 'ok') return null;\n // Destructured only to leave `status` out of what is returned.\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const { status: _status, ...rest } = verdict;\n return rest;\n}\n\nconst BRAIN_COMPLETE_PATH = '/resources/yantra-brain-complete';\n\n/** Keyless chat completion via the same web origin as the classify proxy. */\nexport async function brainCompleteViaBrain(\n messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>,\n opts: { maxTokens?: number; timeoutMs?: number; signal?: AbortSignal } = {},\n): Promise<string | null> {\n const base = (config.CLIENT_URL || '').trim().replace(/\\/+$/, '');\n const endpoint = base ? `${base}${BRAIN_COMPLETE_PATH}` : '';\n if (!endpoint || messages.length === 0) return null;\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), opts.timeoutMs ?? 15000);\n if (opts.signal) {\n if (opts.signal.aborted) controller.abort();\n else opts.signal.addEventListener('abort', () => controller.abort(), { once: true });\n }\n\n try {\n const res = await fetch(endpoint, {\n method: 'POST',\n headers: await brainHeaders(),\n body: JSON.stringify({\n messages,\n model: 'yantra-brain/fast',\n max_tokens: opts.maxTokens ?? 220,\n }),\n signal: controller.signal,\n });\n noteRefusal(res);\n if (!res.ok) return null;\n const data = (await res.json()) as { content?: string | null };\n const content = typeof data?.content === 'string' ? data.content.trim() : '';\n return content || null;\n } catch {\n return null;\n } finally {\n clearTimeout(timeout);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -129,6 +129,17 @@ function queueGatewayMessage(entry) {
129
129
  depth: depth + 1
130
130
  };
131
131
  }
132
+ const DELIVERED_SKEW_MS = 2 * 60 * 1e3;
133
+ function isQueuedMessageDelivered(entry, thread) {
134
+ const text = entry.text.trim();
135
+ if (!text) return false;
136
+ return thread.some((message) => {
137
+ var _a2;
138
+ if (message.role !== "user" || ((_a2 = message.content) != null ? _a2 : "").trim() !== text) return false;
139
+ const at = message.createdAt instanceof Date ? message.createdAt.getTime() : Date.parse(String(message.createdAt));
140
+ return Number.isFinite(at) && at >= entry.queuedAt - DELIVERED_SKEW_MS;
141
+ });
142
+ }
132
143
  function dropQueuedGatewayMessage(id) {
133
144
  const next = queue.filter((entry) => entry.id !== id);
134
145
  if (next.length === queue.length) return;
@@ -142,4 +153,4 @@ function clearQueuedGatewayMessages(channelId) {
142
153
  queue = next;
143
154
  persist();
144
155
  changed();
145
- }export{MAX_QUEUED_MESSAGES,clearQueuedGatewayMessages,dropQueuedGatewayMessage,queueGatewayMessage,queuedGatewayMessageCount,queuedGatewayMessages,restoreQueuedGatewayMessages,subscribeQueuedGatewayMessages};//# sourceMappingURL=queuedGatewayMessages.js.map
156
+ }export{MAX_QUEUED_MESSAGES,clearQueuedGatewayMessages,dropQueuedGatewayMessage,isQueuedMessageDelivered,queueGatewayMessage,queuedGatewayMessageCount,queuedGatewayMessages,restoreQueuedGatewayMessages,subscribeQueuedGatewayMessages};//# sourceMappingURL=queuedGatewayMessages.js.map