@adminide-stack/yantra-mobile 12.0.53-alpha.2 → 12.0.53-alpha.21
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/CustomDrawer.js +11 -1
- package/lib/components/CustomDrawer.js.map +1 -1
- package/lib/components/KeyboardComposerDock.js +14 -7
- package/lib/components/KeyboardComposerDock.js.map +1 -1
- package/lib/components/NavigationHeader/NavigationHeader.js +3 -1
- package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -1
- package/lib/features/apps/AppsCatalog.js +7 -1
- package/lib/features/apps/AppsCatalog.js.map +1 -1
- package/lib/features/apps/mobileAppCatalog.js +1 -1
- package/lib/features/apps/mobileAppCatalog.js.map +1 -1
- package/lib/features/canvas/canvasBoardsSnapshot.js +9 -1
- package/lib/features/canvas/canvasBoardsSnapshot.js.map +1 -1
- package/lib/features/chat/ChatTranscript.js +7 -0
- package/lib/features/chat/ChatTranscript.js.map +1 -1
- package/lib/hooks/useCdecliChannel.js +63 -7
- package/lib/hooks/useCdecliChannel.js.map +1 -1
- package/lib/hooks/useChatApi.js +225 -26
- package/lib/hooks/useChatApi.js.map +1 -1
- package/lib/hooks/useChatStream.js +105 -36
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/screens/CanvasBoard/index.js +1 -1
- package/lib/screens/CanvasBoard/index.js.map +1 -1
- package/lib/screens/Chat/index.js +1 -3
- package/lib/screens/Chat/index.js.map +1 -1
- package/lib/screens/Home/components/ChatHistoryLanding.js +18 -6
- package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -1
- package/lib/state/chatThreadMerge.js +76 -2
- package/lib/state/chatThreadMerge.js.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobileAppCatalog.js","sources":["../../../src/features/apps/mobileAppCatalog.ts"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport type { MaterialCommunityIcons } from '@expo/vector-icons';\n\ntype IconName = ComponentProps<typeof MaterialCommunityIcons>['name'];\n\nexport type AppGroupId = 'Create' | 'Automate' | 'Sell' | 'Money' | 'Team' | 'More';\n\nexport type MobileAppItem = {\n key: string;\n label: string;\n icon: IconName;\n group: AppGroupId;\n /** Prompt sent as the first chat turn when the app has no native screen. */\n prompt: string;\n /**\n * Which surface host opens this app, if it has one. `webbuilder` is the\n * historical name of the shared WebView surface shell (`screens/WebBuilder`),\n * which is not web-builder-specific: it takes a `url` route param, mints the\n * system token, injects the bridge + cdecli token, stamps `embed=mobile` and\n * owns the timeout/retry UI. Anything with a deployed surface SPA can use it.\n */\n nativeRoute?: 'webbuilder' | 'canvas-native' | 'characters-native' | 'chat-native';\n /**\n * Deployed surface SPA for this app, as the `<slug>-surface` name. Resolved\n * against SURFACE_URL_TEMPLATE so one env var covers every surface and stays\n * per-environment, instead of 20+ near-identical URLs in eas.json.\n *\n * The slug is the SURFACE's name, which is not always the tile key\n * (issue-ticket -> issues, shop -> shopping, notebook -> notebooklm).\n */\n surfaceSlug?: string;\n /**\n * Explicit env override, for the surfaces people actually run locally\n * (design 5174, canvas 5175, webbuilder). Wins over `surfaceSlug`.\n */\n surfaceUrlKey?: 'WEB_BUILDER_URL' | 'CANVAS_SURFACE_URL' | 'DESIGN_SURFACE_URL';\n};\n\n/**\n * Mobile Apps launcher. Labels, groups, and prompts stay in lockstep with the\n * browser ChatTopBar Apps menu (`packages-modules/account/browser`).\n */\nexport const MOBILE_APP_CATALOG: readonly MobileAppItem[] = [\n {\n key: 'design',\n label: 'Design',\n icon: 'palette-outline',\n group: 'Create',\n prompt: 'Design a 10-slide pitch deck for a coffee subscription startup',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'DESIGN_SURFACE_URL',\n surfaceSlug: 'design',\n },\n {\n key: 'canvas',\n label: 'Canvas',\n icon: 'view-grid-outline',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'CANVAS_SURFACE_URL',\n surfaceSlug: 'canvas',\n },\n {\n key: 'canvas-native',\n label: 'Canvas Native',\n icon: 'vector-square',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n // Renders through Module Federation: the viewer downloads from the\n // canvas-surface origin on open. First native surface; the classic\n // Canvas tile keeps the full web editor in the WebView host.\n nativeRoute: 'canvas-native',\n },\n {\n key: 'webbuilder',\n label: 'Web Builder',\n icon: 'application-outline',\n group: 'Create',\n prompt: 'Build a landing page for my bakery with a menu and a contact form',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'WEB_BUILDER_URL',\n surfaceSlug: 'webbuilder',\n },\n {\n key: 'paper2poster',\n label: 'Paper 2 Poster',\n icon: 'presentation',\n group: 'Create',\n prompt: 'Turn this research paper into a conference poster I can edit.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'paper2poster',\n },\n {\n key: 'codezen',\n label: 'Codezen',\n icon: 'code-braces',\n group: 'Create',\n prompt: 'Clone my repository and fix the failing test',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'codezen',\n },\n {\n key: 'character',\n label: 'Characters',\n icon: 'drama-masks',\n group: 'Create',\n prompt: 'Create a stoic philosophy mentor character I can chat with',\n // Native federated experience (gallery -> detail -> chat); the web\n // surface remains reachable through the surface lane if ever needed.\n nativeRoute: 'characters-native',\n surfaceSlug: 'character',\n },\n {\n key: 'characters-native',\n label: 'Characters Native',\n icon: 'account-star',\n group: 'Create',\n prompt: 'Show me characters I can chat with.',\n nativeRoute: 'characters-native',\n surfaceSlug: 'character',\n },\n {\n key: 'chat-native',\n label: 'Chats',\n icon: 'message-text-outline',\n group: 'Team',\n prompt: 'Show me my conversations.',\n // Person-to-person messaging, federated like canvas: the whole UI\n // downloads from the chat-native surface on open. Not the AI chat -\n // that is the Home tab.\n nativeRoute: 'chat-native',\n },\n {\n key: 'workflow',\n label: 'Workflow',\n icon: 'sitemap',\n group: 'Automate',\n prompt: 'Build a workflow that runs every morning and sends me a summary of what changed.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'workflow',\n },\n {\n key: 'computer',\n label: 'Computer',\n icon: 'monitor',\n group: 'Automate',\n prompt: 'Open a browser and compare the top 5 CRM tools with screenshots',\n },\n {\n key: 'crm',\n label: 'CRM',\n icon: 'account-group-outline',\n group: 'Sell',\n prompt: 'Look at my pipeline and tell me which deals are going cold and what I should send them.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'crm',\n },\n {\n key: 'salesiq',\n label: 'SalesIQ',\n icon: 'radar',\n group: 'Sell',\n prompt: 'Show me who is on my website right now',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'salesiq',\n },\n {\n key: 'inbox',\n label: 'Inbox',\n icon: 'inbox-outline',\n group: 'Sell',\n prompt: 'Go through my inbox, tell me what actually needs a reply, and draft those replies.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'inbox',\n },\n {\n key: 'mailcampaign',\n label: 'Mail Campaigns',\n icon: 'bullhorn-outline',\n group: 'Sell',\n prompt: 'Draft an email campaign announcing what we shipped this month.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'mailcampaign',\n },\n {\n key: 'expenses',\n label: 'Expenses',\n icon: 'receipt',\n group: 'Money',\n prompt: 'Summarise my spend this month and flag anything unusual.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'expenses',\n },\n {\n key: 'cards-wallet',\n label: 'Cards & Wallet',\n icon: 'credit-card-outline',\n group: 'Money',\n prompt: 'Issue a $200 virtual card for monthly ad spend',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'cards-wallet',\n },\n {\n key: 'billing',\n label: 'Billing',\n icon: 'currency-usd',\n group: 'Money',\n prompt: 'Create an invoice for a $2,400 consulting project',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'billing',\n },\n {\n key: 'kpi-motivation',\n label: 'KPI Motivation',\n icon: 'trophy-outline',\n group: 'Team',\n prompt: 'Set up a weekly sales KPI scoreboard for my team',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'kpi-motivation',\n },\n {\n key: 'issue-ticket',\n label: 'Issue Ticket',\n icon: 'bug-outline',\n group: 'Team',\n prompt: 'Create a bug board and triage my open issues',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'issues',\n },\n {\n key: 'notebook',\n label: 'Study',\n icon: 'school-outline',\n group: 'Team',\n prompt: 'Make a study guide from my lecture notes',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'notebooklm',\n },\n {\n key: 'resume',\n label: 'Resume',\n icon: 'file-account-outline',\n group: 'Team',\n prompt: 'Draft a resume for a senior product designer with 8 years of experience',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'resume',\n },\n {\n key: 'job-search',\n label: 'Job Search',\n icon: 'briefcase-outline',\n group: 'Team',\n prompt: 'Find remote senior backend engineer roles and tailor my resume for one',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'job-search',\n },\n {\n key: 'shop',\n label: 'Shop',\n icon: 'cart-outline',\n group: 'More',\n prompt: 'Compare the best options for what I need and show me the tradeoffs.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'shopping',\n },\n {\n key: 'travel',\n label: 'Travel',\n icon: 'airplane',\n group: 'More',\n prompt: 'Plan a 4-day trip for me: flights, somewhere to stay, and what to do each day.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'travel',\n },\n {\n key: 'social-events',\n label: 'yantra.live',\n icon: 'ticket-outline',\n group: 'More',\n prompt: 'What concerts are happening nearby this weekend?',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'social-events',\n },\n];\n\nexport const APP_GROUP_ORDER: readonly AppGroupId[] = ['Create', 'Automate', 'Sell', 'Money', 'Team', 'More'];\n\nexport function matchesAppQuery(label: string, query: string): boolean {\n const q = query.toLowerCase().replace(/[^a-z0-9]/g, '');\n if (!q) return true;\n return label\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n .includes(q);\n}\n\nexport function groupMobileApps(items: readonly MobileAppItem[], query: string) {\n const hits = items.filter((item) => matchesAppQuery(item.label, query));\n return APP_GROUP_ORDER.map((label) => ({\n label,\n items: hits.filter((item) => item.group === label),\n })).filter((group) => group.items.length > 0);\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"mobileAppCatalog.js","sources":["../../../src/features/apps/mobileAppCatalog.ts"],"sourcesContent":["import type { ComponentProps } from 'react';\nimport type { MaterialCommunityIcons } from '@expo/vector-icons';\n\ntype IconName = ComponentProps<typeof MaterialCommunityIcons>['name'];\n\nexport type AppGroupId = 'Create' | 'Automate' | 'Sell' | 'Money' | 'Team' | 'More';\n\nexport type MobileAppItem = {\n key: string;\n label: string;\n icon: IconName;\n group: AppGroupId;\n /** Prompt sent as the first chat turn when the app has no native screen. */\n prompt: string;\n /**\n * Which surface host opens this app, if it has one. `webbuilder` is the\n * historical name of the shared WebView surface shell (`screens/WebBuilder`),\n * which is not web-builder-specific: it takes a `url` route param, mints the\n * system token, injects the bridge + cdecli token, stamps `embed=mobile` and\n * owns the timeout/retry UI. Anything with a deployed surface SPA can use it.\n */\n nativeRoute?: 'webbuilder' | 'canvas-native' | 'characters-native' | 'chat-native' | 'billing-native';\n /**\n * Deployed surface SPA for this app, as the `<slug>-surface` name. Resolved\n * against SURFACE_URL_TEMPLATE so one env var covers every surface and stays\n * per-environment, instead of 20+ near-identical URLs in eas.json.\n *\n * The slug is the SURFACE's name, which is not always the tile key\n * (issue-ticket -> issues, shop -> shopping, notebook -> notebooklm).\n */\n surfaceSlug?: string;\n /**\n * Explicit env override, for the surfaces people actually run locally\n * (design 5174, canvas 5175, webbuilder). Wins over `surfaceSlug`.\n */\n surfaceUrlKey?: 'WEB_BUILDER_URL' | 'CANVAS_SURFACE_URL' | 'DESIGN_SURFACE_URL';\n};\n\n/**\n * Mobile Apps launcher. Labels, groups, and prompts stay in lockstep with the\n * browser ChatTopBar Apps menu (`packages-modules/account/browser`).\n */\nexport const MOBILE_APP_CATALOG: readonly MobileAppItem[] = [\n {\n key: 'design',\n label: 'Design',\n icon: 'palette-outline',\n group: 'Create',\n prompt: 'Design a 10-slide pitch deck for a coffee subscription startup',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'DESIGN_SURFACE_URL',\n surfaceSlug: 'design',\n },\n {\n key: 'canvas',\n label: 'Canvas',\n icon: 'view-grid-outline',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'CANVAS_SURFACE_URL',\n surfaceSlug: 'canvas',\n },\n {\n key: 'canvas-native',\n label: 'Canvas Native',\n icon: 'vector-square',\n group: 'Create',\n prompt: 'Open a canvas and help me lay out the assets for this project.',\n // Renders through Module Federation: the viewer downloads from the\n // canvas-surface origin on open. First native surface; the classic\n // Canvas tile keeps the full web editor in the WebView host.\n nativeRoute: 'canvas-native',\n },\n {\n key: 'webbuilder',\n label: 'Web Builder',\n icon: 'application-outline',\n group: 'Create',\n prompt: 'Build a landing page for my bakery with a menu and a contact form',\n nativeRoute: 'webbuilder',\n surfaceUrlKey: 'WEB_BUILDER_URL',\n surfaceSlug: 'webbuilder',\n },\n {\n key: 'paper2poster',\n label: 'Paper 2 Poster',\n icon: 'presentation',\n group: 'Create',\n prompt: 'Turn this research paper into a conference poster I can edit.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'paper2poster',\n },\n {\n key: 'codezen',\n label: 'Codezen',\n icon: 'code-braces',\n group: 'Create',\n prompt: 'Clone my repository and fix the failing test',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'codezen',\n },\n {\n key: 'character',\n label: 'Characters',\n icon: 'drama-masks',\n group: 'Create',\n prompt: 'Create a stoic philosophy mentor character I can chat with',\n // Native federated experience (gallery -> detail -> chat); the web\n // surface remains reachable through the surface lane if ever needed.\n nativeRoute: 'characters-native',\n surfaceSlug: 'character',\n },\n {\n key: 'characters-native',\n label: 'Characters Native',\n icon: 'account-star',\n group: 'Create',\n prompt: 'Show me characters I can chat with.',\n nativeRoute: 'characters-native',\n surfaceSlug: 'character',\n },\n {\n key: 'chat-native',\n label: 'Chats',\n icon: 'message-text-outline',\n group: 'Team',\n prompt: 'Show me my conversations.',\n // Person-to-person messaging, federated like canvas: the whole UI\n // downloads from the chat-native surface on open. Not the AI chat -\n // that is the Home tab.\n nativeRoute: 'chat-native',\n },\n {\n key: 'workflow',\n label: 'Workflow',\n icon: 'sitemap',\n group: 'Automate',\n prompt: 'Build a workflow that runs every morning and sends me a summary of what changed.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'workflow',\n },\n {\n key: 'computer',\n label: 'Computer',\n icon: 'monitor',\n group: 'Automate',\n prompt: 'Open a browser and compare the top 5 CRM tools with screenshots',\n },\n {\n key: 'crm',\n label: 'CRM',\n icon: 'account-group-outline',\n group: 'Sell',\n prompt: 'Look at my pipeline and tell me which deals are going cold and what I should send them.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'crm',\n },\n {\n key: 'salesiq',\n label: 'SalesIQ',\n icon: 'radar',\n group: 'Sell',\n prompt: 'Show me who is on my website right now',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'salesiq',\n },\n {\n key: 'inbox',\n label: 'Inbox',\n icon: 'inbox-outline',\n group: 'Sell',\n prompt: 'Go through my inbox, tell me what actually needs a reply, and draft those replies.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'inbox',\n },\n {\n key: 'mailcampaign',\n label: 'Mail Campaigns',\n icon: 'bullhorn-outline',\n group: 'Sell',\n prompt: 'Draft an email campaign announcing what we shipped this month.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'mailcampaign',\n },\n {\n key: 'expenses',\n label: 'Expenses',\n icon: 'receipt',\n group: 'Money',\n prompt: 'Summarise my spend this month and flag anything unusual.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'expenses',\n },\n {\n key: 'cards-wallet',\n label: 'Cards & Wallet',\n icon: 'credit-card-outline',\n group: 'Money',\n prompt: 'Issue a $200 virtual card for monthly ad spend',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'cards-wallet',\n },\n {\n key: 'billing',\n label: 'Billing',\n icon: 'currency-usd',\n group: 'Money',\n prompt: 'Create an invoice for a $2,400 consulting project',\n nativeRoute: 'billing-native',\n surfaceSlug: 'billing',\n },\n {\n key: 'kpi-motivation',\n label: 'KPI Motivation',\n icon: 'trophy-outline',\n group: 'Team',\n prompt: 'Set up a weekly sales KPI scoreboard for my team',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'kpi-motivation',\n },\n {\n key: 'issue-ticket',\n label: 'Issue Ticket',\n icon: 'bug-outline',\n group: 'Team',\n prompt: 'Create a bug board and triage my open issues',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'issues',\n },\n {\n key: 'notebook',\n label: 'Study',\n icon: 'school-outline',\n group: 'Team',\n prompt: 'Make a study guide from my lecture notes',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'notebooklm',\n },\n {\n key: 'resume',\n label: 'Resume',\n icon: 'file-account-outline',\n group: 'Team',\n prompt: 'Draft a resume for a senior product designer with 8 years of experience',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'resume',\n },\n {\n key: 'job-search',\n label: 'Job Search',\n icon: 'briefcase-outline',\n group: 'Team',\n prompt: 'Find remote senior backend engineer roles and tailor my resume for one',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'job-search',\n },\n {\n key: 'shop',\n label: 'Shop',\n icon: 'cart-outline',\n group: 'More',\n prompt: 'Compare the best options for what I need and show me the tradeoffs.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'shopping',\n },\n {\n key: 'travel',\n label: 'Travel',\n icon: 'airplane',\n group: 'More',\n prompt: 'Plan a 4-day trip for me: flights, somewhere to stay, and what to do each day.',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'travel',\n },\n {\n key: 'social-events',\n label: 'yantra.live',\n icon: 'ticket-outline',\n group: 'More',\n prompt: 'What concerts are happening nearby this weekend?',\n nativeRoute: 'webbuilder',\n surfaceSlug: 'social-events',\n },\n];\n\nexport const APP_GROUP_ORDER: readonly AppGroupId[] = ['Create', 'Automate', 'Sell', 'Money', 'Team', 'More'];\n\nexport function matchesAppQuery(label: string, query: string): boolean {\n const q = query.toLowerCase().replace(/[^a-z0-9]/g, '');\n if (!q) return true;\n return label\n .toLowerCase()\n .replace(/[^a-z0-9]/g, '')\n .includes(q);\n}\n\nexport function groupMobileApps(items: readonly MobileAppItem[], query: string) {\n const hits = items.filter((item) => matchesAppQuery(item.label, query));\n return APP_GROUP_ORDER.map((label) => ({\n label,\n items: hits.filter((item) => item.group === label),\n })).filter((group) => group.items.length > 0);\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {getCachedCanvasBoards,setCachedCanvasBoards}from'./surfaceIndex.js';var _a;
|
|
2
|
+
let AsyncStorage = null;
|
|
3
|
+
try {
|
|
4
|
+
AsyncStorage = (_a = require("@react-native-async-storage/async-storage").default) != null ? _a : null;
|
|
5
|
+
} catch (e) {
|
|
6
|
+
AsyncStorage = null;
|
|
7
|
+
}
|
|
8
|
+
const SNAPSHOT_KEY = "yantra.canvas-boards.snapshot.v1";
|
|
2
9
|
let lastWritten = "";
|
|
3
10
|
function isBoardSummary(value) {
|
|
4
11
|
if (!value || typeof value !== "object") return false;
|
|
@@ -24,6 +31,7 @@ async function hydrateCanvasBoardsSnapshot(indexUrl) {
|
|
|
24
31
|
if (!trimmed) return null;
|
|
25
32
|
const memory = getCachedCanvasBoards(trimmed);
|
|
26
33
|
if (memory) return memory;
|
|
34
|
+
if (!AsyncStorage) return null;
|
|
27
35
|
try {
|
|
28
36
|
const raw = await AsyncStorage.getItem(SNAPSHOT_KEY);
|
|
29
37
|
const parsed = parseCanvasBoardsSnapshot(raw);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvasBoardsSnapshot.js","sources":["../../../src/features/canvas/canvasBoardsSnapshot.ts"],"sourcesContent":["import
|
|
1
|
+
{"version":3,"file":"canvasBoardsSnapshot.js","sources":["../../../src/features/canvas/canvasBoardsSnapshot.ts"],"sourcesContent":["import { getCachedCanvasBoards, setCachedCanvasBoards, type CanvasBoardSummary } from './surfaceIndex';\n\n// Guarded require, not a top-level import: this module ships OTA inside the\n// federated canvas remote onto host binaries we do not control, and\n// @react-native-async-storage/async-storage THROWS AT MODULE EVAL when its\n// native module is absent (\"NativeModule: AsyncStorage is null\") - which took\n// the whole remote down on binaries built before the dependency existed. Same\n// lesson as the expo-video renderer (ViewerExpose try/require): storage is a\n// best-effort cache, so degrade to memory-only instead of crashing.\ntype StorageLike = { getItem(key: string): Promise<string | null>; setItem(key: string, value: string): Promise<void> };\nlet AsyncStorage: StorageLike | null = null;\ntry {\n // eslint-disable-next-line @typescript-eslint/no-require-imports, global-require\n AsyncStorage = require('@react-native-async-storage/async-storage').default ?? null;\n} catch {\n AsyncStorage = null;\n}\n\nconst SNAPSHOT_KEY = 'yantra.canvas-boards.snapshot.v1';\n\ntype StoredSnapshot = {\n url: string;\n boards: CanvasBoardSummary[];\n};\n\nlet lastWritten = '';\n\nfunction isBoardSummary(value: unknown): value is CanvasBoardSummary {\n if (!value || typeof value !== 'object') return false;\n const row = value as CanvasBoardSummary;\n return typeof row.channelId === 'string' && row.channelId.length > 0 && Boolean(row.board);\n}\n\nexport function parseCanvasBoardsSnapshot(raw: string | null | undefined): StoredSnapshot | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as StoredSnapshot;\n if (!parsed?.url || !Array.isArray(parsed.boards)) return null;\n const boards = parsed.boards.filter(isBoardSummary);\n return { url: parsed.url, boards };\n } catch {\n return null;\n }\n}\n\nexport async function hydrateCanvasBoardsSnapshot(indexUrl: string): Promise<CanvasBoardSummary[] | null> {\n const trimmed = (indexUrl || '').trim();\n if (!trimmed) return null;\n const memory = getCachedCanvasBoards(trimmed);\n if (memory) return memory;\n if (!AsyncStorage) return null;\n try {\n const raw = await AsyncStorage.getItem(SNAPSHOT_KEY);\n const parsed = parseCanvasBoardsSnapshot(raw);\n if (!parsed || parsed.url !== trimmed) return null;\n setCachedCanvasBoards(trimmed, parsed.boards);\n lastWritten = raw ?? lastWritten;\n return parsed.boards;\n } catch {\n return null;\n }\n}\n\nexport function persistCanvasBoardsSnapshot(indexUrl: string, boards: CanvasBoardSummary[]): void {\n const trimmed = (indexUrl || '').trim();\n if (!trimmed) return;\n setCachedCanvasBoards(trimmed, boards);\n const raw = JSON.stringify({ url: trimmed, boards } satisfies StoredSnapshot);\n if (raw === lastWritten) return;\n lastWritten = raw;\n void AsyncStorage?.setItem(SNAPSHOT_KEY, raw).catch(() => {\n /* disk write is best-effort */\n });\n}\n"],"names":[],"mappings":"4EAAA,IAAA,EAAA;AAaA,IAAI,YAAmC,GAAA,IAAA;AACvC,IAAI;AAEF,EAAA,YAAA,GAAA,CAAe,EAAQ,GAAA,OAAA,CAAA,2CAA2C,CAAE,CAAA,OAAA,KAArD,IAAgE,GAAA,EAAA,GAAA,IAAA;AACjF,CAAQ,CAAA,OAAA,CAAA,EAAA;AACN,EAAe,YAAA,GAAA,IAAA;AACjB;AACA,MAAM,YAAe,GAAA,kCAAA;AAKrB,IAAI,WAAc,GAAA,EAAA;AAClB,SAAS,eAAe,KAA6C,EAAA;AACnE,EAAA,IAAI,CAAC,KAAA,IAAS,OAAO,KAAA,KAAU,UAAiB,OAAA,KAAA;AAChD,EAAA,MAAM,GAAM,GAAA,KAAA;AACZ,EAAO,OAAA,OAAO,GAAI,CAAA,SAAA,KAAc,QAAY,IAAA,GAAA,CAAI,UAAU,MAAS,GAAA,CAAA,IAAK,OAAQ,CAAA,GAAA,CAAI,KAAK,CAAA;AAC3F;AACO,SAAS,0BAA0B,GAAuD,EAAA;AAC/F,EAAI,IAAA,CAAC,KAAY,OAAA,IAAA;AACjB,EAAI,IAAA;AACF,IAAM,MAAA,MAAA,GAAS,IAAK,CAAA,KAAA,CAAM,GAAG,CAAA;AAC7B,IAAI,IAAA,EAAC,iCAAQ,GAAO,CAAA,IAAA,CAAC,MAAM,OAAQ,CAAA,MAAA,CAAO,MAAM,CAAA,EAAU,OAAA,IAAA;AAC1D,IAAA,MAAM,MAAS,GAAA,MAAA,CAAO,MAAO,CAAA,MAAA,CAAO,cAAc,CAAA;AAClD,IAAO,OAAA;AAAA,MACL,KAAK,MAAO,CAAA,GAAA;AAAA,MACZ;AAAA,KACF;AAAA,GACM,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX;AACA,eAAsB,4BAA4B,QAAwD,EAAA;AACxG,EAAM,MAAA,OAAA,GAAA,CAAW,QAAY,IAAA,EAAA,EAAI,IAAK,EAAA;AACtC,EAAI,IAAA,CAAC,SAAgB,OAAA,IAAA;AACrB,EAAM,MAAA,MAAA,GAAS,sBAAsB,OAAO,CAAA;AAC5C,EAAA,IAAI,QAAe,OAAA,MAAA;AACnB,EAAI,IAAA,CAAC,cAAqB,OAAA,IAAA;AAC1B,EAAI,IAAA;AACF,IAAA,MAAM,GAAM,GAAA,MAAM,YAAa,CAAA,OAAA,CAAQ,YAAY,CAAA;AACnD,IAAM,MAAA,MAAA,GAAS,0BAA0B,GAAG,CAAA;AAC5C,IAAA,IAAI,CAAC,MAAA,IAAU,MAAO,CAAA,GAAA,KAAQ,SAAgB,OAAA,IAAA;AAC9C,IAAsB,qBAAA,CAAA,OAAA,EAAS,OAAO,MAAM,CAAA;AAC5C,IAAA,WAAA,GAAc,GAAO,IAAA,IAAA,GAAA,GAAA,GAAA,WAAA;AACrB,IAAA,OAAO,MAAO,CAAA,MAAA;AAAA,GACR,CAAA,OAAA,CAAA,EAAA;AACN,IAAO,OAAA,IAAA;AAAA;AAEX"}
|
|
@@ -74,6 +74,13 @@ function ChatTranscript({
|
|
|
74
74
|
const streamProse = displayAskUserAssistantText(rawStream, streamAskAnswered).trim();
|
|
75
75
|
const streamTools = parseToolActivity(stripAskUser(rawStream), true);
|
|
76
76
|
if (!streamProse && streamTools.steps.length === 0) return history;
|
|
77
|
+
if ((last == null ? void 0 : last.role) === "assistant") {
|
|
78
|
+
const lastText = last.content.trim();
|
|
79
|
+
const streamText = rawStream.trim();
|
|
80
|
+
if (lastText === streamText || lastText.startsWith(streamText) || streamText.startsWith(lastText)) {
|
|
81
|
+
return history;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
77
84
|
return [...history, {
|
|
78
85
|
id: "__streaming__",
|
|
79
86
|
role: "assistant",
|
|
@@ -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 } from 'react';\nimport { FlatList, Platform, Pressable, StyleSheet, 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 { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { closeOpenMarkdownFences } from './streamingMarkdown';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean; askAnswered?: boolean };\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\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 const askAnswered =\n Boolean(parseAskUser(msg.content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(msg.content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(msg.content), false);\n const hasAsk = Boolean(parseAskUser(msg.content));\n if (hasAsk && !visible && tools.steps.length === 0 && !(msg.attachments?.length ?? 0)) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(msg.content).trim();\n if (!leftover && !(msg.attachments?.length ?? 0)) return;\n }\n rows.push({ ...msg, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n const rawStream = streamingContent ?? '';\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) return history;\n return [\n ...history,\n {\n id: '__streaming__',\n role: 'assistant',\n content: rawStream,\n streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent]);\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 // Tall bubbles: scrollToEnd can stop at the start of the last cell.\n list.scrollToOffset({ offset: Number.MAX_SAFE_INTEGER, 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, 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}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={7}\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, 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 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.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}>\n <Markdown style={USER_MARKDOWN}>{text}</Markdown>\n </View>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\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 markdownColor={markdownColor}\n codeBg={codeBg}\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 markdownColor,\n codeBg,\n tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n markdownColor: string;\n codeBg: string;\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 const text = parseToolActivity(\n displayAskUserAssistantText(item.content, !!item.askAnswered),\n !!item.streaming,\n ).text;\n\n return (\n <Pressable\n style={[styles.assistantCol, tightTop && styles.assistantColTight]}\n onPress={dismissKeyboard}\n accessible={false}\n >\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? (\n <Markdown style={assistantMarkdownStyle(markdownColor, codeBg)}>\n {item.streaming ? closeOpenMarkdownFences(text) : text}\n </Markdown>\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </Pressable>\n );\n});\n\nconst USER_MARKDOWN = {\n body: { color: '#ffffff', fontSize: 15, lineHeight: 22 },\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\nfunction assistantMarkdownStyle(markdownColor: string, codeBg: string) {\n return {\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700' as const,\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700' as const,\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600' as const,\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' as const },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 2 },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n fence: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n };\n}\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n justifyContent: 'flex-end',\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 assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\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 } from 'react';\nimport { FlatList, Platform, Pressable, StyleSheet, 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 { parseToolActivity } from './toolActivity';\nimport { parseAskUser, stripAskUser, displayAskUserAssistantText } from './askUser';\nimport { collapseDuplicateUserRows, groupConsecutiveRoleRows, type RoleGroup } from './transcriptGroups';\nimport { closeOpenMarkdownFences } from './streamingMarkdown';\n\nexport type TranscriptMessage = {\n id: string;\n role: string;\n content: string;\n attachments?: MessageAttachment[];\n};\n\ntype TranscriptRow = TranscriptMessage & { streaming?: boolean; askAnswered?: boolean };\ntype TranscriptGroup = RoleGroup<TranscriptRow>;\n\nconst USER_BUBBLE_BG = '#2563eb';\n\nexport function ChatTranscript({\n messages,\n streamingContent,\n renderMessageActions,\n listContentStyle,\n isDark = false,\n}: {\n messages: TranscriptMessage[];\n streamingContent?: string;\n renderMessageActions?: (message: { id: string; role: string; content: string }) => React.ReactNode;\n listContentStyle?: StyleProp<ViewStyle>;\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 const askAnswered =\n Boolean(parseAskUser(msg.content)) && messages.slice(index + 1).some((m) => m.role === 'user');\n const visible = displayAskUserAssistantText(msg.content, askAnswered).trim();\n const tools = parseToolActivity(stripAskUser(msg.content), false);\n const hasAsk = Boolean(parseAskUser(msg.content));\n if (hasAsk && !visible && tools.steps.length === 0 && !(msg.attachments?.length ?? 0)) {\n return;\n }\n if (!hasAsk) {\n const leftover = stripAskUser(msg.content).trim();\n if (!leftover && !(msg.attachments?.length ?? 0)) return;\n }\n rows.push({ ...msg, streaming: false, askAnswered });\n });\n return rows;\n }, [messages]);\n\n const rows = useMemo<TranscriptRow[]>(() => {\n const history = collapseDuplicateUserRows(historyRows);\n const rawStream = streamingContent ?? '';\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) return history;\n if (last?.role === 'assistant') {\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 streaming: true,\n askAnswered: streamAskAnswered,\n },\n ];\n }, [historyRows, streamingContent]);\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 // Tall bubbles: scrollToEnd can stop at the start of the last cell.\n list.scrollToOffset({ offset: Number.MAX_SAFE_INTEGER, 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, 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}\n initialNumToRender={12}\n maxToRenderPerBatch={8}\n updateCellsBatchingPeriod={50}\n windowSize={7}\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, 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 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.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}>\n <Markdown style={USER_MARKDOWN}>{text}</Markdown>\n </View>\n ) : null}\n </View>\n );\n })}\n </Pressable>\n );\n }\n\n const markdownColor = isDark ? '#e2e8f0' : '#111827';\n const codeBg = isDark ? '#1e293b' : '#f3f4f6';\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 markdownColor={markdownColor}\n codeBg={codeBg}\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 markdownColor,\n codeBg,\n tightTop,\n renderMessageActions,\n}: {\n item: TranscriptRow;\n isDark: boolean;\n markdownColor: string;\n codeBg: string;\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 const text = parseToolActivity(\n displayAskUserAssistantText(item.content, !!item.askAnswered),\n !!item.streaming,\n ).text;\n\n return (\n <Pressable\n style={[styles.assistantCol, tightTop && styles.assistantColTight]}\n onPress={dismissKeyboard}\n accessible={false}\n >\n {attachments.length > 0 ? (\n <MessageAttachmentPreviews attachments={attachments} align=\"start\" isDark={isDark} />\n ) : null}\n {toolActivity.steps.length > 0 ? (\n <ToolTimeline steps={toolActivity.steps} active={!!item.streaming} isDark={isDark} />\n ) : null}\n {text ? (\n <Markdown style={assistantMarkdownStyle(markdownColor, codeBg)}>\n {item.streaming ? closeOpenMarkdownFences(text) : text}\n </Markdown>\n ) : null}\n {renderMessageActions\n ? renderMessageActions({ id: item.id, role: 'assistant', content: item.content })\n : null}\n </Pressable>\n );\n});\n\nconst USER_MARKDOWN = {\n body: { color: '#ffffff', fontSize: 15, lineHeight: 22 },\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\nfunction assistantMarkdownStyle(markdownColor: string, codeBg: string) {\n return {\n body: { color: markdownColor, fontSize: 15, lineHeight: 22 },\n heading1: {\n fontSize: 18,\n fontWeight: '700' as const,\n marginTop: 12,\n marginBottom: 4,\n color: markdownColor,\n },\n heading2: {\n fontSize: 16,\n fontWeight: '700' as const,\n marginTop: 10,\n marginBottom: 4,\n color: markdownColor,\n },\n heading3: {\n fontSize: 15,\n fontWeight: '600' as const,\n marginTop: 8,\n marginBottom: 4,\n color: markdownColor,\n },\n strong: { fontWeight: '700' as const },\n paragraph: { marginTop: 4, marginBottom: 4 },\n list_item: { marginVertical: 2 },\n bullet_list: { marginVertical: 4 },\n ordered_list: { marginVertical: 4 },\n code_inline: {\n backgroundColor: codeBg,\n paddingHorizontal: 4,\n borderRadius: 4,\n fontSize: 14,\n color: markdownColor,\n },\n code_block: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n fence: {\n backgroundColor: codeBg,\n padding: 12,\n borderRadius: 8,\n marginVertical: 8,\n fontSize: 14,\n color: markdownColor,\n },\n };\n}\n\nconst styles = StyleSheet.create({\n list: {\n flex: 1,\n },\n listContent: {\n paddingTop: 8,\n paddingBottom: 8,\n paddingHorizontal: 8,\n },\n listContentFill: {\n flexGrow: 1,\n justifyContent: 'flex-end',\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 assistantGroup: {\n width: '100%',\n marginBottom: 4,\n },\n assistantCol: {\n width: '100%',\n alignItems: 'flex-start',\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,4 +1,4 @@
|
|
|
1
|
-
import {useRef,useCallback,useEffect}from'react';import {AppState}from'react-native';import {quickReplyViaBrain}from'../services/brainQuickReply.js';import {useGatewaySendMessageMutation,useMessengerStreamDeltaSubscription,useGatewayInboundMessageByChannelSubscription}from'common/graphql';var __defProp = Object.defineProperty;
|
|
1
|
+
import {useRef,useCallback,useEffect}from'react';import {AppState}from'react-native';import {useApolloClient,gql}from'@apollo/client/index.js';import {quickReplyViaBrain}from'../services/brainQuickReply.js';import {useGatewaySendMessageMutation,useMessengerStreamDeltaSubscription,useGatewayInboundMessageByChannelSubscription}from'common/graphql';var __defProp = Object.defineProperty;
|
|
2
2
|
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
|
|
3
3
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
4
4
|
var __propIsEnum = Object.prototype.propertyIsEnumerable;
|
|
@@ -14,6 +14,17 @@ var __spreadValues = (a, b) => {
|
|
|
14
14
|
}
|
|
15
15
|
return a;
|
|
16
16
|
};
|
|
17
|
+
const MESSENGER_ACTIVE_STREAM_QUERY = gql`
|
|
18
|
+
query MessengerActiveStreamResume($channelId: String!) {
|
|
19
|
+
messengerActiveStream(channelId: $channelId) {
|
|
20
|
+
runId
|
|
21
|
+
seq
|
|
22
|
+
channelId
|
|
23
|
+
text
|
|
24
|
+
isFinal
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
`;
|
|
17
28
|
function stripModelCostHeader(content) {
|
|
18
29
|
const normalized = content.replace(/\r\n/g, "\n");
|
|
19
30
|
return normalized.replace(/^\s*(?:[^\w\n]+\s*)?[a-z0-9][a-z0-9._-]*\s*\(\s*\$[\d.]+\s*\/\s*MTok\s+in\s*\)\s*\n+/i, "");
|
|
@@ -31,6 +42,15 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
31
42
|
const reconnectedStreamRef = useRef(false);
|
|
32
43
|
const streamCompletedRef = useRef(false);
|
|
33
44
|
const accumulatedLenRef = useRef(0);
|
|
45
|
+
const lastRunIdRef = useRef(null);
|
|
46
|
+
const apolloClient = useApolloClient();
|
|
47
|
+
const seededChannelRef = useRef(null);
|
|
48
|
+
const ingestAccumulatedText = useCallback((accumulated) => {
|
|
49
|
+
const fullText = stripModelCostHeader(accumulated);
|
|
50
|
+
const newChunk = fullText.substring(accumulatedLenRef.current);
|
|
51
|
+
accumulatedLenRef.current = fullText.length;
|
|
52
|
+
if (newChunk) cbRef.current.onChunk(newChunk);
|
|
53
|
+
}, []);
|
|
34
54
|
const quickReplyShownRef = useRef(false);
|
|
35
55
|
const turnSeqRef = useRef(0);
|
|
36
56
|
const timeoutRef = useRef(null);
|
|
@@ -74,6 +94,10 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
74
94
|
const delta = (_a = data == null ? void 0 : data.data) == null ? void 0 : _a.messengerStreamDelta;
|
|
75
95
|
if (!delta) return;
|
|
76
96
|
if (delta.isFinal) return;
|
|
97
|
+
if (delta.runId && delta.runId !== lastRunIdRef.current) {
|
|
98
|
+
accumulatedLenRef.current = 0;
|
|
99
|
+
}
|
|
100
|
+
if (delta.runId) lastRunIdRef.current = delta.runId;
|
|
77
101
|
if (streamCompletedRef.current) return;
|
|
78
102
|
if (!pendingRef.current) {
|
|
79
103
|
if (!reconnectedStreamRef.current) {
|
|
@@ -87,17 +111,46 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
87
111
|
}
|
|
88
112
|
hasReceivedDeltasRef.current = true;
|
|
89
113
|
armTimeout();
|
|
90
|
-
|
|
91
|
-
const newChunk = fullText.substring(accumulatedLenRef.current);
|
|
92
|
-
accumulatedLenRef.current = fullText.length;
|
|
93
|
-
if (newChunk) {
|
|
94
|
-
cbRef.current.onChunk(newChunk);
|
|
95
|
-
}
|
|
114
|
+
ingestAccumulatedText((_d = delta.text) != null ? _d : "");
|
|
96
115
|
},
|
|
97
116
|
onError: (err) => {
|
|
98
117
|
console.error("[useCdecliChannel] stream delta subscription error:", err);
|
|
99
118
|
}
|
|
100
119
|
});
|
|
120
|
+
useEffect(() => {
|
|
121
|
+
if (streamSkip || !streamChannelId) return void 0;
|
|
122
|
+
if (seededChannelRef.current === streamChannelId) return void 0;
|
|
123
|
+
seededChannelRef.current = streamChannelId;
|
|
124
|
+
accumulatedLenRef.current = 0;
|
|
125
|
+
streamCompletedRef.current = false;
|
|
126
|
+
reconnectedStreamRef.current = false;
|
|
127
|
+
lastRunIdRef.current = null;
|
|
128
|
+
let cancelled = false;
|
|
129
|
+
apolloClient.query({
|
|
130
|
+
query: MESSENGER_ACTIVE_STREAM_QUERY,
|
|
131
|
+
variables: {
|
|
132
|
+
channelId: streamChannelId
|
|
133
|
+
},
|
|
134
|
+
fetchPolicy: "network-only",
|
|
135
|
+
errorPolicy: "all"
|
|
136
|
+
}).then(({
|
|
137
|
+
data
|
|
138
|
+
}) => {
|
|
139
|
+
if (cancelled) return;
|
|
140
|
+
const snapshot = data == null ? void 0 : data.messengerActiveStream;
|
|
141
|
+
if (!(snapshot == null ? void 0 : snapshot.text) || snapshot.isFinal) return;
|
|
142
|
+
if (streamCompletedRef.current) return;
|
|
143
|
+
if (accumulatedLenRef.current > 0) return;
|
|
144
|
+
reconnectedStreamRef.current = true;
|
|
145
|
+
hasReceivedDeltasRef.current = true;
|
|
146
|
+
if (snapshot.runId) lastRunIdRef.current = snapshot.runId;
|
|
147
|
+
ingestAccumulatedText(snapshot.text);
|
|
148
|
+
}).catch(() => {
|
|
149
|
+
});
|
|
150
|
+
return () => {
|
|
151
|
+
cancelled = true;
|
|
152
|
+
};
|
|
153
|
+
}, [streamChannelId, streamSkip, apolloClient, ingestAccumulatedText]);
|
|
101
154
|
useGatewayInboundMessageByChannelSubscription({
|
|
102
155
|
variables: {
|
|
103
156
|
channelId: streamChannelId
|
|
@@ -123,6 +176,7 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
123
176
|
reconnectedStreamRef.current = false;
|
|
124
177
|
streamCompletedRef.current = true;
|
|
125
178
|
accumulatedLenRef.current = 0;
|
|
179
|
+
lastRunIdRef.current = null;
|
|
126
180
|
if (timeoutRef.current) {
|
|
127
181
|
clearTimeout(timeoutRef.current);
|
|
128
182
|
timeoutRef.current = null;
|
|
@@ -144,6 +198,7 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
144
198
|
reconnectedStreamRef.current = false;
|
|
145
199
|
streamCompletedRef.current = false;
|
|
146
200
|
accumulatedLenRef.current = 0;
|
|
201
|
+
lastRunIdRef.current = null;
|
|
147
202
|
quickReplyShownRef.current = false;
|
|
148
203
|
const turn = ++turnSeqRef.current;
|
|
149
204
|
void quickReplyViaBrain(text).then((answer) => {
|
|
@@ -195,6 +250,7 @@ function useCdecliChannel(isConnected, accountId, channelId, callbacks, model, s
|
|
|
195
250
|
reconnectedStreamRef.current = false;
|
|
196
251
|
streamCompletedRef.current = false;
|
|
197
252
|
accumulatedLenRef.current = 0;
|
|
253
|
+
lastRunIdRef.current = null;
|
|
198
254
|
if (timeoutRef.current) {
|
|
199
255
|
clearTimeout(timeoutRef.current);
|
|
200
256
|
timeoutRef.current = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCdecliChannel.js","sources":["../../src/hooks/useCdecliChannel.ts"],"sourcesContent":["/**\n * useCdecliChannel — wires the cdecli-serve messenger-gateway channel into the mobile chat UI.\n *\n * Kept in sync with `packages-modules/account/browser/src/hooks/useCdecliChannel.ts`.\n * When the CDeCLI channel is connected:\n * - `sendMessage(text, chatId, media)` calls `gatewaySendMessage`\n * - `MessengerStreamDelta` subscription delivers streaming chunks via `onChunk`\n * - `GatewayInboundMessageByChannel` delivers the final reply via `onComplete`\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { quickReplyViaBrain } from '../services/brainQuickReply';\nimport {\n useGatewaySendMessageMutation,\n useGatewayInboundMessageByChannelSubscription,\n useMessengerStreamDeltaSubscription,\n} from 'common/graphql';\n\nfunction stripModelCostHeader(content: string): string {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n return normalized.replace(\n /^\\s*(?:[^\\w\\n]+\\s*)?[a-z0-9][a-z0-9._-]*\\s*\\(\\s*\\$[\\d.]+\\s*\\/\\s*MTok\\s+in\\s*\\)\\s*\\n+/i,\n '',\n );\n}\n\n// `error?: undefined` on the success arm keeps `result.error` reachable on the\n// union: with strictNullChecks off (repo-wide), TS will not narrow the\n// discriminated union after an `if (result.ok) continue`.\nexport type SteerResult = { ok: true; error?: undefined } | { ok: false; error: string };\n\nexport interface CdecliChannelCallbacks {\n onChunk: (text: string) => void;\n /**\n * A fast provisional answer from `yantra-brain`, painted while the real agent\n * is still bootstrapping. Distinct from onChunk on purpose: the UI shows it as\n * the reply-so-far and REPLACES it the moment real deltas arrive, so the brain\n * fills the silence without ever stacking on top of the agent's answer.\n */\n onQuickReply?: (text: string) => void;\n onComplete: (text: string) => void;\n onError: (error: string) => void;\n}\n\nexport function isNoActiveSessionError(message: string): boolean {\n return /no active session/i.test(message);\n}\n\n/**\n * Idle window, not a total budget. The timer is re-armed on every streamed\n * delta (a turn that is actively streaming is not stuck), and paused while the\n * app is backgrounded (iOS suspends the WebSocket + throttles JS timers, so a\n * wall-clock timer would otherwise fire a false \"did not respond\" the instant\n * the app returns to the foreground). It fires only after this much CONTINUOUS\n * silence with the app in the foreground.\n */\nconst CDECLI_RESPONSE_TIMEOUT_MS = 300_000;\n\nexport function useCdecliChannel(\n isConnected: boolean,\n accountId: string,\n channelId: string | undefined,\n callbacks: CdecliChannelCallbacks,\n model?: string,\n skill?: string,\n) {\n const [sendMutation] = useGatewaySendMessageMutation();\n const cbRef = useRef(callbacks);\n cbRef.current = callbacks;\n\n const pendingRef = useRef<{ text: string; sentAt: number } | null>(null);\n const hasReceivedDeltasRef = useRef(false);\n const reconnectedStreamRef = useRef(false);\n const streamCompletedRef = useRef(false);\n const accumulatedLenRef = useRef(0);\n // True while a brain quick-reply is what the user is looking at. The first\n // real delta clears the provisional text before painting, so the agent's\n // answer replaces the brain's rather than appending to it.\n const quickReplyShownRef = useRef(false);\n // Identity of the current turn for the brain race. A counter, not a\n // timestamp: two sends can share a millisecond, and a timestamp would then\n // let a brain answer for a superseded turn paint over the live one.\n const turnSeqRef = useRef(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const streamSkip = !channelId;\n const streamChannelId = channelId || accountId;\n\n /** (Re)start the idle timer for the in-flight turn. No-op if none pending. */\n const armTimeout = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n if (!pendingRef.current) {\n timeoutRef.current = null;\n return;\n }\n timeoutRef.current = setTimeout(() => {\n if (pendingRef.current) {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n timeoutRef.current = null;\n cbRef.current.onError(\n 'CDeCLI agent did not respond within 300 seconds. The query may still be processing.',\n );\n }\n }, CDECLI_RESPONSE_TIMEOUT_MS);\n }, []);\n\n // Pause the idle timer in the background, re-arm fresh on return. iOS\n // suspends the streaming WebSocket and throttles JS timers while\n // backgrounded, so a running wall-clock timer either fires against a\n // connection that cannot deliver or fires the instant the app resumes -\n // both read to the user as a spurious timeout. On resume the subscription\n // reconnects and redelivers the final message if the turn finished while\n // away; if it is still pending, the fresh window starts counting from the\n // foreground.\n useEffect(() => {\n const sub = AppState.addEventListener('change', (next) => {\n if (next === 'active') {\n if (pendingRef.current && !streamCompletedRef.current) armTimeout();\n } else if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n });\n return () => sub.remove();\n }, [armTimeout]);\n\n useMessengerStreamDeltaSubscription({\n variables: { channelId: streamChannelId },\n skip: streamSkip,\n onData: ({ data }) => {\n const delta = data?.data?.messengerStreamDelta;\n if (!delta) return;\n\n if (delta.isFinal) return;\n\n if (streamCompletedRef.current) return;\n\n if (!pendingRef.current) {\n if (!reconnectedStreamRef.current) {\n reconnectedStreamRef.current = true;\n accumulatedLenRef.current = 0;\n }\n }\n\n if (quickReplyShownRef.current) {\n // The real answer has started: clear the brain's provisional reply\n // before the first chunk lands, so it is replaced, not appended to.\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n hasReceivedDeltasRef.current = true;\n\n // Heartbeat: a turn that is actively streaming is not stuck, so\n // push the idle deadline out on every delta. The timer now fires\n // only after real silence, never mid-stream on a long query.\n armTimeout();\n\n const fullText = stripModelCostHeader(delta.text ?? '');\n const newChunk = fullText.substring(accumulatedLenRef.current);\n accumulatedLenRef.current = fullText.length;\n\n if (newChunk) {\n cbRef.current.onChunk(newChunk);\n }\n },\n onError: (err) => {\n console.error('[useCdecliChannel] stream delta subscription error:', err);\n },\n });\n\n useGatewayInboundMessageByChannelSubscription({\n variables: { channelId: streamChannelId },\n skip: !isConnected || streamSkip,\n onData: ({ data }) => {\n const msg = data?.data?.gatewayInboundMessageByChannel;\n if (!msg?.text) return;\n const sanitizedText = stripModelCostHeader(msg.text);\n\n if (!hasReceivedDeltasRef.current) {\n if (quickReplyShownRef.current) {\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n cbRef.current.onChunk(sanitizedText);\n }\n\n cbRef.current.onComplete(sanitizedText);\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = true;\n accumulatedLenRef.current = 0;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n onError: (err) => {\n console.error('[useCdecliChannel] subscription error:', err);\n cbRef.current.onError(err.message || 'CDeCLI subscription error');\n },\n });\n\n const sendMessage = useCallback(\n async (\n text: string,\n chatId = 'messenger',\n media: Array<{ type: string; url: string; data?: string; mimeType?: string; filename?: string }> = [],\n ): Promise<boolean> => {\n if (!isConnected) return false;\n\n pendingRef.current = { text, sentAt: Date.now() };\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n quickReplyShownRef.current = false;\n\n // Ask the brain the same question in parallel with the send. On a cold\n // first turn the agent can take minutes before its first delta; the\n // brain answers in seconds. Its reply is painted only if nothing real\n // has arrived yet, and is replaced in place the moment it does. Fire\n // and forget: a brain failure changes nothing about the turn.\n const turn = ++turnSeqRef.current;\n void quickReplyViaBrain(text).then((answer) => {\n if (!answer) return;\n // Stale if the turn moved on, or the agent already spoke.\n if (turnSeqRef.current !== turn || !pendingRef.current) return;\n if (hasReceivedDeltasRef.current || streamCompletedRef.current) return;\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(answer);\n });\n\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n ...(media.length > 0 ? { media: media as never } : {}),\n ...(model || skill\n ? { metadata: { ...(model && { model }), ...(skill && { skill }) } }\n : {}),\n },\n },\n });\n\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI send failed';\n cbRef.current.onError(errMsg);\n pendingRef.current = null;\n return false;\n }\n\n armTimeout();\n\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n cbRef.current.onError(msg);\n pendingRef.current = null;\n return false;\n }\n },\n [isConnected, accountId, channelId, sendMutation, model, skill, armTimeout],\n );\n\n useEffect(\n () => () => {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n [],\n );\n\n /**\n * Inject a follow-up into the turn that is currently streaming.\n * Same `gatewaySendMessage` mutation with `metadata.steer = true` (browser\n * `useCdecliChannel.steer`). Do NOT reset streaming refs: the existing\n * delta subscription must keep running.\n *\n * \"Cannot steer: no active session\" is an expected race before cdecli has\n * started the turn. Callers should queue and retry; do not `console.error`\n * (that pops the RN LogBox and looks like a crash).\n */\n const steer = useCallback(\n async (text: string, chatId = 'messenger'): Promise<SteerResult> => {\n if (!isConnected) return { ok: false, error: 'CDeCLI is not connected.' };\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n metadata: { steer: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI steer failed';\n if (!isNoActiveSessionError(errMsg)) {\n console.warn('[useCdecliChannel] steer failed:', errMsg);\n }\n return { ok: false, error: errMsg };\n }\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn('[useCdecliChannel] steer mutation error:', msg);\n return { ok: false, error: msg };\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n /**\n * Abort the in-flight turn. `metadata.cancel = true` maps to\n * `POST /v1/chat/cancel` on cdecli-serve (browser parity).\n */\n const cancel = useCallback(\n async (chatId = 'messenger'): Promise<boolean> => {\n if (!isConnected) return false;\n pendingRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text: '',\n metadata: { cancel: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n console.error('[useCdecliChannel] cancel failed:', payload?.error || 'CDeCLI cancel failed');\n return false;\n }\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliChannel] cancel mutation error:', msg);\n return false;\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n return { sendMessage, steer, cancel };\n}\n"],"names":["_a"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCdecliChannel.js","sources":["../../src/hooks/useCdecliChannel.ts"],"sourcesContent":["/**\n * useCdecliChannel — wires the cdecli-serve messenger-gateway channel into the mobile chat UI.\n *\n * Kept in sync with `packages-modules/account/browser/src/hooks/useCdecliChannel.ts`.\n * When the CDeCLI channel is connected:\n * - `sendMessage(text, chatId, media)` calls `gatewaySendMessage`\n * - `MessengerStreamDelta` subscription delivers streaming chunks via `onChunk`\n * - `GatewayInboundMessageByChannel` delivers the final reply via `onComplete`\n */\n\nimport { useCallback, useEffect, useRef } from 'react';\nimport { AppState } from 'react-native';\nimport { gql, useApolloClient } from '@apollo/client';\nimport { quickReplyViaBrain } from '../services/brainQuickReply';\nimport {\n useGatewaySendMessageMutation,\n useGatewayInboundMessageByChannelSubscription,\n useMessengerStreamDeltaSubscription,\n} from 'common/graphql';\n\n/**\n * Resume snapshot for a channel's ACTIVE stream (server-owned).\n * Same document as browser `useCdecliChannel` — older backends error and we skip.\n */\nconst MESSENGER_ACTIVE_STREAM_QUERY = gql`\n query MessengerActiveStreamResume($channelId: String!) {\n messengerActiveStream(channelId: $channelId) {\n runId\n seq\n channelId\n text\n isFinal\n }\n }\n`;\n\nfunction stripModelCostHeader(content: string): string {\n const normalized = content.replace(/\\r\\n/g, '\\n');\n return normalized.replace(\n /^\\s*(?:[^\\w\\n]+\\s*)?[a-z0-9][a-z0-9._-]*\\s*\\(\\s*\\$[\\d.]+\\s*\\/\\s*MTok\\s+in\\s*\\)\\s*\\n+/i,\n '',\n );\n}\n\n// `error?: undefined` on the success arm keeps `result.error` reachable on the\n// union: with strictNullChecks off (repo-wide), TS will not narrow the\n// discriminated union after an `if (result.ok) continue`.\nexport type SteerResult = { ok: true; error?: undefined } | { ok: false; error: string };\n\nexport interface CdecliChannelCallbacks {\n onChunk: (text: string) => void;\n /**\n * A fast provisional answer from `yantra-brain`, painted while the real agent\n * is still bootstrapping. Distinct from onChunk on purpose: the UI shows it as\n * the reply-so-far and REPLACES it the moment real deltas arrive, so the brain\n * fills the silence without ever stacking on top of the agent's answer.\n */\n onQuickReply?: (text: string) => void;\n onComplete: (text: string) => void;\n onError: (error: string) => void;\n}\n\nexport function isNoActiveSessionError(message: string): boolean {\n return /no active session/i.test(message);\n}\n\n/**\n * Idle window, not a total budget. The timer is re-armed on every streamed\n * delta (a turn that is actively streaming is not stuck), and paused while the\n * app is backgrounded (iOS suspends the WebSocket + throttles JS timers, so a\n * wall-clock timer would otherwise fire a false \"did not respond\" the instant\n * the app returns to the foreground). It fires only after this much CONTINUOUS\n * silence with the app in the foreground.\n */\nconst CDECLI_RESPONSE_TIMEOUT_MS = 300_000;\n\nexport function useCdecliChannel(\n isConnected: boolean,\n accountId: string,\n channelId: string | undefined,\n callbacks: CdecliChannelCallbacks,\n model?: string,\n skill?: string,\n) {\n const [sendMutation] = useGatewaySendMessageMutation();\n const cbRef = useRef(callbacks);\n cbRef.current = callbacks;\n\n const pendingRef = useRef<{ text: string; sentAt: number } | null>(null);\n const hasReceivedDeltasRef = useRef(false);\n const reconnectedStreamRef = useRef(false);\n const streamCompletedRef = useRef(false);\n const accumulatedLenRef = useRef(0);\n const lastRunIdRef = useRef<string | null>(null);\n const apolloClient = useApolloClient();\n const seededChannelRef = useRef<string | null>(null);\n\n /**\n * Forward newly-accumulated stream text. Shared by live deltas and the\n * mount-time resume seed so neither gaps nor double-delivery happen when\n * the user leaves Chat for history and comes back mid-turn.\n */\n const ingestAccumulatedText = useCallback((accumulated: string) => {\n const fullText = stripModelCostHeader(accumulated);\n const newChunk = fullText.substring(accumulatedLenRef.current);\n accumulatedLenRef.current = fullText.length;\n if (newChunk) cbRef.current.onChunk(newChunk);\n }, []);\n // True while a brain quick-reply is what the user is looking at. The first\n // real delta clears the provisional text before painting, so the agent's\n // answer replaces the brain's rather than appending to it.\n const quickReplyShownRef = useRef(false);\n // Identity of the current turn for the brain race. A counter, not a\n // timestamp: two sends can share a millisecond, and a timestamp would then\n // let a brain answer for a superseded turn paint over the live one.\n const turnSeqRef = useRef(0);\n const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const streamSkip = !channelId;\n const streamChannelId = channelId || accountId;\n\n /** (Re)start the idle timer for the in-flight turn. No-op if none pending. */\n const armTimeout = useCallback(() => {\n if (timeoutRef.current) clearTimeout(timeoutRef.current);\n if (!pendingRef.current) {\n timeoutRef.current = null;\n return;\n }\n timeoutRef.current = setTimeout(() => {\n if (pendingRef.current) {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n timeoutRef.current = null;\n cbRef.current.onError(\n 'CDeCLI agent did not respond within 300 seconds. The query may still be processing.',\n );\n }\n }, CDECLI_RESPONSE_TIMEOUT_MS);\n }, []);\n\n // Pause the idle timer in the background, re-arm fresh on return. iOS\n // suspends the streaming WebSocket and throttles JS timers while\n // backgrounded, so a running wall-clock timer either fires against a\n // connection that cannot deliver or fires the instant the app resumes -\n // both read to the user as a spurious timeout. On resume the subscription\n // reconnects and redelivers the final message if the turn finished while\n // away; if it is still pending, the fresh window starts counting from the\n // foreground.\n useEffect(() => {\n const sub = AppState.addEventListener('change', (next) => {\n if (next === 'active') {\n if (pendingRef.current && !streamCompletedRef.current) armTimeout();\n } else if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n });\n return () => sub.remove();\n }, [armTimeout]);\n\n useMessengerStreamDeltaSubscription({\n variables: { channelId: streamChannelId },\n skip: streamSkip,\n onData: ({ data }) => {\n const delta = data?.data?.messengerStreamDelta;\n if (!delta) return;\n\n if (delta.isFinal) return;\n\n if (delta.runId && delta.runId !== lastRunIdRef.current) {\n accumulatedLenRef.current = 0;\n }\n if (delta.runId) lastRunIdRef.current = delta.runId;\n\n if (streamCompletedRef.current) return;\n\n // Pick up a turn that kept running after the user left Chat for history.\n if (!pendingRef.current) {\n if (!reconnectedStreamRef.current) {\n reconnectedStreamRef.current = true;\n accumulatedLenRef.current = 0;\n }\n }\n\n if (quickReplyShownRef.current) {\n // The real answer has started: clear the brain's provisional reply\n // before the first chunk lands, so it is replaced, not appended to.\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n hasReceivedDeltasRef.current = true;\n\n // Heartbeat: a turn that is actively streaming is not stuck, so\n // push the idle deadline out on every delta. The timer now fires\n // only after real silence, never mid-stream on a long query.\n armTimeout();\n\n ingestAccumulatedText(delta.text ?? '');\n },\n onError: (err) => {\n console.error('[useCdecliChannel] stream delta subscription error:', err);\n },\n });\n\n // Seed the in-flight reply when Chat remounts (history → chat) mid-stream.\n useEffect(() => {\n if (streamSkip || !streamChannelId) return undefined;\n if (seededChannelRef.current === streamChannelId) return undefined;\n seededChannelRef.current = streamChannelId;\n accumulatedLenRef.current = 0;\n streamCompletedRef.current = false;\n reconnectedStreamRef.current = false;\n lastRunIdRef.current = null;\n\n let cancelled = false;\n apolloClient\n .query({\n query: MESSENGER_ACTIVE_STREAM_QUERY,\n variables: { channelId: streamChannelId },\n fetchPolicy: 'network-only',\n errorPolicy: 'all',\n })\n .then(({ data }) => {\n if (cancelled) return;\n const snapshot = data?.messengerActiveStream as\n | { runId?: string; text?: string; isFinal?: boolean }\n | null\n | undefined;\n if (!snapshot?.text || snapshot.isFinal) return;\n if (streamCompletedRef.current) return;\n if (accumulatedLenRef.current > 0) return;\n reconnectedStreamRef.current = true;\n hasReceivedDeltasRef.current = true;\n if (snapshot.runId) lastRunIdRef.current = snapshot.runId;\n ingestAccumulatedText(snapshot.text);\n })\n .catch(() => {\n /* pre-#701 backend or transient failure — live deltas self-heal */\n });\n return () => {\n cancelled = true;\n };\n }, [streamChannelId, streamSkip, apolloClient, ingestAccumulatedText]);\n\n useGatewayInboundMessageByChannelSubscription({\n variables: { channelId: streamChannelId },\n skip: !isConnected || streamSkip,\n onData: ({ data }) => {\n const msg = data?.data?.gatewayInboundMessageByChannel;\n if (!msg?.text) return;\n const sanitizedText = stripModelCostHeader(msg.text);\n\n if (!hasReceivedDeltasRef.current) {\n if (quickReplyShownRef.current) {\n quickReplyShownRef.current = false;\n cbRef.current.onQuickReply?.('');\n }\n cbRef.current.onChunk(sanitizedText);\n }\n\n cbRef.current.onComplete(sanitizedText);\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = true;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n onError: (err) => {\n console.error('[useCdecliChannel] subscription error:', err);\n cbRef.current.onError(err.message || 'CDeCLI subscription error');\n },\n });\n\n const sendMessage = useCallback(\n async (\n text: string,\n chatId = 'messenger',\n media: Array<{ type: string; url: string; data?: string; mimeType?: string; filename?: string }> = [],\n ): Promise<boolean> => {\n if (!isConnected) return false;\n\n pendingRef.current = { text, sentAt: Date.now() };\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n quickReplyShownRef.current = false;\n\n // Ask the brain the same question in parallel with the send. On a cold\n // first turn the agent can take minutes before its first delta; the\n // brain answers in seconds. Its reply is painted only if nothing real\n // has arrived yet, and is replaced in place the moment it does. Fire\n // and forget: a brain failure changes nothing about the turn.\n const turn = ++turnSeqRef.current;\n void quickReplyViaBrain(text).then((answer) => {\n if (!answer) return;\n // Stale if the turn moved on, or the agent already spoke.\n if (turnSeqRef.current !== turn || !pendingRef.current) return;\n if (hasReceivedDeltasRef.current || streamCompletedRef.current) return;\n quickReplyShownRef.current = true;\n cbRef.current.onQuickReply?.(answer);\n });\n\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n ...(media.length > 0 ? { media: media as never } : {}),\n ...(model || skill\n ? { metadata: { ...(model && { model }), ...(skill && { skill }) } }\n : {}),\n },\n },\n });\n\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI send failed';\n cbRef.current.onError(errMsg);\n pendingRef.current = null;\n return false;\n }\n\n armTimeout();\n\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n cbRef.current.onError(msg);\n pendingRef.current = null;\n return false;\n }\n },\n [isConnected, accountId, channelId, sendMutation, model, skill, armTimeout],\n );\n\n useEffect(\n () => () => {\n pendingRef.current = null;\n hasReceivedDeltasRef.current = false;\n reconnectedStreamRef.current = false;\n streamCompletedRef.current = false;\n accumulatedLenRef.current = 0;\n lastRunIdRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n },\n [],\n );\n\n /**\n * Inject a follow-up into the turn that is currently streaming.\n * Same `gatewaySendMessage` mutation with `metadata.steer = true` (browser\n * `useCdecliChannel.steer`). Do NOT reset streaming refs: the existing\n * delta subscription must keep running.\n *\n * \"Cannot steer: no active session\" is an expected race before cdecli has\n * started the turn. Callers should queue and retry; do not `console.error`\n * (that pops the RN LogBox and looks like a crash).\n */\n const steer = useCallback(\n async (text: string, chatId = 'messenger'): Promise<SteerResult> => {\n if (!isConnected) return { ok: false, error: 'CDeCLI is not connected.' };\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text,\n metadata: { steer: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n const errMsg = payload?.error || 'CDeCLI steer failed';\n if (!isNoActiveSessionError(errMsg)) {\n console.warn('[useCdecliChannel] steer failed:', errMsg);\n }\n return { ok: false, error: errMsg };\n }\n return { ok: true };\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.warn('[useCdecliChannel] steer mutation error:', msg);\n return { ok: false, error: msg };\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n /**\n * Abort the in-flight turn. `metadata.cancel = true` maps to\n * `POST /v1/chat/cancel` on cdecli-serve (browser parity).\n */\n const cancel = useCallback(\n async (chatId = 'messenger'): Promise<boolean> => {\n if (!isConnected) return false;\n pendingRef.current = null;\n if (timeoutRef.current) {\n clearTimeout(timeoutRef.current);\n timeoutRef.current = null;\n }\n try {\n const result = await sendMutation({\n variables: {\n input: {\n channelType: 'cdecli-serve',\n accountId,\n chatId,\n text: '',\n metadata: { cancel: true },\n },\n },\n });\n const payload = result.data?.gatewaySendMessage;\n if (!payload?.success) {\n console.error('[useCdecliChannel] cancel failed:', payload?.error || 'CDeCLI cancel failed');\n return false;\n }\n return true;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n console.error('[useCdecliChannel] cancel mutation error:', msg);\n return false;\n }\n },\n [isConnected, accountId, sendMutation],\n );\n\n return { sendMessage, steer, cancel };\n}\n"],"names":["_a"],"mappings":"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"}
|