@ateam-ai-mobile/core 0.1.6 → 0.1.7

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
@@ -197,18 +197,367 @@ declare function isDevHost(u: string | undefined | null): boolean;
197
197
  * wraps this for reactivity; imperative code can call it directly. */
198
198
  declare function getSession(): Session;
199
199
 
200
+ /**
201
+ * SDK-owned session state machine (R6).
202
+ *
203
+ * Until R6 the JWT lifecycle lived in the app (apps/mobile/src/stores/
204
+ * authStore.ts): token storage, the decode/user projection, the startup
205
+ * restore + silent refresh, and the refresh decision table. A second host
206
+ * (host-demo, a third-party app) had to re-implement all of it. R6 moves the
207
+ * MECHANISM here; the host keeps only what is genuinely its own — secure
208
+ * storage and the side effects of signing out.
209
+ *
210
+ * Two seams keep this free of any app/Expo import and free of an auth<->bridge
211
+ * cycle (the plan's explicit constraint):
212
+ * - `persistence` the host's secure JWT storage (get/save/clear). The SDK
213
+ * never imports expo-secure-store.
214
+ * - `onLogout` the host's teardown, run in ONE place so the order is
215
+ * owned here: stop the device/relay bridge, unregister push
216
+ * (while the token still exists), clear plugin storage +
217
+ * bundle cache + device-tool registry, close chat. The SDK
218
+ * calls it; it never imports the stores that do the work.
219
+ *
220
+ * The refresh decision table and JWT projection are moved VERBATIM from the app
221
+ * so behaviour is identical (the R0.5 authRefresh baseline is the oracle).
222
+ */
223
+
224
+ interface SessionUser {
225
+ actorId: string;
226
+ email?: string;
227
+ displayName?: string;
228
+ tenantRole?: string | null;
229
+ serviceRole?: string | null;
230
+ platformRoles?: string[];
231
+ roles: string[];
232
+ }
233
+ type SessionStatus = 'loading' | 'authenticated' | 'signed_out';
234
+ interface AuthSessionState {
235
+ token: string | null;
236
+ user: SessionUser | null;
237
+ status: SessionStatus;
238
+ }
239
+ /**
240
+ * The host's secure JWT storage. Only the token lives here — tenant selection
241
+ * and env are resolved through the config getters, not stored by the session
242
+ * (derive-never-store).
243
+ */
244
+ interface SessionPersistence {
245
+ loadToken(): Promise<string | null>;
246
+ saveToken(token: string): Promise<void>;
247
+ clearToken(): Promise<void>;
248
+ }
249
+ interface AuthSessionDeps {
250
+ persistence: SessionPersistence;
251
+ /**
252
+ * Host teardown for sign-out, run by `logout()` WHILE THE TOKEN STILL EXISTS
253
+ * (push unregister needs the JWT) and BEFORE the SDK clears it. The host does
254
+ * the app-specific work — stop the bridge, unregister push, clear the tenant
255
+ * keys, evict plugin storage / bundle cache / device-tool registry, close
256
+ * chat — in this one hook, so there is no auth->bridge import cycle.
257
+ */
258
+ onLogout?: () => void | Promise<void>;
259
+ /**
260
+ * Called when a token is adopted (login, or a successful refresh) with the
261
+ * projected user, so the host can persist side data (e.g. its TENANT key)
262
+ * without the session reaching into app storage.
263
+ */
264
+ onToken?: (token: string, user: SessionUser | null) => void | Promise<void>;
265
+ }
266
+ declare function userFromJwt(token: string): SessionUser | null;
267
+ interface AuthSession {
268
+ /** Current token (synchronous, in-memory) — the TokenProvider read path. */
269
+ getToken(): string | null;
270
+ getUser(): SessionUser | null;
271
+ getState(): AuthSessionState;
272
+ subscribe(cb: (s: AuthSessionState) => void): () => void;
273
+ /** Startup: load the persisted token, project the user, and silently refresh
274
+ * an expired JWT (ok → adopt, auth_failed → sign out, transient → keep). */
275
+ restore(): Promise<void>;
276
+ /** Adopt a freshly-minted token (host authenticated the user). */
277
+ login(token: string): Promise<void>;
278
+ /** Sign out: host teardown (with token still present) → clear token → state. */
279
+ logout(): Promise<void>;
280
+ /** The 401 refresh path. Adopts a new token on success; returns the API
281
+ * client's outcome shape ('ok' → retry, 'auth_failed' → sign out). */
282
+ refresh(): Promise<RefreshOutcome>;
283
+ }
284
+ declare function createAuthSession(deps: AuthSessionDeps): AuthSession;
285
+
286
+ /**
287
+ * SDK-owned push registration lifecycle (R7).
288
+ *
289
+ * R6 left the sign-out push unregister as a temporary APP hook (authStore's
290
+ * teardown dynamically imported getCurrentPushToken + unregisterDevice). R7
291
+ * finishes it with an SDK-owned service: the token→backend LIFECYCLE lives here
292
+ * (register when a token is acquired, re-register when it refreshes, unregister
293
+ * on sign-out, remember the current token), and the host supplies only the two
294
+ * genuinely native/host pieces via an injected backend:
295
+ * - registerDevice / unregisterDevice (the Core notif-api calls)
296
+ * The host's native layer (FCM/APNs acquisition, permission prompts, token
297
+ * refresh listeners) feeds tokens in via setToken(); it does not own the
298
+ * backend lifecycle any more.
299
+ *
300
+ * No Expo/RN import here — the same seam works for any host.
301
+ */
302
+ interface PushBackend {
303
+ /** Register a device push token with the backend (needs a live JWT). */
304
+ registerDevice(token: string): Promise<void>;
305
+ /** Unregister a device push token from the backend. */
306
+ unregisterDevice(token: string): Promise<void>;
307
+ }
308
+ interface PushRegistration {
309
+ /**
310
+ * Adopt a freshly-acquired native push token and register it with the backend.
311
+ * Idempotent — re-registering the SAME token is a no-op; a NEW token (refresh)
312
+ * registers the new one. Pass null to forget without a backend call.
313
+ * Best-effort: a backend failure is logged, never thrown (push is not
314
+ * allowed to break sign-in).
315
+ */
316
+ setToken(token: string | null): Promise<void>;
317
+ /** The current registered token, or null. */
318
+ getToken(): string | null;
319
+ /**
320
+ * Sign-out: unregister the current token from the backend and forget it.
321
+ * Fire-and-forget on the network call so a flaky connection can't BLOCK
322
+ * sign-out (the ordering contract: this runs while the JWT still exists, then
323
+ * the session clears the JWT). Returns once the token is forgotten locally.
324
+ */
325
+ unregister(): Promise<void>;
326
+ }
327
+ declare function createPushRegistration(backend: PushBackend): PushRegistration;
328
+
329
+ /**
330
+ * Register a reaction to run when the app returns to the foreground (e.g. the
331
+ * host's battery refresh, or an ACS GET safety-net). Returns an unsubscribe.
332
+ */
333
+ declare function onAppForeground(cb: () => void): () => void;
334
+ /**
335
+ * The host calls this from its single AppState listener when the app becomes
336
+ * `active`. Order is owned here: force the ACS WS to reconnect first, then run
337
+ * the registered host reactions. Each step is guarded so one failure can't stop
338
+ * the rest.
339
+ */
340
+ declare function notifyAppForeground(): void;
341
+
342
+ /**
343
+ * SDK-owned app-gate cascade (R9).
344
+ *
345
+ * The host's root layout used to inline the "which screen do we show right now"
346
+ * decision — a linear cascade (loading → onboarding → auth → tenant →
347
+ * capabilities → app) with a pre-auth PRIORITY (a web→app handoff beats an
348
+ * invite, which beats the default login). That decision is generic navigation
349
+ * orchestration: every host makes the same one. R9 moves the DECISION here as a
350
+ * pure function; the host keeps the presentation (its branded phase screens) and
351
+ * the Expo Router `<Stack>` that owns route registration.
352
+ *
353
+ * No react-native import — a pure function of the flags the host already tracks
354
+ * (auth comes from the SDK session; onboarding/capabilities/invite/handoff are
355
+ * host state), so it is trivially unit-testable and host-agnostic.
356
+ */
357
+ type AdasPhase = 'loading' | 'onboarding' | 'handoff' | 'invite' | 'login' | 'tenant' | 'capabilities' | 'app';
358
+ interface AdasGateFlags {
359
+ /** Host config/service-URL hydration finished. */
360
+ configHydrated: boolean;
361
+ /** The SDK session is still restoring (status === 'loading'). */
362
+ authLoading: boolean;
363
+ /** First-launch onboarding: true = done, false = show it, undefined = still checking. */
364
+ onboarded: boolean | undefined;
365
+ /** The SDK session is authenticated. */
366
+ isAuthenticated: boolean;
367
+ /** Host capability init still running. */
368
+ capabilitiesLoading: boolean;
369
+ /** A pending web→app handoff (one-time code → session) — the strongest pre-auth signal. */
370
+ handoffPending: boolean;
371
+ /** A pending invite (tenant join → OAuth). */
372
+ invitePending: boolean;
373
+ /** Whether the host requires an explicit tenant pick before entering the app
374
+ * (multi-tenant). A single-tenant host passes false and skips the phase. */
375
+ requireTenantPick: boolean;
376
+ /** The user has confirmed/selected a tenant (auto-true in single-tenant). */
377
+ tenantConfirmed: boolean;
378
+ /** Capability onboarding done. */
379
+ hasCompletedSetup: boolean;
380
+ }
381
+ /**
382
+ * Resolve the single screen phase to render, in the exact order the host used:
383
+ * a still-hydrating app shows `loading`; then first-launch `onboarding`; then,
384
+ * if signed out, the pre-auth priority handoff > invite > login; then (only when
385
+ * the host requires it) the tenant pick; then capability setup; then the app.
386
+ */
387
+ declare function resolveAdasPhase(f: AdasGateFlags): AdasPhase;
388
+
389
+ interface ConsumerProviderConfig {
390
+ google?: {
391
+ web_client_id?: string;
392
+ ios_client_id?: string;
393
+ android_client_id?: string;
394
+ client_id?: string;
395
+ };
396
+ apple?: {
397
+ service_id: string;
398
+ };
399
+ }
400
+ interface ConsumerCapabilityConfig {
401
+ ok: boolean;
402
+ tenant: string;
403
+ providers: ConsumerProviderConfig;
404
+ linked_channels: string[];
405
+ }
406
+ interface ConsumerSignInResult {
407
+ ok: boolean;
408
+ jwt: string;
409
+ actor: {
410
+ id: string;
411
+ email: string;
412
+ role: string;
413
+ };
414
+ }
415
+ interface OtpStartResult {
416
+ ok: boolean;
417
+ challenge_id: string;
418
+ sent_to: string;
419
+ provider: string;
420
+ debug_code?: string;
421
+ }
422
+ /**
423
+ * Fetch the public consumer config for the active tenant. Returns the public
424
+ * OAuth client IDs needed to render Google/Apple sign-in buttons.
425
+ */
426
+ declare function fetchConsumerConfig(): Promise<ConsumerCapabilityConfig>;
427
+ /**
428
+ * Exchange a Google id_token for a tenant-scoped consumer JWT. The id_token
429
+ * comes from the host's native Google sign-in flow.
430
+ */
431
+ declare function signInWithGoogle(idToken: string): Promise<ConsumerSignInResult>;
432
+ /** Exchange an Apple id_token for a tenant-scoped consumer JWT. */
433
+ declare function signInWithApple(idToken: string): Promise<ConsumerSignInResult>;
434
+ interface HandoffExchangeResult extends ConsumerSignInResult {
435
+ tenant?: string;
436
+ }
437
+ declare function exchangeHandoff(code: string): Promise<HandoffExchangeResult>;
438
+ interface InvitePreview {
439
+ ok: boolean;
440
+ org: string;
441
+ role: string | null;
442
+ }
443
+ /**
444
+ * Preview an invite (no auth) to render the "Join <Org> as <role>?" screen.
445
+ * Throws with a friendly message on 404 (invalid/expired).
446
+ */
447
+ declare function previewInvite(code: string): Promise<InvitePreview>;
448
+ /**
449
+ * Exchange an invite + Google id_token for a tenant-scoped consumer JWT.
450
+ * NOTE: no `tenant` field — the invite carries the tenant. Sending `tenant`
451
+ * instead would be the open-signup path (403 on invite-only tenants).
452
+ */
453
+ declare function joinWithGoogle(invite: string, idToken: string): Promise<ConsumerSignInResult>;
454
+ /** Exchange an invite + Apple id_token for a tenant-scoped consumer JWT. */
455
+ declare function joinWithApple(invite: string, idToken: string): Promise<ConsumerSignInResult>;
456
+ declare function openJoinWithGoogle(tenant: string, idToken: string): Promise<ConsumerSignInResult>;
457
+ declare function openJoinWithApple(tenant: string, idToken: string): Promise<ConsumerSignInResult>;
458
+ /**
459
+ * Start a channel-link OTP challenge. The consumer must be authenticated
460
+ * (their JWT is passed via the Authorization header).
461
+ */
462
+ declare function linkChannelStart(jwt: string, channel: string, handle: string): Promise<OtpStartResult>;
463
+ /** Verify the OTP and complete the channel link. */
464
+ declare function linkChannelVerify(jwt: string, challengeId: string, code: string): Promise<{
465
+ ok: boolean;
466
+ channel: string;
467
+ handle: string;
468
+ actorId: string;
469
+ transferred: boolean;
470
+ }>;
471
+
472
+ /**
473
+ * Deep-link parsing (R9) — the generic navigation-orchestration half of the
474
+ * host's invite/handoff capture. Extracting a code from a link's query string
475
+ * is the same for any host; only the URL SCHEME (adas://, universal-link
476
+ * domains) is host-specific and stays in the host's Expo Router config.
477
+ */
478
+ /**
479
+ * Pull a query param's value out of a deep link, URL-decoded. Returns null when
480
+ * the link is empty, the param is absent, or the value is empty. Tolerant of a
481
+ * malformed encoding (falls back to the raw match) so a valid code is never
482
+ * dropped on a stray `%`.
483
+ */
484
+ declare function parseDeepLinkParam(url: string | null | undefined, param: string): string | null;
485
+ /** A tenant-invite code from `?invite=<code>`. */
486
+ declare function parseInviteCode(url: string | null | undefined): string | null;
487
+ /**
488
+ * A web→app handoff code from `?handoff=<code>`. Deliberately keyed on `handoff=`
489
+ * and NOT `code=`, so it never collides with an OAuth `?code=` redirect.
490
+ */
491
+ declare function parseHandoffCode(url: string | null | undefined): string | null;
492
+
493
+ type WorkspaceState = 'active' | 'expired' | 'suspended' | 'unknown';
494
+ /** Server-granted capabilities. Open-ended on purpose: a plan can introduce a
495
+ * new capability name and old app versions simply won't grant it. */
496
+ interface WorkspaceCapabilities {
497
+ chat?: boolean;
498
+ live_updates?: boolean;
499
+ ui_commands?: boolean;
500
+ [capability: string]: boolean | undefined;
501
+ }
502
+ interface WorkspaceSnapshot {
503
+ state: WorkspaceState;
504
+ reason: string | null;
505
+ trialEndsAt: string | null;
506
+ expiresInDays: number | null;
507
+ capabilities: WorkspaceCapabilities;
508
+ /** Server-authored copy. Rendered VERBATIM — the client never composes it. */
509
+ message: string | null;
510
+ }
511
+ interface WorkspaceStore {
512
+ /** null = not loaded / endpoint absent — treat the app as fully capable. */
513
+ snapshot: WorkspaceSnapshot | null;
514
+ loaded: boolean;
515
+ /** Fetch GET /api/session and store the workspace snapshot (identity-guarded). */
516
+ refresh: () => Promise<void>;
517
+ /** Reset AND invalidate any in-flight refresh. Call on signout / tenant switch
518
+ * (the host must clear before refreshing a new identity). */
519
+ clear: () => void;
520
+ }
521
+ declare const useWorkspaceStore: zustand.UseBoundStore<zustand.StoreApi<WorkspaceStore>>;
522
+ /**
523
+ * Is a capability granted? The SERVER decides; the client never infers it from
524
+ * a local rule. Two deliberate, asymmetric defaults:
525
+ * - No snapshot loaded (endpoint absent, or not fetched yet) → GRANTED, so the
526
+ * app behaves exactly as it did before this layer existed.
527
+ * - Snapshot loaded but this capability missing/unknown → NOT granted, so a new
528
+ * plan can restrict an old app version and an unrecognised name never leaks
529
+ * access.
530
+ */
531
+ declare function capabilityEnabled(name: string): boolean;
532
+ /** Reactive form of capabilityEnabled for components (re-renders on change). */
533
+ declare function useCapability(name: string): boolean;
534
+
200
535
  interface TenantMembership {
201
536
  id: string;
202
537
  name: string;
203
538
  role: string | null;
204
539
  }
