@adminide-stack/yantra-mobile 12.0.69-alpha.21 → 12.0.69-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import {isConnectorReply,stripConnectorConnect,parseConnectorConnect,isSiteLoginRequest,pickConnectorCredential,CONNECTOR_CONNECT_WEB_RETURN_PATH,isSiteLoginCredential,credentialSavedAt}from'common';var __defProp = Object.defineProperty;
1
+ import {parseConnectorConnect,isSiteLoginRequest,pickConnectorCredential,CONNECTOR_CONNECT_WEB_RETURN_PATH,isSiteLoginCredential,credentialSavedAt}from'common';export{displayConnectorConnectMessages,displayConnectorConnectText}from'common';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
@@ -30,21 +30,6 @@ function pendingConnectorConnect(messages) {
30
30
  }
31
31
  return null;
32
32
  }
33
- function displayConnectorConnectText(text, streaming) {
34
- const stripped = stripConnectorConnect(text);
35
- if (!streaming) return stripped;
36
- const open = stripped.indexOf("[CONNECTOR_CONNECT");
37
- return open >= 0 ? stripped.slice(0, open).trim() : stripped;
38
- }
39
- function displayConnectorConnectMessages(messages) {
40
- return messages.filter((msg) => !(msg.role === "user" && isConnectorReply(msg.content))).map((msg) => {
41
- if (msg.role !== "assistant") return msg;
42
- const content = displayConnectorConnectText(msg.content, false);
43
- return content === msg.content ? msg : __spreadProps(__spreadValues({}, msg), {
44
- content
45
- });
46
- });
47
- }
48
33
  function connectorConnectKey(pending) {
49
34
  return `${pending.messageIndex}:${pending.payload.requestId}`;
50
35
  }
