@guideai/react-native 0.1.0 → 0.2.0
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/README.md +27 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +52 -4
- package/dist/index.mjs +2 -2
- package/dist/ui.cjs +1 -1
- package/dist/ui.mjs +1 -1
- package/dist/{useOnboarding-DhVtf0U2.js → useOnboarding-DAEGoYSs.js} +120 -27
- package/dist/useOnboarding-DAEGoYSs.js.map +1 -0
- package/dist/{useOnboarding-C7eFhp2o.cjs → useOnboarding-DWkCW5v0.cjs} +120 -27
- package/dist/useOnboarding-DWkCW5v0.cjs.map +1 -0
- package/package.json +2 -2
- package/dist/useOnboarding-C7eFhp2o.cjs.map +0 -1
- package/dist/useOnboarding-DhVtf0U2.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useOnboarding-DWkCW5v0.cjs","sources":["../../core/src/chat/chat-view.ts","../../core/src/copilot/tool-labels.ts","../../core/src/platform/memory-kv-store.ts","../../core/src/copilot/harness-validator.ts","../../core/src/copilot/conversation-memory.ts","../../core/src/copilot/tool-executor.ts","../../core/src/core/api-errors.ts","../../core/src/core/endpoints.ts","../../core/src/copilot/copilot-harness.ts","../src/transport/rn-transport.ts","../src/platform/rn-platform.ts","../src/auth/host-auth.ts","../src/screen/screen-registry.ts","../src/tools/rn-tool-registry.ts","../src/runtime.ts","../src/api/rn-api-client.ts","../src/react/context.ts","../src/react/navigation.ts","../src/debug/full-trace.ts","../src/setup.ts","../src/react/useGuideAI.ts","../src/react/useOnboarding.ts"],"sourcesContent":["// ============================================================\n// @guideai/core — Chat View Interface\n// ============================================================\n//\n// The seam between the chat *brain* (ChatManager) and whatever\n// displays it. `ChatRenderer` is the built-in DOM implementation;\n// `HeadlessChatView` emits events instead so a host application can\n// render the conversation with its own components.\n//\n// Every method here is a command to display something. Nothing in\n// this file reads the page — page reading (snapshot, fingerprint,\n// detection) lives in the brain and runs in both modes.\n\nimport type { AssistantPlanStep, ToolCallResponse, DemoSuggestion } from '../copilot/copilot-types';\nimport type {\n ChatCallbacks,\n ChatMessage,\n ChatMode,\n ChatRenderOptions,\n EscalationSignal,\n GeneratedStep,\n ToolCallDisplayInfo,\n} from './chat-types';\nimport type { NavigationAction } from '../copilot/copilot-types';\nimport type { VoiceLanguagePreference } from '../voice/voice-language';\nimport type { VoiceMediaCommand } from '../voice/voice-language';\n\n/**\n * What the mic control is doing right now. Drives the button's look and\n * its accessible label; the brain owns the transitions.\n */\nexport type VoiceStatus =\n | 'idle'\n | 'connecting'\n | 'listening'\n | 'thinking'\n | 'speaking'\n | 'error';\n\n/** One-shot recording status for the separate record-and-send control. */\nexport type BatchVoiceStatus =\n | 'idle'\n | 'requesting'\n | 'recording'\n | 'transcribing'\n | 'error';\n\nexport type WalkthroughButtonState = 'building' | 'ready' | 'error';\n\n/** A field the intro card may ask for — the server's closed set. */\nexport type IntroField = 'name' | 'email' | 'company' | 'role' | 'phone';\n\nexport interface IntroCardRequest {\n fields: IntroField[];\n /**\n * The site looks visitors up by work email, so the card says so under the\n * fields (UK GDPR transparency: told before the email is given).\n */\n lookupNotice?: boolean;\n /**\n * The visitor pressed Save. Resolves to an error message to show on the\n * card, or null once saved (the caller then hides the card).\n */\n onSubmit: (values: Partial<Record<IntroField, string>>) => Promise<string | null>;\n onSkip: () => void;\n}\n\n/**\n * Display surface for a chat conversation.\n *\n * Implemented by `ChatRenderer` (DOM) and `HeadlessChatView` (events).\n * `ChatManager` depends on this and never on the concrete renderer.\n */\nexport interface ChatView {\n // ── Lifecycle ──────────────────────────────────────────────\n render(callbacks: ChatCallbacks, options?: ChatRenderOptions): void;\n remove(): void;\n isOpen(): boolean;\n\n // ── Messages ───────────────────────────────────────────────\n addMessage(msg: ChatMessage): void;\n loadHistory(messages: ChatMessage[]): void;\n clearMessages(): void;\n addWalkthroughCompleteMessage(): void;\n /** Disable open confirmation cards after an answer given by voice. */\n settleConfirmation?(answer: 'confirmed' | 'cancelled'): void;\n\n // ── Streaming ──────────────────────────────────────────────\n beginStreamingMessage(): void;\n appendStreamDelta(delta: string): void;\n finalizeStreamingMessage(msg: ChatMessage): void;\n setStreaming(streaming: boolean): void;\n\n // ── Busy state ─────────────────────────────────────────────\n setLoading(loading: boolean): void;\n setLoadingStatus(status: string): void;\n\n // ── Mode ───────────────────────────────────────────────────\n setMode(mode: ChatMode): void;\n showModeBanner(): void;\n showModeSwitch(): void;\n showModePill(): void;\n showModeIntro(): void;\n\n // ── Tool calls ─────────────────────────────────────────────\n addToolCallCard(tool: string, description?: string): void;\n updateToolCallCard(tool: string, success: boolean, summary?: string): void;\n /** Draw the plan the assistant stated for the message being streamed. */\n renderPlan(plan: { steps: AssistantPlanStep[]; note?: string | null }): void;\n /** Advance the plan step that owns this endpoint. */\n markPlanStep(tool: string, state: 'running' | 'done' | 'failed'): void;\n\n // ── Walkthroughs & demos ───────────────────────────────────\n setWalkthroughButtonState(\n messageId: string,\n state: WalkthroughButtonState,\n errorMessage?: string,\n ): void;\n playDemo(demo: DemoSuggestion): boolean;\n /** Control the active demo/native media without an agent round-trip. */\n controlActiveMedia(command: VoiceMediaCommand, demo?: DemoSuggestion | null): Promise<boolean>;\n\n // ── Input ──────────────────────────────────────────────────\n setInputValue(value: string): void;\n\n // ── Voice ──────────────────────────────────────────────────\n /** Reveal the mic control. Hidden until the site is known to support voice. */\n showVoiceButton(): void;\n /** Hide the mic control again (plan lost, capability withdrawn). */\n hideVoiceButton(): void;\n /** Paint the mic control for the current voice state. */\n setVoiceStatus(status: VoiceStatus, message?: string): void;\n /** Show partial/final speech beside the live controls without hiding chat history. */\n setLiveVoiceTranscript(event: { role: 'user' | 'assistant'; text: string; final: boolean }): void;\n /** Accessible first-use chooser. Null means the visitor cancelled. */\n requestVoiceLanguage(\n options: VoiceLanguagePreference[],\n current?: VoiceLanguagePreference | null,\n ): Promise<VoiceLanguagePreference | null>;\n /** Show the selected language on voice surfaces. */\n setVoiceLanguage(language: VoiceLanguagePreference): void;\n /** Reveal the lower-cost record-one-message control. */\n showBatchVoiceButton(): void;\n /** Hide the record-one-message control. */\n hideBatchVoiceButton(): void;\n /** Paint the record-one-message control for its current state. */\n setBatchVoiceStatus(status: BatchVoiceStatus, message?: string): void;\n /**\n * Show the spoken-reply switch for recorded messages; null hides it.\n * Optional: headless hosts read replies themselves.\n */\n setVoiceReplyState?(enabled: boolean | null): void;\n /** Live support became unavailable: hide the \"talk to a person\" control. */\n hideSupportButton?(): void;\n\n // ── Support handoff ────────────────────────────────────────\n showSupportButton(): void;\n\n // ── Intro card (a skill's `collects_intro`) ────────────────\n /** Offer the skippable \"who are you\" card. Optional: headless hosts may omit it. */\n showIntroCard?(request: IntroCardRequest): void;\n /** Take the card down (saved, skipped, or no longer wanted). */\n hideIntroCard?(): void;\n\n // ── Expand / collapse (docked sidebar) ─────────────────────\n expand(dock?: 'right' | 'left' | 'bottom'): void;\n collapse(): void;\n toggleExpand(dock?: 'right' | 'left' | 'bottom'): void;\n isExpandedSidebar(): boolean;\n}\n\n/**\n * Events emitted by `HeadlessChatView`, keyed by event name.\n *\n * These mirror the `ChatView` commands: where the DOM renderer would\n * draw something, the headless view announces it instead.\n */\nexport interface ChatViewEvents {\n /** A complete message was added to the conversation. */\n message: ChatMessage;\n /** Prior conversation was restored. */\n history: ChatMessage[];\n /** The conversation was cleared. */\n cleared: void;\n /** A walkthrough finished. */\n walkthroughComplete: void;\n\n /** A streaming answer started. */\n 'stream:start': void;\n /** A chunk of a streaming answer arrived. */\n 'stream:delta': { delta: string; text: string };\n /** A streaming answer completed. */\n 'stream:end': ChatMessage;\n /** Streaming became active or inactive. */\n streaming: { streaming: boolean };\n\n /** Busy state changed. `status` carries the latest status line. */\n loading: { loading: boolean; status: string | null };\n\n /** The active chat mode changed. */\n mode: { mode: ChatMode };\n /** A mode affordance should be surfaced (banner, switch, pill, intro). */\n modeHint: { kind: 'banner' | 'switch' | 'pill' | 'intro'; mode: ChatMode };\n\n /** A tool call started. */\n 'tool:start': { tool: string; description?: string };\n /** A tool call finished. */\n 'tool:end': { tool: string; success: boolean; summary?: string };\n\n /** The assistant stated what it is about to do. */\n 'plan:start': { plan: { steps: AssistantPlanStep[]; note?: string | null } };\n /** A plan step changed state as its endpoint ran. */\n 'plan:step': { tool: string; state: 'running' | 'done' | 'failed' };\n\n /** A walkthrough build changed state. */\n walkthroughState: {\n messageId: string;\n state: WalkthroughButtonState;\n error?: string;\n };\n /** A demo is ready to play. Host decides whether it can show it. */\n demo: DemoSuggestion;\n\n /** The brain wants text placed in the input box. */\n inputValue: { value: string };\n\n /** The mic control became available or unavailable. */\n 'voice:available': { available: boolean };\n /** Voice status changed. */\n 'voice:status': { status: VoiceStatus; message?: string };\n /** Partial and final transcript updates for host-owned voice UI. */\n 'voice:transcript': { role: 'user' | 'assistant'; text: string; final: boolean };\n /** Headless hosts can render their own chooser when this event is emitted. */\n 'voice:languageRequested': {\n options: VoiceLanguagePreference[];\n current: VoiceLanguagePreference | null;\n /** Answer with the visitor's pick, or null to keep `current`. */\n choose: (language: VoiceLanguagePreference | null) => void;\n };\n 'voice:languageChanged': { language: VoiceLanguagePreference };\n /** Batch voice became available or unavailable. */\n 'batchVoice:available': { available: boolean };\n /** Batch voice status changed. */\n 'batchVoice:status': { status: BatchVoiceStatus; message?: string };\n\n /** Escalation to human support should be offered. */\n supportOffered: void;\n /** Whether a \"talk to a person\" control should show at all (live support on). */\n 'support:available': { available: boolean };\n /** Live support with a person: where the conversation stands. */\n 'support:state': HeadlessSupportState;\n /** Live support with a person: a message on the thread (visitor, agent or system). */\n 'support:message': HeadlessSupportMessage;\n\n /** The agent speaks first: show this line (and its quick replies) as a greeting. */\n opener: { text: string; replies: string[] };\n /** Ask the visitor for these details before chatting. Call `onSubmit` / `onSkip`. */\n 'intro:requested': IntroCardRequest;\n /** The intro card is no longer needed. */\n 'intro:hidden': void;\n /** A pending action was answered (by the visitor, or by voice). */\n 'confirmation:settled': { answer: 'confirmed' | 'cancelled' };\n /** Spoken replies are on or off; null when the site has no voice. */\n voiceReply: { enabled: boolean | null };\n /** A tool call needs explicit user confirmation before running. */\n confirmationNeeded: ToolCallResponse;\n\n /** The view was torn down. */\n closed: void;\n}\n\nexport type ChatViewEventName = keyof ChatViewEvents;\n\nexport type ChatViewListener<K extends ChatViewEventName> = (\n payload: ChatViewEvents[K],\n) => void;\n\n/**\n * One answer, flattened for a host application.\n *\n * `text` is the same string our own chat bubble would display. Everything\n * the brain produced alongside it is here too, and `raw` is the untouched\n * message if the host wants something this shape does not name.\n */\nexport interface HeadlessReply {\n /** Display-ready answer text. */\n text: string;\n /** Present when GuideAI routed this turn to the host's configured assistant. */\n intent: 'delegate' | null;\n /** Model-supplied explanation of the routing decision. */\n intentReason: string | null;\n /** Walkthrough steps, selectors already resolved against the live page. */\n steps: GeneratedStep[] | null;\n /** Title for the walkthrough, when there is one. */\n title: string | null;\n /** A playable demo, when the brain suggested one. */\n demo: DemoSuggestion | null;\n /** A page the answer wants to send the visitor to. */\n navigation: NavigationAction | null;\n /** Present when the brain recommends handing off to a human. */\n escalation: EscalationSignal | null;\n /** Tools executed while producing this answer, in order. */\n toolsUsed: ToolCallDisplayInfo[];\n /** A tool awaiting explicit user confirmation before it runs. */\n pendingAction: ToolCallResponse | null;\n /**\n * Pictures the agent drew for this answer (diagrams, timelines, charts).\n * Each also sits in `text`, where it belongs, as a Markdown image.\n */\n images: HeadlessImage[];\n /** The untouched message the brain produced. */\n raw: ChatMessage;\n}\n\n/** A picture drawn for an answer: a PNG to show, and the SVG it was drawn as. */\nexport interface HeadlessImage {\n url: string;\n svg: string | null;\n alt: string;\n width: number;\n height: number;\n}\n\n/** Only pictures our server drew and stored are passed on. */\nconst DRAWING_URL = /^https:\\/\\/res\\.cloudinary\\.com\\/[\\w-]+\\/image\\/upload\\//;\nconst IMAGE_BLOCK = /```image[ \\t]*\\n([\\s\\S]*?)```/g;\n\n/**\n * Image blocks become Markdown images a host can render with whatever it\n * already uses for Markdown, and are listed on their own as well.\n */\nexport function headlessImages(text: string): { text: string; images: HeadlessImage[] } {\n const images: HeadlessImage[] = [];\n const out = (text || '').replace(IMAGE_BLOCK, (_block, raw: string) => {\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(raw.trim()) as Record<string, unknown>;\n } catch {\n return '';\n }\n if (typeof data.url !== 'string' || !DRAWING_URL.test(data.url)) return '';\n const image: HeadlessImage = {\n url: data.url,\n svg: typeof data.svg === 'string' && DRAWING_URL.test(data.svg) ? data.svg : null,\n alt: typeof data.alt === 'string' ? data.alt.slice(0, 200) : '',\n width: Math.round(Number(data.width)) || 1600,\n height: Math.round(Number(data.height)) || 900,\n };\n images.push(image);\n return `![${image.alt.replace(/[[\\]]/g, '')}](${image.url})`;\n });\n return { text: out, images };\n}\n\n/** Live support with a person, as a headless host draws it. */\nexport interface HeadlessSupportState {\n /**\n * `intake`: ask for name, email and the issue, then call `support.start()`.\n * `open`: the thread is live; `support.send()` adds to it.\n * `closed`: the host closed its panel; the thread is kept for this session.\n * `unavailable`: live support is switched off for this site.\n */\n status: 'intake' | 'open' | 'closed' | 'unavailable';\n /** People from the site's team online right now. */\n agentsOnline: number;\n /** The site's own opening line for live support, if it set one. */\n greeting: string;\n /** What the visitor gave last time (or identify() data), to prefill intake. */\n contact: { name: string; email: string };\n}\n\nexport interface HeadlessSupportMessage {\n id: string;\n from: 'visitor' | 'agent' | 'system';\n /** The agent's name, when the inbox shares it. */\n name: string | null;\n text: string;\n at: string;\n}\n\n/** A completed answer produced outside GuideAI but presented through its normal UI pipeline. */\nexport interface ExternalReply {\n text: string;\n steps?: GeneratedStep[] | null;\n title?: string | null;\n navigation?: NavigationAction | null;\n}\n\n/** Flattens a brain-produced message into the host-facing reply shape. */\nexport function toHeadlessReply(msg: ChatMessage): HeadlessReply {\n // The headless view has already turned pictures into Markdown images.\n const { text, images } = msg.images ? { text: msg.text, images: msg.images } : headlessImages(msg.text);\n return {\n text,\n intent: msg.intent ?? null,\n intentReason: msg.intentReason ?? null,\n steps: msg.guideSteps ?? null,\n title: msg.guideTitle ?? null,\n demo: msg.demo ?? null,\n navigation: msg.navigation ?? null,\n escalation: msg.escalation ?? null,\n toolsUsed: msg.toolCalls ?? [],\n pendingAction: msg.confirmationNeeded ? msg.pendingToolCall ?? null : null,\n images,\n raw: msg,\n };\n}\n","/**\n * Human-readable descriptions for tool calls.\n *\n * The chat UI must never show a raw endpoint or tool id — \"get sites\" or\n * \"api_v1_get_usage_summary\" tells the user nothing about what is happening.\n * The model supplies a `label` on each tool call; when it doesn't (older\n * prompts, or a call recovered from a legacy single-call response) these\n * helpers derive one from the HTTP method and the tool name.\n */\n\nimport type { ToolCallResponse } from './copilot-types';\n\n/** Verb prefixes the model bakes into tool names — stripped before phrasing. */\nconst NAME_PREFIXES = [\n /^api[\\s_-]*v?\\d*[\\s_-]*/i,\n /^(get|fetch|list|read|retrieve|show|query|search|find|lookup|look up)[\\s_-]+/i,\n /^(create|add|post|new|insert|register)[\\s_-]+/i,\n /^(update|patch|put|edit|modify|set)[\\s_-]+/i,\n /^(delete|remove|destroy|archive)[\\s_-]+/i,\n /^(check|verify|validate|count|send|export|download)[\\s_-]+/i,\n];\n\n/** Method → the verb shown to the user. */\nconst METHOD_VERBS: Record<string, string> = {\n GET: 'Retrieving',\n POST: 'Creating',\n PUT: 'Updating',\n PATCH: 'Updating',\n DELETE: 'Removing',\n};\n\n/**\n * Verbs implied by the tool name itself. Checked before the method verb so a\n * POST search endpoint reads \"Looking up …\" rather than \"Creating …\".\n */\nconst NAME_VERBS: { pattern: RegExp; verb: string }[] = [\n { pattern: /^(search|find|lookup|look_up|query)/i, verb: 'Looking up' },\n { pattern: /^(list|get_all|getall)/i, verb: 'Retrieving' },\n { pattern: /^(count|stats|statistics|summary|analytics|metrics|usage|report)/i, verb: 'Gathering' },\n { pattern: /^(check|verify|validate|status)/i, verb: 'Checking' },\n { pattern: /^(create|add|new|insert|register)/i, verb: 'Creating' },\n { pattern: /^(update|edit|modify|set|patch)/i, verb: 'Updating' },\n { pattern: /^(delete|remove|destroy|archive)/i, verb: 'Removing' },\n { pattern: /^(send|email|notify|invite)/i, verb: 'Sending' },\n { pattern: /^(export|download)/i, verb: 'Preparing' },\n];\n\n/** Nouns that already read as a thing, so no \"information\" suffix is added. */\nconst SELF_DESCRIBING = /\\b(information|details|data|summary|stats|statistics|activity|history|settings|status|usage|overview|report|list|metrics|analytics|breakdown)\\b/i;\n\n/** Turn a snake/camel/kebab tool name into spaced words. */\nfunction humanizeName(name: string): string {\n // Split camelCase and normalize separators first, so the verb prefixes\n // below match \"getUserActivity\" as readily as \"get_user_activity\".\n let out = name\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_\\-/]+/g, ' ')\n // Generated names carry the API version in the middle\n // (\"list_stores_api_v1_stores_get\") and end in the HTTP method. Both\n // are noise wherever they sit.\n .replace(/\\bapi\\s*v?\\d+\\b/gi, ' ')\n .replace(/\\s+(get|post|put|patch|delete)$/i, '')\n .replace(/\\s+/g, ' ')\n .trim()\n .toLowerCase();\n for (const prefix of NAME_PREFIXES) {\n out = out.replace(prefix, '');\n }\n // Generated names also repeat the resource (\"stores … stores\", \"survey …\n // surveys\"). Keep one mention, preferring the path form at the tail, and\n // drop bare path-param tokens that describe nothing.\n const kept: string[] = [];\n for (const word of out.trim().split(' ').reverse()) {\n const stem = word.replace(/s$/, '');\n if (!word || stem === 'id' || stem === 'uuid') continue;\n if (kept.some((k) => k.replace(/s$/, '') === stem)) continue;\n kept.push(word);\n }\n return kept.reverse().join(' ');\n}\n\nfunction pickVerb(toolName: string, noun: string, method: string): string {\n // Action verb from the tool name itself (\"search_users\" -> Looking up).\n const bare = toolName.replace(NAME_PREFIXES[0], '');\n for (const { pattern, verb } of NAME_VERBS) {\n if (pattern.test(bare)) return verb;\n }\n // Otherwise let the subject choose (\"usage summary\" -> Gathering).\n for (const { pattern, verb } of NAME_VERBS) {\n if (pattern.test(noun)) return verb;\n }\n return METHOD_VERBS[method.toUpperCase()] || 'Retrieving';\n}\n\n/**\n * Describe one tool call in plain language.\n *\n * Prefers the model-supplied label; otherwise builds a phrase such as\n * \"Retrieving site information\" from the tool name and HTTP method.\n */\nexport function describeToolCall(toolCall: ToolCallResponse): string {\n const supplied = (toolCall.label || '').trim();\n if (supplied) return supplied;\n\n let noun = humanizeName(toolCall.tool || '');\n if (!noun) return 'Retrieving information';\n\n const verb = pickVerb(toolCall.tool || '', noun, toolCall.method || 'GET');\n\n // REST convention: a trailing \"/me\" means the caller's own record.\n const ownScope = /\\s+(me|self|current|mine)$/.test(noun);\n if (ownScope) noun = `your ${noun.replace(/\\s+(me|self|current|mine)$/, '')}`;\n\n if (SELF_DESCRIBING.test(noun) || ownScope) return `${verb} ${noun}`;\n\n // \"sites\" + \" information\" reads better singular: \"site information\".\n const singular = noun.replace(/([a-z])(?<!s|u|i)s$/, '$1');\n return `${verb} ${singular} information`;\n}\n\n/** Status line while a concurrent batch is in flight. */\nexport function describeToolBatch(toolCalls: ToolCallResponse[]): string {\n if (toolCalls.length === 0) return 'Working on it';\n if (toolCalls.length === 1) return describeToolCall(toolCalls[0]);\n return `Gathering information from ${toolCalls.length} sources`;\n}\n","// ============================================================\n// @guideai/core — In-memory key/value store\n// ============================================================\n//\n// The reference KVStore. Two jobs: it proves the contract is satisfiable\n// with no DOM and no host, and it is what a test injects instead of\n// depending on whatever `localStorage` the runtime happens to provide.\n// ============================================================\n\nimport type { KVStore } from './platform-adapter';\n\nexport class MemoryKVStore implements KVStore {\n private readonly entries = new Map<string, string>();\n\n getItem(key: string): string | null {\n const value = this.entries.get(key);\n return value === undefined ? null : value;\n }\n\n setItem(key: string, value: string): void {\n this.entries.set(key, String(value));\n }\n\n removeItem(key: string): void {\n this.entries.delete(key);\n }\n\n /** Test convenience — not part of the KVStore contract. */\n clear(): void {\n this.entries.clear();\n }\n}\n","/**\n * Client-side validation for the Copilot Harness.\n *\n * Defense-in-depth: the backend has CopilotGuardrails, but the SDK\n * should also validate to catch issues early, prevent bad requests,\n * and give clear error messages before data reaches the server.\n */\n\nimport type {\n ToolCallResponse,\n ToolExecutionResult,\n CopilotToolDef,\n} from './copilot-types';\n\n/** Validation result with pass/fail and optional error message. */\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n /** Sanitized/cleaned value (when applicable). */\n sanitized?: string;\n}\n\n/** Enhanced validation result with error classification. */\nexport interface ValidationResultEnhanced extends ValidationResult {\n errorType?: string;\n warning?: string;\n /**\n * The failure as the model needs to read it — status, and the field the API\n * named. `error` is what a person sees, and never carries any of that.\n */\n detail?: string;\n}\n\n/** Guide step validation result. */\nexport interface GuideStepValidation {\n valid: boolean;\n steps: Record<string, unknown>[];\n warnings: string[];\n}\n\n// Max user message length (prevent huge payloads)\nconst MAX_MESSAGE_LENGTH = 2000;\n\n// Max tool result size sent to format endpoint (characters)\nconst MAX_TOOL_RESULT_SIZE = 50_000;\n\n// Allowed HTTP methods for tool calls\nconst VALID_METHODS = new Set(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'CLIENT']);\n\n// Patterns that suggest injection attempts in user input\nconst INJECTION_PATTERN =\n /<script|javascript:|on\\w+\\s*=|union\\s+select|drop\\s+table|insert\\s+into|delete\\s+from/i;\n\n// Patterns that look like leaked credentials (should never be in responses)\nconst CREDENTIAL_PATTERN =\n /sk[_-]live[_-]\\w{10,}|pk[_-]live[_-]\\w{10,}|Bearer\\s+\\S{20,}/g;\n\nconst THINKING_BLOCK_PATTERN =\n /<thinking\\b[^>]*>[\\s\\S]*?<\\/thinking>/gi;\n\nconst THINKING_LINE_PATTERN =\n /^\\s*<\\/?thinking\\b[^>]*>\\s*$/gim;\n\n/**\n * The machine payload a guide response carries in its prose.\n *\n * The model answers a walkthrough request with a ```guide fenced block of\n * step JSON. The backend parses that into `guide_steps` and `navigation`\n * — the structured fields the UI actually acts on — but leaves the fence\n * in `answer`, so any client that renders `answer` verbatim shows the\n * visitor a wall of JSON instead of a walkthrough.\n *\n * Stripped here rather than in one renderer because every consumer of the\n * harness has the same problem, and the steps are already available in\n * parsed form by the time anyone reads this text.\n */\nconst GUIDE_FENCE_PATTERN =\n /```(?:guide|guideai-guide)\\s*\\n?[\\s\\S]*?(?:```|$)/gi;\n\n// Bare tag selectors that are too generic for the guide player\nconst GENERIC_SELECTORS = new Set([\n 'a', 'button', 'input', 'select', 'textarea', 'div', 'span',\n 'li', 'p', 'form', 'table', 'img', 'label',\n]);\n\n\nexport class HarnessValidator {\n private registeredTools: CopilotToolDef[] = [];\n\n /** Update the list of registered tools (called after harness init). */\n setRegisteredTools(tools: CopilotToolDef[]): void {\n this.registeredTools = tools;\n }\n\n // ----------------------------------------------------------------\n // Input validation\n // ----------------------------------------------------------------\n\n /**\n * Validate and sanitize user input before sending to the backend.\n *\n * Checks:\n * - Not empty\n * - Within max length\n * - No obvious injection patterns\n */\n validateUserInput(question: string): ValidationResult {\n if (!question || !question.trim()) {\n return { valid: false, error: 'Message cannot be empty.' };\n }\n\n const trimmed = question.trim();\n\n if (trimmed.length > MAX_MESSAGE_LENGTH) {\n return {\n valid: true,\n sanitized: trimmed.slice(0, MAX_MESSAGE_LENGTH),\n };\n }\n\n // Flag but don't block injection attempts — the backend guardrails\n // handle actual blocking. We just log a warning client-side.\n if (INJECTION_PATTERN.test(trimmed)) {\n console.warn('[GuideAI] Potential injection pattern detected in user input.');\n }\n\n return { valid: true, sanitized: trimmed };\n }\n\n // ----------------------------------------------------------------\n // Tool call validation\n // ----------------------------------------------------------------\n\n /**\n * Validate a tool call response from the backend before execution.\n *\n * Checks:\n * - Required fields present (tool, method, path)\n * - Method is a valid HTTP method\n * - Tool exists in the registered tools list\n * - Path looks valid (starts with /)\n */\n validateToolCall(toolCall: ToolCallResponse): ValidationResult {\n if (!toolCall) {\n return { valid: false, error: 'Tool call is missing.' };\n }\n\n if (!toolCall.tool || typeof toolCall.tool !== 'string') {\n return { valid: false, error: 'Tool call missing \"tool\" name.' };\n }\n\n if (!toolCall.method || typeof toolCall.method !== 'string') {\n return { valid: false, error: 'Tool call missing \"method\".' };\n }\n\n const method = toolCall.method.toUpperCase();\n if (!VALID_METHODS.has(method)) {\n return {\n valid: false,\n error: `Invalid tool method \"${toolCall.method}\". Expected one of: ${[...VALID_METHODS].join(', ')}.`,\n };\n }\n\n if (!toolCall.path || typeof toolCall.path !== 'string') {\n return { valid: false, error: 'Tool call missing \"path\".' };\n }\n\n if (!toolCall.path.startsWith('/')) {\n return {\n valid: false,\n error: `Tool path must start with \"/\". Got: \"${toolCall.path}\".`,\n };\n }\n\n // Verify the tool exists in our registered tools\n if (this.registeredTools.length > 0) {\n const found = this.registeredTools.some(\n (t) => t.name === toolCall.tool,\n );\n if (!found) {\n return {\n valid: false,\n error: `Tool \"${toolCall.tool}\" is not registered. Available: ${this.registeredTools.map((t) => t.name).slice(0, 5).join(', ')}...`,\n };\n }\n }\n\n return { valid: true };\n }\n\n // ----------------------------------------------------------------\n // Response validation\n // ----------------------------------------------------------------\n\n /**\n * Validate and sanitize the backend response before showing to user.\n *\n * - Strip leaked credentials from answer text\n * - Validate response structure\n */\n validateResponse(response: unknown): ValidationResult {\n if (!response || typeof response !== 'object') {\n return { valid: false, error: 'Invalid response from server.' };\n }\n\n const resp = response as Record<string, unknown>;\n\n if (typeof resp.answer !== 'string') {\n return { valid: false, error: 'Response missing \"answer\" field.' };\n }\n\n // Strip any leaked credentials from the answer\n const sanitizedAnswer = this.sanitizeAnswer(resp.answer);\n if (sanitizedAnswer !== resp.answer) {\n console.warn('[GuideAI] Sanitized hidden reasoning or leaked credentials from response.');\n }\n\n return { valid: true, sanitized: sanitizedAnswer };\n }\n\n /**\n * Sanitize the answer text — strip leaked credentials, hidden reasoning\n * and machine payload the visitor should never read.\n */\n sanitizeAnswer(answer: string): string {\n return answer\n .replace(THINKING_BLOCK_PATTERN, '')\n .replace(THINKING_LINE_PATTERN, '')\n .replace(GUIDE_FENCE_PATTERN, '')\n .replace(CREDENTIAL_PATTERN, '[REDACTED]')\n .trim();\n }\n\n // ----------------------------------------------------------------\n // Tool result validation\n // ----------------------------------------------------------------\n\n /**\n * Validate a tool execution result before sending to the format endpoint.\n *\n * - Check structure\n * - Truncate oversized results\n * - Strip credential-looking values\n */\n validateToolResult(result: ToolExecutionResult): ValidationResult {\n if (!result) {\n return { valid: false, error: 'Tool result is missing.' };\n }\n\n if (typeof result.success !== 'boolean') {\n return { valid: false, error: 'Tool result missing \"success\" field.' };\n }\n\n // Truncate oversized data before sending to format endpoint\n if (result.data) {\n const serialized = JSON.stringify(result.data);\n if (serialized.length > MAX_TOOL_RESULT_SIZE) {\n result.data = this._truncateData(result.data);\n }\n }\n\n return { valid: true };\n }\n\n /**\n * Enhanced tool result validation — classifies errors and detects empty-success.\n *\n * Returns errorType for contextual UI messages and warning for\n * success-but-empty scenarios.\n */\n validateToolResultEnhanced(result: ToolExecutionResult): ValidationResultEnhanced {\n if (!result) {\n return { valid: false, error: 'Tool result is missing.', errorType: 'unknown' };\n }\n\n if (typeof result.success !== 'boolean') {\n return { valid: false, error: 'Tool result missing \"success\" field.', errorType: 'unknown' };\n }\n\n // Classify failed requests\n if (!result.success) {\n const errorType = this._classifyError(result);\n const friendlyMsg = this._friendlyErrorMessage(errorType);\n const raw = (result.error || '').trim();\n return {\n valid: false,\n error: friendlyMsg,\n errorType,\n detail: raw ? `${friendlyMsg} (${raw})` : friendlyMsg,\n };\n }\n\n // success=true but data is empty/null\n if (this._isEmptyData(result.data)) {\n return {\n valid: true,\n warning: 'That went through, but there was nothing to show for it yet.',\n };\n }\n\n // Truncate oversized data\n if (result.data) {\n const serialized = JSON.stringify(result.data);\n if (serialized.length > MAX_TOOL_RESULT_SIZE) {\n result.data = this._truncateData(result.data);\n }\n }\n\n return { valid: true };\n }\n\n // ----------------------------------------------------------------\n // Formatted response validation\n // ----------------------------------------------------------------\n\n /**\n * Validate that an LLM-formatted answer is substantial and relevant.\n * Returns the answer as-is if OK, or a fallback if not.\n */\n validateFormattedResponse(answer: string, toolResult?: ToolExecutionResult): string {\n if (!answer || answer.trim().length < 15) {\n return 'I retrieved the data but couldn\\'t generate a complete summary. Please try again.';\n }\n\n // Check for LLM refusal patterns when tool data was fetched successfully\n const refusalPatterns = /^(i cannot|i'm unable|i don't have|sorry, i can't)/i;\n if (refusalPatterns.test(answer.trim()) && toolResult?.success && toolResult.data) {\n return 'The data was fetched successfully. Please rephrase your question for a better summary.';\n }\n\n return this.sanitizeAnswer(answer);\n }\n\n // ----------------------------------------------------------------\n // Guide step validation\n // ----------------------------------------------------------------\n\n /**\n * Validate guide steps before rendering the walkthrough button.\n * Ensures steps have minimum required fields and catches generic selectors.\n */\n validateGuideSteps(\n steps: Record<string, unknown>[] | undefined | null,\n ): GuideStepValidation {\n if (!steps || steps.length === 0) {\n return { valid: false, steps: [], warnings: ['No guide steps returned.'] };\n }\n\n const warnings: string[] = [];\n const validSteps: Record<string, unknown>[] = [];\n\n for (let i = 0; i < steps.length; i++) {\n const step = steps[i];\n const title = step.title as string | undefined;\n const body = step.body as string | undefined;\n const selector = step.target_selector as string | undefined;\n const action = step.action as string | undefined;\n\n // Must have title or body\n if (!title && !body) {\n warnings.push(`Step ${i + 1} has no title or body — skipped.`);\n continue;\n }\n\n // Warn about generic selectors\n if (selector && GENERIC_SELECTORS.has(selector.trim())) {\n warnings.push(\n `Step ${i + 1} has a generic selector \"${selector}\" — it may not highlight correctly.`,\n );\n step.target_selector = undefined;\n if (action && action !== 'none') {\n step.action = 'none';\n }\n }\n\n validSteps.push(step);\n }\n\n return {\n valid: validSteps.length > 0,\n steps: validSteps,\n warnings,\n };\n }\n\n // ----------------------------------------------------------------\n // Private helpers\n // ----------------------------------------------------------------\n\n private _classifyError(result: ToolExecutionResult): string {\n const status = result.status;\n const error = (result.error || '').toLowerCase();\n\n if (status === 401 || status === 403) return 'auth';\n if (status === 404) return 'not_found';\n if (status === 429) return 'rate_limit';\n if (status && status >= 500) return 'server';\n // Bad input is the one failure the model can fix by itself, so it gets its\n // own type: the chain repairs a 400/422 instead of dead-ending on it.\n if (status === 400 || status === 422) return 'validation';\n if (error.includes('timed out') || error.includes('timeout')) return 'timeout';\n if (error.includes('connect') || error.includes('network')) return 'connection';\n if (status && status >= 400) return 'client';\n return result.error_type || 'unknown';\n }\n\n /**\n * What a person reads when a request fails.\n *\n * No status codes, no endpoint names, no field paths — those belong to the\n * model (see `detail`), which can act on them. A person can only be told,\n * plainly, that the thing did not work and roughly why.\n */\n private _friendlyErrorMessage(errorType: string): string {\n switch (errorType) {\n case 'auth':\n return 'Your session has expired. Please log in again and retry.';\n case 'not_found':\n return \"I couldn't find that record. It may have been removed.\";\n case 'rate_limit':\n return 'Too many requests right now. Please try again in a moment.';\n case 'timeout':\n return 'That took too long to respond. Please try again.';\n case 'connection':\n return \"I couldn't reach the service. Please check your connection and try again.\";\n case 'server':\n return 'The service ran into a problem. Please try again shortly.';\n case 'validation':\n return \"Some of the details in that request weren't accepted.\";\n default:\n return \"I wasn't able to complete that request.\";\n }\n }\n\n private _isEmptyData(data: unknown): boolean {\n if (data === null || data === undefined) return true;\n if (Array.isArray(data)) return data.length === 0;\n if (typeof data === 'string') return data.trim().length === 0;\n if (typeof data === 'object') {\n const obj = data as Record<string, unknown>;\n const keys = Object.keys(obj).filter((k) => !k.startsWith('_'));\n if (keys.length === 0) return true;\n return keys.every((k) => {\n const v = obj[k];\n return v === null || v === undefined || v === '' ||\n (Array.isArray(v) && v.length === 0) ||\n (typeof v === 'object' && v !== null && Object.keys(v).length === 0);\n });\n }\n return false;\n }\n\n /**\n * Truncate large data payloads to prevent token waste at the format endpoint.\n */\n private _truncateData(data: unknown): unknown {\n if (Array.isArray(data)) {\n return data.slice(0, 25);\n }\n\n if (data && typeof data === 'object') {\n const obj = data as Record<string, unknown>;\n // Look for common list keys and truncate them\n for (const key of ['data', 'results', 'items', 'records', 'rows']) {\n if (Array.isArray(obj[key])) {\n return {\n ...obj,\n [key]: (obj[key] as unknown[]).slice(0, 25),\n _truncated: true,\n };\n }\n }\n }\n\n return data;\n }\n}\n","/**\n * Conversation memory for the copilot harness.\n *\n * Stores chat history in a host-supplied key/value store with a\n * configurable TTL. Web passes localStorage; a mobile runtime passes\n * MMKV; a test passes MemoryKVStore.\n * Scoped by site_id — no cross-site data leaking.\n */\n\nimport type { ConversationMessage } from './copilot-types';\nimport type { KVStore } from '../platform/platform-adapter';\nimport { MemoryKVStore } from '../platform/memory-kv-store';\n\nconst DEFAULT_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours\nconst DEFAULT_MAX_MESSAGES = 50;\n\n/** Keys that should never appear in conversation context sent to the LLM. */\nconst SENSITIVE_KEY_PATTERN =\n /^(password|passwd|secret|token|api[_-]?key|admin[_-]?key|access[_-]?key|private[_-]?key|authorization|ssn|credit[_-]?card|cvv|cvc|pin)$/i;\n\n/** Credential patterns that should be redacted from any text. */\nconst CREDENTIAL_PATTERN =\n /sk[_-]live[_-]\\w{10,}|pk[_-]live[_-]\\w{10,}|Bearer\\s+\\S{20,}/g;\n\ninterface StoredConversation {\n messages: ConversationMessage[];\n updated_at: number;\n}\n\nexport class ConversationMemory {\n private storageKey: string;\n private ttlMs: number;\n private maxMessages: number;\n\n private readonly store: KVStore;\n private listener: ((message: ConversationMessage) => void) | null = null;\n\n /**\n * @param store Where history lives. Defaults to memory so core never\n * reaches for a global; the web SDK passes its localStorage adapter.\n */\n constructor(\n siteId: string,\n ttlMs?: number,\n maxMessages?: number,\n store: KVStore = new MemoryKVStore(),\n ) {\n this.store = store;\n this.storageKey = `guideai_chat_${siteId}`;\n this.ttlMs = ttlMs ?? DEFAULT_TTL_MS;\n this.maxMessages = maxMessages ?? DEFAULT_MAX_MESSAGES;\n }\n\n /** Get all messages, checking TTL. Returns empty array if expired. */\n getMessages(): ConversationMessage[] {\n try {\n const raw = this.store.getItem(this.storageKey);\n if (!raw) return [];\n\n const data: StoredConversation = JSON.parse(raw);\n\n // Check TTL\n if (Date.now() - data.updated_at > this.ttlMs) {\n this.clear();\n return [];\n }\n\n return data.messages || [];\n } catch {\n return [];\n }\n }\n\n /** Add a message and persist to the store. Auto-generates an ID if not provided. */\n addMessage(message: ConversationMessage): void {\n if (!message.id) {\n message.id = `msg_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;\n }\n\n const messages = this.getMessages();\n messages.push(message);\n\n // Trim to max\n const trimmed = messages.slice(-this.maxMessages);\n\n this._save(trimmed);\n\n try {\n this.listener?.(message);\n } catch {\n // A listener never breaks the conversation.\n }\n }\n\n /** Called with every message added from now on; null stops it. */\n onMessage(listener: ((message: ConversationMessage) => void) | null): void {\n this.listener = listener;\n }\n\n /**\n * Get recent messages formatted for LLM context.\n * Includes tool result summaries so the LLM knows what data was\n * previously returned and can make informed follow-up tool calls.\n * Caps total payload to ~4000 chars to stay within token budget.\n */\n getRecentForLLM(count: number = 10): { role: string; content: string }[] {\n const messages = this.getMessages();\n const MAX_MSG_CHARS = 500;\n const MAX_TOOL_RESULT_CHARS = 800;\n const MAX_TOTAL_CHARS = 4000;\n\n const recent = messages.slice(-count).map((msg) => {\n let content = msg.content;\n\n // Truncate long base content\n if (msg.role === 'assistant' && content.length > MAX_MSG_CHARS) {\n content = content.slice(0, MAX_MSG_CHARS) + '...';\n }\n\n // Append tool result summary so LLM knows what data was returned\n if (msg.toolResult?.success && msg.toolResult.data) {\n const summary = this._summarizeToolResult(msg.toolResult.data, MAX_TOOL_RESULT_CHARS);\n if (summary) {\n content += `\\n\\n[Tool result data: ${summary}]`;\n }\n }\n\n // Append tool call records for better LLM context\n if (msg.toolCalls && msg.toolCalls.length > 0) {\n const callSummary = msg.toolCalls\n .map((tc) => {\n // method/path are only known for client-executed calls.\n const route = tc.method && tc.path ? ` ${tc.method} ${tc.path}` : '';\n const detail = tc.summary ? ` - ${tc.summary}` : '';\n return `${tc.tool}${route}: ${tc.success ? 'ok' : 'failed'}${detail}`;\n })\n .join('; ');\n content += `\\n[Tool calls: ${callSummary}]`;\n }\n\n // Strip any leaked credentials before sending to backend\n content = content.replace(CREDENTIAL_PATTERN, '[REDACTED]');\n\n return { role: msg.role, content };\n });\n\n // Trim from oldest if total exceeds budget\n let total = recent.reduce((sum, m) => sum + m.content.length, 0);\n while (recent.length > 2 && total > MAX_TOTAL_CHARS) {\n const removed = recent.shift()!;\n total -= removed.content.length;\n }\n\n return recent;\n }\n\n /**\n * Create a compact summary of tool result data for LLM context.\n * Extracts key fields, array lengths, and available resource names.\n */\n private _summarizeToolResult(data: unknown, maxChars: number): string {\n try {\n // Strip sensitive fields before summarizing\n const cleaned = this._stripSensitiveFields(data);\n const json = JSON.stringify(cleaned);\n\n // If small enough, include as-is\n if (json.length <= maxChars) {\n return json;\n }\n\n // For arrays, always include accurate total count and as many items as fit\n if (Array.isArray(cleaned)) {\n const total = cleaned.length;\n const pickFields = (item: unknown): unknown => {\n if (typeof item === 'object' && item !== null) {\n const obj = item as Record<string, unknown>;\n const picked: Record<string, unknown> = {};\n for (const key of ['name', 'id', 'title', 'path', 'endpoint', 'url', 'type', 'method', 'label', 'slug', 'status']) {\n if (key in obj) picked[key] = obj[key];\n }\n return Object.keys(picked).length > 0 ? picked : Object.keys(obj).slice(0, 4).reduce((acc, k) => ({ ...acc, [k]: obj[k] }), {});\n }\n return item;\n };\n\n // Try fitting as many items as possible within budget\n const compactItems = cleaned.map(pickFields);\n // Start with all, shrink if too large\n let itemsToShow = compactItems;\n let summary = JSON.stringify({ total_count: total, items: itemsToShow });\n if (summary.length > maxChars) {\n // Binary search for how many items fit\n let lo = 1, hi = compactItems.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n const test = JSON.stringify({ total_count: total, items_shown: mid, items: compactItems.slice(0, mid) });\n if (test.length <= maxChars) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n itemsToShow = compactItems.slice(0, lo);\n summary = JSON.stringify({ total_count: total, items_shown: lo, items: itemsToShow });\n }\n return summary.length <= maxChars ? summary : summary.slice(0, maxChars) + '...';\n }\n\n // For objects, include top-level keys and truncate\n if (typeof cleaned === 'object' && cleaned !== null) {\n const keys = Object.keys(cleaned as Record<string, unknown>);\n const summary = JSON.stringify({ keys, preview: json.slice(0, maxChars - 100) });\n return summary.slice(0, maxChars);\n }\n\n return json.slice(0, maxChars);\n } catch {\n return '';\n }\n }\n\n /** Recursively strip fields with sensitive-looking keys (defense-in-depth). */\n private _stripSensitiveFields(data: unknown, depth: number = 0): unknown {\n if (depth > 5) return data;\n\n if (Array.isArray(data)) {\n return data.slice(0, 50).map((item) => this._stripSensitiveFields(item, depth + 1));\n }\n\n if (typeof data === 'object' && data !== null) {\n const result: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(data as Record<string, unknown>)) {\n result[key] = SENSITIVE_KEY_PATTERN.test(key)\n ? '[REDACTED]'\n : this._stripSensitiveFields(value, depth + 1);\n }\n return result;\n }\n\n return data;\n }\n\n /** Clear all conversation history. */\n clear(): void {\n try {\n this.store.removeItem(this.storageKey);\n } catch {\n // the store may be unavailable\n }\n }\n\n /** Check if there is any conversation history. */\n hasHistory(): boolean {\n return this.getMessages().length > 0;\n }\n\n private _save(messages: ConversationMessage[]): void {\n try {\n const data: StoredConversation = {\n messages,\n updated_at: Date.now(),\n };\n this.store.setItem(this.storageKey, JSON.stringify(data));\n } catch {\n // the store may be full or unavailable\n }\n }\n}\n","/**\n * Client-side tool executor for the copilot harness.\n *\n * Reads the user's auth token from a host-supplied CredentialSource\n * (localStorage, sessionStorage, or cookie on the web) and calls the\n * customer's API endpoints directly from the client.\n */\n\nimport type { CopilotAuthConfig, ToolCallResponse, ToolExecutionResult } from './copilot-types';\nimport type { CredentialSource } from '../platform/platform-adapter';\nimport type { HttpTransport } from '../transport/http-transport';\n\n/**\n * Encode one query key or value the way `URLSearchParams` does.\n *\n * Differs from bare `encodeURIComponent` in two ways that reach the wire:\n * space becomes `+`, and `!'()~` are escaped. Matching it exactly keeps\n * the request byte-identical to what customer APIs receive today.\n */\nfunction encodeParam(value: string): string {\n return encodeURIComponent(value)\n .replace(/%20/g, '+')\n .replace(/[!'()~]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);\n}\n\n/** Caps on how much of a failed response body is quoted back to the model. */\nconst MAX_ERROR_DETAIL_CHARS = 400;\nconst MAX_ERROR_DETAIL_ITEMS = 5;\n\nexport class ToolExecutor {\n private authConfig: CopilotAuthConfig;\n private baseUrl: string;\n\n private readonly credentials: CredentialSource;\n\n /**\n * @param credentials Where configured auth sources are read from.\n * Defaults to the browser's; a mobile host supplies its own, and\n * reports null for sources it does not have (a cookie jar, say)\n * rather than inventing a value.\n */\n private readonly transport: HttpTransport;\n\n /**\n * @param transport How this platform reaches the customer's API.\n * Defaults to the browser's, so existing callers are unaffected.\n */\n constructor(\n authConfig: CopilotAuthConfig,\n baseUrl: string,\n credentials: CredentialSource,\n transport: HttpTransport,\n ) {\n this.credentials = credentials;\n this.transport = transport;\n this.authConfig = authConfig;\n this.baseUrl = baseUrl.replace(/\\/+$/, '');\n }\n\n /** Read the user's auth token from the configured browser storage. */\n getAuthToken(): string | null {\n // An unauthenticated toolset has no token to find, and looking for one\n // would defeat the point of the setting.\n if (this.authConfig.type === 'none') return null;\n\n try {\n // 0. A token the host supplied directly (a native app's own login).\n const supplied = this.authConfig.type === 'bearer' ? this.credentials.authToken?.() : null;\n if (supplied) return supplied;\n\n let raw: string | null = null;\n\n // 1. Try the configured source + key first\n const storageKey = this.authConfig.key;\n if (storageKey && this.authConfig.source) {\n raw = this.credentials.read(this.authConfig.source, storageKey);\n }\n\n if (raw) {\n const token = this._extractToken(raw);\n if (token) return token;\n }\n\n // 2. Fallback: scan localStorage for any JWT token\n // Works even when the configured key is wrong or source is not set\n //\n // SECURITY (disabled — do not re-enable without review):\n // This blind scan walks ALL of localStorage and returns the first\n // JWT-shaped value it finds. That can grab an unrelated bearer token\n // the customer never intended the copilot to use, which is then\n // (optionally) forwarded to GuideAI's backend. The copilot must only\n // ever use the *explicitly configured* auth source/key. Kept here,\n // commented out, so the fallback behaviour is documented and can be\n // reasoned about if a configured-key path ever regresses.\n // return this._scanStorageForJwt();\n return null;\n } catch {\n return null;\n }\n }\n\n /**\n * Extract the actual JWT from a raw storage value.\n *\n * Handles all common storage formats automatically:\n * - Plain JWT string: \"eyJhbGciOi...\"\n * - JSON with known field: {\"access_token\": \"eyJ...\"} or {\"token\": \"eyJ...\"}\n * - JSON with any string field that looks like a JWT\n *\n * If token_field is configured, it takes priority and extracts that specific field.\n */\n private _extractToken(raw: string): string | null {\n // If it looks like a JWT already (starts with eyJ), return directly\n if (raw.startsWith('eyJ')) return raw;\n\n // Try parsing as JSON\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n // If token_field is configured, allow a dotted path into nested JSON.\n if (this.authConfig.token_field) {\n const value = this._readObjectPath(parsed, this.authConfig.token_field);\n return typeof value === 'string' ? value : null;\n }\n\n // Auto-detect: check common field names first\n const commonFields = ['access_token', 'token', 'jwt', 'id_token', 'auth_token', 'accessToken', 'idToken', 'authToken'];\n for (const field of commonFields) {\n if (typeof parsed[field] === 'string' && parsed[field]) {\n return parsed[field];\n }\n }\n\n // Fallback: find any string value that looks like a JWT\n for (const value of Object.values(parsed)) {\n if (typeof value === 'string' && value.startsWith('eyJ')) {\n return value;\n }\n }\n }\n\n // Parsed as a plain JSON string (e.g. `\"eyJ...\"` with quotes in storage)\n if (typeof parsed === 'string') return parsed;\n } catch {\n // Not valid JSON — use raw value as-is\n }\n\n return raw;\n }\n\n /** Execute an API tool call client-side using the user's auth. */\n async execute(toolCall: ToolCallResponse): Promise<ToolExecutionResult> {\n try {\n const method = toolCall.method.toUpperCase();\n\n // Last line of defence against a dependent call that slipped into a\n // concurrent batch: the id it needed never arrived, so the template\n // segment is still in the path. Firing this hits a nonsense URL (or,\n // worse, a real one built from the literal text), so fail it here and\n // let the model reissue the call once it has the value.\n const unresolved = [\n ...this._unresolvedPathParams(toolCall),\n ...this._unresolvedHeaders(toolCall),\n ];\n if (unresolved.length > 0) {\n return {\n success: false,\n error: `Missing ${unresolved.join(', ')} — this needs data from another request first.`,\n };\n }\n\n const url = this._buildUrl(toolCall);\n const headers = this._buildHeaders(toolCall);\n\n // Body for non-GET requests.\n let body: string | undefined;\n if (toolCall.body && method !== 'GET') {\n body = JSON.stringify(toolCall.body);\n } else if (toolCall.params && method !== 'GET') {\n body = JSON.stringify(toolCall.params);\n }\n\n const response = await this.transport.request({\n url,\n // The tool's own verb, not a coerced one: rewriting a DELETE as a\n // POST would silently change what the customer's API is asked to do.\n method: method as 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD',\n headers,\n body,\n });\n\n let data: unknown;\n const contentType = response.headers.get('content-type') || '';\n if (contentType.includes('application/json')) {\n data = await response.json();\n } else {\n data = await response.text();\n }\n\n // `ok` is derived rather than read off the response: the transport\n // contract deliberately omits it so each platform cannot disagree\n // about what counts as success. 2xx, matching the spec.\n const ok = response.status >= 200 && response.status < 300;\n\n return {\n success: ok,\n data,\n status: response.status,\n error: ok\n ? undefined\n : response.status === 401 || response.status === 403\n ? 'Your session has expired. Please refresh the page or log in again.'\n // No `statusText`: HTTP/2 and /3 do not send a reason phrase, so\n // it is empty for any modern backend. The status code carries the\n // diagnosis; the body's own error detail is appended when present.\n : this._describeHttpError(response.status, data),\n };\n } catch (err) {\n return {\n success: false,\n error: err instanceof Error ? err.message : 'Unknown error executing tool call',\n };\n }\n }\n\n /**\n * Turn a failed response into an error the model can act on.\n *\n * \"HTTP 422: Unprocessable Entity\" tells nobody which field was wrong, so a\n * rejected write used to end the chain with a status line the user could do\n * nothing with. The body almost always names the problem — FastAPI puts a\n * loc/msg list in `detail`, most other APIs use `message`, `error` or an\n * `errors` array — so surface that alongside the status.\n */\n private _describeHttpError(status: number, data: unknown): string {\n const base = `HTTP ${status}`;\n const detail = this._extractErrorDetail(data);\n return detail ? `${base} — ${detail}` : base;\n }\n\n private _extractErrorDetail(data: unknown): string | null {\n if (typeof data === 'string') {\n const text = data.trim();\n return text ? text.slice(0, MAX_ERROR_DETAIL_CHARS) : null;\n }\n if (!data || typeof data !== 'object') return null;\n\n const body = data as Record<string, unknown>;\n const parts: string[] = [];\n\n for (const key of ['detail', 'message', 'error', 'errors', 'error_description']) {\n const value = body[key];\n if (value === undefined || value === null) continue;\n\n if (typeof value === 'string') {\n if (value.trim()) parts.push(value.trim());\n } else if (Array.isArray(value)) {\n for (const entry of value.slice(0, MAX_ERROR_DETAIL_ITEMS)) {\n const described = this._describeFieldError(entry);\n if (described) parts.push(described);\n }\n } else if (typeof value === 'object') {\n // { errors: { field: [\"is required\"] } }\n for (const [field, msg] of Object.entries(value as Record<string, unknown>)\n .slice(0, MAX_ERROR_DETAIL_ITEMS)) {\n const text = Array.isArray(msg) ? msg.join(', ') : String(msg);\n parts.push(`${field}: ${text}`);\n }\n }\n if (parts.length > 0) break;\n }\n\n if (parts.length === 0) return null;\n return parts.join('; ').slice(0, MAX_ERROR_DETAIL_CHARS);\n }\n\n /** One entry of a validation-error list, e.g. FastAPI's {loc, msg, type}. */\n private _describeFieldError(entry: unknown): string | null {\n if (typeof entry === 'string') return entry.trim() || null;\n if (!entry || typeof entry !== 'object') return null;\n\n const item = entry as Record<string, unknown>;\n const message = item.msg ?? item.message ?? item.error ?? item.detail;\n const location = Array.isArray(item.loc)\n ? item.loc.filter((part) => part !== 'body').join('.')\n : (item.field ?? item.param ?? item.name);\n\n if (!message) return location ? `${location}: invalid` : null;\n return location ? `${location}: ${message}` : String(message);\n }\n\n /**\n * Execute several independent tool calls concurrently.\n *\n * The model batches only calls that do not depend on each other's results,\n * so a batch of four costs roughly one call's wall-clock time instead of\n * four sequential round trips. Results come back in request order.\n *\n * A rejected promise is impossible here — `execute` already converts thrown\n * errors into a failed ToolExecutionResult — but the settled form is used\n * anyway so one exception can never discard the whole batch.\n */\n async executeBatch(\n toolCalls: ToolCallResponse[],\n onEach?: (toolCall: ToolCallResponse, result: ToolExecutionResult) => void,\n ): Promise<ToolExecutionResult[]> {\n const settled = await Promise.allSettled(\n toolCalls.map(async (toolCall) => {\n const result = await this.execute(toolCall);\n onEach?.(toolCall, result);\n return result;\n }),\n );\n\n return settled.map((outcome, i) => {\n if (outcome.status === 'fulfilled') return outcome.value;\n const result: ToolExecutionResult = {\n success: false,\n error: outcome.reason instanceof Error\n ? outcome.reason.message\n : 'Unknown error executing tool call',\n };\n onEach?.(toolCalls[i], result);\n return result;\n });\n }\n\n private _buildHeaders(toolCall: ToolCallResponse): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n };\n\n const blocked = new Set([\n 'authorization',\n 'cookie',\n 'host',\n 'origin',\n 'referer',\n 'x-forwarded-for',\n 'x-real-ip',\n 'content-length',\n 'transfer-encoding',\n 'connection',\n ]);\n\n // Header names are case-insensitive on the wire, but a plain object is\n // not: \"X-Site-Id\" from the toolset config and \"x-site-id\" from the model\n // are two keys, and `fetch` sends the pair as one comma-joined value.\n // A scoping header that arrives as \"<id>, <id>\" does not fail — it fails\n // to parse server-side and the request is answered for the default\n // record, which is a wrong answer nothing surfaces. So merge by the\n // lowercased name and let the last writer win.\n const canonical = new Map<string, string>();\n\n const mergeHeader = (name: string, value: string | null | undefined) => {\n const lower = name.toLowerCase();\n if (!value || blocked.has(lower)) return;\n const existing = canonical.get(lower);\n if (existing !== undefined && existing !== name) delete headers[existing];\n canonical.set(lower, name);\n headers[name] = value;\n };\n\n for (const name of Object.keys(headers)) canonical.set(name.toLowerCase(), name);\n\n if (this.authConfig.type === 'none') {\n // Deliberately unauthenticated — only the custom headers below apply.\n } else if (this.authConfig.type === 'bearer') {\n const token = this.getAuthToken();\n if (!token) {\n throw new Error('No auth token found in browser storage. The user may not be logged in.');\n }\n headers.Authorization = `Bearer ${token}`;\n } else if (this.authConfig.type === 'api_key' && this.authConfig.key && this.authConfig.header) {\n mergeHeader(this.authConfig.header, this.authConfig.key);\n }\n\n if (this.authConfig.headers && typeof this.authConfig.headers === 'object') {\n for (const [name, rawValue] of Object.entries(this.authConfig.headers)) {\n mergeHeader(name, this._resolveHeaderValue(rawValue));\n }\n }\n\n if (toolCall.headers && typeof toolCall.headers === 'object') {\n for (const [name, value] of Object.entries(toolCall.headers)) {\n mergeHeader(name, value);\n }\n }\n\n // Declared in Copilot Tools as supplied by the app; last, so a value the\n // model made up for the same header never replaces the app's own.\n for (const declared of this.authConfig.app_headers ?? []) {\n if (!declared?.name) continue;\n let value: string | null = null;\n try {\n value = this.credentials.read('app', declared.name);\n } catch {\n value = null;\n }\n mergeHeader(declared.name, value);\n }\n\n return headers;\n }\n\n private _resolveHeaderValue(\n value: string | {\n source: 'localStorage' | 'sessionStorage' | 'cookie';\n key: string;\n token_field?: string;\n },\n ): string | null {\n if (typeof value === 'string') return value;\n\n let raw: string | null = null;\n try {\n raw = this.credentials.read(value.source, value.key);\n } catch {\n return null;\n }\n\n if (!raw) return null;\n\n if (value.token_field) {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n const selected = this._readObjectPath(parsed, value.token_field);\n return typeof selected === 'string' ? selected : null;\n }\n } catch {\n return raw;\n }\n }\n\n return this._extractToken(raw) || raw;\n }\n\n /**\n * Path template segments that `_buildUrl` would leave unsubstituted.\n *\n * A value that is itself a placeholder (\"{id}\", \"<site_id>\", \"TBD\") counts\n * as unresolved too — substituting it would produce a URL that looks valid\n * but addresses nothing.\n */\n private _unresolvedPathParams(toolCall: ToolCallResponse): string[] {\n const segments = (toolCall.path || '').match(/\\{(\\w+)\\}/g) || [];\n if (segments.length === 0) return [];\n\n const params = toolCall.params || {};\n const placeholder = /^\\s*(?:[{<[].*[}>\\]]|[$:].+|tbd|todo|placeholder|unknown|none|null|n\\/a)\\s*$/i;\n\n return segments\n .map((segment) => segment.slice(1, -1))\n .filter((name) => {\n const value = params[name];\n if (value === undefined || value === null || value === '') return true;\n return typeof value === 'string' && placeholder.test(value);\n });\n }\n\n /**\n * Headers whose value is still a placeholder the model never filled in.\n *\n * Worse than an unresolved path segment: a scoping header like X-Site-Id\n * sent as \"{site_id}\" does not 404, it answers about whatever the default\n * record is. Failing the call here is what makes the mistake visible.\n */\n private _unresolvedHeaders(toolCall: ToolCallResponse): string[] {\n const headers = toolCall.headers;\n if (!headers) return [];\n\n const placeholder = /^\\s*(?:[{<[].*[}>\\]]|[$:].+|tbd|todo|placeholder|unknown|none|null|n\\/a)\\s*$/i;\n return Object.entries(headers)\n .filter(([, value]) => typeof value === 'string' && placeholder.test(value))\n .map(([name]) => name);\n }\n\n private _buildUrl(toolCall: ToolCallResponse): string {\n let path = toolCall.path;\n const method = toolCall.method.toUpperCase();\n\n // Replace path parameters if params contain them\n if (toolCall.params) {\n for (const [key, value] of Object.entries(toolCall.params)) {\n const placeholder = `{${key}}`;\n if (path.includes(placeholder)) {\n path = path.replace(placeholder, encodeURIComponent(String(value)));\n }\n }\n }\n\n // Built by hand rather than with `new URL`. Hermes has no `URL`, and\n // React Native's polyfill has historically ignored the base argument\n // and not propagated `searchParams` mutations back to the parent. A\n // dropped query string would not throw — the request would return 200\n // with unfiltered rows and the model would answer from wrong data.\n let url: string;\n if (/^[a-z][a-z0-9+.-]*:\\/\\//i.test(path)) {\n // An absolute path replaces the base outright, as `new URL` does.\n url = path;\n } else {\n // A leading slash is root-relative, so any path on the base is\n // discarded — matching `new URL` rather than \"fixing\" it, because\n // customer toolsets are configured against today's behaviour.\n const origin = this._origin(this.baseUrl);\n url = path.startsWith('/') ? `${origin}${path}` : `${this.baseUrl}/${path}`;\n }\n\n // Add query params for GET requests\n if (toolCall.params && method === 'GET') {\n const pairs: string[] = [];\n for (const [key, value] of Object.entries(toolCall.params)) {\n // Skip path params already substituted\n if (!toolCall.path.includes(`{${key}}`)) {\n pairs.push(`${encodeParam(key)}=${encodeParam(String(value))}`);\n }\n }\n if (pairs.length > 0) {\n url += (url.includes('?') ? '&' : '?') + pairs.join('&');\n }\n }\n\n return url;\n }\n\n /** Scheme + authority of a base URL, without its path. */\n private _origin(base: string): string {\n const match = /^([a-z][a-z0-9+.-]*:\\/\\/[^/]+)/i.exec(base);\n return match ? match[1] : base;\n }\n\n\n private _readObjectPath(source: Record<string, unknown>, path: string): unknown {\n const segments = path.split('.').map((segment) => segment.trim()).filter(Boolean);\n let current: unknown = source;\n\n for (const segment of segments) {\n if (!current || typeof current !== 'object' || Array.isArray(current)) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[segment];\n }\n\n return current;\n }\n}\n","// ============================================================\n// @guideai/core — Structured transport errors\n// ============================================================\n//\n// Thrown by whatever reaches the network, caught by the agent loop and\n// the chat layer via `instanceof`. They carry a status and plain detail\n// objects — no platform types — so the same class works everywhere.\n//\n// They live in core because the loop catches them: if the SDK and core\n// each declared their own, `instanceof` would silently return false and\n// plan-limit errors would degrade to a generic failure message.\n// ============================================================\n\n/** Structured error for authentication failures (HTTP 401/403). */\nexport class AuthError extends Error {\n readonly status: number;\n\n constructor(status: number) {\n super('Your session has expired. Please refresh the page or log in again.');\n this.name = 'AuthError';\n this.status = status;\n }\n}\n\n/** Structured error for usage limit responses (HTTP 429). */\nexport class UsageLimitError extends Error {\n readonly status: number;\n readonly limitType: string;\n readonly plan: string;\n readonly current: number;\n readonly limit: number;\n readonly upgradeHint: string;\n\n constructor(detail: {\n message: string;\n limit_type?: string;\n plan?: string;\n current?: number;\n limit?: number;\n upgrade_hint?: string;\n }) {\n super(detail.message);\n this.name = 'UsageLimitError';\n this.status = 429;\n this.limitType = detail.limit_type || 'unknown';\n this.plan = detail.plan || 'unknown';\n this.current = detail.current || 0;\n this.limit = detail.limit || 0;\n this.upgradeHint = detail.upgrade_hint || '';\n }\n}\n\nexport class PlanAccessError extends Error {\n readonly status = 402;\n readonly feature: string;\n readonly plan: string;\n readonly upgradeHint: string;\n\n constructor(detail: {\n message?: string;\n feature?: string;\n plan?: string;\n upgrade_hint?: string;\n }) {\n super(detail.message || 'This feature is not available on the current plan.');\n this.name = 'PlanAccessError';\n this.feature = detail.feature || 'unknown';\n this.plan = detail.plan || 'unknown';\n this.upgradeHint = detail.upgrade_hint || '';\n }\n}\n\n/**\n * Thrown when a body was meant to be encrypted and could not be.\n *\n * Never downgrade to plaintext here: the caller configured encryption,\n * and a request that quietly stops honouring it is worse than one that\n * fails.\n */\nexport class EncryptionError extends Error {\n constructor(path: string) {\n super(`[3Guide] Refusing to send ${path} unencrypted: encryption failed.`);\n this.name = 'EncryptionError';\n }\n}\n\n/** Thrown when a request exceeds its time budget. */\nexport class TimeoutError extends Error {\n readonly timeoutMs: number;\n\n constructor(path: string, timeoutMs: number) {\n super(`[3Guide] ${path} timed out after ${timeoutMs}ms`);\n this.name = 'TimeoutError';\n this.timeoutMs = timeoutMs;\n }\n}\n","// ============================================================\n// @guideai/core — Endpoint Path Builder\n// ============================================================\n//\n// Constructs API paths from segments at runtime instead of\n// storing them as readable string literals. Combined with\n// the obfuscator's string encryption, paths become very\n// hard to discover via static analysis of the bundle.\n// ============================================================\n\nconst _j = (parts: string[]) => parts.join('/');\nconst _v = ['', 'api', 'v1'];\n\nexport const EP = {\n feedback: () => _j([..._v, 'ml', 'feedback']),\n weights: () => _j([..._v, 'ml', 'weights']),\n modelInfo: () => _j([..._v, 'ml', 'model-info']),\n guides: () => _j([..._v, 'guides', 'published']),\n guidesRecord: () => _j([..._v, 'guides', 'record']),\n events: () => _j([..._v, 'events']),\n chatAsk: () => _j([..._v, 'chat', 'ask']),\n chatExecuteTool: () => _j([..._v, 'chat', 'execute-tool']),\n onboardingCurrent: (userId: string) => `${_j([..._v, 'onboarding', 'current'])}?user_id=${encodeURIComponent(userId)}`,\n onboardingObserve: () => _j([..._v, 'onboarding', 'observe']),\n onboardingDismiss: () => _j([..._v, 'onboarding', 'dismiss']),\n onboardingEnroll: () => _j([..._v, 'onboarding', 'enroll']),\n onboardingSkip: () => _j([..._v, 'onboarding', 'skip']),\n onboardingComplete: () => _j([..._v, 'onboarding', 'complete']),\n onboardingWatched: () => _j([..._v, 'onboarding', 'watched']),\n demoSearch: () => _j([..._v, 'demos', 'search']),\n chatRoute: () => _j([..._v, 'chat', 'route']),\n chatOpener: () => _j([..._v, 'chat', 'opener']),\n chatOpenerSpeech: () => _j([..._v, 'chat', 'opener', 'speech']),\n chatIntro: () => _j([..._v, 'chat', 'intro']),\n chatEscalate: () => _j([..._v, 'chat', 'escalate']),\n chatEscalationConfig: () => _j([..._v, 'chat', 'escalate', 'config']),\n chatConversationWebhook: () => _j([..._v, 'chat', 'conversation-webhook']),\n chatFeedback: () => _j([..._v, 'chat', 'feedback']),\n coachScreen: () => _j([..._v, 'chat', 'coach', 'screen']),\n coachAsk: () => _j([..._v, 'chat', 'coach', 'ask']),\n coachExplain: () => _j([..._v, 'chat', 'coach', 'explain']),\n coachReplan: () => _j([..._v, 'chat', 'coach', 'replan']),\n coachConfirm: () => _j([..._v, 'chat', 'coach', 'confirm']),\n coachSpeech: () => _j([..._v, 'chat', 'coach', 'speech']),\n paymentCheckout: () => _j([..._v, 'connectors', 'payments', 'checkout']),\n chatContinue: () => _j([..._v, 'chat', 'continue-tool-chain']),\n /** A Do it run: plan a screen, act, look again (the agent service). */\n doItRuns: () => _j([..._v, 'guide-executions']),\n doItRun: (runId: string, op?: 'advance' | 'observe' | 'respond' | 'cancel') =>\n _j([..._v, 'guide-executions', encodeURIComponent(runId), ...(op ? [op] : [])]),\n chatFormat: () => _j([..._v, 'chat', 'format-tool-result']),\n copilotTools: () => _j([..._v, 'copilot-tools', 'active']),\n copilotStream: () => _j([..._v, 'copilot', 'stream']),\n resolveScore: () => _j([..._v, 'resolve', 'score']),\n resolveLocate: () => _j([..._v, 'resolve', 'locate']),\n resolveRoute: () => _j([..._v, 'resolve', 'route']),\n resolveGraph: () => _j([..._v, 'resolve', 'graph']),\n visitors: () => _j([..._v, 'visitors', 'register']),\n announcements: () => _j([..._v, 'announcements']),\n pageScan: () => _j([..._v, 'page-scan', 'scan']),\n pageScanReveal: () => _j([..._v, 'page-scan', 'reveal']),\n supportStatus: () => _j([..._v, 'support', 'public', 'status']),\n supportConvs: () => _j([..._v, 'support', 'public', 'conversations']),\n supportMsgs: (conversationId: string) =>\n _j([..._v, 'support', 'public', 'conversations', conversationId, 'messages']),\n realtimeSession: () => _j([..._v, 'realtime', 'session']),\n realtimeTranscriptionSession: () => _j([..._v, 'realtime', 'transcription', 'session']),\n realtimeSpeech: () => _j([..._v, 'realtime', 'speech']),\n listenSession: () => _j([..._v, 'realtime', 'listen', 'session']),\n speakSession: () => _j([..._v, 'realtime', 'speak', 'session']),\n liveSession: () => _j([..._v, 'realtime', 'live', 'session']),\n // v=2: neutral voice codes (stream / pipeline / live / live-rtc).\n voiceCapabilities: () => `${_j([..._v, 'realtime', 'capabilities'])}?v=2`,\n liveVoiceMinute: () => _j([..._v, 'realtime', 'live', 'minute']),\n batchVoiceTranscribe: () => _j([..._v, 'realtime', 'batch', 'transcribe']),\n documentFormSuggestions: () => _j([..._v, 'realtime', 'document', 'form-suggestions']),\n} as const;\n","/**\n * Copilot Agent Harness — client-side orchestrator.\n *\n * Architecture:\n * - Backend plans the next step and formats final answers\n * - Backend streams progress via SSE events during the loop\n * - SDK can execute safe read tools in the browser and continue the chain\n * - SDK reads the stream, shows status updates, and displays the final answer\n * - For confirmed tool calls, SDK executes the customer API request in the browser\n *\n * Memory persists through an injected KVStore with TTL (localStorage on\n * the web, MMKV on mobile). Multi-tenant safe via site_id scoping.\n */\n\nimport { AuthError, PlanAccessError, UsageLimitError } from '../core/api-errors';\nimport type { CopilotApi } from '../core/api-contract';\nimport { describeToolCall, describeToolBatch } from './tool-labels';\nimport type { PageSnapshot } from '../chat/page-snapshot-types';\nimport { EP } from '../core/endpoints';\nimport { ConversationMemory } from './conversation-memory';\nimport { HarnessValidator } from './harness-validator';\nimport { ToolExecutor } from './tool-executor';\nimport type { ClientToolRegistry } from './client-tool-registry';\nimport type { HttpResponse } from '../transport/http-transport';\nimport type { CredentialSource, KVStore } from '../platform/platform-adapter';\nimport type { HttpTransport } from '../transport/http-transport';\nimport type {\n AgentSSEEvent,\n AgentStatusCallback,\n AssistantPlan,\n CopilotActiveTools,\n CopilotConfig,\n ChatAskResponseWithCopilot,\n ConversationMessage,\n FormatToolResultResponse,\n HarnessResponse,\n PlanCallback,\n TextDeltaCallback,\n ToolChainStep,\n ToolCallEventCallback,\n ToolCallRecord,\n ToolCallResponse,\n ToolExecutionResult,\n} from './copilot-types';\n\n/** Maximum rounds of tool calling in one chain (each round = one model call). */\nconst MAX_CHAIN_ROUNDS = 5;\n/** Maximum tools a single question may run in total, across all rounds. */\nconst MAX_TOTAL_TOOL_CALLS = 10;\n/**\n * How many times a chain may hand a wholly-failed round back to the model to\n * fix. One is enough: a call rejected for bad input is usually repairable from\n * the validation message, and a second failure means the model is guessing.\n */\nconst MAX_REPAIR_ROUNDS = 1;\n/**\n * Failures the model can plausibly correct by reissuing the call. Anything\n * else — auth, rate limit, a server fault — will fail again identically, so\n * retrying it only spends the user's time.\n */\nconst REPAIRABLE_ERROR_TYPES = new Set(['validation', 'client', 'not_found']);\n\n/** A call only GuideAI's servers can make (an MCP tool). */\nfunction isServerTool(toolCall: ToolCallResponse): boolean {\n return (toolCall.method || '').toUpperCase() === 'MCP';\n}\n\nexport class CopilotHarness {\n private api: CopilotApi;\n private config: CopilotConfig;\n private memory: ConversationMemory;\n private validator: HarnessValidator;\n private toolExecutor: ToolExecutor | null = null;\n private tools: ClientToolRegistry;\n private readonly credentials: CredentialSource;\n private readonly transport: HttpTransport;\n private activeTools: CopilotActiveTools | null = null;\n private initialized = false;\n /** Told about the assistant's plan as soon as the server states one. */\n private onPlan?: PlanCallback;\n private lastPlanFingerprint: string | null = null;\n /** The plan in force for the message being answered. */\n private currentPlan?: AssistantPlan;\n /** Tool inputs the host page supplies — whatever the user has open. */\n private toolContext?: Record<string, string>;\n\n /**\n * @param tools How this platform actually touches the screen. Defaults to\n * the browser registry so every existing caller is unaffected; a mobile\n * runtime passes one that dispatches native effects instead. The loop\n * below never learns which it is holding.\n */\n constructor(\n api: CopilotApi,\n config: CopilotConfig,\n tools: ClientToolRegistry,\n storage: KVStore,\n transport: HttpTransport,\n credentials: CredentialSource,\n ) {\n this.api = api;\n this.config = config;\n this.tools = tools;\n this.transport = transport;\n this.credentials = credentials;\n // Core defaults to in-memory storage so it never touches a global;\n // the web SDK hands it the browser's.\n this.memory = new ConversationMemory(\n config.siteId,\n config.memoryTtlMs,\n config.maxMemoryMessages,\n storage,\n );\n this.validator = new HarnessValidator();\n this.validator.setRegisteredTools(this.tools.copilotDefinitions());\n }\n\n /** Hear every turn the conversation records (typed, spoken, replies). */\n onMessageRecorded(listener: ((message: ConversationMessage) => void) | null): void {\n this.memory.onMessage(listener);\n }\n\n /** Initialize the harness — fetch active tools for the site. */\n async initialize(): Promise<boolean> {\n if (this.initialized) return this.isEnabled();\n\n try {\n const response = await this.api.get<CopilotActiveTools | null>(\n EP.copilotTools(),\n );\n\n if (response && response.tools && response.tools.length > 0) {\n this.activeTools = response;\n this.validator.setRegisteredTools([\n ...response.tools,\n ...this.tools.copilotDefinitions(),\n ]);\n // Built here, not injected: the auth config and base URL only\n // exist once the server has told us which toolset is active.\n this.toolExecutor = new ToolExecutor(\n response.auth_config,\n response.base_url,\n this.credentials,\n this.transport,\n );\n }\n } catch {\n // Customer API tools are optional; SDK-owned browser tools stay available.\n }\n\n this.initialized = true;\n return this.isEnabled();\n }\n\n /**\n * Register the listener that renders the assistant's plan.\n *\n * Set on the harness rather than passed per message: `processMessage` is\n * already called positionally from several places, and a plan belongs to\n * the UI for the whole session, not to one call.\n */\n setPlanCallback(callback: PlanCallback | undefined): void {\n this.onPlan = callback;\n }\n\n /**\n * Values the host page supplies for tool inputs.\n *\n * Sent with every turn, including chain rounds — a later round builds calls\n * too, and dropping it there is how a chain that started on the right\n * record finishes on the default one.\n */\n /** Bind to the SDK's analytics session. Carries no message content. */\n setAnalyticsSessionId(sessionId: string): void {\n this.config.analyticsSessionId = sessionId;\n }\n\n /** Read the analytics bundle's ids on every question. Ids only. */\n setAnalyticsIds(read: () => { session_id?: string; distinct_id?: string }): void {\n this.config.analyticsIds = read;\n }\n\n setToolContext(values: Record<string, string> | undefined): void {\n this.toolContext = values && Object.keys(values).length > 0 ? values : undefined;\n }\n\n /**\n * The app's tool inputs as the API names them. A value declared in Copilot\n * Tools as filling other inputs (app key `course` fills `content_id`) is\n * sent under those names too; one the app set by that name already wins.\n */\n private contextForTools(): Record<string, string> | undefined {\n if (!this.toolContext) return undefined;\n const out = { ...this.toolContext };\n for (const input of this.activeTools?.auth_config?.app_inputs ?? []) {\n const value = input?.key ? this.toolContext[input.key] : undefined;\n if (value === undefined) continue;\n for (const name of input.fills ?? []) {\n if (name && !(name in out)) out[name] = value;\n }\n }\n return out;\n }\n\n /** Whether copilot action tools are available for this site. */\n isEnabled(): boolean {\n return this.tools.definitions().length > 0\n || (this.activeTools !== null && this.activeTools.tools.length > 0);\n }\n\n /**\n * Process a user message through the harness.\n *\n * 1. Sends question + history + page context to backend /ask\n * 2. If backend returns SSE stream (agent loop running), reads events in real-time\n * 3. Calls onStatus callback with progress updates during the loop\n * 4. Returns the final response when the stream completes\n * 5. For non-tool queries, returns JSON response directly\n */\n async processMessage(\n question: string,\n pageUrl?: string,\n pageSnapshot?: PageSnapshot,\n chatMode?: string,\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n onToolCallEvent?: ToolCallEventCallback,\n abortSignal?: AbortSignal,\n userContext?: Record<string, unknown>,\n onboardingUserId?: string,\n ): Promise<HarnessResponse> {\n // Validate and sanitize user input\n const inputCheck = this.validator.validateUserInput(question);\n if (!inputCheck.valid) {\n return this._errorResponse(inputCheck.error || 'Invalid input.');\n }\n const sanitizedQuestion = inputCheck.sanitized || question;\n // Each question gets its own plan; the guard against redrawing only\n // applies within one chain.\n this.lastPlanFingerprint = null;\n this.currentPlan = undefined;\n\n // Save user message to memory\n this.memory.addMessage({\n role: 'user',\n content: sanitizedQuestion,\n timestamp: Date.now(),\n });\n\n // Get conversation history for LLM context\n const conversationHistory = this.memory.getRecentForLLM(10);\n\n // Get user's auth token for authenticated tool calls (plain JWT string).\n // Only forward it to GuideAI's backend when the site has explicitly opted\n // in via auth_config.forward_user_token — otherwise the token stays in the\n // browser and tools run client-side (the default, privacy-preserving path).\n // Forwarding an end-user bearer token to a third-party backend is a real\n // data-flow, so it must never be unconditional. See copilot-types.ts.\n const mayForwardToken = this.activeTools?.auth_config?.forward_user_token === true;\n const userToken: string | null = mayForwardToken\n ? (this.toolExecutor?.getAuthToken() ?? null)\n : null;\n\n let analytics: { session_id?: string; distinct_id?: string } = {};\n try { analytics = this.config.analyticsIds?.() ?? {}; } catch { /* ids are optional */ }\n\n const requestBody = {\n question: sanitizedQuestion,\n page_url: pageUrl,\n session_id: analytics.session_id || this.config.analyticsSessionId || undefined,\n distinct_id: analytics.distinct_id || undefined,\n page_snapshot: pageSnapshot,\n chat_mode: chatMode || 'guidance',\n assistant_label: this.config.assistantLabel?.trim() || undefined,\n delegate_instruction: this.config.delegationInstruction?.trim().slice(0, 1000) || undefined,\n conversation_history: conversationHistory,\n stream: this.api.supportsStreaming,\n user_token: userToken || undefined,\n prefer_client_tool_execution: !!this.toolExecutor || this.tools.definitions().length > 0,\n client_tools: chatMode === 'assistant' ? this.tools.definitions() : undefined,\n user_context: userContext,\n onboarding_user_id: onboardingUserId,\n tool_context: this.contextForTools(),\n client_capabilities: this.config.clientCapabilities?.length\n ? this.config.clientCapabilities\n : undefined,\n };\n\n // Use postRaw to get the unparsed response so we can check content-type\n // If SSE → read stream; if JSON → parse directly\n let rawResponse: HttpResponse;\n try {\n rawResponse = await this.api.postRaw(EP.chatAsk(), requestBody, abortSignal);\n } catch (err) {\n if (abortSignal?.aborted) {\n return this._errorResponse('Response cancelled.');\n }\n if (err instanceof AuthError) {\n return this._errorResponse(err.message);\n }\n // Account-level gates (429 credits / 402 plan) are already parsed into\n // structured errors by the api client. Flattening them into the generic\n // \"Failed to get response from server.\" here is what made an exhausted\n // account look like an outage, so let the chat error handler render them.\n if (err instanceof UsageLimitError || err instanceof PlanAccessError) {\n throw err;\n }\n return this._errorResponse('Failed to get response from server.');\n }\n\n const contentType = rawResponse.headers.get('content-type') || '';\n\n // --- SSE stream from backend agent loop ---\n if (contentType.includes('text/event-stream')) {\n return this._readSSEStream(\n rawResponse,\n sanitizedQuestion,\n onStatus,\n onTextDelta,\n onToolCallEvent,\n abortSignal,\n );\n }\n\n // --- Regular JSON response (no tool call, or guidance mode) ---\n let response: ChatAskResponseWithCopilot;\n try {\n response = await rawResponse.json();\n } catch {\n return this._errorResponse('Failed to parse server response.');\n }\n\n // Buffered response — show it immediately rather than animating it in.\n if (onTextDelta && response.answer) {\n this._emitAnswerText(response.answer, onTextDelta, abortSignal);\n }\n\n const harnessResponse = this._handleJsonResponse(response, chatMode);\n\n if (harnessResponse.autoExecuteToolCall && harnessResponse.pendingToolCall) {\n return this._executePendingToolCall(\n harnessResponse.pendingToolCalls ?? [harnessResponse.pendingToolCall],\n sanitizedQuestion,\n onStatus,\n onTextDelta,\n onToolCallEvent,\n );\n }\n\n return harnessResponse;\n }\n\n /**\n * Execute a confirmed tool call after user approval.\n * Called when user clicks \"Yes, do it\" for write/destructive actions.\n * Executes in the browser when possible, then formats the result with LLM.\n */\n async executeToolCall(\n toolCall: ToolCallResponse,\n originalQuestion: string,\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n ): Promise<HarnessResponse> {\n if (!this.canExecuteTool(toolCall)) {\n const errorMsg = 'Tool execution is unavailable in this browser context.';\n this.memory.addMessage({\n role: 'assistant',\n content: errorMsg,\n timestamp: Date.now(),\n });\n return {\n mode: 'answer',\n answer: errorMsg,\n toolResult: {\n success: false,\n error: errorMsg,\n },\n autoExecute: false,\n };\n }\n\n const result = await this.executeLocalTool(toolCall);\n\n const resultCheck = this.validator.validateToolResultEnhanced(result);\n if (!resultCheck.valid) {\n // A confirmed write the API rejected for its input is the case that\n // most deserves a second attempt: the person already said yes, and the\n // API has just said which field was wrong. Hand that to the model once.\n // Its corrected call comes back through the normal path, so a write is\n // confirmed again before it runs — corrected, not silently re-sent.\n if (REPAIRABLE_ERROR_TYPES.has(resultCheck.errorType || '')) {\n onStatus?.('That did not go through — adjusting the request...');\n const repaired = await this._continueToolChain(\n toolCall.tool,\n { error: resultCheck.detail || result.error || resultCheck.error },\n originalQuestion,\n [{\n tool_name: toolCall.tool,\n tool_result: { error: resultCheck.detail || result.error || resultCheck.error },\n }],\n onStatus,\n onTextDelta,\n );\n if (repaired.pendingToolCall || repaired.answer) {\n return repaired;\n }\n }\n\n const errorMsg = resultCheck.error || 'Tool execution failed.';\n this.memory.addMessage({\n role: 'assistant',\n content: errorMsg,\n toolResult: result,\n timestamp: Date.now(),\n });\n return {\n mode: 'answer',\n answer: errorMsg,\n toolResult: result,\n autoExecute: false,\n };\n }\n\n if (resultCheck.warning) {\n this.memory.addMessage({\n role: 'assistant',\n content: resultCheck.warning,\n toolResult: result,\n timestamp: Date.now(),\n });\n return {\n mode: 'answer',\n answer: resultCheck.warning,\n toolResult: result,\n autoExecute: false,\n };\n }\n\n return this._formatToolResult(\n toolCall.tool,\n result.data,\n originalQuestion,\n onStatus,\n onTextDelta,\n );\n }\n\n /** Alias for executeToolCall. */\n async confirmAndExecute(\n toolCall: ToolCallResponse,\n originalQuestion: string,\n ): Promise<HarnessResponse> {\n return this.executeToolCall(toolCall, originalQuestion);\n }\n\n /** Get conversation history for display in chat. */\n getConversationHistory() {\n return this.memory.getMessages();\n }\n\n /** Recent redacted context for semantic conversational follow-ups. */\n getRecentConversationContext(count = 8): { role: string; content: string }[] {\n return this.memory.getRecentForLLM(count);\n }\n\n /** Persist a completed Realtime turn so voice and text share one history. */\n recordRealtimeTurn(role: 'user' | 'assistant', content: string): void {\n const normalized = content.trim().slice(0, 4_000);\n if (!normalized) return;\n this.memory.addMessage({ role, content: normalized, timestamp: Date.now() });\n }\n\n /** Check if there is existing conversation history. */\n hasHistory(): boolean {\n return this.memory.hasHistory();\n }\n\n /** Clear conversation history. */\n clearHistory(): void {\n this.memory.clear();\n }\n\n // ----------------------------------------------------------------\n // Private methods\n // ----------------------------------------------------------------\n\n /**\n * Read SSE events from the backend agent loop stream.\n * Supports incremental text streaming, tool call display events, and cancellation.\n * Updates status via callback and resolves with the final response.\n */\n private async _readSSEStream(\n rawResponse: HttpResponse,\n originalQuestion: string,\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n onToolCallEvent?: ToolCallEventCallback,\n abortSignal?: AbortSignal,\n ): Promise<HarnessResponse> {\n // No stream: a proxy stripped it, or the platform cannot read one.\n // The buffered body carries the same answer, so use it rather than\n // failing the turn.\n if (!rawResponse.stream) {\n return this._handleJsonResponse(\n await rawResponse.json<ChatAskResponseWithCopilot>(),\n undefined,\n );\n }\n\n let buffer = '';\n let finalResponse: HarnessResponse | null = null;\n let accumulatedText = '';\n const toolCallRecords: ToolCallRecord[] = [];\n\n try {\n for await (const chunk of rawResponse.stream) {\n // Breaking out runs the stream's own cleanup (lock release), the\n // same way cancelling the reader used to.\n if (abortSignal?.aborted) break;\n\n buffer += chunk;\n\n // Parse SSE events from buffer.\n // SSE protocol: events are separated by \"\\n\\n\". Each event has\n // one or more \"field: value\" lines. We only process complete events\n // (those followed by a double newline) and keep any trailing\n // incomplete data in the buffer for the next chunk.\n const eventBlocks = buffer.split('\\n\\n');\n // Last element is either '' (buffer ended with \\n\\n) or an\n // incomplete event still accumulating — keep it in the buffer.\n buffer = eventBlocks.pop() || '';\n\n for (const block of eventBlocks) {\n const trimmedBlock = block.trim();\n if (!trimmedBlock) continue;\n\n // Extract the data line(s) from the event block\n for (const rawLine of trimmedBlock.split('\\n')) {\n const line = rawLine.trim();\n if (!line.startsWith('data: ')) continue;\n\n const jsonStr = line.slice(6);\n let event: AgentSSEEvent;\n try {\n event = JSON.parse(jsonStr);\n } catch {\n // Malformed JSON in a complete event — skip\n continue;\n }\n\n switch (event.type) {\n case 'status':\n onStatus?.(event.message || 'Processing...');\n break;\n\n case 'text_delta':\n if (event.delta) {\n accumulatedText += event.delta;\n onTextDelta?.(event.delta);\n }\n break;\n\n case 'tool_call_start':\n if (event.tool) {\n onToolCallEvent?.({\n type: 'start',\n tool: event.tool,\n description: event.description,\n });\n }\n onStatus?.(event.description ? `${event.description}...` : 'Looking up data...');\n break;\n\n case 'tool_call_done':\n if (event.tool) {\n toolCallRecords.push({\n tool: event.tool,\n description: event.description || '',\n success: event.success ?? true,\n summary: event.summary,\n });\n onToolCallEvent?.({\n type: 'done',\n tool: event.tool,\n success: event.success,\n summary: event.summary,\n });\n }\n break;\n\n case 'tool_result':\n onStatus?.(\n event.success\n ? `Got data from ${(event.tool || '').replace(/_/g, ' ').slice(0, 40)}...`\n : 'Tool call failed.',\n );\n break;\n\n case 'error':\n finalResponse = {\n mode: 'answer',\n answer: event.message || 'An error occurred.',\n autoExecute: false,\n };\n break;\n\n case 'done':\n if (event.response) {\n finalResponse = this._parseStreamedResponse(event.response);\n // If done has an empty answer, use accumulated streamed text\n // (strip tool_call blocks the LLM may have emitted)\n if (!finalResponse.answer && accumulatedText) {\n const stripped = this._stripToolCallArtifacts(accumulatedText);\n finalResponse.answer = stripped || 'Here is the result of your request.';\n }\n }\n break;\n }\n }\n }\n }\n } catch (err) {\n // Handle abort specifically\n if (abortSignal?.aborted) {\n if (accumulatedText) {\n // Strip tool_call artifacts from partial text\n const stripped = this._stripToolCallArtifacts(accumulatedText);\n const partialResponse: HarnessResponse = {\n mode: 'answer',\n answer: stripped || 'Response cancelled.',\n autoExecute: false,\n };\n this.memory.addMessage({\n role: 'assistant',\n content: accumulatedText,\n toolCalls: toolCallRecords.length > 0 ? toolCallRecords : undefined,\n timestamp: Date.now(),\n });\n return partialResponse;\n }\n return this._errorResponse('Response cancelled.');\n }\n console.error('[GuideAI] SSE stream error:', err);\n }\n\n // If stream ended without 'done' but we have accumulated text, use it\n // (strip tool_call artifacts that the LLM may have emitted during streaming)\n if (!finalResponse && accumulatedText) {\n const stripped = this._stripToolCallArtifacts(accumulatedText);\n finalResponse = {\n mode: 'answer',\n answer: stripped || 'Here is the result of your request.',\n autoExecute: false,\n };\n }\n\n if (finalResponse) {\n if (finalResponse.autoExecuteToolCall && finalResponse.pendingToolCall) {\n return this._executePendingToolCall(\n finalResponse.pendingToolCalls ?? [finalResponse.pendingToolCall],\n originalQuestion,\n onStatus,\n onTextDelta,\n onToolCallEvent,\n );\n }\n\n this.memory.addMessage({\n role: 'assistant',\n content: finalResponse.answer,\n toolResult: finalResponse.toolResult,\n toolCalls: toolCallRecords.length > 0 ? toolCallRecords : undefined,\n guideSteps: finalResponse.guideSteps,\n guideTitle: finalResponse.guideTitle,\n navigation: finalResponse.navigation,\n mode: finalResponse.mode,\n confirmationNeeded: finalResponse.confirmationNeeded,\n pendingToolCall: finalResponse.pendingToolCall,\n demo: finalResponse.demo,\n timestamp: Date.now(),\n });\n return finalResponse;\n }\n\n return this._errorResponse('Agent loop completed but no response was received.');\n }\n\n /**\n * Parse the ChatAskResponse from a streamed \"done\" event into HarnessResponse.\n */\n /**\n * Strip tool_call artifacts from streamed text so raw JSON is never shown.\n *\n * Handles:\n * - Fenced code blocks: ```tool_call\\n{...}```\n * - Bare JSON objects with a \"tool\" key: {\"tool\":\"list_guides\",\"params\":{}}\n * - Standalone mode: lines (mode: execute)\n */\n private _stripToolCallArtifacts(text: string): string {\n // Remove hidden reasoning blocks before any other cleanup.\n let stripped = text\n .replace(/<thinking\\b[^>]*>[\\s\\S]*?<\\/thinking>/gi, '')\n .replace(/^\\s*<\\/?thinking\\b[^>]*>\\s*$/gim, '');\n\n // 1. Remove fenced tool_call / tool_code blocks\n stripped = stripped\n .replace(/```tool_\\w+\\s*\\n[\\s\\S]*?```/g, '')\n .replace(/```tool_\\w+[\\s\\S]*?```/g, '');\n\n // 2. Remove bare JSON objects containing a \"tool\" key\n // Use brace-matching to handle nested objects like {\"tool\":\"x\",\"params\":{\"a\":1}}\n let result = '';\n let i = 0;\n while (i < stripped.length) {\n if (stripped[i] === '{') {\n let depth = 0;\n let j = i;\n while (j < stripped.length) {\n if (stripped[j] === '{') depth++;\n else if (stripped[j] === '}') {\n depth--;\n if (depth === 0) break;\n }\n j++;\n }\n if (depth === 0 && j < stripped.length) {\n const block = stripped.slice(i, j + 1);\n let parsed: unknown = null;\n try {\n parsed = JSON.parse(block);\n } catch {\n // LLM sometimes uses Jinja2-style double braces: {{\"tool\":..., \"params\":{{}}}}\n if (block.includes('{{')) {\n try {\n parsed = JSON.parse(block.replace(/\\{\\{/g, '{').replace(/\\}\\}/g, '}'));\n } catch {\n // Not valid JSON even after unwrap\n }\n }\n }\n if (parsed && typeof parsed === 'object' && 'tool' in (parsed as Record<string, unknown>)) {\n // Skip this block — it's a tool call artifact\n i = j + 1;\n continue;\n }\n }\n }\n result += stripped[i];\n i++;\n }\n\n // 3. Remove standalone mode: lines\n result = result.replace(/^\\s*mode:\\s*\\w+\\s*$/gm, '');\n\n return result.trim();\n }\n\n private _parseStreamedResponse(\n response: ChatAskResponseWithCopilot,\n ): HarnessResponse {\n const respCheck = this.validator.validateResponse(response);\n const answer = respCheck.sanitized || response.answer || '';\n\n // Handle confirmed tool call from agent loop\n if (response.tool_call?.requires_confirm) {\n return {\n mode: 'answer',\n answer,\n confirmationNeeded: true,\n pendingToolCall: response.tool_call,\n autoExecute: false,\n };\n }\n\n if (response.tool_call) {\n return {\n mode: 'answer',\n answer,\n pendingToolCall: response.tool_call,\n pendingToolCalls: response.tool_calls?.length\n ? response.tool_calls\n : [response.tool_call],\n autoExecuteToolCall: true,\n autoExecute: false,\n };\n }\n\n let guideSteps = response.guide_steps || undefined;\n if (guideSteps && guideSteps.length > 0) {\n const stepCheck = this.validator.validateGuideSteps(guideSteps);\n guideSteps = stepCheck.valid ? stepCheck.steps : undefined;\n }\n\n let toolResult: ToolExecutionResult | undefined;\n if (response.tool_result) {\n toolResult = {\n success: (response.tool_result as Record<string, unknown>).success as boolean ?? true,\n data: (response.tool_result as Record<string, unknown>).data,\n error: (response.tool_result as Record<string, unknown>).error as string | undefined,\n status: (response.tool_result as Record<string, unknown>).status as number | undefined,\n };\n }\n\n return {\n mode: response.mode || 'answer',\n answer,\n intent: response.intent,\n intentReason: response.intent_reason || undefined,\n guideSteps,\n guideTitle: response.guide_title || undefined,\n navigation: response.navigation || undefined,\n toolResult,\n autoExecute: false, // disabled — never auto-click/scroll/fill on user's page\n escalation: response.escalation || undefined,\n demo: response.demo || undefined,\n point: response.point || undefined,\n actions: response.actions?.length ? response.actions : undefined,\n payment: response.payment || undefined,\n coach: response.coach || undefined,\n quickReplies: response.quick_replies?.length ? response.quick_replies : undefined,\n };\n }\n\n /**\n * Handle a regular JSON response (non-streaming).\n */\n /**\n * Surface a plan to the UI once per chain.\n *\n * The chain revisits the server on every round, and a mid-chain response\n * may restate the plan. Re-announcing an identical plan would redraw it and\n * make the assistant look like it had started over, so only a changed plan\n * is emitted.\n */\n private _announcePlan(plan?: AssistantPlan | null): AssistantPlan | undefined {\n if (!plan || !Array.isArray(plan.steps) || plan.steps.length === 0) {\n return undefined;\n }\n const fingerprint = JSON.stringify(plan.steps.map((s) => [s.title, s.tool]));\n if (fingerprint !== this.lastPlanFingerprint) {\n this.lastPlanFingerprint = fingerprint;\n this.currentPlan = plan;\n this.onPlan?.(plan);\n }\n return plan;\n }\n\n private _handleJsonResponse(\n response: ChatAskResponseWithCopilot,\n chatMode?: string,\n toolResult?: ToolExecutionResult,\n ): HarnessResponse {\n const respCheck = this.validator.validateResponse(response);\n if (!respCheck.valid) {\n return this._errorResponse(respCheck.error || 'Invalid server response.');\n }\n\n const answer = respCheck.sanitized || response.answer;\n // Announced the moment it arrives, not when the chain finishes: the point\n // of a plan is that the user reads it while the work happens.\n const plan = this._announcePlan(response.plan) ?? this.currentPlan;\n\n // Confirmed tool calls — need user approval before execution\n if (response.tool_call && response.tool_call.requires_confirm) {\n return {\n mode: 'answer',\n answer,\n plan,\n confirmationNeeded: true,\n pendingToolCall: response.tool_call,\n autoExecute: false,\n };\n }\n\n if (response.tool_call) {\n return {\n mode: 'answer',\n answer,\n plan,\n pendingToolCall: response.tool_call,\n pendingToolCalls: response.tool_calls?.length\n ? response.tool_calls\n : [response.tool_call],\n autoExecuteToolCall: true,\n autoExecute: false,\n };\n }\n\n // Validate guide steps\n let guideSteps = response.guide_steps || undefined;\n if (guideSteps && guideSteps.length > 0) {\n const stepCheck = this.validator.validateGuideSteps(guideSteps);\n if (stepCheck.warnings.length > 0) {\n console.warn('[GuideAI] Guide step warnings:', stepCheck.warnings);\n }\n guideSteps = stepCheck.valid ? stepCheck.steps : undefined;\n }\n\n const harnessResult: HarnessResponse = {\n mode: response.mode || 'guide',\n answer,\n plan,\n intent: response.intent,\n intentReason: response.intent_reason || undefined,\n guideSteps,\n guideTitle: response.guide_title || undefined,\n navigation: response.navigation || undefined,\n toolResult,\n autoExecute: false, // disabled — never auto-click/scroll/fill on user's page\n escalation: response.escalation || undefined,\n demo: response.demo || undefined,\n point: response.point || undefined,\n actions: response.actions?.length ? response.actions : undefined,\n payment: response.payment || undefined,\n coach: response.coach || undefined,\n quickReplies: response.quick_replies?.length ? response.quick_replies : undefined,\n };\n\n // Save to memory with full response data for history restoration\n this.memory.addMessage({\n role: 'assistant',\n content: answer,\n toolResult,\n guideSteps: harnessResult.guideSteps,\n guideTitle: harnessResult.guideTitle,\n navigation: harnessResult.navigation,\n mode: harnessResult.mode,\n escalation: harnessResult.escalation,\n demo: harnessResult.demo,\n timestamp: Date.now(),\n });\n\n return harnessResult;\n }\n\n private async _continueToolChain(\n latestToolName: string,\n latestToolResult: unknown,\n originalQuestion: string,\n toolHistory: ToolChainStep[],\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n latestBatch?: ToolChainStep[],\n budget?: { toolsRemaining: number; forceAnswer: boolean },\n ): Promise<HarnessResponse> {\n const conversationHistory = this.memory.getRecentForLLM(6);\n\n if (onStatus) {\n onStatus(\n budget?.forceAnswer\n ? 'Writing up what I found...'\n : latestBatch && latestBatch.length > 1\n ? 'Compiling the information together...'\n : 'Reviewing what came back...',\n );\n }\n\n const response = await this.api.post<ChatAskResponseWithCopilot>(\n EP.chatContinue(),\n {\n original_question: originalQuestion,\n latest_tool_name: latestToolName,\n latest_tool_result: latestToolResult,\n latest_batch: latestBatch ?? [],\n conversation_history: conversationHistory,\n tool_history: toolHistory,\n tools_remaining: budget?.toolsRemaining,\n force_answer: budget?.forceAnswer ?? false,\n tool_context: this.contextForTools(),\n client_tools: this.tools.definitions(),\n },\n );\n\n const harnessResponse = this._handleJsonResponse(\n response,\n 'assistant',\n { success: true, data: latestToolResult },\n );\n\n if (!harnessResponse.autoExecuteToolCall && onTextDelta && harnessResponse.answer) {\n this._emitAnswerText(harnessResponse.answer, onTextDelta);\n }\n\n return harnessResponse;\n }\n\n /**\n * Remove calls that have already been made in this chain.\n *\n * The model is told not to repeat itself, and mostly does not, but a long\n * chain occasionally re-requests an endpoint it already has — which costs a\n * request and a model round trip to learn nothing new.\n */\n private _dropRepeats(\n toolCalls: ToolCallResponse[],\n executed: Set<string>,\n callKey: (call: ToolCallResponse) => string,\n ): ToolCallResponse[] {\n return toolCalls.filter((call) => !executed.has(callKey(call)));\n }\n\n /**\n * Run a client-executed tool chain.\n *\n * Each round the model may request either one tool or a batch of\n * independent tools. A batch is executed concurrently, so the wall-clock\n * cost of gathering four unrelated resources is one request plus one model\n * round trip, rather than four of each. Dependent calls still arrive one at\n * a time — the model only batches what it can run without waiting.\n */\n private async _runClientToolChain(\n initialToolCalls: ToolCallResponse[],\n originalQuestion: string,\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n onToolCallEvent?: ToolCallEventCallback,\n ): Promise<HarnessResponse> {\n if (!this.hasAnyLocalTools()) {\n return this._errorResponse('Tool execution is unavailable in this browser context.');\n }\n\n const toolHistory: ToolChainStep[] = [];\n // Identity of every call already made, so a later round cannot repeat it.\n // Keyed on tool + params + body: the same endpoint for a different record\n // is a legitimately different call, the same endpoint twice over is not.\n // The body has to count too — a write that was rejected and then corrected\n // reuses the same path with a different payload, and keying on params\n // alone would discard the correction as a repeat of the failure.\n const executed = new Set<string>();\n const callKey = (call: ToolCallResponse) =>\n `${call.tool}:${JSON.stringify(call.params ?? {})}:${JSON.stringify(call.body ?? {})}`;\n\n let currentBatch: ToolCallResponse[] = this._dropRepeats(initialToolCalls, executed, callKey);\n let toolsRun = 0;\n let repairsUsed = 0;\n\n for (let iteration = 0; currentBatch.length > 0 && iteration < MAX_CHAIN_ROUNDS; iteration += 1) {\n // Total fan-out cap. The per-round cap alone let a broad question walk\n // through a dozen-plus endpoints, which is slow and rarely adds\n // anything the earlier calls did not already cover.\n if (toolsRun + currentBatch.length > MAX_TOTAL_TOOL_CALLS) {\n currentBatch = currentBatch.slice(0, Math.max(0, MAX_TOTAL_TOOL_CALLS - toolsRun));\n if (currentBatch.length === 0) break;\n }\n currentBatch.forEach((call) => executed.add(callKey(call)));\n toolsRun += currentBatch.length;\n\n const results = await this.executeLocalToolBatch(\n currentBatch,\n (toolCall, result) => {\n // Report each call the moment it lands, so a slow endpoint in the\n // batch does not hold back the others' status in the UI.\n const check = this.validator.validateToolResultEnhanced(result);\n onToolCallEvent?.({\n type: 'done',\n tool: toolCall.tool,\n success: check.valid,\n // The card's badge already shows Complete/Error and its title\n // already shows the label, so only send extra detail worth\n // reading — a warning, or why it failed.\n summary: check.valid\n ? check.warning\n : (check.error || 'Failed'),\n });\n },\n );\n\n const batchSteps: ToolChainStep[] = [];\n const failures: string[] = [];\n let anyRepairable = false;\n let lastGoodResult: ToolExecutionResult | undefined;\n\n results.forEach((rawResult, i) => {\n const toolCall = currentBatch[i];\n const check = this.validator.validateToolResultEnhanced(rawResult);\n\n if (!check.valid) {\n failures.push(check.error || 'Tool execution failed.');\n if (REPAIRABLE_ERROR_TYPES.has(check.errorType || '')) anyRepairable = true;\n // The model gets the full detail — status and the field the API\n // named — because it can act on it. The person never sees this.\n batchSteps.push({\n tool_name: toolCall.tool,\n tool_result: {\n error: check.detail || rawResult.error || check.error || 'Tool execution failed.',\n },\n });\n return;\n }\n\n lastGoodResult = rawResult;\n batchSteps.push({ tool_name: toolCall.tool, tool_result: rawResult.data });\n });\n\n // Every call in the batch failed. When the API rejected the input it\n // said why, and the model can act on that: give it one round to correct\n // the parameters rather than showing the user a bare status line it can\n // do nothing with. A failure it cannot fix — auth, rate limit, a server\n // fault — still ends the chain here.\n const allFailed = failures.length === results.length;\n const canRepair = allFailed && anyRepairable && repairsUsed < MAX_REPAIR_ROUNDS;\n\n if (allFailed && !canRepair) {\n const errorMsg = failures[0] || 'Tool execution failed.';\n this.memory.addMessage({\n role: 'assistant',\n content: errorMsg,\n toolResult: results[0],\n timestamp: Date.now(),\n });\n if (onTextDelta) {\n onTextDelta(errorMsg);\n }\n return {\n mode: 'answer',\n answer: errorMsg,\n toolResult: results[0],\n autoExecute: false,\n };\n }\n\n if (canRepair) {\n repairsUsed += 1;\n onStatus?.('That did not go through — adjusting and trying again...');\n // A corrected retry carries different params, so it is a different\n // call by identity and survives the no-repeats filter. Only an\n // identical reissue is dropped, which is what we want.\n }\n\n toolHistory.push(...batchSteps);\n\n const latestStep = batchSteps[batchSteps.length - 1];\n // On the last permitted round, ask for the summary outright. Left to\n // decide for itself the model keeps requesting endpoints until it is\n // cut off, which is exactly how a chain ends with data but no answer.\n const toolsRemaining = Math.max(0, MAX_TOTAL_TOOL_CALLS - toolsRun);\n const forceAnswer = toolsRemaining === 0 || iteration === MAX_CHAIN_ROUNDS - 1;\n\n const continued = await this._continueToolChain(\n latestStep.tool_name,\n latestStep.tool_result,\n originalQuestion,\n toolHistory,\n onStatus,\n onTextDelta,\n batchSteps,\n { toolsRemaining, forceAnswer },\n );\n\n const nextBatch = this._dropRepeats(\n continued.pendingToolCalls\n ?? (continued.pendingToolCall ? [continued.pendingToolCall] : []),\n executed,\n callKey,\n );\n\n if (continued.autoExecuteToolCall && nextBatch.length > 0) {\n currentBatch = nextBatch;\n onStatus?.(describeToolBatch(currentBatch));\n for (const toolCall of currentBatch) {\n onToolCallEvent?.({\n type: 'start',\n tool: toolCall.tool,\n description: describeToolCall(toolCall),\n });\n }\n continue;\n }\n\n if (!continued.toolResult && lastGoodResult) {\n continued.toolResult = lastGoodResult;\n }\n return continued;\n }\n\n // Budget spent with the model still asking for more. Rather than telling\n // the user we gave up, spend one final round asking it to answer from\n // everything already gathered.\n if (toolHistory.length > 0) {\n const last = toolHistory[toolHistory.length - 1];\n return this._continueToolChain(\n last.tool_name,\n last.tool_result,\n originalQuestion,\n toolHistory,\n onStatus,\n onTextDelta,\n undefined,\n { toolsRemaining: 0, forceAnswer: true },\n );\n }\n\n const fallback = 'I gathered several data points but hit the maximum number of tool steps.';\n this.memory.addMessage({\n role: 'assistant',\n content: fallback,\n timestamp: Date.now(),\n });\n return {\n mode: 'answer',\n answer: fallback,\n autoExecute: false,\n };\n }\n\n private hasAnyLocalTools(): boolean {\n return !!this.toolExecutor || this.tools.definitions().length > 0;\n }\n\n private canExecuteTool(toolCall: ToolCallResponse): boolean {\n if (isServerTool(toolCall)) return true;\n if (this.tools.canExecute(toolCall.tool)) {\n return this.tools.canExecuteCall(toolCall);\n }\n return !!this.toolExecutor;\n }\n\n private executeLocalTool(toolCall: ToolCallResponse): Promise<ToolExecutionResult> {\n if (isServerTool(toolCall)) return this.executeOnServer(toolCall);\n if (this.tools.canExecute(toolCall.tool)) {\n return this.tools.execute(toolCall);\n }\n if (this.toolExecutor) return this.toolExecutor.execute(toolCall);\n return Promise.resolve({\n success: false,\n error: 'Tool execution is unavailable in this browser context.',\n error_type: 'tool_unavailable',\n });\n }\n\n /**\n * An MCP tool, after the user approved it: we cannot call it — it is\n * reached from GuideAI's network with a secret the client never holds —\n * so the backend runs it, told who the user is by their token.\n */\n private async executeOnServer(toolCall: ToolCallResponse): Promise<ToolExecutionResult> {\n try {\n return await this.api.post<ToolExecutionResult>(EP.chatExecuteTool(), {\n tool: toolCall.tool,\n method: toolCall.method,\n path: toolCall.path,\n params: toolCall.params,\n body: toolCall.body,\n user_token: this.toolExecutor?.getAuthToken() ?? undefined,\n });\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : 'The tool could not be run.',\n error_type: 'tool_unavailable',\n };\n }\n }\n\n private executeLocalToolBatch(\n toolCalls: ToolCallResponse[],\n onComplete?: (toolCall: ToolCallResponse, result: ToolExecutionResult) => void,\n ): Promise<ToolExecutionResult[]> {\n return Promise.all(toolCalls.map(async (toolCall) => {\n const result = await this.executeLocalTool(toolCall);\n onComplete?.(toolCall, result);\n return result;\n }));\n }\n\n private async _formatToolResult(\n toolName: string,\n toolResult: unknown,\n originalQuestion: string,\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n ): Promise<HarnessResponse> {\n const conversationHistory = this.memory.getRecentForLLM(3);\n\n try {\n if (onStatus) {\n onStatus('Processing results...');\n }\n\n const rawResponse = await this.api.postRaw(\n EP.chatFormat(),\n {\n tool_name: toolName,\n tool_result: toolResult,\n original_question: originalQuestion,\n conversation_history: conversationHistory,\n stream: this.api.supportsStreaming,\n },\n );\n\n const contentType = rawResponse.headers.get('content-type') || '';\n if (contentType.includes('text/event-stream')) {\n return this._readFormatToolResultStream(\n rawResponse,\n toolResult,\n onStatus,\n onTextDelta,\n );\n }\n\n const formatted = await rawResponse.json() as FormatToolResultResponse;\n\n const rawAnswer = formatted?.answer || 'Here is the result of your request.';\n const answer = this.validator.validateFormattedResponse(\n rawAnswer,\n { success: true, data: toolResult },\n );\n const navigation = formatted?.navigation || undefined;\n const guideSteps = formatted?.guide_steps || undefined;\n const guideTitle = formatted?.guide_title || undefined;\n const mode = formatted?.mode === 'execute' ? 'execute' : 'answer';\n\n this.memory.addMessage({\n role: 'assistant',\n content: answer,\n toolResult: { success: true, data: toolResult },\n guideSteps,\n guideTitle,\n navigation,\n mode,\n timestamp: Date.now(),\n });\n\n return {\n mode,\n answer,\n guideSteps,\n guideTitle,\n navigation,\n toolResult: { success: true, data: toolResult },\n autoExecute: false,\n };\n } catch {\n const fallback = 'I fetched the data successfully but had trouble formatting it.';\n this.memory.addMessage({\n role: 'assistant',\n content: fallback,\n timestamp: Date.now(),\n });\n return {\n mode: 'answer',\n answer: fallback,\n toolResult: { success: true, data: toolResult },\n autoExecute: false,\n };\n }\n }\n\n private async _readFormatToolResultStream(\n rawResponse: HttpResponse,\n toolResult: unknown,\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n ): Promise<HarnessResponse> {\n let buffer = '';\n let accumulatedText = '';\n let finalResponse: FormatToolResultResponse | null = null;\n let streamError: string | null = null;\n\n // No stream — a proxy stripped it, or the platform cannot read one.\n // Seed the result from the buffered body and fall through to the same\n // formatting tail, rather than duplicating it on a second path.\n if (!rawResponse.stream) {\n try {\n finalResponse = await rawResponse.json<FormatToolResultResponse>();\n } catch {\n streamError = 'I got the data but had trouble formatting it.';\n }\n }\n\n try {\n for await (const chunk of rawResponse.stream ?? []) {\n buffer += chunk;\n const eventBlocks = buffer.split('\\n\\n');\n buffer = eventBlocks.pop() || '';\n\n for (const block of eventBlocks) {\n const trimmedBlock = block.trim();\n if (!trimmedBlock) continue;\n\n for (const rawLine of trimmedBlock.split('\\n')) {\n const line = rawLine.trim();\n if (!line.startsWith('data: ')) continue;\n\n let event: AgentSSEEvent;\n try {\n event = JSON.parse(line.slice(6));\n } catch {\n continue;\n }\n\n switch (event.type) {\n case 'status':\n onStatus?.(event.message || 'Processing results...');\n break;\n case 'text_delta':\n if (event.delta) {\n accumulatedText += event.delta;\n onTextDelta?.(event.delta);\n }\n break;\n case 'error':\n streamError = event.message || 'I got the data but had trouble formatting it.';\n break;\n case 'done':\n if (event.format_response) {\n finalResponse = event.format_response;\n }\n break;\n }\n }\n }\n }\n } catch {\n streamError = 'I got the data but had trouble formatting it.';\n }\n\n if (streamError) {\n this.memory.addMessage({\n role: 'assistant',\n content: streamError,\n timestamp: Date.now(),\n });\n return {\n mode: 'answer',\n answer: streamError,\n toolResult: { success: true, data: toolResult },\n autoExecute: false,\n };\n }\n\n const rawAnswer = finalResponse?.answer || accumulatedText || 'Here is the result of your request.';\n const answer = this.validator.validateFormattedResponse(\n rawAnswer,\n { success: true, data: toolResult },\n );\n const navigation = finalResponse?.navigation || undefined;\n const guideSteps = finalResponse?.guide_steps || undefined;\n const guideTitle = finalResponse?.guide_title || undefined;\n const mode = finalResponse?.mode === 'execute' ? 'execute' : 'answer';\n\n this.memory.addMessage({\n role: 'assistant',\n content: answer,\n toolResult: { success: true, data: toolResult },\n guideSteps,\n guideTitle,\n navigation,\n mode,\n timestamp: Date.now(),\n });\n\n return {\n mode,\n answer,\n guideSteps,\n guideTitle,\n navigation,\n toolResult: { success: true, data: toolResult },\n autoExecute: false,\n };\n }\n\n private async _executePendingToolCall(\n toolCalls: ToolCallResponse[],\n originalQuestion: string,\n onStatus?: AgentStatusCallback,\n onTextDelta?: TextDeltaCallback,\n onToolCallEvent?: ToolCallEventCallback,\n ): Promise<HarnessResponse> {\n if (toolCalls.length === 0) {\n return this._errorResponse('No tool call to execute.');\n }\n\n onStatus?.(describeToolBatch(toolCalls));\n for (const toolCall of toolCalls) {\n onToolCallEvent?.({\n type: 'start',\n tool: toolCall.tool,\n description: describeToolCall(toolCall),\n });\n }\n\n const result = await this._runClientToolChain(\n toolCalls,\n originalQuestion,\n onStatus,\n onTextDelta,\n onToolCallEvent,\n );\n\n if (!result.toolResult) {\n onToolCallEvent?.({\n type: 'done',\n tool: toolCalls[0].tool,\n success: false,\n summary: result.answer || 'Failed',\n });\n }\n\n return result;\n }\n\n /**\n * Emit a fully-buffered answer to the UI.\n *\n * This used to fake a typing effect by releasing one word every 12ms. The\n * response has already arrived in full by this point, so that delay was pure\n * added latency — a 250-word answer sat behind three seconds of animation\n * after the data was on the client. Real streaming still arrives token by\n * token over SSE; a buffered answer is shown as soon as we have it.\n */\n private _emitAnswerText(\n text: string,\n onTextDelta: TextDeltaCallback,\n abortSignal?: AbortSignal,\n ): void {\n if (abortSignal?.aborted) return;\n onTextDelta(text);\n }\n\n private _errorResponse(message: string): HarnessResponse {\n return {\n mode: 'guide',\n answer: message,\n autoExecute: false,\n };\n }\n}\n","// ============================================================\n// @guideai/react-native — HTTP transport\n// ============================================================\n//\n// React Native's `fetch` is implemented over XHR and does NOT support\n// `response.body.getReader()` — reading a body progressively through it\n// is impossible, and the failure is silent: you get the whole body at\n// the end, so streaming \"works\" but never streams.\n//\n// So this goes to XMLHttpRequest directly, which does expose progressive\n// `responseText` at readyState 3.\n// ============================================================\n\nimport type {\n HttpRequest,\n HttpResponse,\n HttpTransport,\n TextStream,\n} from '@guideai/core';\n\n/**\n * The header the backend accepts in place of a browser `Origin`.\n *\n * A native app sends no `Origin` (there is no browser and no CORS), and\n * `get_effective_request_origin()` in the backend's request_origin_guard\n * falls back to this one. It is then matched against the site's own\n * domain + aliases, so it can only ever name a domain the site owner has\n * already registered — it cannot authorize an unlisted one.\n *\n * It is NOT proof of app identity: anyone who extracts the public token\n * from an APK can send the same header. The web SDK has the identical\n * property. Treat it as origin declaration, not authentication.\n */\nexport const REQUEST_ORIGIN_HEADER = 'x-guideai-request-origin';\n\n/**\n * Parse `getAllResponseHeaders()`'s CRLF-delimited block into a lookup.\n *\n * `raw` is nullable: React Native returns null before headers arrive and\n * on some reused connections. The DOM types say `string`, which is why\n * this crashed as \"Cannot read property 'split' of null\" only on the\n * second request of a turn — the tool call.\n */\nfunction parseHeaders(raw: string | null): { get(name: string): string | null } {\n const map = new Map<string, string>();\n for (const line of (raw ?? '').split(/\\r?\\n/)) {\n const colon = line.indexOf(':');\n if (colon <= 0) continue;\n map.set(line.slice(0, colon).trim().toLowerCase(), line.slice(colon + 1).trim());\n }\n return { get: (name: string) => map.get(name.toLowerCase()) ?? null };\n}\n\nexport interface RNTransportOptions {\n /**\n * The registered site domain this app reports as its origin, e.g.\n * `https://app.example.com`. Required by the backend's origin guard;\n * without it every public endpoint answers 403.\n */\n requestOrigin?: string;\n}\n\n/**\n * How long to wait for a server to START responding, in ms.\n *\n * Not a total-duration cap: `xhr.timeout` measures the whole request, and\n * a chat stream legitimately stays open for minutes, so setting that would\n * cut replies off mid-sentence. This fires only while nothing at all has\n * come back — the case where a tool set points at a host that is not\n * there, and the request otherwise hangs for the two minutes the OS takes\n * to give up while the assistant shows a spinner.\n */\nconst RESPONSE_START_TIMEOUT_MS = 20_000;\n\nexport class RNHttpTransport implements HttpTransport {\n /**\n * True when XHR exists at all. React Native always provides it; the\n * check exists so a Node test runner or an unusual runtime degrades to\n * a buffered response rather than throwing at construction.\n */\n readonly supportsStreaming: boolean;\n\n constructor(private readonly options: RNTransportOptions = {}) {\n this.supportsStreaming = typeof XMLHttpRequest !== 'undefined';\n }\n\n request(req: HttpRequest): Promise<HttpResponse> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open(req.method, req.url);\n\n const headers: Record<string, string> = { ...req.headers };\n if (this.options.requestOrigin && !headers[REQUEST_ORIGIN_HEADER]) {\n headers[REQUEST_ORIGIN_HEADER] = this.options.requestOrigin;\n }\n for (const [name, value] of Object.entries(headers)) {\n xhr.setRequestHeader(name, value);\n }\n\n // Streaming consumers park here until each new slice arrives.\n const doneCallbacks: Array<() => void> = [];\n let delivered = 0;\n let pending: string[] = [];\n let finished = false;\n let failure: Error | null = null;\n let wake: (() => void) | null = null;\n\n const push = (chunk: string) => {\n if (chunk) pending.push(chunk);\n wake?.();\n wake = null;\n };\n\n const drainResponseText = () => {\n // responseText is cumulative, so yield only what is new. Getting\n // this offset wrong duplicates or drops text with no error.\n const text = xhr.responseText;\n if (text.length > delivered) {\n const slice = text.slice(delivered);\n delivered = text.length;\n push(slice);\n }\n };\n\n let startTimer: ReturnType<typeof setTimeout> | null = null;\n const clearStartTimer = () => {\n if (startTimer === null) return;\n clearTimeout(startTimer);\n startTimer = null;\n };\n\n const fail = (error: Error) => {\n clearStartTimer();\n // A timeout has already rejected; a later onabort/onerror from the\n // same abort() must not reject a second time.\n if (finished) return;\n failure = error;\n finished = true;\n push('');\n reject(error);\n };\n\n // Resolve once headers are known, so the caller can inspect the\n // content type and start reading before the body completes.\n let resolved = false;\n const resolveOnce = () => {\n if (resolved) return;\n resolved = true;\n // Headers are in — the server is alive, so the rest of the\n // response may take as long as it needs.\n clearStartTimer();\n\n const stream: TextStream = {\n async *[Symbol.asyncIterator]() {\n while (true) {\n if (pending.length) {\n const batch = pending;\n pending = [];\n for (const chunk of batch) yield chunk;\n continue;\n }\n if (failure) throw failure;\n if (finished) return;\n await new Promise<void>((wakeUp) => { wake = wakeUp; });\n }\n },\n };\n\n // Body text for the non-streaming path. Read lazily: touching\n // responseText early is fine, but the value is only complete once\n // the request finishes, so json()/text() await that.\n const whenDone = new Promise<string>((done, error) => {\n const settle = () => {\n if (failure) error(failure);\n else done(xhr.responseText);\n };\n if (finished) settle();\n else doneCallbacks.push(settle);\n });\n\n resolve({\n status: xhr.status,\n headers: parseHeaders(xhr.getAllResponseHeaders()),\n json: async <T,>() => JSON.parse(await whenDone) as T,\n text: () => whenDone,\n stream,\n });\n };\n\n xhr.onreadystatechange = () => {\n if (xhr.readyState === 2) {\n resolveOnce();\n } else if (xhr.readyState === 3) {\n resolveOnce();\n drainResponseText();\n } else if (xhr.readyState === 4) {\n resolveOnce();\n drainResponseText();\n finished = true;\n push('');\n for (const callback of doneCallbacks) callback();\n }\n };\n\n xhr.onerror = () => fail(new Error(`Network request failed: ${req.method} ${req.url}`));\n xhr.ontimeout = () => fail(new Error(`Request timed out: ${req.url}`));\n xhr.onabort = () => fail(new Error(`Request aborted: ${req.url}`));\n\n if (req.signal) {\n // Core's AbortSignal is declared with only `aborted` — it never\n // originates cancellation, so there is no addEventListener here.\n // Checking once before send covers an already-aborted signal.\n if (req.signal.aborted) {\n fail(new Error(`Request aborted: ${req.url}`));\n return;\n }\n }\n\n // Armed only now, so the callback closes over a fully initialised\n // `fail`. A tool set pointing at an unreachable host otherwise\n // leaves the assistant spinning for however long the OS takes to\n // give up — about two minutes — with no way to tell it failed.\n startTimer = setTimeout(() => {\n startTimer = null;\n fail(new Error(\n `No response after ${RESPONSE_START_TIMEOUT_MS}ms: ${req.method} ${req.url}`,\n ));\n // Abort last: onabort calls fail(), which is now a no-op because\n // the timeout above already settled the promise.\n try { xhr.abort(); } catch { /* already gone */ }\n }, RESPONSE_START_TIMEOUT_MS);\n\n xhr.send(req.body ?? null);\n });\n }\n}\n","// ============================================================\n// @guideai/react-native — platform adapter\n// ============================================================\n//\n// Core's KVStore is SYNCHRONOUS (see platform-adapter.ts): conversation\n// memory reads storage inside getMessages(), which the agent loop calls\n// from ~20 sync places. That rules out AsyncStorage entirely.\n//\n// MMKV and expo-sqlite/kv-store are both sync and both work. This module\n// does not import either one — it takes whichever the host already has,\n// so this package adds no native dependency of its own.\n// ============================================================\n\nimport type { CredentialSource, KVStore, PlatformAdapter } from '@guideai/core';\n\n/**\n * The slice of MMKV this needs. Declared structurally so `new MMKV()`\n * satisfies it without this package depending on react-native-mmkv.\n */\nexport interface SyncNativeStore {\n getString(key: string): string | undefined;\n set(key: string, value: string): void;\n delete(key: string): void;\n}\n\n/** Adapts MMKV's getString/set/delete to core's getItem/setItem/removeItem. */\nexport class NativeKVStore implements KVStore {\n constructor(private readonly store: SyncNativeStore) {}\n\n getItem(key: string): string | null {\n // MMKV returns undefined for a missing key; core's contract says null.\n return this.store.getString(key) ?? null;\n }\n\n setItem(key: string, value: string): void {\n this.store.set(key, value);\n }\n\n removeItem(key: string): void {\n this.store.delete(key);\n }\n}\n\n/** In-memory KVStore. Session storage, and the fallback when no native store is supplied. */\nexport class MemoryStore implements KVStore {\n private readonly map = new Map<string, string>();\n getItem(key: string): string | null { return this.map.get(key) ?? null; }\n setItem(key: string, value: string): void { this.map.set(key, value); }\n removeItem(key: string): void { this.map.delete(key); }\n}\n\n/**\n * Credentials on React Native.\n *\n * A native app has no cookie jar, so `cookie` returns null — the contract\n * explicitly prefers that over an empty string that would read as\n * \"present but blank\". `localStorage`/`sessionStorage` map onto the\n * stores the host provided, which is where an RN app actually keeps a\n * token.\n */\nexport class RNCredentialSource implements CredentialSource {\n /** The signed-in user's token, as the host last handed it over. */\n private hostToken: string | null = null;\n /** Values the host app supplies for declared headers, by lower-cased name. */\n private appValues = new Map<string, string>();\n\n constructor(\n private readonly storage: KVStore,\n private readonly session: KVStore,\n ) {}\n\n /**\n * Set by the runtime from `getAuthToken()` before every turn, so the\n * synchronous read inside a tool call sees the host's current login.\n */\n setAuthToken(token: string | null): void {\n this.hostToken = token?.trim() || null;\n }\n\n authToken(): string | null {\n return this.hostToken;\n }\n\n /** Set from `getRequestHeaders()` before every turn. */\n setAppValues(values: Record<string, string>): void {\n this.appValues = new Map(Object.entries(values).map(([k, v]) => [k.toLowerCase(), v]));\n }\n\n read(source: string, key: string): string | null {\n switch (source) {\n case 'localStorage': return this.storage.getItem(key);\n case 'sessionStorage': return this.session.getItem(key);\n // A header the toolset declares as supplied by the app.\n case 'app': return this.appValues.get(key.toLowerCase()) ?? null;\n // No cookie jar on a device. Saying so is the honest answer.\n case 'cookie': return null;\n default: return null;\n }\n }\n}\n\nexport interface RNPlatformOptions {\n /** An MMKV instance (or anything sync-shaped). Omitted → memory only, lost on relaunch. */\n store?: SyncNativeStore;\n}\n\nexport function rnPlatform(\n options: RNPlatformOptions = {},\n): Required<PlatformAdapter> & { credentials: RNCredentialSource } {\n const storage: KVStore = options.store ? new NativeKVStore(options.store) : new MemoryStore();\n const session: KVStore = new MemoryStore();\n return { storage, session, credentials: new RNCredentialSource(storage, session) };\n}\n","// ============================================================\n// @guideai/react-native — the host's own login, for its own API\n// ============================================================\n//\n// In assistant mode the model calls the site's API as the signed-in user,\n// from the device, with that user's token. The web SDK finds the token in\n// browser storage under a key the dashboard names. A native app keeps it\n// wherever it likes — SecureStore, AsyncStorage — often behind an async\n// read, and refreshes it when it expires. So the host hands it over:\n//\n// GuideAI.configure({\n// apiKey, origin,\n// getAuthToken: () => AsyncStorage.getItem('accessToken'),\n// onAuthExpired: () => refreshSession(), // returns the new token\n// });\n//\n// The token is read before each turn and cached for the synchronous read\n// inside a tool call. A 401 from the site's API asks for a new one once\n// and repeats that request; a second 401 is the API's real answer.\n//\n// The token goes only to the site's own API (the toolset's base URL). It\n// reaches GuideAI's backend only if the site opted in with\n// `forward_user_token`, exactly as on the web.\n// ============================================================\n\nimport type { HttpRequest, HttpResponse, HttpTransport } from '@guideai/core';\nimport type { RNCredentialSource } from '../platform/rn-platform';\n\n/** Returns the signed-in user's token, or null when signed out. May be async. */\nexport type AuthTokenGetter = () => string | null | undefined | Promise<string | null | undefined>;\n\n/**\n * Values for the headers the site's toolset declares as supplied by the app\n * (Copilot Tools → \"Supplied by your app\"). May be async. Only declared\n * names are ever sent.\n */\nexport type RequestHeadersGetter = () =>\n | Record<string, string> | null | undefined\n | Promise<Record<string, string> | null | undefined>;\n\nexport interface HostAuthOptions {\n getAuthToken?: AuthTokenGetter;\n /** Called once after a 401 from the site's API; returns the refreshed token. */\n onAuthExpired?: AuthTokenGetter;\n getRequestHeaders?: RequestHeadersGetter;\n}\n\n/** Never sent from here: the token has its own path, and these are the transport's. */\nconst RESERVED_HEADERS = new Set(['authorization', 'host', 'content-length', 'cookie']);\n\nexport class HostAuth {\n private pendingRenewal: Promise<string | null> | null = null;\n\n constructor(\n private readonly credentials: RNCredentialSource,\n private readonly options: HostAuthOptions = {},\n ) {}\n\n get enabled(): boolean {\n return Boolean(this.options.getAuthToken);\n }\n\n current(): string | null {\n return this.credentials.authToken();\n }\n\n /** Read the host's token and extra headers now. Never throws. */\n async refresh(): Promise<string | null> {\n if (this.options.getRequestHeaders) {\n let raw: Record<string, string> | null | undefined = null;\n try {\n raw = await this.options.getRequestHeaders();\n } catch (error) {\n console.warn('[GuideAI] getRequestHeaders failed; sending none:', error);\n }\n const clean: Record<string, string> = {};\n for (const [name, value] of Object.entries(raw ?? {})) {\n if (/^[A-Za-z0-9-]{1,64}$/.test(name) && !RESERVED_HEADERS.has(name.toLowerCase())\n && typeof value === 'string' && value && !/[\\r\\n]/.test(value)) {\n clean[name] = value;\n }\n }\n this.credentials.setAppValues(clean);\n }\n if (!this.options.getAuthToken) return null;\n let token: string | null = null;\n try {\n token = (await this.options.getAuthToken())?.trim() || null;\n } catch (error) {\n console.warn('[GuideAI] getAuthToken failed; continuing signed out:', error);\n }\n this.credentials.setAuthToken(token);\n return token;\n }\n\n /**\n * A new token after a 401, or null when there is none to be had.\n *\n * Shared while in flight: a batch of parallel tool calls that all hit an\n * expired token triggers one refresh, not one per call.\n */\n renew(): Promise<string | null> {\n if (!this.pendingRenewal) {\n const ask = this.options.onAuthExpired ?? this.options.getAuthToken;\n this.pendingRenewal = (async () => {\n let token: string | null = null;\n try {\n token = ask ? (await ask())?.trim() || null : null;\n } catch (error) {\n console.warn('[GuideAI] onAuthExpired failed:', error);\n }\n this.credentials.setAuthToken(token);\n return token;\n })().finally(() => { this.pendingRenewal = null; });\n }\n return this.pendingRenewal;\n }\n}\n\nfunction bearerOf(headers: Record<string, string>): string | null {\n const name = Object.keys(headers).find((key) => key.toLowerCase() === 'authorization');\n const value = name ? headers[name] : '';\n return value?.startsWith('Bearer ') ? value.slice(7) : null;\n}\n\n/**\n * The transport tool calls use, with one retry on an expired host token.\n *\n * Only a request carrying the host's own token is retried — a 401 for any\n * other credential (a static API key, a different login) is not ours to\n * refresh, and is passed straight back.\n */\nexport class HostAuthTransport implements HttpTransport {\n constructor(\n private readonly inner: HttpTransport,\n private readonly auth: HostAuth,\n ) {}\n\n get supportsStreaming(): boolean {\n return this.inner.supportsStreaming;\n }\n\n async request(req: HttpRequest): Promise<HttpResponse> {\n const response = await this.inner.request(req);\n if (response.status !== 401 || !this.auth.enabled) return response;\n\n const sent = bearerOf(req.headers);\n if (!sent || sent !== this.auth.current()) return response;\n\n const renewed = await this.auth.renew();\n if (!renewed || renewed === sent) return response;\n\n const headers = { ...req.headers };\n for (const key of Object.keys(headers)) {\n if (key.toLowerCase() === 'authorization') delete headers[key];\n }\n headers.Authorization = `Bearer ${renewed}`;\n return this.inner.request({ ...req, headers });\n }\n}\n","// ============================================================\n// @guideai/react-native — screen registry\n// ============================================================\n//\n// THE constraint of the whole mobile runtime: there is no auto-capture.\n//\n// The web SDK builds its snapshot with `document.querySelectorAll`.\n// React Native has no queryable view tree at runtime — neither do Flutter\n// or Compose — so nothing here can walk the screen and discover what is\n// on it. The host declares its elements, or the snapshot is empty.\n//\n// No partial heuristic is offered on purpose. A snapshot that silently\n// found half a screen would produce an assistant that confidently points\n// at controls that are not there, which is worse than one that admits it\n// cannot see. An undeclared screen reports zero elements and the model\n// falls back to route-level guidance, which the mobile scanner already\n// grounds — so adoption is incremental rather than all-or-nothing.\n// ============================================================\n\nimport type { PageElement, PageSnapshot } from '@guideai/core';\n\n/** What a host says about one control on screen. */\nexport interface DeclaredElement {\n /**\n * Stable id, unique within the screen. Becomes the snapshot's\n * `selector` — legitimate because the backend treats `selector` as an\n * opaque round-trip token: it renders elements to the model as numbered\n * [ID]s and only checks the string is specific (not the word \"button\").\n */\n id: string;\n /** Visible text. What the model matches the user's words against. */\n label: string;\n /** Coarse kind, mapped to the `tag` the backend's prompt expects. */\n kind?: 'button' | 'link' | 'input' | 'text' | 'toggle' | 'image';\n /** Current value, for inputs. */\n value?: string;\n /** Accessibility label when it says more than the visible text. */\n accessibilityLabel?: string;\n /** Placeholder text for an empty input. */\n placeholder?: string;\n /** True when mounted but off-screen or collapsed. */\n hidden?: boolean;\n /** Heading of the group this sits in, e.g. \"Sharing options\". */\n sectionTitle?: string;\n}\n\n/** Actions a declared element can perform, supplied by the host at registration. */\nexport interface ElementHandlers {\n /** Invoked by guideai_click_element. */\n press?: () => void | Promise<void>;\n /** Invoked by guideai_fill_field / guideai_fill_form. */\n setValue?: (value: string) => void | Promise<void>;\n /** Invoked by guideai_scroll_to_element. */\n scrollTo?: () => void | Promise<void>;\n}\n\nexport interface RegisteredElement extends DeclaredElement {\n handlers: ElementHandlers;\n}\n\n/**\n * The `tag` the backend's snapshot prompt expects. It reuses web\n * vocabulary because the backend and the model already speak it — this\n * is presentation for the planner, not a claim that RN renders HTML.\n */\nconst KIND_TO_TAG: Record<NonNullable<DeclaredElement['kind']>, string> = {\n button: 'button',\n link: 'a',\n input: 'input',\n text: 'p',\n toggle: 'input',\n image: 'img',\n};\n\n/**\n * One screen's declared elements.\n *\n * Registration is keyed by id, so a remount replaces rather than\n * duplicates — React can mount a component twice (StrictMode, fast\n * refresh) and a duplicate would show the model two copies of one button.\n */\nexport class ScreenRegistry {\n private readonly elements = new Map<string, RegisteredElement>();\n\n register(element: DeclaredElement, handlers: ElementHandlers = {}): () => void {\n this.elements.set(element.id, { ...element, handlers });\n return () => {\n // Only unregister if this exact registration still owns the id — a\n // remount that re-registered first must not be clobbered by the old\n // instance's cleanup running afterwards.\n if (this.elements.get(element.id)?.handlers === handlers) {\n this.elements.delete(element.id);\n }\n };\n }\n\n get(id: string): RegisteredElement | undefined {\n return this.elements.get(id);\n }\n\n /** Resolve by id first, then by visible label — the model may send either. */\n find(request: { selector?: string; name?: string }): RegisteredElement | undefined {\n if (request.selector) {\n const byId = this.elements.get(request.selector);\n if (byId) return byId;\n }\n const name = (request.name ?? request.selector ?? '').trim().toLowerCase();\n if (!name) return undefined;\n for (const element of this.elements.values()) {\n if (element.label.trim().toLowerCase() === name) return element;\n if (element.accessibilityLabel?.trim().toLowerCase() === name) return element;\n }\n return undefined;\n }\n\n all(): RegisteredElement[] {\n return Array.from(this.elements.values());\n }\n\n clear(): void {\n this.elements.clear();\n }\n}\n\nexport interface SnapshotInput {\n /**\n * The route path, matching what the mobile scanner emitted (`/share`).\n * Grounding is exact string equality on the backend, so this must be\n * the scanner's `path` — not the native screen name.\n */\n path: string;\n /** Human title of the screen. */\n title: string;\n registry: ScreenRegistry;\n}\n\nfunction toPageElement(element: RegisteredElement): PageElement {\n const tag = KIND_TO_TAG[element.kind ?? 'button'];\n return {\n tag,\n text: element.label,\n selector: element.id,\n ...(element.accessibilityLabel ? { aria_label: element.accessibilityLabel } : {}),\n ...(element.value !== undefined ? { value: element.value } : {}),\n ...(element.placeholder ? { placeholder: element.placeholder } : {}),\n ...(element.hidden ? { hidden: true } : {}),\n ...(element.sectionTitle ? { section_title: element.sectionTitle } : {}),\n };\n}\n\n/**\n * Build the snapshot the backend already consumes.\n *\n * Same `PageSnapshot` type the web SDK produces — no mobile-specific\n * shape — which is what lets one backend serve both without a branch.\n */\nexport function buildSnapshot(input: SnapshotInput): PageSnapshot {\n const elements = input.registry.all().map(toPageElement);\n return {\n url: input.path,\n title: input.title,\n elements,\n };\n}\n","// ============================================================\n// @guideai/react-native — native client tool registry\n// ============================================================\n//\n// The mobile counterpart to BrowserAgentToolRegistry. The agent loop\n// never learns which one it is holding — that is what keeps a single\n// loop serving both platforms.\n//\n// TOOL NAMES ARE NOT FREE. The backend's _CLIENT_BROWSER_TOOL_DESCRIPTIONS\n// (docker/core/app/api/v1/chat.py) is a hardcoded allowlist of nine names\n// and it SILENTLY DROPS anything else — a `guideai_tap` would vanish with\n// no error and the model would simply never call it. So this reuses the\n// nine, and omits the ones a phone cannot honour.\n//\n// Omission IS the capability negotiation: `definitions()` is the\n// declaration the backend turns into the planner's prompt, so a tool left\n// out is a tool the model is never offered. There is deliberately no\n// second capability flag, because two lists could disagree.\n//\n// implemented omitted\n// guideai_click_element → press guideai_hover_element (no hover on touch)\n// guideai_fill_field → setValue guideai_refresh (no page to reload)\n// guideai_fill_form → setValue guideai_highlight_element (needs measure(); v2)\n// guideai_scroll_to_element → scrollTo\n// guideai_navigate → navigation ref\n// guideai_go_back → navigation ref\n// ============================================================\n\nimport type {\n ClientToolDefinition,\n ClientToolRegistry,\n CopilotToolDef,\n ToolCallResponse,\n ToolExecutionResult,\n} from '@guideai/core';\nimport type { ScreenRegistry } from '../screen/screen-registry';\n\n/** Matches the browser registry so the backend sees one round-trip convention. */\nconst CLIENT_PATH_PREFIX = '/__guideai/client/';\n\nconst elementProperties = {\n selector: {\n type: 'string',\n description: 'An element id from the current screen snapshot.',\n },\n name: {\n type: 'string',\n description: 'Visible text or accessible name when a reliable id is unavailable.',\n },\n};\n\nexport const RN_AGENT_TOOL_DEFINITIONS: ClientToolDefinition[] = [\n {\n name: 'guideai_fill_field',\n description: 'Fill one non-sensitive field on the current screen using its id, label, or accessible name.',\n consequential: true,\n parameters: {\n type: 'object',\n properties: {\n fieldName: { type: 'string', description: 'Label, accessible name, or placeholder of the field.' },\n selector: elementProperties.selector,\n value: { type: 'string', description: 'The value the user explicitly asked to enter.' },\n },\n required: ['value'],\n additionalProperties: false,\n },\n },\n {\n name: 'guideai_fill_form',\n description: 'Fill several non-sensitive fields on the current screen in order and report each outcome.',\n consequential: true,\n parameters: {\n type: 'object',\n properties: {\n fields: {\n type: 'array',\n maxItems: 25,\n items: {\n type: 'object',\n properties: {\n fieldName: { type: 'string' }, selector: { type: 'string' }, value: { type: 'string' },\n },\n required: ['value'],\n },\n },\n },\n required: ['fields'],\n additionalProperties: false,\n },\n },\n {\n name: 'guideai_scroll_to_element',\n description: 'Scroll an element on the current screen into view.',\n parameters: { type: 'object', properties: elementProperties, additionalProperties: false },\n },\n {\n name: 'guideai_click_element',\n description: 'Tap one enabled control on the current screen after user confirmation.',\n consequential: true,\n parameters: { type: 'object', properties: elementProperties, additionalProperties: false },\n },\n {\n name: 'guideai_navigate',\n description: 'Navigate to another screen in the app.',\n consequential: true,\n parameters: {\n type: 'object',\n properties: { url: { type: 'string', description: 'The destination screen name.' } },\n required: ['url'], additionalProperties: false,\n },\n },\n {\n name: 'guideai_go_back',\n description: 'Go back one screen in the app navigation stack.',\n consequential: true,\n parameters: { type: 'object', properties: {}, additionalProperties: false },\n },\n];\n\n/**\n * How the registry reaches React Navigation.\n *\n * A ref the provider owns, so core never learns React Navigation exists\n * and this package does not depend on it either — the host wires it.\n */\nexport interface NavigationBridge {\n /** `url` is the scanner's native screen name (`ShareForm`), not the path. */\n navigate(screen: string): void | Promise<void>;\n goBack(): void | Promise<void>;\n canGoBack?(): boolean;\n}\n\nexport interface RNToolRegistryOptions {\n getRegistry: () => ScreenRegistry | undefined;\n navigation?: NavigationBridge;\n}\n\nfunction failure(error: string, errorType = 'action_failed'): ToolExecutionResult {\n return { success: false, error, error_type: errorType };\n}\n\nfunction stringArg(value: unknown): string | undefined {\n return typeof value === 'string' && value.trim() ? value : undefined;\n}\n\nexport class RNAgentToolRegistry implements ClientToolRegistry {\n /**\n * Serializes execution, matching the browser registry. On a phone this\n * matters as much as on the web: two taps dispatched concurrently can\n * interleave a navigation with a field fill and land the text on the\n * wrong screen.\n */\n private queue: Promise<unknown> = Promise.resolve();\n\n constructor(private readonly options: RNToolRegistryOptions) {}\n\n definitions(): ClientToolDefinition[] {\n const available = RN_AGENT_TOOL_DEFINITIONS.filter((tool) => {\n // Without a navigation bridge the app genuinely cannot navigate, so\n // the tools are withheld rather than advertised and then failed.\n if (tool.name === 'guideai_navigate' || tool.name === 'guideai_go_back') {\n return Boolean(this.options.navigation);\n }\n return true;\n });\n return available.map((tool) => ({ ...tool, parameters: { ...tool.parameters } }));\n }\n\n copilotDefinitions(): CopilotToolDef[] {\n return this.definitions().map((tool) => ({\n name: tool.name,\n description: tool.description,\n method: 'CLIENT',\n path: `${CLIENT_PATH_PREFIX}${tool.name}`,\n body_schema: tool.parameters,\n risk_level: tool.consequential ? 'write' : 'read',\n }));\n }\n\n canExecute(name: string): boolean {\n return this.definitions().some((tool) => tool.name === name);\n }\n\n canExecuteCall(toolCall: ToolCallResponse): boolean {\n return toolCall.method.toUpperCase() === 'CLIENT'\n && toolCall.path === `${CLIENT_PATH_PREFIX}${toolCall.tool}`\n && this.canExecute(toolCall.tool);\n }\n\n execute(toolCall: ToolCallResponse): Promise<ToolExecutionResult> {\n if (!this.canExecuteCall(toolCall)) {\n return Promise.resolve(failure('Native tool is not registered.', 'tool_not_found'));\n }\n const args = { ...(toolCall.params ?? {}), ...(toolCall.body ?? {}) };\n const run = async () => this.executeNow(toolCall.tool, args);\n const result = this.queue.then(run, run);\n this.queue = result.then(() => undefined, () => undefined);\n return result;\n }\n\n executeBatch(\n toolCalls: ToolCallResponse[],\n onComplete?: (toolCall: ToolCallResponse, result: ToolExecutionResult) => void,\n ): Promise<ToolExecutionResult[]> {\n return Promise.all(toolCalls.map(async (toolCall) => {\n const result = await this.execute(toolCall);\n onComplete?.(toolCall, result);\n return result;\n }));\n }\n\n private async fillOne(\n field: { fieldName?: string; selector?: string; value: string },\n ): Promise<{ field: string; filled: boolean; error?: string }> {\n const label = field.fieldName ?? field.selector ?? 'field';\n const registry = this.options.getRegistry();\n if (!registry) return { field: label, filled: false, error: 'no_active_screen' };\n\n const element = registry.find({ selector: field.selector, name: field.fieldName });\n if (!element) return { field: label, filled: false, error: 'element_not_found' };\n if (!element.handlers.setValue) {\n return { field: label, filled: false, error: 'element_not_fillable' };\n }\n await element.handlers.setValue(field.value);\n return { field: element.label, filled: true };\n }\n\n private async executeNow(name: string, args: Record<string, unknown>): Promise<ToolExecutionResult> {\n try {\n const registry = this.options.getRegistry();\n\n switch (name) {\n case 'guideai_fill_field': {\n const outcome = await this.fillOne({\n fieldName: stringArg(args.fieldName),\n selector: stringArg(args.selector),\n value: String(args.value ?? ''),\n });\n return { success: outcome.filled, data: outcome, error: outcome.error };\n }\n\n case 'guideai_fill_form': {\n const requested = Array.isArray(args.fields) ? args.fields.slice(0, 25) : [];\n const outcomes: Array<{ index: number; field: string; filled: boolean; error?: string }> = [];\n for (let index = 0; index < requested.length; index += 1) {\n const entry = requested[index] as Record<string, unknown> | undefined;\n if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {\n outcomes.push({ index, field: `field ${index + 1}`, filled: false, error: 'invalid_field' });\n continue;\n }\n if (entry.value == null) {\n outcomes.push({ index, field: `field ${index + 1}`, filled: false, error: 'value_required' });\n continue;\n }\n outcomes.push({ index, ...await this.fillOne({\n fieldName: stringArg(entry.fieldName),\n selector: stringArg(entry.selector),\n value: String(entry.value),\n }) });\n }\n const filledCount = outcomes.filter((outcome) => outcome.filled).length;\n return {\n // Matches the browser registry: the tool completed even when a\n // field was refused. The model needs per-field outcomes to\n // explain a partial result rather than a transport error.\n success: outcomes.length > 0,\n data: { success: outcomes.length > 0 && filledCount === outcomes.length, filledCount, outcomes },\n error: outcomes.length === 0 ? 'fields_required' : undefined,\n };\n }\n\n case 'guideai_click_element': {\n if (!registry) return failure('no_active_screen', 'element_not_found');\n const element = registry.find({ selector: stringArg(args.selector), name: stringArg(args.name) });\n if (!element) return failure('element_not_found', 'element_not_found');\n if (!element.handlers.press) return failure('element_not_pressable');\n await element.handlers.press();\n return { success: true, data: { pressed: element.label } };\n }\n\n case 'guideai_scroll_to_element': {\n if (!registry) return failure('no_active_screen', 'element_not_found');\n const element = registry.find({ selector: stringArg(args.selector), name: stringArg(args.name) });\n if (!element) return failure('element_not_found', 'element_not_found');\n if (!element.handlers.scrollTo) return failure('element_not_scrollable');\n await element.handlers.scrollTo();\n return { success: true, data: { scrolledTo: element.label } };\n }\n\n case 'guideai_navigate': {\n const destination = stringArg(args.url);\n if (!destination) return failure('url_required');\n if (!this.options.navigation) return failure('navigation_unavailable');\n await this.options.navigation.navigate(destination);\n return { success: true, data: { navigatedTo: destination } };\n }\n\n case 'guideai_go_back': {\n const navigation = this.options.navigation;\n if (!navigation) return failure('navigation_unavailable');\n if (navigation.canGoBack && !navigation.canGoBack()) {\n return failure('no_history');\n }\n await navigation.goBack();\n return { success: true, data: { wentBack: true } };\n }\n\n default:\n return failure(`Unknown native tool: ${name}`, 'tool_not_found');\n }\n } catch (error) {\n // Contract: execute() never throws — failures come back as results.\n return failure(error instanceof Error ? error.message : String(error));\n }\n }\n}\n","// ============================================================\n// @guideai/react-native — runtime assembly\n// ============================================================\n//\n// Wires the four platform pieces into the shape @guideai/core's agent\n// loop expects. This is the whole \"binding\" — there is no second agent\n// loop, no mobile-specific brain, and no protocol layer: core's harness\n// takes six injected dependencies and this supplies the ones that differ\n// on a device.\n//\n// Kept free of React so it can be tested in plain Node and reused by a\n// host that does not use hooks. The React layer sits on top of this.\n// ============================================================\n\nimport type {\n ClientToolRegistry,\n HttpTransport,\n KVStore,\n PageSnapshot,\n} from '@guideai/core';\nimport { RNHttpTransport } from './transport/rn-transport';\nimport { rnPlatform, type RNCredentialSource, type SyncNativeStore } from './platform/rn-platform';\nimport { HostAuth, HostAuthTransport, type HostAuthOptions } from './auth/host-auth';\nimport { ScreenRegistry, buildSnapshot } from './screen/screen-registry';\nimport { RNAgentToolRegistry, type NavigationBridge } from './tools/rn-tool-registry';\n\nexport interface ActiveScreen {\n /**\n * The route path as the mobile scanner emitted it (`/share`).\n * Grounding on the backend is exact string equality against this, so it\n * must be the scanner's `path` — never the native screen name.\n */\n path: string;\n title: string;\n registry: ScreenRegistry;\n}\n\nexport interface GuideAIRuntimeOptions {\n /** The registered site domain this app reports as its origin. */\n requestOrigin?: string;\n /** MMKV (or any sync store). Omitted → memory only, lost on relaunch. */\n store?: SyncNativeStore;\n /** How the tool registry reaches React Navigation. */\n navigation?: NavigationBridge;\n /** The host's own login, for tool calls to its API. */\n auth?: HostAuthOptions;\n}\n\n/**\n * Holds the mutable \"what screen am I on\" state and hands core the four\n * seams it needs. One instance per app.\n */\nexport class GuideAIRuntime {\n readonly transport: HttpTransport;\n readonly storage: KVStore;\n readonly credentials: RNCredentialSource;\n readonly tools: ClientToolRegistry;\n readonly auth: HostAuth;\n\n private active: ActiveScreen | undefined;\n\n constructor(options: GuideAIRuntimeOptions = {}) {\n const platform = rnPlatform({ store: options.store });\n this.storage = platform.storage;\n this.credentials = platform.credentials;\n this.auth = new HostAuth(this.credentials, options.auth);\n // Tool calls reach the site's API through this; an expired host token\n // is refreshed once and the call repeated.\n this.transport = new HostAuthTransport(\n new RNHttpTransport({ requestOrigin: options.requestOrigin }),\n this.auth,\n );\n this.tools = new RNAgentToolRegistry({\n getRegistry: () => this.active?.registry,\n navigation: options.navigation,\n });\n }\n\n /**\n * Called on every navigation. Replacing the whole screen (rather than\n * merging) is deliberate: elements from the previous screen are gone,\n * and leaving them visible to the model would have it offer controls\n * that are no longer mounted.\n */\n setActiveScreen(screen: ActiveScreen | undefined): void {\n this.active = screen;\n }\n\n getActiveScreen(): ActiveScreen | undefined {\n return this.active;\n }\n\n /**\n * What the host passes as the page snapshot on each turn.\n *\n * Returns undefined when no screen is registered — not an empty\n * snapshot. The difference matters: an empty snapshot asserts \"this\n * screen has no controls\", while undefined says \"nothing was declared\",\n * and only the second lets the backend fall back to route-level\n * guidance rather than concluding the screen is bare.\n */\n captureSnapshot(): PageSnapshot | undefined {\n if (!this.active) return undefined;\n return buildSnapshot({\n path: this.active.path,\n title: this.active.title,\n registry: this.active.registry,\n });\n }\n}\n","// ============================================================\n// @guideai/react-native — API client\n// ============================================================\n//\n// Implements core's CopilotApi over RNHttpTransport. The harness takes a\n// CopilotApi, and the web SDK's ApiClient is browser-bound (FormData,\n// keepalive, document.cookie), so mobile needs its own.\n//\n// Deliberately NOT a port of the SDK's client. It implements the four\n// members the harness actually calls and nothing else — no telemetry\n// beacons, no multipart upload, no unload handling, none of which exist\n// on a device.\n// ============================================================\n\nimport type { CopilotApi, HttpResponse } from '@guideai/core';\nimport { AuthError, PlanAccessError, UsageLimitError } from '@guideai/core';\nimport { REQUEST_ORIGIN_HEADER, RNHttpTransport } from '../transport/rn-transport';\n\nexport interface RNApiClientOptions {\n /** Backend origin, e.g. `https://cdn.3guideai.com` (no trailing slash). */\n baseUrl: string;\n /** The site's public token. */\n publicKey: string;\n siteId?: string;\n /**\n * The registered site domain this app reports as its origin. Required\n * in practice: the backend's origin guard rejects a request it cannot\n * attribute to an allowed domain, and a native app sends no Origin.\n */\n requestOrigin?: string;\n /**\n * Which of the site's agents (Agent Blueprints) answers, by its key.\n * Omitted: the site's main one.\n */\n agent?: string;\n /** Default time budget for JSON calls. Streaming calls are not budgeted. */\n timeoutMs?: number;\n}\n\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\n/**\n * Map a failing status onto the structured errors the chat layer checks\n * with `instanceof`. Without this, a plan limit renders as a generic\n * \"request failed\" and the visitor is told nothing actionable.\n */\nasync function raiseForStatus(response: HttpResponse, path: string): Promise<void> {\n if (response.status >= 200 && response.status < 300) return;\n\n if (response.status === 401 || response.status === 403) {\n throw new AuthError(response.status);\n }\n\n // The 402/429 bodies carry the detail the UI needs; a body that is not\n // JSON must not mask the status, so parsing failure falls through.\n let detail: Record<string, unknown> = {};\n try {\n detail = (await response.json<Record<string, unknown>>()) ?? {};\n } catch {\n detail = {};\n }\n\n if (response.status === 402) {\n throw new PlanAccessError({\n feature: String(detail.feature ?? 'assistant'),\n plan: String(detail.plan ?? 'unknown'),\n message: typeof detail.message === 'string' ? detail.message : undefined,\n });\n }\n\n if (response.status === 429) {\n throw new UsageLimitError({\n message: typeof detail.message === 'string' ? detail.message : 'Usage limit reached',\n limit_type: String(detail.limit_type ?? 'requests'),\n plan: String(detail.plan ?? 'unknown'),\n });\n }\n\n throw new Error(`HTTP ${response.status} for ${path}`);\n}\n\n/** A refused upload, with the server's own words when it gave any. */\nexport class UploadError extends Error {\n constructor(readonly status: number, message: string) {\n super(message);\n this.name = 'UploadError';\n }\n}\n\nexport class RNApiClient implements CopilotApi {\n private readonly transport: RNHttpTransport;\n private readonly baseUrl: string;\n private readonly timeoutMs: number;\n\n constructor(private readonly options: RNApiClientOptions) {\n this.transport = new RNHttpTransport({ requestOrigin: options.requestOrigin });\n this.baseUrl = options.baseUrl.replace(/\\/+$/, '');\n this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n }\n\n get supportsStreaming(): boolean {\n return this.transport.supportsStreaming;\n }\n\n private headers(): Record<string, string> {\n const headers: Record<string, string> = {\n 'content-type': 'application/json',\n 'x-api-key': this.options.publicKey,\n };\n if (this.options.siteId) headers['x-site-id'] = this.options.siteId;\n if (this.options.agent) headers['x-guideai-agent'] = this.options.agent;\n return headers;\n }\n\n private url(path: string): string {\n return path.startsWith('http')\n ? path\n : `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n }\n\n /**\n * Time budget for JSON calls.\n *\n * Core's AbortSignal is declared with only `aborted` — it never\n * originates cancellation — so the deadline is enforced here by racing\n * rather than by constructing a controller inside the loop.\n */\n private async withTimeout<T>(work: Promise<T>, timeoutMs: number, path: string): Promise<T> {\n // ReturnType<typeof setTimeout>, not number: under Node's types this\n // is a Timeout object, and hardcoding number breaks any consumer\n // whose tsconfig pulls in @types/node (Expo's does).\n let timer: ReturnType<typeof setTimeout> | undefined;\n try {\n return await Promise.race([\n work,\n new Promise<never>((_, reject) => {\n timer = setTimeout(\n () => reject(new Error(`Request timed out after ${timeoutMs}ms: ${path}`)),\n timeoutMs,\n );\n }),\n ]);\n } finally {\n if (timer !== undefined) clearTimeout(timer);\n }\n }\n\n async get<T>(path: string, timeoutMs?: number): Promise<T> {\n const request = async (): Promise<T> => {\n const response = await this.transport.request({\n url: this.url(path), method: 'GET', headers: this.headers(),\n });\n await raiseForStatus(response, path);\n return response.json<T>();\n };\n return this.withTimeout(request(), timeoutMs ?? this.timeoutMs, path);\n }\n\n async post<T>(path: string, body?: unknown, timeoutMs?: number): Promise<T> {\n const request = async (): Promise<T> => {\n const response = await this.transport.request({\n url: this.url(path), method: 'POST', headers: this.headers(),\n body: JSON.stringify(body ?? {}),\n });\n await raiseForStatus(response, path);\n return response.json<T>();\n };\n return this.withTimeout(request(), timeoutMs ?? this.timeoutMs, path);\n }\n\n /**\n * Upload a file the device already wrote (a voice recording) as\n * multipart. XHR rather than fetch: it is the transport this package\n * already trusts on RN, and it takes RN's `{ uri, name, type }` parts.\n */\n async postFile<T>(\n path: string,\n field: string,\n file: { uri: string; name: string; type: string },\n fields: Record<string, string> = {},\n timeoutMs?: number,\n ): Promise<T> {\n const form = new FormData();\n form.append(field, file);\n for (const [name, value] of Object.entries(fields)) form.append(name, value);\n const headers = this.headers();\n delete headers['content-type']; // XHR writes the multipart boundary\n if (this.options.requestOrigin) headers[REQUEST_ORIGIN_HEADER] = this.options.requestOrigin;\n\n const request = new Promise<{ status: number; body: string }>((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n xhr.open('POST', this.url(path));\n for (const [name, value] of Object.entries(headers)) xhr.setRequestHeader(name, value);\n xhr.onreadystatechange = () => {\n if (xhr.readyState === 4) resolve({ status: xhr.status, body: xhr.responseText });\n };\n xhr.onerror = () => reject(new Error(`Network error for ${path}`));\n xhr.send(form);\n });\n const { status, body } = await this.withTimeout(request, timeoutMs ?? this.timeoutMs, path);\n let parsed: unknown = null;\n try { parsed = body ? JSON.parse(body) : null; } catch { parsed = null; }\n if (status < 200 || status >= 300) {\n const detail = (parsed as { detail?: unknown } | null)?.detail;\n throw new UploadError(status, typeof detail === 'string' ? detail : `HTTP ${status} for ${path}`);\n }\n return parsed as T;\n }\n\n async postRaw(path: string, body?: unknown, signal?: AbortSignal): Promise<HttpResponse> {\n // Not time-budgeted: a stream is long-lived by design, and the caller\n // owns cancellation through `signal`.\n const response = await this.transport.request({\n url: this.url(path), method: 'POST', headers: this.headers(),\n body: JSON.stringify(body ?? {}), signal,\n });\n await raiseForStatus(response, path);\n return response;\n }\n}\n","// ============================================================\n// @guideai/react-native — React context\n// ============================================================\n//\n// Two separate contexts, deliberately.\n//\n// The RUNTIME is app-wide and never changes identity, so it can also be\n// set once as a module singleton — a host that does not want to wrap its\n// app in a provider calls configureGuideAI() instead.\n//\n// The REGISTRY is per-screen. That is the one thing a singleton cannot\n// express: a button three components deep needs to reach *its* screen's\n// registry, and which screen that is depends on position in the tree.\n// This is what context is genuinely for.\n// ============================================================\n\nimport { createContext, useContext } from 'react';\nimport type { GuideAIRuntime } from '../runtime';\nimport type { ScreenRegistry } from '../screen/screen-registry';\n\nexport const RuntimeContext = createContext<GuideAIRuntime | undefined>(undefined);\nexport const RegistryContext = createContext<ScreenRegistry | undefined>(undefined);\n\n/**\n * The module-level runtime, for hosts that skip the provider.\n *\n * A plain class with no React state, so nothing re-renders when it is\n * set — which is why a singleton is safe here and would not be for\n * anything the UI reads reactively.\n */\nlet moduleRuntime: GuideAIRuntime | undefined;\n\n/**\n * Install the app-wide runtime without a provider.\n *\n * Call once at startup. Deliberately a function rather than a\n * module-construction side effect: `requestOrigin` often comes from an\n * env fetch, and a constructor at import time cannot wait for it.\n */\nexport function configureGuideAI(runtime: GuideAIRuntime): void {\n moduleRuntime = runtime;\n}\n\n/** Test seam. Clears the singleton so suites do not leak state between cases. */\nexport function resetGuideAIRuntime(): void {\n moduleRuntime = undefined;\n}\n\nexport function getConfiguredRuntime(): GuideAIRuntime | undefined {\n return moduleRuntime;\n}\n\n/**\n * The active runtime: provider first, module singleton second.\n *\n * Throws rather than returning undefined. A missing runtime means the\n * host forgot to configure the SDK, and every downstream symptom of that\n * (no snapshot, no tools, silent no-ops) is far harder to diagnose than\n * one explicit error at the point of use.\n */\nexport function useGuideAIRuntime(): GuideAIRuntime {\n const fromContext = useContext(RuntimeContext);\n const runtime = fromContext ?? moduleRuntime;\n if (!runtime) {\n throw new Error(\n 'GuideAI is not configured. Call configureGuideAI(new GuideAIRuntime({...})) '\n + 'at app startup, or wrap your app in <GuideAIProvider>.',\n );\n }\n return runtime;\n}\n\n/** The enclosing screen's registry, or undefined outside a <GuideAIScreen>. */\nexport function useScreenRegistry(): ScreenRegistry | undefined {\n return useContext(RegistryContext);\n}\n","// ============================================================\n// @guideai/react-native — navigation bridge\n// ============================================================\n//\n// Adapts a React Navigation ref to the NavigationBridge the tool\n// registry expects.\n//\n// This package does NOT depend on @react-navigation/native. The ref is\n// accepted structurally, so a host on any navigator — or none — can\n// supply an equivalent, and core never learns which router is in use.\n// ============================================================\n\nimport type { NavigationBridge } from '../tools/rn-tool-registry';\n\n/** The slice of React Navigation's ref this needs. */\nexport interface NavigationRefLike {\n isReady?(): boolean;\n navigate(screen: string, params?: object): void;\n goBack(): void;\n canGoBack?(): boolean;\n}\n\n/**\n * Wrap a navigation ref (or a getter for one) as a NavigationBridge.\n *\n * Accepts a getter as well as the ref itself because\n * `createNavigationContainerRef()` is populated asynchronously — reading\n * `.current` at module scope captures null and every navigation silently\n * no-ops.\n */\nexport function navigationBridge(\n source: NavigationRefLike | (() => NavigationRefLike | null | undefined),\n): NavigationBridge {\n const resolve = (): NavigationRefLike | null | undefined =>\n (typeof source === 'function' ? source() : source);\n\n return {\n navigate(screen: string) {\n const ref = resolve();\n // isReady() is false until the container mounts. Navigating then\n // is dropped by React Navigation without an error, so surface it\n // as a failed tool result instead.\n if (!ref || (ref.isReady && !ref.isReady())) {\n throw new Error('navigation_not_ready');\n }\n ref.navigate(screen);\n },\n\n goBack() {\n const ref = resolve();\n if (!ref || (ref.isReady && !ref.isReady())) {\n throw new Error('navigation_not_ready');\n }\n ref.goBack();\n },\n\n canGoBack() {\n const ref = resolve();\n if (!ref || (ref.isReady && !ref.isReady())) return false;\n // A ref without canGoBack cannot prove there is history; assume\n // there is and let goBack() be a no-op rather than blocking it.\n return ref.canGoBack ? ref.canGoBack() : true;\n },\n };\n}\n","// ============================================================\n// @guideai/react-native — full conversation trace\n// ============================================================\n//\n// Logs EVERYTHING, unabridged: every message you type, every request\n// body sent, every streamed frame received, every tool call planned and\n// executed, and every answer.\n//\n// Separate from debug-logger.ts, which is a deliberately terse summary.\n// This one hides nothing, because a truncated log is exactly how a real\n// failure stays invisible — the previous \"HTTP 0\" and \"split of null\"\n// bugs were both diagnosed only after the missing detail was added.\n//\n// Output goes to console.log, which Expo prints in the terminal running\n// `expo start`. Redirect that terminal to a file to keep a transcript.\n// ============================================================\n\nimport type {\n ClientToolRegistry,\n CopilotApi,\n HttpRequest,\n HttpResponse,\n HttpTransport,\n TextStream,\n ToolCallResponse,\n ToolExecutionResult,\n} from '@guideai/core';\n\n/** Wall-clock stamp so entries can be correlated with the server's logs. */\nconst stamp = () => new Date().toISOString().slice(11, 23);\n\nlet seq = 0;\nconst next = () => (seq += 1);\n\nfunction dump(label: string, value: unknown): void {\n let text: string;\n try {\n text = typeof value === 'string' ? value : JSON.stringify(value, null, 2);\n } catch {\n text = String(value);\n }\n // No truncation. The point of this module is that nothing is hidden.\n console.log(`${label}\\n${text}`);\n}\n\nfunction line(icon: string, text: string): void {\n console.log(`[${stamp()}] ${icon} ${text}`);\n}\n\n/**\n * Wrap CopilotApi to trace GuideAI's own endpoints in full.\n *\n * Logs the complete request body — including `page_snapshot`, the full\n * `client_tools` list and `conversation_history` — and every streamed\n * SSE frame as it arrives.\n */\nexport function traceApi(api: CopilotApi): CopilotApi {\n return {\n supportsStreaming: api.supportsStreaming,\n\n async get<T>(path: string, timeoutMs?: number): Promise<T> {\n const id = next();\n line('→', `#${id} GET ${path}`);\n try {\n const result = await api.get<T>(path, timeoutMs);\n dump(`[${stamp()}] ← #${id} GET ${path} responded:`, result);\n return result;\n } catch (error) {\n line('✗', `#${id} GET ${path} FAILED: ${describeError(error)}`);\n throw error;\n }\n },\n\n async post<T>(path: string, body?: unknown, timeoutMs?: number): Promise<T> {\n const id = next();\n dump(`[${stamp()}] → #${id} POST ${path} body:`, body);\n try {\n const result = await api.post<T>(path, body, timeoutMs);\n dump(`[${stamp()}] ← #${id} POST ${path} responded:`, result);\n return result;\n } catch (error) {\n line('✗', `#${id} POST ${path} FAILED: ${describeError(error)}`);\n throw error;\n }\n },\n\n async postRaw(path: string, body?: unknown, signal?: AbortSignal): Promise<HttpResponse> {\n const id = next();\n dump(`[${stamp()}] → #${id} POST ${path} body:`, body);\n\n try {\n const response = await api.postRaw(path, body, signal);\n line('←', `#${id} ${response.status} ${path}`\n + ` (${response.headers.get('content-type') ?? 'no content-type'})`);\n\n // Tee the stream so every SSE frame is logged without consuming\n // it — the harness still receives every chunk in order.\n if (response.stream) {\n return { ...response, stream: teeStream(response.stream, id) };\n }\n return response;\n } catch (error) {\n line('✗', `#${id} POST ${path} FAILED: ${describeError(error)}`);\n throw error;\n }\n },\n };\n}\n\n/**\n * Log each SSE frame while passing it through untouched.\n *\n * Buffers on the frame delimiter rather than logging raw chunks: a chunk\n * boundary falls mid-frame, and half a frame in a log is unreadable.\n */\nfunction teeStream(stream: TextStream, id: number): TextStream {\n return {\n async *[Symbol.asyncIterator]() {\n let buffer = '';\n for await (const chunk of stream) {\n buffer += chunk;\n const parts = buffer.split('\\n\\n');\n buffer = parts.pop() ?? '';\n for (const frame of parts) {\n const trimmed = frame.trim();\n if (trimmed) console.log(`[${stamp()}] ⋯ #${id} frame: ${trimmed}`);\n }\n yield chunk;\n }\n if (buffer.trim()) console.log(`[${stamp()}] ⋯ #${id} frame: ${buffer.trim()}`);\n line('⋯', `#${id} stream ended`);\n },\n };\n}\n\n/**\n * Wrap the transport to trace CUSTOMER API calls — the ones a tool call\n * actually makes. These do not go through CopilotApi, so without this\n * they are invisible.\n */\nexport function traceTransport(transport: HttpTransport): HttpTransport {\n return {\n supportsStreaming: transport.supportsStreaming,\n async request(req: HttpRequest): Promise<HttpResponse> {\n const id = next();\n console.log(\n `[${stamp()}] ⇢ #${id} ${req.method} ${req.url}\\n`\n + ` headers: ${JSON.stringify(req.headers)}\\n`\n + ` body: ${req.body ?? '(none)'}`,\n );\n try {\n const response = await transport.request(req);\n line('⇠', `#${id} ${response.status} ${req.url}`);\n return response;\n } catch (error) {\n // Name the URL: \"cannot resolve host\" is unactionable without it,\n // and a device reports exactly that as a bare HTTP 0.\n line('✗', `#${id} ${req.method} ${req.url} FAILED: ${describeError(error)}`);\n throw error;\n }\n },\n };\n}\n\n/** Wrap the tool registry to trace every planned and executed call. */\nexport function traceTools(tools: ClientToolRegistry): ClientToolRegistry {\n const report = (call: ToolCallResponse, result: ToolExecutionResult) => {\n if (result.success) {\n dump(`[${stamp()}] ✓ executed ${call.tool} →`, result.data);\n } else {\n line('✗', `${call.tool} FAILED: ${result.error ?? 'unknown'}`\n + `${result.error_type ? ` (${result.error_type})` : ''}`);\n }\n };\n\n return {\n definitions: () => tools.definitions(),\n copilotDefinitions: () => tools.copilotDefinitions(),\n canExecute: (name: string) => tools.canExecute(name),\n canExecuteCall: (call: ToolCallResponse) => tools.canExecuteCall(call),\n\n execute: async (call: ToolCallResponse) => {\n dump(`[${stamp()}] ⚙ TOOL CALL ${call.tool} (${call.method} ${call.path}):`, {\n params: call.params, body: call.body, requires_confirm: call.requires_confirm,\n });\n const result = await tools.execute(call);\n report(call, result);\n return result;\n },\n\n executeBatch: async (calls: ToolCallResponse[], onComplete?) => {\n line('⚙', `BATCH of ${calls.length}: ${calls.map((c) => c.tool).join(', ')}`);\n return tools.executeBatch(calls, (call, result) => {\n report(call, result);\n onComplete?.(call, result);\n });\n },\n\n destroy: tools.destroy ? () => tools.destroy?.() : undefined,\n };\n}\n\nfunction describeError(error: unknown): string {\n if (error instanceof Error) {\n return `${error.name}: ${error.message}`;\n }\n return String(error);\n}\n\n/** Log a message you typed, so the transcript reads as a conversation. */\nexport function traceUserMessage(text: string): void {\n console.log(`\\n${'═'.repeat(60)}`);\n console.log(`[${stamp()}] 👤 YOU: ${text}`);\n console.log('═'.repeat(60));\n}\n\n/** Log the assistant's final answer, in full. */\nexport function traceAssistantAnswer(text: string, mode?: string): void {\n console.log(`[${stamp()}] 🤖 ASSISTANT${mode ? ` (${mode})` : ''}:\\n${text}`);\n console.log(`${'═'.repeat(60)}\\n`);\n}\n","// ============================================================\n// @guideai/react-native — the whole public setup surface\n// ============================================================\n//\n// A host app should own exactly two things: its UI, and the message the\n// user typed. Everything else — the agent loop, the transport, the tool\n// registry, the snapshot format, the wire protocol — is ours, so that\n// upgrading the agent is a package bump and never a migration.\n//\n// That is why this file exists. Before it, wiring the SDK meant\n// constructing RNApiClient, GuideAIRuntime and CopilotHarness by hand\n// and passing six positional arguments in the right order. Every one of\n// those is an internal class, so any change to them was a breaking\n// change for every customer.\n//\n// Now:\n//\n// GuideAI.configure({ apiKey: 'pk_live_…', origin: 'https://app.acme.com' });\n// <GuideAIChat />\n//\n// Nothing else is required, and nothing internal is named.\n// ============================================================\n\nimport { CopilotHarness } from '@guideai/core';\nimport { GuideAIRuntime } from './runtime';\nimport { RNApiClient } from './api/rn-api-client';\nimport { configureGuideAI } from './react/context';\nimport { navigationBridge, type NavigationRefLike } from './react/navigation';\nimport { traceApi, traceTools, traceTransport } from './debug/full-trace';\nimport type { SyncNativeStore } from './platform/rn-platform';\nimport type { AuthTokenGetter, RequestHeadersGetter } from './auth/host-auth';\n\nexport interface GuideAIConfig {\n /**\n * The site's public token, from the dashboard. The only required\n * field: it identifies the site, and everything else about that site\n * — its tools, knowledge base, model and prompts — is resolved\n * server-side from it.\n */\n apiKey: string;\n /** The application's stable user ID after login, if already known. */\n visitorId?: string;\n\n /**\n * A domain registered on this site (its own domain, or one of its\n * `domain_aliases`).\n *\n * Required in practice, and it cannot be fetched from the dashboard:\n * the backend uses it to AUTHORIZE the request, so it must accompany\n * the very first call. A native app sends no `Origin` header, so this\n * travels as `x-guideai-request-origin`. Set it once; a mismatch is\n * answered with 403.\n */\n origin?: string;\n\n /**\n * Where the GuideAI backend lives. Defaults to production; set it to test\n * against a local or staging GuideAI (`http://localhost:8000`).\n */\n baseUrl?: string;\n\n /**\n * How the assistant navigates between screens — a React Navigation\n * ref, or a getter for one. Omitted, the assistant simply does not\n * offer navigation rather than offering it and failing.\n */\n navigation?: NavigationRefLike | (() => NavigationRefLike | null | undefined);\n\n /**\n * A synchronous key/value store (MMKV, or `expo-sqlite/kv-store`) for\n * conversation history across launches. Omitted, history is kept in\n * memory and resets on relaunch.\n */\n store?: SyncNativeStore;\n\n /** The assistant's visible name in its own replies. */\n assistantName?: string;\n\n /**\n * Which of the site's agents (Agent Blueprints) answers, by its key —\n * e.g. 'tutor'. Omitted: the site's main agent.\n */\n agent?: string;\n\n /**\n * The signed-in user's token for the site's own API. Read before every\n * turn; may be async (AsyncStorage, SecureStore). Return null when\n * signed out. The assistant then calls your endpoints as that user,\n * from the device.\n */\n getAuthToken?: AuthTokenGetter;\n\n /**\n * Called once when your API answers 401 to a tool call. Refresh the\n * session and return the new token; the call is repeated with it.\n * Omitted: getAuthToken() is asked again.\n */\n onAuthExpired?: AuthTokenGetter;\n\n /**\n * Values for the headers your toolset declares in Copilot Tools as\n * \"Supplied by your app\" — a device id, a tenant: `async () => ({\n * 'device-id': id })`. Read before every turn; sent only to your API,\n * never to GuideAI, and only for declared names.\n */\n getRequestHeaders?: RequestHeadersGetter;\n\n /**\n * Values the app knows right now — the course open on screen: `{ course:\n * '42' }`. Each fills the API inputs of the same name, or those the\n * toolset declares it fills (Copilot Tools → \"Supplied by your app\").\n * Change it with GuideAI.setToolContext().\n */\n toolContext?: Record<string, string>;\n\n /**\n * Facts about the signed-in user for the agent to pitch answers to —\n * `{ age: 12, class: 'JSS1', country: 'Nigeria', language: 'en' }`.\n * Sent with every turn; change it with GuideAI.setUserProfile().\n */\n userProfile?: GuideAIUserProfile;\n\n /**\n * Log the full conversation and every request. Development only —\n * it prints message content to the console.\n */\n debug?: boolean;\n}\n\n/** Short facts about the user: strings, numbers, booleans or short lists of them. */\nexport type GuideAIUserProfile = Record<string, string | number | boolean | Array<string | number>>;\n\n/** Production, the same host the web SDK uses. */\nconst DEFAULT_BASE_URL = 'https://cdn.3guideai.com';\n\ninterface Configured {\n runtime: GuideAIRuntime;\n harness: CopilotHarness;\n /**\n * The plain API client, for UI that needs to ask the backend something\n * outside a conversation — whether a human is reachable, for one.\n * Untraced: these are status probes, not agent traffic.\n */\n api: RNApiClient;\n debug: boolean;\n visitorId: string | null;\n profile: GuideAIUserProfile | null;\n}\n\nlet configured: Configured | null = null;\n\n/**\n * Configure the assistant. Call once, at app startup.\n *\n * Deliberately a function rather than a provider or a module constant:\n * `apiKey` often arrives from a remote config or an env fetch, and a\n * constructor running at import time cannot wait for it.\n */\nfunction configure(config: GuideAIConfig): void {\n const debug = config.debug ?? false;\n\n const baseApi = new RNApiClient({\n baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,\n publicKey: config.apiKey,\n requestOrigin: config.origin,\n agent: config.agent?.trim() || undefined,\n });\n\n const runtime = new GuideAIRuntime({\n requestOrigin: config.origin,\n store: config.store,\n auth: {\n getAuthToken: config.getAuthToken,\n onAuthExpired: config.onAuthExpired,\n getRequestHeaders: config.getRequestHeaders,\n },\n navigation: config.navigation\n ? navigationBridge(\n typeof config.navigation === 'function'\n ? config.navigation\n : () => config.navigation as NavigationRefLike,\n )\n : undefined,\n });\n\n // Registers the runtime app-wide, so <GuideAIScreen> and the hooks\n // resolve it without the host wrapping their app in a provider.\n configureGuideAI(runtime);\n\n const harness = new CopilotHarness(\n debug ? traceApi(baseApi) : baseApi,\n {\n // The site id is resolved server-side from the API key; the\n // harness only needs a stable string to key its local memory.\n // Per agent too: a tutor and a course builder in one app must not\n // read each other's conversation.\n siteId: config.agent?.trim() ? `${config.apiKey}:${config.agent.trim()}` : config.apiKey,\n assistantLabel: config.assistantName,\n },\n debug ? traceTools(runtime.tools) : runtime.tools,\n runtime.storage,\n debug ? traceTransport(runtime.transport) : runtime.transport,\n runtime.credentials,\n );\n\n if (config.toolContext) harness.setToolContext(config.toolContext);\n\n configured = {\n runtime, harness, api: baseApi, debug,\n visitorId: config.visitorId ?? null,\n profile: config.userProfile ?? null,\n };\n}\n\n/** Call after login so chat and the checklist use the same person. */\nfunction identify(userId: string | null): void {\n if (!configured) throw new Error('Call GuideAI.configure() before identify().');\n configured.visitorId = userId?.trim() || null;\n}\n\n/** Replace the tool inputs the app knows now (e.g. when another course opens). */\nfunction setToolContext(values: Record<string, string> | null): void {\n if (!configured) throw new Error('Call GuideAI.configure() before setToolContext().');\n configured.harness.setToolContext(values ?? undefined);\n}\n\n/** Replace what the agent knows about the user (after login, or a profile edit). */\nfunction setUserProfile(profile: GuideAIUserProfile | null): void {\n if (!configured) throw new Error('Call GuideAI.configure() before setUserProfile().');\n configured.profile = profile;\n}\n\n/**\n * The configured instance, for the UI components.\n *\n * Throws rather than returning null: a missing configuration otherwise\n * surfaces as an assistant that silently answers nothing, which is far\n * harder to diagnose than one explicit error at startup.\n */\nexport function requireConfigured(): Configured {\n if (!configured) {\n throw new Error(\n 'GuideAI is not configured. Call GuideAI.configure({ apiKey, origin }) '\n + 'at app startup, before rendering <GuideAIChat />.',\n );\n }\n return configured;\n}\n\n/** Whether configure() has run. Lets a host render its own fallback. */\nfunction isConfigured(): boolean {\n return configured !== null;\n}\n\n/** Test seam, and for hosts that switch sites at runtime. */\nfunction reset(): void {\n configured = null;\n}\n\nexport const GuideAI = { configure, identify, setUserProfile, setToolContext, isConfigured, reset };\n","// ============================================================\n// @guideai/react-native — chat hook\n// ============================================================\n//\n// One hook serving both UI requirements: it returns the conversation and\n// a send function, so the default <GuideAIChat> is built on exactly this\n// and a host's own UI gets the same thing. There is no privileged path\n// for the built-in UI — if this hook cannot express a UI, the default\n// one could not have been built either.\n// ============================================================\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport { describeToolCall, EP, headlessImages } from '@guideai/core';\nimport type { CopilotHarness, HarnessResponse, HeadlessImage, ToolCallResponse } from '@guideai/core';\nimport { useGuideAIRuntime } from './context';\nimport { traceAssistantAnswer, traceUserMessage } from '../debug/full-trace';\nimport { requireConfigured } from '../setup';\nimport { UploadError } from '../api/rn-api-client';\n\nexport interface GuideAIMessage {\n id: string;\n role: 'user' | 'assistant';\n content: string;\n /** True while tokens are still arriving for this message. */\n streaming?: boolean;\n error?: boolean;\n /**\n * Follow-up suggestions to draw as chips. A tap is just\n * `send(suggestion)`.\n */\n suggestions?: string[];\n /**\n * Pictures the agent drew. They are also in `content` as Markdown\n * images where they belong; use this list to place them yourself.\n */\n images?: HeadlessImage[];\n /**\n * A change to the user's data waiting for their yes (\"Create the\n * course\"). Draw Approve / Cancel and call `confirm(id)` or `cancel(id)`.\n */\n pendingAction?: { label: string; tool: string };\n}\n\n/** A recording the host made (expo-audio or any recorder) and saved to a file. */\nexport interface GuideAIVoiceRecording {\n /** `file://…` URI of the finished recording. */\n uri: string;\n /** e.g. 'audio/m4a'. Omitted: guessed from the file extension. */\n mimeType?: string;\n /** BCP-47 language the user spoke, e.g. 'en-NG'. Defaults to 'en-US'. */\n languageCode?: string;\n}\n\nexport interface GuideAIVoiceResult {\n /** What was heard, already sent as the next message; null when nothing was. */\n text: string | null;\n /** Why nothing was sent, in words to show the user. */\n error?: string;\n}\n\n/**\n * Mobile runs in assistant mode only.\n *\n * Guidance mode drives on-screen walkthroughs — highlight this, scroll to\n * that — which the browser SDK performs against live DOM nodes. On a\n * device there is no DOM to drive, no hover, and no highlight overlay\n * (`guideai_highlight_element` is not among the tools this package\n * advertises), so a guidance turn would come back with steps nothing can\n * execute.\n *\n * The backend picks a different prompt template per mode\n * (COPILOT_ACTION_PROMPT_GUIDANCE vs _ASSISTANT), so this is not a\n * cosmetic label — sending 'guidance' asks the model to plan the wrong\n * kind of answer. Narrowed to one value rather than defaulted, so a\n * guidance turn cannot be requested by mistake.\n */\nexport type GuideAIChatMode = 'assistant';\n\n/**\n * Everything a custom chat UI needs, and nothing about how to draw it.\n *\n * A host owns two things — their design, and the text the user typed.\n * This hook owns the rest: the conversation, the streaming, the tool\n * calls, the transport. That split is what lets the agent be upgraded\n * underneath a host's UI without them changing a line.\n */\nexport interface UseGuideAIResult {\n /** The conversation so far, oldest first. */\n messages: GuideAIMessage[];\n\n /**\n * The current input text, and its setter.\n *\n * Managed here so a host binds it straight to their TextInput rather\n * than reimplementing \"clear on send, block while busy\" — the two\n * rules every chat UI needs and each one gets subtly wrong.\n */\n input: string;\n setInput(text: string): void;\n\n /**\n * Send `input`, or an explicit string.\n *\n * Ignores empty text and calls made while a turn is in flight, so a\n * host does not have to guard their send button.\n */\n send(question?: string): Promise<void>;\n\n /** True from send() until the turn settles. Drives a disabled state. */\n busy: boolean;\n\n /** Whether `send()` would do anything right now. */\n canSend: boolean;\n\n /** Status text from the agent loop (\"Thinking\", \"Reading cart\"). */\n status: string | null;\n\n /** Re-send the last user message. Useful after a failed turn. */\n retry(): Promise<void>;\n\n /** Drop the conversation and start over. */\n clear(): void;\n\n /** Approve the message's `pendingAction`; the agent carries it out. */\n confirm(messageId: string): Promise<void>;\n\n /** Decline the message's `pendingAction`. Nothing is changed. */\n cancel(messageId: string): void;\n\n /**\n * Push-to-talk: transcribe a recording and send it as the next message.\n * Recording is the host's (no native module here); this uploads it.\n */\n sendVoice(recording: GuideAIVoiceRecording): Promise<GuideAIVoiceResult>;\n\n /** True while a recording is being transcribed. */\n transcribing: boolean;\n}\n\nexport interface UseGuideAIOptions {\n /**\n * Log the full conversation and every request. Defaults to whatever\n * GuideAI.configure({ debug }) was given, so a host sets it once.\n */\n trace?: boolean;\n\n /**\n * The harness, for tests and for a host running two assistants at\n * once. Omitted — the normal case — it comes from GuideAI.configure(),\n * so a host never holds a reference to it.\n */\n harness?: CopilotHarness;\n}\n\nconst AUDIO_TYPES: Record<string, string> = {\n m4a: 'audio/m4a', mp4: 'audio/mp4', aac: 'audio/aac', caf: 'audio/x-caf',\n wav: 'audio/wav', mp3: 'audio/mpeg', webm: 'audio/webm', ogg: 'audio/ogg', '3gp': 'audio/3gpp',\n};\n\nfunction audioType(uri: string): { type: string; name: string } {\n const extension = (uri.split('?')[0].split('.').pop() ?? '').toLowerCase();\n const type = AUDIO_TYPES[extension] ?? 'audio/m4a';\n return { type, name: `voice.${AUDIO_TYPES[extension] ? extension : 'm4a'}` };\n}\n\n/** The fields a finished turn sets on its reply. */\nfunction replyFields(\n result: HarnessResponse,\n streamed: string,\n): Pick<GuideAIMessage, 'content' | 'images' | 'suggestions' | 'pendingAction'> {\n const { text, images } = headlessImages(answerText(result, streamed));\n const call = result.confirmationNeeded ? result.pendingToolCall : undefined;\n return {\n content: text,\n images: images.length ? images : undefined,\n suggestions: result.quickReplies?.length ? result.quickReplies.slice(0, 4) : undefined,\n pendingAction: call ? { label: call.label?.trim() || describeToolCall(call), tool: call.tool } : undefined,\n };\n}\n\nlet nextId = 0;\nconst nextMessageId = () => `m${(nextId += 1)}`;\n\n/**\n * What to show for a reply, once the machine payload is stripped out.\n *\n * A guide answer is a ```guide fenced block of step JSON and often nothing\n * else, so sanitizing it can legitimately leave an empty string — and an\n * empty assistant bubble reads as a bug. The steps themselves are already\n * parsed into `guideSteps`/`navigation`, so say what is happening in the\n * words the model put in the steps rather than showing the JSON that\n * produced them.\n */\nfunction answerText(\n result: { answer?: string; guideSteps?: unknown[]; guideTitle?: string },\n fallback: string,\n): string {\n const answer = (result.answer ?? '').trim();\n if (answer) return answer;\n\n const first = result.guideSteps?.[0] as Record<string, unknown> | undefined;\n const body = typeof first?.body === 'string' ? first.body.trim() : '';\n if (body) return body;\n\n const title = typeof first?.title === 'string' ? first.title.trim() : '';\n if (title) return title;\n\n return fallback || 'Done.';\n}\n\n/**\n * What the visitor is told when a turn fails.\n *\n * Deliberately ONE message, with no detail about the cause.\n *\n * An earlier version classified the failure — timeout, network, 401, 5xx —\n * and said something different for each. That is still leaking the\n * diagnosis: a visitor cannot act on \"the request timed out\", and being\n * told which of our services broke is not their problem. Worse, the class\n * names read as blame (\"check your connection\") for what is usually our\n * own misconfiguration.\n *\n * The real diagnosis is logged in full, immediately before this is called,\n * so nothing is lost for whoever is debugging.\n */\nconst FAILURE_MESSAGE =\n \"I'm sorry, I can't complete your request right now. \"\n + 'Would you like to talk to a human?';\n\nexport function useGuideAI(options: UseGuideAIOptions = {}): UseGuideAIResult {\n const runtime = useGuideAIRuntime();\n const configured = requireConfigured();\n const harness = options.harness ?? configured.harness;\n const trace = options.trace ?? configured.debug;\n // Mobile is assistant-mode only; see GuideAIChatMode.\n const mode: GuideAIChatMode = 'assistant';\n\n const [messages, setMessages] = useState<GuideAIMessage[]>([]);\n const [input, setInput] = useState('');\n const [busy, setBusy] = useState(false);\n const [status, setStatus] = useState<string | null>(null);\n const [transcribing, setTranscribing] = useState(false);\n // Kept for retry(), which must survive the message list being cleared.\n const lastQuestion = useRef<string | null>(null);\n // The tool call behind each message's pendingAction, with the question\n // that led to it — the harness needs both to carry it out.\n const pending = useRef(new Map<string, { call: ToolCallResponse; question: string }>());\n\n // Guards against setting state after unmount — a streamed turn can\n // outlive the screen that started it.\n const mounted = useRef(true);\n useEffect(() => () => { mounted.current = false; }, []);\n\n /**\n * Rehydrate the conversation the harness already persisted.\n *\n * The harness has written every turn to its store all along — that is\n * what `store` in GuideAI.configure() is for, and what lets the model\n * answer follow-ups. Nothing ever read it back into the view, so closing\n * the sheet unmounted this hook and the transcript appeared to be gone\n * while the model still remembered it: the visitor sees an empty panel\n * and the assistant answers as though mid-conversation.\n *\n * Runs once on mount, and only when the view has nothing of its own, so\n * a remount mid-conversation cannot duplicate what is already on screen.\n */\n useEffect(() => {\n // Guarded rather than called directly: `harness` is a documented\n // option, so a host (and every test fake) may pass an object that\n // implements only the turn-taking surface. A missing history API means\n // \"nothing to restore\", never a crash on mount.\n if (typeof harness.hasHistory !== 'function'\n || typeof harness.getConversationHistory !== 'function') return;\n if (!harness.hasHistory()) return;\n\n setMessages((previous) => {\n if (previous.length > 0) return previous;\n return harness.getConversationHistory()\n .filter((entry) => typeof entry.content === 'string' && entry.content.trim())\n .map((entry, index) => ({\n id: entry.id ?? `h${index}`,\n role: entry.role,\n content: entry.content,\n }));\n });\n }, [harness]);\n\n /**\n * Fetch the site's active tool set once, before the first turn.\n *\n * REQUIRED, not an optimisation. The harness builds its ToolExecutor\n * inside initialize() — it cannot be injected, because the base URL and\n * auth config only exist once the server says which tool set is active.\n * Without this, `toolExecutor` stays null, `canExecuteTool()` returns\n * false for every customer API tool, and the turn dies with\n * \"Tool execution is unavailable in this browser context.\"\n *\n * The web SDK does this in chat-manager.ts; mobile needs its own call.\n */\n const initialized = useRef<Promise<boolean> | null>(null);\n const ensureInitialized = useCallback(() => {\n // Cached as a promise so concurrent sends share one request, and a\n // failure does not permanently disable tools — initialize() swallows\n // its own errors and simply leaves customer tools unavailable.\n if (!initialized.current) initialized.current = harness.initialize();\n return initialized.current;\n }, [harness]);\n\n /**\n * Run one agent turn into the assistant message `replyId`: stream its\n * text, then settle it with the answer, pictures, suggestions and any\n * action awaiting approval. Shared by a typed message and an approval.\n */\n const runTurn = useCallback(async (\n replyId: string,\n question: string,\n work: (\n onStatus: (status: string) => void,\n onDelta: (delta: string) => void,\n ) => Promise<HarnessResponse>,\n ) => {\n setBusy(true);\n setStatus(null);\n let streamed = '';\n const appendDelta = (delta: string) => {\n streamed += delta;\n if (!mounted.current) return;\n setMessages((prev) => prev.map((message) => (\n message.id === replyId\n ? { ...message, content: message.content + delta }\n : message\n )));\n };\n\n try {\n // The user's own token for the site's API, read fresh each turn: it\n // may have been refreshed or signed out since the last one.\n await runtime.auth?.refresh();\n const result = await work((next) => { if (mounted.current) setStatus(next); }, appendDelta);\n\n if (trace) traceAssistantAnswer(result.answer ?? '(empty)', result.mode);\n if (result.confirmationNeeded && result.pendingToolCall) {\n pending.current.set(replyId, { call: result.pendingToolCall, question });\n }\n\n if (!mounted.current) return;\n setMessages((prev) => prev.map((message) => (\n message.id === replyId\n // Streamed deltas and the final answer are the same text; trust\n // the final one, which is also what a non-streaming transport\n // returns. Keeping the streamed text when it is longer would\n // duplicate on a transport that replays.\n ? { ...message, ...replyFields(result, streamed), streaming: false }\n : message\n )));\n } catch (error) {\n // Always log the real cause. A non-Error throw would otherwise be\n // rendered as a bare \"Something went wrong\" with the actual\n // diagnosis discarded — which is exactly the case that is hardest\n // to debug from a phone.\n console.log('[GuideAI] ✗ turn failed:', error);\n if (error instanceof Error && error.stack) {\n console.log('[GuideAI] stack:', error.stack);\n }\n\n if (!mounted.current) return;\n setMessages((prev) => prev.map((message) => (\n message.id === replyId\n ? {\n ...message,\n content: FAILURE_MESSAGE,\n streaming: false,\n error: true,\n }\n : message\n )));\n } finally {\n if (mounted.current) {\n setBusy(false);\n setStatus(null);\n }\n }\n }, [runtime, trace]);\n\n const send = useCallback(async (question?: string) => {\n const text = (question ?? input).trim();\n if (!text || busy) return;\n\n lastQuestion.current = text;\n // Clear the box immediately: leaving the text in place while a turn\n // runs invites a double-send.\n if (question === undefined) setInput('');\n\n if (trace) traceUserMessage(text);\n\n const replyId = nextMessageId();\n setMessages((prev) => [\n ...prev,\n { id: nextMessageId(), role: 'user', content: text },\n { id: replyId, role: 'assistant', content: '', streaming: true },\n ]);\n\n await runTurn(replyId, text, async (onStatus, onDelta) => {\n // Must complete before the first turn, or customer API tools are\n // unavailable for it.\n await ensureInitialized();\n const screen = runtime.getActiveScreen();\n const { profile, visitorId } = requireConfigured();\n return harness.processMessage(\n text,\n screen?.path,\n runtime.captureSnapshot(),\n mode,\n onStatus,\n onDelta,\n undefined,\n undefined,\n // Who is asking, as the app knows them; the agent pitches to it.\n profile ? { profile } : undefined,\n visitorId ?? undefined,\n );\n });\n }, [busy, input, harness, mode, runtime, ensureInitialized, trace, runTurn]);\n\n const retry = useCallback(async () => {\n if (!lastQuestion.current || busy) return;\n // Drop the failed exchange so the retry does not stack a second\n // copy of the question under the first.\n setMessages((prev) => prev.slice(0, -2));\n await send(lastQuestion.current);\n }, [busy, send]);\n\n const clear = useCallback(() => {\n setMessages([]);\n lastQuestion.current = null;\n pending.current.clear();\n }, []);\n\n const settleAction = useCallback((messageId: string) => {\n const action = pending.current.get(messageId);\n pending.current.delete(messageId);\n setMessages((prev) => prev.map((message) => (\n message.id === messageId ? { ...message, pendingAction: undefined } : message\n )));\n return action;\n }, []);\n\n const confirm = useCallback(async (messageId: string) => {\n if (busy) return;\n const action = settleAction(messageId);\n if (!action) return;\n const replyId = nextMessageId();\n setMessages((prev) => [...prev, { id: replyId, role: 'assistant', content: '', streaming: true }]);\n await runTurn(replyId, action.question, async (onStatus, onDelta) => {\n await ensureInitialized();\n return harness.executeToolCall(action.call, action.question, onStatus, onDelta);\n });\n }, [busy, settleAction, runTurn, ensureInitialized, harness]);\n\n const cancel = useCallback((messageId: string) => {\n if (!settleAction(messageId)) return;\n setMessages((prev) => [...prev, { id: nextMessageId(), role: 'assistant', content: \"Okay, I haven't changed anything.\" }]);\n }, [settleAction]);\n\n const sendVoice = useCallback(async (recording: GuideAIVoiceRecording): Promise<GuideAIVoiceResult> => {\n if (busy || transcribing) return { text: null, error: 'Wait for the current reply to finish.' };\n const guessed = audioType(recording.uri);\n setTranscribing(true);\n try {\n const result = await requireConfigured().api.postFile<{ text?: string }>(\n EP.batchVoiceTranscribe(),\n 'audio',\n { uri: recording.uri, name: guessed.name, type: recording.mimeType || guessed.type },\n { language_code: recording.languageCode || 'en-US' },\n 45_000,\n );\n const heard = result?.text?.trim() || '';\n if (!heard) return { text: null, error: \"I didn't catch that. Please try again.\" };\n if (mounted.current) setTranscribing(false);\n await send(heard);\n return { text: heard };\n } catch (error) {\n console.log('[GuideAI] ✗ voice transcription failed:', error);\n // 422 is the recording itself (too short, silent): the server's words\n // say what to do. Anything else is ours, and the user is told plainly.\n const message = error instanceof UploadError && error.status === 422\n ? error.message\n : \"I couldn't process that recording. Please try again or type your message.\";\n return { text: null, error: message };\n } finally {\n if (mounted.current) setTranscribing(false);\n }\n }, [busy, transcribing, send]);\n\n return {\n messages,\n input,\n setInput,\n send,\n busy,\n canSend: Boolean(input.trim()) && !busy,\n status,\n retry,\n clear,\n confirm,\n cancel,\n sendVoice,\n transcribing,\n };\n}\n","import { useCallback, useEffect, useState } from 'react';\nimport { EP, type OnboardingState } from '@guideai/core';\nimport { requireConfigured } from '../setup';\nimport { useGuideAIRuntime } from './context';\n\n/** Reads the same durable checklist as the browser adapter. */\nexport function useOnboarding(): {\n checklist: OnboardingState | null;\n loading: boolean;\n refresh: () => Promise<void>;\n dismiss: () => Promise<void>;\n} {\n const runtime = useGuideAIRuntime();\n const [checklist, setChecklist] = useState<OnboardingState | null>(null);\n const [loading, setLoading] = useState(true);\n\n const refresh = useCallback(async () => {\n const { api, visitorId } = requireConfigured();\n if (!visitorId) { setChecklist(null); setLoading(false); return; }\n try {\n const current = await api.get<OnboardingState | null>(EP.onboardingCurrent(visitorId));\n const screen = runtime.getActiveScreen()?.path;\n const updated = current && !current.completed && !current.dismissed && screen\n ? await api.post<OnboardingState | null>(EP.onboardingObserve(), { user_id: visitorId, screen })\n : current;\n setChecklist(updated);\n } catch {\n // The host application keeps working if the checklist service is unavailable.\n } finally { setLoading(false); }\n }, [runtime]);\n\n const dismiss = useCallback(async () => {\n const { api, visitorId } = requireConfigured();\n if (!visitorId) return;\n const updated = await api.post<OnboardingState | null>(EP.onboardingDismiss(), { user_id: visitorId });\n setChecklist(updated);\n }, []);\n\n useEffect(() => {\n void refresh();\n const timer = setInterval(() => void refresh(), 5000);\n return () => clearInterval(timer);\n }, [refresh]);\n\n return { checklist, loading, refresh, dismiss };\n}\n"],"names":["CREDENTIAL_PATTERN","_j","line","_a","failure","createContext","useContext","configured","useState","useRef","useEffect","useCallback","next","_b"],"mappings":"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"}
|