205
540
  /**
206
- * List the tenants the CURRENT user belongs to (GET /api/tenants/list).
541
+ * List the tenants the CURRENT user belongs to.
207
542
  *
208
543
  * The list is membership-scoped: for consumer JWTs the backend returns ONLY
209
544
  * tenants where the user's login identity (google/apple oauth or embed id)
210
545
  * has an active actor; builder JWTs get their user-doc memberships. It never
211
546
  * leaks the full tenant list. Returns [] when signed out or unknown.
547
+ *
548
+ * IT CALLS CONSUMER-AUTH, NOT CORE. This used to be
549
+ * `apiFetch('/api/tenants/list')` against the Core API, which is the wrong
550
+ * service: the membership a consumer JWT proves is held by consumer-auth, so
551
+ * Core could not answer it for the case that matters — a signed-in consumer
552
+ * asking which teams they are in. Hence the explicit fetch with the bearer
553
+ * token rather than the shared `apiFetch` helper, which is Core-scoped.
554
+ *
555
+ * EVERY FAILURE PATH RETURNS [] deliberately: no token, non-OK response,
556
+ * unparseable body, or a thrown fetch. "I could not find out" and "you belong
557
+ * to nothing" are genuinely different, but this signature has no room to say
558
+ * so — the caller renders a team list, and an empty one is the safe render.
559
+ * If that distinction ever matters, widen the return type rather than
560
+ * inventing a tenant.
212
561
  */
