@ateam-ai-mobile/ui 0.1.5 → 0.1.8

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/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { Message, JobProgress, UIAction, AdasSession, TenantMembership } from '@ateam-ai-mobile/core';
3
- import * as react from 'react';
4
- import react__default from 'react';
5
- import { PluginSurfaceMeta, PlatformPluginManifest } from '@ateam-ai-mobile/plugin-runtime';
2
+ import { Message, JobProgress, UIAction, AdasSession, TenantMembership, HandoffExchangeResult, ConsumerSignInResult, InvitePreview, ConsumerCapabilityConfig } from '@ateam-ai-mobile/core';
3
+ import * as React$1 from 'react';
4
+ import React__default, { ReactNode } from 'react';
5
+ import * as _ateam_ai_mobile_plugin_runtime from '@ateam-ai-mobile/plugin-runtime';
6
+ import { PluginSurfaceMeta, PlatformPluginManifest, VoiceState, VoiceTranscriptEntry, VoiceSessionProvider, VoicePersona, VoiceStartOptions, VoiceCaller, VoiceSnapshot, VoiceEvent, VoiceSubscription } from '@ateam-ai-mobile/plugin-runtime';
6
7
  import * as zustand from 'zustand';
7
8
 
8
9
  interface AdasChatScreenProps {
@@ -70,7 +71,7 @@ interface PluginRendererProps {
70
71
  /** Plugin manifest (from API) */
71
72
  manifest: any;
72
73
  /** Shared command buffer from parent — captures commands before plugin mounts */
73
- commandBufferRef?: react__default.MutableRefObject<any[]>;
74
+ commandBufferRef?: React__default.MutableRefObject<any[]>;
74
75
  /** Engine-supplied props from sys.focusUiPlugin(id, props) — forwarded to
75
76
  * the plugin Component as `PluginProps.props`. */
76
77
  props?: any;
@@ -126,7 +127,7 @@ declare function EmbeddedPluginWebView({ pluginId, manifest, commandBufferRef }:
126
127
  declare function usePluginBridge(pluginId: string, connectorId?: string, commandBufferRef?: React.MutableRefObject<any[]>, wsRef?: {
127
128
  current: WebSocket | null;
128
129
  }): {
129
- webViewRef: react.RefObject<any>;
130
+ webViewRef: React$1.RefObject<any>;
130
131
  sendInit: () => void;
131
132
  handleMessage: (event: {
132
133
  nativeEvent: {
@@ -174,6 +175,203 @@ interface LightboxState {
174
175
  }
175
176
  declare const useLightboxStore: zustand.UseBoundStore<zustand.StoreApi<LightboxState>>;
176
177
 
178
+ interface WorkspaceBannerProps {
179
+ /** Extra top inset (e.g. safe-area) so the banner clears the status bar. */
180
+ topInset?: number;
181
+ }
182
+ declare function WorkspaceBanner({ topInset }: WorkspaceBannerProps): react_jsx_runtime.JSX.Element | null;
183
+
184
+ interface HandoffScreenProps {
185
+ /** The one-time handoff code the host captured from the deep link (null = nothing pending). */
186
+ code: string | null;
187
+ /**
188
+ * Adopt the handed-off tenant. AWAITED before onSignedIn so the host's active
189
+ * tenant is set before the session reads it (the exchange result carries the
190
+ * tenant the code was bound to). No-op is fine when the result has no tenant.
191
+ */
192
+ onAdoptTenant: (tenant: string) => void | Promise<void>;
193
+ /** Adopt the session JWT (host wires its authStore.login → core session + tenantConfirmed). */
194
+ onSignedIn: (jwt: string) => void | Promise<void>;
195
+ /** Clear the consumed single-use code so a relaunch never retries it. Called after success. */
196
+ onDone: () => void;
197
+ /** Back to sign in — the host toggles the pre-auth phase (not a router). */
198
+ onCancel: () => void;
199
+ }
200
+ declare function HandoffScreen({ code, onAdoptTenant, onSignedIn, onDone, onCancel }: HandoffScreenProps): react_jsx_runtime.JSX.Element;
201
+
202
+ /**
203
+ * Handoff exchange — the pure, testable half of HandoffScreen (R9B).
204
+ *
205
+ * Kept JSX-free in its own module (like R6's runLogoutTeardown) so the
206
+ * security-sensitive error mapper and the adopt-before-sign-in ORDERING can be
207
+ * node-tested directly, without rendering the RN component.
208
+ */
209
+
210
+ interface HandoffExchangeDeps {
211
+ /** Trade the one-time code for a session JWT (host injects core.exchangeHandoff). */
212
+ exchange: (code: string) => Promise<HandoffExchangeResult>;
213
+ /** Adopt the handed-off tenant (host's single tenant writer). */
214
+ onAdoptTenant: (tenant: string) => void | Promise<void>;
215
+ /** Adopt the session JWT. */
216
+ onSignedIn: (jwt: string) => void | Promise<void>;
217
+ /** Clear the consumed single-use code. */
218
+ onDone: () => void;
219
+ }
220
+ /**
221
+ * Run the handoff: exchange → (adopt tenant) → sign in → clear the code, in that
222
+ * exact order. The tenant adoption is AWAITED BEFORE onSignedIn so the host's
223
+ * active tenant is set before the session reads it — a cross-tenant handoff must
224
+ * never sign in under the wrong tenant. Throws on any failure (the caller maps it
225
+ * to a user-facing message); onSignedIn / onDone never run on the error path.
226
+ */
227
+ declare function runHandoffExchange(code: string, deps: HandoffExchangeDeps): Promise<void>;
228
+ /**
229
+ * Map the server's rejection to plainer copy. SECURITY: the exchange endpoint
230
+ * deliberately does NOT tell an anonymous caller whether a code was unknown (404)
231
+ * vs used/expired (410) — distinguishing them would confirm a real code to a
232
+ * replay attacker. So both collapse to ONE message; never promise "expired" vs
233
+ * "already used". 429 is the one safe-to-distinguish case (a rate-limit hint
234
+ * leaks nothing about code validity).
235
+ */
236
+ declare function handoffErrorMessage(msg?: string): string;
237
+
238
+ interface OpenJoinScreenProps {
239
+ /** Adopt the joined tenant (host's single ACTIVE_TENANT writer), awaited before sign-in. */
240
+ onAdoptTenant: (tenant: string) => void | Promise<void>;
241
+ /** Adopt the session JWT (host: authStore.login → core session + tenantConfirmed). */
242
+ onSignedIn: (jwt: string) => void | Promise<void>;
243
+ /** Back to sign in — the host toggles the pre-auth phase (not a router). */
244
+ onBack: () => void;
245
+ }
246
+ declare function OpenJoinScreen({ onAdoptTenant, onSignedIn, onBack }: OpenJoinScreenProps): react_jsx_runtime.JSX.Element;
247
+
248
+ /**
249
+ * Open self-signup — the pure, testable half of OpenJoinScreen (R9B).
250
+ *
251
+ * JSX-free so the join ORDERING (join → adopt tenant → sign in) and the error
252
+ * mapper are node-testable without rendering. The native id_token acquisition
253
+ * happens in the component via getOAuthProvider(); the acquired token is passed
254
+ * in here, so this module stays pure and provider-free.
255
+ */
256
+
257
+ interface OpenJoinDeps {
258
+ /** Exchange { tenant, id_token } for a session JWT (host injects core.openJoinWith*). */
259
+ join: (slug: string, idToken: string) => Promise<ConsumerSignInResult>;
260
+ /** Adopt the joined tenant (host's single ACTIVE_TENANT writer). */
261
+ onAdoptTenant: (tenant: string) => void | Promise<void>;
262
+ /** Adopt the session JWT. */
263
+ onSignedIn: (jwt: string) => void | Promise<void>;
264
+ }
265
+ /**
266
+ * Complete an open self-signup with an already-acquired id_token.
267
+ *
268
+ * Returns false WITHOUT side effects when idToken is null — the caller acquired
269
+ * no token (the user cancelled the native sheet, or it returned none), which is
270
+ * a silent no-op, never an error. Otherwise: join → adopt the joined tenant →
271
+ * sign in, in that order (the tenant is adopted only AFTER the server confirms
272
+ * the join, and BEFORE sign-in so the session reads the right tenant). Throws on
273
+ * a join failure (the caller maps it to a message); sign-in never runs then.
274
+ */
275
+ declare function completeOpenJoin(slug: string, idToken: string | null, deps: OpenJoinDeps): Promise<boolean>;
276
+ /**
277
+ * Map the server's open-join rejection to plainer copy. Unlike the handoff
278
+ * exchange, these ARE safe to distinguish — the caller already knows the tenant
279
+ * slug they typed, so "invite-only" / "no such team" leak nothing.
280
+ */
281
+ declare function openJoinErrorMessage(msg?: string): string;
282
+
283
+ interface JoinScreenProps {
284
+ /** The invite code the host captured from the deep link. */
285
+ code: string | null;
286
+ /** Adopt the session JWT (host: authStore.login → core session + tenantConfirmed). */
287
+ onSignedIn: (jwt: string) => void | Promise<void>;
288
+ /** Clear the consumed single-use invite so a relaunch never retries it. */
289
+ onDone: () => void;
290
+ /** Dismiss on a bad invite — the host toggles the pre-auth phase (not a router). */
291
+ onCancel: () => void;
292
+ }
293
+ declare function JoinScreen({ code, onSignedIn, onDone, onCancel }: JoinScreenProps): react_jsx_runtime.JSX.Element;
294
+
295
+ /**
296
+ * Invite join — the pure, testable half of JoinScreen (R9B).
297
+ *
298
+ * JSX-free so the join ORDERING and the header derivation are node-testable
299
+ * without rendering. The native id_token acquisition happens in the component via
300
+ * getOAuthProvider(); the acquired token is passed in here.
301
+ */
302
+
303
+ interface InviteJoinDeps {
304
+ /** Exchange { invite, id_token } for a tenant-scoped JWT (host injects core.joinWith*). */
305
+ join: (code: string, idToken: string) => Promise<ConsumerSignInResult>;
306
+ /** Clear the consumed single-use invite (host store). */
307
+ onDone: () => void;
308
+ /** Adopt the session JWT. */
309
+ onSignedIn: (jwt: string) => void | Promise<void>;
310
+ }
311
+ /**
312
+ * Complete an invite join with an already-acquired id_token.
313
+ *
314
+ * Returns false WITHOUT side effects when idToken is null (user cancelled the
315
+ * native sheet). Otherwise: join → clear the consumed invite → sign in, in that
316
+ * order (matching the app: the invite is cleared before sign-in so a relaunch
317
+ * never retries a burned code). Throws on a join failure; the invite is NOT
318
+ * cleared and sign-in never runs then, so the user can retry the same invite.
319
+ */
320
+ declare function completeInviteJoin(code: string, idToken: string | null, deps: InviteJoinDeps): Promise<boolean>;
321
+ /** The invite header copy. Role is optional — "Join <Org>?" when absent. */
322
+ declare function inviteHeadline(preview: InvitePreview | null): string;
323
+
324
+ interface LoginScreenProps {
325
+ /** The reactive pre-auth tenant pick (host: useSession().tenant). Re-fetches config on change. */
326
+ activeTenant: string;
327
+ /** The fallback tenant for the consumer-disabled self-heal guard (host: DEFAULT_TENANT). */
328
+ defaultTenant: string;
329
+ /** Reset to the default tenant + persist + bump activeTenant so this screen refetches. */
330
+ onConsumerDisabled: () => void | Promise<void>;
331
+ /** Adopt the session JWT (host: authStore.login → core session + tenantConfirmed). */
332
+ onSignedIn: (jwt: string) => void | Promise<void>;
333
+ /** Open the host's "join a team by name" (OpenJoin) sub-view. Link hidden when omitted. */
334
+ onJoinTeam?: () => void;
335
+ }
336
+ declare function LoginScreen({ activeTenant, defaultTenant, onConsumerDisabled, onSignedIn, onJoinTeam }: LoginScreenProps): react_jsx_runtime.JSX.Element;
337
+
338
+ /**
339
+ * Login — the pure, testable half of LoginScreen (R9B).
340
+ *
341
+ * JSX-free so the tenant client-id derivation, the consumer-disabled self-heal
342
+ * decision, and the sign-in ordering are node-testable without rendering.
343
+ */
344
+
345
+ /**
346
+ * The Google client ids to sign in with, from the tenant's consumer config.
347
+ * webClientId sets the id_token AUDIENCE the server verifies; iosClientId makes
348
+ * the native account picker show (missing → the SDK silently falls back to a web
349
+ * flow on iOS). Accepts the legacy single `client_id` as the web id.
350
+ */
351
+ declare function googleClientIds(config: ConsumerCapabilityConfig): {
352
+ webClientId: string;
353
+ iosClientId: string;
354
+ };
355
+ /**
356
+ * Whether a failed config fetch should self-heal by falling back to the default
357
+ * tenant. A consumer-DISABLED tenant 404s with "capability not enabled"; rather
358
+ * than dead-end the user on red text we coerce back to the default and refetch.
359
+ * Guarded by currentTenant !== defaultTenant so we never loop on the default.
360
+ */
361
+ declare function shouldRecoverToDefaultTenant(errorMsg: string | undefined, currentTenant: string, defaultTenant: string): boolean;
362
+ interface ProviderSignInDeps {
363
+ /** Trade the provider id_token for a session JWT (host injects core.signInWith*). */
364
+ exchange: (idToken: string) => Promise<ConsumerSignInResult>;
365
+ /** Adopt the session JWT. */
366
+ onSignedIn: (jwt: string) => void | Promise<void>;
367
+ }
368
+ /**
369
+ * Complete a provider sign-in with an already-acquired id_token: exchange → sign
370
+ * in. Returns false WITHOUT side effects on a null token (user cancelled the
371
+ * native sheet). Throws on an exchange failure; sign-in never runs then.
372
+ */
373
+ declare function completeProviderSignIn(idToken: string | null, deps: ProviderSignInDeps): Promise<boolean>;
374
+
177
375
  interface Props {
178
376
  visible: boolean;
179
377
  onClose: () => void;
@@ -182,38 +380,204 @@ interface Props {
182
380
  }
183
381
  declare function VoiceCallSheet({ visible, onClose, skillSlug }: Props): react_jsx_runtime.JSX.Element | null;
184
382
 
185
- interface TranscriptEntry {
186
- role: 'user' | 'ai' | 'tool';
187
- text: string;
188
- ts: number;
383
+ type CallState = VoiceState;
384
+ type TranscriptEntry = VoiceTranscriptEntry;
385
+ interface InternalState {
386
+ callState: CallState;
387
+ sessionId: string | null;
388
+ error: string | null;
389
+ transcript: TranscriptEntry[];
390
+ aiPartial: string;
391
+ userPartial: string;
392
+ muted: boolean;
393
+ /** User VAD, from the mini-app's speechActivity events. */
394
+ userSpeaking: boolean;
395
+ /** Epoch ms of the last speaking:false, or null before the first utterance. */
396
+ lastUserSpeechEndedAt: number | null;
397
+ /** The last device-tool call the model made (name + when), or null. */
398
+ lastDeviceTool: {
399
+ name: string;
400
+ at: number;
401
+ } | null;
402
+ /** The WebView should be mounted (a session is wanted / live). */
403
+ mounted: boolean;
404
+ /** The WebView source, computed ONCE at start() so state changes (transcript,
405
+ * partials) never remount the WebView and drop the session. */
406
+ webViewSource: {
407
+ uri: string;
408
+ } | null;
409
+ skillSlug?: string;
410
+ /** Per-call persona override ({ name, style }) — sent to the voice page on the
411
+ * session URL and declared on POST /session; the server layers it. */
412
+ persona?: VoicePersona;
413
+ }
414
+ declare class VoiceSessionManager implements VoiceSessionProvider {
415
+ private state;
416
+ private listeners;
417
+ private webView;
418
+ private pending;
419
+ /**
420
+ * WHO owns the one physical session. Set to the caller that started it from
421
+ * idle (a plugin's pluginId, or the PLATFORM symbol for the built-in sheet),
422
+ * and kept after stop until the next start reassigns it — so a plugin can read
423
+ * its own just-ended transcript but another plugin never can.
424
+ *
425
+ * ISOLATION (GPT blocker on 8881225): every access is scoped through this. A
426
+ * different plugin must not read, control, or attach to another plugin's live
427
+ * conversation just because it declared voice.
428
+ */
429
+ private owner;
430
+ /** EVERY start() still in its pre-mount awaits (tool preload, token refresh).
431
+ * The session is not 'connecting' yet, so a stop() arriving now would otherwise
432
+ * be an idle no-op — and a start would then mount a call for a screen that has
433
+ * already gone. A set, not one slot: two concurrent starts must BOTH be
434
+ * cancellable. stop() cancels every pending start of its caller (the platform:
435
+ * all of them); another plugin's stop cancels none. */
436
+ private pendingStarts;
437
+ /**
438
+ * The device-tool names Core ALLOWED for this voice session — Core's sanitized
439
+ * allowset, delivered to the phone in a `deviceToolAllowset` message.
440
+ * Authorization gates against THIS (necessary, alongside the registry's can-do).
441
+ *
442
+ * WHY (GPT gate 1, corrected): the phone only PROPOSES its available tools
443
+ * (sendDeviceToolList); CORE is the session authority and decides which are
444
+ * accepted (session.device_tools). Freezing the phone's own proposal would let
445
+ * a tool Core REJECTED still run locally. So the authz set is populated from
446
+ * Core's accepted set, never from listDeviceTools(). Until Core's set arrives
447
+ * this is null and every call FAILS CLOSED. Pinned once per session; the live
448
+ * registry (which drifts via lazy loads / host registration / tenant changes)
449
+ * never feeds it.
450
+ */
451
+ private frozenTools;
452
+ /** The voice `sessionId` the current allowset is BOUND to. Authorization
453
+ * requires it to equal the active session's id (state.sessionId), so a stale
454
+ * allowset from a previous/other session is refused. */
455
+ private frozenSessionId;
456
+ /** A CLEAN call (skills:'none') started by a plugin offers and runs ONLY that
457
+ * plugin's own device tools — not other solutions' tools that happen to be
458
+ * registered on the same phone. null = every registered tool (a normal call,
459
+ * or the platform's). Set by every start(); like `owner`, kept after stop.
460
+ * ONE identity: the caller is the TRUSTED manifest pluginId gateVoice stamps,
461
+ * and the bundleManager registers a plugin's tools under that same id (never
462
+ * the bundle's own short `id`), so exact equality is the whole check. */
463
+ private toolScope;
464
+ /** The platform (built-in sheet, mic indicator, session host) sees and
465
+ * controls any session — it is the user's own surface. A plugin caller may
466
+ * only touch a session it owns. */
467
+ private canAccess;
468
+ private foreignError;
469
+ /** The offscreen WebView sets its ref here so the session can inject into it. */
470
+ setWebView(ref: {
471
+ injectJavaScript?: (js: string) => void;
472
+ } | null): void;
473
+ /** Whole state — the host reads `mounted` + `webViewSource` to decide what to
474
+ * render, and every reader derives its snapshot from this. */
475
+ getInternal(): Readonly<InternalState>;
476
+ readonly injectedTrackHook = "\n(function() {\n if (window._adaTrackHookInstalled) return;\n window._adaTrackHookInstalled = true;\n window._allAudioTracks = [];\n window._adaPeerConnections = [];\n\n function track(t) {\n if (t && t.kind === 'audio' && window._allAudioTracks.indexOf(t) === -1) {\n window._allAudioTracks.push(t);\n }\n }\n\n try {\n if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {\n const origGUM = navigator.mediaDevices.getUserMedia.bind(navigator.mediaDevices);\n navigator.mediaDevices.getUserMedia = async function(constraints) {\n const stream = await origGUM(constraints);\n if (constraints && constraints.audio) {\n try { stream.getAudioTracks().forEach(track); } catch (e) {}\n }\n return stream;\n };\n }\n } catch (e) {}\n\n try {\n if (window.RTCPeerConnection) {\n const origPC = window.RTCPeerConnection;\n const origAddTrack = origPC.prototype.addTrack;\n const origAddStream = origPC.prototype.addStream;\n const origAddTransceiver = origPC.prototype.addTransceiver;\n\n origPC.prototype.addTrack = function(t) {\n if (t && t.kind === 'audio') track(t);\n return origAddTrack.apply(this, arguments);\n };\n if (origAddStream) {\n origPC.prototype.addStream = function(stream) {\n try { stream.getAudioTracks().forEach(track); } catch (e) {}\n return origAddStream.apply(this, arguments);\n };\n }\n if (origAddTransceiver) {\n origPC.prototype.addTransceiver = function(arg) {\n if (arg && arg.kind === undefined && arg.track && arg.track.kind === 'audio') {\n track(arg.track);\n }\n const tx = origAddTransceiver.apply(this, arguments);\n try {\n if (tx && tx.sender && tx.sender.track && tx.sender.track.kind === 'audio') track(tx.sender.track);\n } catch (e) {}\n return tx;\n };\n }\n }\n } catch (e) {}\n\n window._adaSetMuted = function(muted) {\n let n = 0;\n if (Array.isArray(window._allAudioTracks)) {\n window._allAudioTracks.forEach(function(t) {\n try { t.enabled = !muted; n++; } catch (e) {}\n });\n }\n return n;\n };\n})(); true;\n";
477
+ start(opts?: VoiceStartOptions, caller?: VoiceCaller): Promise<VoiceSnapshot>;
478
+ stop(caller?: VoiceCaller): void;
479
+ setMuted(muted: boolean, caller?: VoiceCaller): void;
480
+ prompt(input: string, caller?: VoiceCaller): void;
481
+ /**
482
+ * Stream a silent observation into the live session — tell the model without
483
+ * asking it to speak. The cheap channel behind the gated props.voice.observe();
484
+ * every item stays in the model's context for the call, so a caller sends only
485
+ * material changes. Owner-scoped like every other control.
486
+ */
487
+ observe(text: string, caller?: VoiceCaller): void;
488
+ getSnapshot(caller?: VoiceCaller): VoiceSnapshot;
489
+ private realSnapshot;
490
+ private foreignSnapshot;
491
+ subscribe(cb: (event: VoiceEvent) => void, caller?: VoiceCaller): VoiceSubscription;
492
+ transcript(caller?: VoiceCaller): TranscriptEntry[];
493
+ private buildUri;
494
+ /** Handle a postMessage from the WebView. `event` is the RN WebView event. */
495
+ handleMessage(event: any): void;
496
+ /**
497
+ * Final authorization = Core's frozen allowset ∩ the current valid local
498
+ * registry, bound to the ACTIVE session. All must hold:
499
+ * - a Core allowset arrived and is bound to THIS session (frozenSessionId ===
500
+ * the active state.sessionId) — a missing or stale/wrong-session allowset
501
+ * fails closed;
502
+ * - the tool is IN that allowset (Core approved it); AND
503
+ * - the tool is STILL registered locally right now (removed-after-approval is
504
+ * refused; a lazily-added tool Core never approved is refused) — and, on a
505
+ * clean call, registered by the call's owner.
506
+ * No fallback to the live registry alone, ever.
507
+ */
508
+ private isToolAuthorized;
509
+ /** Tell the mini-app the call was refused — a stated reason, never a silent drop.
510
+ * `notReady` distinguishes a transient "allowset not here yet" (retry) from a
511
+ * final "not allowed in this session". */
512
+ private postDeviceToolRefusal;
513
+ /** PROPOSE the phone's available tools — this is an offer to Core, not the
514
+ * authorization set. Core sanitizes it and returns the accepted set in a
515
+ * `deviceToolAllowset` message, which is what actually gates execution. */
516
+ private sendDeviceToolList;
517
+ /** Run a device tool and post the outcome back into the WebView. */
518
+ private runDeviceTool;
519
+ private setState;
520
+ private appendTranscript;
521
+ private emit;
522
+ private waitForConnect;
523
+ private resolvePending;
524
+ private rejectPending;
525
+ /** TEST ONLY: reset the singleton to a clean idle state (mirrors the
526
+ * __resetSessionSyncForTest precedent). Not part of any surface. */
527
+ __resetForTest(): void;
189
528
  }
190
- type CallState = 'idle' | 'connecting' | 'connected' | 'error';
529
+ /** The one live session. Handed to setVoiceProvider(voiceSession) at startup. */
530
+ declare const voiceSession: VoiceSessionManager;
531
+
191
532
  declare function useVoiceCall({ skillSlug }?: {
192
533
  skillSlug?: string;
193
534
  }): {
194
- callState: CallState;
535
+ callState: _ateam_ai_mobile_plugin_runtime.VoiceState;
195
536
  sessionId: string | null;
196
537
  error: string | null;
197
- transcript: TranscriptEntry[];
538
+ transcript: _ateam_ai_mobile_plugin_runtime.VoiceTranscriptEntry[];
198
539
  aiPartial: string;
199
540
  userPartial: string;
200
541
  muted: boolean;
201
- startCall: () => Promise<void>;
542
+ startCall: (opts?: {
543
+ skillSlug?: string;
544
+ }) => Promise<_ateam_ai_mobile_plugin_runtime.VoiceSnapshot>;
202
545
  stopCall: () => void;
203
546
  toggleMute: () => void;
204
- handleWebViewMessage: (event: any) => void;
205
- getWebViewSource: () => {
206
- uri: string;
207
- };
208
- webViewRef: react.RefObject<any>;
209
- injectedTrackHook: string;
210
547
  };
211
548
 
549
+ declare function VoiceSessionHost(): react_jsx_runtime.JSX.Element | null;
550
+
551
+ declare function VoiceMicIndicator({ topInset }: {
552
+ topInset?: number;
553
+ }): react_jsx_runtime.JSX.Element | null;
554
+
212
555
  /**
213
556
  * Host integration points for @ateam-ai-mobile/ui — same DI pattern as
214
557
  * @ateam-ai-mobile/plugin-runtime's host.ts. Everything has a safe default; the host
215
558
  * (or @ateam-ai-mobile/expo) wires real implementations at startup.
216
559
  */
560
+
561
+ interface OAuthGoogleConfig {
562
+ webClientId?: string;
563
+ iosClientId?: string;
564
+ }
565
+ interface OAuthProvider {
566
+ /** Acquire a Google OIDC id_token. `cfg` present → tenant client ids; absent → host bundled id. Null on user-cancel. */
567
+ signInWithGoogle(cfg?: OAuthGoogleConfig): Promise<string | null>;
568
+ /** Acquire an Apple id_token. Null on user-cancel. */
569
+ signInWithApple(): Promise<string | null>;
570
+ /** AppleAuthentication.isAvailableAsync() (iOS 13+); resolves false everywhere else. Async so the screen probes in an effect, never renders a stale false. */
571
+ isAppleAvailable(): Promise<boolean>;
572
+ /** The real Apple HIG button as a slot — the screen decides WHEN to show it + supplies its layout `style`, the host supplies the native node + HIG chrome. */
573
+ renderAppleButton(props: {
574
+ onPress: () => void;
575
+ disabled: boolean;
576
+ style?: unknown;
577
+ }): ReactNode;
578
+ }
579
+ /** Register a native OAuth implementation (e.g. via @ateam-ai-mobile/expo). */
580
+ declare function setOAuthProvider(oauth: OAuthProvider | null): void;
217
581
  interface ClipboardProvider {
218
582
  setString(text: string): Promise<void>;
219
583
  }
@@ -230,4 +594,4 @@ declare function setUIActionNavigator(navigate: UIActionNavigator | null): void;
230
594
  type ExtraHostsGetter = () => string[];
231
595
  declare function setPluginWebViewExtraHosts(getter: ExtraHostsGetter | null): void;
232
596
 
233
- export { AdasChatScreen, type AdasChatScreenProps, type CallState, ChatInput, type ClipboardProvider, DaySeparator, EmbeddedPluginWebView, JobProgressBar, Lightbox, MessageBubble, Plugin, PluginSurface, TableCardBlock, TenantPicker, type TenantPickerProps, type TranscriptEntry, TypingIndicator, type UIActionRoute, UIActionsRow, VoiceCallSheet, WidgetHost, WidgetWebView, buildPluginBridgeJS, openWidget, setClipboardProvider, setPluginWebViewExtraHosts, setUIActionNavigator, useLightboxStore, usePluginBridge, useVoiceCall, useWidgetHostStore };
597
+ export { AdasChatScreen, type AdasChatScreenProps, type CallState, ChatInput, type ClipboardProvider, DaySeparator, EmbeddedPluginWebView, type HandoffExchangeDeps, HandoffScreen, type HandoffScreenProps, type InviteJoinDeps, JobProgressBar, JoinScreen, type JoinScreenProps, Lightbox, LoginScreen, type LoginScreenProps, MessageBubble, type OAuthGoogleConfig, type OAuthProvider, type OpenJoinDeps, OpenJoinScreen, type OpenJoinScreenProps, Plugin, PluginSurface, type ProviderSignInDeps, TableCardBlock, TenantPicker, type TenantPickerProps, type TranscriptEntry, TypingIndicator, type UIActionRoute, UIActionsRow, VoiceCallSheet, VoiceMicIndicator, VoiceSessionHost, WidgetHost, WidgetWebView, WorkspaceBanner, type WorkspaceBannerProps, buildPluginBridgeJS, completeInviteJoin, completeOpenJoin, completeProviderSignIn, googleClientIds, handoffErrorMessage, inviteHeadline, openJoinErrorMessage, openWidget, runHandoffExchange, setClipboardProvider, setOAuthProvider, setPluginWebViewExtraHosts, setUIActionNavigator, shouldRecoverToDefaultTenant, useLightboxStore, usePluginBridge, useVoiceCall, useWidgetHostStore, voiceSession };