@@ -191,4 +176,4 @@ function parkedHandbackCredential(rows, payload, credentialId, nowMs) {
191
176
  if (!fits) return null;
192
177
  const savedAt = credentialSavedAt(row);
193
178
  return savedAt > 0 && nowMs - savedAt <= PARKED_HANDBACK_MAX_AGE_MS ? row : null;
194
- }export{CONNECTORS_PATH,PARKED_HANDBACK_MAX_AGE_MS,VAULT_NEW_PATH,buildConnectorConnectUrl,connectorConnectActionLabel,connectorConnectKey,connectorConnectName,connectorConnectReason,connectorConnectTarget,credentialFingerprint,decideConnectorConnectOutcome,displayConnectorConnectMessages,displayConnectorConnectText,parkedHandbackCredential,pendingConnectorConnect,pickConnectorConnectCredential,snapshotConnectorCredentials};//# sourceMappingURL=connectorConnect.js.map
179
+ }export{CONNECTORS_PATH,PARKED_HANDBACK_MAX_AGE_MS,VAULT_NEW_PATH,buildConnectorConnectUrl,connectorConnectActionLabel,connectorConnectKey,connectorConnectName,connectorConnectReason,connectorConnectTarget,credentialFingerprint,decideConnectorConnectOutcome,parkedHandbackCredential,pendingConnectorConnect,pickConnectorConnectCredential,snapshotConnectorCredentials};//# sourceMappingURL=connectorConnect.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"connectorConnect.js","sources":["../../../src/features/chat/connectorConnect.ts"],"sourcesContent":["/**\n * The `[CONNECTOR_CONNECT]` card, mobile side.\n *\n * The marker and its replies are parsed by common's CONNECTOR_CONNECT_PROTOCOL,\n * the same module the web chat uses, so every marker shape the model drifts into\n * is read the same way on both. This file holds only what is specific to the\n * phone: which request is still waiting, the card's copy, the web page the card\n * opens (the phone cannot host the web's inline forms, so it opens them in an\n * in-app browser session), and how to tell, once that session ends, whether the\n * vault really changed.\n *\n * Host-only: this module imports `common`, which the federated surface remotes\n * must not bundle. A remote's chat gets its messages already cleaned\n * (displayConnectorConnectMessages) and its card as a host render prop.\n */\nimport {\n CONNECTOR_CONNECT_WEB_RETURN_PATH,\n credentialSavedAt,\n isConnectorReply,\n isSiteLoginCredential,\n isSiteLoginRequest,\n parseConnectorConnect,\n pickConnectorCredential,\n stripConnectorConnect,\n type ConnectorConnectHandback,\n type ConnectorConnectPayload,\n type ConnectorConnectionTarget,\n type ConnectorCredentialListRow,\n} from 'common';\n\n/** forms-stack's \"Save to vault\" page as the web app mounts it (AccountRoutes.AccountConnectorsVaultNew). */\nexport const VAULT_NEW_PATH = '/account/connectors/vault/new';\n\n/** A connect request still waiting on the user, and where it sits in the thread. */\nexport interface PendingConnectorConnect {\n payload: ConnectorConnectPayload;\n /** Index of the assistant message that carried the marker. */\n messageIndex: number;\n}\n\n/**\n * The connect request still awaiting an answer: the newest marker in the\n * assistant messages since the last user message. Any user message after it - the\n * `[CONNECTOR_CONNECTED:…]` / `[CONNECTOR_DECLINED:…]` reply the card sends, or\n * the user simply typing on - settles it, so a reloaded thread never brings back a\n * card that was already answered (the same rule as pendingAskUser).\n */\nexport function pendingConnectorConnect(\n messages: ReadonlyArray<{ role: string; content: string }>,\n): PendingConnectorConnect | null {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n const msg = messages[i];\n if (msg.role === 'user') return null;\n const payload = msg.role === 'assistant' ? parseConnectorConnect(msg.content) : null;\n if (payload) return { payload, messageIndex: i };\n }\n return null;\n}\n\n/**\n * Assistant prose with every connect marker taken out - the card stands in for\n * it. While the reply is still streaming, a marker that has started but not yet\n * closed is cut off at its start too, so its half-written JSON never flashes in\n * the bubble. A finished reply loses only complete marker shapes, so text that\n * merely starts like one is not swallowed for good.\n */\nexport function displayConnectorConnectText(text: string, streaming: boolean): string {\n const stripped = stripConnectorConnect(text);\n if (!streaming) return stripped;\n const open = stripped.indexOf('[CONNECTOR_CONNECT');\n return open >= 0 ? stripped.slice(0, open).trim() : stripped;\n}\n\n/**\n * A thread as the transcript shows it: the card's `[CONNECTOR_CONNECTED:…]` /\n * `[CONNECTOR_DECLINED:…]` replies are protocol, not something the user said, so\n * they go; assistant messages lose their connect markers and keep their prose (a\n * reply that was only a marker is left empty, which the transcript skips). Every\n * other field of a row is kept, ids included, so rows do not remount.\n *\n * The host runs this before handing a thread to ChatTranscript, so the transcript\n * (which the surface remotes bundle too) never needs the protocol parser.\n */\nexport function displayConnectorConnectMessages<T extends { role: string; content: string }>(\n messages: ReadonlyArray<T>,\n): T[] {\n return messages\n .filter((msg) => !(msg.role === 'user' && isConnectorReply(msg.content)))\n .map((msg) => {\n if (msg.role !== 'assistant') return msg;\n const content = displayConnectorConnectText(msg.content, false);\n return content === msg.content ? msg : { ...msg, content };\n });\n}\n\n/**\n * What a local \"answered\" mark is keyed on. The request id alone is not enough: a\n * marker that carries no id gets one derived from its text, so the same marker\n * repeated in a later turn has the same id, and keying on the id alone would hide\n * that new card too.\n */\nexport function connectorConnectKey(pending: PendingConnectorConnect): string {\n return `${pending.messageIndex}:${pending.payload.requestId}`;\n}\n\n/** A connector key as a readable name: `google-ads` -> `google ads` (the web's cleanup, minus the template lookup). */\nfunction readableConnector(connector: string): string {\n const cleaned = connector\n .replace(/-app-[a-z0-9]+$/i, '')\n .replace(/\\s*\\((?:app|api|oauth)\\)\\s*$/i, '')\n .replace(/[_-]+/g, ' ')\n .trim();\n return cleaned || connector;\n}\n\n/** Card title, and the name a new site login is saved under: \"Amazon login\", \"amazon.com login\", \"openai\". */\nexport function connectorConnectName(payload: ConnectorConnectPayload): string {\n if (payload.title) return payload.title;\n if (isSiteLoginRequest(payload)) return payload.form?.site ? `${payload.form.site} login` : 'Website login';\n return readableConnector(payload.connector);\n}\n\n/** The card's one line under the title; the agent's own reason wins. */\nexport function connectorConnectReason(payload: ConnectorConnectPayload, name: string): string {\n if (payload.reason) return payload.reason;\n if (isSiteLoginRequest(payload)) {\n return `Save your ${payload.form?.site ?? 'website'} sign-in to the vault so I can log in on your Computer.`;\n }\n return `Connect ${name} to use it from this chat.`;\n}\n\n/**\n * What the card looks for in the vault: the web card's target for the same request (common's\n * matchConnectorCredentials), minus the template's formId, which only the web can resolve. A site\n * login takes saved logins for its site only; the credential the agent named wins when it is one\n * of the org's and fits, else the most recently updated match - the row the agent signs in with.\n *\n * For a site login this is the same target the web card builds, so the pick is the web's. For a\n * catalog connector it is only the card's status (\"Connected\" / \"Update\"): without the formId a\n * name-only match can take another connector's credential (\"Azure OpenAI\" for OpenAI) or miss one\n * saved under another name, so the phone never acts on it - the web page it opens picks (see\n * buildConnectorConnectUrl, and pickConnectorConnectCredential for the status).\n */\nexport function connectorConnectTarget(payload: ConnectorConnectPayload): Omit<ConnectorConnectionTarget, 'id'> {\n const siteLogin = isSiteLoginRequest(payload);\n return {\n name: connectorConnectName(payload),\n siteLogin,\n site: siteLogin ? (payload.form?.site ?? payload.form?.loginUrl) : undefined,\n credentialId: payload.credentialId,\n };\n}\n\n/** A name as the matcher compares it (common's matchConnectorCredentials): lower case, letters and digits only. */\nconst matchKey = (name: string | undefined) => (name ?? '').toLowerCase().replace(/[^a-z0-9]/g, '');\n\n/**\n * The saved credential the phone's card stands on for a request - \"Update\" when there is one, and the one the card\n * measures the browser session against - or undefined.\n *\n * A site login: common's pick on connectorConnectTarget, the web card's own target, so the very row the web card\n * and the agent use. A catalog connector: the web card also matches the template's formId, which the phone cannot\n * resolve, so a card titled \"OpenAI API key\" missed the \"OpenAI\" credential the web card finds by formId and showed\n * \"Connect\" where the web shows \"Update\". A credential saved from a template is named for it, so after the card\n * title the phone also looks under the connector the agent named (`openai`, `google-calendar` as \"google\n * calendar\"); the first name that matches wins. Only the phone's label and its after-the-session check rest on\n * this: the web page the card opens makes the pick it edits (buildConnectorConnectUrl).\n */\nexport function pickConnectorConnectCredential<T extends ConnectorCredentialListRow>(\n rows: ReadonlyArray<T | null | undefined>,\n payload: ConnectorConnectPayload,\n): T | undefined {\n const target = connectorConnectTarget(payload);\n if (target.siteLogin) return pickConnectorCredential(rows, target);\n const tried = new Set<string>();\n for (const name of [target.name, readableConnector(payload.connector)]) {\n const key = matchKey(name);\n if (key && !tried.has(key)) {\n tried.add(key);\n const hit = pickConnectorCredential(rows, { ...target, name });\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n/** The primary action: a saved connection is updated in place, a new one is saved or connected. */\nexport function connectorConnectActionLabel(payload: ConnectorConnectPayload, connected: boolean): string {\n if (connected) return 'Update';\n return isSiteLoginRequest(payload) ? 'Save login' : 'Connect';\n}\n\n/** `a=1&b=2`, skipping empty values. Hand-rolled: React Native's URLSearchParams cannot be relied on. */\nfunction toQuery(params: ReadonlyArray<readonly [string, string | null | undefined]>): string {\n return params\n .filter((entry): entry is readonly [string, string] => Boolean(entry[1]?.trim()))\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value.trim())}`)\n .join('&');\n}\n\nexport interface ConnectorConnectUrlInput {\n /** The web app's origin (CLIENT_URL). */\n origin: string;\n payload: ConnectorConnectPayload;\n /** Card title; a site login is saved under it. */\n name: string;\n /**\n * A site login only: update THIS saved login in place (the card's \"Update\"); absent saves a new one. Never sent\n * for a catalog connector, whose page makes the pick itself (see buildConnectorConnectUrl).\n */\n credentialId?: string | null;\n /**\n * A site login only: the project the vault form works in - where a new login is saved, and for an update the\n * project the login LIVES in (the form refuses to change a login from any other project, and the card matches\n * the whole org's).\n */\n projectId?: string | null;\n}\n\n/** The web Connectors entry that resolves a connector to its template first (ConnectorsRedirect). */\nexport const CONNECTORS_PATH = '/account/connectors';\n\n/**\n * The web page the card opens.\n *\n * A site login opens forms-stack's \"Save to vault\" page. Given `credentialId` that\n * page UPDATES that credential (username / email and password alike) instead of\n * saving a duplicate next to it. `returnTo` sends it, once saved, to the web\n * return page that hands the credential id back to the app, and `requestId`\n * rides along so the hand-back names the request it answers.\n *\n * Any other connector opens the Connectors entry with the agent's `connector` and\n * `aliases` as they came, and the page there is the web card itself\n * (ConnectorCredentialEdit): it resolves them against the org's template pool with\n * the web card's own resolver (useCredentialTemplateByKey), so a display name like\n * \"Google Calendar\" or a drifted key reaches the same template on both, and then\n * makes the web card's pick with the template's formId - which saved credential is\n * this connector's, to update in place, or none, to connect a new one. The phone\n * cannot resolve that formId, so it sends no pick of its own: a name-only pick can be\n * another connector's credential (\"Azure OpenAI\" for OpenAI), which the form would\n * then overwrite, or miss one saved under another name and start a duplicate. Only\n * the agent's own `credentialId` rides along, as the hint the web card honours when\n * it fits. Once saved the page returns through the same return page as the vault\n * form, so the phone learns which credential it was.\n */\nexport function buildConnectorConnectUrl({\n origin,\n payload,\n name,\n credentialId,\n projectId,\n}: ConnectorConnectUrlInput): string {\n const base = origin.trim().replace(/\\/+$/, '');\n if (isSiteLoginRequest(payload)) {\n const query = toQuery([\n ['name', name],\n ['site', payload.form?.site],\n ['loginUrl', payload.form?.loginUrl],\n ['agent', payload.form?.agentName],\n ['credentialId', credentialId],\n ['projectId', projectId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ]);\n return `${base}${VAULT_NEW_PATH}?${query}`;\n }\n const aliases = (payload.aliases ?? []).map((alias) => ['aliases', alias] as const);\n return `${base}${CONNECTORS_PATH}?${toQuery([\n ['connector', payload.connector],\n ...aliases,\n ['title', payload.title],\n ['credentialId', payload.credentialId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ])}`;\n}\n\n/** A saved credential as the card reads it: the matched fields, its version (`updatedAt`) and where it lives. */\nexport type CredentialVersionRow = ConnectorCredentialListRow;\n\n/**\n * A saved credential's identity AND version. An update keeps the id but moves\n * `updatedAt`, so comparing these before and after the browser tells a real save\n * apart from a closed browser.\n */\nexport function credentialFingerprint(row: CredentialVersionRow | null | undefined): string | null {\n if (!row?.id) return null;\n return `${row.id}@${row.updatedAt == null ? '' : String(row.updatedAt)}`;\n}\n\n/** The vault as it stood when the browser opened: every credential's version, and the one being updated. */\nexport interface ConnectorConnectBefore {\n /** Credential id -> fingerprint, for every row the read returned. */\n versions: Record<string, string>;\n /** The credential the card opened the form ON (\"Update\"), if any. */\n targetId?: string | null;\n}\n\n/** Record the vault before the browser opens, so the check afterwards can tell what the session changed. */\nexport function snapshotConnectorCredentials(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n targetId?: string | null,\n): ConnectorConnectBefore {\n const versions: Record<string, string> = {};\n for (const row of rows) {\n const fingerprint = credentialFingerprint(row);\n if (row?.id && fingerprint) versions[row.id] = fingerprint;\n }\n return { versions, targetId: targetId ?? null };\n}\n\nexport type ConnectorConnectOutcomeDecision =\n | { kind: 'connected'; credentialId?: string }\n /** Nothing changed in the vault: the card stays up, the agent is told nothing. */\n | { kind: 'unchanged' };\n\n/**\n * Whether the browser session produced a connection, and with which credential.\n *\n * 1. The session ended on the app's return URL: the vault page saved, and says\n * which credential. That is the answer, unless it names a different request.\n * 2. Otherwise (the person closed the browser, or the connector's page has no way\n * back to the app) the vault is read again and compared with `before`:\n * - the credential the form was opened ON has a newer version: updated in place\n * (found by id, so an edit that also renamed it still counts);\n * - the credential matching this request (formId / name) is new or newer;\n * - exactly one active credential appeared that was not there before: the\n * session created it, under whatever name the person gave it.\n * A credential some other flow merely touched is never taken, and a card never\n * claims a connection it cannot see.\n */\nexport function decideConnectorConnectOutcome({\n requestId,\n handback,\n before,\n after,\n}: {\n requestId: string;\n handback: ConnectorConnectHandback | null;\n /** The vault before the browser opened; without it nothing but a hand-back counts. */\n before: ConnectorConnectBefore | null;\n /** The vault after the browser closed, re-read from the server; null when that read failed. */\n after: { rows: ReadonlyArray<CredentialVersionRow | null | undefined>; match: CredentialVersionRow | null } | null;\n}): ConnectorConnectOutcomeDecision {\n if (handback && (!handback.requestId || handback.requestId === requestId)) {\n return { kind: 'connected', credentialId: handback.credentialId ?? after?.match?.id ?? undefined };\n }\n if (!before || !after) return { kind: 'unchanged' };\n const { versions, targetId } = before;\n const changed = (row: CredentialVersionRow | null | undefined): row is CredentialVersionRow => {\n const now = credentialFingerprint(row);\n return Boolean(now && row?.id && versions[row.id] !== now);\n };\n if (targetId) {\n const target = after.rows.find((row) => row?.id === targetId);\n if (target?.status === 'active' && changed(target)) return { kind: 'connected', credentialId: targetId };\n }\n if (changed(after.match)) return { kind: 'connected', credentialId: after.match.id ?? undefined };\n const created = after.rows.filter((row): row is CredentialVersionRow =>\n Boolean(row?.id && row.status === 'active' && !versions[row.id]),\n );\n if (created.length === 1) return { kind: 'connected', credentialId: created[0].id ?? undefined };\n return { kind: 'unchanged' };\n}\n\n/**\n * How long after its save a hand-back that reached the app with no browser session waiting is still taken: the\n * session the app was killed during (Android kills it behind Custom Tabs, and the return page cold-starts it).\n */\nexport const PARKED_HANDBACK_MAX_AGE_MS = 30 * 60 * 1000;\n\n/**\n * The credential a PARKED hand-back names, when it may be taken as this request's answer; null otherwise.\n *\n * Anyone can open the app's scheme URL, and the vault snapshot the card took before the browser opened died with\n * the app, so \"the vault has it\" is not enough. The credential must be:\n * - an ACTIVE credential of the org (`rows` is a fresh read);\n * - one this request could have saved: for a site login, the login the card itself picks for the request with\n * that id named (its site or name; never another site's login or a catalog credential); for a catalog\n * connector, a connector credential, never a website login - the phone cannot resolve the template, so a name\n * alone cannot rule a connector credential in or out (see connectorConnectTarget);\n * - saved in the last PARKED_HANDBACK_MAX_AGE_MS, the stand-in for \"changed during the session\": every save on\n * the web page moves `updatedAt`, while an id lifted from anywhere else names a credential saved long ago.\n */\nexport function parkedHandbackCredential(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n payload: ConnectorConnectPayload,\n credentialId: string | null | undefined,\n nowMs: number,\n): CredentialVersionRow | null {\n if (!credentialId) return null;\n const row = rows.find((r) => r?.id === credentialId && r.status === 'active');\n if (!row) return null;\n const fits = isSiteLoginRequest(payload)\n ? pickConnectorCredential(rows, { ...connectorConnectTarget(payload), credentialId })?.id === credentialId\n : !isSiteLoginCredential(row);\n if (!fits) return null;\n const savedAt = credentialSavedAt(row);\n return savedAt > 0 && nowMs - savedAt <= PARKED_HANDBACK_MAX_AGE_MS ? row : null;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"connectorConnect.js","sources":["../../../src/features/chat/connectorConnect.ts"],"sourcesContent":["/**\n * The `[CONNECTOR_CONNECT]` card, mobile side.\n *\n * The marker and its replies are parsed by common's CONNECTOR_CONNECT_PROTOCOL,\n * the same module the web chat uses, so every marker shape the model drifts into\n * is read the same way on both. This file holds only what is specific to the\n * phone: which request is still waiting, the card's copy, the web page the card\n * opens (the phone cannot host the web's inline forms, so it opens them in an\n * in-app browser session), and how to tell, once that session ends, whether the\n * vault really changed.\n *\n * Host-only: this module imports `common`, which the federated surface remotes\n * must not bundle. A remote's chat gets its messages already cleaned\n * (displayConnectorConnectMessages) and its card as a host render prop.\n */\nimport {\n CONNECTOR_CONNECT_WEB_RETURN_PATH,\n credentialSavedAt,\n isSiteLoginCredential,\n isSiteLoginRequest,\n parseConnectorConnect,\n pickConnectorCredential,\n type ConnectorConnectHandback,\n type ConnectorConnectPayload,\n type ConnectorConnectionTarget,\n type ConnectorCredentialListRow,\n} from 'common';\n\n/** How a transcript shows a thread with connect markers in it - shared with the web host's surface chat. */\nexport { displayConnectorConnectMessages, displayConnectorConnectText } from 'common';\n\n/** forms-stack's \"Save to vault\" page as the web app mounts it (AccountRoutes.AccountConnectorsVaultNew). */\nexport const VAULT_NEW_PATH = '/account/connectors/vault/new';\n\n/** A connect request still waiting on the user, and where it sits in the thread. */\nexport interface PendingConnectorConnect {\n payload: ConnectorConnectPayload;\n /** Index of the assistant message that carried the marker. */\n messageIndex: number;\n}\n\n/**\n * The connect request still awaiting an answer: the newest marker in the\n * assistant messages since the last user message. Any user message after it - the\n * `[CONNECTOR_CONNECTED:…]` / `[CONNECTOR_DECLINED:…]` reply the card sends, or\n * the user simply typing on - settles it, so a reloaded thread never brings back a\n * card that was already answered (the same rule as pendingAskUser).\n */\nexport function pendingConnectorConnect(\n messages: ReadonlyArray<{ role: string; content: string }>,\n): PendingConnectorConnect | null {\n for (let i = messages.length - 1; i >= 0; i -= 1) {\n const msg = messages[i];\n if (msg.role === 'user') return null;\n const payload = msg.role === 'assistant' ? parseConnectorConnect(msg.content) : null;\n if (payload) return { payload, messageIndex: i };\n }\n return null;\n}\n\n/**\n * What a local \"answered\" mark is keyed on. The request id alone is not enough: a\n * marker that carries no id gets one derived from its text, so the same marker\n * repeated in a later turn has the same id, and keying on the id alone would hide\n * that new card too.\n */\nexport function connectorConnectKey(pending: PendingConnectorConnect): string {\n return `${pending.messageIndex}:${pending.payload.requestId}`;\n}\n\n/** A connector key as a readable name: `google-ads` -> `google ads` (the web's cleanup, minus the template lookup). */\nfunction readableConnector(connector: string): string {\n const cleaned = connector\n .replace(/-app-[a-z0-9]+$/i, '')\n .replace(/\\s*\\((?:app|api|oauth)\\)\\s*$/i, '')\n .replace(/[_-]+/g, ' ')\n .trim();\n return cleaned || connector;\n}\n\n/** Card title, and the name a new site login is saved under: \"Amazon login\", \"amazon.com login\", \"openai\". */\nexport function connectorConnectName(payload: ConnectorConnectPayload): string {\n if (payload.title) return payload.title;\n if (isSiteLoginRequest(payload)) return payload.form?.site ? `${payload.form.site} login` : 'Website login';\n return readableConnector(payload.connector);\n}\n\n/** The card's one line under the title; the agent's own reason wins. */\nexport function connectorConnectReason(payload: ConnectorConnectPayload, name: string): string {\n if (payload.reason) return payload.reason;\n if (isSiteLoginRequest(payload)) {\n return `Save your ${payload.form?.site ?? 'website'} sign-in to the vault so I can log in on your Computer.`;\n }\n return `Connect ${name} to use it from this chat.`;\n}\n\n/**\n * What the card looks for in the vault: the web card's target for the same request (common's\n * matchConnectorCredentials), minus the template's formId, which only the web can resolve. A site\n * login takes saved logins for its site only; the credential the agent named wins when it is one\n * of the org's and fits, else the most recently updated match - the row the agent signs in with.\n *\n * For a site login this is the same target the web card builds, so the pick is the web's. For a\n * catalog connector it is only the card's status (\"Connected\" / \"Update\"): without the formId a\n * name-only match can take another connector's credential (\"Azure OpenAI\" for OpenAI) or miss one\n * saved under another name, so the phone never acts on it - the web page it opens picks (see\n * buildConnectorConnectUrl, and pickConnectorConnectCredential for the status).\n */\nexport function connectorConnectTarget(payload: ConnectorConnectPayload): Omit<ConnectorConnectionTarget, 'id'> {\n const siteLogin = isSiteLoginRequest(payload);\n return {\n name: connectorConnectName(payload),\n siteLogin,\n site: siteLogin ? (payload.form?.site ?? payload.form?.loginUrl) : undefined,\n credentialId: payload.credentialId,\n };\n}\n\n/** A name as the matcher compares it (common's matchConnectorCredentials): lower case, letters and digits only. */\nconst matchKey = (name: string | undefined) => (name ?? '').toLowerCase().replace(/[^a-z0-9]/g, '');\n\n/**\n * The saved credential the phone's card stands on for a request - \"Update\" when there is one, and the one the card\n * measures the browser session against - or undefined.\n *\n * A site login: common's pick on connectorConnectTarget, the web card's own target, so the very row the web card\n * and the agent use. A catalog connector: the web card also matches the template's formId, which the phone cannot\n * resolve, so a card titled \"OpenAI API key\" missed the \"OpenAI\" credential the web card finds by formId and showed\n * \"Connect\" where the web shows \"Update\". A credential saved from a template is named for it, so after the card\n * title the phone also looks under the connector the agent named (`openai`, `google-calendar` as \"google\n * calendar\"); the first name that matches wins. Only the phone's label and its after-the-session check rest on\n * this: the web page the card opens makes the pick it edits (buildConnectorConnectUrl).\n */\nexport function pickConnectorConnectCredential<T extends ConnectorCredentialListRow>(\n rows: ReadonlyArray<T | null | undefined>,\n payload: ConnectorConnectPayload,\n): T | undefined {\n const target = connectorConnectTarget(payload);\n if (target.siteLogin) return pickConnectorCredential(rows, target);\n const tried = new Set<string>();\n for (const name of [target.name, readableConnector(payload.connector)]) {\n const key = matchKey(name);\n if (key && !tried.has(key)) {\n tried.add(key);\n const hit = pickConnectorCredential(rows, { ...target, name });\n if (hit) return hit;\n }\n }\n return undefined;\n}\n\n/** The primary action: a saved connection is updated in place, a new one is saved or connected. */\nexport function connectorConnectActionLabel(payload: ConnectorConnectPayload, connected: boolean): string {\n if (connected) return 'Update';\n return isSiteLoginRequest(payload) ? 'Save login' : 'Connect';\n}\n\n/** `a=1&b=2`, skipping empty values. Hand-rolled: React Native's URLSearchParams cannot be relied on. */\nfunction toQuery(params: ReadonlyArray<readonly [string, string | null | undefined]>): string {\n return params\n .filter((entry): entry is readonly [string, string] => Boolean(entry[1]?.trim()))\n .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value.trim())}`)\n .join('&');\n}\n\nexport interface ConnectorConnectUrlInput {\n /** The web app's origin (CLIENT_URL). */\n origin: string;\n payload: ConnectorConnectPayload;\n /** Card title; a site login is saved under it. */\n name: string;\n /**\n * A site login only: update THIS saved login in place (the card's \"Update\"); absent saves a new one. Never sent\n * for a catalog connector, whose page makes the pick itself (see buildConnectorConnectUrl).\n */\n credentialId?: string | null;\n /**\n * A site login only: the project the vault form works in - where a new login is saved, and for an update the\n * project the login LIVES in (the form refuses to change a login from any other project, and the card matches\n * the whole org's).\n */\n projectId?: string | null;\n}\n\n/** The web Connectors entry that resolves a connector to its template first (ConnectorsRedirect). */\nexport const CONNECTORS_PATH = '/account/connectors';\n\n/**\n * The web page the card opens.\n *\n * A site login opens forms-stack's \"Save to vault\" page. Given `credentialId` that\n * page UPDATES that credential (username / email and password alike) instead of\n * saving a duplicate next to it. `returnTo` sends it, once saved, to the web\n * return page that hands the credential id back to the app, and `requestId`\n * rides along so the hand-back names the request it answers.\n *\n * Any other connector opens the Connectors entry with the agent's `connector` and\n * `aliases` as they came, and the page there is the web card itself\n * (ConnectorCredentialEdit): it resolves them against the org's template pool with\n * the web card's own resolver (useCredentialTemplateByKey), so a display name like\n * \"Google Calendar\" or a drifted key reaches the same template on both, and then\n * makes the web card's pick with the template's formId - which saved credential is\n * this connector's, to update in place, or none, to connect a new one. The phone\n * cannot resolve that formId, so it sends no pick of its own: a name-only pick can be\n * another connector's credential (\"Azure OpenAI\" for OpenAI), which the form would\n * then overwrite, or miss one saved under another name and start a duplicate. Only\n * the agent's own `credentialId` rides along, as the hint the web card honours when\n * it fits. Once saved the page returns through the same return page as the vault\n * form, so the phone learns which credential it was.\n */\nexport function buildConnectorConnectUrl({\n origin,\n payload,\n name,\n credentialId,\n projectId,\n}: ConnectorConnectUrlInput): string {\n const base = origin.trim().replace(/\\/+$/, '');\n if (isSiteLoginRequest(payload)) {\n const query = toQuery([\n ['name', name],\n ['site', payload.form?.site],\n ['loginUrl', payload.form?.loginUrl],\n ['agent', payload.form?.agentName],\n ['credentialId', credentialId],\n ['projectId', projectId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ]);\n return `${base}${VAULT_NEW_PATH}?${query}`;\n }\n const aliases = (payload.aliases ?? []).map((alias) => ['aliases', alias] as const);\n return `${base}${CONNECTORS_PATH}?${toQuery([\n ['connector', payload.connector],\n ...aliases,\n ['title', payload.title],\n ['credentialId', payload.credentialId],\n ['requestId', payload.requestId],\n ['returnTo', CONNECTOR_CONNECT_WEB_RETURN_PATH],\n ])}`;\n}\n\n/** A saved credential as the card reads it: the matched fields, its version (`updatedAt`) and where it lives. */\nexport type CredentialVersionRow = ConnectorCredentialListRow;\n\n/**\n * A saved credential's identity AND version. An update keeps the id but moves\n * `updatedAt`, so comparing these before and after the browser tells a real save\n * apart from a closed browser.\n */\nexport function credentialFingerprint(row: CredentialVersionRow | null | undefined): string | null {\n if (!row?.id) return null;\n return `${row.id}@${row.updatedAt == null ? '' : String(row.updatedAt)}`;\n}\n\n/** The vault as it stood when the browser opened: every credential's version, and the one being updated. */\nexport interface ConnectorConnectBefore {\n /** Credential id -> fingerprint, for every row the read returned. */\n versions: Record<string, string>;\n /** The credential the card opened the form ON (\"Update\"), if any. */\n targetId?: string | null;\n}\n\n/** Record the vault before the browser opens, so the check afterwards can tell what the session changed. */\nexport function snapshotConnectorCredentials(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n targetId?: string | null,\n): ConnectorConnectBefore {\n const versions: Record<string, string> = {};\n for (const row of rows) {\n const fingerprint = credentialFingerprint(row);\n if (row?.id && fingerprint) versions[row.id] = fingerprint;\n }\n return { versions, targetId: targetId ?? null };\n}\n\nexport type ConnectorConnectOutcomeDecision =\n | { kind: 'connected'; credentialId?: string }\n /** Nothing changed in the vault: the card stays up, the agent is told nothing. */\n | { kind: 'unchanged' };\n\n/**\n * Whether the browser session produced a connection, and with which credential.\n *\n * 1. The session ended on the app's return URL: the vault page saved, and says\n * which credential. That is the answer, unless it names a different request.\n * 2. Otherwise (the person closed the browser, or the connector's page has no way\n * back to the app) the vault is read again and compared with `before`:\n * - the credential the form was opened ON has a newer version: updated in place\n * (found by id, so an edit that also renamed it still counts);\n * - the credential matching this request (formId / name) is new or newer;\n * - exactly one active credential appeared that was not there before: the\n * session created it, under whatever name the person gave it.\n * A credential some other flow merely touched is never taken, and a card never\n * claims a connection it cannot see.\n */\nexport function decideConnectorConnectOutcome({\n requestId,\n handback,\n before,\n after,\n}: {\n requestId: string;\n handback: ConnectorConnectHandback | null;\n /** The vault before the browser opened; without it nothing but a hand-back counts. */\n before: ConnectorConnectBefore | null;\n /** The vault after the browser closed, re-read from the server; null when that read failed. */\n after: { rows: ReadonlyArray<CredentialVersionRow | null | undefined>; match: CredentialVersionRow | null } | null;\n}): ConnectorConnectOutcomeDecision {\n if (handback && (!handback.requestId || handback.requestId === requestId)) {\n return { kind: 'connected', credentialId: handback.credentialId ?? after?.match?.id ?? undefined };\n }\n if (!before || !after) return { kind: 'unchanged' };\n const { versions, targetId } = before;\n const changed = (row: CredentialVersionRow | null | undefined): row is CredentialVersionRow => {\n const now = credentialFingerprint(row);\n return Boolean(now && row?.id && versions[row.id] !== now);\n };\n if (targetId) {\n const target = after.rows.find((row) => row?.id === targetId);\n if (target?.status === 'active' && changed(target)) return { kind: 'connected', credentialId: targetId };\n }\n if (changed(after.match)) return { kind: 'connected', credentialId: after.match.id ?? undefined };\n const created = after.rows.filter((row): row is CredentialVersionRow =>\n Boolean(row?.id && row.status === 'active' && !versions[row.id]),\n );\n if (created.length === 1) return { kind: 'connected', credentialId: created[0].id ?? undefined };\n return { kind: 'unchanged' };\n}\n\n/**\n * How long after its save a hand-back that reached the app with no browser session waiting is still taken: the\n * session the app was killed during (Android kills it behind Custom Tabs, and the return page cold-starts it).\n */\nexport const PARKED_HANDBACK_MAX_AGE_MS = 30 * 60 * 1000;\n\n/**\n * The credential a PARKED hand-back names, when it may be taken as this request's answer; null otherwise.\n *\n * Anyone can open the app's scheme URL, and the vault snapshot the card took before the browser opened died with\n * the app, so \"the vault has it\" is not enough. The credential must be:\n * - an ACTIVE credential of the org (`rows` is a fresh read);\n * - one this request could have saved: for a site login, the login the card itself picks for the request with\n * that id named (its site or name; never another site's login or a catalog credential); for a catalog\n * connector, a connector credential, never a website login - the phone cannot resolve the template, so a name\n * alone cannot rule a connector credential in or out (see connectorConnectTarget);\n * - saved in the last PARKED_HANDBACK_MAX_AGE_MS, the stand-in for \"changed during the session\": every save on\n * the web page moves `updatedAt`, while an id lifted from anywhere else names a credential saved long ago.\n */\nexport function parkedHandbackCredential(\n rows: ReadonlyArray<CredentialVersionRow | null | undefined>,\n payload: ConnectorConnectPayload,\n credentialId: string | null | undefined,\n nowMs: number,\n): CredentialVersionRow | null {\n if (!credentialId) return null;\n const row = rows.find((r) => r?.id === credentialId && r.status === 'active');\n if (!row) return null;\n const fits = isSiteLoginRequest(payload)\n ? pickConnectorCredential(rows, { ...connectorConnectTarget(payload), credentialId })?.id === credentialId\n : !isSiteLoginCredential(row);\n if (!fits) return null;\n const savedAt = credentialSavedAt(row);\n return savedAt > 0 && nowMs - savedAt <= PARKED_HANDBACK_MAX_AGE_MS ? row : null;\n}\n"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import {jsx}from'react/jsx-runtime';import {useMemo,useCallback}from'react';import {PendingConnectorConnectCard}from'../features/chat/ConnectorConnectPrompt.js';import {displayConnectorConnectMessages,displayConnectorConnectText}from'../features/chat/connectorConnect.js';function displayStreaming(text) {
1
+ import {jsx}from'react/jsx-runtime';import {useMemo,useCallback}from'react';import {PendingConnectorConnectCard}from'../features/chat/ConnectorConnectPrompt.js';import {displayConnectorConnectMessages,displayConnectorConnectText}from'common';function displayStreaming(text) {
2
2
  return text ? displayConnectorConnectText(text, true) || null : null;
3
3
  }
