@adminide-stack/yantra-mobile 12.0.128-alpha.0 → 12.0.128-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/NavigationHeader/NavigationHeader.js +5 -5
- package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
- package/lib/compute.js +7 -3
- package/lib/compute.js.map +1 -1
- package/lib/contexts/ChatLiveStreamContext.js +2 -1
- package/lib/contexts/ChatLiveStreamContext.js.map +1 -1
- package/lib/features/apps/nativeAppNavigation.js +1 -27
- package/lib/features/apps/nativeAppNavigation.js.map +1 -1
- package/lib/features/attachments/composerAttachmentHandoff.js +4 -1
- package/lib/features/attachments/composerAttachmentHandoff.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +3 -3
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/features/shopping/chat/openShopNative.js +1 -27
- package/lib/features/shopping/chat/openShopNative.js.map +1 -1
- package/lib/hooks/useCdecliAutoConnect.js +29 -4
- package/lib/hooks/useCdecliAutoConnect.js.map +1 -1
- package/lib/hooks/useCdecliChannel.js +7 -3
- package/lib/hooks/useCdecliChannel.js.map +1 -1
- package/lib/hooks/useChatStream.js +56 -11
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/routes.json +4 -2
- package/lib/screens/Characters/index.js +9 -18
- package/lib/screens/Characters/index.js.map +1 -1
- package/lib/screens/Chat/index.js +111 -62
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/HomeScreen.js +9 -39
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/state/chatThreadMerge.js +1 -1
- package/lib/state/chatThreadMerge.js.map +1 -1
- package/package.json +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"nativeAppNavigation.js","sources":["../../../src/features/apps/nativeAppNavigation.ts"],"sourcesContent":["/**\n * Opening a native app screen for a typed ask the brain routed there (see\n * nativeAppHandoff): the params the screen reads, and the three ways in -\n * from Home, from an empty Chat, from a Chat with a conversation in it.\n */\nimport { CommonActions, StackActions } from '@react-navigation/native';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\n\n/** What the native screen is opened with: the ask goes to its agent as the screen opens. */\nexport function nativeHandoffParams(opts: {\n orgName?: string | null;\n query: string;\n files?: MessageAttachment[] | null;\n /** The empty chat the ask was typed into, which the app's agent continues. */\n channelId?: string | null;\n}): Record<string, unknown> {\n return {\n ...(opts.orgName ? { orgName: opts.orgName } : {}),\n ...(opts.channelId ? { channelId: opts.channelId } : {}),\n initialAgentQuery: opts.query,\n ...(opts.files?.length ? { initialAgentAttachments: opts.files } : {}),\n };\n}\n\n/** The slice of a React Navigation `navigation` object the hand-off needs. */\nexport interface HandoffNavigation {\n dispatch: (action: unknown) => void;\n getParent?: () => HandoffNavigation | undefined;\n getState?: () => { routeNames?: readonly string[] } | undefined;\n}\n\ntype StackState = { routes: Array<{ name: string; params?: object }>; [key: string]: unknown };\n\nfunction isBareChatRoute(name: string): boolean {\n return /(^|\\.)Chat$/.test(name);\n}\n\n/** The navigator that declares `routeName`: the screen's own, or the nearest ancestor that does. */\nfunction navigatorOwning(navigation: HandoffNavigation, routeName: string): HandoffNavigation | null {\n for (let nav: HandoffNavigation | undefined = navigation; nav; nav = nav.getParent?.()) {\n if (nav.getState?.()?.routeNames?.includes(routeName)) return nav;\n }\n return null;\n}\n\n/**\n * From Home: open the app on the ROOT stack (Home is a drawer screen inside it)\n * and drop leftover Chat routes and an older copy of the app, so Back is Home.\n * Returns false when no navigator owns the route, so the caller keeps the turn\n * on the chat path instead of losing it.\n */\nexport function openNativeAppFromHome(\n navigation: HandoffNavigation,\n route: string,\n params: Record<string, unknown>,\n): boolean {\n const stack = navigatorOwning(navigation, route);\n if (!stack) return false;\n stack.dispatch((state: StackState) => {\n const kept = state.routes.filter((r) => !isBareChatRoute(r.name) && r.name !== route);\n const routes = [...kept, { name: route, params }];\n return CommonActions.reset({ ...state, routes, index: routes.length - 1 });\n });\n return true;\n}\n\n/** From an empty Chat: swap Chat for the app so Back returns to Home, not an empty thread. */\nexport function replaceWithNativeApp(\n navigation: { dispatch: (action: unknown) => void },\n route: string,\n params: Record<string, unknown>,\n): void {\n navigation.dispatch(StackActions.replace(route, params));\n}\n\n/** From a Chat with a conversation in it: open the app on top, keeping the thread. */\nexport function pushNativeApp(\n navigation: { dispatch: (action: unknown) => void },\n route: string,\n params: Record<string, unknown>,\n): void {\n navigation.dispatch(CommonActions.navigate({ name: route, params }));\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AASO,SAAS,oBAAoB,IAMR,EAAA;AAf5B,EAAA,IAAA,EAAA;AAgBE,EAAO,OAAA,cAAA,CAAA,aAAA,CAAA,cAAA,CAAA,cAAA,CAAA,EAAA,EACD,KAAK,OAAU,GAAA;AAAA,IACjB,SAAS,IAAK,CAAA;AAAA,GACZ,GAAA,EACA,CAAA,EAAA,IAAA,CAAK,SAAY,GAAA;AAAA,IACnB,WAAW,IAAK,CAAA;AAAA,GAClB,GAAI,EANC,CAAA,EAAA;AAAA,IAOL,mBAAmB,IAAK,CAAA;AAAA,GACpB,CAAA,EAAA,CAAA,CAAA,EAAA,GAAA,IAAA,CAAK,KAAL,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,MAAS,IAAA;AAAA,IACvB,yBAAyB,IAAK,CAAA;AAAA,MAC5B,EAAC,CAAA;AAET;
|
|
1
|
+
{"version":3,"file":"nativeAppNavigation.js","sources":["../../../src/features/apps/nativeAppNavigation.ts"],"sourcesContent":["/**\n * Opening a native app screen for a typed ask the brain routed there (see\n * nativeAppHandoff): the params the screen reads, and the three ways in -\n * from Home, from an empty Chat, from a Chat with a conversation in it.\n */\nimport { CommonActions, StackActions } from '@react-navigation/native';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\n\n/** What the native screen is opened with: the ask goes to its agent as the screen opens. */\nexport function nativeHandoffParams(opts: {\n orgName?: string | null;\n query: string;\n files?: MessageAttachment[] | null;\n /** The empty chat the ask was typed into, which the app's agent continues. */\n channelId?: string | null;\n}): Record<string, unknown> {\n return {\n ...(opts.orgName ? { orgName: opts.orgName } : {}),\n ...(opts.channelId ? { channelId: opts.channelId } : {}),\n initialAgentQuery: opts.query,\n ...(opts.files?.length ? { initialAgentAttachments: opts.files } : {}),\n };\n}\n\n/** The slice of a React Navigation `navigation` object the hand-off needs. */\nexport interface HandoffNavigation {\n dispatch: (action: unknown) => void;\n getParent?: () => HandoffNavigation | undefined;\n getState?: () => { routeNames?: readonly string[] } | undefined;\n}\n\ntype StackState = { routes: Array<{ name: string; params?: object }>; [key: string]: unknown };\n\nfunction isBareChatRoute(name: string): boolean {\n return /(^|\\.)Chat$/.test(name);\n}\n\n/** The navigator that declares `routeName`: the screen's own, or the nearest ancestor that does. */\nfunction navigatorOwning(navigation: HandoffNavigation, routeName: string): HandoffNavigation | null {\n for (let nav: HandoffNavigation | undefined = navigation; nav; nav = nav.getParent?.()) {\n if (nav.getState?.()?.routeNames?.includes(routeName)) return nav;\n }\n return null;\n}\n\n/**\n * From Home: open the app on the ROOT stack (Home is a drawer screen inside it)\n * and drop leftover Chat routes and an older copy of the app, so Back is Home.\n * Returns false when no navigator owns the route, so the caller keeps the turn\n * on the chat path instead of losing it.\n */\nexport function openNativeAppFromHome(\n navigation: HandoffNavigation,\n route: string,\n params: Record<string, unknown>,\n): boolean {\n const stack = navigatorOwning(navigation, route);\n if (!stack) return false;\n stack.dispatch((state: StackState) => {\n const kept = state.routes.filter((r) => !isBareChatRoute(r.name) && r.name !== route);\n const routes = [...kept, { name: route, params }];\n return CommonActions.reset({ ...state, routes, index: routes.length - 1 });\n });\n return true;\n}\n\n/** From an empty Chat: swap Chat for the app so Back returns to Home, not an empty thread. */\nexport function replaceWithNativeApp(\n navigation: { dispatch: (action: unknown) => void },\n route: string,\n params: Record<string, unknown>,\n): void {\n navigation.dispatch(StackActions.replace(route, params));\n}\n\n/** From a Chat with a conversation in it: open the app on top, keeping the thread. */\nexport function pushNativeApp(\n navigation: { dispatch: (action: unknown) => void },\n route: string,\n params: Record<string, unknown>,\n): void {\n navigation.dispatch(CommonActions.navigate({ name: route, params }));\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AASO,SAAS,oBAAoB,IAMR,EAAA;AAf5B,EAAA,IAAA,EAAA;AAgBE,EAAO,OAAA,cAAA,CAAA,aAAA,CAAA,cAAA,CAAA,cAAA,CAAA,EAAA,EACD,KAAK,OAAU,GAAA;AAAA,IACjB,SAAS,IAAK,CAAA;AAAA,GACZ,GAAA,EACA,CAAA,EAAA,IAAA,CAAK,SAAY,GAAA;AAAA,IACnB,WAAW,IAAK,CAAA;AAAA,GAClB,GAAI,EANC,CAAA,EAAA;AAAA,IAOL,mBAAmB,IAAK,CAAA;AAAA,GACpB,CAAA,EAAA,CAAA,CAAA,EAAA,GAAA,IAAA,CAAK,KAAL,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,MAAS,IAAA;AAAA,IACvB,yBAAyB,IAAK,CAAA;AAAA,MAC5B,EAAC,CAAA;AAET;AAsDgB,SAAA,oBAAA,CAAqB,UAElC,EAAA,KAAA,EAAe,MAAuC,EAAA;AACvD,EAAA,UAAA,CAAW,QAAS,CAAA,YAAA,CAAa,OAAQ,CAAA,KAAA,EAAO,MAAM,CAAC,CAAA;AACzD;AAGgB,SAAA,aAAA,CAAc,UAE3B,EAAA,KAAA,EAAe,MAAuC,EAAA;AACvD,EAAW,UAAA,CAAA,QAAA,CAAS,cAAc,QAAS,CAAA;AAAA,IACzC,IAAM,EAAA,KAAA;AAAA,IACN;AAAA,GACD,CAAC,CAAA;AACJ"}
|
|
@@ -6,10 +6,13 @@ function stashComposerAttachments(channelId, attachments) {
|
|
|
6
6
|
function hasComposerAttachments(channelId) {
|
|
7
7
|
return Boolean(channelId && stash.has(channelId));
|
|
8
8
|
}
|
|
9
|
+
function peekComposerAttachments(channelId) {
|
|
10
|
+
return channelId ? stash.get(channelId) : void 0;
|
|
11
|
+
}
|
|
9
12
|
function takeComposerAttachments(channelId) {
|
|
10
13
|
if (!channelId) return void 0;
|
|
11
14
|
const next = stash.get(channelId);
|
|
12
15
|
if (!next) return void 0;
|
|
13
16
|
stash.delete(channelId);
|
|
14
17
|
return next;
|
|
15
|
-
}export{hasComposerAttachments,stashComposerAttachments,takeComposerAttachments};//# sourceMappingURL=composerAttachmentHandoff.js.map
|
|
18
|
+
}export{hasComposerAttachments,peekComposerAttachments,stashComposerAttachments,takeComposerAttachments};//# sourceMappingURL=composerAttachmentHandoff.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composerAttachmentHandoff.js","sources":["../../../src/features/attachments/composerAttachmentHandoff.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\n\n/**\n * Home → Chat cannot put base64 payloads in navigation params (size limits).\n * Stash by channel id, then Chat consumes once CDeCLI is ready to auto-fire.\n */\nconst stash = new Map<string, MessageAttachment[]>();\n\nexport function stashComposerAttachments(channelId: string, attachments: MessageAttachment[] | undefined): void {\n if (!channelId || !attachments?.length) return;\n stash.set(channelId, attachments);\n}\n\nexport function hasComposerAttachments(channelId: string | null | undefined): boolean {\n return Boolean(channelId && stash.has(channelId));\n}\n\nexport function takeComposerAttachments(channelId: string | null | undefined): MessageAttachment[] | undefined {\n if (!channelId) return undefined;\n const next = stash.get(channelId);\n if (!next) return undefined;\n stash.delete(channelId);\n return next;\n}\n"],"names":[],"mappings":"AAMA,MAAM,KAAA,uBAAY,GAAiC,EAAA;AACnC,SAAA,wBAAA,CAAyB,WAAmB,WAAoD,EAAA;AAC9G,EAAA,IAAI,CAAC,SAAA,IAAa,EAAC,WAAA,IAAA,IAAA,GAAA,MAAA,GAAA,WAAA,CAAa,MAAQ,CAAA,EAAA;AACxC,EAAM,KAAA,CAAA,GAAA,CAAI,WAAW,WAAW,CAAA;AAClC;AACO,SAAS,uBAAuB,SAA+C,EAAA;AACpF,EAAA,OAAO,OAAQ,CAAA,SAAA,IAAa,KAAM,CAAA,GAAA,CAAI,SAAS,CAAC,CAAA;AAClD;AACO,SAAS,wBAAwB,SAAuE,EAAA;AAC7G,EAAI,IAAA,CAAC,WAAkB,OAAA,MAAA;AACvB,EAAM,MAAA,IAAA,GAAO,KAAM,CAAA,GAAA,CAAI,SAAS,CAAA;AAChC,EAAI,IAAA,CAAC,MAAa,OAAA,MAAA;AAClB,EAAA,KAAA,CAAM,OAAO,SAAS,CAAA;AACtB,EAAO,OAAA,IAAA;AACT"}
|
|
1
|
+
{"version":3,"file":"composerAttachmentHandoff.js","sources":["../../../src/features/attachments/composerAttachmentHandoff.ts"],"sourcesContent":["import type { MessageAttachment } from '../../hooks/useChatStream';\n\n/**\n * Home → Chat cannot put base64 payloads in navigation params (size limits).\n * Stash by channel id, then Chat consumes once CDeCLI is ready to auto-fire.\n */\nconst stash = new Map<string, MessageAttachment[]>();\n\nexport function stashComposerAttachments(channelId: string, attachments: MessageAttachment[] | undefined): void {\n if (!channelId || !attachments?.length) return;\n stash.set(channelId, attachments);\n}\n\nexport function hasComposerAttachments(channelId: string | null | undefined): boolean {\n return Boolean(channelId && stash.has(channelId));\n}\n\n/** What is stashed for a channel, left in place - to show it before the send takes it. */\nexport function peekComposerAttachments(channelId: string | null | undefined): MessageAttachment[] | undefined {\n return channelId ? stash.get(channelId) : undefined;\n}\n\nexport function takeComposerAttachments(channelId: string | null | undefined): MessageAttachment[] | undefined {\n if (!channelId) return undefined;\n const next = stash.get(channelId);\n if (!next) return undefined;\n stash.delete(channelId);\n return next;\n}\n"],"names":[],"mappings":"AAMA,MAAM,KAAA,uBAAY,GAAiC,EAAA;AACnC,SAAA,wBAAA,CAAyB,WAAmB,WAAoD,EAAA;AAC9G,EAAA,IAAI,CAAC,SAAA,IAAa,EAAC,WAAA,IAAA,IAAA,GAAA,MAAA,GAAA,WAAA,CAAa,MAAQ,CAAA,EAAA;AACxC,EAAM,KAAA,CAAA,GAAA,CAAI,WAAW,WAAW,CAAA;AAClC;AACO,SAAS,uBAAuB,SAA+C,EAAA;AACpF,EAAA,OAAO,OAAQ,CAAA,SAAA,IAAa,KAAM,CAAA,GAAA,CAAI,SAAS,CAAC,CAAA;AAClD;AAGO,SAAS,wBAAwB,SAAuE,EAAA;AAC7G,EAAA,OAAO,SAAY,GAAA,KAAA,CAAM,GAAI,CAAA,SAAS,CAAI,GAAA,MAAA;AAC5C;AACO,SAAS,wBAAwB,SAAuE,EAAA;AAC7G,EAAI,IAAA,CAAC,WAAkB,OAAA,MAAA;AACvB,EAAM,MAAA,IAAA,GAAO,KAAM,CAAA,GAAA,CAAI,SAAS,CAAA;AAChC,EAAI,IAAA,CAAC,MAAa,OAAA,MAAA;AAClB,EAAA,KAAA,CAAM,OAAO,SAAS,CAAA;AACtB,EAAO,OAAA,IAAA;AACT"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {jsx,Fragment,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useCallback,useEffect,memo}from'react';import {View,StyleSheet,FlatList,Platform,Pressable,Text}from'react-native';import {dismissKeyboard}from'../../utils/keyboardController.js';import {useKeyboardLiftHeight}from'../../components/KeyboardComposerDock.js';import
|
|
1
|
+
import {jsx,Fragment,jsxs}from'react/jsx-runtime';import {useRef,useMemo,useCallback,useEffect,memo}from'react';import {View,StyleSheet,FlatList,Platform,Pressable,Text}from'react-native';import Markdown from'react-native-markdown-display';import {dismissKeyboard}from'../../utils/keyboardController.js';import {useKeyboardLiftHeight}from'../../components/KeyboardComposerDock.js';import {shouldRenderUserTranscriptTurn,displayUserMessageText}from'../attachments/displayUserMessageText.js';import {MessageAttachmentPreviews}from'../attachments/MessageAttachmentPreviews.js';import {ToolTimeline}from'./ToolTimeline.js';import {ChatWidgetCard}from'./ChatWidgetCard.js';import {splitChatWidgets}from'./chatWidgets.js';import {parseToolActivity}from'./toolActivity.js';import {parseAskUser,displayAskUserAssistantText,stripAskUser}from'./askUser.js';import {collapseDuplicateUserRows,groupConsecutiveRoleRows}from'./transcriptGroups.js';import {AssistantMarkdown}from'./AssistantMarkdown.js';import {useChatLinkOpener}from'../links/chatLinkOpener.js';import {autolinkBareUrls}from'../messaging/chatLinks.js';import {PreResponseStrip}from'./PreResponseStrip.js';import {splitPreResponse}from'./preResponse.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -172,7 +172,7 @@ const TranscriptGroupView = memo(function TranscriptGroupView2({
|
|
|
172
172
|
}, [openLink]);
|
|
173
173
|
if (group.role === "user") {
|
|
174
174
|
return /* @__PURE__ */ jsx(Pressable, { style: styles.userCol, onPress: dismissKeyboard, accessible: false, children: group.items.map((item, index) => {
|
|
175
|
-
var _a, _b;
|
|
175
|
+
var _a, _b, _c;
|
|
176
176
|
const attachments = (_a = item.attachments) != null ? _a : [];
|
|
177
177
|
const text = displayUserMessageText((_b = item.displayContent) != null ? _b : item.content);
|
|
178
178
|
if (!text && attachments.length === 0) return null;
|
|
@@ -181,7 +181,7 @@ const TranscriptGroupView = memo(function TranscriptGroupView2({
|
|
|
181
181
|
text ? /* @__PURE__ */ jsx(View, { style: [styles.userBubble, item.queued && styles.userBubbleQueued], children: /* @__PURE__ */ jsx(Markdown, { style: USER_MARKDOWN, onLinkPress, children: autolinkBareUrls(text) }) }) : null,
|
|
182
182
|
item.queued ? /* @__PURE__ */ jsx(Text, { style: [styles.queuedCaption, {
|
|
183
183
|
color: isDark ? "#94a3b8" : "#64748b"
|
|
184
|
-
}], children: "Queued \xB7 sends when the gateway connects" }) : null
|
|
184
|
+
}], children: (_c = item.queuedNote) != null ? _c : "Queued \xB7 sends when the gateway connects" }) : null
|
|
185
185
|
] }, item.id);
|
|
186
186
|
}) });
|
|
187
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatTranscript.js","sources":["../../../src/features/chat/ChatTranscript.tsx"],"sourcesContent":["/**\n * Chat thread list that can show attachment previews.\n *\n * `@messenger-box/platform-mobile` PlanModeView maps each row to GiftedChat\n * `{ text }` only, so an image send becomes the gateway caption\n * \"Attached: IMG_0005.jpg\" with no thumbnail. This list keeps streaming\n * markdown for the assistant and draws Manus-style previews on the user side.\n *\n * Rows stay oldest → newest (same as web). A previous `inverted` FlatList\n * reversed that order on device. The composer dock already lifts with the\n * keyboard; this list just fills the leftover space and pins to the latest\n * turn.\n */\nimport React, { memo, useCallback, useEffect, useMemo, useRef, type ReactNode } from 'react';\nimport { FlatList, Platform, Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport { dismissKeyboard } from '../../utils/keyboardController';\nimport { useKeyboardLiftHeight } from '../../components/KeyboardComposerDock';\nimport Markdown from 'react-native-markdown-display';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\nimport { ToolTimeline } from './ToolTimeline';\nimport { ChatWidgetCard } from './ChatWidgetCard';\nimport { splitChatWidgets } from './chatWidgets';\nimport { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { AssistantMarkdown } from './AssistantMarkdown';\nimport { useChatLinkOpener } from '../links/chatLinkOpener';\nimport { autolinkBareUrls } from '../messaging/chatLinks';\nimport { PreResponseStrip } from './PreResponseStrip';\nimport { splitPreResponse } from './preResponse';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n displayContent?: string;\n attachments?: MessageAttachment[];\n /** Typed while the gateway was down: shown in place, waiting on the outbox. */\n queued?: boolean;\n};\n\ntype TranscriptRow = TranscriptMessage & {\n streaming?: boolean;\n askAnswered?: boolean;\n /** The fast lane's quick answer for this turn, drawn as a strip - never as text. */\n preResponse?: string | null;\n};\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nexport const USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n quickReply,\n renderMessageActions,\n listContentStyle,\n listFooter,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n /** The running turn's quick answer (fast lane or brain), already split off the stream. */\n quickReply?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n listFooter?: ReactNode;\n isDark?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptGroup>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n const ignoreScrollUntilRef = useRef(0);\n const keyboardHeight = useKeyboardLiftHeight();\n\n const historyRows = useMemo<TranscriptRow[]>(() => {\n const rows: TranscriptRow[] = [];\n messages.forEach((msg, index) => {\n if (msg.role === 'user') {\n if (shouldRenderUserTranscriptTurn(msg.content, msg.attachments)) {\n rows.push({ ...msg, streaming: false });\n }\n return;\n }\n if (msg.role !== 'assistant') {\n rows.push({ ...msg, streaming: false });\n return;\n }\n // A persisted reply carries its quick answer prepended in the gateway's\n // markers; the row keeps the two apart from here on.\n const { preResponse, text: content } = splitPreResponse(msg.content);\n const hasAttachments = (msg.attachments?.length ?? 0) > 0;\n const askAnswered =\n Boolean(parseAskUser(content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(content), false);\n const hasAsk = Boolean(parseAskUser(content));\n if (hasAsk && !visible && tools.steps.length === 0 && !hasAttachments && !preResponse) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(content).trim();\n if (!leftover && !hasAttachments && !preResponse) return;\n }\n rows.push({ ...msg, content, preResponse, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n // The chat hook hands the quick answer over split off the stream; a raw\n // stream (another host) may still carry the markers, open mid-stream.\n const { preResponse: streamPre, text: rawStream } = splitPreResponse(streamingContent ?? '');\n const livePre = quickReply?.trim() || streamPre;\n const last = history[history.length - 1];\n const streamAskAnswered = last?.role === 'user' && Boolean(parseAskUser(rawStream));\n const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();\n const streamTools = parseToolActivity(stripAskUser(rawStream), true);\n if (!streamProse && streamTools.steps.length === 0 && !livePre) return history;\n if (last?.role === 'assistant' && (!livePre || last.preResponse === livePre)) {\n const lastText = last.content.trim();\n const streamText = rawStream.trim();\n if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {\n return history;\n }\n }\n return [\n ...history,\n {\n id: '__streaming__',\n role: 'assistant',\n content: rawStream,\n preResponse: livePre,\n streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent, quickReply]);\n\n const groups = useMemo(() => groupConsecutiveRoleRows(rows), [rows]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (groups.length === 0) return;\n isProgrammaticScrollRef.current = true;\n stickToBottomRef.current = true;\n const list = listRef.current;\n if (!list) return;\n list.scrollToEnd({ animated });\n },\n [groups.length],\n );\n\n useEffect(() => {\n if (groups.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [groups.length, streamingContent?.length, quickReply?.length, scrollToLatest]);\n\n // Spacer and list height update on the same keyboard frame. Scroll only\n // after that shrink, or we stay on the top of a long turn (image 1).\n useEffect(() => {\n if (keyboardHeight <= 0) return undefined;\n stickToBottomRef.current = true;\n ignoreScrollUntilRef.current = Date.now() + 400;\n const frame = requestAnimationFrame(() => scrollToLatest(false));\n const afterLayout = setTimeout(() => scrollToLatest(false), 64);\n const afterKeyboard = setTimeout(() => scrollToLatest(false), 280);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(afterLayout);\n clearTimeout(afterKeyboard);\n };\n }, [keyboardHeight, scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n if (Date.now() < ignoreScrollUntilRef.current) {\n stickToBottomRef.current = true;\n return;\n }\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptGroup }) => (\n <TranscriptGroupView group={item} isDark={isDark} renderMessageActions={renderMessageActions} />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <View style={styles.list}>\n <FlatList\n ref={listRef}\n data={groups}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={`${streamingContent ?? ''}:${quickReply ?? ''}:${Boolean(listFooter)}`}\n ListFooterComponent={listFooter ? <>{listFooter}</> : null}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={21}\n scrollEnabled\n nestedScrollEnabled={false}\n directionalLockEnabled\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"handled\"\n keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle, !listFooter && styles.listContentFill]}\n style={styles.list}\n />\n </View>\n );\n}\n\nconst TranscriptGroupView = memo(function TranscriptGroupView({\n group,\n isDark,\n renderMessageActions,\n}: {\n group: TranscriptGroup;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n // A link the person pasted opens like one the assistant sent: in the app or a browser sheet.\n const openLink = useChatLinkOpener();\n const onLinkPress = useCallback(\n (url: string) => {\n openLink(url);\n return false;\n },\n [openLink],\n );\n if (group.role === 'user') {\n return (\n <Pressable style={styles.userCol} onPress={dismissKeyboard} accessible={false}>\n {group.items.map((item, index) => {\n const attachments = item.attachments ?? [];\n const text = displayUserMessageText(item.displayContent ?? item.content, attachments);\n if (!text && attachments.length === 0) return null;\n return (\n <View key={item.id} style={[styles.userTurn, index > 0 && styles.userFollow]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={[styles.userBubble, item.queued && styles.userBubbleQueued]}>\n <Markdown style={USER_MARKDOWN} onLinkPress={onLinkPress}>\n {/* Bare URLs the person typed become links, as in the assistant's replies. */}\n {autolinkBareUrls(text)}\n </Markdown>\n </View>\n ) : null}\n {item.queued ? (\n <Text style={[styles.queuedCaption, { color: isDark ? '#94a3b8' : '#64748b' }]}>\n Queued · sends when the gateway connects\n </Text>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\n const last = group.items[group.items.length - 1];\n\n return (\n <View style={styles.assistantGroup}>\n {group.items.map((item, index) => (\n <AssistantTurnView\n key={item.id}\n item={item}\n isDark={isDark}\n tightTop={index > 0}\n renderMessageActions={item.streaming || item !== last ? undefined : renderMessageActions}\n />\n ))}\n </View>\n );\n});\n\nconst AssistantTurnView = memo(function AssistantTurnView({\n item,\n isDark,\n tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n tightTop: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const attachments = item.attachments ?? [];\n const stripped = stripAskUser(item.content);\n const toolActivity = parseToolActivity(stripped, !!item.streaming);\n // Widget envelopes become native cards below the text, never raw JSON.\n const { text, widgets } = splitChatWidgets(\n parseToolActivity(displayAskUserAssistantText(item.content, !!item.askAnswered), !!item.streaming).text,\n !!item.streaming,\n );\n\n // The quick answer stays open while it is all there is to read: until the\n // agent's own words start on a live turn, or for good on a reply that never\n // got anything past it. Tool steps alone do not fold it - they are not an answer.\n const preResponseOpen = item.streaming\n ? !text.trim()\n : !text.trim() && toolActivity.steps.length === 0 && widgets.length === 0 && attachments.length === 0;\n\n return (\n <View style={[styles.assistantCol, tightTop && styles.assistantColTight]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {item.preResponse ? (\n <PreResponseStrip\n text={item.preResponse}\n expanded={preResponseOpen}\n live={!!item.streaming}\n isDark={isDark}\n />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? <AssistantMarkdown text={text} streaming={!!item.streaming} isDark={isDark} /> : null}\n {widgets.map((widget, i) => (\n <ChatWidgetCard key={`${widget.component}-${i}`} widget={widget} isDark={isDark} />\n ))}\n {renderMessageActions\n ? renderMessageActions({\n id: item.id,\n role: 'assistant',\n // Copy and share take what the user reads, not the wire markup.\n content: text.trim() || item.preResponse || item.content,\n })\n : null}\n </View>\n );\n});\n\nconst USER_MARKDOWN = {\n body: { color: '#ffffff', fontSize: 15, lineHeight: 22 },\n // White and underlined: the bubble is blue, so the usual blue link would vanish.\n link: { color: '#ffffff', textDecorationLine: 'underline' as const },\n paragraph: { marginTop: 4, marginBottom: 4, color: '#ffffff' },\n strong: { fontWeight: '700' as const, color: '#ffffff' },\n em: { color: '#ffffff' },\n text: { color: '#ffffff' },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n list_item: { marginVertical: 2, color: '#ffffff' },\n};\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n minHeight: 0,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 12,\n paddingHorizontal: 4,\n },\n userTurn: {\n maxWidth: '92%',\n alignItems: 'flex-end',\n },\n userFollow: {\n marginTop: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n paddingHorizontal: 12,\n paddingVertical: 8,\n },\n // Same bubble, held back: a queued turn reads as pending, not as delivered.\n userBubbleQueued: {\n opacity: 0.55,\n },\n queuedCaption: {\n fontSize: 11,\n fontWeight: '500',\n marginTop: 4,\n marginRight: 2,\n },\n assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n flexGrow: 0,\n alignItems: 'stretch',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantColTight: {\n marginTop: -4,\n },\n});\n"],"names":["rows","TranscriptGroupView","AssistantTurnView"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatTranscript.js","sources":["../../../src/features/chat/ChatTranscript.tsx"],"sourcesContent":["/**\n * Chat thread list that can show attachment previews.\n *\n * `@messenger-box/platform-mobile` PlanModeView maps each row to GiftedChat\n * `{ text }` only, so an image send becomes the gateway caption\n * \"Attached: IMG_0005.jpg\" with no thumbnail. This list keeps streaming\n * markdown for the assistant and draws Manus-style previews on the user side.\n *\n * Rows stay oldest → newest (same as web). A previous `inverted` FlatList\n * reversed that order on device. The composer dock already lifts with the\n * keyboard; this list just fills the leftover space and pins to the latest\n * turn.\n */\nimport React, { memo, useCallback, useEffect, useMemo, useRef, type ReactNode } from 'react';\nimport { FlatList, Platform, Pressable, StyleSheet, Text, View, type StyleProp, type ViewStyle } from 'react-native';\nimport Markdown from 'react-native-markdown-display';\nimport { dismissKeyboard } from '../../utils/keyboardController';\nimport { useKeyboardLiftHeight } from '../../components/KeyboardComposerDock';\nimport type { MessageAttachment } from '../../hooks/useChatStream';\nimport { displayUserMessageText, shouldRenderUserTranscriptTurn } from '../attachments/displayUserMessageText';\nimport { MessageAttachmentPreviews } from '../attachments/MessageAttachmentPreviews';\nimport { ToolTimeline } from './ToolTimeline';\nimport { ChatWidgetCard } from './ChatWidgetCard';\nimport { splitChatWidgets } from './chatWidgets';\nimport { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { AssistantMarkdown } from './AssistantMarkdown';\nimport { useChatLinkOpener } from '../links/chatLinkOpener';\nimport { autolinkBareUrls } from '../messaging/chatLinks';\nimport { PreResponseStrip } from './PreResponseStrip';\nimport { splitPreResponse } from './preResponse';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n displayContent?: string;\n attachments?: MessageAttachment[];\n /** Typed while the gateway was down: shown in place, waiting on the outbox. */\n queued?: boolean;\n /** What the caption under a queued turn says, when it is not waiting on the gateway to connect. */\n queuedNote?: string;\n};\n\ntype TranscriptRow = TranscriptMessage & {\n streaming?: boolean;\n askAnswered?: boolean;\n /** The fast lane's quick answer for this turn, drawn as a strip - never as text. */\n preResponse?: string | null;\n};\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nexport const USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n quickReply,\n renderMessageActions,\n listContentStyle,\n listFooter,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n /** The running turn's quick answer (fast lane or brain), already split off the stream. */\n quickReply?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\n listFooter?: ReactNode;\n isDark?: boolean;\n}) {\n const listRef = useRef<FlatList<TranscriptGroup>>(null);\n const stickToBottomRef = useRef(true);\n const isProgrammaticScrollRef = useRef(false);\n const ignoreScrollUntilRef = useRef(0);\n const keyboardHeight = useKeyboardLiftHeight();\n\n const historyRows = useMemo<TranscriptRow[]>(() => {\n const rows: TranscriptRow[] = [];\n messages.forEach((msg, index) => {\n if (msg.role === 'user') {\n if (shouldRenderUserTranscriptTurn(msg.content, msg.attachments)) {\n rows.push({ ...msg, streaming: false });\n }\n return;\n }\n if (msg.role !== 'assistant') {\n rows.push({ ...msg, streaming: false });\n return;\n }\n // A persisted reply carries its quick answer prepended in the gateway's\n // markers; the row keeps the two apart from here on.\n const { preResponse, text: content } = splitPreResponse(msg.content);\n const hasAttachments = (msg.attachments?.length ?? 0) > 0;\n const askAnswered =\n Boolean(parseAskUser(content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(content), false);\n const hasAsk = Boolean(parseAskUser(content));\n if (hasAsk && !visible && tools.steps.length === 0 && !hasAttachments && !preResponse) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(content).trim();\n if (!leftover && !hasAttachments && !preResponse) return;\n }\n rows.push({ ...msg, content, preResponse, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n // The chat hook hands the quick answer over split off the stream; a raw\n // stream (another host) may still carry the markers, open mid-stream.\n const { preResponse: streamPre, text: rawStream } = splitPreResponse(streamingContent ?? '');\n const livePre = quickReply?.trim() || streamPre;\n const last = history[history.length - 1];\n const streamAskAnswered = last?.role === 'user' && Boolean(parseAskUser(rawStream));\n const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();\n const streamTools = parseToolActivity(stripAskUser(rawStream), true);\n if (!streamProse && streamTools.steps.length === 0 && !livePre) return history;\n if (last?.role === 'assistant' && (!livePre || last.preResponse === livePre)) {\n const lastText = last.content.trim();\n const streamText = rawStream.trim();\n if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {\n return history;\n }\n }\n return [\n ...history,\n {\n id: '__streaming__',\n role: 'assistant',\n content: rawStream,\n preResponse: livePre,\n streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent, quickReply]);\n\n const groups = useMemo(() => groupConsecutiveRoleRows(rows), [rows]);\n\n const scrollToLatest = useCallback(\n (animated: boolean) => {\n if (groups.length === 0) return;\n isProgrammaticScrollRef.current = true;\n stickToBottomRef.current = true;\n const list = listRef.current;\n if (!list) return;\n list.scrollToEnd({ animated });\n },\n [groups.length],\n );\n\n useEffect(() => {\n if (groups.length === 0 || !stickToBottomRef.current) return undefined;\n const t = setTimeout(() => scrollToLatest(false), 0);\n return () => clearTimeout(t);\n }, [groups.length, streamingContent?.length, quickReply?.length, scrollToLatest]);\n\n // Spacer and list height update on the same keyboard frame. Scroll only\n // after that shrink, or we stay on the top of a long turn (image 1).\n useEffect(() => {\n if (keyboardHeight <= 0) return undefined;\n stickToBottomRef.current = true;\n ignoreScrollUntilRef.current = Date.now() + 400;\n const frame = requestAnimationFrame(() => scrollToLatest(false));\n const afterLayout = setTimeout(() => scrollToLatest(false), 64);\n const afterKeyboard = setTimeout(() => scrollToLatest(false), 280);\n return () => {\n cancelAnimationFrame(frame);\n clearTimeout(afterLayout);\n clearTimeout(afterKeyboard);\n };\n }, [keyboardHeight, scrollToLatest]);\n\n const handleScroll = useCallback(\n (e: {\n nativeEvent: {\n contentOffset: { y: number };\n contentSize: { height: number };\n layoutMeasurement: { height: number };\n };\n }) => {\n if (Date.now() < ignoreScrollUntilRef.current) {\n stickToBottomRef.current = true;\n return;\n }\n const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;\n const nearLatest = contentOffset.y + layoutMeasurement.height >= contentSize.height - 80;\n if (!isProgrammaticScrollRef.current) {\n stickToBottomRef.current = nearLatest;\n } else if (nearLatest) {\n isProgrammaticScrollRef.current = false;\n }\n },\n [],\n );\n\n const handleContentSizeChange = useCallback(() => {\n if (!stickToBottomRef.current) return;\n scrollToLatest(false);\n }, [scrollToLatest]);\n\n const renderItem = useCallback(\n ({ item }: { item: TranscriptGroup }) => (\n <TranscriptGroupView group={item} isDark={isDark} renderMessageActions={renderMessageActions} />\n ),\n [isDark, renderMessageActions],\n );\n\n return (\n <View style={styles.list}>\n <FlatList\n ref={listRef}\n data={groups}\n keyExtractor={(item) => item.id}\n renderItem={renderItem}\n extraData={`${streamingContent ?? ''}:${quickReply ?? ''}:${Boolean(listFooter)}`}\n ListFooterComponent={listFooter ? <>{listFooter}</> : null}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={21}\n scrollEnabled\n nestedScrollEnabled={false}\n directionalLockEnabled\n onScroll={handleScroll}\n onContentSizeChange={handleContentSizeChange}\n onLayout={() => {\n if (stickToBottomRef.current) scrollToLatest(false);\n }}\n scrollEventThrottle={16}\n keyboardShouldPersistTaps=\"handled\"\n keyboardDismissMode={Platform.OS === 'ios' ? 'interactive' : 'on-drag'}\n automaticallyAdjustKeyboardInsets={false}\n automaticallyAdjustContentInsets={false}\n contentInsetAdjustmentBehavior=\"never\"\n contentContainerStyle={[styles.listContent, listContentStyle, !listFooter && styles.listContentFill]}\n style={styles.list}\n />\n </View>\n );\n}\n\nconst TranscriptGroupView = memo(function TranscriptGroupView({\n group,\n isDark,\n renderMessageActions,\n}: {\n group: TranscriptGroup;\n isDark: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n // A link the person pasted opens like one the assistant sent: in the app or a browser sheet.\n const openLink = useChatLinkOpener();\n const onLinkPress = useCallback(\n (url: string) => {\n openLink(url);\n return false;\n },\n [openLink],\n );\n if (group.role === 'user') {\n return (\n <Pressable style={styles.userCol} onPress={dismissKeyboard} accessible={false}>\n {group.items.map((item, index) => {\n const attachments = item.attachments ?? [];\n const text = displayUserMessageText(item.displayContent ?? item.content, attachments);\n if (!text && attachments.length === 0) return null;\n return (\n <View key={item.id} style={[styles.userTurn, index > 0 && styles.userFollow]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"end\" isDark={isDark} />\n ) : null}\n {text ? (\n <View style={[styles.userBubble, item.queued && styles.userBubbleQueued]}>\n <Markdown style={USER_MARKDOWN} onLinkPress={onLinkPress}>\n {/* Bare URLs the person typed become links, as in the assistant's replies. */}\n {autolinkBareUrls(text)}\n </Markdown>\n </View>\n ) : null}\n {item.queued ? (\n <Text style={[styles.queuedCaption, { color: isDark ? '#94a3b8' : '#64748b' }]}>\n {item.queuedNote ?? 'Queued · sends when the gateway connects'}\n </Text>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\n const last = group.items[group.items.length - 1];\n\n return (\n <View style={styles.assistantGroup}>\n {group.items.map((item, index) => (\n <AssistantTurnView\n key={item.id}\n item={item}\n isDark={isDark}\n tightTop={index > 0}\n renderMessageActions={item.streaming || item !== last ? undefined : renderMessageActions}\n />\n ))}\n </View>\n );\n});\n\nconst AssistantTurnView = memo(function AssistantTurnView({\n item,\n isDark,\n tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n tightTop: boolean;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n}) {\n const attachments = item.attachments ?? [];\n const stripped = stripAskUser(item.content);\n const toolActivity = parseToolActivity(stripped, !!item.streaming);\n // Widget envelopes become native cards below the text, never raw JSON.\n const { text, widgets } = splitChatWidgets(\n parseToolActivity(displayAskUserAssistantText(item.content, !!item.askAnswered), !!item.streaming).text,\n !!item.streaming,\n );\n\n // The quick answer stays open while it is all there is to read: until the\n // agent's own words start on a live turn, or for good on a reply that never\n // got anything past it. Tool steps alone do not fold it - they are not an answer.\n const preResponseOpen = item.streaming\n ? !text.trim()\n : !text.trim() && toolActivity.steps.length === 0 && widgets.length === 0 && attachments.length === 0;\n\n return (\n <View style={[styles.assistantCol, tightTop && styles.assistantColTight]}>\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {item.preResponse ? (\n <PreResponseStrip\n text={item.preResponse}\n expanded={preResponseOpen}\n live={!!item.streaming}\n isDark={isDark}\n />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? <AssistantMarkdown text={text} streaming={!!item.streaming} isDark={isDark} /> : null}\n {widgets.map((widget, i) => (\n <ChatWidgetCard key={`${widget.component}-${i}`} widget={widget} isDark={isDark} />\n ))}\n {renderMessageActions\n ? renderMessageActions({\n id: item.id,\n role: 'assistant',\n // Copy and share take what the user reads, not the wire markup.\n content: text.trim() || item.preResponse || item.content,\n })\n : null}\n </View>\n );\n});\n\nconst USER_MARKDOWN = {\n body: { color: '#ffffff', fontSize: 15, lineHeight: 22 },\n // White and underlined: the bubble is blue, so the usual blue link would vanish.\n link: { color: '#ffffff', textDecorationLine: 'underline' as const },\n paragraph: { marginTop: 4, marginBottom: 4, color: '#ffffff' },\n strong: { fontWeight: '700' as const, color: '#ffffff' },\n em: { color: '#ffffff' },\n text: { color: '#ffffff' },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n list_item: { marginVertical: 2, color: '#ffffff' },\n};\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n minHeight: 0,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n },\n userCol: {\n width: '100%',\n alignItems: 'flex-end',\n marginBottom: 12,\n paddingHorizontal: 4,\n },\n userTurn: {\n maxWidth: '92%',\n alignItems: 'flex-end',\n },\n userFollow: {\n marginTop: 4,\n },\n userBubble: {\n backgroundColor: USER_BUBBLE_BG,\n borderRadius: 16,\n paddingHorizontal: 12,\n paddingVertical: 8,\n },\n // Same bubble, held back: a queued turn reads as pending, not as delivered.\n userBubbleQueued: {\n opacity: 0.55,\n },\n queuedCaption: {\n fontSize: 11,\n fontWeight: '500',\n marginTop: 4,\n marginRight: 2,\n },\n assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n flexGrow: 0,\n alignItems: 'stretch',\n marginBottom: 8,\n paddingHorizontal: 4,\n },\n assistantColTight: {\n marginTop: -4,\n },\n});\n"],"names":["rows","TranscriptGroupView","AssistantTurnView"],"mappings":"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"}
|
|
@@ -31,16 +31,6 @@ function shopHandoffParams(opts) {
|
|
|
31
31
|
if ((_a = opts.files) == null ? void 0 : _a.length) params.initialAgentAttachments = opts.files;
|
|
32
32
|
return params;
|
|
33
33
|
}
|
|
34
|
-
function isBareChatRoute(name) {
|
|
35
|
-
return /(^|\.)Chat$/.test(name);
|
|
36
|
-
}
|
|
37
|
-
function navigatorOwning(navigation, routeName) {
|
|
38
|
-
var _a, _b, _c, _d;
|
|
39
|
-
for (let nav = navigation; nav; nav = (_a = nav.getParent) == null ? void 0 : _a.call(nav)) {
|
|
40
|
-
if ((_d = (_c = (_b = nav.getState) == null ? void 0 : _b.call(nav)) == null ? void 0 : _c.routeNames) == null ? void 0 : _d.includes(routeName)) return nav;
|
|
41
|
-
}
|
|
42
|
-
return null;
|
|
43
|
-
}
|
|
44
34
|
function replaceWithShopNative(navigation, params) {
|
|
45
35
|
if (typeof navigation.replace === "function") {
|
|
46
36
|
navigation.replace(SHOP_ROUTE, params);
|
|
@@ -53,20 +43,4 @@ function pushShopNative(navigation, params) {
|
|
|
53
43
|
name: SHOP_ROUTE,
|
|
54
44
|
params
|
|
55
45
|
}));
|
|
56
|
-
}
|
|
57
|
-
function openShopFromHome(navigation, params) {
|
|
58
|
-
const stack = navigatorOwning(navigation, SHOP_ROUTE);
|
|
59
|
-
if (!stack) return false;
|
|
60
|
-
stack.dispatch((state) => {
|
|
61
|
-
const kept = state.routes.filter((route) => !isBareChatRoute(route.name) && !/(^|\.)ShoppingNative$/.test(route.name));
|
|
62
|
-
const routes = [...kept, {
|
|
63
|
-
name: SHOP_ROUTE,
|
|
64
|
-
params
|
|
65
|
-
}];
|
|
66
|
-
return CommonActions.reset(__spreadProps(__spreadValues({}, state), {
|
|
67
|
-
routes,
|
|
68
|
-
index: routes.length - 1
|
|
69
|
-
}));
|
|
70
|
-
});
|
|
71
|
-
return true;
|
|
72
|
-
}export{isBareChatRoute,navigatorOwning,openShopFromHome,pushShopNative,replaceWithShopNative,shopHandoffParams};//# sourceMappingURL=openShopNative.js.map
|
|
46
|
+
}export{pushShopNative,replaceWithShopNative,shopHandoffParams};//# sourceMappingURL=openShopNative.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openShopNative.js","sources":["../../../../src/features/shopping/chat/openShopNative.ts"],"sourcesContent":["import { CommonActions, StackActions } from '@react-navigation/native';\nimport type { MessageAttachment } from '../../../hooks/useChatStream';\n\nconst SHOP_ROUTE = 'MainStack.ShoppingNative';\n\n/** The slice of a React Navigation `navigation` object the hand-off needs. */\nexport interface ShopNavigation {\n dispatch: (action: unknown) => void;\n getParent?: () => ShopNavigation | undefined;\n getState?: () => { routeNames?: readonly string[] } | undefined;\n}\n\ntype StackState = { routes: Array<{ name: string; params?: object }>; [key: string]: unknown };\n\nexport function shopHandoffParams(opts: {\n orgName?: string | null;\n query: string;\n files?: MessageAttachment[] | null;\n /**\n * The chat this turn was typed into, when it has nothing else in it. Shop\n * continues that conversation instead of minting a second one, so History\n * does not gain an empty \"New chat\" for every hand-off.\n */\n channelId?: string | null;\n}): Record<string, unknown> {\n const params: Record<string, unknown> = {\n ...(opts.orgName ? { orgName: opts.orgName } : {}),\n ...(opts.channelId ? { channelId: opts.channelId } : {}),\n initialAgentQuery: opts.query,\n openAgent: true,\n };\n if (opts.files?.length) params.initialAgentAttachments = opts.files;\n return params;\n}\n\nexport function isBareChatRoute(name: string): boolean {\n return /(^|\\.)Chat$/.test(name);\n}\n\n/**\n * The navigator that declares `routeName`: the screen's own, or the nearest\n * ancestor that does. A screen's `dispatch(state => …)` hands the callback the\n * state of the navigator the SCREEN lives in, so a reset built for a different\n * navigator has to be dispatched from that navigator.\n */\nexport function navigatorOwning(navigation: ShopNavigation, routeName: string): ShopNavigation | null {\n for (let nav: ShopNavigation | undefined = navigation; nav; nav = nav.getParent?.()) {\n if (nav.getState?.()?.routeNames?.includes(routeName)) return nav;\n }\n return null;\n}\n\n/** From Chat: swap Chat for Shop so Back returns to Home, not an empty thread. */\nexport function replaceWithShopNative(\n navigation: { dispatch: (action: unknown) => void; replace?: (name: string, params?: object) => void },\n params: Record<string, unknown>,\n) {\n if (typeof navigation.replace === 'function') {\n navigation.replace(SHOP_ROUTE, params);\n return;\n }\n navigation.dispatch(StackActions.replace(SHOP_ROUTE, params));\n}\n\n/** Push Shop on top of the current screen (keeps an existing Chat thread). */\nexport function pushShopNative(navigation: { dispatch: (action: unknown) => void }, params: Record<string, unknown>) {\n navigation.dispatch(CommonActions.navigate({ name: SHOP_ROUTE, params }));\n}\n\n/**\n * From Home: open Shop and drop leftover Chat routes so Back is Home,\n * not a blank Chat that never received the shopping message.\n *\n * Home is a DRAWER screen and Shop is a route of the ROOT stack around it. The\n * reset therefore has to run on the stack: built from the drawer's state it\n * names routes the drawer does not have, every router answers it with `null`,\n * and a release build drops the action without a word - the composer had\n * already been cleared, so the person's message and photos were simply gone.\n *\n * Returns false when no navigator owns the Shop route, so the caller keeps the\n * message and takes the chat path instead of losing the turn.\n */\nexport function openShopFromHome(navigation: ShopNavigation, params: Record<string, unknown>): boolean {\n const stack = navigatorOwning(navigation, SHOP_ROUTE);\n if (!stack) return false;\n stack.dispatch((state: StackState) => {\n const kept = state.routes.filter(\n (route) => !isBareChatRoute(route.name) && !/(^|\\.)ShoppingNative$/.test(route.name),\n );\n const routes = [...kept, { name: SHOP_ROUTE, params }];\n return CommonActions.reset({\n ...state,\n routes,\n index: routes.length - 1,\n });\n });\n return true;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAEA,MAAM,UAAa,GAAA,0BAAA;AAiBZ,SAAS,kBAAkB,IAUN,EAAA;AA7B5B,EAAA,IAAA,EAAA;AA8BE,EAAM,MAAA,MAAA,GAAkC,aAClC,CAAA,cAAA,CAAA,cAAA,CAAA,EAAA,EAAA,IAAA,CAAK,OAAU,GAAA;AAAA,IACjB,SAAS,IAAK,CAAA;AAAA,GACZ,GAAA,EACA,CAAA,EAAA,IAAA,CAAK,SAAY,GAAA;AAAA,IACnB,WAAW,IAAK,CAAA;AAAA,GAClB,GAAI,EANkC,CAAA,EAAA;AAAA,IAOtC,mBAAmB,IAAK,CAAA,KAAA;AAAA,IACxB,SAAW,EAAA;AAAA,GACb,CAAA;AACA,EAAA,IAAA,CAAI,UAAK,KAAL,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,MAAQ,EAAA,MAAA,CAAO,0BAA0B,IAAK,CAAA,KAAA;AAC9D,EAAO,OAAA,MAAA;AACT;
|
|
1
|
+
{"version":3,"file":"openShopNative.js","sources":["../../../../src/features/shopping/chat/openShopNative.ts"],"sourcesContent":["import { CommonActions, StackActions } from '@react-navigation/native';\nimport type { MessageAttachment } from '../../../hooks/useChatStream';\n\nconst SHOP_ROUTE = 'MainStack.ShoppingNative';\n\n/** The slice of a React Navigation `navigation` object the hand-off needs. */\nexport interface ShopNavigation {\n dispatch: (action: unknown) => void;\n getParent?: () => ShopNavigation | undefined;\n getState?: () => { routeNames?: readonly string[] } | undefined;\n}\n\ntype StackState = { routes: Array<{ name: string; params?: object }>; [key: string]: unknown };\n\nexport function shopHandoffParams(opts: {\n orgName?: string | null;\n query: string;\n files?: MessageAttachment[] | null;\n /**\n * The chat this turn was typed into, when it has nothing else in it. Shop\n * continues that conversation instead of minting a second one, so History\n * does not gain an empty \"New chat\" for every hand-off.\n */\n channelId?: string | null;\n}): Record<string, unknown> {\n const params: Record<string, unknown> = {\n ...(opts.orgName ? { orgName: opts.orgName } : {}),\n ...(opts.channelId ? { channelId: opts.channelId } : {}),\n initialAgentQuery: opts.query,\n openAgent: true,\n };\n if (opts.files?.length) params.initialAgentAttachments = opts.files;\n return params;\n}\n\nexport function isBareChatRoute(name: string): boolean {\n return /(^|\\.)Chat$/.test(name);\n}\n\n/**\n * The navigator that declares `routeName`: the screen's own, or the nearest\n * ancestor that does. A screen's `dispatch(state => …)` hands the callback the\n * state of the navigator the SCREEN lives in, so a reset built for a different\n * navigator has to be dispatched from that navigator.\n */\nexport function navigatorOwning(navigation: ShopNavigation, routeName: string): ShopNavigation | null {\n for (let nav: ShopNavigation | undefined = navigation; nav; nav = nav.getParent?.()) {\n if (nav.getState?.()?.routeNames?.includes(routeName)) return nav;\n }\n return null;\n}\n\n/** From Chat: swap Chat for Shop so Back returns to Home, not an empty thread. */\nexport function replaceWithShopNative(\n navigation: { dispatch: (action: unknown) => void; replace?: (name: string, params?: object) => void },\n params: Record<string, unknown>,\n) {\n if (typeof navigation.replace === 'function') {\n navigation.replace(SHOP_ROUTE, params);\n return;\n }\n navigation.dispatch(StackActions.replace(SHOP_ROUTE, params));\n}\n\n/** Push Shop on top of the current screen (keeps an existing Chat thread). */\nexport function pushShopNative(navigation: { dispatch: (action: unknown) => void }, params: Record<string, unknown>) {\n navigation.dispatch(CommonActions.navigate({ name: SHOP_ROUTE, params }));\n}\n\n/**\n * From Home: open Shop and drop leftover Chat routes so Back is Home,\n * not a blank Chat that never received the shopping message.\n *\n * Home is a DRAWER screen and Shop is a route of the ROOT stack around it. The\n * reset therefore has to run on the stack: built from the drawer's state it\n * names routes the drawer does not have, every router answers it with `null`,\n * and a release build drops the action without a word - the composer had\n * already been cleared, so the person's message and photos were simply gone.\n *\n * Returns false when no navigator owns the Shop route, so the caller keeps the\n * message and takes the chat path instead of losing the turn.\n */\nexport function openShopFromHome(navigation: ShopNavigation, params: Record<string, unknown>): boolean {\n const stack = navigatorOwning(navigation, SHOP_ROUTE);\n if (!stack) return false;\n stack.dispatch((state: StackState) => {\n const kept = state.routes.filter(\n (route) => !isBareChatRoute(route.name) && !/(^|\\.)ShoppingNative$/.test(route.name),\n );\n const routes = [...kept, { name: SHOP_ROUTE, params }];\n return CommonActions.reset({\n ...state,\n routes,\n index: routes.length - 1,\n });\n });\n return true;\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;AAEA,MAAM,UAAa,GAAA,0BAAA;AAiBZ,SAAS,kBAAkB,IAUN,EAAA;AA7B5B,EAAA,IAAA,EAAA;AA8BE,EAAM,MAAA,MAAA,GAAkC,aAClC,CAAA,cAAA,CAAA,cAAA,CAAA,EAAA,EAAA,IAAA,CAAK,OAAU,GAAA;AAAA,IACjB,SAAS,IAAK,CAAA;AAAA,GACZ,GAAA,EACA,CAAA,EAAA,IAAA,CAAK,SAAY,GAAA;AAAA,IACnB,WAAW,IAAK,CAAA;AAAA,GAClB,GAAI,EANkC,CAAA,EAAA;AAAA,IAOtC,mBAAmB,IAAK,CAAA,KAAA;AAAA,IACxB,SAAW,EAAA;AAAA,GACb,CAAA;AACA,EAAA,IAAA,CAAI,UAAK,KAAL,KAAA,IAAA,GAAA,MAAA,GAAA,EAAA,CAAY,MAAQ,EAAA,MAAA,CAAO,0BAA0B,IAAK,CAAA,KAAA;AAC9D,EAAO,OAAA,MAAA;AACT;AAmBgB,SAAA,qBAAA,CAAsB,YAGnC,MAAiC,EAAA;AAClC,EAAI,IAAA,OAAO,UAAW,CAAA,OAAA,KAAY,UAAY,EAAA;AAC5C,IAAW,UAAA,CAAA,OAAA,CAAQ,YAAY,MAAM,CAAA;AACrC,IAAA;AAAA;AAEF,EAAA,UAAA,CAAW,QAAS,CAAA,YAAA,CAAa,OAAQ,CAAA,UAAA,EAAY,MAAM,CAAC,CAAA;AAC9D;AAGgB,SAAA,cAAA,CAAe,YAE5B,MAAiC,EAAA;AAClC,EAAW,UAAA,CAAA,QAAA,CAAS,cAAc,QAAS,CAAA;AAAA,IACzC,IAAM,EAAA,UAAA;AAAA,IACN;AAAA,GACD,CAAC,CAAA;AACJ"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useState,useRef,useMemo,useCallback,useEffect}from'react';import {useGatewayConnectMutation}from'common/graphql';import {config}from'../config/env-config.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {mintSurfaceSystemToken}from'./mintSurfaceSystemToken.js';import {useObtainSystemToken}from'./useObtainSystemToken.js';import {resolveCdecliAgentTokenGraphqlUrl}from'./systemTokenJwt.js';import {isWorkspaceBootstrapPending}from'./workspaceBootstrapPending.js';var __defProp = Object.defineProperty;
|
|
1
|
+
import {useState,useRef,useMemo,useCallback,useEffect}from'react';import {AppState}from'react-native';import {useGatewayConnectMutation}from'common/graphql';import {config}from'../config/env-config.js';import {usePrerequisiteIds}from'./usePrerequisiteIds.js';import {mintSurfaceSystemToken}from'./mintSurfaceSystemToken.js';import {useObtainSystemToken}from'./useObtainSystemToken.js';import {resolveCdecliAgentTokenGraphqlUrl}from'./systemTokenJwt.js';import {isWorkspaceBootstrapPending}from'./workspaceBootstrapPending.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
3
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
4
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -18,6 +18,7 @@ const CHANNEL_TYPE = "cdecli-serve";
|
|
|
18
18
|
function normalizeGraphqlUrl(url) {
|
|
19
19
|
return url.trim().replace(/\/$/, "");
|
|
20
20
|
}
|
|
21
|
+
const RECONNECT_DELAYS_MS = [2e3, 5e3, 15e3, 3e4, 6e4];
|
|
21
22
|
function useCdecliAutoConnect(isSelected, channelId) {
|
|
22
23
|
const [status, setStatus] = useState("idle");
|
|
23
24
|
const [error, setError] = useState(null);
|
|
@@ -26,6 +27,9 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
26
27
|
const connectingRef = useRef(false);
|
|
27
28
|
const connectedRef = useRef(false);
|
|
28
29
|
const attemptRef = useRef(0);
|
|
30
|
+
const failuresRef = useRef(0);
|
|
31
|
+
const [reconnectTick, setReconnectTick] = useState(0);
|
|
32
|
+
const retryingRef = useRef(false);
|
|
29
33
|
const {
|
|
30
34
|
accountUserId,
|
|
31
35
|
orgName,
|
|
@@ -91,7 +95,9 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
91
95
|
return false;
|
|
92
96
|
}
|
|
93
97
|
setStatus("connected");
|
|
98
|
+
setError(null);
|
|
94
99
|
connectedRef.current = true;
|
|
100
|
+
failuresRef.current = 0;
|
|
95
101
|
return true;
|
|
96
102
|
}, []);
|
|
97
103
|
const runConnect = useCallback(async (opts) => {
|
|
@@ -147,7 +153,8 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
147
153
|
const superseded = () => attempt !== attemptRef.current;
|
|
148
154
|
connectingRef.current = true;
|
|
149
155
|
if (!isSilentReconnect) setStatus("connecting");
|
|
150
|
-
setError(null);
|
|
156
|
+
if (!retryingRef.current) setError(null);
|
|
157
|
+
retryingRef.current = false;
|
|
151
158
|
try {
|
|
152
159
|
const token = await obtainTokenForAgent(false);
|
|
153
160
|
if (superseded()) return;
|
|
@@ -195,7 +202,25 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
195
202
|
}
|
|
196
203
|
};
|
|
197
204
|
doConnect();
|
|
198
|
-
}, [isSelected, obtainTokenForAgent, connectMutation, applyConnectResult, accountId, prerequisitesLoading, accountUserId, channelId, localTokenLoading, mintOnCluster, endpoint]);
|
|
205
|
+
}, [isSelected, obtainTokenForAgent, connectMutation, applyConnectResult, accountId, prerequisitesLoading, accountUserId, channelId, localTokenLoading, mintOnCluster, endpoint, reconnectTick]);
|
|
206
|
+
useEffect(() => {
|
|
207
|
+
if (!isSelected || status !== "error") return void 0;
|
|
208
|
+
const retry = () => {
|
|
209
|
+
if (connectingRef.current || connectedRef.current) return;
|
|
210
|
+
retryingRef.current = true;
|
|
211
|
+
setReconnectTick((tick) => tick + 1);
|
|
212
|
+
};
|
|
213
|
+
const delay = RECONNECT_DELAYS_MS[Math.min(failuresRef.current, RECONNECT_DELAYS_MS.length - 1)];
|
|
214
|
+
failuresRef.current += 1;
|
|
215
|
+
const timer = setTimeout(retry, delay);
|
|
216
|
+
const foreground = AppState.addEventListener("change", (next) => {
|
|
217
|
+
if (next === "active") retry();
|
|
218
|
+
});
|
|
219
|
+
return () => {
|
|
220
|
+
clearTimeout(timer);
|
|
221
|
+
foreground.remove();
|
|
222
|
+
};
|
|
223
|
+
}, [isSelected, status]);
|
|
199
224
|
const connectWithSkill = useCallback(async (skill, model, systemPrompt, skillId) => {
|
|
200
225
|
var _a;
|
|
201
226
|
console.log("[useCdecliAutoConnect] connectWithSkill called with skill=%s skillId=%s model=%s systemPrompt=%d chars", skill, skillId != null ? skillId : "", model, (_a = systemPrompt == null ? void 0 : systemPrompt.length) != null ? _a : 0);
|
|
@@ -264,4 +289,4 @@ function useCdecliAutoConnect(isSelected, channelId) {
|
|
|
264
289
|
obtainAgentToken,
|
|
265
290
|
agentEndpoint: endpoint
|
|
266
291
|
};
|
|
267
|
-
}export{useCdecliAutoConnect};//# sourceMappingURL=useCdecliAutoConnect.js.map
|
|
292
|
+
}export{RECONNECT_DELAYS_MS,useCdecliAutoConnect};//# sourceMappingURL=useCdecliAutoConnect.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCdecliAutoConnect.js","sources":["../../src/hooks/useCdecliAutoConnect.ts"],"sourcesContent":["/**\n * Auto-connects the cdecli-serve messenger-gateway channel when the CDeCLI\n * gateway is selected from the dropdown. Obtains/creates a system token and\n * calls the `gatewayConnect` mutation so the backend provider has a live\n * session with the cdecli-agent endpoint.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { useGatewayConnectMutation } from 'common/graphql';\nimport { config } from '../config/env-config';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\nimport { mintSurfaceSystemToken } from './mintSurfaceSystemToken';\nimport { useObtainSystemToken } from './useObtainSystemToken';\nimport { resolveCdecliAgentTokenGraphqlUrl } from './systemTokenJwt';\nimport { isWorkspaceBootstrapPending } from './workspaceBootstrapPending';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport type CdecliAutoConnectStatus = 'idle' | 'connecting' | 'connected' | 'error';\n\nexport interface UseCdecliAutoConnectResult {\n /** Whether the backend cdecli-serve channel is connected. */\n channelConnected: boolean;\n /** Current connection status. */\n status: CdecliAutoConnectStatus;\n /** Error message if status is 'error'. */\n error: string | null;\n /** Persistence mode reported by the backend provider ('backend' | 'frontend'). */\n persistenceMode: 'backend' | 'frontend' | undefined;\n /** Active skill name on the current CDeCLI session (null if none). */\n activeSkill: string | null;\n /** Per-user accountId used for this gateway connection. */\n accountId: string;\n /** Reconnect the CDeCLI session with a specific skill pre-activated. */\n connectWithSkill: (skill: string, model?: string, systemPrompt?: string, skillId?: string) => Promise<void>;\n /**\n * Revoke the dead system JWT, remint against the agent-trusted backend, and\n * reconnect. Used when cdecli-agent 401s session create (invalid HS256 signature).\n */\n recoverAgentAuth: () => Promise<boolean>;\n /**\n * The token this connection hands the agent: cdecli names the caller by its subject, and the person's Computer\n * is resolved from that. A call the app makes on that Computer itself (the one-time code card typing into the\n * page) carries the same token, so it reaches the Computer the agent is driving - another token's subject is a\n * different Computer, or none. Null when it cannot be had (not signed in yet, mint failed).\n */\n obtainAgentToken: () => Promise<string | null>;\n /** The cdecli-agent this connection talks to (CDECLI_AGENT_ENDPOINT). */\n agentEndpoint: string;\n}\n\n// ─── Constants ───────────────────────────────────────────────────\n\nconst CHANNEL_TYPE = 'cdecli-serve';\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\n// ─── Hook ────────────────────────────────────────────────────────\n\n/**\n * When `isSelected` is true, auto-connects the cdecli-serve channel on the\n * messenger-gateway backend using the hosted endpoint and a system token.\n */\nexport function useCdecliAutoConnect(isSelected: boolean, channelId?: string): UseCdecliAutoConnectResult {\n const [status, setStatus] = useState<CdecliAutoConnectStatus>('idle');\n const [error, setError] = useState<string | null>(null);\n const [persistenceMode, setPersistenceMode] = useState<'backend' | 'frontend' | undefined>(undefined);\n const [activeSkill, setActiveSkill] = useState<string | null>(null);\n const connectingRef = useRef(false);\n const connectedRef = useRef(false);\n /**\n * Each connect takes a number; only the latest may write the status. A chat switch\n * starts a new connect while the last one may still be minting its token, and that\n * superseded attempt failing later must not mark the live connection as down (the\n * composer then reads \"Connecting…\" and refuses to send while the agent works).\n */\n const attemptRef = useRef(0);\n\n const { accountUserId, orgName, projectId, tagId, loading: prerequisitesLoading } = usePrerequisiteIds();\n\n // Per-user accountId so each user gets their own cdecli-agent session.\n // Wait for the profile query to load before connecting — otherwise all\n // users fall back to 'default' and share a single cdecli-agent session.\n const accountId = accountUserId ?? 'default';\n\n const prevChannelIdRef = useRef<string | undefined>(channelId);\n /** Why the last cluster mint returned null, so callers can show the real reason. */\n const mintErrorRef = useRef<string | null>(null);\n\n const [connectMutation] = useGatewayConnectMutation();\n\n const endpoint = (config.CDECLI_AGENT_ENDPOINT || 'https://cdecli-agent.cdebase.dev').trim();\n const tokenGraphqlUrl = useMemo(\n () => resolveCdecliAgentTokenGraphqlUrl(config.GRAPHQL_URL, endpoint, config.CANVAS_BACKEND_GRAPHQL_URL),\n [endpoint],\n );\n const mintOnCluster = normalizeGraphqlUrl(tokenGraphqlUrl) !== normalizeGraphqlUrl(config.GRAPHQL_URL);\n\n const {\n loading: localTokenLoading,\n obtainSystemToken,\n regenerateSystemToken,\n } = useObtainSystemToken(isSelected && !mintOnCluster);\n\n const obtainTokenForAgent = useCallback(\n async (discardExistingRow = false): Promise<string | null> => {\n if (mintOnCluster) {\n // Same gate web applies before minting (browser useCdecliAutoConnect):\n // no org/project yet means the workspace bootstrap is still running and\n // scope discovery would fail. Skip quietly - the effect re-runs once\n // usePrerequisiteIds resolves them.\n if (!orgName || !projectId) {\n mintErrorRef.current = 'Your workspace is still being set up - this will connect once it finishes.';\n return null;\n }\n try {\n const minted = await mintSurfaceSystemToken({\n graphqlUrl: tokenGraphqlUrl,\n // Preferred scope: kept whenever the cluster knows it (see mintScope.ts).\n orgName,\n projectId,\n tagId,\n discoverScope: true,\n forceRemint: discardExistingRow,\n });\n mintErrorRef.current = null;\n return minted.token?.trim() || null;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const pending = isWorkspaceBootstrapPending(msg);\n // Keep the specific reason. The generic caller message used to hide\n // genuinely actionable text like \"No organization on the canvas\n // backend. Sign in once in the canvas harness, then retry.\"\n mintErrorRef.current = pending ? `${msg} (minting against ${tokenGraphqlUrl})` : msg;\n if (pending) {\n console.warn('[useCdecliAutoConnect] cluster system-token mint not ready yet:', msg);\n } else {\n console.error('[useCdecliAutoConnect] cluster system-token mint failed:', err);\n }\n return null;\n }\n }\n return discardExistingRow ? regenerateSystemToken() : obtainSystemToken();\n },\n [mintOnCluster, tokenGraphqlUrl, orgName, projectId, tagId, obtainSystemToken, regenerateSystemToken],\n );\n\n const applyConnectResult = useCallback((data: { gatewayConnect?: unknown } | null | undefined) => {\n const result = data?.gatewayConnect as\n | {\n persistenceMode?: string | null;\n state?: string | null;\n connected?: boolean | null;\n lastError?: string | null;\n }\n | undefined;\n if (result?.persistenceMode) {\n setPersistenceMode(result.persistenceMode as 'backend' | 'frontend');\n }\n if (result?.state === 'ERROR') {\n setStatus('error');\n setError(result.lastError ?? 'Failed to connect to CDeCLI agent');\n connectedRef.current = false;\n return false;\n }\n setStatus('connected');\n connectedRef.current = true;\n return true;\n }, []);\n\n const runConnect = useCallback(\n async (opts: {\n attempt: number;\n token: string | null;\n skill?: string;\n skillId?: string;\n model?: string;\n systemPrompt?: string;\n }) => {\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint,\n ...(opts.token ? { token: opts.token } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n ...(opts.skill ? { skill: opts.skill } : {}),\n ...(opts.skillId ? { skillId: opts.skillId } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n ...(opts.systemPrompt ? { systemPrompt: opts.systemPrompt } : {}),\n },\n },\n },\n });\n // A newer connect owns the status now; this result is not the current one.\n if (opts.attempt !== attemptRef.current) return true;\n return applyConnectResult(data);\n },\n [connectMutation, accountId, endpoint, channelId, applyConnectResult],\n );\n\n // Auto-connect when CDeCLI gateway is selected\n useEffect(() => {\n if (!isSelected) {\n connectingRef.current = false;\n return;\n }\n\n if (prerequisitesLoading || !accountUserId) return;\n\n if (!mintOnCluster && localTokenLoading) return;\n\n let isSilentReconnect = false;\n if (connectedRef.current || connectingRef.current) {\n if (channelId && prevChannelIdRef.current !== channelId) {\n isSilentReconnect = connectedRef.current;\n prevChannelIdRef.current = channelId;\n connectedRef.current = false;\n } else {\n return;\n }\n }\n\n const doConnect = async () => {\n attemptRef.current += 1;\n const attempt = attemptRef.current;\n const superseded = () => attempt !== attemptRef.current;\n connectingRef.current = true;\n if (!isSilentReconnect) setStatus('connecting');\n setError(null);\n\n try {\n const token = await obtainTokenForAgent(false);\n if (superseded()) return;\n if (!token) {\n if (mintOnCluster) {\n setStatus('error');\n setError(mintErrorRef.current ?? 'Could not mint a system token for the hosted CDeCLI agent.');\n connectedRef.current = false;\n return;\n }\n console.warn('[useCdecliAutoConnect] No system token, connecting without auth');\n }\n\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint,\n ...(token ? { token } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n },\n },\n },\n });\n\n if (superseded()) return;\n console.log(\n '[useCdecliAutoConnect] connectMutation result (account=%s mintOnCluster=%s):',\n accountId,\n mintOnCluster,\n JSON.stringify(data),\n );\n applyConnectResult(data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (superseded()) {\n console.warn('[useCdecliAutoConnect] superseded connect failed (ignored):', msg);\n return;\n }\n console.error('[useCdecliAutoConnect] connect error:', msg);\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n } finally {\n if (!superseded()) connectingRef.current = false;\n }\n };\n\n doConnect();\n }, [\n isSelected,\n obtainTokenForAgent,\n connectMutation,\n applyConnectResult,\n accountId,\n prerequisitesLoading,\n accountUserId,\n channelId,\n localTokenLoading,\n mintOnCluster,\n endpoint,\n ]);\n\n const connectWithSkill = useCallback(\n async (skill: string, model?: string, systemPrompt?: string, skillId?: string) => {\n console.log(\n '[useCdecliAutoConnect] connectWithSkill called with skill=%s skillId=%s model=%s systemPrompt=%d chars',\n skill,\n skillId ?? '',\n model,\n systemPrompt?.length ?? 0,\n );\n attemptRef.current += 1;\n const attempt = attemptRef.current;\n setStatus('connecting');\n setError(null);\n\n try {\n const token = await obtainTokenForAgent(false);\n await runConnect({ attempt, token, skill, skillId, model, systemPrompt });\n setActiveSkill(skill);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] connectWithSkill error:', msg);\n if (attempt !== attemptRef.current) return;\n setStatus('error');\n setError(msg);\n }\n },\n [obtainTokenForAgent, runConnect],\n );\n\n const recoverAgentAuth = useCallback(async (): Promise<boolean> => {\n attemptRef.current += 1;\n const attempt = attemptRef.current;\n try {\n const token = await obtainTokenForAgent(true);\n // A newer connect took over; it reveals the token this one reminted.\n if (attempt !== attemptRef.current) return true;\n if (!token) {\n setStatus('error');\n setError(\n mintErrorRef.current ?? 'Could not remint the CDeCLI system token. Sign in again, then retry.',\n );\n connectedRef.current = false;\n return false;\n }\n const ok = await runConnect({ attempt, token });\n if (ok && attempt === attemptRef.current) setError(null);\n return ok;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] recoverAgentAuth error:', msg);\n if (attempt !== attemptRef.current) return false;\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n return false;\n }\n }, [obtainTokenForAgent, runConnect]);\n\n // The same token the connect above hands the agent (a reveal of the one system token row, or the cluster mint),\n // never a fresh one: rotating it here would leave the chat's session on a revoked token.\n const obtainAgentToken = useCallback(() => obtainTokenForAgent(false), [obtainTokenForAgent]);\n\n return {\n channelConnected: status === 'connected',\n status,\n error,\n persistenceMode,\n activeSkill,\n accountId,\n connectWithSkill,\n recoverAgentAuth,\n obtainAgentToken,\n agentEndpoint: endpoint,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCdecliAutoConnect.js","sources":["../../src/hooks/useCdecliAutoConnect.ts"],"sourcesContent":["/**\n * Auto-connects the cdecli-serve messenger-gateway channel when the CDeCLI\n * gateway is selected from the dropdown. Obtains/creates a system token and\n * calls the `gatewayConnect` mutation so the backend provider has a live\n * session with the cdecli-agent endpoint.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { AppState } from 'react-native';\nimport { useGatewayConnectMutation } from 'common/graphql';\nimport { config } from '../config/env-config';\nimport { usePrerequisiteIds } from './usePrerequisiteIds';\nimport { mintSurfaceSystemToken } from './mintSurfaceSystemToken';\nimport { useObtainSystemToken } from './useObtainSystemToken';\nimport { resolveCdecliAgentTokenGraphqlUrl } from './systemTokenJwt';\nimport { isWorkspaceBootstrapPending } from './workspaceBootstrapPending';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport type CdecliAutoConnectStatus = 'idle' | 'connecting' | 'connected' | 'error';\n\nexport interface UseCdecliAutoConnectResult {\n /** Whether the backend cdecli-serve channel is connected. */\n channelConnected: boolean;\n /** Current connection status. */\n status: CdecliAutoConnectStatus;\n /** Error message if status is 'error'. */\n error: string | null;\n /** Persistence mode reported by the backend provider ('backend' | 'frontend'). */\n persistenceMode: 'backend' | 'frontend' | undefined;\n /** Active skill name on the current CDeCLI session (null if none). */\n activeSkill: string | null;\n /** Per-user accountId used for this gateway connection. */\n accountId: string;\n /** Reconnect the CDeCLI session with a specific skill pre-activated. */\n connectWithSkill: (skill: string, model?: string, systemPrompt?: string, skillId?: string) => Promise<void>;\n /**\n * Revoke the dead system JWT, remint against the agent-trusted backend, and\n * reconnect. Used when cdecli-agent 401s session create (invalid HS256 signature).\n */\n recoverAgentAuth: () => Promise<boolean>;\n /**\n * The token this connection hands the agent: cdecli names the caller by its subject, and the person's Computer\n * is resolved from that. A call the app makes on that Computer itself (the one-time code card typing into the\n * page) carries the same token, so it reaches the Computer the agent is driving - another token's subject is a\n * different Computer, or none. Null when it cannot be had (not signed in yet, mint failed).\n */\n obtainAgentToken: () => Promise<string | null>;\n /** The cdecli-agent this connection talks to (CDECLI_AGENT_ENDPOINT). */\n agentEndpoint: string;\n}\n\n// ─── Constants ───────────────────────────────────────────────────\n\nconst CHANNEL_TYPE = 'cdecli-serve';\n\nfunction normalizeGraphqlUrl(url: string): string {\n return url.trim().replace(/\\/$/, '');\n}\n\n/**\n * After a failed connect, try again on this schedule, then every minute while the\n * gateway stays selected. Without it one failed connect (a backend restart, a\n * dropped network) left the chat disconnected until the user switched chats or\n * restarted the app - and messages parked for \"as soon as it connects\" sat there.\n */\nexport const RECONNECT_DELAYS_MS = [2_000, 5_000, 15_000, 30_000, 60_000] as const;\n\n// ─── Hook ────────────────────────────────────────────────────────\n\n/**\n * When `isSelected` is true, auto-connects the cdecli-serve channel on the\n * messenger-gateway backend using the hosted endpoint and a system token.\n */\nexport function useCdecliAutoConnect(isSelected: boolean, channelId?: string): UseCdecliAutoConnectResult {\n const [status, setStatus] = useState<CdecliAutoConnectStatus>('idle');\n const [error, setError] = useState<string | null>(null);\n const [persistenceMode, setPersistenceMode] = useState<'backend' | 'frontend' | undefined>(undefined);\n const [activeSkill, setActiveSkill] = useState<string | null>(null);\n const connectingRef = useRef(false);\n const connectedRef = useRef(false);\n /**\n * Each connect takes a number; only the latest may write the status. A chat switch\n * starts a new connect while the last one may still be minting its token, and that\n * superseded attempt failing later must not mark the live connection as down (the\n * composer then reads \"Connecting…\" and refuses to send while the agent works).\n */\n const attemptRef = useRef(0);\n /** Failed connects in a row; picks the next retry's delay. */\n const failuresRef = useRef(0);\n /** Bumped to run the connect effect again after a failure. */\n const [reconnectTick, setReconnectTick] = useState(0);\n /** The connect in flight is a retry: the error on screen stays until it succeeds. */\n const retryingRef = useRef(false);\n\n const { accountUserId, orgName, projectId, tagId, loading: prerequisitesLoading } = usePrerequisiteIds();\n\n // Per-user accountId so each user gets their own cdecli-agent session.\n // Wait for the profile query to load before connecting — otherwise all\n // users fall back to 'default' and share a single cdecli-agent session.\n const accountId = accountUserId ?? 'default';\n\n const prevChannelIdRef = useRef<string | undefined>(channelId);\n /** Why the last cluster mint returned null, so callers can show the real reason. */\n const mintErrorRef = useRef<string | null>(null);\n\n const [connectMutation] = useGatewayConnectMutation();\n\n const endpoint = (config.CDECLI_AGENT_ENDPOINT || 'https://cdecli-agent.cdebase.dev').trim();\n const tokenGraphqlUrl = useMemo(\n () => resolveCdecliAgentTokenGraphqlUrl(config.GRAPHQL_URL, endpoint, config.CANVAS_BACKEND_GRAPHQL_URL),\n [endpoint],\n );\n const mintOnCluster = normalizeGraphqlUrl(tokenGraphqlUrl) !== normalizeGraphqlUrl(config.GRAPHQL_URL);\n\n const {\n loading: localTokenLoading,\n obtainSystemToken,\n regenerateSystemToken,\n } = useObtainSystemToken(isSelected && !mintOnCluster);\n\n const obtainTokenForAgent = useCallback(\n async (discardExistingRow = false): Promise<string | null> => {\n if (mintOnCluster) {\n // Same gate web applies before minting (browser useCdecliAutoConnect):\n // no org/project yet means the workspace bootstrap is still running and\n // scope discovery would fail. Skip quietly - the effect re-runs once\n // usePrerequisiteIds resolves them.\n if (!orgName || !projectId) {\n mintErrorRef.current = 'Your workspace is still being set up - this will connect once it finishes.';\n return null;\n }\n try {\n const minted = await mintSurfaceSystemToken({\n graphqlUrl: tokenGraphqlUrl,\n // Preferred scope: kept whenever the cluster knows it (see mintScope.ts).\n orgName,\n projectId,\n tagId,\n discoverScope: true,\n forceRemint: discardExistingRow,\n });\n mintErrorRef.current = null;\n return minted.token?.trim() || null;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n const pending = isWorkspaceBootstrapPending(msg);\n // Keep the specific reason. The generic caller message used to hide\n // genuinely actionable text like \"No organization on the canvas\n // backend. Sign in once in the canvas harness, then retry.\"\n mintErrorRef.current = pending ? `${msg} (minting against ${tokenGraphqlUrl})` : msg;\n if (pending) {\n console.warn('[useCdecliAutoConnect] cluster system-token mint not ready yet:', msg);\n } else {\n console.error('[useCdecliAutoConnect] cluster system-token mint failed:', err);\n }\n return null;\n }\n }\n return discardExistingRow ? regenerateSystemToken() : obtainSystemToken();\n },\n [mintOnCluster, tokenGraphqlUrl, orgName, projectId, tagId, obtainSystemToken, regenerateSystemToken],\n );\n\n const applyConnectResult = useCallback((data: { gatewayConnect?: unknown } | null | undefined) => {\n const result = data?.gatewayConnect as\n | {\n persistenceMode?: string | null;\n state?: string | null;\n connected?: boolean | null;\n lastError?: string | null;\n }\n | undefined;\n if (result?.persistenceMode) {\n setPersistenceMode(result.persistenceMode as 'backend' | 'frontend');\n }\n if (result?.state === 'ERROR') {\n setStatus('error');\n setError(result.lastError ?? 'Failed to connect to CDeCLI agent');\n connectedRef.current = false;\n return false;\n }\n setStatus('connected');\n setError(null);\n connectedRef.current = true;\n failuresRef.current = 0;\n return true;\n }, []);\n\n const runConnect = useCallback(\n async (opts: {\n attempt: number;\n token: string | null;\n skill?: string;\n skillId?: string;\n model?: string;\n systemPrompt?: string;\n }) => {\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint,\n ...(opts.token ? { token: opts.token } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n ...(opts.skill ? { skill: opts.skill } : {}),\n ...(opts.skillId ? { skillId: opts.skillId } : {}),\n ...(opts.model ? { model: opts.model } : {}),\n ...(opts.systemPrompt ? { systemPrompt: opts.systemPrompt } : {}),\n },\n },\n },\n });\n // A newer connect owns the status now; this result is not the current one.\n if (opts.attempt !== attemptRef.current) return true;\n return applyConnectResult(data);\n },\n [connectMutation, accountId, endpoint, channelId, applyConnectResult],\n );\n\n // Auto-connect when CDeCLI gateway is selected\n useEffect(() => {\n if (!isSelected) {\n connectingRef.current = false;\n return;\n }\n\n if (prerequisitesLoading || !accountUserId) return;\n\n if (!mintOnCluster && localTokenLoading) return;\n\n let isSilentReconnect = false;\n if (connectedRef.current || connectingRef.current) {\n if (channelId && prevChannelIdRef.current !== channelId) {\n isSilentReconnect = connectedRef.current;\n prevChannelIdRef.current = channelId;\n connectedRef.current = false;\n } else {\n return;\n }\n }\n\n const doConnect = async () => {\n attemptRef.current += 1;\n const attempt = attemptRef.current;\n const superseded = () => attempt !== attemptRef.current;\n connectingRef.current = true;\n if (!isSilentReconnect) setStatus('connecting');\n if (!retryingRef.current) setError(null);\n retryingRef.current = false;\n\n try {\n const token = await obtainTokenForAgent(false);\n if (superseded()) return;\n if (!token) {\n if (mintOnCluster) {\n setStatus('error');\n setError(mintErrorRef.current ?? 'Could not mint a system token for the hosted CDeCLI agent.');\n connectedRef.current = false;\n return;\n }\n console.warn('[useCdecliAutoConnect] No system token, connecting without auth');\n }\n\n const { data } = await connectMutation({\n variables: {\n input: {\n channelType: CHANNEL_TYPE,\n accountId,\n options: {\n endpoint,\n ...(token ? { token } : {}),\n ...(channelId ? { chatId: channelId } : {}),\n },\n },\n },\n });\n\n if (superseded()) return;\n console.log(\n '[useCdecliAutoConnect] connectMutation result (account=%s mintOnCluster=%s):',\n accountId,\n mintOnCluster,\n JSON.stringify(data),\n );\n applyConnectResult(data);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n if (superseded()) {\n console.warn('[useCdecliAutoConnect] superseded connect failed (ignored):', msg);\n return;\n }\n console.error('[useCdecliAutoConnect] connect error:', msg);\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n } finally {\n if (!superseded()) connectingRef.current = false;\n }\n };\n\n doConnect();\n }, [\n isSelected,\n obtainTokenForAgent,\n connectMutation,\n applyConnectResult,\n accountId,\n prerequisitesLoading,\n accountUserId,\n channelId,\n localTokenLoading,\n mintOnCluster,\n endpoint,\n reconnectTick,\n ]);\n\n // A failed connect is retried with backoff, and at once when the app comes back\n // to the foreground - the usual moment a dropped connection is back.\n useEffect(() => {\n if (!isSelected || status !== 'error') return undefined;\n const retry = () => {\n if (connectingRef.current || connectedRef.current) return;\n retryingRef.current = true;\n setReconnectTick((tick) => tick + 1);\n };\n const delay = RECONNECT_DELAYS_MS[Math.min(failuresRef.current, RECONNECT_DELAYS_MS.length - 1)];\n failuresRef.current += 1;\n const timer = setTimeout(retry, delay);\n const foreground = AppState.addEventListener('change', (next) => {\n if (next === 'active') retry();\n });\n return () => {\n clearTimeout(timer);\n foreground.remove();\n };\n }, [isSelected, status]);\n\n const connectWithSkill = useCallback(\n async (skill: string, model?: string, systemPrompt?: string, skillId?: string) => {\n console.log(\n '[useCdecliAutoConnect] connectWithSkill called with skill=%s skillId=%s model=%s systemPrompt=%d chars',\n skill,\n skillId ?? '',\n model,\n systemPrompt?.length ?? 0,\n );\n attemptRef.current += 1;\n const attempt = attemptRef.current;\n setStatus('connecting');\n setError(null);\n\n try {\n const token = await obtainTokenForAgent(false);\n await runConnect({ attempt, token, skill, skillId, model, systemPrompt });\n setActiveSkill(skill);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] connectWithSkill error:', msg);\n if (attempt !== attemptRef.current) return;\n setStatus('error');\n setError(msg);\n }\n },\n [obtainTokenForAgent, runConnect],\n );\n\n const recoverAgentAuth = useCallback(async (): Promise<boolean> => {\n attemptRef.current += 1;\n const attempt = attemptRef.current;\n try {\n const token = await obtainTokenForAgent(true);\n // A newer connect took over; it reveals the token this one reminted.\n if (attempt !== attemptRef.current) return true;\n if (!token) {\n setStatus('error');\n setError(\n mintErrorRef.current ?? 'Could not remint the CDeCLI system token. Sign in again, then retry.',\n );\n connectedRef.current = false;\n return false;\n }\n const ok = await runConnect({ attempt, token });\n if (ok && attempt === attemptRef.current) setError(null);\n return ok;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliAutoConnect] recoverAgentAuth error:', msg);\n if (attempt !== attemptRef.current) return false;\n setStatus('error');\n setError(msg);\n connectedRef.current = false;\n return false;\n }\n }, [obtainTokenForAgent, runConnect]);\n\n // The same token the connect above hands the agent (a reveal of the one system token row, or the cluster mint),\n // never a fresh one: rotating it here would leave the chat's session on a revoked token.\n const obtainAgentToken = useCallback(() => obtainTokenForAgent(false), [obtainTokenForAgent]);\n\n return {\n channelConnected: status === 'connected',\n status,\n error,\n persistenceMode,\n activeSkill,\n accountId,\n connectWithSkill,\n recoverAgentAuth,\n obtainAgentToken,\n agentEndpoint: endpoint,\n };\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {useRef,useCallback,useEffect}from'react';import {AppState}from'react-native';import {useApolloClient}from'@apollo/client/index.js';import {isConnectorReply}from'common';import {useGatewaySendMessageMutation,useMessengerStreamDeltaSubscription,useGatewayInboundMessageByChannelSubscription}from'common/graphql';import {quickReplyViaBrain}from'../services/brainQuickReply.js';import {stripModelCostHeader,MESSENGER_ACTIVE_STREAM_QUERY}from'./messengerStream.js';import {resolveAssistantReply,CHAT_RUN_TIMEOUT_TEXT,EMPTY_CHAT_RESPONSE_TEXT,CDECLI_MAX_RUN_MS}from'../features/chat/emptyChatResponse.js';import {splitPreResponse,joinPreResponse,isFastLaneRunId}from'../features/chat/preResponse.js';import {clientTimezone}from'../utils/clientTimezone.js';var __defProp = Object.defineProperty;
|
|
1
|
+
import {useRef,useCallback,useEffect}from'react';import {AppState}from'react-native';import {useApolloClient}from'@apollo/client/index.js';import {isConnectorReply}from'common';import {useGatewaySendMessageMutation,useMessengerStreamDeltaSubscription,useGatewayInboundMessageByChannelSubscription}from'common/graphql';import {quickReplyViaBrain}from'../services/brainQuickReply.js';import {stripModelCostHeader,MESSENGER_ACTIVE_STREAM_QUERY}from'./messengerStream.js';import {resolveAssistantReply,CHAT_RUN_TIMEOUT_TEXT,EMPTY_CHAT_RESPONSE_TEXT,CDECLI_MAX_RUN_MS}from'../features/chat/emptyChatResponse.js';import {splitPreResponse,joinPreResponse,isFastLaneRunId}from'../features/chat/preResponse.js';import {clientTimezone}from'../utils/clientTimezone.js';import {isAgentJwtRejected}from'./systemTokenJwt.js';var __defProp = Object.defineProperty;
|
|
2
2
|
var __defProps = Object.defineProperties;
|
|
3
3
|
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
|
|
4
4
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
@@ -23,6 +23,10 @@ const GATEWAY_NOTICE_ID_PREFIX = "handoff-";
|
|
|
23
23
|
function isNoActiveSessionError(message) {
|
|
24
24
|
return /no active session/i.test(message);
|
|
25
25
|
}
|
|
26
|
+
function isTransportFailure(err) {
|
|
27
|
+
if (err instanceof TypeError) return true;
|
|
28
|
+
return Boolean(err && typeof err === "object" && err.networkError);
|
|
29
|
+
}
|
|
26
30
|
const CDECLI_RESPONSE_TIMEOUT_MS = 3e5;
|
|
27
31
|
function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, skill) {
|
|
28
32
|
const [sendMutation] = useGatewaySendMessageMutation();
|
|
@@ -343,10 +347,10 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
343
347
|
return true;
|
|
344
348
|
} catch (err) {
|
|
345
349
|
const msg = err instanceof Error ? err.message : String(err);
|
|
346
|
-
cbRef.current.onError(msg);
|
|
347
350
|
pendingRef.current = null;
|
|
348
351
|
runStartedAtRef.current = null;
|
|
349
352
|
clearRunTimers();
|
|
353
|
+
if (!isTransportFailure(err) || isAgentJwtRejected(msg)) cbRef.current.onError(msg);
|
|
350
354
|
return false;
|
|
351
355
|
}
|
|
352
356
|
}, [isConnected, accountId, sendMutation, model, skill, armTimeout, armMaxRun, clearRunTimers]);
|
|
@@ -539,4 +543,4 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
539
543
|
steer,
|
|
540
544
|
cancel
|
|
541
545
|
};
|
|
542
|
-
}export{isNoActiveSessionError,useCdecliChannel};//# sourceMappingURL=useCdecliChannel.js.map
|
|
546
|
+
}export{isNoActiveSessionError,isTransportFailure,useCdecliChannel};//# sourceMappingURL=useCdecliChannel.js.map
|