@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.mts +385 -21
- package/dist/index.d.ts +385 -21
- package/dist/index.js +1270 -175
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1264 -176
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/WorkspaceBanner.tsx +71 -0
- package/src/auth/HandoffScreen.tsx +94 -0
- package/src/auth/JoinScreen.tsx +145 -0
- package/src/auth/LoginScreen.tsx +181 -0
- package/src/auth/OpenJoinScreen.tsx +131 -0
- package/src/auth/handoffExchange.ts +49 -0
- package/src/auth/inviteJoin.ts +44 -0
- package/src/auth/login.ts +57 -0
- package/src/auth/openJoin.ts +56 -0
- package/src/chat/MessageBubble.tsx +3 -0
- package/src/index.ts +38 -1
- package/src/plugin/Plugin.tsx +15 -9
- package/src/providers.ts +44 -0
- package/src/voice/VoiceCallSheet.tsx +4 -32
- package/src/voice/VoiceMicIndicator.tsx +99 -0
- package/src/voice/VoiceSessionHost.tsx +77 -0
- package/src/voice/useVoiceCall.ts +40 -231
- package/src/voice/voiceSession.ts +807 -0
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
|
|
4
|
-
import
|
|
5
|
-
import
|
|
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?:
|
|
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:
|
|
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
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
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:
|
|
535
|
+
callState: _ateam_ai_mobile_plugin_runtime.VoiceState;
|
|
195
536
|
sessionId: string | null;
|
|
196
537
|
error: string | null;
|
|
197
|
-
transcript:
|
|
538
|
+
transcript: _ateam_ai_mobile_plugin_runtime.VoiceTranscriptEntry[];
|
|
198
539
|
aiPartial: string;
|
|
199
540
|
userPartial: string;
|
|
200
541
|
muted: boolean;
|
|
201
|
-
startCall: (
|
|
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 };
|