@adminide-stack/yantra-mobile 12.0.53-alpha.3 → 12.0.53-alpha.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import {useRef,useCallback,useEffect}from'react';import {AppState}from'react-native';import {quickReplyViaBrain}from'../services/brainQuickReply.js';import {useGatewaySendMessageMutation,useMessengerStreamDeltaSubscription,useGatewayInboundMessageByChannelSubscription}from'common/graphql';var __defProp = Object.defineProperty;
1
+ import {useRef,useCallback,useEffect}from'react';import {AppState}from'react-native';import {useApolloClient,gql}from'@apollo/client/index.js';import {quickReplyViaBrain}from'../services/brainQuickReply.js';import {useGatewaySendMessageMutation,useMessengerStreamDeltaSubscription,useGatewayInboundMessageByChannelSubscription}from'common/graphql';var __defProp = Object.defineProperty;
2
2
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
3
3
  var __hasOwnProp = Object.prototype.hasOwnProperty;
4
4
  var __propIsEnum = Object.prototype.propertyIsEnumerable;
@@ -14,6 +14,17 @@ var __spreadValues = (a, b) => {
14
14
  }
15
15
  return a;
16
16
  };
17
+ const MESSENGER_ACTIVE_STREAM_QUERY = gql`
18
+ query MessengerActiveStreamResume($channelId: String!) {
19
+ messengerActiveStream(channelId: $channelId) {
20
+ runId
21
+ seq
22
+ channelId
23
+ text
24
+ isFinal
25
+ }
26
+ }
27
+ `;
17
28
  function stripModelCostHeader(content) {
18
29
  const normalized = content.replace(/\r\n/g, "\n");
19
30
  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, "");
@@ -31,6 +42,15 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
31
42
  const reconnectedStreamRef = useRef(false);
32
43
  const streamCompletedRef = useRef(false);
33
44
  const accumulatedLenRef = useRef(0);
45
+ const lastRunIdRef = useRef(null);
46
+ const apolloClient = useApolloClient();
47
+ const seededChannelRef = useRef(null);
48
+ const ingestAccumulatedText = useCallback((accumulated) => {
49
+ const fullText = stripModelCostHeader(accumulated);
50
+ const newChunk = fullText.substring(accumulatedLenRef.current);
51
+ accumulatedLenRef.current = fullText.length;
52
+ if (newChunk) cbRef.current.onChunk(newChunk);
53
+ }, []);
34
54
  const quickReplyShownRef = useRef(false);
35
55
  const turnSeqRef = useRef(0);
36
56
  const timeoutRef = useRef(null);
@@ -74,6 +94,10 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
74
94
  const delta = (_a = data == null ? void 0 : data.data) == null ? void 0 : _a.messengerStreamDelta;
75
95
  if (!delta) return;
76
96
  if (delta.isFinal) return;
97
+ if (delta.runId && delta.runId !== lastRunIdRef.current) {
98
+ accumulatedLenRef.current = 0;
99
+ }
100
+ if (delta.runId) lastRunIdRef.current = delta.runId;
77
101
  if (streamCompletedRef.current) return;
78
102
  if (!pendingRef.current) {
79
103
  if (!reconnectedStreamRef.current) {
@@ -87,17 +111,46 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
87
111
  }
88
112
  hasReceivedDeltasRef.current = true;
89
113
  armTimeout();
90
- const fullText = stripModelCostHeader((_d = delta.text) != null ? _d : "");
91
- const newChunk = fullText.substring(accumulatedLenRef.current);
92
- accumulatedLenRef.current = fullText.length;
93
- if (newChunk) {
94
- cbRef.current.onChunk(newChunk);
95
- }
114
+ ingestAccumulatedText((_d = delta.text) != null ? _d : "");
96
115
  },
97
116
  onError: (err) => {
98
117
  console.error("[useCdecliChannel] stream delta subscription error:", err);
99
118
  }
100
119
  });