213
562
  declare function listMyTenants(): Promise<TenantMembership[]>;
214
563
  /**
@@ -346,6 +695,20 @@ declare function blocksToText(blocks?: Array<{
346
695
  content: any;
347
696
  }>): string;
348
697
  declare const useChatStore: zustand.UseBoundStore<zustand.StoreApi<ChatState>>;
698
+ /** What this client can execute, supplied by the host (see setClientDeclarationProvider). */
699
+ type ClientDeclaration = {
700
+ device_host: boolean;
701
+ tools: Array<{
702
+ name: string;
703
+ }>;
704
+ };
705
+ /**
706
+ * Host DI: what to tell the server about this client on connect.
707
+ *
708
+ * Kept as a provider rather than a value because the answer changes — plugins
709
+ * load after boot, so the tool list is not known when the socket first opens.
710
+ */
711
+ declare function setClientDeclarationProvider(fn: (() => ClientDeclaration | null) | null): void;
349
712
  /** Tear down the live connection (env/auth switch, SDK unmount). */
350
713
  declare function closeChatStream(): void;
351
714
 
@@ -782,4 +1145,4 @@ interface AcsKvStorage {
782
1145
  }
783
1146
  declare function setAcsCacheStorage(kv: AcsKvStorage | null): void;
784
1147
 
