@guideai/react-native 0.0.0-stage → 0.1.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.
@@ -0,0 +1,1043 @@
1
+ import React from 'react';
2
+
3
+ interface ToolCallResponse {
4
+ tool: string;
5
+ method: string;
6
+ path: string;
7
+ params?: Record<string, unknown>;
8
+ body?: Record<string, unknown>;
9
+ headers?: Record<string, string>;
10
+ requires_confirm: boolean;
11
+ /**
12
+ * Human-readable phrase for the UI ("Retrieving site information"), supplied
13
+ * by the model. `describeToolCall()` derives one when it is missing — the
14
+ * chat UI never shows the raw tool/endpoint name.
15
+ */
16
+ label?: string | null;
17
+ }
18
+ interface NavigationAction {
19
+ url: string;
20
+ description?: string;
21
+ }
22
+ interface ToolExecutionResult {
23
+ success: boolean;
24
+ data?: unknown;
25
+ error?: string;
26
+ error_type?: string;
27
+ status?: number;
28
+ }
29
+ interface CopilotToolDef {
30
+ name: string;
31
+ description: string;
32
+ method: string;
33
+ path: string;
34
+ params?: {
35
+ name: string;
36
+ in: string;
37
+ type: string;
38
+ required: boolean;
39
+ description?: string;
40
+ }[];
41
+ body_schema?: Record<string, unknown>;
42
+ risk_level: 'read' | 'write' | 'destructive';
43
+ }
44
+ /** A host/browser tool advertised to the planner but executed only by the SDK. */
45
+ interface ClientToolDefinition {
46
+ name: string;
47
+ description: string;
48
+ parameters: Record<string, unknown>;
49
+ consequential?: boolean;
50
+ }
51
+ interface CopilotConfig {
52
+ siteId: string;
53
+ /** Runtime label from the SDK script; this is the assistant's visible name. */
54
+ assistantLabel?: string;
55
+ /** Host-owned rule describing which requests another assistant should handle. */
56
+ delegationInstruction?: string;
57
+ /**
58
+ * The analytics session this chat belongs to. Sent with each question so a
59
+ * repeated "how do I …" can be correlated with whether the user then got
60
+ * there — the question alone says something is hard, the pairing says
61
+ * whether anything fixed it. Carries no message content.
62
+ */
63
+ analyticsSessionId?: string;
64
+ /**
65
+ * The product-analytics bundle's own ids, read at send time because that
66
+ * bundle can load after the chat and rotates its session when idle. Its
67
+ * session is the one the visitor's events are recorded under, so it wins
68
+ * over analyticsSessionId when present. Ids only, never content.
69
+ */
70
+ analyticsIds?: () => {
71
+ session_id?: string;
72
+ distinct_id?: string;
73
+ };
74
+ /**
75
+ * What this runtime can render beyond plain answers and walkthroughs. The
76
+ * server only sends a richer reply (a coach run for "coach") to a client
77
+ * that lists it, so a runtime that cannot play one keeps getting what it can.
78
+ */
79
+ clientCapabilities?: string[];
80
+ memoryTtlMs?: number;
81
+ maxMemoryMessages?: number;
82
+ }
83
+ /** A published interactive demo the assistant suggests playing inline (Storylane-style). */
84
+ interface DemoSuggestion {
85
+ share_token: string;
86
+ title: string;
87
+ description?: string | null;
88
+ /** Player URL with embed=1&autostart=1 — for in-chat iframe playback. */
89
+ embed_url: string;
90
+ /** Plain player URL — for "open in new tab". */
91
+ share_url: string;
92
+ thumbnail_url?: string | null;
93
+ step_count?: number;
94
+ }
95
+ /**
96
+ * One element on the visitor's page the reply rings, with a caption. Only
97
+ * from a turn whose skill grants `can_point`; the server checked the page
98
+ * snapshot listed it.
99
+ */
100
+ interface PagePoint {
101
+ selector: string;
102
+ text?: string;
103
+ caption: string;
104
+ }
105
+ /** A button to a page the site has. Only from a skill with `cta_buttons`. */
106
+ interface ChatAction {
107
+ label: string;
108
+ url: string;
109
+ }
110
+ /**
111
+ * A checkout offer from the site's payment connector (agent flow). Name, price
112
+ * and currency come from the site's settings, never from the model; the
113
+ * checkout itself is created only when the visitor presses Pay.
114
+ */
115
+ interface ChatPayment {
116
+ item_id: string;
117
+ name: string;
118
+ /** Smallest currency unit (cents, kobo). */
119
+ amount_minor: number;
120
+ currency: string;
121
+ provider: 'stripe' | 'paystack' | 'flutterwave' | string;
122
+ }
123
+ /** One stop on a coach run: reach a screen ("go"), or work through it ("do"). */
124
+ interface CoachMilestone {
125
+ title: string;
126
+ /**
127
+ * A route from the site's scanned map: the screen this stop happens on.
128
+ * Plans from servers before Oct 2026 may leave it null.
129
+ */
130
+ route: string | null;
131
+ /** The page's name in the scan ("Onboarding"): what a link to it says. */
132
+ page?: string | null;
133
+ kind: 'go' | 'do';
134
+ /** The task can succeed without it; the visitor may skip it. */
135
+ optional?: boolean;
136
+ /** The link the site's skill and KB agree leads to this stop's page. */
137
+ way?: CoachTarget | null;
138
+ /** This stop's controls in the skill's order, each found in the KB. */
139
+ targets?: CoachTarget[];
140
+ /** Inputs the task cannot finish without (labels), held before Done. */
141
+ needs?: string[];
142
+ }
143
+ /**
144
+ * One step of a site skill's flow, checked against the scanned app: the
145
+ * skill says it is needed, the KB says where it is.
146
+ */
147
+ interface CoachTarget {
148
+ label: string;
149
+ kind: 'link' | 'control' | 'field' | 'toggle' | 'section';
150
+ route: string;
151
+ /** What the visitor does with it. */
152
+ action: 'go' | 'open' | 'fill' | 'choose' | 'switch';
153
+ /** The section or menu it sits in. */
154
+ under?: string;
155
+ /** The folded group to open first when it is hidden. */
156
+ reveal?: string;
157
+ /** For a link: the page it opens. */
158
+ opens?: string;
159
+ required?: boolean;
160
+ /** The scanned fingerprint, when the KB has one. */
161
+ fingerprint?: Record<string, unknown>;
162
+ }
163
+ /**
164
+ * A coach run: "how do I…" answered by taking the visitor to the screen and
165
+ * talking them through it, rather than only pointing.
166
+ */
167
+ interface CoachPlan {
168
+ goal: string;
169
+ intro: string;
170
+ /** Asked first when the task has more than one path; options are chips. */
171
+ question: {
172
+ text: string;
173
+ options: string[];
174
+ } | null;
175
+ milestones: CoachMilestone[];
176
+ /** The page the task itself is done on — the run is never coached elsewhere. */
177
+ task_route?: string | null;
178
+ /**
179
+ * "site", "workspace"…: the plan wanted the visitor to switch it. They are
180
+ * asked once to check they are in the right one instead of switching.
181
+ */
182
+ confirm_context?: string | null;
183
+ /**
184
+ * Set when this plan is new: sent back to /chat/coach/confirm once the run
185
+ * finishes on the task's page, and only then shared with the next visitor.
186
+ */
187
+ proof_key?: string | null;
188
+ done_when: {
189
+ route: string | null;
190
+ text: string | null;
191
+ };
192
+ settings?: {
193
+ voice: 'off' | 'browser' | 'neural';
194
+ explain_options: boolean;
195
+ offer_help: boolean;
196
+ vision_per_run: number;
197
+ };
198
+ cached?: boolean;
199
+ }
200
+ /** Escalation signal from the AI assistant. */
201
+ interface EscalationSignal {
202
+ offered: boolean;
203
+ reason?: 'low_confidence' | 'cannot_resolve' | 'user_requested';
204
+ confidence?: number;
205
+ }
206
+ /** Callback for reporting agent loop progress to the UI. */
207
+ type AgentStatusCallback = (status: string) => void;
208
+ /** One step of the plan the assistant stated before it started working. */
209
+ interface AssistantPlanStep {
210
+ title: string;
211
+ /** Endpoint this step uses; absent when the assistant does it itself. */
212
+ tool?: string | null;
213
+ /** Set when nothing available can do this step. */
214
+ blocked_reason?: string | null;
215
+ }
216
+ interface AssistantPlan {
217
+ steps: AssistantPlanStep[];
218
+ note?: string | null;
219
+ }
220
+ type PlanCallback = (plan: AssistantPlan) => void;
221
+ interface HarnessResponse {
222
+ mode: 'answer' | 'guide' | 'execute' | 'coach';
223
+ answer: string;
224
+ /** What the assistant said it would do, shown while it works. */
225
+ plan?: AssistantPlan;
226
+ intent?: 'delegate';
227
+ intentReason?: string;
228
+ guideSteps?: Record<string, unknown>[];
229
+ guideTitle?: string;
230
+ navigation?: NavigationAction;
231
+ toolResult?: ToolExecutionResult;
232
+ confirmationNeeded?: boolean;
233
+ pendingToolCall?: ToolCallResponse;
234
+ /** Full concurrent batch when the model requested more than one tool. */
235
+ pendingToolCalls?: ToolCallResponse[];
236
+ /** When true, the ChatManager should auto-execute the pendingToolCall (read-only action). */
237
+ autoExecuteToolCall?: boolean;
238
+ autoExecute: boolean;
239
+ /** Escalation signal from the AI. */
240
+ escalation?: EscalationSignal;
241
+ /** Interactive demo suggestion the chat UI can play inline. */
242
+ demo?: DemoSuggestion;
243
+ point?: PagePoint;
244
+ actions?: ChatAction[];
245
+ payment?: ChatPayment;
246
+ coach?: CoachPlan;
247
+ /** One-tap replies; a tap sends the label as the next message. */
248
+ quickReplies?: string[];
249
+ }
250
+ interface ConversationMessage {
251
+ id?: string;
252
+ role: 'user' | 'assistant';
253
+ content: string;
254
+ toolResult?: ToolExecutionResult;
255
+ toolCalls?: ToolCallRecord[];
256
+ timestamp: number;
257
+ /** Guide steps generated by the assistant (for walkthrough buttons). */
258
+ guideSteps?: Record<string, unknown>[];
259
+ /** Title for the guide walkthrough. */
260
+ guideTitle?: string;
261
+ /** Navigation action returned by the assistant. */
262
+ navigation?: NavigationAction;
263
+ /** Response mode (answer, guide, execute). */
264
+ mode?: 'answer' | 'guide' | 'execute' | 'coach';
265
+ /** Whether this message has a pending tool call needing confirmation. */
266
+ confirmationNeeded?: boolean;
267
+ /** The pending tool call awaiting user confirmation. */
268
+ pendingToolCall?: ToolCallResponse;
269
+ /** Escalation signal from the AI. */
270
+ escalation?: EscalationSignal;
271
+ /** Interactive demo suggestion attached to this assistant message. */
272
+ demo?: DemoSuggestion;
273
+ }
274
+ /** Record of a tool call for conversation history context. */
275
+ interface ToolCallRecord {
276
+ tool: string;
277
+ description?: string;
278
+ success: boolean;
279
+ summary?: string;
280
+ /** Present when the record came from a client-executed call rather than SSE. */
281
+ method?: string;
282
+ path?: string;
283
+ }
284
+ /** Callback for streaming text deltas to the UI. */
285
+ type TextDeltaCallback = (delta: string) => void;
286
+ /** Callback for tool call lifecycle events. */
287
+ type ToolCallEventCallback = (event: ToolCallDisplayEvent) => void;
288
+ /** Event describing tool call progress for UI display. */
289
+ interface ToolCallDisplayEvent {
290
+ type: 'start' | 'done';
291
+ tool: string;
292
+ description?: string;
293
+ success?: boolean;
294
+ summary?: string;
295
+ }
296
+
297
+ /** Shared onboarding contract. UI targets are interpreted by each platform. */
298
+ /** One highlighted action "Show me" plays, in order, on the step's page. */
299
+ interface OnboardingAction {
300
+ id: string;
301
+ title: string;
302
+ instruction: string;
303
+ action: 'click' | 'fill' | 'select' | 'check';
304
+ /** Words on the control; the page is searched for them when no fingerprint matches. */
305
+ target_label: string;
306
+ target_fingerprint: Record<string, unknown>;
307
+ kind: string;
308
+ context: string;
309
+ }
310
+ interface OnboardingStepState {
311
+ id: string;
312
+ title: string;
313
+ instruction: string;
314
+ required: boolean;
315
+ state: 'complete' | 'skipped' | 'available' | 'locked';
316
+ /** Complete on the person's own tick (a manual step), not on evidence. */
317
+ self_reported?: boolean;
318
+ location: {
319
+ surface: 'web' | 'mobile' | 'desktop' | 'other';
320
+ screen: string;
321
+ target_label: string;
322
+ target_fingerprint: Record<string, unknown>;
323
+ /** A help hint on `screen` that "Show me" plays instead of highlighting a control. */
324
+ hint_id?: string | null;
325
+ };
326
+ completion: {
327
+ type: 'event' | 'screen' | 'text_visible' | 'manual';
328
+ value: string;
329
+ };
330
+ actions?: OnboardingAction[];
331
+ }
332
+ /** The button that opens the checklist; every field falls back to the card-matching default. */
333
+ interface OnboardingLauncher {
334
+ label?: string;
335
+ invite_label?: string;
336
+ complete_label?: string;
337
+ show_progress?: boolean;
338
+ shape?: 'rounded' | 'pill';
339
+ background_color?: string | null;
340
+ text_color?: string | null;
341
+ }
342
+ interface OnboardingAppearance {
343
+ template: 'classic' | 'minimal' | 'bold';
344
+ accent_color: string;
345
+ /** Missing means the default card; position missing means left. */
346
+ layout?: 'card' | 'modal' | 'side';
347
+ position?: 'left' | 'right';
348
+ launcher?: OnboardingLauncher;
349
+ }
350
+ interface OnboardingState {
351
+ id: string;
352
+ title: string;
353
+ goal: string;
354
+ start_mode: 'automatic' | 'ask' | 'checklist';
355
+ chat_policy: 'unrestricted' | 'setup_only';
356
+ allow_dismissal: boolean;
357
+ appearance?: OnboardingAppearance;
358
+ completed: boolean;
359
+ dismissed: boolean;
360
+ completed_required: number;
361
+ total_required: number;
362
+ /** Every step, optional ones included; a skipped optional step counts as done. Missing from older servers. */
363
+ completed_steps?: number;
364
+ total_steps?: number;
365
+ steps: OnboardingStepState[];
366
+ }
367
+
368
+ /** A picture drawn for an answer: a PNG to show, and the SVG it was drawn as. */
369
+ interface HeadlessImage {
370
+ url: string;
371
+ svg: string | null;
372
+ alt: string;
373
+ width: number;
374
+ height: number;
375
+ }
376
+
377
+ interface PageElement {
378
+ tag: string;
379
+ text: string;
380
+ selector: string;
381
+ aria_label?: string;
382
+ name?: string;
383
+ role?: string;
384
+ type?: string;
385
+ placeholder?: string;
386
+ /** href attribute for anchor elements */
387
+ href?: string;
388
+ /** Current value of input/textarea/select elements */
389
+ value?: string;
390
+ /** Associated label text (from <label> or aria-labelledby) */
391
+ label?: string;
392
+ /** More descriptive form label when available. */
393
+ form_label?: string;
394
+ /** True when element exists in DOM but is not currently visible */
395
+ hidden?: boolean;
396
+ /** Describes the container: "hamburger menu", "dropdown", "sidebar", etc. */
397
+ container?: string;
398
+ /** State of the container: "collapsed", "closed", etc. */
399
+ containerState?: string;
400
+ /** Nearby descriptive text from the element's parent container. */
401
+ parent_text?: string;
402
+ /** Title of the nearest visible section heading. */
403
+ section_title?: string;
404
+ /** Source URL when the element is inside an iframe */
405
+ iframe_src?: string;
406
+ }
407
+ interface PageSectionLink {
408
+ text: string;
409
+ href: string;
410
+ selector: string;
411
+ }
412
+ interface PageSection {
413
+ title: string;
414
+ selector: string;
415
+ text?: string;
416
+ links: PageSectionLink[];
417
+ elements: PageElement[];
418
+ }
419
+ interface PageSnapshot {
420
+ url: string;
421
+ title: string;
422
+ page_text?: string;
423
+ sections?: PageSection[];
424
+ elements: PageElement[];
425
+ }
426
+
427
+ interface ClientToolRegistry {
428
+ /** Tools this host can run, in the shape the backend expects. */
429
+ definitions(): ClientToolDefinition[];
430
+ /** The same tools shaped for the copilot planner and the validator. */
431
+ copilotDefinitions(): CopilotToolDef[];
432
+ /** Whether a tool name belongs to this registry. */
433
+ canExecute(name: string): boolean;
434
+ /** Whether a specific planned call is one this registry should run. */
435
+ canExecuteCall(toolCall: ToolCallResponse): boolean;
436
+ /** Run one call. Never throws — failures come back as a result. */
437
+ execute(toolCall: ToolCallResponse): Promise<ToolExecutionResult>;
438
+ /**
439
+ * Run several calls, reporting each as it lands.
440
+ *
441
+ * `onComplete` fires per call so a slow one cannot hold back the others'
442
+ * progress in the UI.
443
+ */
444
+ executeBatch(toolCalls: ToolCallResponse[], onComplete?: (toolCall: ToolCallResponse, result: ToolExecutionResult) => void): Promise<ToolExecutionResult[]>;
445
+ /**
446
+ * Release anything the registry is holding (timers, highlights).
447
+ *
448
+ * Optional: the harness has no teardown path today, so requiring it
449
+ * would force every registry to implement code nothing calls.
450
+ */
451
+ destroy?(): void;
452
+ }
453
+
454
+ /**
455
+ * A synchronous key/value store.
456
+ *
457
+ * Synchronous on purpose: the conversation memory reads storage inside
458
+ * `getMessages()`, which the agent loop calls from ~20 sync places.
459
+ * Making reads async would push `await` through the whole loop and change
460
+ * its public signatures for no functional gain.
461
+ *
462
+ * Web: `localStorage` already *is* this shape.
463
+ * React Native: MMKV or `expo-sqlite/kv-store` (both sync). A host whose
464
+ * only store is AsyncStorage must hydrate a snapshot at boot and write
465
+ * through — the honest cost of not rewriting the loop.
466
+ */
467
+ interface KVStore {
468
+ getItem(key: string): string | null;
469
+ setItem(key: string, value: string): void;
470
+ removeItem(key: string): void;
471
+ }
472
+ /**
473
+ * Where a host keeps credentials the copilot may read, named by the
474
+ * source a site configured (`localStorage`, `sessionStorage`, `cookie`).
475
+ *
476
+ * Returns null for a source this platform does not have. A mobile app has
477
+ * no cookie jar, and saying so plainly is better than returning an empty
478
+ * string that reads as "present but blank".
479
+ */
480
+ interface CredentialSource {
481
+ read(source: string, key: string): string | null;
482
+ }
483
+
484
+ /**
485
+ * Decoded text chunks arriving as the response streams.
486
+ *
487
+ * Text, not bytes, on purpose. Decoding UTF-8 incrementally is the one
488
+ * part of stream handling that is easy to get subtly wrong: a multi-byte
489
+ * character split across a chunk boundary is corrupted unless the decoder
490
+ * carries state between chunks. Browsers have a spec-correct
491
+ * `TextDecoder`; React Native's polyfills vary, and one that ignores
492
+ * incremental mode mangles non-ASCII text silently, at runtime, past
493
+ * every typecheck and test.
494
+ *
495
+ * So each platform decodes where it knows the answer, and core consumes
496
+ * strings. **Implementations MUST handle characters split across chunk
497
+ * boundaries** — that is a contract, not a hint.
498
+ */
499
+ type TextStream = AsyncIterable<string>;
500
+ /** A request, already serialized. */
501
+ interface HttpRequest {
502
+ url: string;
503
+ /**
504
+ * Customer toolsets declare their own verbs — a `destructive` tool is
505
+ * typically a DELETE — so this cannot be narrowed to GET/POST without
506
+ * silently rewriting those calls.
507
+ */
508
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD';
509
+ headers: Record<string, string>;
510
+ /** Pre-serialized body. Multipart stays platform-side; see HttpTransport. */
511
+ body?: string;
512
+ signal?: AbortSignal;
513
+ /**
514
+ * Best-effort delivery while the app is tearing down. Platforms without
515
+ * an equivalent ignore it — the semantics are already "may be dropped".
516
+ */
517
+ keepalive?: boolean;
518
+ }
519
+ /**
520
+ * What the agent loop actually reads off a response.
521
+ *
522
+ * Deliberately narrower than `Response`. `ok` is absent because nothing
523
+ * reads it and its presence invites `if (!res.ok)` to drift between
524
+ * platforms; `blob`/`arrayBuffer`/`redirected` are absent because they
525
+ * have no callers.
526
+ */
527
+ interface HttpResponse {
528
+ readonly status: number;
529
+ /**
530
+ * Header lookup. Shaped as `{ get() }` rather than a flat record to
531
+ * match `Headers`, so a browser transport can forward it directly.
532
+ */
533
+ readonly headers: {
534
+ get(name: string): string | null;
535
+ };
536
+ json<T = unknown>(): Promise<T>;
537
+ text(): Promise<string>;
538
+ /**
539
+ * Present only when this response carries a readable stream. Absent is
540
+ * normal and handled: the caller falls back to `json()`.
541
+ */
542
+ stream?: TextStream;
543
+ }
544
+ interface HttpTransport {
545
+ request(req: HttpRequest): Promise<HttpResponse>;
546
+ /**
547
+ * Whether this platform can deliver progressive bytes at all.
548
+ *
549
+ * Read by the agent loop to decide what to *ask the backend for*. It is
550
+ * a property of the transport rather than a setting, so a host cannot
551
+ * request streaming its platform physically cannot read — that would
552
+ * produce an empty answer with no error.
553
+ *
554
+ * Note both browsers and React Native can stream (fetch bodies and
555
+ * progressive XHR respectively), so this is true on both. It exists for
556
+ * the runtime that genuinely cannot.
557
+ */
558
+ readonly supportsStreaming: boolean;
559
+ }
560
+
561
+ interface CopilotApi {
562
+ /** JSON GET with the platform's default time budget. */
563
+ get<T>(path: string, timeoutMs?: number): Promise<T>;
564
+ /** JSON POST. Encrypts the body when the client has crypto configured. */
565
+ post<T>(path: string, body?: unknown, timeoutMs?: number): Promise<T>;
566
+ /**
567
+ * POST returning the response unparsed, so the caller can stream it or
568
+ * branch on content-type. Not time-budgeted: a stream is long-lived by
569
+ * design and the caller owns cancellation through `signal`.
570
+ */
571
+ postRaw(path: string, body?: unknown, signal?: AbortSignal): Promise<HttpResponse>;
572
+ /** Whether this platform can read a response progressively. */
573
+ readonly supportsStreaming: boolean;
574
+ }
575
+
576
+ /**
577
+ * Copilot Agent Harness — client-side orchestrator.
578
+ *
579
+ * Architecture:
580
+ * - Backend plans the next step and formats final answers
581
+ * - Backend streams progress via SSE events during the loop
582
+ * - SDK can execute safe read tools in the browser and continue the chain
583
+ * - SDK reads the stream, shows status updates, and displays the final answer
584
+ * - For confirmed tool calls, SDK executes the customer API request in the browser
585
+ *
586
+ * Memory persists through an injected KVStore with TTL (localStorage on
587
+ * the web, MMKV on mobile). Multi-tenant safe via site_id scoping.
588
+ */
589
+
590
+ declare class CopilotHarness {
591
+ private api;
592
+ private config;
593
+ private memory;
594
+ private validator;
595
+ private toolExecutor;
596
+ private tools;
597
+ private readonly credentials;
598
+ private readonly transport;
599
+ private activeTools;
600
+ private initialized;
601
+ /** Told about the assistant's plan as soon as the server states one. */
602
+ private onPlan?;
603
+ private lastPlanFingerprint;
604
+ /** The plan in force for the message being answered. */
605
+ private currentPlan?;
606
+ /** Tool inputs the host page supplies — whatever the user has open. */
607
+ private toolContext?;
608
+ /**
609
+ * @param tools How this platform actually touches the screen. Defaults to
610
+ * the browser registry so every existing caller is unaffected; a mobile
611
+ * runtime passes one that dispatches native effects instead. The loop
612
+ * below never learns which it is holding.
613
+ */
614
+ constructor(api: CopilotApi, config: CopilotConfig, tools: ClientToolRegistry, storage: KVStore, transport: HttpTransport, credentials: CredentialSource);
615
+ /** Hear every turn the conversation records (typed, spoken, replies). */
616
+ onMessageRecorded(listener: ((message: ConversationMessage) => void) | null): void;
617
+ /** Initialize the harness — fetch active tools for the site. */
618
+ initialize(): Promise<boolean>;
619
+ /**
620
+ * Register the listener that renders the assistant's plan.
621
+ *
622
+ * Set on the harness rather than passed per message: `processMessage` is
623
+ * already called positionally from several places, and a plan belongs to
624
+ * the UI for the whole session, not to one call.
625
+ */
626
+ setPlanCallback(callback: PlanCallback | undefined): void;
627
+ /**
628
+ * Values the host page supplies for tool inputs.
629
+ *
630
+ * Sent with every turn, including chain rounds — a later round builds calls
631
+ * too, and dropping it there is how a chain that started on the right
632
+ * record finishes on the default one.
633
+ */
634
+ /** Bind to the SDK's analytics session. Carries no message content. */
635
+ setAnalyticsSessionId(sessionId: string): void;
636
+ /** Read the analytics bundle's ids on every question. Ids only. */
637
+ setAnalyticsIds(read: () => {
638
+ session_id?: string;
639
+ distinct_id?: string;
640
+ }): void;
641
+ setToolContext(values: Record<string, string> | undefined): void;
642
+ /** Whether copilot action tools are available for this site. */
643
+ isEnabled(): boolean;
644
+ /**
645
+ * Process a user message through the harness.
646
+ *
647
+ * 1. Sends question + history + page context to backend /ask
648
+ * 2. If backend returns SSE stream (agent loop running), reads events in real-time
649
+ * 3. Calls onStatus callback with progress updates during the loop
650
+ * 4. Returns the final response when the stream completes
651
+ * 5. For non-tool queries, returns JSON response directly
652
+ */
653
+ processMessage(question: string, pageUrl?: string, pageSnapshot?: PageSnapshot, chatMode?: string, onStatus?: AgentStatusCallback, onTextDelta?: TextDeltaCallback, onToolCallEvent?: ToolCallEventCallback, abortSignal?: AbortSignal, userContext?: Record<string, unknown>, onboardingUserId?: string): Promise<HarnessResponse>;
654
+ /**
655
+ * Execute a confirmed tool call after user approval.
656
+ * Called when user clicks "Yes, do it" for write/destructive actions.
657
+ * Executes in the browser when possible, then formats the result with LLM.
658
+ */
659
+ executeToolCall(toolCall: ToolCallResponse, originalQuestion: string, onStatus?: AgentStatusCallback, onTextDelta?: TextDeltaCallback): Promise<HarnessResponse>;
660
+ /** Alias for executeToolCall. */
661
+ confirmAndExecute(toolCall: ToolCallResponse, originalQuestion: string): Promise<HarnessResponse>;
662
+ /** Get conversation history for display in chat. */
663
+ getConversationHistory(): ConversationMessage[];
664
+ /** Recent redacted context for semantic conversational follow-ups. */
665
+ getRecentConversationContext(count?: number): {
666
+ role: string;
667
+ content: string;
668
+ }[];
669
+ /** Persist a completed Realtime turn so voice and text share one history. */
670
+ recordRealtimeTurn(role: 'user' | 'assistant', content: string): void;
671
+ /** Check if there is existing conversation history. */
672
+ hasHistory(): boolean;
673
+ /** Clear conversation history. */
674
+ clearHistory(): void;
675
+ /**
676
+ * Read SSE events from the backend agent loop stream.
677
+ * Supports incremental text streaming, tool call display events, and cancellation.
678
+ * Updates status via callback and resolves with the final response.
679
+ */
680
+ private _readSSEStream;
681
+ /**
682
+ * Parse the ChatAskResponse from a streamed "done" event into HarnessResponse.
683
+ */
684
+ /**
685
+ * Strip tool_call artifacts from streamed text so raw JSON is never shown.
686
+ *
687
+ * Handles:
688
+ * - Fenced code blocks: ```tool_call\n{...}```
689
+ * - Bare JSON objects with a "tool" key: {"tool":"list_guides","params":{}}
690
+ * - Standalone mode: lines (mode: execute)
691
+ */
692
+ private _stripToolCallArtifacts;
693
+ private _parseStreamedResponse;
694
+ /**
695
+ * Handle a regular JSON response (non-streaming).
696
+ */
697
+ /**
698
+ * Surface a plan to the UI once per chain.
699
+ *
700
+ * The chain revisits the server on every round, and a mid-chain response
701
+ * may restate the plan. Re-announcing an identical plan would redraw it and
702
+ * make the assistant look like it had started over, so only a changed plan
703
+ * is emitted.
704
+ */
705
+ private _announcePlan;
706
+ private _handleJsonResponse;
707
+ private _continueToolChain;
708
+ /**
709
+ * Remove calls that have already been made in this chain.
710
+ *
711
+ * The model is told not to repeat itself, and mostly does not, but a long
712
+ * chain occasionally re-requests an endpoint it already has — which costs a
713
+ * request and a model round trip to learn nothing new.
714
+ */
715
+ private _dropRepeats;
716
+ /**
717
+ * Run a client-executed tool chain.
718
+ *
719
+ * Each round the model may request either one tool or a batch of
720
+ * independent tools. A batch is executed concurrently, so the wall-clock
721
+ * cost of gathering four unrelated resources is one request plus one model
722
+ * round trip, rather than four of each. Dependent calls still arrive one at
723
+ * a time — the model only batches what it can run without waiting.
724
+ */
725
+ private _runClientToolChain;
726
+ private hasAnyLocalTools;
727
+ private canExecuteTool;
728
+ private executeLocalTool;
729
+ private executeLocalToolBatch;
730
+ private _formatToolResult;
731
+ private _readFormatToolResultStream;
732
+ private _executePendingToolCall;
733
+ /**
734
+ * Emit a fully-buffered answer to the UI.
735
+ *
736
+ * This used to fake a typing effect by releasing one word every 12ms. The
737
+ * response has already arrived in full by this point, so that delay was pure
738
+ * added latency — a 250-word answer sat behind three seconds of animation
739
+ * after the data was on the client. Real streaming still arrives token by
740
+ * token over SSE; a buffered answer is shown as soon as we have it.
741
+ */
742
+ private _emitAnswerText;
743
+ private _errorResponse;
744
+ }
745
+
746
+ /**
747
+ * The slice of MMKV this needs. Declared structurally so `new MMKV()`
748
+ * satisfies it without this package depending on react-native-mmkv.
749
+ */
750
+ interface SyncNativeStore {
751
+ getString(key: string): string | undefined;
752
+ set(key: string, value: string): void;
753
+ delete(key: string): void;
754
+ }
755
+
756
+ /** Returns the signed-in user's token, or null when signed out. May be async. */
757
+ type AuthTokenGetter = () => string | null | undefined | Promise<string | null | undefined>;
758
+
759
+ /** What a host says about one control on screen. */
760
+ interface DeclaredElement {
761
+ /**
762
+ * Stable id, unique within the screen. Becomes the snapshot's
763
+ * `selector` — legitimate because the backend treats `selector` as an
764
+ * opaque round-trip token: it renders elements to the model as numbered
765
+ * [ID]s and only checks the string is specific (not the word "button").
766
+ */
767
+ id: string;
768
+ /** Visible text. What the model matches the user's words against. */
769
+ label: string;
770
+ /** Coarse kind, mapped to the `tag` the backend's prompt expects. */
771
+ kind?: 'button' | 'link' | 'input' | 'text' | 'toggle' | 'image';
772
+ /** Current value, for inputs. */
773
+ value?: string;
774
+ /** Accessibility label when it says more than the visible text. */
775
+ accessibilityLabel?: string;
776
+ /** Placeholder text for an empty input. */
777
+ placeholder?: string;
778
+ /** True when mounted but off-screen or collapsed. */
779
+ hidden?: boolean;
780
+ /** Heading of the group this sits in, e.g. "Sharing options". */
781
+ sectionTitle?: string;
782
+ }
783
+ /** Actions a declared element can perform, supplied by the host at registration. */
784
+ interface ElementHandlers {
785
+ /** Invoked by guideai_click_element. */
786
+ press?: () => void | Promise<void>;
787
+ /** Invoked by guideai_fill_field / guideai_fill_form. */
788
+ setValue?: (value: string) => void | Promise<void>;
789
+ /** Invoked by guideai_scroll_to_element. */
790
+ scrollTo?: () => void | Promise<void>;
791
+ }
792
+
793
+ /** The slice of React Navigation's ref this needs. */
794
+ interface NavigationRefLike {
795
+ isReady?(): boolean;
796
+ navigate(screen: string, params?: object): void;
797
+ goBack(): void;
798
+ canGoBack?(): boolean;
799
+ }
800
+
801
+ interface GuideAIConfig {
802
+ /**
803
+ * The site's public token, from the dashboard. The only required
804
+ * field: it identifies the site, and everything else about that site
805
+ * — its tools, knowledge base, model and prompts — is resolved
806
+ * server-side from it.
807
+ */
808
+ apiKey: string;
809
+ /** The application's stable user ID after login, if already known. */
810
+ visitorId?: string;
811
+ /**
812
+ * A domain registered on this site (its own domain, or one of its
813
+ * `domain_aliases`).
814
+ *
815
+ * Required in practice, and it cannot be fetched from the dashboard:
816
+ * the backend uses it to AUTHORIZE the request, so it must accompany
817
+ * the very first call. A native app sends no `Origin` header, so this
818
+ * travels as `x-guideai-request-origin`. Set it once; a mismatch is
819
+ * answered with 403.
820
+ */
821
+ origin?: string;
822
+ /**
823
+ * Where the GuideAI backend lives. Defaults to the hosted service, so
824
+ * only self-hosted deployments set it.
825
+ */
826
+ baseUrl?: string;
827
+ /**
828
+ * How the assistant navigates between screens — a React Navigation
829
+ * ref, or a getter for one. Omitted, the assistant simply does not
830
+ * offer navigation rather than offering it and failing.
831
+ */
832
+ navigation?: NavigationRefLike | (() => NavigationRefLike | null | undefined);
833
+ /**
834
+ * A synchronous key/value store (MMKV, or `expo-sqlite/kv-store`) for
835
+ * conversation history across launches. Omitted, history is kept in
836
+ * memory and resets on relaunch.
837
+ */
838
+ store?: SyncNativeStore;
839
+ /** The assistant's visible name in its own replies. */
840
+ assistantName?: string;
841
+ /**
842
+ * Which of the site's agents (Agent Blueprints) answers, by its key —
843
+ * e.g. 'tutor'. Omitted: the site's main agent.
844
+ */
845
+ agent?: string;
846
+ /**
847
+ * The signed-in user's token for the site's own API. Read before every
848
+ * turn; may be async (AsyncStorage, SecureStore). Return null when
849
+ * signed out. The assistant then calls your endpoints as that user,
850
+ * from the device.
851
+ */
852
+ getAuthToken?: AuthTokenGetter;
853
+ /**
854
+ * Called once when your API answers 401 to a tool call. Refresh the
855
+ * session and return the new token; the call is repeated with it.
856
+ * Omitted: getAuthToken() is asked again.
857
+ */
858
+ onAuthExpired?: AuthTokenGetter;
859
+ /**
860
+ * Facts about the signed-in user for the agent to pitch answers to —
861
+ * `{ age: 12, class: 'JSS1', country: 'Nigeria', language: 'en' }`.
862
+ * Sent with every turn; change it with GuideAI.setUserProfile().
863
+ */
864
+ userProfile?: GuideAIUserProfile;
865
+ /**
866
+ * Log the full conversation and every request. Development only —
867
+ * it prints message content to the console.
868
+ */
869
+ debug?: boolean;
870
+ }
871
+ /** Short facts about the user: strings, numbers, booleans or short lists of them. */
872
+ type GuideAIUserProfile = Record<string, string | number | boolean | Array<string | number>>;
873
+ /**
874
+ * Configure the assistant. Call once, at app startup.
875
+ *
876
+ * Deliberately a function rather than a provider or a module constant:
877
+ * `apiKey` often arrives from a remote config or an env fetch, and a
878
+ * constructor running at import time cannot wait for it.
879
+ */
880
+ declare function configure(config: GuideAIConfig): void;
881
+ /** Call after login so chat and the checklist use the same person. */
882
+ declare function identify(userId: string | null): void;
883
+ /** Replace what the agent knows about the user (after login, or a profile edit). */
884
+ declare function setUserProfile(profile: GuideAIUserProfile | null): void;
885
+ /** Whether configure() has run. Lets a host render its own fallback. */
886
+ declare function isConfigured(): boolean;
887
+ /** Test seam, and for hosts that switch sites at runtime. */
888
+ declare function reset(): void;
889
+ declare const GuideAI: {
890
+ configure: typeof configure;
891
+ identify: typeof identify;
892
+ setUserProfile: typeof setUserProfile;
893
+ isConfigured: typeof isConfigured;
894
+ reset: typeof reset;
895
+ };
896
+
897
+ interface GuideAIScreenProps {
898
+ /** React Navigation screen name, e.g. "ShareForm". Used by navigate(). */
899
+ name: string;
900
+ /** Scanner route path, e.g. "/share". Used for grounding. */
901
+ path: string;
902
+ /** Human title shown to the model. Defaults to `name`. */
903
+ title?: string;
904
+ /**
905
+ * Whether this screen is the one currently on top.
906
+ *
907
+ * Defaults to true. React Navigation keeps previous screens MOUNTED in
908
+ * the stack, so without this a backgrounded screen would keep claiming
909
+ * to be active and the model would be told about controls the user
910
+ * cannot see. Hosts using React Navigation should pass
911
+ * `useIsFocused()`.
912
+ */
913
+ active?: boolean;
914
+ children: React.ReactNode;
915
+ }
916
+ declare function GuideAIScreen({ name, path, title, active, children, }: GuideAIScreenProps): React.ReactElement;
917
+
918
+ type UseGuideAIElementOptions = Omit<DeclaredElement, 'id'> & ElementHandlers;
919
+ /**
920
+ * Declare one element to the assistant.
921
+ *
922
+ * Registration is an effect, so it runs after mount and unwinds on
923
+ * unmount. Handlers are kept in a ref and read through a stable wrapper:
924
+ * without that, an inline `press={() => …}` would be a new function on
925
+ * every render and re-register the element on every render with it.
926
+ */
927
+ declare function useGuideAIElement(id: string, options: UseGuideAIElementOptions): void;
928
+
929
+ interface GuideAIMessage {
930
+ id: string;
931
+ role: 'user' | 'assistant';
932
+ content: string;
933
+ /** True while tokens are still arriving for this message. */
934
+ streaming?: boolean;
935
+ error?: boolean;
936
+ /**
937
+ * Follow-up suggestions to draw as chips. A tap is just
938
+ * `send(suggestion)`.
939
+ */
940
+ suggestions?: string[];
941
+ /**
942
+ * Pictures the agent drew. They are also in `content` as Markdown
943
+ * images where they belong; use this list to place them yourself.
944
+ */
945
+ images?: HeadlessImage[];
946
+ /**
947
+ * A change to the user's data waiting for their yes ("Create the
948
+ * course"). Draw Approve / Cancel and call `confirm(id)` or `cancel(id)`.
949
+ */
950
+ pendingAction?: {
951
+ label: string;
952
+ tool: string;
953
+ };
954
+ }
955
+ /** A recording the host made (expo-audio or any recorder) and saved to a file. */
956
+ interface GuideAIVoiceRecording {
957
+ /** `file://…` URI of the finished recording. */
958
+ uri: string;
959
+ /** e.g. 'audio/m4a'. Omitted: guessed from the file extension. */
960
+ mimeType?: string;
961
+ /** BCP-47 language the user spoke, e.g. 'en-NG'. Defaults to 'en-US'. */
962
+ languageCode?: string;
963
+ }
964
+ interface GuideAIVoiceResult {
965
+ /** What was heard, already sent as the next message; null when nothing was. */
966
+ text: string | null;
967
+ /** Why nothing was sent, in words to show the user. */
968
+ error?: string;
969
+ }
970
+ /**
971
+ * Everything a custom chat UI needs, and nothing about how to draw it.
972
+ *
973
+ * A host owns two things — their design, and the text the user typed.
974
+ * This hook owns the rest: the conversation, the streaming, the tool
975
+ * calls, the transport. That split is what lets the agent be upgraded
976
+ * underneath a host's UI without them changing a line.
977
+ */
978
+ interface UseGuideAIResult {
979
+ /** The conversation so far, oldest first. */
980
+ messages: GuideAIMessage[];
981
+ /**
982
+ * The current input text, and its setter.
983
+ *
984
+ * Managed here so a host binds it straight to their TextInput rather
985
+ * than reimplementing "clear on send, block while busy" — the two
986
+ * rules every chat UI needs and each one gets subtly wrong.
987
+ */
988
+ input: string;
989
+ setInput(text: string): void;
990
+ /**
991
+ * Send `input`, or an explicit string.
992
+ *
993
+ * Ignores empty text and calls made while a turn is in flight, so a
994
+ * host does not have to guard their send button.
995
+ */
996
+ send(question?: string): Promise<void>;
997
+ /** True from send() until the turn settles. Drives a disabled state. */
998
+ busy: boolean;
999
+ /** Whether `send()` would do anything right now. */
1000
+ canSend: boolean;
1001
+ /** Status text from the agent loop ("Thinking", "Reading cart"). */
1002
+ status: string | null;
1003
+ /** Re-send the last user message. Useful after a failed turn. */
1004
+ retry(): Promise<void>;
1005
+ /** Drop the conversation and start over. */
1006
+ clear(): void;
1007
+ /** Approve the message's `pendingAction`; the agent carries it out. */
1008
+ confirm(messageId: string): Promise<void>;
1009
+ /** Decline the message's `pendingAction`. Nothing is changed. */
1010
+ cancel(messageId: string): void;
1011
+ /**
1012
+ * Push-to-talk: transcribe a recording and send it as the next message.
1013
+ * Recording is the host's (no native module here); this uploads it.
1014
+ */
1015
+ sendVoice(recording: GuideAIVoiceRecording): Promise<GuideAIVoiceResult>;
1016
+ /** True while a recording is being transcribed. */
1017
+ transcribing: boolean;
1018
+ }
1019
+ interface UseGuideAIOptions {
1020
+ /**
1021
+ * Log the full conversation and every request. Defaults to whatever
1022
+ * GuideAI.configure({ debug }) was given, so a host sets it once.
1023
+ */
1024
+ trace?: boolean;
1025
+ /**
1026
+ * The harness, for tests and for a host running two assistants at
1027
+ * once. Omitted — the normal case — it comes from GuideAI.configure(),
1028
+ * so a host never holds a reference to it.
1029
+ */
1030
+ harness?: CopilotHarness;
1031
+ }
1032
+ declare function useGuideAI(options?: UseGuideAIOptions): UseGuideAIResult;
1033
+
1034
+ /** Reads the same durable checklist as the browser adapter. */
1035
+ declare function useOnboarding(): {
1036
+ checklist: OnboardingState | null;
1037
+ loading: boolean;
1038
+ refresh: () => Promise<void>;
1039
+ dismiss: () => Promise<void>;
1040
+ };
1041
+
1042
+ export { GuideAI, GuideAIScreen, useGuideAI, useGuideAIElement, useOnboarding };
1043
+ export type { AuthTokenGetter, DeclaredElement, ElementHandlers, GuideAIConfig, HeadlessImage as GuideAIImage, GuideAIMessage, GuideAIScreenProps, GuideAIUserProfile, GuideAIVoiceRecording, GuideAIVoiceResult, NavigationRefLike, OnboardingState, OnboardingStepState, SyncNativeStore, UseGuideAIElementOptions, UseGuideAIResult };