@adminide-stack/yantra-mobile 12.0.69-alpha.18 → 12.0.69-alpha.19
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/contexts/ChatLiveStreamContext.js +5 -1
- package/lib/contexts/ChatLiveStreamContext.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +1 -1
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/chat/QueuedFollowUps.js +81 -0
- package/lib/features/chat/QueuedFollowUps.js.map +1 -0
- package/lib/hooks/useCdecliChannel.js +126 -4
- package/lib/hooks/useCdecliChannel.js.map +1 -1
- package/lib/hooks/useChatStream.js +83 -63
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/screens/Chat/index.js +41 -21
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/state/chatThreadMerge.js +6 -1
- 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 { useApolloClient } from '@apollo/client';\nimport { isConnectorReply } from 'common';\nimport { quickReplyViaBrain } from '../services/brainQuickReply';\nimport { MESSENGER_ACTIVE_STREAM_QUERY, stripModelCostHeader } from './messengerStream';\nimport {\n CHAT_RUN_TIMEOUT_TEXT,\n CDECLI_MAX_RUN_MS,\n EMPTY_CHAT_RESPONSE_TEXT,\n resolveAssistantReply,\n} from '../features/chat/emptyChatResponse';\nimport { isFastLaneRunId, splitPreResponse } from '../features/chat/preResponse';\nimport {\n useGatewaySendMessageMutation,\n useGatewayInboundMessageByChannelSubscription,\n useMessengerStreamDeltaSubscription,\n} from 'common/graphql';\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` or `CDECLI_FAST_LANE=pre`.\n * Shown as a Quick answer strip — never the main bubble. Pass the full\n * text so far (not a delta). Do not pass `''` to hide it when the agent\n * starts; web keeps the strip beside the agent reply.\n */\n onQuickReply?: (text: string) => void;\n /**\n * The agent started a new run (typically after the fast lane). Clear the\n * main bubble so the agent's tokens do not append onto the provisional\n * article.\n */\n onStreamRestart?: () => 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; chatId: string } | 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 const quickReplyShownRef = useRef(false);\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, asFastLane = false) => {\n const stripped = stripModelCostHeader(accumulated);\n const { preResponse, text: fullText } = splitPreResponse(stripped);\n if (preResponse) {\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(preResponse);\n }\n if (asFastLane) {\n const fast = (preResponse || fullText).trim();\n if (fast) {\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(fast);\n }\n return;\n }\n const newChunk = fullText.substring(accumulatedLenRef.current);\n accumulatedLenRef.current = fullText.length;\n if (newChunk) cbRef.current.onChunk(newChunk);\n }, []);\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 const maxRunTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const runStartedAtRef = useRef<number | null>(null);\n const stoppingRef = useRef(false);\n\n const streamSkip = !channelId;\n const streamChannelId = channelId || accountId;\n\n const clearRunTimers = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n if (maxRunTimerRef.current) {\n clearTimeout(maxRunTimerRef.current);\n maxRunTimerRef.current = null;\n }\n }, []);\n\n const stopTurn = useCallback(\n (reason: 'idle' | 'max-run') => {\n const pending = pendingRef.current;\n if (!pending || stoppingRef.current) return;\n stoppingRef.current = true;\n const chatId = pending.chatId;\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n streamCompletedRef.current = true;\n runStartedAtRef.current = null;\n clearRunTimers();\n void sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text: '',\n metadata: { cancel: true },\n },\n },\n }).catch(() => {\n /* local stop still lands a standard reply */\n });\n cbRef.current.onComplete(reason === 'max-run' ? CHAT_RUN_TIMEOUT_TEXT : EMPTY_CHAT_RESPONSE_TEXT);\n stoppingRef.current = false;\n },\n [accountId, clearRunTimers, sendMutation],\n );\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(() => stopTurn('idle'), CDECLI_RESPONSE_TIMEOUT_MS);\n }, [stopTurn]);\n\n /** Hard 2-minute cap from send start — still-streaming turns are cancelled too. */\n const armMaxRun = useCallback(() => {\n if (maxRunTimerRef.current) clearTimeout(maxRunTimerRef.current);\n const started = runStartedAtRef.current;\n if (!pendingRef.current || started == null) {\n maxRunTimerRef.current = null;\n return;\n }\n const remaining = CDECLI_MAX_RUN_MS - (Date.now() - started);\n maxRunTimerRef.current = setTimeout(() => stopTurn('max-run'), Math.max(0, remaining));\n }, [stopTurn]);\n\n // Pause JS timers in the background; on resume re-arm from wall-clock start\n // so a 2-minute run that finished while away is stopped immediately.\n useEffect(() => {\n const sub = AppState.addEventListener('change', (next) => {\n if (next === 'active') {\n if (pendingRef.current && !streamCompletedRef.current) {\n armTimeout();\n armMaxRun();\n }\n } else {\n clearRunTimers();\n }\n });\n return () => sub.remove();\n }, [armMaxRun, armTimeout, clearRunTimers]);\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 const prevRunId = lastRunIdRef.current;\n if (delta.runId && delta.runId !== prevRunId) {\n accumulatedLenRef.current = 0;\n // Fast-lane run (`fast-…`) then agent (`serve-…`): start a fresh\n // bubble instead of appending the agent onto the provisional article.\n if (prevRunId && !isFastLaneRunId(delta.runId)) {\n cbRef.current.onStreamRestart?.();\n }\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 hasReceivedDeltasRef.current = true;\n armTimeout();\n ingestAccumulatedText(delta.text ?? '', isFastLaneRunId(delta.runId));\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: streamSkip,\n onData: ({ data }) => {\n const msg = data?.data?.gatewayInboundMessageByChannel;\n if (!msg) return;\n if (streamCompletedRef.current) return;\n const sanitizedText = stripModelCostHeader(msg.text ?? '');\n const { preResponse, text: finalText } = splitPreResponse(sanitizedText);\n if (preResponse) cbRef.current.onQuickReply?.(preResponse);\n // Fast-lane-only inbound: keep waiting for the agent complete.\n if (msg.text && !finalText.trim() && preResponse) return;\n if (!finalText.trim() && !pendingRef.current && !hasReceivedDeltasRef.current) return;\n\n if (finalText.trim() && !hasReceivedDeltasRef.current) {\n cbRef.current.onChunk(finalText);\n }\n\n cbRef.current.onComplete(resolveAssistantReply(finalText));\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 runStartedAtRef.current = null;\n clearRunTimers();\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(), chatId };\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 // Never run it for media turns — the brain only sees `text`, so\n // \"what is this\" + a photo becomes a generic \"what are you referring to\".\n // Nor for a connect card's `[CONNECTOR_CONNECTED:…]` / `[CONNECTOR_DECLINED:…]`\n // reply: it answers the agent's parked request and asks nothing, so a brain\n // \"answer\" would paint a bogus bubble over the agent's real follow-up.\n const turn = ++turnSeqRef.current;\n if (media.length === 0 && !isConnectorReply(text)) {\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\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 runStartedAtRef.current = null;\n clearRunTimers();\n return false;\n }\n\n runStartedAtRef.current = Date.now();\n armTimeout();\n armMaxRun();\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 runStartedAtRef.current = null;\n clearRunTimers();\n return false;\n }\n },\n [isConnected, accountId, channelId, sendMutation, model, skill, armTimeout, armMaxRun, clearRunTimers],\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 runStartedAtRef.current = null;\n clearRunTimers();\n },\n [clearRunTimers],\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 runStartedAtRef.current = null;\n clearRunTimers();\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 const errMsg = payload?.error || 'CDeCLI cancel failed';\n // Trash-clear / greet flows cancel with nothing running — expected.\n if (!isNoActiveSessionError(errMsg)) {\n console.warn('[useCdecliChannel] cancel failed:', errMsg);\n }\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, clearRunTimers],\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 { useApolloClient } from '@apollo/client';\nimport { isConnectorReply } from 'common';\nimport { quickReplyViaBrain } from '../services/brainQuickReply';\nimport { MESSENGER_ACTIVE_STREAM_QUERY, stripModelCostHeader } from './messengerStream';\nimport {\n CHAT_RUN_TIMEOUT_TEXT,\n CDECLI_MAX_RUN_MS,\n EMPTY_CHAT_RESPONSE_TEXT,\n resolveAssistantReply,\n} from '../features/chat/emptyChatResponse';\nimport { isFastLaneRunId, splitPreResponse } from '../features/chat/preResponse';\nimport {\n useGatewaySendMessageMutation,\n useGatewayInboundMessageByChannelSubscription,\n useMessengerStreamDeltaSubscription,\n} from 'common/graphql';\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\n/**\n * What became of a message sent while a turn was running:\n * - `queued`: the gateway queued it for the agent, which answers it when the\n * running turn ends. `id` cancels it (`cancelQueued`).\n * - `answered`: the gateway answered it on the spot (a status question) with a\n * notice that arrives through `onNotice`.\n * - `started`: the running turn had already ended, so the agent took it right\n * away; its answer arrives as a follow-up.\n */\nexport type QueuedSendResult =\n | { status: 'queued'; id: string; error?: undefined }\n | { status: 'answered' | 'started'; id?: undefined; error?: undefined }\n | { status: 'failed'; id?: undefined; error: string };\n\n/** The gateway's send-result ids for a message it queued or answered itself. */\nconst QUEUED_EXTERNAL_ID_PREFIX = 'handoff-queued-';\nconst STATUS_EXTERNAL_ID_PREFIX = 'handoff-status-';\n/** Messages from the gateway itself (a status answer, a long-wait report). */\nconst GATEWAY_NOTICE_ID_PREFIX = 'handoff-';\n\nexport interface CdecliChannelCallbacks {\n onChunk: (text: string) => void;\n /**\n * A fast provisional answer from `yantra-brain` or `CDECLI_FAST_LANE=pre`.\n * Shown as a Quick answer strip — never the main bubble. Pass the full\n * text so far (not a delta). Do not pass `''` to hide it when the agent\n * starts; web keeps the strip beside the agent reply.\n */\n onQuickReply?: (text: string) => void;\n /**\n * The agent started a new run (typically after the fast lane). Clear the\n * main bubble so the agent's tokens do not append onto the provisional\n * article.\n */\n onStreamRestart?: () => void;\n onComplete: (text: string) => void;\n onError: (error: string) => void;\n /**\n * A message from the gateway about queued work (a status answer, a\n * long-wait report) that arrived while a reply is still streaming. It is\n * shown beside that reply and does NOT end it.\n */\n onNotice?: (text: 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; chatId: string } | 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 const quickReplyShownRef = useRef(false);\n /**\n * The run the last finished turn streamed on. After a turn ends the agent\n * can still speak with no send behind it - the gateway runs a request it\n * queued while the agent was busy and follows up with the answer. That\n * arrives as a NEW run (or a new final message) and must open a round of\n * its own; a late delta from THIS run must not.\n */\n const completedRunIdRef = useRef<string | null>(null);\n /** Final messages already delivered, so a redelivery never paints twice. */\n const deliveredInboundRef = useRef<string[]>([]);\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, asFastLane = false) => {\n const stripped = stripModelCostHeader(accumulated);\n const { preResponse, text: fullText } = splitPreResponse(stripped);\n if (preResponse) {\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(preResponse);\n }\n if (asFastLane) {\n const fast = (preResponse || fullText).trim();\n if (fast) {\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(fast);\n }\n return;\n }\n const newChunk = fullText.substring(accumulatedLenRef.current);\n accumulatedLenRef.current = fullText.length;\n if (newChunk) cbRef.current.onChunk(newChunk);\n }, []);\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 const maxRunTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const runStartedAtRef = useRef<number | null>(null);\n const stoppingRef = useRef(false);\n\n const streamSkip = !channelId;\n const streamChannelId = channelId || accountId;\n\n const clearRunTimers = useCallback(() => {\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n if (maxRunTimerRef.current) {\n clearTimeout(maxRunTimerRef.current);\n maxRunTimerRef.current = null;\n }\n }, []);\n\n const stopTurn = useCallback(\n (reason: 'idle' | 'max-run') => {\n const pending = pendingRef.current;\n if (!pending || stoppingRef.current) return;\n stoppingRef.current = true;\n const chatId = pending.chatId;\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n streamCompletedRef.current = true;\n completedRunIdRef.current = lastRunIdRef.current;\n runStartedAtRef.current = null;\n clearRunTimers();\n void sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text: '',\n metadata: { cancel: true },\n },\n },\n }).catch(() => {\n /* local stop still lands a standard reply */\n });\n cbRef.current.onComplete(reason === 'max-run' ? CHAT_RUN_TIMEOUT_TEXT : EMPTY_CHAT_RESPONSE_TEXT);\n stoppingRef.current = false;\n },\n [accountId, clearRunTimers, sendMutation],\n );\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(() => stopTurn('idle'), CDECLI_RESPONSE_TIMEOUT_MS);\n }, [stopTurn]);\n\n /** Hard 2-minute cap from send start — still-streaming turns are cancelled too. */\n const armMaxRun = useCallback(() => {\n if (maxRunTimerRef.current) clearTimeout(maxRunTimerRef.current);\n const started = runStartedAtRef.current;\n if (!pendingRef.current || started == null) {\n maxRunTimerRef.current = null;\n return;\n }\n const remaining = CDECLI_MAX_RUN_MS - (Date.now() - started);\n maxRunTimerRef.current = setTimeout(() => stopTurn('max-run'), Math.max(0, remaining));\n }, [stopTurn]);\n\n // Pause JS timers in the background; on resume re-arm from wall-clock start\n // so a 2-minute run that finished while away is stopped immediately.\n useEffect(() => {\n const sub = AppState.addEventListener('change', (next) => {\n if (next === 'active') {\n if (pendingRef.current && !streamCompletedRef.current) {\n armTimeout();\n armMaxRun();\n }\n } else {\n clearRunTimers();\n }\n });\n return () => sub.remove();\n }, [armMaxRun, armTimeout, clearRunTimers]);\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 // A new run after the turn finished, with no send behind it, is the\n // agent following up. Open a round for it rather than drop it until\n // the user happens to send again.\n if (\n streamCompletedRef.current &&\n delta.runId &&\n delta.runId !== completedRunIdRef.current &&\n !isFastLaneRunId(delta.runId)\n ) {\n streamCompletedRef.current = false;\n hasReceivedDeltasRef.current = false;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n }\n\n const prevRunId = lastRunIdRef.current;\n if (delta.runId && delta.runId !== prevRunId) {\n accumulatedLenRef.current = 0;\n // Fast-lane run (`fast-…`) then agent (`serve-…`): start a fresh\n // bubble instead of appending the agent onto the provisional article.\n if (prevRunId && !isFastLaneRunId(delta.runId)) {\n cbRef.current.onStreamRestart?.();\n }\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 hasReceivedDeltasRef.current = true;\n armTimeout();\n ingestAccumulatedText(delta.text ?? '', isFastLaneRunId(delta.runId));\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: streamSkip,\n onData: ({ data }) => {\n const msg = data?.data?.gatewayInboundMessageByChannel;\n if (!msg) return;\n const externalId = msg.externalId ?? '';\n if (externalId && deliveredInboundRef.current.includes(externalId)) return;\n const sanitizedText = stripModelCostHeader(msg.text ?? '');\n const { preResponse, text: finalText } = splitPreResponse(sanitizedText);\n // The gateway speaking about queued work (a status answer, a\n // long-wait report) while a reply is still on its way belongs beside\n // that reply. Taken as the round's final message it would cut the\n // reply off, and the rest of its stream would be dropped.\n if (\n externalId.startsWith(GATEWAY_NOTICE_ID_PREFIX) &&\n finalText.trim() &&\n !streamCompletedRef.current &&\n (pendingRef.current || hasReceivedDeltasRef.current)\n ) {\n deliveredInboundRef.current = [...deliveredInboundRef.current.slice(-49), externalId];\n cbRef.current.onNotice?.(finalText.trim());\n return;\n }\n if (streamCompletedRef.current) {\n // A final message after the turn finished that did not stream -\n // e.g. the gateway's word on a message it queued, or a queued\n // request's answer - is a follow-up of its own. Only a message\n // this hook has not delivered yet can be one.\n if (!externalId || !finalText.trim()) return;\n streamCompletedRef.current = false;\n hasReceivedDeltasRef.current = false;\n accumulatedLenRef.current = 0;\n }\n if (preResponse) cbRef.current.onQuickReply?.(preResponse);\n // Fast-lane-only inbound: keep waiting for the agent complete.\n if (msg.text && !finalText.trim() && preResponse) return;\n if (!finalText.trim() && !pendingRef.current && !hasReceivedDeltasRef.current) return;\n\n if (finalText.trim() && !hasReceivedDeltasRef.current) {\n cbRef.current.onChunk(finalText);\n }\n\n cbRef.current.onComplete(resolveAssistantReply(finalText));\n if (externalId) deliveredInboundRef.current = [...deliveredInboundRef.current.slice(-49), externalId];\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = true;\n completedRunIdRef.current = lastRunIdRef.current;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n runStartedAtRef.current = null;\n clearRunTimers();\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(), chatId };\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 // Never run it for media turns — the brain only sees `text`, so\n // \"what is this\" + a photo becomes a generic \"what are you referring to\".\n // Nor for a connect card's `[CONNECTOR_CONNECTED:…]` / `[CONNECTOR_DECLINED:…]`\n // reply: it answers the agent's parked request and asks nothing, so a brain\n // \"answer\" would paint a bogus bubble over the agent's real follow-up.\n const turn = ++turnSeqRef.current;\n if (media.length === 0 && !isConnectorReply(text)) {\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\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 runStartedAtRef.current = null;\n clearRunTimers();\n return false;\n }\n\n runStartedAtRef.current = Date.now();\n armTimeout();\n armMaxRun();\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 runStartedAtRef.current = null;\n clearRunTimers();\n return false;\n }\n },\n [isConnected, accountId, channelId, sendMutation, model, skill, armTimeout, armMaxRun, clearRunTimers],\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 runStartedAtRef.current = null;\n clearRunTimers();\n },\n [clearRunTimers],\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 * Send a message while a turn is running. The gateway queues it for the\n * agent, which answers it when the running turn ends - the way Claude Code\n * and Cursor queue a message typed mid-turn. `queueAware` tells the gateway\n * this client shows the queue itself, so no \"Got it\" reply comes back.\n *\n * Unlike `sendMessage` this touches none of the in-flight round's refs: the\n * running reply keeps streaming, and the queued message's answer arrives\n * later as a follow-up round of its own.\n */\n const sendQueued = useCallback(\n async (\n text: string,\n chatId = 'messenger',\n media: Array<{ type: string; url: string; data?: string; mimeType?: string; filename?: string }> = [],\n ): Promise<QueuedSendResult> => {\n if (!isConnected) return { status: 'failed', 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 ...(media.length > 0 ? { media: media as never } : {}),\n metadata: { queueAware: true, ...(model && { model }), ...(skill && { skill }) },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) return { status: 'failed', error: payload?.error || 'CDeCLI send failed' };\n const externalId = payload.externalId ?? '';\n if (externalId.startsWith(QUEUED_EXTERNAL_ID_PREFIX)) {\n return { status: 'queued', id: externalId.slice(QUEUED_EXTERNAL_ID_PREFIX.length) };\n }\n if (externalId.startsWith(STATUS_EXTERNAL_ID_PREFIX)) return { status: 'answered' };\n return { status: 'started' };\n } catch (err) {\n return { status: 'failed', error: err instanceof Error ? err.message : String(err) };\n }\n },\n [isConnected, accountId, sendMutation, model, skill],\n );\n\n /** Take one queued message back before the agent gets to it. */\n const cancelQueued = useCallback(\n async (id: 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: { channelType: 'cdecli-serve', accountId, chatId, text: '', metadata: { dequeue: id } },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n return payload?.success\n ? { ok: true }\n : { ok: false, error: payload?.error || 'Could not cancel that message.' };\n } catch (err) {\n return { ok: false, error: err instanceof Error ? err.message : String(err) };\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 runStartedAtRef.current = null;\n // The stopped run is over for this client: a delta it still emits\n // must not reopen as a follow-up. The next run (a queued message the\n // agent now takes) does, since its runId differs.\n if (hasReceivedDeltasRef.current || lastRunIdRef.current) {\n streamCompletedRef.current = true;\n completedRunIdRef.current = lastRunIdRef.current;\n hasReceivedDeltasRef.current = false;\n }\n clearRunTimers();\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 const errMsg = payload?.error || 'CDeCLI cancel failed';\n // Trash-clear / greet flows cancel with nothing running — expected.\n if (!isNoActiveSessionError(errMsg)) {\n console.warn('[useCdecliChannel] cancel failed:', errMsg);\n }\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, clearRunTimers],\n );\n\n return { sendMessage, sendQueued, cancelQueued, steer, cancel };\n}\n"],"names":["_a"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useState,useRef,useCallback,useEffect,useMemo,useSyncExternalStore}from'react';import {useCreateAccountChatFileUploadLinksMutation,useCreateAccountChatFileDownloadLinksMutation}from'common/graphql';import {markChannelHistoryRunning,useChatMutations,useChatMessages,clearChannelHistoryRunning,patchChannelHistoryPreview}from'./useChatApi.js';import {useCdecliChannel
|
|
1
|
+
import {useState,useRef,useCallback,useEffect,useMemo,useSyncExternalStore}from'react';import {useCreateAccountChatFileUploadLinksMutation,useCreateAccountChatFileDownloadLinksMutation}from'common/graphql';import {markChannelHistoryRunning,useChatMutations,useChatMessages,clearChannelHistoryRunning,patchChannelHistoryPreview}from'./useChatApi.js';import {useCdecliChannel}from'./useCdecliChannel.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {isAgentJwtRejected,shouldInlineGatewayMediaForHostedAgent}from'./systemTokenJwt.js';import {config}from'../config/env-config.js';import {queueGatewayMessage,MAX_QUEUED_MESSAGES,dropQueuedGatewayMessage,queuedGatewayMessages,subscribeQueuedGatewayMessages,restoreQueuedGatewayMessages,clearQueuedGatewayMessages}from'../services/queuedGatewayMessages.js';import {isImageAttachment}from'../features/attachments/isImageAttachment.js';import {buildGatewayMedia,buildGatewayMediaPreferUrls}from'../features/attachments/buildGatewayMedia.js';import {preserveAttachmentPreviews}from'../features/attachments/preserveAttachmentPreviews.js';import {releasePreviewBytes}from'../features/attachments/releasePreviewBytes.js';import {resolveAssistantReply}from'../features/chat/emptyChatResponse.js';import {appendOrReplaceAssistant,appendFollowUpAssistant,mergeOptimisticGatewayMessages,collapseConsecutiveAssistants}from'../state/chatThreadMerge.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -63,10 +63,9 @@ function useChatStream(sessionId, routing = {
|
|
|
63
63
|
const [isStreaming, setIsStreaming] = useState(false);
|
|
64
64
|
const [error, setError] = useState(null);
|
|
65
65
|
const isSendingRef = useRef(false);
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const
|
|
69
|
-
});
|
|
66
|
+
const [queuedFollowUps, setQueuedFollowUps] = useState([]);
|
|
67
|
+
const queuedFollowUpsRef = useRef([]);
|
|
68
|
+
const [queueNotice, setQueueNotice] = useState(null);
|
|
70
69
|
const sendMessageRef = useRef(async () => "failed");
|
|
71
70
|
const [cdecliStreamOverrideId, setCdecliStreamOverrideId] = useState(void 0);
|
|
72
71
|
const userMsgForSaveRef = useRef(null);
|
|
@@ -74,6 +73,7 @@ function useChatStream(sessionId, routing = {
|
|
|
74
73
|
const sessionIdRef = useRef(sessionId);
|
|
75
74
|
sessionIdRef.current = sessionId || cdecliStreamOverrideId || null;
|
|
76
75
|
const cdecliRoundHandledRef = useRef(false);
|
|
76
|
+
const followUpRoundRef = useRef(false);
|
|
77
77
|
const authRetryUsedRef = useRef(false);
|
|
78
78
|
const recoveringAuthRef = useRef(false);
|
|
79
79
|
const routingRef = useRef(routing);
|
|
@@ -124,7 +124,8 @@ function useChatStream(sessionId, routing = {
|
|
|
124
124
|
clearPendingStream();
|
|
125
125
|
saveSessionIdRef.current = null;
|
|
126
126
|
userMsgForSaveRef.current = null;
|
|
127
|
-
|
|
127
|
+
followUpRoundRef.current = false;
|
|
128
|
+
setQueueNotice(null);
|
|
128
129
|
setResponse("");
|
|
129
130
|
setQuickReply("");
|
|
130
131
|
setIsLoading(false);
|
|
@@ -143,6 +144,7 @@ function useChatStream(sessionId, routing = {
|
|
|
143
144
|
onChunk: (text) => {
|
|
144
145
|
if (!isSendingRef.current) {
|
|
145
146
|
isSendingRef.current = true;
|
|
147
|
+
followUpRoundRef.current = true;
|
|
146
148
|
cdecliRoundHandledRef.current = false;
|
|
147
149
|
saveSessionIdRef.current = sessionIdRef.current;
|
|
148
150
|
setIsLoading(true);
|
|
@@ -150,23 +152,30 @@ function useChatStream(sessionId, routing = {
|
|
|
150
152
|
setError(null);
|
|
151
153
|
const sid = sessionIdRef.current;
|
|
152
154
|
if (sid) markChannelHistoryRunning(sid, text);
|
|
155
|
+
const started = queuedFollowUpsRef.current;
|
|
156
|
+
if (started.length > 0) {
|
|
157
|
+
queuedFollowUpsRef.current = [];
|
|
158
|
+
setQueuedFollowUps([]);
|
|
159
|
+
setMessages((prev) => [...prev, ...started.map((q) => ({
|
|
160
|
+
role: "user",
|
|
161
|
+
content: q.text,
|
|
162
|
+
attachments: q.attachments
|
|
163
|
+
}))]);
|
|
164
|
+
}
|
|
153
165
|
}
|
|
154
166
|
pendingStreamRef.current += text;
|
|
155
|
-
if (!sessionLiveRef.current) {
|
|
156
|
-
sessionLiveRef.current = true;
|
|
157
|
-
void flushPendingSteersRef.current();
|
|
158
|
-
}
|
|
159
167
|
if (streamFlushTimerRef.current == null) {
|
|
160
168
|
streamFlushTimerRef.current = setTimeout(flushPendingStream, 50);
|
|
161
169
|
}
|
|
162
170
|
},
|
|
163
171
|
// The brain's provisional answer is the WHOLE reply-so-far, not a chunk:
|
|
164
172
|
// it is set outright, and set to '' when the real stream supersedes it.
|
|
165
|
-
// It deliberately does not mark the session live - steers wait for the
|
|
166
|
-
// agent, which is the only side that can act on them.
|
|
167
173
|
onQuickReply: (text) => {
|
|
168
174
|
setQuickReply(text);
|
|
169
175
|
},
|
|
176
|
+
onNotice: (text) => {
|
|
177
|
+
setQueueNotice(text);
|
|
178
|
+
},
|
|
170
179
|
onStreamRestart: () => {
|
|
171
180
|
clearPendingStream();
|
|
172
181
|
pendingStreamRef.current = "";
|
|
@@ -183,7 +192,6 @@ function useChatStream(sessionId, routing = {
|
|
|
183
192
|
if (await tryRecoverAndResendRef.current(text)) return;
|
|
184
193
|
if (cdecliRoundHandledRef.current) return;
|
|
185
194
|
cdecliRoundHandledRef.current = true;
|
|
186
|
-
pendingSteersRef.current = [];
|
|
187
195
|
setError(isAgentJwtRejected(text) ? text : "CDeCLI agent error. Check gateway status and server-side LLM configuration.");
|
|
188
196
|
finalizeCdecliRound(isAgentJwtRejected(text) ? void 0 : text);
|
|
189
197
|
})();
|
|
@@ -194,15 +202,16 @@ function useChatStream(sessionId, routing = {
|
|
|
194
202
|
const reply = resolveAssistantReply(text);
|
|
195
203
|
const sid = saveSessionIdRef.current;
|
|
196
204
|
const um = userMsgForSaveRef.current;
|
|
197
|
-
const
|
|
198
|
-
pendingSteersRef.current = [];
|
|
205
|
+
const followUp = followUpRoundRef.current;
|
|
199
206
|
saveSessionIdRef.current = null;
|
|
200
207
|
userMsgForSaveRef.current = null;
|
|
201
|
-
|
|
202
|
-
|
|
208
|
+
followUpRoundRef.current = false;
|
|
209
|
+
setQueueNotice(null);
|
|
210
|
+
const assistant = {
|
|
203
211
|
role: "assistant",
|
|
204
212
|
content: reply
|
|
205
|
-
}
|
|
213
|
+
};
|
|
214
|
+
setMessages((prev) => followUp ? appendFollowUpAssistant(prev, assistant) : appendOrReplaceAssistant(prev, assistant));
|
|
206
215
|
const previewSid = sid || sessionIdRef.current;
|
|
207
216
|
if (previewSid) {
|
|
208
217
|
clearChannelHistoryRunning(previewSid);
|
|
@@ -241,13 +250,6 @@ function useChatStream(sessionId, routing = {
|
|
|
241
250
|
console.error("[useChatStream] saveMessages (cdecli):", e);
|
|
242
251
|
});
|
|
243
252
|
}
|
|
244
|
-
if (leftover.length > 0) {
|
|
245
|
-
const [first, ...rest] = leftover;
|
|
246
|
-
pendingSteersRef.current = rest;
|
|
247
|
-
queueMicrotask(() => {
|
|
248
|
-
void sendMessageRef.current(first, void 0, sid, true);
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
253
|
},
|
|
252
254
|
onError: (err) => {
|
|
253
255
|
if (cdecliRoundHandledRef.current) return;
|
|
@@ -255,7 +257,6 @@ function useChatStream(sessionId, routing = {
|
|
|
255
257
|
if (await tryRecoverAndResendRef.current(err)) return;
|
|
256
258
|
if (cdecliRoundHandledRef.current) return;
|
|
257
259
|
cdecliRoundHandledRef.current = true;
|
|
258
|
-
pendingSteersRef.current = [];
|
|
259
260
|
setError(err || "CDeCLI stream error.");
|
|
260
261
|
finalizeCdecliRound(resolveAssistantReply(""));
|
|
261
262
|
})();
|
|
@@ -263,29 +264,14 @@ function useChatStream(sessionId, routing = {
|
|
|
263
264
|
}), [saveMessages, accountUserId, finalizeCdecliRound, flushPendingStream, clearPendingStream]);
|
|
264
265
|
const {
|
|
265
266
|
sendMessage: sendCdecliMessage,
|
|
266
|
-
|
|
267
|
+
sendQueued: sendQueuedCdecli,
|
|
268
|
+
cancelQueued: cancelQueuedCdecli,
|
|
267
269
|
cancel: cancelCdecliTurn
|
|
268
270
|
} = useCdecliChannel(routing.channelConnected, routing.accountId, streamChannelId, cdecliCallbacks);
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
while (pendingSteersRef.current.length > 0 && sessionLiveRef.current && isSendingRef.current) {
|
|
274
|
-
const next = pendingSteersRef.current[0];
|
|
275
|
-
const result = await steerCdecliMessage(next, sid);
|
|
276
|
-
if (result.ok) {
|
|
277
|
-
pendingSteersRef.current.shift();
|
|
278
|
-
continue;
|
|
279
|
-
}
|
|
280
|
-
if (isNoActiveSessionError(result.error)) {
|
|
281
|
-
return;
|
|
282
|
-
}
|
|
283
|
-
pendingSteersRef.current.shift();
|
|
284
|
-
setError(result.error || "Could not send follow-up. Try again.");
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
}, [steerCdecliMessage]);
|
|
288
|
-
flushPendingSteersRef.current = flushPendingSteers;
|
|
271
|
+
const setQueue = useCallback((next) => {
|
|
272
|
+
queuedFollowUpsRef.current = next;
|
|
273
|
+
setQueuedFollowUps(next);
|
|
274
|
+
}, []);
|
|
289
275
|
useEffect(() => {
|
|
290
276
|
if (sessionId && cdecliStreamOverrideId && sessionId === cdecliStreamOverrideId) {
|
|
291
277
|
setCdecliStreamOverrideId(void 0);
|
|
@@ -295,9 +281,10 @@ function useChatStream(sessionId, routing = {
|
|
|
295
281
|
const prevSid = lastHydratedSessionIdRef.current;
|
|
296
282
|
if (prevSid && sessionId && prevSid !== sessionId && isSendingRef.current) {
|
|
297
283
|
clearPendingStream();
|
|
298
|
-
|
|
284
|
+
setQueue([]);
|
|
285
|
+
setQueueNotice(null);
|
|
299
286
|
isSendingRef.current = false;
|
|
300
|
-
|
|
287
|
+
followUpRoundRef.current = false;
|
|
301
288
|
cdecliRoundHandledRef.current = false;
|
|
302
289
|
saveSessionIdRef.current = null;
|
|
303
290
|
userMsgForSaveRef.current = null;
|
|
@@ -356,7 +343,7 @@ function useChatStream(sessionId, routing = {
|
|
|
356
343
|
setQuickReply("");
|
|
357
344
|
}
|
|
358
345
|
}
|
|
359
|
-
}, [sessionId, backendMessages, backendMessagesLoaded, clearPendingStream]);
|
|
346
|
+
}, [sessionId, backendMessages, backendMessagesLoaded, clearPendingStream, setQueue]);
|
|
360
347
|
const sendMessage = useCallback(async (content, attachments, sessionIdOverride, alreadyVisible = false, queuedId = void 0) => {
|
|
361
348
|
var _a;
|
|
362
349
|
if (!content.trim() && !(attachments == null ? void 0 : attachments.length)) return "failed";
|
|
@@ -394,15 +381,30 @@ function useChatStream(sessionId, routing = {
|
|
|
394
381
|
setError("Missing chat session id.");
|
|
395
382
|
return "failed";
|
|
396
383
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
384
|
+
const media2 = (attachments == null ? void 0 : attachments.length) ? buildGatewayMedia(attachments, {
|
|
385
|
+
includeData: true
|
|
386
|
+
}) : [];
|
|
387
|
+
const outcome = await sendQueuedCdecli(sendText, effectiveSessionId, media2);
|
|
388
|
+
if (outcome.status === "failed") {
|
|
389
|
+
setError(outcome.error || "Could not send the message. Try again.");
|
|
390
|
+
return "failed";
|
|
391
|
+
}
|
|
400
392
|
if (queuedId) dropQueuedGatewayMessage(queuedId);
|
|
401
|
-
|
|
393
|
+
setError(null);
|
|
394
|
+
if (outcome.status === "queued") {
|
|
395
|
+
setQueue([...queuedFollowUpsRef.current, {
|
|
396
|
+
id: outcome.id,
|
|
397
|
+
text: sendText,
|
|
398
|
+
attachments: releasePreviewBytes(attachments)
|
|
399
|
+
}]);
|
|
400
|
+
if (alreadyVisible) setMessages((prev) => withoutTrailingUserMessage(prev, sendText));
|
|
401
|
+
} else if (!alreadyVisible) {
|
|
402
|
+
setMessages((prev) => [...prev, userMessage]);
|
|
403
|
+
}
|
|
402
404
|
return "sent";
|
|
403
405
|
}
|
|
404
406
|
isSendingRef.current = true;
|
|
405
|
-
|
|
407
|
+
followUpRoundRef.current = false;
|
|
406
408
|
ignoreHydrationRef.current = false;
|
|
407
409
|
if (effectiveSessionId && sendText) {
|
|
408
410
|
titlePatchedChannelsRef.current.add(effectiveSessionId);
|
|
@@ -492,7 +494,7 @@ function useChatStream(sessionId, routing = {
|
|
|
492
494
|
}
|
|
493
495
|
if (ok && queuedId) dropQueuedGatewayMessage(queuedId);
|
|
494
496
|
return ok ? "sent" : "failed";
|
|
495
|
-
}, [sessionId, sendCdecliMessage, patchChannelTitle,
|
|
497
|
+
}, [sessionId, sendCdecliMessage, patchChannelTitle, sendQueuedCdecli, setQueue, createAccountChatFileUploadLinksMutation, createAccountChatFileDownloadLinksMutation]);
|
|
496
498
|
sendMessageRef.current = sendMessage;
|
|
497
499
|
const queuedChannelId = sessionId || cdecliStreamOverrideId || null;
|
|
498
500
|
const queuedSnapshot = useCallback(() => queuedGatewayMessages(queuedChannelId, routing.accountId), [queuedChannelId, routing.accountId]);
|
|
@@ -566,8 +568,6 @@ function useChatStream(sessionId, routing = {
|
|
|
566
568
|
const cancel = useCallback(() => {
|
|
567
569
|
var _a;
|
|
568
570
|
clearPendingStream();
|
|
569
|
-
pendingSteersRef.current = [];
|
|
570
|
-
sessionLiveRef.current = false;
|
|
571
571
|
const sid = (_a = saveSessionIdRef.current) != null ? _a : sessionId;
|
|
572
572
|
void cancelCdecliTurn(sid != null ? sid : "messenger");
|
|
573
573
|
if (!cdecliRoundHandledRef.current) {
|
|
@@ -578,10 +578,8 @@ function useChatStream(sessionId, routing = {
|
|
|
578
578
|
const clearMessages = useCallback(() => {
|
|
579
579
|
var _a;
|
|
580
580
|
clearPendingStream();
|
|
581
|
-
|
|
582
|
-
const hadActiveTurn = isSendingRef.current || sessionLiveRef.current;
|
|
581
|
+
const hadActiveTurn = isSendingRef.current;
|
|
583
582
|
const sid = (_a = saveSessionIdRef.current) != null ? _a : sessionId;
|
|
584
|
-
sessionLiveRef.current = false;
|
|
585
583
|
isSendingRef.current = false;
|
|
586
584
|
cdecliRoundHandledRef.current = true;
|
|
587
585
|
saveSessionIdRef.current = null;
|
|
@@ -591,6 +589,9 @@ function useChatStream(sessionId, routing = {
|
|
|
591
589
|
}
|
|
592
590
|
ignoreHydrationRef.current = true;
|
|
593
591
|
if (sid) clearQueuedGatewayMessages(sid);
|
|
592
|
+
for (const queued of queuedFollowUpsRef.current) void cancelQueuedCdecli(queued.id, sid != null ? sid : "messenger");
|
|
593
|
+
setQueue([]);
|
|
594
|
+
setQueueNotice(null);
|
|
594
595
|
setMessages([]);
|
|
595
596
|
setResponse("");
|
|
596
597
|
setQuickReply("");
|
|
@@ -598,7 +599,20 @@ function useChatStream(sessionId, routing = {
|
|
|
598
599
|
setIsLoading(false);
|
|
599
600
|
setIsStreaming(false);
|
|
600
601
|
if (sid) clearChannelHistoryRunning(sid);
|
|
601
|
-
}, [cancelCdecliTurn, clearPendingStream, sessionId]);
|
|
602
|
+
}, [cancelCdecliTurn, cancelQueuedCdecli, clearPendingStream, sessionId, setQueue]);
|
|
603
|
+
const cancelQueuedFollowUp = useCallback(async (id) => {
|
|
604
|
+
const sid = sessionIdRef.current;
|
|
605
|
+
if (!sid) return;
|
|
606
|
+
const result = await cancelQueuedCdecli(id, sid);
|
|
607
|
+
if (result.ok) {
|
|
608
|
+
setQueue(queuedFollowUpsRef.current.filter((q) => q.id !== id));
|
|
609
|
+
} else {
|
|
610
|
+
setError(result.error || "Could not cancel that message.");
|
|
611
|
+
}
|
|
612
|
+
}, [cancelQueuedCdecli, setQueue]);
|
|
613
|
+
const sendQueuedNow = useCallback(() => {
|
|
614
|
+
if (isSendingRef.current) cancel();
|
|
615
|
+
}, [cancel]);
|
|
602
616
|
const messagesLoading = !!sessionId && backendMessagesLoading && !backendMessagesLoaded;
|
|
603
617
|
return {
|
|
604
618
|
messages,
|
|
@@ -608,12 +622,18 @@ function useChatStream(sessionId, routing = {
|
|
|
608
622
|
isLoading,
|
|
609
623
|
isStreaming,
|
|
610
624
|
messagesLoading,
|
|
611
|
-
hasMessages: messages.length > 0 || !!response || !!quickReply || queuedMessages.length > 0,
|
|
625
|
+
hasMessages: messages.length > 0 || !!response || !!quickReply || queuedMessages.length > 0 || queuedFollowUps.length > 0,
|
|
612
626
|
/** Turns waiting on the gateway, oldest first. Rendered as queued bubbles. */
|
|
613
627
|
queuedMessages,
|
|
614
628
|
/** Send the queue now (the notice's retry), independent of the reconnect edge. */
|
|
615
629
|
flushQueuedMessages,
|
|
616
630
|
discardQueuedMessages,
|
|
631
|
+
/** Sent while the agent was busy and queued for it, oldest first. */
|
|
632
|
+
queuedFollowUps,
|
|
633
|
+
/** The gateway's latest word on the running work, while it runs. */
|
|
634
|
+
queueNotice,
|
|
635
|
+
cancelQueuedFollowUp,
|
|
636
|
+
sendQueuedNow,
|
|
617
637
|
sendMessage,
|
|
618
638
|
cancel,
|
|
619
639
|
clearMessages
|