1140 lines · 39.2 KB · TypeScript
RawDownload
import { type LiveActivity } from "expo-widgets";import Constants from "expo-constants";import * as Notifications from "expo-notifications";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import { AppState, Platform } from "react-native";import type { EnvironmentId } from "@t3tools/contracts";import {  type RelayDeviceRegistrationRequest,  type RelayAgentActivitySnapshotResponse,  type RelayLiveActivityRegistrationRequest,} from "@t3tools/contracts/relay";import { findErrorTraceId } from "@t3tools/client-runtime/errors";import { ManagedRelay } from "@t3tools/client-runtime/relay";import {  isAtomCommandInterrupted,  settleAsyncResult,  squashAtomCommandFailure,} from "@t3tools/client-runtime/state/runtime"; import type { SavedRemoteConnection } from "../../lib/connection";import { runtime } from "../../lib/runtime";import { appAtomRegistry } from "../../state/atom-registry";import { environmentServerConfigsAtom } from "../../state/server";import type { Preferences } from "../../persistence/mobile-preferences";import {  clearAgentAwarenessRegistrationRecord,  loadAgentAwarenessDeviceId,  loadAgentAwarenessRegistrationRecord,  loadOrCreateAgentAwarenessDeviceId,  loadPreferences,  saveAgentAwarenessRegistrationRecord,} from "../../persistence/imperative";import AgentActivity, { type AgentActivityProps } from "../../widgets/AgentActivity";import { resolveCloudPublicConfig } from "../cloud/publicConfig";import { supportsAgentAwarenessPush } from "./capabilities";import { makeRelayDeviceRegistrationRequest, resolveApsEnvironment } from "./registrationPayload"; const REMOTE_ACTIVITY_REGISTRATION_RETRY_MS = 15_000; const AgentAwarenessOperation = Schema.Literals([  "read-notification-permissions",  "read-native-push-token",  "read-device-registration-relay-token",  "read-device-unregistration-relay-token",  "read-live-activity-registration-relay-token",  "load-device-registration-identifier",  "load-device-registration-preferences",  "load-device-unregistration-identifier",  "read-live-activity-push-token",  "load-live-activity-registration-identifier",  "list-active-live-activities",  "load-live-activity-prime-preferences",  "prime-live-activity",]); export class AgentAwarenessOperationError extends Schema.TaggedErrorClass<AgentAwarenessOperationError>()(  "AgentAwarenessOperationError",  {    operation: AgentAwarenessOperation,    cause: Schema.Defect(),  },) {  override get message(): string {    return `Agent awareness operation ${this.operation} failed.`;  }} const environmentConnections = new Map<EnvironmentId, SavedRemoteConnection>();const activityPushTokenListeners = new WeakSet<LiveActivity<AgentActivityProps>>();// Activity tokens the relay recently accepted, by acceptance time. The refresh// runs on sign-in, every app foreground, and every environment-connection// update, which arrive in bursts and spammed identical registrations. But the// registration is not a pure no-op: the relay replays the current aggregate to// this device on every accepted registration, and that replay is the// foreground reconciliation that repairs drifted or orphaned activities. So// dedupe only within a short window — bursts collapse to one request, while a// foreground after real time away still triggers a replay. Cleared on// sign-out/identity change alongside the device registration state.const ACTIVITY_TOKEN_REREGISTER_INTERVAL_MS = 60_000;const registeredActivityPushTokens = new Map<string, number>();let pushTokenSubscription: { remove: () => void } | null = null;let appStateSubscription: { remove: () => void } | null = null; // Whether the relay has actually accepted this device's registration. The// notification/Live Activity settings toggles must reflect this rather than// only local iOS permission or saved preferences: if the registration request// never succeeded, the device cannot receive anything, so the switches must// not read as enabled.export type AgentAwarenessRegistrationStatus = "unknown" | "pending" | "registered" | "failed";let registrationStatus: AgentAwarenessRegistrationStatus = "unknown";const registrationStatusListeners = new Set<() => void>(); function setRegistrationStatus(next: AgentAwarenessRegistrationStatus): void {  if (registrationStatus === next) {    return;  }  registrationStatus = next;  for (const listener of registrationStatusListeners) {    listener();  }} export function getAgentAwarenessRegistrationStatus(): AgentAwarenessRegistrationStatus {  return registrationStatus;} export function subscribeAgentAwarenessRegistrationStatus(listener: () => void): () => void {  registrationStatusListeners.add(listener);  return () => {    registrationStatusListeners.delete(listener);  };}let activeLiveActivityRegistrationRetry: ReturnType<typeof setTimeout> | null = null;let relayTokenProvider: (() => Promise<string | null>) | null = null;let relayTokenProviderIdentity: string | null = null;let deviceRegistrationGeneration = 0;let activeDeviceRegistration: {  readonly input: DeviceRegistrationInput;  operation: Promise<void>;} | null = null;let pendingDeviceRegistration: {  readonly input: DeviceRegistrationInput;  readonly context: string;} | null = null; interface DeviceRegistrationInput {  readonly observedPushToken?: string;} interface RegisterDeviceInput extends DeviceRegistrationInput {  readonly preferencesOverride?: Partial<Preferences>;} export function mergeAgentAwarenessRegistrationPreferences(  stored: Preferences,  override: Partial<Preferences> | undefined,): Preferences {  return { ...stored, ...override };} export function normalizeAgentAwarenessRelayBaseUrl(  value: string | null | undefined,): string | null {  const trimmed = value?.trim();  if (!trimmed) {    return null;  }  return trimmed.replace(/\/+$/g, "");} function readRelayConfig(): { readonly url: string } | null {  const relayUrl = resolveCloudPublicConfig().relay.url;  if (!relayUrl) {    logRegistrationDebug("relay registration skipped; relay config missing");    return null;  }   return { url: relayUrl };} function canRegisterRemoteLiveActivities(): boolean {  return Platform.OS === "ios";} export function shouldRegisterAgentAwarenessDeviceForProvider(  previousIdentity: string | null,  identity: string | undefined,): boolean {  return identity === undefined || identity !== previousIdentity;} export function setAgentAwarenessRelayTokenProvider(  provider: (() => Promise<string | null>) | null,  identity?: string,): void {  const isExistingIdentity =    provider !== null &&    !shouldRegisterAgentAwarenessDeviceForProvider(relayTokenProviderIdentity, identity);  if (!isExistingIdentity) {    deviceRegistrationGeneration++;    activeDeviceRegistration = null;    pendingDeviceRegistration = null;    registeredActivityPushTokens.clear();  }  relayTokenProvider = provider;  relayTokenProviderIdentity = provider ? (identity ?? null) : null;  if (!provider) {    pushTokenSubscription?.remove();    pushTokenSubscription = null;    appStateSubscription?.remove();    appStateSubscription = null;    if (activeLiveActivityRegistrationRetry) {      clearTimeout(activeLiveActivityRegistrationRetry);      activeLiveActivityRegistrationRetry = null;    }    // Without a signed-in user the relay can no longer update or end these    // activities, so they would sit orphaned on the lock screen.    endLocalLiveActivities("live activity cleanup after cloud sign-out failed");    setRegistrationStatus("unknown");    // Sign-out is the only thing that invalidates a stored registration, so the    // next sign-in re-registers.    void clearAgentAwarenessRegistrationRecord().catch((error: unknown) => {      logRegistrationError("clear registration record on sign-out failed", error);    });    return;  }  ensurePushTokenListener();  ensureAppStateListener();  runRegistrationInBackground(    refreshActiveLiveActivityRemoteRegistration(),    "active live activity registration after cloud sign-in failed",  );  if (isExistingIdentity) {    // Same account re-activating (e.g. Clerk token refresh) normally needs no    // re-registration — but if the previous attempt never succeeded, this is    // the only trigger that will retry it before the next cold start.    if (registrationStatus !== "registered") {      enqueueDeviceRegistration({}, "device registration retry after cloud session refresh failed");    }    return;  }  enqueueDeviceRegistration({}, "device registration after cloud sign-in failed");} // Detach the provider and native listeners without the destructive sign-out// cleanup. For provider teardown while the user is still signed in (e.g. the// auth bridge unmounting/remounting), ending lock-screen activities and wiping// the persisted registration would be wrong — the relay still holds a valid// registration and the next mount reuses it.export function releaseAgentAwarenessRelayTokenProvider(): void {  relayTokenProvider = null;  relayTokenProviderIdentity = null;  pushTokenSubscription?.remove();  pushTokenSubscription = null;  appStateSubscription?.remove();  appStateSubscription = null;  if (activeLiveActivityRegistrationRetry) {    clearTimeout(activeLiveActivityRegistrationRetry);    activeLiveActivityRegistrationRetry = null;  }} function iosMajorVersion(): number {  const version = Platform.Version;  if (typeof version === "number") {    return Math.floor(version);  }  const major = Number.parseInt(version.split(".")[0] ?? "", 10);  return Number.isFinite(major) ? major : 18;} function nativePushTokenRegistration(observedPushToken?: string) {  return Effect.gen(function* () {    if (!canRegisterRemoteLiveActivities() || !supportsAgentAwarenessPush()) {      return { notificationsEnabled: false, pushToken: null };    }    if (observedPushToken) {      return { notificationsEnabled: true, pushToken: observedPushToken };    }    const permissions = yield* Effect.tryPromise({      try: () => Notifications.getPermissionsAsync(),      catch: (cause) =>        new AgentAwarenessOperationError({          operation: "read-notification-permissions",          cause,        }),    });    if (!permissions.granted) {      return { notificationsEnabled: false, pushToken: null };    }    const token = yield* Effect.tryPromise({      try: () => Notifications.getDevicePushTokenAsync(),      catch: (cause) =>        new AgentAwarenessOperationError({          operation: "read-native-push-token",          cause,        }),    }).pipe(      Effect.tapError((error) =>        Effect.sync(() => {          logRegistrationError("native APNs token lookup failed", error);        }),      ),      Effect.orElseSucceed(() => null),    );    const pushToken =      token?.type === "ios" && typeof token.data === "string" && token.data.trim().length > 0        ? token.data.trim()        : null;    return { notificationsEnabled: pushToken !== null, pushToken };  });} const relayToken = (  operation: "read-device-registration-relay-token" | "read-live-activity-registration-relay-token",) =>  Effect.gen(function* () {    const provider = relayTokenProvider;    if (!provider) {      return null;    }    return yield* Effect.tryPromise({      try: provider,      catch: (cause) => new AgentAwarenessOperationError({ operation, cause }),    });  }); // Stable fingerprint of everything the relay stores for this device. When it// matches the last accepted registration for the same account, re-registering// is a no-op, so a launch that changed nothing skips the request entirely.function registrationSignature(body: RelayDeviceRegistrationRequest): string {  return [    body.deviceId,    body.pushToken ?? "",    body.bundleId ?? "",    body.apsEnvironment ?? "",    body.appVersion ?? "",    body.label,    body.iosMajorVersion,    body.preferences.notificationsEnabled,    body.preferences.liveActivitiesEnabled,    body.preferences.notifyOnApproval,    body.preferences.notifyOnInput,    body.preferences.notifyOnCompletion,    body.preferences.notifyOnFailure,  ].join("|");} function registerDeviceWithRelay(  body: RelayDeviceRegistrationRequest,  expectedGeneration: number,): Effect.Effect<void, unknown, ManagedRelay.ManagedRelayClient> {  return Effect.gen(function* () {    if (expectedGeneration !== deviceRegistrationGeneration) {      logRegistrationDebug("device registration cancelled before relay request", {        expectedGeneration,        currentGeneration: deviceRegistrationGeneration,      });      return;    }    const relayConfig = readRelayConfig();    if (!relayConfig) {      // Nothing is in flight and nothing can succeed until configuration      // appears; "pending" would otherwise stick forever.      setRegistrationStatus("unknown");      return;    }    const token = yield* relayToken("read-device-registration-relay-token");    if (expectedGeneration !== deviceRegistrationGeneration) {      logRegistrationDebug("device registration cancelled after auth lookup", {        expectedGeneration,        currentGeneration: deviceRegistrationGeneration,      });      return;    }    if (!token) {      logRegistrationDebug("relay device registration skipped; user is not signed in");      setRegistrationStatus("unknown");      return;    }     // Skip the request when this account already registered an identical    // payload; the relay upsert would be a no-op. The record is only cleared on    // sign-out, so a device stays registered across launches without re-hitting    // the relay every time the app opens.    const identity = relayTokenProviderIdentity ?? "";    const persisted = yield* Effect.tryPromise({      try: () => loadAgentAwarenessRegistrationRecord(),      catch: (cause) => cause,    }).pipe(Effect.orElseSucceed(() => null));    if (expectedGeneration !== deviceRegistrationGeneration) {      // Signed out while the record loaded — do not resurrect the cleared      // record or report the previous account's registration as current.      logRegistrationDebug("device registration cancelled after record lookup", {        expectedGeneration,        currentGeneration: deviceRegistrationGeneration,      });      return;    }    const payload = body;    // The relay URL participates so pointing the app at a different relay    // invalidates the record and re-registers there.    const signature = `${relayConfig.url}|${registrationSignature(payload)}`;    if (persisted && persisted.identity === identity && persisted.signature === signature) {      setRegistrationStatus("registered");      logRegistrationDebug("relay device registration skipped; already registered for account", {        expectedGeneration,      });      return;    }     const client = yield* ManagedRelay.ManagedRelayClient;    logRegistrationDebug("relay device registration request started", {      expectedGeneration,    });    yield* client.registerDevice({      clerkToken: token,      payload,    });    if (expectedGeneration !== deviceRegistrationGeneration) {      // Signed out while the request was in flight: the sign-out path already      // reset the status and cleared the record for the next account, so a      // stale success must not overwrite either.      logRegistrationDebug("device registration completed after sign-out; result discarded", {        expectedGeneration,        currentGeneration: deviceRegistrationGeneration,      });      return;    }    setRegistrationStatus("registered");    yield* Effect.promise(() =>      saveAgentAwarenessRegistrationRecord({        identity,        signature,      }).catch((error: unknown) => {        logRegistrationError("persist registration record failed", error);      }),    );    logRegistrationDebug("relay device registration request completed", {      expectedGeneration,    });  });} function unregisterDeviceWithRelay(input: {  readonly deviceId: string;  readonly tokenProvider: () => Promise<string | null>;}): Effect.Effect<void, unknown, ManagedRelay.ManagedRelayClient> {  return Effect.gen(function* () {    if (!readRelayConfig()) return;    const token = yield* Effect.tryPromise({      try: input.tokenProvider,      catch: (cause) =>        new AgentAwarenessOperationError({          operation: "read-device-unregistration-relay-token",          cause,        }),    });    if (!token) {      logRegistrationDebug("relay device unregistration skipped; user is not signed in");      return;    }     const client = yield* ManagedRelay.ManagedRelayClient;    yield* client.unregisterDevice({      clerkToken: token,      deviceId: input.deviceId,    });  });} // The environment descriptor advertises whether agent-activity publishes// currently leave that server (`capabilities.agentActivityPublishing`). Only// an explicit false skips the seed card: older servers omit the capability// but may still publish.function environmentPublishesAgentActivity(environmentId: EnvironmentId): boolean {  return (    appAtomRegistry.get(environmentServerConfigsAtom).get(environmentId)?.environment.capabilities      .agentActivityPublishing !== false  );} // Arms the lock-screen card the moment the user starts agent work from this// phone, while the app is still foregrounded and the fresh activity's token// can be registered immediately. The seeded row is a best-effort placeholder;// the relay's registration replay repaints it with the authoritative// aggregate within seconds. No-ops when a card is already armed, and skips// environments that report publishing disabled — the seed would sit on// "Connecting" forever with no update ever arriving to repaint or end it.export function armAgentAwarenessLiveActivityForLocalWork(input: {  readonly environmentId: EnvironmentId;  readonly threadTitle: string;  readonly projectTitle: string;}): void {  if (!canRegisterRemoteLiveActivities() || !relayTokenProvider) {    return;  }  if (!environmentPublishesAgentActivity(input.environmentId)) {    logRegistrationDebug("live activity arming skipped; environment does not publish", {      environmentId: input.environmentId,    });    return;  }  void loadPreferences()    .catch(() => null)    .then((preferences) => {      if (preferences?.liveActivitiesEnabled === false) {        return;      }      armAgentAwarenessLiveActivityForLocalWorkNow(input);    });} function armAgentAwarenessLiveActivityForLocalWorkNow(input: {  readonly threadTitle: string;  readonly projectTitle: string;}): void {  try {    if (AgentActivity.getInstances().length > 0) {

Lines 1–500 of 1140.