@adminide-stack/yantra-mobile 12.0.53-alpha.0 → 12.0.53-alpha.11
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.
- package/lib/components/KeyboardComposerDock.js +41 -5
- package/lib/components/KeyboardComposerDock.js.map +1 -1
- package/lib/features/attachments/ComposerAttachMenu.js +189 -0
- package/lib/features/attachments/ComposerAttachMenu.js.map +1 -0
- package/lib/features/attachments/useImageAttachments.js +23 -15
- package/lib/features/attachments/useImageAttachments.js.map +1 -1
- package/lib/features/canvas/canvasBoardsSnapshot.js +45 -0
- package/lib/features/canvas/canvasBoardsSnapshot.js.map +1 -0
- package/lib/features/canvas/nativeViewerRegistry.js +9 -1
- package/lib/features/canvas/nativeViewerRegistry.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +7 -0
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/chat/chatHistorySnapshot.js +103 -0
- package/lib/features/chat/chatHistorySnapshot.js.map +1 -0
- package/lib/graphql/typePolicies.js +139 -0
- package/lib/graphql/typePolicies.js.map +1 -0
- package/lib/hooks/useCdecliChannel.js +63 -7
- package/lib/hooks/useCdecliChannel.js.map +1 -1
- package/lib/hooks/useChatApi.js +232 -26
- package/lib/hooks/useChatApi.js.map +1 -1
- package/lib/hooks/useChatStream.js +105 -36
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/module.js +5 -2
- package/lib/module.js.map +1 -1
- package/lib/screens/Chat/index.js +8 -38
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/HomeScreen.js +18 -31
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/Home/components/CanvasPrewarm.js +3 -2
- package/lib/screens/Home/components/CanvasPrewarm.js.map +1 -1
- package/lib/screens/Home/components/ChatHistoryLanding.js +60 -12
- package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
- package/lib/state/chatThreadMerge.js +76 -2
- package/lib/state/chatThreadMerge.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCdecliChannel.js","sources":["../../src/hooks/useCdecliChannel.ts"],"sourcesContent":["/**\n * useCdecliChannel — wires the cdecli-serve messenger-gateway channel into the mobile chat UI.\n *\n * Kept in sync with `packages-modules/account/browser/src/hooks/useCdecliChannel.ts`.\n * When the CDeCLI channel is connected:\n * - `sendMessage(text, chatId, media)` calls `gatewaySendMessage`\n * - `MessengerStreamDelta` subscription delivers streaming chunks via `onChunk`\n * - `GatewayInboundMessageByChannel` delivers the final reply via `onComplete`\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { quickReplyViaBrain } from '../services/brainQuickReply';\nimport {\n useGatewaySendMessageMutation,\n useGatewayInboundMessageByChannelSubscription,\n useMessengerStreamDeltaSubscription,\n} from 'common/graphql';\n\nfunction stripModelCostHeader(content: string): string {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n return normalized.replace(\n /^\\s*(?:[^\\w\\n]+\\s*)?[a-z0-9][a-z0-9._-]*\\s*\\(\\s*\\$[\\d.]+\\s*\\/\\s*MTok\\s+in\\s*\\)\\s*\\n+/i,\n '',\n );\n}\n\n// `error?: undefined` on the success arm keeps `result.error` reachable on the\n// union: with strictNullChecks off (repo-wide), TS will not narrow the\n// discriminated union after an `if (result.ok) continue`.\nexport type SteerResult = { ok: true; error?: undefined } | { ok: false; error: string };\n\nexport interface CdecliChannelCallbacks {\n onChunk: (text: string) => void;\n /**\n * A fast provisional answer from `yantra-brain`, painted while the real agent\n * is still bootstrapping. Distinct from onChunk on purpose: the UI shows it as\n * the reply-so-far and REPLACES it the moment real deltas arrive, so the brain\n * fills the silence without ever stacking on top of the agent's answer.\n */\n onQuickReply?: (text: string) => void;\n onComplete: (text: string) => void;\n onError: (error: string) => void;\n}\n\nexport function isNoActiveSessionError(message: string): boolean {\n return /no active session/i.test(message);\n}\n\n/**\n * Idle window, not a total budget. The timer is re-armed on every streamed\n * delta (a turn that is actively streaming is not stuck), and paused while the\n * app is backgrounded (iOS suspends the WebSocket + throttles JS timers, so a\n * wall-clock timer would otherwise fire a false \"did not respond\" the instant\n * the app returns to the foreground). It fires only after this much CONTINUOUS\n * silence with the app in the foreground.\n */\nconst CDECLI_RESPONSE_TIMEOUT_MS = 300_000;\n\nexport function useCdecliChannel(\n isConnected: boolean,\n accountId: string,\n channelId: string | undefined,\n callbacks: CdecliChannelCallbacks,\n model?: string,\n skill?: string,\n) {\n const [sendMutation] = useGatewaySendMessageMutation();\n const cbRef = useRef(callbacks);\n cbRef.current = callbacks;\n\n const pendingRef = useRef<{ text: string; sentAt: number } | null>(null);\n const hasReceivedDeltasRef = useRef(false);\n const reconnectedStreamRef = useRef(false);\n const streamCompletedRef = useRef(false);\n const accumulatedLenRef = useRef(0);\n // True while a brain quick-reply is what the user is looking at. The first\n // real delta clears the provisional text before painting, so the agent's\n // answer replaces the brain's rather than appending to it.\n const quickReplyShownRef = useRef(false);\n // Identity of the current turn for the brain race. A counter, not a\n // timestamp: two sends can share a millisecond, and a timestamp would then\n // let a brain answer for a superseded turn paint over the live one.\n const turnSeqRef = useRef(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const streamSkip = !channelId;\n const streamChannelId = channelId || accountId;\n\n /** (Re)start the idle timer for the in-flight turn. No-op if none pending. */\n const armTimeout = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n if (!pendingRef.current) {\n timeoutRef.current = null;\n return;\n }\n timeoutRef.current = setTimeout(() => {\n if (pendingRef.current) {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n timeoutRef.current = null;\n cbRef.current.onError(\n 'CDeCLI agent did not respond within 300 seconds. The query may still be processing.',\n );\n }\n }, CDECLI_RESPONSE_TIMEOUT_MS);\n }, []);\n\n // Pause the idle timer in the background, re-arm fresh on return. iOS\n // suspends the streaming WebSocket and throttles JS timers while\n // backgrounded, so a running wall-clock timer either fires against a\n // connection that cannot deliver or fires the instant the app resumes -\n // both read to the user as a spurious timeout. On resume the subscription\n // reconnects and redelivers the final message if the turn finished while\n // away; if it is still pending, the fresh window starts counting from the\n // foreground.\n useEffect(() => {\n const sub = AppState.addEventListener('change', (next) => {\n if (next === 'active') {\n if (pendingRef.current && !streamCompletedRef.current) armTimeout();\n } else if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n });\n return () => sub.remove();\n }, [armTimeout]);\n\n useMessengerStreamDeltaSubscription({\n variables: { channelId: streamChannelId },\n skip: streamSkip,\n onData: ({ data }) => {\n const delta = data?.data?.messengerStreamDelta;\n if (!delta) return;\n\n if (delta.isFinal) return;\n\n if (streamCompletedRef.current) return;\n\n if (!pendingRef.current) {\n if (!reconnectedStreamRef.current) {\n reconnectedStreamRef.current = true;\n accumulatedLenRef.current = 0;\n }\n }\n\n if (quickReplyShownRef.current) {\n // The real answer has started: clear the brain's provisional reply\n // before the first chunk lands, so it is replaced, not appended to.\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n hasReceivedDeltasRef.current = true;\n\n // Heartbeat: a turn that is actively streaming is not stuck, so\n // push the idle deadline out on every delta. The timer now fires\n // only after real silence, never mid-stream on a long query.\n armTimeout();\n\n const fullText = stripModelCostHeader(delta.text ?? '');\n const newChunk = fullText.substring(accumulatedLenRef.current);\n accumulatedLenRef.current = fullText.length;\n\n if (newChunk) {\n cbRef.current.onChunk(newChunk);\n }\n },\n onError: (err) => {\n console.error('[useCdecliChannel] stream delta subscription error:', err);\n },\n });\n\n useGatewayInboundMessageByChannelSubscription({\n variables: { channelId: streamChannelId },\n skip: !isConnected || streamSkip,\n onData: ({ data }) => {\n const msg = data?.data?.gatewayInboundMessageByChannel;\n if (!msg?.text) return;\n const sanitizedText = stripModelCostHeader(msg.text);\n\n if (!hasReceivedDeltasRef.current) {\n if (quickReplyShownRef.current) {\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n cbRef.current.onChunk(sanitizedText);\n }\n\n cbRef.current.onComplete(sanitizedText);\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = true;\n accumulatedLenRef.current = 0;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n onError: (err) => {\n console.error('[useCdecliChannel] subscription error:', err);\n cbRef.current.onError(err.message || 'CDeCLI subscription error');\n },\n });\n\n const sendMessage = useCallback(\n async (\n text: string,\n chatId = 'messenger',\n media: Array<{ type: string; url: string; data?: string; mimeType?: string; filename?: string }> = [],\n ): Promise<boolean> => {\n if (!isConnected) return false;\n\n pendingRef.current = { text, sentAt: Date.now() };\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n quickReplyShownRef.current = false;\n\n // Ask the brain the same question in parallel with the send. On a cold\n // first turn the agent can take minutes before its first delta; the\n // brain answers in seconds. Its reply is painted only if nothing real\n // has arrived yet, and is replaced in place the moment it does. Fire\n // and forget: a brain failure changes nothing about the turn.\n const turn = ++turnSeqRef.current;\n void quickReplyViaBrain(text).then((answer) => {\n if (!answer) return;\n // Stale if the turn moved on, or the agent already spoke.\n if (turnSeqRef.current !== turn || !pendingRef.current) return;\n if (hasReceivedDeltasRef.current || streamCompletedRef.current) return;\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(answer);\n });\n\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n ...(media.length > 0 ? { media: media as never } : {}),\n ...(model || skill\n ? { metadata: { ...(model && { model }), ...(skill && { skill }) } }\n : {}),\n },\n },\n });\n\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI send failed';\n cbRef.current.onError(errMsg);\n pendingRef.current = null;\n return false;\n }\n\n armTimeout();\n\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n cbRef.current.onError(msg);\n pendingRef.current = null;\n return false;\n }\n },\n [isConnected, accountId, channelId, sendMutation, model, skill, armTimeout],\n );\n\n useEffect(\n () => () => {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n [],\n );\n\n /**\n * Inject a follow-up into the turn that is currently streaming.\n * Same `gatewaySendMessage` mutation with `metadata.steer = true` (browser\n * `useCdecliChannel.steer`). Do NOT reset streaming refs: the existing\n * delta subscription must keep running.\n *\n * \"Cannot steer: no active session\" is an expected race before cdecli has\n * started the turn. Callers should queue and retry; do not `console.error`\n * (that pops the RN LogBox and looks like a crash).\n */\n const steer = useCallback(\n async (text: string, chatId = 'messenger'): Promise<SteerResult> => {\n if (!isConnected) return { ok: false, error: 'CDeCLI is not connected.' };\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n metadata: { steer: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI steer failed';\n if (!isNoActiveSessionError(errMsg)) {\n console.warn('[useCdecliChannel] steer failed:', errMsg);\n }\n return { ok: false, error: errMsg };\n }\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn('[useCdecliChannel] steer mutation error:', msg);\n return { ok: false, error: msg };\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n /**\n * Abort the in-flight turn. `metadata.cancel = true` maps to\n * `POST /v1/chat/cancel` on cdecli-serve (browser parity).\n */\n const cancel = useCallback(\n async (chatId = 'messenger'): Promise<boolean> => {\n if (!isConnected) return false;\n pendingRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text: '',\n metadata: { cancel: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n console.error('[useCdecliChannel] cancel failed:', payload?.error || 'CDeCLI cancel failed');\n return false;\n }\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliChannel] cancel mutation error:', msg);\n return false;\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n return { sendMessage, steer, cancel };\n}\n"],"names":["_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCdecliChannel.js","sources":["../../src/hooks/useCdecliChannel.ts"],"sourcesContent":["/**\n * useCdecliChannel — wires the cdecli-serve messenger-gateway channel into the mobile chat UI.\n *\n * Kept in sync with `packages-modules/account/browser/src/hooks/useCdecliChannel.ts`.\n * When the CDeCLI channel is connected:\n * - `sendMessage(text, chatId, media)` calls `gatewaySendMessage`\n * - `MessengerStreamDelta` subscription delivers streaming chunks via `onChunk`\n * - `GatewayInboundMessageByChannel` delivers the final reply via `onComplete`\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { gql, useApolloClient } from '@apollo/client';\nimport { quickReplyViaBrain } from '../services/brainQuickReply';\nimport {\n useGatewaySendMessageMutation,\n useGatewayInboundMessageByChannelSubscription,\n useMessengerStreamDeltaSubscription,\n} from 'common/graphql';\n\n/**\n * Resume snapshot for a channel's ACTIVE stream (server-owned).\n * Same document as browser `useCdecliChannel` — older backends error and we skip.\n */\nconst MESSENGER_ACTIVE_STREAM_QUERY = gql`\n query MessengerActiveStreamResume($channelId: String!) {\n messengerActiveStream(channelId: $channelId) {\n runId\n seq\n channelId\n text\n isFinal\n }\n }\n`;\n\nfunction stripModelCostHeader(content: string): string {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n return normalized.replace(\n /^\\s*(?:[^\\w\\n]+\\s*)?[a-z0-9][a-z0-9._-]*\\s*\\(\\s*\\$[\\d.]+\\s*\\/\\s*MTok\\s+in\\s*\\)\\s*\\n+/i,\n '',\n );\n}\n\n// `error?: undefined` on the success arm keeps `result.error` reachable on the\n// union: with strictNullChecks off (repo-wide), TS will not narrow the\n// discriminated union after an `if (result.ok) continue`.\nexport type SteerResult = { ok: true; error?: undefined } | { ok: false; error: string };\n\nexport interface CdecliChannelCallbacks {\n onChunk: (text: string) => void;\n /**\n * A fast provisional answer from `yantra-brain`, painted while the real agent\n * is still bootstrapping. Distinct from onChunk on purpose: the UI shows it as\n * the reply-so-far and REPLACES it the moment real deltas arrive, so the brain\n * fills the silence without ever stacking on top of the agent's answer.\n */\n onQuickReply?: (text: string) => void;\n onComplete: (text: string) => void;\n onError: (error: string) => void;\n}\n\nexport function isNoActiveSessionError(message: string): boolean {\n return /no active session/i.test(message);\n}\n\n/**\n * Idle window, not a total budget. The timer is re-armed on every streamed\n * delta (a turn that is actively streaming is not stuck), and paused while the\n * app is backgrounded (iOS suspends the WebSocket + throttles JS timers, so a\n * wall-clock timer would otherwise fire a false \"did not respond\" the instant\n * the app returns to the foreground). It fires only after this much CONTINUOUS\n * silence with the app in the foreground.\n */\nconst CDECLI_RESPONSE_TIMEOUT_MS = 300_000;\n\nexport function useCdecliChannel(\n isConnected: boolean,\n accountId: string,\n channelId: string | undefined,\n callbacks: CdecliChannelCallbacks,\n model?: string,\n skill?: string,\n) {\n const [sendMutation] = useGatewaySendMessageMutation();\n const cbRef = useRef(callbacks);\n cbRef.current = callbacks;\n\n const pendingRef = useRef<{ text: string; sentAt: number } | null>(null);\n const hasReceivedDeltasRef = useRef(false);\n const reconnectedStreamRef = useRef(false);\n const streamCompletedRef = useRef(false);\n const accumulatedLenRef = useRef(0);\n const lastRunIdRef = useRef<string | null>(null);\n const apolloClient = useApolloClient();\n const seededChannelRef = useRef<string | null>(null);\n\n /**\n * Forward newly-accumulated stream text. Shared by live deltas and the\n * mount-time resume seed so neither gaps nor double-delivery happen when\n * the user leaves Chat for history and comes back mid-turn.\n */\n const ingestAccumulatedText = useCallback((accumulated: string) => {\n const fullText = stripModelCostHeader(accumulated);\n const newChunk = fullText.substring(accumulatedLenRef.current);\n accumulatedLenRef.current = fullText.length;\n if (newChunk) cbRef.current.onChunk(newChunk);\n }, []);\n // True while a brain quick-reply is what the user is looking at. The first\n // real delta clears the provisional text before painting, so the agent's\n // answer replaces the brain's rather than appending to it.\n const quickReplyShownRef = useRef(false);\n // Identity of the current turn for the brain race. A counter, not a\n // timestamp: two sends can share a millisecond, and a timestamp would then\n // let a brain answer for a superseded turn paint over the live one.\n const turnSeqRef = useRef(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const streamSkip = !channelId;\n const streamChannelId = channelId || accountId;\n\n /** (Re)start the idle timer for the in-flight turn. No-op if none pending. */\n const armTimeout = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n if (!pendingRef.current) {\n timeoutRef.current = null;\n return;\n }\n timeoutRef.current = setTimeout(() => {\n if (pendingRef.current) {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n timeoutRef.current = null;\n cbRef.current.onError(\n 'CDeCLI agent did not respond within 300 seconds. The query may still be processing.',\n );\n }\n }, CDECLI_RESPONSE_TIMEOUT_MS);\n }, []);\n\n // Pause the idle timer in the background, re-arm fresh on return. iOS\n // suspends the streaming WebSocket and throttles JS timers while\n // backgrounded, so a running wall-clock timer either fires against a\n // connection that cannot deliver or fires the instant the app resumes -\n // both read to the user as a spurious timeout. On resume the subscription\n // reconnects and redelivers the final message if the turn finished while\n // away; if it is still pending, the fresh window starts counting from the\n // foreground.\n useEffect(() => {\n const sub = AppState.addEventListener('change', (next) => {\n if (next === 'active') {\n if (pendingRef.current && !streamCompletedRef.current) armTimeout();\n } else if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n });\n return () => sub.remove();\n }, [armTimeout]);\n\n useMessengerStreamDeltaSubscription({\n variables: { channelId: streamChannelId },\n skip: streamSkip,\n onData: ({ data }) => {\n const delta = data?.data?.messengerStreamDelta;\n if (!delta) return;\n\n if (delta.isFinal) return;\n\n if (delta.runId && delta.runId !== lastRunIdRef.current) {\n accumulatedLenRef.current = 0;\n }\n if (delta.runId) lastRunIdRef.current = delta.runId;\n\n if (streamCompletedRef.current) return;\n\n // Pick up a turn that kept running after the user left Chat for history.\n if (!pendingRef.current) {\n if (!reconnectedStreamRef.current) {\n reconnectedStreamRef.current = true;\n accumulatedLenRef.current = 0;\n }\n }\n\n if (quickReplyShownRef.current) {\n // The real answer has started: clear the brain's provisional reply\n // before the first chunk lands, so it is replaced, not appended to.\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n hasReceivedDeltasRef.current = true;\n\n // Heartbeat: a turn that is actively streaming is not stuck, so\n // push the idle deadline out on every delta. The timer now fires\n // only after real silence, never mid-stream on a long query.\n armTimeout();\n\n ingestAccumulatedText(delta.text ?? '');\n },\n onError: (err) => {\n console.error('[useCdecliChannel] stream delta subscription error:', err);\n },\n });\n\n // Seed the in-flight reply when Chat remounts (history → chat) mid-stream.\n useEffect(() => {\n if (streamSkip || !streamChannelId) return undefined;\n if (seededChannelRef.current === streamChannelId) return undefined;\n seededChannelRef.current = streamChannelId;\n accumulatedLenRef.current = 0;\n streamCompletedRef.current = false;\n reconnectedStreamRef.current = false;\n lastRunIdRef.current = null;\n\n let cancelled = false;\n apolloClient\n .query({\n query: MESSENGER_ACTIVE_STREAM_QUERY,\n variables: { channelId: streamChannelId },\n fetchPolicy: 'network-only',\n errorPolicy: 'all',\n })\n .then(({ data }) => {\n if (cancelled) return;\n const snapshot = data?.messengerActiveStream as\n | { runId?: string; text?: string; isFinal?: boolean }\n | null\n | undefined;\n if (!snapshot?.text || snapshot.isFinal) return;\n if (streamCompletedRef.current) return;\n if (accumulatedLenRef.current > 0) return;\n reconnectedStreamRef.current = true;\n hasReceivedDeltasRef.current = true;\n if (snapshot.runId) lastRunIdRef.current = snapshot.runId;\n ingestAccumulatedText(snapshot.text);\n })\n .catch(() => {\n /* pre-#701 backend or transient failure — live deltas self-heal */\n });\n return () => {\n cancelled = true;\n };\n }, [streamChannelId, streamSkip, apolloClient, ingestAccumulatedText]);\n\n useGatewayInboundMessageByChannelSubscription({\n variables: { channelId: streamChannelId },\n skip: !isConnected || streamSkip,\n onData: ({ data }) => {\n const msg = data?.data?.gatewayInboundMessageByChannel;\n if (!msg?.text) return;\n const sanitizedText = stripModelCostHeader(msg.text);\n\n if (!hasReceivedDeltasRef.current) {\n if (quickReplyShownRef.current) {\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n cbRef.current.onChunk(sanitizedText);\n }\n\n cbRef.current.onComplete(sanitizedText);\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = true;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n onError: (err) => {\n console.error('[useCdecliChannel] subscription error:', err);\n cbRef.current.onError(err.message || 'CDeCLI subscription error');\n },\n });\n\n const sendMessage = useCallback(\n async (\n text: string,\n chatId = 'messenger',\n media: Array<{ type: string; url: string; data?: string; mimeType?: string; filename?: string }> = [],\n ): Promise<boolean> => {\n if (!isConnected) return false;\n\n pendingRef.current = { text, sentAt: Date.now() };\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n quickReplyShownRef.current = false;\n\n // Ask the brain the same question in parallel with the send. On a cold\n // first turn the agent can take minutes before its first delta; the\n // brain answers in seconds. Its reply is painted only if nothing real\n // has arrived yet, and is replaced in place the moment it does. Fire\n // and forget: a brain failure changes nothing about the turn.\n const turn = ++turnSeqRef.current;\n void quickReplyViaBrain(text).then((answer) => {\n if (!answer) return;\n // Stale if the turn moved on, or the agent already spoke.\n if (turnSeqRef.current !== turn || !pendingRef.current) return;\n if (hasReceivedDeltasRef.current || streamCompletedRef.current) return;\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(answer);\n });\n\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n ...(media.length > 0 ? { media: media as never } : {}),\n ...(model || skill\n ? { metadata: { ...(model && { model }), ...(skill && { skill }) } }\n : {}),\n },\n },\n });\n\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI send failed';\n cbRef.current.onError(errMsg);\n pendingRef.current = null;\n return false;\n }\n\n armTimeout();\n\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n cbRef.current.onError(msg);\n pendingRef.current = null;\n return false;\n }\n },\n [isConnected, accountId, channelId, sendMutation, model, skill, armTimeout],\n );\n\n useEffect(\n () => () => {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n [],\n );\n\n /**\n * Inject a follow-up into the turn that is currently streaming.\n * Same `gatewaySendMessage` mutation with `metadata.steer = true` (browser\n * `useCdecliChannel.steer`). Do NOT reset streaming refs: the existing\n * delta subscription must keep running.\n *\n * \"Cannot steer: no active session\" is an expected race before cdecli has\n * started the turn. Callers should queue and retry; do not `console.error`\n * (that pops the RN LogBox and looks like a crash).\n */\n const steer = useCallback(\n async (text: string, chatId = 'messenger'): Promise<SteerResult> => {\n if (!isConnected) return { ok: false, error: 'CDeCLI is not connected.' };\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n metadata: { steer: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI steer failed';\n if (!isNoActiveSessionError(errMsg)) {\n console.warn('[useCdecliChannel] steer failed:', errMsg);\n }\n return { ok: false, error: errMsg };\n }\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn('[useCdecliChannel] steer mutation error:', msg);\n return { ok: false, error: msg };\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n /**\n * Abort the in-flight turn. `metadata.cancel = true` maps to\n * `POST /v1/chat/cancel` on cdecli-serve (browser parity).\n */\n const cancel = useCallback(\n async (chatId = 'messenger'): Promise<boolean> => {\n if (!isConnected) return false;\n pendingRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text: '',\n metadata: { cancel: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n console.error('[useCdecliChannel] cancel failed:', payload?.error || 'CDeCLI cancel failed');\n return false;\n }\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliChannel] cancel mutation error:', msg);\n return false;\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n return { sendMessage, steer, cancel };\n}\n"],"names":["_a"],"mappings":";;;;;;;;;;;;;;;;AAoBA,MAAM,6BAAgC,GAAA,GAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,CAAA;AAWtC,SAAS,qBAAqB,OAAyB,EAAA;AACrD,EAAA,MAAM,UAAa,GAAA,OAAA,CAAQ,OAAQ,CAAA,OAAA,EAAS,IAAI,CAAA;AAChD,EAAO,OAAA,UAAA,CAAW,OAAQ,CAAA,uFAAA,EAAyF,EAAE,CAAA;AACvH;AAwBO,SAAS,uBAAuB,OAA0B,EAAA;AAC/D,EAAO,OAAA,oBAAA,CAAqB,KAAK,OAAO,CAAA;AAC1C;AAUA,MAAM,0BAA6B,GAAA,GAAA;AAC5B,SAAS,iBAAiB,WAAsB,EAAA,SAAA,EAAmB,SAA+B,EAAA,SAAA,EAAmC,OAAgB,KAAgB,EAAA;AAC1K,EAAM,MAAA,CAAC,YAAY,CAAA,GAAI,6BAA8B,EAAA;AACrD,EAAM,MAAA,KAAA,GAAQ,OAAO,SAAS,CAAA;AAC9B,EAAA,KAAA,CAAM,OAAU,GAAA,SAAA;AAChB,EAAM,MAAA,UAAA,GAAa,OAGT,IAAI,CAAA;AACd,EAAM,MAAA,oBAAA,GAAuB,OAAO,KAAK,CAAA;AACzC,EAAM,MAAA,oBAAA,GAAuB,OAAO,KAAK,CAAA;AACzC,EAAM,MAAA,kBAAA,GAAqB,OAAO,KAAK,CAAA;AACvC,EAAM,MAAA,iBAAA,GAAoB,OAAO,CAAC,CAAA;AAClC,EAAM,MAAA,YAAA,GAAe,OAAsB,IAAI,CAAA;AAC/C,EAAA,MAAM,eAAe,eAAgB,EAAA;AACrC,EAAM,MAAA,gBAAA,GAAmB,OAAsB,IAAI,CAAA;AAOnD,EAAM,MAAA,qBAAA,GAAwB,WAAY,CAAA,CAAC,WAAwB,KAAA;AACjE,IAAM,MAAA,QAAA,GAAW,qBAAqB,WAAW,CAAA;AACjD,IAAA,MAAM,QAAW,GAAA,QAAA,CAAS,SAAU,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAC7D,IAAA,iBAAA,CAAkB,UAAU,QAAS,CAAA,MAAA;AACrC,IAAA,IAAI,QAAU,EAAA,KAAA,CAAM,OAAQ,CAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,GAC9C,EAAG,EAAE,CAAA;AAIL,EAAM,MAAA,kBAAA,GAAqB,OAAO,KAAK,CAAA;AAIvC,EAAM,MAAA,UAAA,GAAa,OAAO,CAAC,CAAA;AAC3B,EAAM,MAAA,UAAA,GAAa,OAA6C,IAAI,CAAA;AACpE,EAAA,MAAM,aAAa,CAAC,SAAA;AACpB,EAAA,MAAM,kBAAkB,SAAa,IAAA,SAAA;AAGrC,EAAM,MAAA,UAAA,GAAa,YAAY,MAAM;AACnC,IAAA,IAAI,UAAW,CAAA,OAAA,EAAsB,YAAA,CAAA,UAAA,CAAW,OAAO,CAAA;AACvD,IAAI,IAAA,CAAC,WAAW,OAAS,EAAA;AACvB,MAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,MAAA;AAAA;AAEF,IAAW,UAAA,CAAA,OAAA,GAAU,WAAW,MAAM;AACpC,MAAA,IAAI,WAAW,OAAS,EAAA;AACtB,QAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,QAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,QAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,QAAM,KAAA,CAAA,OAAA,CAAQ,QAAQ,qFAAqF,CAAA;AAAA;AAC7G,OACC,0BAA0B,CAAA;AAAA,GAC/B,EAAG,EAAE,CAAA;AAUL,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,GAAM,GAAA,QAAA,CAAS,gBAAiB,CAAA,QAAA,EAAU,CAAQ,IAAA,KAAA;AACtD,MAAA,IAAI,SAAS,QAAU,EAAA;AACrB,QAAA,IAAI,UAAW,CAAA,OAAA,IAAW,CAAC,kBAAA,CAAmB,SAAoB,UAAA,EAAA;AAAA,OACpE,MAAA,IAAW,WAAW,OAAS,EAAA;AAC7B,QAAA,YAAA,CAAa,WAAW,OAAO,CAAA;AAC/B,QAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AAAA;AACvB,KACD,CAAA;AACD,IAAO,OAAA,MAAM,IAAI,MAAO,EAAA;AAAA,GAC1B,EAAG,CAAC,UAAU,CAAC,CAAA;AACf,EAAoC,mCAAA,CAAA;AAAA,IAClC,SAAW,EAAA;AAAA,MACT,SAAW,EAAA;AAAA,KACb;AAAA,IACA,IAAM,EAAA,UAAA;AAAA,IACN,QAAQ,CAAC;AAAA,MACP;AAAA,KACI,KAAA;AAzJV,MAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA0JM,MAAM,MAAA,KAAA,GAAA,CAAQ,EAAM,GAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAA,IAAA,KAAN,IAAY,GAAA,MAAA,GAAA,EAAA,CAAA,oBAAA;AAC1B,MAAA,IAAI,CAAC,KAAO,EAAA;AACZ,MAAA,IAAI,MAAM,OAAS,EAAA;AACnB,MAAA,IAAI,KAAM,CAAA,KAAA,IAAS,KAAM,CAAA,KAAA,KAAU,aAAa,OAAS,EAAA;AACvD,QAAA,iBAAA,CAAkB,OAAU,GAAA,CAAA;AAAA;AAE9B,MAAA,IAAI,KAAM,CAAA,KAAA,EAAoB,YAAA,CAAA,OAAA,GAAU,KAAM,CAAA,KAAA;AAC9C,MAAA,IAAI,mBAAmB,OAAS,EAAA;AAGhC,MAAI,IAAA,CAAC,WAAW,OAAS,EAAA;AACvB,QAAI,IAAA,CAAC,qBAAqB,OAAS,EAAA;AACjC,UAAA,oBAAA,CAAqB,OAAU,GAAA,IAAA;AAC/B,UAAA,iBAAA,CAAkB,OAAU,GAAA,CAAA;AAAA;AAC9B;AAEF,MAAA,IAAI,mBAAmB,OAAS,EAAA;AAG9B,QAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAC7B,QAAM,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,KAAA,CAAA,OAAA,EAAQ,iBAAd,IAA6B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EAAA,CAAA;AAAA;AAE/B,MAAA,oBAAA,CAAqB,OAAU,GAAA,IAAA;AAK/B,MAAW,UAAA,EAAA;AACX,MAAsB,qBAAA,CAAA,CAAA,EAAA,GAAA,KAAA,CAAM,IAAN,KAAA,IAAA,GAAA,EAAA,GAAc,EAAE,CAAA;AAAA,KACxC;AAAA,IACA,SAAS,CAAO,GAAA,KAAA;AACd,MAAQ,OAAA,CAAA,KAAA,CAAM,uDAAuD,GAAG,CAAA;AAAA;AAC1E,GACD,CAAA;AAGD,EAAA,SAAA,CAAU,MAAM;AACd,IAAI,IAAA,UAAA,IAAc,CAAC,eAAA,EAAwB,OAAA,MAAA;AAC3C,IAAI,IAAA,gBAAA,CAAiB,OAAY,KAAA,eAAA,EAAwB,OAAA,MAAA;AACzD,IAAA,gBAAA,CAAiB,OAAU,GAAA,eAAA;AAC3B,IAAA,iBAAA,CAAkB,OAAU,GAAA,CAAA;AAC5B,IAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAC7B,IAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,IAAA,YAAA,CAAa,OAAU,GAAA,IAAA;AACvB,IAAA,IAAI,SAAY,GAAA,KAAA;AAChB,IAAA,YAAA,CAAa,KAAM,CAAA;AAAA,MACjB,KAAO,EAAA,6BAAA;AAAA,MACP,SAAW,EAAA;AAAA,QACT,SAAW,EAAA;AAAA,OACb;AAAA,MACA,WAAa,EAAA,cAAA;AAAA,MACb,WAAa,EAAA;AAAA,KACd,CAAE,CAAA,IAAA,CAAK,CAAC;AAAA,MACP;AAAA,KACI,KAAA;AACJ,MAAA,IAAI,SAAW,EAAA;AACf,MAAA,MAAM,WAAW,IAAM,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAA,qBAAA;AAKvB,MAAA,IAAI,EAAC,QAAA,IAAA,IAAA,GAAA,MAAA,GAAA,QAAA,CAAU,IAAQ,CAAA,IAAA,QAAA,CAAS,OAAS,EAAA;AACzC,MAAA,IAAI,mBAAmB,OAAS,EAAA;AAChC,MAAI,IAAA,iBAAA,CAAkB,UAAU,CAAG,EAAA;AACnC,MAAA,oBAAA,CAAqB,OAAU,GAAA,IAAA;AAC/B,MAAA,oBAAA,CAAqB,OAAU,GAAA,IAAA;AAC/B,MAAA,IAAI,QAAS,CAAA,KAAA,EAAoB,YAAA,CAAA,OAAA,GAAU,QAAS,CAAA,KAAA;AACpD,MAAA,qBAAA,CAAsB,SAAS,IAAI,CAAA;AAAA,KACpC,CAAE,CAAA,KAAA,CAAM,MAAM;AAAA,KAEd,CAAA;AACD,IAAA,OAAO,MAAM;AACX,MAAY,SAAA,GAAA,IAAA;AAAA,KACd;AAAA,KACC,CAAC,eAAA,EAAiB,UAAY,EAAA,YAAA,EAAc,qBAAqB,CAAC,CAAA;AACrE,EAA8C,6CAAA,CAAA;AAAA,IAC5C,SAAW,EAAA;AAAA,MACT,SAAW,EAAA;AAAA,KACb;AAAA,IACA,IAAA,EAAM,CAAC,WAAe,IAAA,UAAA;AAAA,IACtB,QAAQ,CAAC;AAAA,MACP;AAAA,KACI,KAAA;AA5OV,MAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA;AA6OM,MAAM,MAAA,GAAA,GAAA,CAAM,EAAM,GAAA,IAAA,IAAA,IAAA,GAAA,MAAA,GAAA,IAAA,CAAA,IAAA,KAAN,IAAY,GAAA,MAAA,GAAA,EAAA,CAAA,8BAAA;AACxB,MAAI,IAAA,EAAC,2BAAK,IAAM,CAAA,EAAA;AAChB,MAAM,MAAA,aAAA,GAAgB,oBAAqB,CAAA,GAAA,CAAI,IAAI,CAAA;AACnD,MAAI,IAAA,CAAC,qBAAqB,OAAS,EAAA;AACjC,QAAA,IAAI,mBAAmB,OAAS,EAAA;AAC9B,UAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAC7B,UAAM,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,KAAA,CAAA,OAAA,EAAQ,iBAAd,IAA6B,GAAA,MAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAA,EAAA,CAAA;AAAA;AAE/B,QAAM,KAAA,CAAA,OAAA,CAAQ,QAAQ,aAAa,CAAA;AAAA;AAErC,MAAM,KAAA,CAAA,OAAA,CAAQ,WAAW,aAAa,CAAA;AACtC,MAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,MAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,MAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,MAAA,kBAAA,CAAmB,OAAU,GAAA,IAAA;AAC7B,MAAA,iBAAA,CAAkB,OAAU,GAAA,CAAA;AAC5B,MAAA,YAAA,CAAa,OAAU,GAAA,IAAA;AACvB,MAAA,IAAI,WAAW,OAAS,EAAA;AACtB,QAAA,YAAA,CAAa,WAAW,OAAO,CAAA;AAC/B,QAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AAAA;AACvB,KACF;AAAA,IACA,SAAS,CAAO,GAAA,KAAA;AACd,MAAQ,OAAA,CAAA,KAAA,CAAM,0CAA0C,GAAG,CAAA;AAC3D,MAAA,KAAA,CAAM,OAAQ,CAAA,OAAA,CAAQ,GAAI,CAAA,OAAA,IAAW,2BAA2B,CAAA;AAAA;AAClE,GACD,CAAA;AACD,EAAM,MAAA,WAAA,GAAc,YAAY,OAAO,IAAA,EAAc,SAAS,WAAa,EAAA,KAAA,GAMtE,EAAyB,KAAA;AA9QhC,IAAA,IAAA,EAAA;AA+QI,IAAI,IAAA,CAAC,aAAoB,OAAA,KAAA;AACzB,IAAA,UAAA,CAAW,OAAU,GAAA;AAAA,MACnB,IAAA;AAAA,MACA,MAAA,EAAQ,KAAK,GAAI;AAAA,KACnB;AACA,IAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,IAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,IAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAC7B,IAAA,iBAAA,CAAkB,OAAU,GAAA,CAAA;AAC5B,IAAA,YAAA,CAAa,OAAU,GAAA,IAAA;AACvB,IAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAO7B,IAAM,MAAA,IAAA,GAAO,EAAE,UAAW,CAAA,OAAA;AAC1B,IAAA,KAAK,kBAAmB,CAAA,IAAI,CAAE,CAAA,IAAA,CAAK,CAAU,MAAA,KAAA;AAjSjD,MAAA,IAAAA,GAAA,EAAA,EAAA;AAkSM,MAAA,IAAI,CAAC,MAAQ,EAAA;AAEb,MAAA,IAAI,UAAW,CAAA,OAAA,KAAY,IAAQ,IAAA,CAAC,WAAW,OAAS,EAAA;AACxD,MAAI,IAAA,oBAAA,CAAqB,OAAW,IAAA,kBAAA,CAAmB,OAAS,EAAA;AAChE,MAAA,kBAAA,CAAmB,OAAU,GAAA,IAAA;AAC7B,MAAA,CAAA,EAAA,GAAA,CAAAA,GAAA,GAAA,KAAA,CAAM,OAAQ,EAAA,YAAA,KAAd,wBAAAA,GAA6B,EAAA,MAAA,CAAA;AAAA,KAC9B,CAAA;AACD,IAAI,IAAA;AACF,MAAM,MAAA,MAAA,GAAS,MAAM,YAAa,CAAA;AAAA,QAChC,SAAW,EAAA;AAAA,UACT,KAAO,EAAA,cAAA,CAAA,cAAA,CAAA;AAAA,YACL,WAAa,EAAA,cAAA;AAAA,YACb,SAAA;AAAA,YACA,MAAA;AAAA,YACA;AAAA,WACI,EAAA,KAAA,CAAM,SAAS,CAAI,GAAA;AAAA,YACrB;AAAA,WACE,GAAA,EACA,CAAA,EAAA,KAAA,IAAS,KAAQ,GAAA;AAAA,YACnB,QAAA,EAAU,kCACJ,KAAS,IAAA;AAAA,cACX;AAAA,gBAEE,KAAS,IAAA;AAAA,cACX;AAAA,aACF;AAAA,cAEA,EAAC;AAAA;AAET,OACD,CAAA;AACD,MAAM,MAAA,OAAA,GAAA,CAAU,EAAO,GAAA,MAAA,CAAA,IAAA,KAAP,IAAa,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,kBAAA;AAC7B,MAAI,IAAA,EAAC,mCAAS,OAAS,CAAA,EAAA;AACrB,QAAM,MAAA,MAAA,GAAA,CAAS,mCAAS,KAAS,KAAA,oBAAA;AACjC,QAAM,KAAA,CAAA,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAC5B,QAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,QAAO,OAAA,KAAA;AAAA;AAET,MAAW,UAAA,EAAA;AACX,MAAO,OAAA,IAAA;AAAA,aACA,GAAK,EAAA;AACZ,MAAA,MAAM,MAAM,GAAe,YAAA,KAAA,GAAQ,GAAI,CAAA,OAAA,GAAU,OAAO,GAAG,CAAA;AAC3D,MAAM,KAAA,CAAA,OAAA,CAAQ,QAAQ,GAAG,CAAA;AACzB,MAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,MAAO,OAAA,KAAA;AAAA;AACT,GACF,EAAG,CAAC,WAAa,EAAA,SAAA,EAAW,WAAW,YAAc,EAAA,KAAA,EAAO,KAAO,EAAA,UAAU,CAAC,CAAA;AAC9E,EAAA,SAAA,CAAU,MAAM,MAAM;AACpB,IAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,IAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,IAAA,oBAAA,CAAqB,OAAU,GAAA,KAAA;AAC/B,IAAA,kBAAA,CAAmB,OAAU,GAAA,KAAA;AAC7B,IAAA,iBAAA,CAAkB,OAAU,GAAA,CAAA;AAC5B,IAAA,YAAA,CAAa,OAAU,GAAA,IAAA;AACvB,IAAA,IAAI,WAAW,OAAS,EAAA;AACtB,MAAA,YAAA,CAAa,WAAW,OAAO,CAAA;AAC/B,MAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AAAA;AACvB,GACF,EAAG,EAAE,CAAA;AAYL,EAAA,MAAM,KAAQ,GAAA,WAAA,CAAY,OAAO,IAAA,EAAc,SAAS,WAAsC,KAAA;AAxWhG,IAAA,IAAA,EAAA;AAyWI,IAAI,IAAA,CAAC,aAAoB,OAAA;AAAA,MACvB,EAAI,EAAA,KAAA;AAAA,MACJ,KAAO,EAAA;AAAA,KACT;AACA,IAAI,IAAA;AACF,MAAM,MAAA,MAAA,GAAS,MAAM,YAAa,CAAA;AAAA,QAChC,SAAW,EAAA;AAAA,UACT,KAAO,EAAA;AAAA,YACL,WAAa,EAAA,cAAA;AAAA,YACb,SAAA;AAAA,YACA,MAAA;AAAA,YACA,IAAA;AAAA,YACA,QAAU,EAAA;AAAA,cACR,KAAO,EAAA;AAAA;AACT;AACF;AACF,OACD,CAAA;AACD,MAAM,MAAA,OAAA,GAAA,CAAU,EAAO,GAAA,MAAA,CAAA,IAAA,KAAP,IAAa,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,kBAAA;AAC7B,MAAI,IAAA,EAAC,mCAAS,OAAS,CAAA,EAAA;AACrB,QAAM,MAAA,MAAA,GAAA,CAAS,mCAAS,KAAS,KAAA,qBAAA;AACjC,QAAI,IAAA,CAAC,sBAAuB,CAAA,MAAM,CAAG,EAAA;AACnC,UAAQ,OAAA,CAAA,IAAA,CAAK,oCAAoC,MAAM,CAAA;AAAA;AAEzD,QAAO,OAAA;AAAA,UACL,EAAI,EAAA,KAAA;AAAA,UACJ,KAAO,EAAA;AAAA,SACT;AAAA;AAEF,MAAO,OAAA;AAAA,QACL,EAAI,EAAA;AAAA,OACN;AAAA,aACO,GAAK,EAAA;AACZ,MAAA,MAAM,MAAM,GAAe,YAAA,KAAA,GAAQ,GAAI,CAAA,OAAA,GAAU,OAAO,GAAG,CAAA;AAC3D,MAAQ,OAAA,CAAA,IAAA,CAAK,4CAA4C,GAAG,CAAA;AAC5D,MAAO,OAAA;AAAA,QACL,EAAI,EAAA,KAAA;AAAA,QACJ,KAAO,EAAA;AAAA,OACT;AAAA;AACF,GACC,EAAA,CAAC,WAAa,EAAA,SAAA,EAAW,YAAY,CAAC,CAAA;AAMzC,EAAA,MAAM,MAAS,GAAA,WAAA,CAAY,OAAO,MAAA,GAAS,WAAkC,KAAA;AAvZ/E,IAAA,IAAA,EAAA;AAwZI,IAAI,IAAA,CAAC,aAAoB,OAAA,KAAA;AACzB,IAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AACrB,IAAA,IAAI,WAAW,OAAS,EAAA;AACtB,MAAA,YAAA,CAAa,WAAW,OAAO,CAAA;AAC/B,MAAA,UAAA,CAAW,OAAU,GAAA,IAAA;AAAA;AAEvB,IAAI,IAAA;AACF,MAAM,MAAA,MAAA,GAAS,MAAM,YAAa,CAAA;AAAA,QAChC,SAAW,EAAA;AAAA,UACT,KAAO,EAAA;AAAA,YACL,WAAa,EAAA,cAAA;AAAA,YACb,SAAA;AAAA,YACA,MAAA;AAAA,YACA,IAAM,EAAA,EAAA;AAAA,YACN,QAAU,EAAA;AAAA,cACR,MAAQ,EAAA;AAAA;AACV;AACF;AACF,OACD,CAAA;AACD,MAAM,MAAA,OAAA,GAAA,CAAU,EAAO,GAAA,MAAA,CAAA,IAAA,KAAP,IAAa,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,kBAAA;AAC7B,MAAI,IAAA,EAAC,mCAAS,OAAS,CAAA,EAAA;AACrB,QAAA,OAAA,CAAQ,KAAM,CAAA,mCAAA,EAAA,CAAqC,OAAS,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,OAAA,CAAA,KAAA,KAAS,sBAAsB,CAAA;AAC3F,QAAO,OAAA,KAAA;AAAA;AAET,MAAO,OAAA,IAAA;AAAA,aACA,GAAK,EAAA;AACZ,MAAA,MAAM,MAAM,GAAe,YAAA,KAAA,GAAQ,GAAI,CAAA,OAAA,GAAU,OAAO,GAAG,CAAA;AAC3D,MAAQ,OAAA,CAAA,KAAA,CAAM,6CAA6C,GAAG,CAAA;AAC9D,MAAO,OAAA,KAAA;AAAA;AACT,GACC,EAAA,CAAC,WAAa,EAAA,SAAA,EAAW,YAAY,CAAC,CAAA;AACzC,EAAO,OAAA;AAAA,IACL,WAAA;AAAA,IACA,KAAA;AAAA,IACA;AAAA,GACF;AACF"}
|
package/lib/hooks/useChatApi.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useApolloClient}from'@apollo/client/index.js';import {SortEnum,RoomType,PostTypeEnum,AiAgentMessageRole}from'common';import {useGetChannelsByUserWithLastMessageQuery,useMessagesQuery,useAddChannelMutation,useSendMessagesMutation,MessagesDocument,GetChannelsByUserWithLastMessageDocument}from'common/graphql';import {useMemo,useCallback}from'react';import {v4}from'uuid';import {isAttachedCaption,historyAttachmentTitle,attachmentsFromUserContent}from'../features/attachments/historyAttachmentLabel.js';var __defProp = Object.defineProperty;
|
|
1
|
+
import {useApolloClient}from'@apollo/client/index.js';import {SortEnum,RoomType,PostTypeEnum,AiAgentMessageRole}from'common';import {useGetChannelsByUserWithLastMessageQuery,useMessagesQuery,useAddChannelMutation,useSendMessagesMutation,OnChatMessageAddedDocument,MessagesDocument,GetChannelsByUserWithLastMessageDocument}from'common/graphql';import {useMemo,useCallback,useEffect}from'react';import {v4}from'uuid';import {isAttachedCaption,historyAttachmentTitle,attachmentsFromUserContent}from'../features/attachments/historyAttachmentLabel.js';import {stripAskUser}from'../features/chat/askUser.js';import {parseToolActivity}from'../features/chat/toolActivity.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -81,11 +81,7 @@ function getThreadMessagesQueryVariables(sessionId) {
|
|
|
81
81
|
return {
|
|
82
82
|
channelId: sessionId,
|
|
83
83
|
limit: MESSAGES_PAGE_LIMIT,
|
|
84
|
-
skip: 0
|
|
85
|
-
sort: {
|
|
86
|
-
key: "createdAt",
|
|
87
|
-
value: SortEnum.Asc
|
|
88
|
-
}
|
|
84
|
+
skip: 0
|
|
89
85
|
};
|
|
90
86
|
}
|
|
91
87
|
function getChatHistoryMessagesQueryVariables(accountUserId, options) {
|
|
@@ -148,6 +144,7 @@ function useChatHistorySessionsFromMessages(accountUserId, options) {
|
|
|
148
144
|
// Later visits read the cache — no repeated network hit.
|
|
149
145
|
fetchPolicy: "cache-first",
|
|
150
146
|
nextFetchPolicy: "cache-first",
|
|
147
|
+
returnPartialData: true,
|
|
151
148
|
context: {
|
|
152
149
|
cacheKey: "new-chat-history-messages"
|
|
153
150
|
}
|
|
@@ -214,7 +211,7 @@ function cleanMessageText(raw) {
|
|
|
214
211
|
return raw.replace(ACTIVE_CONNECTORS_PREFIX_RE, "").replace(/\s+/g, " ").trim();
|
|
215
212
|
}
|
|
216
213
|
function isTransientHistoryText(text) {
|
|
217
|
-
const t = cleanMessageText(text);
|
|
214
|
+
const t = cleanHistoryPreview(text) || cleanMessageText(text);
|
|
218
215
|
if (!t) return true;
|
|
219
216
|
if (/^thinking[.…]*$/i.test(t)) return true;
|
|
220
217
|
if (/^let me check on that requested skill/i.test(t)) return true;
|
|
@@ -231,10 +228,14 @@ function looksLikeAssistantReply(text, role) {
|
|
|
231
228
|
return false;
|
|
232
229
|
}
|
|
233
230
|
function cleanHistoryPreview(raw) {
|
|
234
|
-
|
|
231
|
+
const withoutAsk = stripAskUser(raw);
|
|
232
|
+
const {
|
|
233
|
+
text: withoutTools
|
|
234
|
+
} = parseToolActivity(withoutAsk, false);
|
|
235
|
+
let t = cleanMessageText(withoutTools);
|
|
235
236
|
t = t.replace(/^(thinking[.…]*\s*)+/i, "").trim();
|
|
236
237
|
t = t.replace(/^let me check on that requested skill[^\n.!?]*[.!?-]?\s*/i, "").trim();
|
|
237
|
-
t = t.replace(/^(
|
|
238
|
+
t = t.replace(/^(?:[⚙🔧⚙️⚠][^\n]*\n)+\s*/gu, "").trim();
|
|
238
239
|
return t;
|
|
239
240
|
}
|
|
240
241
|
function buildSessionFromChannel(channel) {
|
|
@@ -297,26 +298,151 @@ function chatHistorySessionsFromChannels(data) {
|
|
|
297
298
|
return channels.filter((c) => Boolean(c == null ? void 0 : c.id) && (!(c == null ? void 0 : c.type) || c.type === RoomType.Aiassistant)).map((c) => buildSessionFromChannel(c)).filter((row) => row !== null).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
|
|
298
299
|
}
|
|
299
300
|
const rememberedHistoryTitles = /* @__PURE__ */ new Map();
|
|
301
|
+
const rememberedHistoryListeners = /* @__PURE__ */ new Set();
|
|
302
|
+
function subscribeRememberedHistory(listener) {
|
|
303
|
+
rememberedHistoryListeners.add(listener);
|
|
304
|
+
return () => {
|
|
305
|
+
rememberedHistoryListeners.delete(listener);
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function mergeRememberedHistory(channelId, patch, notify = true) {
|
|
309
|
+
var _a, _b;
|
|
310
|
+
const existing = (_a = rememberedHistoryTitles.get(channelId)) != null ? _a : {};
|
|
311
|
+
const next = {
|
|
312
|
+
title: patch.title || existing.title,
|
|
313
|
+
preview: patch.preview || existing.preview,
|
|
314
|
+
isAttachment: (_b = patch.isAttachment) != null ? _b : existing.isAttachment,
|
|
315
|
+
running: patch.running !== void 0 ? patch.running : existing.running
|
|
316
|
+
};
|
|
317
|
+
rememberedHistoryTitles.set(channelId, next);
|
|
318
|
+
if (notify && (next.title !== existing.title || next.preview !== existing.preview || next.running !== existing.running)) {
|
|
319
|
+
rememberedHistoryListeners.forEach((fn) => fn());
|
|
320
|
+
}
|
|
321
|
+
return next;
|
|
322
|
+
}
|
|
300
323
|
function rememberHistoryTitle(session) {
|
|
301
|
-
var _a;
|
|
302
|
-
|
|
303
|
-
const title = (_a = session.title) == null ? void 0 : _a.trim();
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
|
|
324
|
+
var _a, _b;
|
|
325
|
+
const existing = rememberedHistoryTitles.get(session.channelId);
|
|
326
|
+
const title = session.isPlaceholder || /^new chat$/i.test(session.title) ? "" : (_a = session.title) == null ? void 0 : _a.trim();
|
|
327
|
+
if (existing == null ? void 0 : existing.running) {
|
|
328
|
+
if (title && !isTransientHistoryText(title)) {
|
|
329
|
+
mergeRememberedHistory(session.channelId, {
|
|
330
|
+
title
|
|
331
|
+
}, false);
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const preview = ((_b = session.preview) == null ? void 0 : _b.trim()) && !isTransientHistoryText(session.preview) ? session.preview.trim() : "";
|
|
336
|
+
if (title && isTransientHistoryText(title) && !preview) return;
|
|
337
|
+
if (!title && !preview) return;
|
|
338
|
+
mergeRememberedHistory(session.channelId, __spreadProps(__spreadValues(__spreadValues({}, title && !isTransientHistoryText(title) ? {
|
|
339
|
+
title
|
|
340
|
+
} : {}), preview ? {
|
|
341
|
+
preview
|
|
342
|
+
} : {}), {
|
|
307
343
|
isAttachment: session.isAttachment
|
|
344
|
+
}), false);
|
|
345
|
+
}
|
|
346
|
+
function deriveHistoryTitleFromPrompt(rawPrompt) {
|
|
347
|
+
const cleaned = cleanMessageText(rawPrompt);
|
|
348
|
+
if (!cleaned || isTransientHistoryText(cleaned) || /^new chat$/i.test(cleaned)) return "";
|
|
349
|
+
if (cleaned.length <= 64) return cleaned;
|
|
350
|
+
return `${cleaned.slice(0, 64).trim()}...`;
|
|
351
|
+
}
|
|
352
|
+
function patchChannelHistoryTitle(client, channelId, rawPrompt) {
|
|
353
|
+
const cleaned = deriveHistoryTitleFromPrompt(rawPrompt);
|
|
354
|
+
if (!channelId || !cleaned) return;
|
|
355
|
+
mergeRememberedHistory(channelId, {
|
|
356
|
+
title: cleaned,
|
|
357
|
+
isAttachment: isAttachedCaption(cleaned)
|
|
358
|
+
});
|
|
359
|
+
const channelCacheId = client.cache.identify({
|
|
360
|
+
__typename: "Channel",
|
|
361
|
+
id: channelId
|
|
362
|
+
});
|
|
363
|
+
if (!channelCacheId) return;
|
|
364
|
+
try {
|
|
365
|
+
client.cache.modify({
|
|
366
|
+
id: channelCacheId,
|
|
367
|
+
fields: {
|
|
368
|
+
title(existing) {
|
|
369
|
+
return cleanChannelTitle(existing) ? existing : cleaned;
|
|
370
|
+
}
|
|
371
|
+
// Do not touch updatedAt here. Bumping it on open/hydrate moves the
|
|
372
|
+
// row to TODAY / "now". Activity time is only written when the user
|
|
373
|
+
// actually sends (touchChannelHistoryUpdatedAt).
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
} catch (err) {
|
|
377
|
+
console.warn("[useChatApi] patchChannelHistoryTitle failed:", err);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function touchChannelHistoryUpdatedAt(client, channelId) {
|
|
381
|
+
if (!channelId) return;
|
|
382
|
+
const channelCacheId = client.cache.identify({
|
|
383
|
+
__typename: "Channel",
|
|
384
|
+
id: channelId
|
|
385
|
+
});
|
|
386
|
+
if (!channelCacheId) return;
|
|
387
|
+
try {
|
|
388
|
+
client.cache.modify({
|
|
389
|
+
id: channelCacheId,
|
|
390
|
+
fields: {
|
|
391
|
+
updatedAt() {
|
|
392
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
} catch (err) {
|
|
397
|
+
console.warn("[useChatApi] touchChannelHistoryUpdatedAt failed:", err);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function patchChannelHistoryPreview(channelId, rawPreview) {
|
|
401
|
+
var _a, _b;
|
|
402
|
+
if (!channelId) return;
|
|
403
|
+
const preview = cleanHistoryPreview(rawPreview);
|
|
404
|
+
if (!preview || isTransientHistoryText(preview) || isAttachedCaption(preview)) return;
|
|
405
|
+
const title = (_b = (_a = rememberedHistoryTitles.get(channelId)) == null ? void 0 : _a.title) != null ? _b : "";
|
|
406
|
+
if (title && preview === title) return;
|
|
407
|
+
mergeRememberedHistory(channelId, {
|
|
408
|
+
preview
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
function markChannelHistoryRunning(channelId, rawPreview) {
|
|
412
|
+
if (!channelId) return;
|
|
413
|
+
const preview = rawPreview ? cleanHistoryPreview(rawPreview) : "";
|
|
414
|
+
const usable = preview && !isTransientHistoryText(preview) && !isAttachedCaption(preview) ? preview : void 0;
|
|
415
|
+
mergeRememberedHistory(channelId, __spreadValues({
|
|
416
|
+
running: true
|
|
417
|
+
}, usable ? {
|
|
418
|
+
preview: usable
|
|
419
|
+
} : {}));
|
|
420
|
+
}
|
|
421
|
+
function clearChannelHistoryRunning(channelId) {
|
|
422
|
+
if (!channelId) return;
|
|
423
|
+
const existing = rememberedHistoryTitles.get(channelId);
|
|
424
|
+
if (!(existing == null ? void 0 : existing.running)) return;
|
|
425
|
+
mergeRememberedHistory(channelId, {
|
|
426
|
+
running: false
|
|
308
427
|
});
|
|
309
428
|
}
|
|
310
429
|
function applyRememberedHistoryTitles(rows) {
|
|
311
430
|
return rows.map((row) => {
|
|
431
|
+
var _a;
|
|
312
432
|
rememberHistoryTitle(row);
|
|
313
|
-
if (!row.isPlaceholder && !/^new chat$/i.test(row.title)) return row;
|
|
314
433
|
const mem = rememberedHistoryTitles.get(row.channelId);
|
|
315
434
|
if (!mem) return row;
|
|
435
|
+
const titleMissing = row.isPlaceholder || /^new chat$/i.test(row.title);
|
|
436
|
+
const previewMissing = !((_a = row.preview) == null ? void 0 : _a.trim());
|
|
437
|
+
const nextTitle = titleMissing && mem.title ? mem.title : row.title;
|
|
438
|
+
const livePreview = mem.running && mem.preview && mem.preview !== nextTitle ? mem.preview : previewMissing && mem.preview && mem.preview !== nextTitle ? mem.preview : row.preview;
|
|
439
|
+
if (!titleMissing && !previewMissing && !mem.running) return row;
|
|
316
440
|
return __spreadProps(__spreadValues({}, row), {
|
|
317
|
-
title:
|
|
318
|
-
|
|
319
|
-
|
|
441
|
+
title: nextTitle,
|
|
442
|
+
preview: livePreview,
|
|
443
|
+
isPlaceholder: titleMissing && mem.title ? false : row.isPlaceholder,
|
|
444
|
+
isAttachment: mem.isAttachment || row.isAttachment,
|
|
445
|
+
isRunning: Boolean(mem.running)
|
|
320
446
|
});
|
|
321
447
|
});
|
|
322
448
|
}
|
|
@@ -366,6 +492,7 @@ function useChatHistorySessionsFromChannels(orgName, options) {
|
|
|
366
492
|
// / createChannel cache writes, not per-visit network hits.
|
|
367
493
|
fetchPolicy: "cache-first",
|
|
368
494
|
nextFetchPolicy: "cache-first",
|
|
495
|
+
returnPartialData: true,
|
|
369
496
|
context: {
|
|
370
497
|
cacheKey: "chat-history-channels-list"
|
|
371
498
|
}
|
|
@@ -382,6 +509,11 @@ function useChatHistorySessionsFromChannels(orgName, options) {
|
|
|
382
509
|
sourceChannelCount
|
|
383
510
|
};
|
|
384
511
|
}
|
|
512
|
+
function usePrefetchChatHistory(orgName) {
|
|
513
|
+
return useChatHistorySessionsFromChannels(orgName, {
|
|
514
|
+
skip: !orgName
|
|
515
|
+
});
|
|
516
|
+
}
|
|
385
517
|
function stripModelCostHeader(content) {
|
|
386
518
|
const normalized = content.replace(/\r\n/g, "\n");
|
|
387
519
|
return normalized.replace(/^\s*(?:[^\w\n]+\s*)?[a-z0-9][a-z0-9._-]*\s*\(\s*\$[\d.]+\s*\/\s*MTok\s+in\s*\)\s*\n+/i, "");
|
|
@@ -483,19 +615,23 @@ function mapPostToChatMessageUI(post, fallbackChannelId) {
|
|
|
483
615
|
};
|
|
484
616
|
}
|
|
485
617
|
function useChatMessages(sessionId, options) {
|
|
618
|
+
const client = useApolloClient();
|
|
486
619
|
const {
|
|
487
620
|
data,
|
|
488
621
|
loading,
|
|
489
622
|
error,
|
|
490
|
-
refetch
|
|
623
|
+
refetch,
|
|
624
|
+
subscribeToMore
|
|
491
625
|
} = useMessagesQuery({
|
|
492
626
|
variables: sessionId ? getThreadMessagesQueryVariables(sessionId) : void 0,
|
|
493
627
|
skip: !sessionId || (void 0 ),
|
|
494
|
-
// cache
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
fetchPolicy: "cache-
|
|
628
|
+
// Same as browser: paint cache immediately, then hit the network so a thread
|
|
629
|
+
// opened from history is not stuck on a cache-first snapshot that only has
|
|
630
|
+
// the latest user post (assistant replies persist after the first query).
|
|
631
|
+
fetchPolicy: "cache-and-network",
|
|
498
632
|
nextFetchPolicy: "cache-first",
|
|
633
|
+
errorPolicy: "all",
|
|
634
|
+
notifyOnNetworkStatusChange: true,
|
|
499
635
|
/**
|
|
500
636
|
* Cache key is per-channel so switching sessions doesn't read another channel's response.
|
|
501
637
|
* Keeping this as a constant ('messages-list') used to cause cross-session bleed in the
|
|
@@ -505,12 +641,77 @@ function useChatMessages(sessionId, options) {
|
|
|
505
641
|
cacheKey: sessionId ? `messages-list:${sessionId}` : "messages-list"
|
|
506
642
|
}
|
|
507
643
|
});
|
|
644
|
+
useEffect(() => {
|
|
645
|
+
if (!sessionId || (void 0 )) return;
|
|
646
|
+
const unsubscribe = subscribeToMore({
|
|
647
|
+
document: OnChatMessageAddedDocument,
|
|
648
|
+
variables: {
|
|
649
|
+
channelId: sessionId
|
|
650
|
+
},
|
|
651
|
+
updateQuery: (prev, {
|
|
652
|
+
subscriptionData
|
|
653
|
+
}) => {
|
|
654
|
+
var _a, _b, _c, _d, _e;
|
|
655
|
+
const post = (_a = subscriptionData == null ? void 0 : subscriptionData.data) == null ? void 0 : _a.chatMessageAdded;
|
|
656
|
+
if (!(post == null ? void 0 : post.id)) return prev;
|
|
657
|
+
if (!(prev == null ? void 0 : prev.messages)) return prev;
|
|
658
|
+
const existing = (_b = prev.messages.data) != null ? _b : [];
|
|
659
|
+
const idx = existing.findIndex((row) => (row == null ? void 0 : row.id) === post.id);
|
|
660
|
+
let nextData;
|
|
661
|
+
let nextTotal;
|
|
662
|
+
if (idx >= 0) {
|
|
663
|
+
nextData = [...existing.slice(0, idx), post, ...existing.slice(idx + 1)];
|
|
664
|
+
nextTotal = (_c = prev.messages.totalCount) != null ? _c : existing.length;
|
|
665
|
+
} else {
|
|
666
|
+
nextData = [...existing, post];
|
|
667
|
+
nextTotal = ((_d = prev.messages.totalCount) != null ? _d : existing.length) + 1;
|
|
668
|
+
const parentId = (_e = post.parentId) != null ? _e : null;
|
|
669
|
+
if (parentId) {
|
|
670
|
+
nextData = nextData.map((root) => {
|
|
671
|
+
var _a2, _b2, _c2, _d2, _e2, _f, _g;
|
|
672
|
+
if ((root == null ? void 0 : root.id) !== parentId) return root;
|
|
673
|
+
const replies = (_b2 = (_a2 = root.replies) == null ? void 0 : _a2.data) != null ? _b2 : [];
|
|
674
|
+
if (replies.some((reply) => (reply == null ? void 0 : reply.id) === post.id)) return root;
|
|
675
|
+
return __spreadProps(__spreadValues({}, root), {
|
|
676
|
+
replies: __spreadProps(__spreadValues({}, (_c2 = root.replies) != null ? _c2 : {}), {
|
|
677
|
+
__typename: (_e2 = (_d2 = root.replies) == null ? void 0 : _d2.__typename) != null ? _e2 : "Messages",
|
|
678
|
+
data: [...replies, post],
|
|
679
|
+
totalCount: ((_g = (_f = root.replies) == null ? void 0 : _f.totalCount) != null ? _g : replies.length) + 1
|
|
680
|
+
})
|
|
681
|
+
});
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
const seen = /* @__PURE__ */ new Set();
|
|
686
|
+
nextData = nextData.filter((row) => {
|
|
687
|
+
if (!(row == null ? void 0 : row.id) || seen.has(row.id)) return false;
|
|
688
|
+
seen.add(row.id);
|
|
689
|
+
return true;
|
|
690
|
+
});
|
|
691
|
+
return __spreadProps(__spreadValues({}, prev), {
|
|
692
|
+
messages: __spreadProps(__spreadValues({}, prev.messages), {
|
|
693
|
+
data: nextData,
|
|
694
|
+
totalCount: nextTotal
|
|
695
|
+
})
|
|
696
|
+
});
|
|
697
|
+
},
|
|
698
|
+
onError: (err) => {
|
|
699
|
+
console.error("[useChatMessages] subscribeToMore error:", err);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
return () => unsubscribe();
|
|
703
|
+
}, [sessionId, void 0 , subscribeToMore, client]);
|
|
508
704
|
const messagesLoaded = data !== void 0;
|
|
509
705
|
const messages = useMemo(() => {
|
|
510
706
|
var _a, _b;
|
|
511
707
|
if (!sessionId) return [];
|
|
512
708
|
const rows = (_b = (_a = data == null ? void 0 : data.messages) == null ? void 0 : _a.data) != null ? _b : [];
|
|
513
|
-
|
|
709
|
+
const ownRows = rows.filter((post) => {
|
|
710
|
+
var _a2;
|
|
711
|
+
const postChannelId = (_a2 = post == null ? void 0 : post.channel) == null ? void 0 : _a2.id;
|
|
712
|
+
return !postChannelId || postChannelId === sessionId;
|
|
713
|
+
});
|
|
714
|
+
return flattenPostsWithReplies(ownRows, sessionId);
|
|
514
715
|
}, [data, sessionId]);
|
|
515
716
|
return {
|
|
516
717
|
messages,
|
|
@@ -768,13 +969,18 @@ function useChatMutations() {
|
|
|
768
969
|
}
|
|
769
970
|
};
|
|
770
971
|
}, [client, sendMessagesMutation]);
|
|
972
|
+
const patchChannelTitle = useCallback((channelId, rawPrompt, bumpActivity = false) => {
|
|
973
|
+
patchChannelHistoryTitle(client, channelId, rawPrompt);
|
|
974
|
+
if (bumpActivity) touchChannelHistoryUpdatedAt(client, channelId);
|
|
975
|
+
}, [client]);
|
|
771
976
|
return {
|
|
772
977
|
createChannel,
|
|
773
978
|
createSession: createChannel,
|
|
774
979
|
saveMessages,
|
|
980
|
+
patchChannelTitle,
|
|
775
981
|
loading: {
|
|
776
982
|
create: createChannelLoading,
|
|
777
983
|
saveMessages: sendMessagesLoading
|
|
778
984
|
}
|
|
779
985
|
};
|
|
780
|
-
}export{AI_ASSISTANT_CHANNELS_QUERY_VARS,HISTORY_PAGE_SIZE,HISTORY_QUERY_BASE,buildSessionFromChannel,chatHistorySessionsFromChannels,chatHistorySessionsFromMessages,enrichHistorySessionsWithUserPrompts,getChatHistoryChannelRefetchQueries,getChatHistoryMessagesQueryVariables,getHistoryChannelsQueryVariables,useChatHistorySessionsFromChannels,useChatHistorySessionsFromMessages,useChatMessages,useChatMutations};//# sourceMappingURL=useChatApi.js.map
|
|
986
|
+
}export{AI_ASSISTANT_CHANNELS_QUERY_VARS,HISTORY_PAGE_SIZE,HISTORY_QUERY_BASE,buildSessionFromChannel,chatHistorySessionsFromChannels,chatHistorySessionsFromMessages,clearChannelHistoryRunning,enrichHistorySessionsWithUserPrompts,getChatHistoryChannelRefetchQueries,getChatHistoryMessagesQueryVariables,getHistoryChannelsQueryVariables,markChannelHistoryRunning,patchChannelHistoryPreview,patchChannelHistoryTitle,subscribeRememberedHistory,touchChannelHistoryUpdatedAt,useChatHistorySessionsFromChannels,useChatHistorySessionsFromMessages,useChatMessages,useChatMutations,usePrefetchChatHistory};//# sourceMappingURL=useChatApi.js.map
|