120
+ useEffect(() => {
121
+ if (streamSkip || !streamChannelId) return void 0;
122
+ if (seededChannelRef.current === streamChannelId) return void 0;
123
+ seededChannelRef.current = streamChannelId;
124
+ accumulatedLenRef.current = 0;
125
+ streamCompletedRef.current = false;
126
+ reconnectedStreamRef.current = false;
127
+ lastRunIdRef.current = null;
128
+ let cancelled = false;
129
+ apolloClient.query({
130
+ query: MESSENGER_ACTIVE_STREAM_QUERY,
131
+ variables: {
132
+ channelId: streamChannelId
133
+ },
134
+ fetchPolicy: "network-only",
135
+ errorPolicy: "all"
136
+ }).then(({
137
+ data
138
+ }) => {
139
+ if (cancelled) return;
140
+ const snapshot = data == null ? void 0 : data.messengerActiveStream;
141
+ if (!(snapshot == null ? void 0 : snapshot.text) || snapshot.isFinal) return;
142
+ if (streamCompletedRef.current) return;
143
+ if (accumulatedLenRef.current > 0) return;
144
+ reconnectedStreamRef.current = true;
145
+ hasReceivedDeltasRef.current = true;
146
+ if (snapshot.runId) lastRunIdRef.current = snapshot.runId;
147
+ ingestAccumulatedText(snapshot.text);
148
+ }).catch(() => {
149
+ });
150
+ return () => {
151
+ cancelled = true;
152
+ };
153
+ }, [streamChannelId, streamSkip, apolloClient, ingestAccumulatedText]);
101
154
  useGatewayInboundMessageByChannelSubscription({
102
155
  variables: {
103
156
  channelId: streamChannelId
@@ -123,6 +176,7 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
123
176
  reconnectedStreamRef.current = false;
124
177
  streamCompletedRef.current = true;
125
178
  accumulatedLenRef.current = 0;
179
+ lastRunIdRef.current = null;
126
180
  if (timeoutRef.current) {
127
181
  clearTimeout(timeoutRef.current);
128
182
  timeoutRef.current = null;
@@ -144,6 +198,7 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
144
198
  reconnectedStreamRef.current = false;
145
199
  streamCompletedRef.current = false;
146
200
  accumulatedLenRef.current = 0;
201
+ lastRunIdRef.current = null;
147
202
  quickReplyShownRef.current = false;
148
203
  const turn = ++turnSeqRef.current;
149
204
  void quickReplyViaBrain(text).then((answer) => {
@@ -195,6 +250,7 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
195
250
  reconnectedStreamRef.current = false;
196
251
  streamCompletedRef.current = false;
197
252
  accumulatedLenRef.current = 0;
253
+ lastRunIdRef.current = null;
198
254
  if (timeoutRef.current) {
199
255
  clearTimeout(timeoutRef.current);
200
256
  timeoutRef.current = null;
@@ -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":"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"}
@@ -311,17 +311,27 @@ function mergeRememberedHistory(channelId, patch, notify = true) {
311
311
  const next = {
312
312
  title: patch.title || existing.title,
313
313
  preview: patch.preview || existing.preview,
314
- isAttachment: (_b = patch.isAttachment) != null ? _b : existing.isAttachment
314
+ isAttachment: (_b = patch.isAttachment) != null ? _b : existing.isAttachment,
315
+ running: patch.running !== void 0 ? patch.running : existing.running
315
316
  };
316
317
  rememberedHistoryTitles.set(channelId, next);
317
- if (notify && (next.title !== existing.title || next.preview !== existing.preview)) {
318
+ if (notify && (next.title !== existing.title || next.preview !== existing.preview || next.running !== existing.running)) {
318
319
  rememberedHistoryListeners.forEach((fn) => fn());
319
320
  }
320
321
  return next;
321
322
  }
322
323
  function rememberHistoryTitle(session) {
323
324
  var _a, _b;
325
+ const existing = rememberedHistoryTitles.get(session.channelId);
324
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
+ }
325
335
  const preview = ((_b = session.preview) == null ? void 0 : _b.trim()) && !isTransientHistoryText(session.preview) ? session.preview.trim() : "";
326
336
  if (title && isTransientHistoryText(title) && !preview) return;
327
337
  if (!title && !preview) return;
@@ -398,6 +408,24 @@ function patchChannelHistoryPreview(channelId, rawPreview) {
398
408
  preview
399
409
  });
400
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
427
+ });
428
+ }
401
429
  function applyRememberedHistoryTitles(rows) {
402
430
  return rows.map((row) => {
403
431
  var _a;
@@ -406,14 +434,15 @@ function applyRememberedHistoryTitles(rows) {
406
434
  if (!mem) return row;
407
435
  const titleMissing = row.isPlaceholder || /^new chat$/i.test(row.title);
408
436
  const previewMissing = !((_a = row.preview) == null ? void 0 : _a.trim());
409
- if (!titleMissing && !previewMissing) return row;
410
437
  const nextTitle = titleMissing && mem.title ? mem.title : row.title;
411
- const nextPreview = previewMissing && mem.preview && mem.preview !== nextTitle ? mem.preview : row.preview;
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;
412
440
  return __spreadProps(__spreadValues({}, row), {
413
441
  title: nextTitle,
414
- preview: nextPreview,
442
+ preview: livePreview,
415
443
  isPlaceholder: titleMissing && mem.title ? false : row.isPlaceholder,
416
- isAttachment: mem.isAttachment || row.isAttachment
444
+ isAttachment: mem.isAttachment || row.isAttachment,
445
+ isRunning: Boolean(mem.running)
417
446
  });
418
447
  });
419
448
  }
@@ -954,4 +983,4 @@ function useChatMutations() {
954
983
  saveMessages: sendMessagesLoading
955
984
  }
956
985
  };
957
- }export{AI_ASSISTANT_CHANNELS_QUERY_VARS,HISTORY_PAGE_SIZE,HISTORY_QUERY_BASE,buildSessionFromChannel,chatHistorySessionsFromChannels,chatHistorySessionsFromMessages,enrichHistorySessionsWithUserPrompts,getChatHistoryChannelRefetchQueries,getChatHistoryMessagesQueryVariables,getHistoryChannelsQueryVariables,patchChannelHistoryPreview,patchChannelHistoryTitle,subscribeRememberedHistory,touchChannelHistoryUpdatedAt,useChatHistorySessionsFromChannels,useChatHistorySessionsFromMessages,useChatMessages,useChatMutations,usePrefetchChatHistory};//# 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