4
4
  function useHostConnectorCard(chat, channelId) {
@@ -1 +1 @@
1
- {"version":3,"file":"useHostConnectorCard.js","sources":["../../src/hooks/useHostConnectorCard.tsx"],"sourcesContent":["/**\n * The `[CONNECTOR_CONNECT]` card for a native surface chat (shop, travel, jobs,\n * resume), built on the surface's HOST screen.\n *\n * The surface UI ships as a federated remote that must not bundle the host's data\n * stack (Apollo, redux, `common`, expo-web-browser) - see useShoppingHostChat. So\n * the host does everything protocol-shaped: it cleans the thread before the remote\n * sees it (the markers and the card's replies are not shown as text) and hands the\n * remote a render function for the card, which the remote only places above its\n * composer. The card is a host component; rendered inside the remote's tree it\n * still reads the host's providers, which wrap that tree.\n *\n * The reply goes to the host channel directly - not through the surface's own\n * `onSend`, which may route text to a local lane or wrap it in context - and only\n * counts as sent when the channel took it (sent, or parked in the durable outbox,\n * whose row then settles the request).\n */\nimport React, { useCallback, useMemo, type ReactNode } from 'react';\nimport { PendingConnectorConnectCard } from '../features/chat/ConnectorConnectPrompt';\nimport { displayConnectorConnectMessages, displayConnectorConnectText } from '../features/chat/connectorConnect';\nimport type { useChatStream } from './useChatStream';\n\ntype HostChatStream = Pick<ReturnType<typeof useChatStream>, 'messages' | 'queuedMessages' | 'sendMessage'>;\n\nexport interface HostConnectorCard {\n /** The card for the request still waiting in the thread (renders nothing when none is). */\n renderConnectorCard: (isDark: boolean) => ReactNode;\n /** A thread as the remote's transcript should show it: markers and card replies out. */\n displayMessages: <T extends { role: string; content: string }>(messages: ReadonlyArray<T>) => T[];\n /** The streaming reply as the remote should show it: a half-written marker never flashes. */\n displayStreaming: (text: string | null | undefined) => string | null;\n}\n\n/** The streaming reply as a remote shows it; module-level so its identity never changes. */\nfunction displayStreaming(text: string | null | undefined): string | null {\n return text ? displayConnectorConnectText(text, true) || null : null;\n}\n\nexport function useHostConnectorCard(chat: HostChatStream, channelId: string): HostConnectorCard {\n const { messages, queuedMessages, sendMessage } = chat;\n\n // The thread plus the outbox: a reply parked while the gateway is down is the\n // newest user row, so the request reads as answered and the card stays gone.\n const rows = useMemo(\n () => [\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ...queuedMessages.map((q) => ({ role: 'user', content: q.text })),\n ],\n [messages, queuedMessages],\n );\n\n const send = useCallback(\n async (text: string) => (await sendMessage(text, undefined, channelId)) !== 'failed',\n [sendMessage, channelId],\n );\n\n const renderConnectorCard = useCallback(\n (isDark: boolean) => <PendingConnectorConnectCard messages={rows} send={send} isDark={isDark} />,\n [rows, send],\n );\n\n return useMemo(\n () => ({ renderConnectorCard, displayMessages: displayConnectorConnectMessages, displayStreaming }),\n [renderConnectorCard],\n );\n}\n"],"names":[],"mappings":"gRAmCA,SAAS,iBAAiB,IAAgD,EAAA;AACxE,EAAA,OAAO,IAAO,GAAA,2BAAA,CAA4B,IAAM,EAAA,IAAI,KAAK,IAAO,GAAA,IAAA;AAClE;AACgB,SAAA,oBAAA,CAAqB,MAAsB,SAAsC,EAAA;AAC/F,EAAM,MAAA;AAAA,IACJ,QAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACE,GAAA,IAAA;AAIJ,EAAA,MAAM,OAAO,OAAQ,CAAA,MAAM,CAAC,GAAG,QAAA,CAAS,IAAI,CAAM,CAAA,MAAA;AAAA,IAChD,MAAM,CAAE,CAAA,IAAA;AAAA,IACR,SAAS,CAAE,CAAA;AAAA,GACX,CAAA,CAAA,EAAG,GAAG,cAAA,CAAe,IAAI,CAAM,CAAA,MAAA;AAAA,IAC/B,IAAM,EAAA,MAAA;AAAA,IACN,SAAS,CAAE,CAAA;AAAA,IACX,CAAC,CAAA,EAAG,CAAC,QAAA,EAAU,cAAc,CAAC,CAAA;AAChC,EAAA,MAAM,IAAO,GAAA,WAAA,CAAY,OAAO,IAAA,KAAkB,MAAM,WAAY,CAAA,IAAA,EAAM,MAAW,EAAA,SAAS,CAAO,KAAA,QAAA,EAAU,CAAC,WAAA,EAAa,SAAS,CAAC,CAAA;AACvI,EAAA,MAAM,mBAAsB,GAAA,WAAA,CAAY,CAAC,MAAA,yBAAqB,2BAA4B,EAAA,EAAA,QAAA,EAAU,IAAM,EAAA,IAAA,EAAY,MAAgB,EAAA,CAAA,EAAI,CAAC,IAAA,EAAM,IAAI,CAAC,CAAA;AACtJ,EAAA,OAAO,QAAQ,OAAO;AAAA,IACpB,mBAAA;AAAA,IACA,eAAiB,EAAA,+BAAA;AAAA,IACjB;AAAA,GACF,CAAA,EAAI,CAAC,mBAAmB,CAAC,CAAA;AAC3B"}
1
+ {"version":3,"file":"useHostConnectorCard.js","sources":["../../src/hooks/useHostConnectorCard.tsx"],"sourcesContent":["/**\n * The `[CONNECTOR_CONNECT]` card for a native surface chat (shop, travel, jobs,\n * resume), built on the surface's HOST screen.\n *\n * The surface UI ships as a federated remote that must not bundle the host's data\n * stack (Apollo, redux, `common`, expo-web-browser) - see useShoppingHostChat. So\n * the host does everything protocol-shaped: it cleans the thread before the remote\n * sees it (the markers and the card's replies are not shown as text) and hands the\n * remote a render function for the card, which the remote only places above its\n * composer. The card is a host component; rendered inside the remote's tree it\n * still reads the host's providers, which wrap that tree.\n *\n * The reply goes to the host channel directly - not through the surface's own\n * `onSend`, which may route text to a local lane or wrap it in context - and only\n * counts as sent when the channel took it (sent, or parked in the durable outbox,\n * whose row then settles the request).\n */\nimport React, { useCallback, useMemo, type ReactNode } from 'react';\nimport { PendingConnectorConnectCard } from '../features/chat/ConnectorConnectPrompt';\nimport { displayConnectorConnectMessages, displayConnectorConnectText } from '../features/chat/connectorConnect';\nimport type { useChatStream } from './useChatStream';\n\ntype HostChatStream = Pick<ReturnType<typeof useChatStream>, 'messages' | 'queuedMessages' | 'sendMessage'>;\n\nexport interface HostConnectorCard {\n /** The card for the request still waiting in the thread (renders nothing when none is). */\n renderConnectorCard: (isDark: boolean) => ReactNode;\n /** A thread as the remote's transcript should show it: markers and card replies out. */\n displayMessages: <T extends { role: string; content: string }>(messages: ReadonlyArray<T>) => T[];\n /** The streaming reply as the remote should show it: a half-written marker never flashes. */\n displayStreaming: (text: string | null | undefined) => string | null;\n}\n\n/** The streaming reply as a remote shows it; module-level so its identity never changes. */\nfunction displayStreaming(text: string | null | undefined): string | null {\n return text ? displayConnectorConnectText(text, true) || null : null;\n}\n\nexport function useHostConnectorCard(chat: HostChatStream, channelId: string): HostConnectorCard {\n const { messages, queuedMessages, sendMessage } = chat;\n\n // The thread plus the outbox: a reply parked while the gateway is down is the\n // newest user row, so the request reads as answered and the card stays gone.\n const rows = useMemo(\n () => [\n ...messages.map((m) => ({ role: m.role, content: m.content })),\n ...queuedMessages.map((q) => ({ role: 'user', content: q.text })),\n ],\n [messages, queuedMessages],\n );\n\n const send = useCallback(\n async (text: string) => (await sendMessage(text, undefined, channelId)) !== 'failed',\n [sendMessage, channelId],\n );\n\n const renderConnectorCard = useCallback(\n (isDark: boolean) => <PendingConnectorConnectCard messages={rows} send={send} isDark={isDark} />,\n [rows, send],\n );\n\n return useMemo(\n () => ({ renderConnectorCard, displayMessages: displayConnectorConnectMessages, displayStreaming }),\n [renderConnectorCard],\n );\n}\n"],"names":[],"mappings":"kPAmCA,SAAS,iBAAiB,IAAgD,EAAA;AACxE,EAAA,OAAO,IAAO,GAAA,2BAAA,CAA4B,IAAM,EAAA,IAAI,KAAK,IAAO,GAAA,IAAA;AAClE;AACgB,SAAA,oBAAA,CAAqB,MAAsB,SAAsC,EAAA;AAC/F,EAAM,MAAA;AAAA,IACJ,QAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACE,GAAA,IAAA;AAIJ,EAAA,MAAM,OAAO,OAAQ,CAAA,MAAM,CAAC,GAAG,QAAA,CAAS,IAAI,CAAM,CAAA,MAAA;AAAA,IAChD,MAAM,CAAE,CAAA,IAAA;AAAA,IACR,SAAS,CAAE,CAAA;AAAA,GACX,CAAA,CAAA,EAAG,GAAG,cAAA,CAAe,IAAI,CAAM,CAAA,MAAA;AAAA,IAC/B,IAAM,EAAA,MAAA;AAAA,IACN,SAAS,CAAE,CAAA;AAAA,IACX,CAAC,CAAA,EAAG,CAAC,QAAA,EAAU,cAAc,CAAC,CAAA;AAChC,EAAA,MAAM,IAAO,GAAA,WAAA,CAAY,OAAO,IAAA,KAAkB,MAAM,WAAY,CAAA,IAAA,EAAM,MAAW,EAAA,SAAS,CAAO,KAAA,QAAA,EAAU,CAAC,WAAA,EAAa,SAAS,CAAC,CAAA;AACvI,EAAA,MAAM,mBAAsB,GAAA,WAAA,CAAY,CAAC,MAAA,yBAAqB,2BAA4B,EAAA,EAAA,QAAA,EAAU,IAAM,EAAA,IAAA,EAAY,MAAgB,EAAA,CAAA,EAAI,CAAC,IAAA,EAAM,IAAI,CAAC,CAAA;AACtJ,EAAA,OAAO,QAAQ,OAAO;AAAA,IACpB,mBAAA;AAAA,IACA,eAAiB,EAAA,+BAAA;AAAA,IACjB;AAAA,GACF,CAAA,EAAI,CAAC,mBAAmB,CAAC,CAAA;AAC3B"}
@@ -1,4 +1,4 @@
1
- import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useMemo,useLayoutEffect,useCallback,useState,useRef,useEffect}from'react';import {useWindowDimensions,Keyboard,View,StyleSheet,Pressable}from'react-native';import {getDefaultLeftItems,Box,Text,InputToolBar}from'@admin-layout/gluestack-ui-mobile';import {useSafeAreaInsets,SafeAreaView}from'react-native-safe-area-context';import {useNavigation,useRoute,CommonActions}from'@react-navigation/native';import {MessageActionBar}from'../../features/chat/MessageActionBar.js';import {ChatTranscript}from'../../features/chat/ChatTranscript.js';import {AskUserCard}from'../../features/chat/AskUserCard.js';import {pendingAskUser}from'../../features/chat/askUser.js';import {PendingConnectorConnectCard}from'../../features/chat/ConnectorConnectPrompt.js';import {QueuedFollowUps}from'../../features/chat/QueuedFollowUps.js';import {displayConnectorConnectMessages,displayConnectorConnectText}from'../../features/chat/connectorConnect.js';import {useCdecliConnection}from'../../contexts/CdecliConnectionContext.js';import {useChatLiveStream}from'../../contexts/ChatLiveStreamContext.js';import {KeyboardAwareScreen,KeyboardComposerDock}from'../../components/KeyboardComposerDock.js';import {YantraBrandLoader,YANTRA_LOADER_SIZE_COMPACT}from'../../components/YantraBrandLoader.js';import ThinkingIndicator from'../../components/ThinkingIndicator.js';import {mobileTokens}from'../../theme/mobileTokens.js';import {useAppTheme}from'../../theme/AppThemeProvider.js';import DeepSearchModal from'../Home/components/DeepSearchModal.js';import {normalizeSummaryText,extractDeepSearchSources}from'../Home/deepSearchUtils.js';import {AudioRecorderPanel}from'../../features/audio-input/AudioRecorderPanel.js';import {NativeDictationBar}from'../../features/audio-input/NativeDictationBar.js';import {isNativeDictationAvailable}from'../../features/audio-input/nativeDictation.js';import {useImageAttachments}from'../../features/attachments/useImageAttachments.js';import {ComposerAttachmentBar}from'../../features/attachments/ComposerAttachmentBar.js';import {useComposerAttachMenu}from'../../features/attachments/ComposerAttachMenu.js';import {hasComposerAttachments,takeComposerAttachments}from'../../features/attachments/composerAttachmentHandoff.js';import {MicErrorBoundary}from'../../features/audio-input/MicErrorBoundary.js';import {requestMicPermission}from'../../features/audio-input/useAudioPermission.js';import {shouldRedirectToShop}from'../../features/shopping/chat/homeShoppingIntent.js';import {replaceWithShopNative,shopHandoffParams,pushShopNative}from'../../features/shopping/chat/openShopNative.js';var __defProp = Object.defineProperty;
1
+ import {jsx,jsxs,Fragment}from'react/jsx-runtime';import {useMemo,useLayoutEffect,useCallback,useState,useRef,useEffect}from'react';import {useWindowDimensions,Keyboard,View,StyleSheet,Pressable}from'react-native';import {getDefaultLeftItems,Box,Text,InputToolBar}from'@admin-layout/gluestack-ui-mobile';import {useSafeAreaInsets,SafeAreaView}from'react-native-safe-area-context';import {useNavigation,useRoute,CommonActions}from'@react-navigation/native';import {MessageActionBar}from'../../features/chat/MessageActionBar.js';import {ChatTranscript}from'../../features/chat/ChatTranscript.js';import {AskUserCard}from'../../features/chat/AskUserCard.js';import {pendingAskUser}from'../../features/chat/askUser.js';import {PendingConnectorConnectCard}from'../../features/chat/ConnectorConnectPrompt.js';import {QueuedFollowUps}from'../../features/chat/QueuedFollowUps.js';import {displayConnectorConnectMessages,displayConnectorConnectText}from'common';import {useCdecliConnection}from'../../contexts/CdecliConnectionContext.js';import {useChatLiveStream}from'../../contexts/ChatLiveStreamContext.js';import {KeyboardAwareScreen,KeyboardComposerDock}from'../../components/KeyboardComposerDock.js';import {YantraBrandLoader,YANTRA_LOADER_SIZE_COMPACT}from'../../components/YantraBrandLoader.js';import ThinkingIndicator from'../../components/ThinkingIndicator.js';import {mobileTokens}from'../../theme/mobileTokens.js';import {useAppTheme}from'../../theme/AppThemeProvider.js';import DeepSearchModal from'../Home/components/DeepSearchModal.js';import {normalizeSummaryText,extractDeepSearchSources}from'../Home/deepSearchUtils.js';import {AudioRecorderPanel}from'../../features/audio-input/AudioRecorderPanel.js';import {NativeDictationBar}from'../../features/audio-input/NativeDictationBar.js';import {isNativeDictationAvailable}from'../../features/audio-input/nativeDictation.js';import {useImageAttachments}from'../../features/attachments/useImageAttachments.js';import {ComposerAttachmentBar}from'../../features/attachments/ComposerAttachmentBar.js';import {useComposerAttachMenu}from'../../features/attachments/ComposerAttachMenu.js';import {hasComposerAttachments,takeComposerAttachments}from'../../features/attachments/composerAttachmentHandoff.js';import {MicErrorBoundary}from'../../features/audio-input/MicErrorBoundary.js';import {requestMicPermission}from'../../features/audio-input/useAudioPermission.js';import {shouldRedirectToShop}from'../../features/shopping/chat/homeShoppingIntent.js';import {replaceWithShopNative,shopHandoffParams,pushShopNative}from'../../features/shopping/chat/openShopNative.js';var __defProp = Object.defineProperty;
2
2
  var __defProps = Object.defineProperties;
3
3
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
4
  var __getOwnPropSymbols = Object.getOwnPropertySymbols;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adminide-stack/yantra-mobile",
3
- "version": "12.0.69-alpha.21",
3
+ "version": "12.0.69-alpha.22",
4
4
  "description": "Sample core for higher packages to depend on",
5
5
  "license": "ISC",
6
6
  "author": "CDMBase LLC",
@@ -26,7 +26,7 @@
26
26
  "uuid": "^9.0.0"
27
27
  },
28
28
  "devDependencies": {
29
- "common": "12.0.69-alpha.20"
29
+ "common": "12.0.69-alpha.22"
30
30
  },
31
31
  "peerDependencies": {
32
32
  "@admin-layout/gluestack-ui-mobile": "*",
@@ -59,5 +59,5 @@
59
59
  "typescript": {
60
60
  "definition": "lib/index.d.ts"
61
61
  },
62
- "gitHead": "50d645d961c0bb3f954501897058dd80efbad750"
62
+ "gitHead": "4dbcd3ab1cf3d76ada56146666fdf3d151cdd62a"
63
63
  }