785
- export { type AcsArtifact, type AcsDeleteReason, type AcsEvent, type AcsKvStorage, type AcsPatchReason, type AcsReachabilityReporter, type AcsSurface, type AdasConfig, type AdasSession, type AdasUser, type AttachmentCache, type AttachmentPreview, type BufferedCommand, type DeleteResult, type HomeAction, type HomeActionLeaf, type HomeActionReason, type JobProgress, type Message, type MessageAttachment, type OutgoingAttachment, type PatchResult, PluginCommandEmitter, type RefreshOutcome, type Session, type SessionEnv, TABLE_BLOCK_CLOSE, TABLE_BLOCK_OPEN, type TenantMembership, type TokenProvider, type ToolCall, type UIAction, ada, apiFetch, apiFetchRaw, blocksToText, borderRadius, closeChatStream, colors, connectAcsWs, connectorCall, deleteArtifact, disconnectAcsWs, fontSize, getConfig, getConsumerAuthApiUrl, getCoreApiUrl, getCoreWsUrl, getSession, getTenant, getToken, getUser, getVoiceApiUrl, initAdas, isAdasInitialized, isDevHost, listHomeActions, listMadeForYou, listMyTenants, listSurface, mcpFetch, mcpToolCall, notifyAcsWsForeground, patchArtifact, patchHomeAction, pluginCommandEmitter, postCommandResult, recordEvent, refreshToken, setAcsCacheStorage, setAcsReachabilityReporter, setHomeActionOrder, setOrder, spacing, switchTenant, unwrapMcpResult, useChatStore, useHomeActionsStore, useWidgetsStore };
1148
+ export { type AcsArtifact, type AcsDeleteReason, type AcsEvent, type AcsKvStorage, type AcsPatchReason, type AcsReachabilityReporter, type AcsSurface, type AdasConfig, type AdasGateFlags, type AdasPhase, type AdasSession, type AdasUser, type AttachmentCache, type AttachmentPreview, type AuthSession, type AuthSessionDeps, type AuthSessionState, type BufferedCommand, type ConsumerCapabilityConfig, type ConsumerProviderConfig, type ConsumerSignInResult, type DeleteResult, type HandoffExchangeResult, type HomeAction, type HomeActionLeaf, type HomeActionReason, type InvitePreview, type JobProgress, type Message, type MessageAttachment, type OtpStartResult, type OutgoingAttachment, type PatchResult, PluginCommandEmitter, type PushBackend, type PushRegistration, type RefreshOutcome, type Session, type SessionEnv, type SessionPersistence, type SessionStatus, type SessionUser, TABLE_BLOCK_CLOSE, TABLE_BLOCK_OPEN, type TenantMembership, type TokenProvider, type ToolCall, type UIAction, type WorkspaceCapabilities, type WorkspaceSnapshot, type WorkspaceState, ada, apiFetch, apiFetchRaw, blocksToText, borderRadius, capabilityEnabled, closeChatStream, colors, connectAcsWs, connectorCall, createAuthSession, createPushRegistration, deleteArtifact, disconnectAcsWs, exchangeHandoff, fetchConsumerConfig, fontSize, getConfig, getConsumerAuthApiUrl, getCoreApiUrl, getCoreWsUrl, getSession, getTenant, getToken, getUser, getVoiceApiUrl, initAdas, isAdasInitialized, isDevHost, joinWithApple, joinWithGoogle, linkChannelStart, linkChannelVerify, listHomeActions, listMadeForYou, listMyTenants, listSurface, mcpFetch, mcpToolCall, notifyAcsWsForeground, notifyAppForeground, onAppForeground, openJoinWithApple, openJoinWithGoogle, parseDeepLinkParam, parseHandoffCode, parseInviteCode, patchArtifact, patchHomeAction, pluginCommandEmitter, postCommandResult, previewInvite, recordEvent, refreshToken, resolveAdasPhase, setAcsCacheStorage, setAcsReachabilityReporter, setClientDeclarationProvider, setHomeActionOrder, setOrder, signInWithApple, signInWithGoogle, spacing, switchTenant, unwrapMcpResult, useCapability, useChatStore, useHomeActionsStore, useWidgetsStore, useWorkspaceStore, userFromJwt };