@mica/sdk (SDK contract v1)
    Preparing search index...

    Interface Facets

    One key per facet. The runtime object behind this shape is the facets Proxy in current.ts.

    interface Facets {
        account: () => {
            bankBalance: Writable<number>;
            citizenid: Writable<string>;
            fetchBalance: () => Promise<void>;
            fetchCitizenId: () => Promise<string>;
            fetchPhoneNumber: () => Promise<string>;
            fetchTransactions: () => Promise<void>;
            historySource: Writable<BankHistorySource>;
            myPhoneNumber: Writable<string>;
            transactions: Writable<Transaction[]>;
            transactionsLoaded: Writable<boolean>;
        };
        accounts: () => {
            blockAccount: (
                input: {
                    app: string;
                    blocked_account_id: number;
                    blocker_account_id: number;
                },
            ) => Promise<unknown>;
            createAccount: (
                input: { app: string; display_name?: string; handle: string },
            ) => Promise<Account>;
            followAccount: (
                input: {
                    app: string;
                    followee_account_id: number;
                    follower_account_id: number;
                },
            ) => Promise<unknown>;
            getAccounts: (
                query: { app: string; handle?: string; limit?: number },
            ) => Promise<{ rows: Account[] }>;
            getFollowers: (query: FollowListQuery) => Promise<FollowPage>;
            getFollowing: (query: FollowListQuery) => Promise<FollowPage>;
            getFollowStats: (
                input: { account_id: number; app: string; viewer_account_id?: number },
            ) => Promise<FollowStats>;
            getMyAccounts: (
                app: string,
            ) => Promise<{ limit: number; rows: Account[] }>;
            getReactionsFor: (
                target: ReactionTarget,
            ) => Promise<Record<number, ReactionSummary>>;
            reactToTarget: (
                payload: {
                    account_id: number;
                    app: string;
                    emoji: string;
                    target_id: number;
                    target_table: string;
                },
            ) => Promise<unknown>;
            searchAccounts: (
                query: AccountSearchQuery,
            ) => Promise<{ nextCursor: number | null; rows: Account[] }>;
            unblockAccount: (
                input: {
                    app: string;
                    blocked_account_id: number;
                    blocker_account_id: number;
                },
            ) => Promise<unknown>;
            unfollowAccount: (
                input: {
                    app: string;
                    followee_account_id: number;
                    follower_account_id: number;
                },
            ) => Promise<unknown>;
            unreactToTarget: (
                payload: {
                    account_id: number;
                    app: string;
                    emoji: string;
                    target_id: number;
                    target_table: string;
                },
            ) => Promise<unknown>;
            updateAccount: (
                input: { id: number } & Partial<Account>,
            ) => Promise<unknown>;
        };
        admin: () => {
            isAdmin: Writable<boolean>;
            refreshAdmin: () => Promise<void>;
        };
        appAction: (
            appId?: string,
        ) => {
            busy: Writable<boolean>;
            notify: (
                n: { message: string; title?: string; type: "error" | "success" },
            ) => string;
            run: (work: () => unknown, options?: AppActionOptions) => Promise<boolean>;
        };
        appEvents: (
            appId: string,
        ) => {
            clear: () => void;
            on: <T = Record<string, unknown>>(
                event: string,
                handler: (e: AppEvent<T>) => void,
            ) => () => void;
            onAny: (handler: (e: AppEvent) => void) => () => void;
        };
        appLevels: (
            config: AppLevelsConfig,
        ) => { back: () => void; release: () => void; title: string };
        appRegistry: () => {
            bundledAddOns: AppManifest[];
            getFirstBootTime: () => string;
            registryStore: {
                getAddOnSource: (appId: string) => Promise<string | undefined>;
                getComponent: (appId: string) => AppComponent | undefined;
                getManifest: (appId: string) => AppManifest | undefined;
                installFromCatalog: (
                    entry: CatalogEntry,
                ) => Promise<{ manifest: AppManifest }>;
                isInstalled: (appId: string) => boolean;
                isKnownApp: (appId: string) => boolean;
                loadComponent: (appId: string) => Promise<AppComponent | undefined>;
                registerAddOn: (manifest: AppManifest, source?: string) => void;
                registerApp: (manifest: AppManifest, component: AppComponent) => void;
                rehydrateSavedRemoteApps: () => Promise<void>;
                subscribe: (
                    this: void,
                    run: Subscriber<AppManifest[]>,
                    invalidate?: () => void,
                ) => Unsubscriber;
                unregisterApp: (appId: string) => void;
            };
            updateCount: Readable<number>;
            updatesStore: Readable<AppUpdate[]>;
        };
        appRegistryWrite: () => {
            fetchRemoteCatalog: () => Promise<
                | { entries: CatalogEntry[]; status: "ok" }
                | { status: "off" }
                | { status: "unavailable" },
            >;
            grantedPermissions: (
                appId: string,
            ) =>
                | readonly (
                    | "messages"
                    | "admin"
                    | "location"
                    | "camera"
                    | "account"
                    | "bank"
                    | "call"
                    | "contacts"
                    | "control-center"
                    | "highscores"
                    | "jobs"
                    | "mail"
                    | "marketplace"
                    | "media"
                    | "notifications"
                    | "reports"
                    | "social"
                    | "storage"
                    | "app-events"
                    | "app-registry"
                    | "app-registry-write"
                    | "clock"
                    | "clock-write"
                    | "devtools"
                    | "display"
                    | "display-write"
                    | "keybinds"
                    | "keybinds-write"
                    | "lock-screen"
                    | "lock-screen-write"
                    | "music"
                    | "navigation"
                    | "notification-settings"
                    | "notification-settings-write"
                    | "system-hardware"
                    | "system-hardware-write"
                    | "theme"
                    | "theme-write"
                    | "wallpaper"
                    | "wallpaper-write"
                )[]
                | undefined;
            installFromCatalog: (
                entry: CatalogEntry,
            ) => Promise<{ manifest: AppManifest }>;
            recordConsent: (
                appId: string,
                permissions: readonly (
                    | "messages"
                    | "admin"
                    | "location"
                    | "camera"
                    | "account"
                    | "bank"
                    | "call"
                    | "contacts"
                    | "control-center"
                    | "highscores"
                    | "jobs"
                    | "mail"
                    | "marketplace"
                    | "media"
                    | "notifications"
                    | "reports"
                    | "social"
                    | "storage"
                    | "app-events"
                    | "app-registry"
                    | "app-registry-write"
                    | "clock"
                    | "clock-write"
                    | "devtools"
                    | "display"
                    | "display-write"
                    | "keybinds"
                    | "keybinds-write"
                    | "lock-screen"
                    | "lock-screen-write"
                    | "music"
                    | "navigation"
                    | "notification-settings"
                    | "notification-settings-write"
                    | "system-hardware"
                    | "system-hardware-write"
                    | "theme"
                    | "theme-write"
                    | "wallpaper"
                    | "wallpaper-write"
                )[],
            ) => void;
            refreshUpdates: () => Promise<AppUpdate[]>;
            registerAddOn: (manifest: AppManifest, source?: string) => void;
            registerApp: (manifest: AppManifest, component: AppComponent) => void;
            unregisterApp: (appId: string) => void;
            updateApp: (appId: string) => Promise<AppManifest>;
        };
        appStorageBytes: (appId: string) => number;
        bank: () => {
            declineInvoice: (id: number) => Promise<InvoiceActionOutcome>;
            fetchInvoices: () => Promise<void>;
            invoices: Writable<Invoice[]>;
            invoicesLoaded: Writable<boolean>;
            payInvoice: (id: number) => Promise<InvoiceActionOutcome>;
            sendMoney: (input: SendMoneyInput) => Promise<SendMoneyOutcome>;
        };
        call: () => {
            answerCall: () => Promise<void>;
            callLog: Writable<PhoneCallLogEntry[]>;
            callStore: {
                answerCall: () => Promise<void>;
                endCall: () => Promise<void>;
                setIncoming: (number: string, name?: string) => void;
                setStatus: (status: CallStatus) => void;
                startCall: (number: string, name?: string) => Promise<void>;
                subscribe: (
                    this: void,
                    run: Subscriber<CallState>,
                    invalidate?: () => void,
                ) => Unsubscriber;
                toggleMute: () => Promise<void>;
                toggleSpeaker: () => Promise<void>;
            };
            endCall: () => Promise<void>;
            loadCallLog: () => Promise<void>;
            startCall: (number: string, name?: string) => Promise<void>;
            toggleSpeaker: () => Promise<void>;
        };
        camera: () => {
            isPreviewingPhoto: Writable<boolean>;
            isTakingPhoto: Writable<boolean>;
        };
        clearAppStorage: (appId: string) => void;
        clock: () => {
            formattedTime: Readable<string>;
            is24Hour: Writable<boolean>;
            time: Writable<TimeState>;
        };
        clockWrite: () => { setIs24Hour: (value: boolean) => void };
        contacts: () => {
            addContact: (
                firstname: string,
                phone: string,
                lastname?: string,
                avatar?: string,
                favorite?: boolean,
            ) => Promise<Contact>;
            contactsStore: {
                add: (
                    draft: Omit<
                        Contact,
                        "id"
                        | "citizenid"
                        | "created_at"
                        | "updated_at",
                    >,
                ) => Promise<Contact>;
                delete: (id: number) => Promise<void>;
                getDeleted: () => Promise<Contact[]>;
                load: () => Promise<void>;
                loaded: {
                    subscribe: (
                        this: void,
                        run: Subscriber<boolean>,
                        invalidate?: () => void,
                    ) => Unsubscriber;
                };
                patch: (id: number, changes: Partial<Contact>) => void;
                restore: (id: number) => Promise<boolean>;
                set: (rows: Contact[]) => void;
                share: (
                    payload: Partial<Contact> & { name?: string; phone: string },
                ) => Promise<void>;
                subscribe: (
                    this: void,
                    run: Subscriber<Contact[]>,
                    invalidate?: () => void,
                ) => Unsubscriber;
                update: (row: Contact) => Promise<void>;
            };
            favoriteContacts: Readable<Contact[]>;
            getDeletedContacts: () => Promise<Contact[]>;
            restoreContact: (id: number) => Promise<boolean>;
            shareContact: (
                firstname: string,
                phone: string,
                lastname?: string,
            ) => Promise<void>;
        };
        controlCenter: (
            appId: string,
        ) => {
            lease: Readable<boolean>;
            registerToggle: (toggle: ControlCenterToggle) => void;
            setToggleActive: (id: string, active: boolean) => void;
            unregisterToggle: (id: string) => void;
        };
        deepLink: (appId: string, handle: () => boolean) => void;
        devTools: () => {
            devToolsUnlocked: Writable<boolean>;
            lock: () => void;
            unlock: () => void;
        };
        directMessages: (
            appId: string,
        ) => {
            markRead: (
                input: { account_id: number; peer_account_id: number },
            ) => Promise<boolean>;
            send: (
                input: { account_id: number; body: string; peer_account_id: number },
            ) => Promise<DirectMessage>;
            thread: (
                query: {
                    account_id: number;
                    cursor?: number | null;
                    limit?: number;
                    peer_account_id: number;
                },
            ) => Promise<{ nextCursor: number | null; rows: DirectMessage[] }>;
            threads: () => Promise<DirectMessageThread[]>;
        };
        display: () => {
            device: Readable<"phone" | "tablet">;
            displaySize: Writable<number>;
            displaySizeDefault: number;
            frame: Readable<{ height: number; width: number }>;
            homeGridColumns: Writable<number>;
            homeGridColumnsDefault: number;
            homeGridColumnsMax: number;
            homeGridColumnsMin: number;
            homeGridRows: Writable<number>;
            homeGridRowsDefault: number;
            homeGridRowsMax: number;
            homeGridRowsMin: number;
            isSizeLimited: Readable<boolean>;
            motionPreference: Writable<MotionPreference>;
            motionPreferenceDefault: MotionPreference;
            phoneBox: Readable<{ height: number; width: number }>;
            phoneScale: Readable<number>;
            reducedMotion: Readable<boolean>;
        };
        displayWrite: () => {
            setDisplaySize: (size: number) => void;
            setFrame: (id: string) => void;
            setFrameColor: (id: string) => void;
            setHomeGridSize: (columns: number, rows: number) => void;
            setMotionPreference: (preference: MotionPreference) => void;
        };
        highscores: () => {
            getLeaderboard: (app: string) => Promise<LeaderboardEntry[]>;
            submitScore: (app: string, score: number) => Promise<void>;
        };
        jobs: () => {
            fetchJobs: () => Promise<void>;
            jobs: Writable<JobView[]>;
            jobsLoaded: Writable<boolean>;
            setActiveJob: (name: string) => Promise<JobActionOutcome>;
            setDuty: (name: string, onDuty: boolean) => Promise<JobActionOutcome>;
        };
        keybinds: () => {
            bindings: Readable<Record<string, string>>;
            findConflict: (actionId: string, key: string) => KeybindAction | undefined;
            groups: Readable<KeybindGroup[]>;
            onKeybind: (
                actionId: string,
                handler: () => void,
                appId?: string,
            ) => () => void;
        };
        keybindsWrite: () => {
            resetBindings: () => void;
            setBinding: (actionId: string, key: string) => void;
        };
        lifecycle: (
            appId: string,
        ) => {
            consumeDeepLink: () => void;
            currentApp: Writable<RunningApp>;
            goHome: () => void;
            onBack: (handler: () => void) => () => void;
            visible: Readable<boolean>;
        };
        locale: (
            appId?: string,
        ) => {
            catalogs: Readable<
                Readonly<
                    Record<
                        string,
                        Readonly<Record<string, Readonly<Record<string, string>>>>,
                    >,
                >,
            >;
            locale: Readable<string>;
            setLocale: (next: string) => void;
        };
        location: () => {
            setWaypoint: (x: number, y: number) => Promise<void>;
            shareLocation: () => Promise<{ id: number; media: MediaPreview }>;
        };
        lockScreen: () => {
            autoLockPolicy: Writable<AutoLockPolicy>;
            autoLockPolicyChoices: Readable<readonly AutoLockPolicyChoice[]>;
            hasPasscode: Writable<boolean>;
        };
        lockScreenWrite: () => {
            clearPasscode: () => Promise<void>;
            setAutoLockPolicy: (policy: AutoLockPolicy) => void;
            setPasscode: (digits: string) => Promise<void>;
        };
        mail: () => {
            addReceivedMail: (newMail: Mail) => void;
            archiveMail: (id: number, archiveState?: boolean) => Promise<void>;
            deleteMail: (id: number) => Promise<void>;
            mailStore: {
                add: (draft: Omit<Mail, "id">) => Promise<Mail>;
                addReceivedMail: (incoming: Mail) => void;
                archive: (id: number, archive?: boolean) => Promise<void>;
                delete: (id: number) => Promise<void>;
                load: () => Promise<void>;
                loaded: {
                    subscribe: (
                        this: void,
                        run: Subscriber<boolean>,
                        invalidate?: () => void,
                    ) => Unsubscriber;
                };
                markAsRead: (id: number) => Promise<void>;
                patch: (id: number, changes: Partial<Mail>) => void;
                set: (rows: Mail[]) => void;
                subscribe: (
                    this: void,
                    run: Subscriber<Mail[]>,
                    invalidate?: () => void,
                ) => Unsubscriber;
                update: (row: Mail) => Promise<void>;
            };
            markAsRead: (id: number) => Promise<void>;
            unreadMailCount: Readable<number>;
        };
        marketplace: () => {
            feedStore: Writable<ListingPage>;
            loadFeed: () => Promise<void>;
            loadMine: () => Promise<void>;
            markSold: (id: number) => Promise<boolean>;
            mineStore: Writable<ListingPage>;
            postListing: (input: CreateListingInput) => Promise<Listing>;
            removeListing: (id: number) => Promise<boolean>;
            searchListings: (q: string) => Promise<ListingPage>;
            viewListing: (
                id: number,
            ) => Promise<
                Listing & { contactPhone: string | null; isOwn: boolean }
                | null,
            >;
        };
        media: () => {
            capturePhoto: (data: string, thumbnail?: string) => Promise<MediaItem>;
            deletePhoto: (id: number) => Promise<void>;
            dropNearby: (mediaId: number) => Promise<{ count: number }>;
            fullMedia: (mediaId: number) => Promise<MediaItem>;
            getDeletedMedia: () => Promise<DeletedMediaItem[]>;
            media: {
                add: (
                    draft: Omit<
                        MediaItem,
                        "id"
                        | "citizenid"
                        | "created_at"
                        | "updated_at",
                    >,
                ) => Promise<MediaItem>;
                delete: (mediaId: number) => Promise<void>;
                dropNearby: (mediaId: number) => Promise<{ count: number }>;
                full: (mediaId: number) => Promise<MediaItem>;
                getDeleted: () => Promise<DeletedMediaItem[]>;
                hasMore: Readable<boolean>;
                hydrate: (item: MediaPreview) => void;
                load: (filter?: Record<string, unknown>) => Promise<void>;
                loaded: Readable<boolean>;
                receive: (mediaId?: number) => Promise<void>;
                restore: (mediaId: number) => Promise<boolean>;
                setThumbnail: (mediaId: number, thumbnail: string) => Promise<boolean>;
                setWaypoint: (x: number, y: number) => Promise<void>;
                shareLocation: () => Promise<{ id: number; media: MediaPreview }>;
                loadMore(): Promise<boolean>;
                prepend(row: MediaItem): void;
                remove(id: number): void;
                replace(row: MediaItem): void;
                subscribe(
                    this: void,
                    run: Subscriber<MediaItem[]>,
                    invalidate?: () => void,
                ): Unsubscriber;
            };
            restoreMedia: (mediaId: number) => Promise<boolean>;
        };
        messages: () => {
            addReceivedMessage: (message: IncomingMessage) => void;
            conversationsStore: {
                activeConversationId: {
                    subscribe: (
                        this: void,
                        run: Subscriber<number | null>,
                        invalidate?: () => void,
                    ) => Unsubscriber;
                };
                addReceivedMessage: (incoming: IncomingMessage) => void;
                archiveConversation: (
                    conversationId: number,
                    archive?: boolean,
                ) => Promise<void>;
                deleteConversation: (conversationId: number) => Promise<void>;
                deleteMessage: (
                    conversationId: number,
                    messageId: number,
                ) => Promise<boolean>;
                editMessage: (
                    conversationId: number,
                    messageId: number,
                    message: string,
                ) => Promise<{ edited?: boolean; message: string } | null>;
                hasMore: {
                    subscribe: (
                        this: void,
                        run: Subscriber<boolean>,
                        invalidate?: () => void,
                    ) => Unsubscriber;
                };
                hasOlderMessages: {
                    subscribe: (
                        this: void,
                        run: Subscriber<Record<number, boolean>>,
                        invalidate?: () => void,
                    ) => Unsubscriber;
                };
                loadConversations: () => Promise<void>;
                loaded: {
                    subscribe: (
                        this: void,
                        run: Subscriber<boolean>,
                        invalidate?: () => void,
                    ) => Unsubscriber;
                };
                loadMessages: (conversationId: number) => Promise<void>;
                loadMoreConversations: () => Promise<boolean>;
                loadOlderMessages: (conversationId: number) => Promise<boolean>;
                markAsRead: (conversationId: number) => Promise<void>;
                messages: {
                    subscribe: (
                        this: void,
                        run: Subscriber<Record<number, UIMessage[]>>,
                        invalidate?: () => void,
                    ) => Unsubscriber;
                };
                renameConversation: (
                    conversationId: number,
                    name: string,
                ) => Promise<void>;
                sendMessage: (
                    conversationId: number,
                    message: string,
                    attachments?: { attachment?: string; photo_id: number }[],
                    replyToId?: number | null,
                ) => Promise<Message | null>;
                setActiveConversationId: (id: number | null) => void;
                startConversation: (
                    phone: string,
                    isGroup?: boolean,
                ) => Promise<UIConversation | null>;
                subscribe: (
                    this: void,
                    run: Subscriber<UIConversation[]>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            loadMessageReactions: (ids: number[]) => Promise<void>;
            messageReactions: ReactionStore;
            sendMessage: (
                conversationId: number,
                text: string,
            ) => Promise<Message | null>;
            startText: (phone: string) => void;
            toggleMessageReaction: (messageId: number, emoji: string) => Promise<void>;
            unreadMessagesCount: Readable<number>;
        };
        music: () => {
            audibleBroadcasts: Readable<AudibleBroadcast[]>;
            canPlay: (input: string) => boolean;
            clearMutedBroadcasters: () => void;
            clearQueue: () => void;
            cycleRepeat: () => void;
            describeMusicError: (
                reason: MusicErrorReason,
                translate?: Translate,
            ) => string;
            enqueue: (input: string) => void;
            maxAudibleBroadcasts: number;
            musicError: Readable<MusicError | null>;
            musicHasNext: Readable<boolean>;
            musicHasPrevious: Readable<boolean>;
            musicIndex: Readable<number>;
            musicMuted: Writable<boolean>;
            musicNowPlaying: Readable<MusicNowPlaying | null>;
            musicPosition: Readable<MusicPosition>;
            musicQueue: Readable<QueueEntry[]>;
            musicRepeat: Readable<MusicRepeat>;
            musicShuffle: Readable<boolean>;
            musicSource: Readable<MusicSource | null>;
            musicStatus: Readable<MusicStatus>;
            musicVolume: Writable<number>;
            muteAllNearby: Readable<boolean>;
            muteBroadcaster: (token: string) => void;
            mutedBroadcasters: Readable<string[]>;
            nearbyBroadcasts: Readable<NearbyBroadcast[]>;
            nextTrack: () => void;
            pauseMusic: () => void;
            playQueueIndex: (index: number) => void;
            playSource: (input: string) => void;
            previousTrack: () => void;
            removeFromQueue: (key: string) => void;
            resumeMusic: () => void;
            seekMusic: (to: number) => void;
            setMusicMuted: (muted: boolean) => void;
            setMusicVolume: (value: number) => void;
            setMuteAllNearby: (on: boolean) => void;
            setRepeat: (mode: MusicRepeat) => void;
            stopMusic: () => void;
            thumbnailUrlFor: (videoId: string | null | undefined) => string | null;
            toggleBroadcasterMute: (token: string) => void;
            toggleMusicMute: () => void;
            toggleMuteAllNearby: () => void;
            toggleShuffle: () => void;
            unmuteBroadcaster: (token: string) => void;
        };
        navigation: () => {
            closePhone: () => void;
            currentApp: Writable<RunningApp>;
            goHome: () => void;
            openApp: (appName: string, props?: Record<string, unknown>) => void;
        };
        notifications: (
            appId?: string,
        ) => {
            clear: (ids: number[]) => Promise<void>;
            clearAll: (targetAppId?: string) => Promise<void>;
            load: () => Promise<void>;
            loaded: Writable<boolean>;
            markRead: (ids: number[]) => Promise<void>;
            notificationsStore: Readable<NotificationItem[]>;
            totalUnread: Readable<number>;
            unreadCount: Readable<number>;
        };
        notificationSettings: () => {
            appNotificationPolicies: Writable<
                { [x: string]: AppNotificationPolicy },
            >;
            appPolicyStore: (appId: string) => Readable<AppNotificationPolicy>;
            badgesEnabled: Writable<boolean>;
            customisedNotificationApps: Readable<string[]>;
            dndEnabled: Writable<boolean>;
            notificationSoundEnabled: Writable<boolean>;
            toastsEnabled: Writable<boolean>;
        };
        notificationSettingsWrite: () => {
            clearAppNotificationPolicy: (appId: string) => void;
            setAppNotificationPolicy: (
                appId: string,
                patch: Partial<AppNotificationPolicy>,
            ) => void;
            setBadgesEnabled: (value: boolean) => void;
            setDndEnabled: (value: boolean) => void;
            setNotificationSoundEnabled: (value: boolean) => void;
            setToastsEnabled: (value: boolean) => void;
        };
        onAppForeground: (appId: string, handler: () => void) => () => void;
        onAppUnmount: (handler: () => void) => void;
        persisted: <T>(
            appId: string,
            key: string,
            initial: T,
            options?: PersistedOptions<T>,
        ) => Writable<T>;
        phoneNotification: () => {
            dismissNotification: (id: string) => void;
            sendNotification: (options: SendNotificationOptions) => string;
            toast: {
                archive: (id: string) => Promise<void>;
                clear: () => void;
                dismiss: (id: string) => void;
                pauseDismiss: (id: string) => void;
                resumeDismiss: (id: string, delay?: number) => void;
                show: (
                    options: Partial<ToastMessage> & {
                        message: string;
                        soundHandled?: boolean;
                    },
                ) => string;
                showCall: (
                    options: {
                        breakThrough?: boolean;
                        name?: string;
                        number: string;
                        onAccept: () => void | Promise<void>;
                        onDecline?: () => void | Promise<void>;
                        onExpire?: () => void | Promise<void>;
                    },
                ) => string;
                showContactShare: (
                    options: {
                        avatar?: string;
                        name: string;
                        onAccept: () => void | Promise<void>;
                        onClick?: () => void | Promise<void>;
                        onDecline?: () => void | Promise<void>;
                        phone: string;
                        senderLabel: string;
                    },
                ) => string;
                showIncomingMessage: (
                    options: {
                        avatar?: string;
                        message: string;
                        onClick?: () => void | Promise<void>;
                        onReply: (replyText: string) => void | Promise<void>;
                        sender: string;
                    },
                ) => string;
                showMail: (
                    options: { onClick?: () => void; sender: string; subject: string },
                ) => string;
                subscribe: (
                    this: void,
                    run: Subscriber<ToastMessage[]>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
        };
        report: () => { submit: (input: SubmitReportInput) => Promise<void> };
        reports: () => {
            loadPendingReports: () => Promise<void>;
            loadReportHistory: () => Promise<void>;
            pendingReportCount: Readable<number>;
            pendingReports: Writable<Report[]>;
            reopenReport: (id: number) => Promise<void>;
            resolvedReports: Writable<Report[]>;
            resolveReport: (
                id: number,
                action: "moderate" | "dismiss",
            ) => Promise<void>;
        };
        searchProvider: (
            appId: string,
        ) => {
            publish: (needle: string, hits: readonly ProvidedHit[]) => void;
            query: Readable<string>;
        };
        service: (
            serviceId: string,
        ) => {
            call: <T = unknown>(
                action: string,
                data?: unknown,
                defaultValue?: T,
            ) => Promise<T>;
            id: string;
        };
        sound: () => { play: (effect: SoundEffect) => void };
        sourceUrl: () => {
            refreshSourceUrl: () => Promise<void>;
            sourceUrl: Writable<string>;
        };
        storage: (
            appId: string,
        ) => {
            clear: () => void;
            getItem: <T = unknown>(key: string, defaultValue?: T) => T | null;
            markUnsynced: (key: string) => void;
            removeItem: (key: string) => void;
            setItem: <T = unknown>(key: string, value: T) => void;
        };
        streamerMode: () => {
            revealGeneration: Readable<number>;
            setStreamerMode: (on: boolean) => void;
            streamerMode: Readable<boolean>;
        };
        systemHardware: () => {
            bluetoothEnabled: Writable<boolean>;
            cellServiceEnabled: Writable<boolean>;
            charge: Writable<number>;
            isBluetoothDiscoverable: Readable<boolean>;
            notificationTone: Writable<string>;
            notificationToneChoices: Readable<readonly RingtoneOption[]>;
            ringMode: Writable<RingMode>;
            ringModeChoices: Readable<readonly RingModeChoice[]>;
            ringtone: Writable<string>;
            ringtoneChoices: Readable<readonly RingtoneOption[]>;
            signalLevel: Writable<number>;
            soundMuted: Writable<boolean>;
            soundVolume: Writable<number>;
            volumeStep: Writable<number>;
            volumeStepChoices: readonly [1, 2, 5, 10, 20];
        };
        systemHardwareWrite: () => {
            previewNotificationTone: (id: string) => void;
            previewRingtone: (id: string) => void;
            setCharge: (level: number) => void;
            setNotificationTone: (id: string) => void;
            setRingMode: (mode: RingMode) => void;
            setRingtone: (id: string) => void;
            setSignal: (level: number) => void;
            setVolume: (val: number) => void;
            setVolumeStep: (percent: number) => void;
            toggleBluetooth: () => void;
            toggleCellService: () => void;
            toggleMute: () => void;
        };
        theme: () => {
            defaultTheme: ThemeState;
            isLightMode: Readable<boolean>;
            sanitizeSeed: (value: unknown) => string;
            schemeStore: Readable<M3Tokens>;
            seedFromRgbString: (value: string) => string | null;
            themeStore: Writable<ThemeState>;
        };
        themeWrite: () => {
            resetTheme: () => void;
            setThemeMode: (mode: ThemeMode) => void;
            setThemeSeed: (seed: string) => void;
        };
        timer: () => {
            after: (ms: number, handler: () => void) => CancelTimer;
            clearAll: () => void;
            every: (ms: number, handler: () => void) => CancelTimer;
        };
        wallpaper: () => {
            activeSeed: Readable<string>;
            backgroundForSeed: (seed: string, mode: ThemeMode) => string;
            defaultWallpaper: WallpaperState;
            presets: readonly WallpaperPreset[];
            seedFromImage: (source: string) => Promise<string | null>;
            wallpaperBackground: Readable<string>;
            wallpaperNeedsContrast: Readable<boolean>;
            wallpaperStore: Writable<WallpaperState>;
        };
        wallpaperWrite: () => {
            resetWallpaper: () => void;
            setPresetWallpaper: (preset: WallpaperPreset) => void;
            setWallpaperImage: (image: string, seed?: string) => void;
            setWallpaperSeed: (seed: string) => void;
        };
    }
    Index
    account: () => {
        bankBalance: Writable<number>;
        citizenid: Writable<string>;
        fetchBalance: () => Promise<void>;
        fetchCitizenId: () => Promise<string>;
        fetchPhoneNumber: () => Promise<string>;
        fetchTransactions: () => Promise<void>;
        historySource: Writable<BankHistorySource>;
        myPhoneNumber: Writable<string>;
        transactions: Writable<Transaction[]>;
        transactionsLoaded: Writable<boolean>;
    }
    accounts: () => {
        blockAccount: (
            input: {
                app: string;
                blocked_account_id: number;
                blocker_account_id: number;
            },
        ) => Promise<unknown>;
        createAccount: (
            input: { app: string; display_name?: string; handle: string },
        ) => Promise<Account>;
        followAccount: (
            input: {
                app: string;
                followee_account_id: number;
                follower_account_id: number;
            },
        ) => Promise<unknown>;
        getAccounts: (
            query: { app: string; handle?: string; limit?: number },
        ) => Promise<{ rows: Account[] }>;
        getFollowers: (query: FollowListQuery) => Promise<FollowPage>;
        getFollowing: (query: FollowListQuery) => Promise<FollowPage>;
        getFollowStats: (
            input: { account_id: number; app: string; viewer_account_id?: number },
        ) => Promise<FollowStats>;
        getMyAccounts: (app: string) => Promise<{ limit: number; rows: Account[] }>;
        getReactionsFor: (
            target: ReactionTarget,
        ) => Promise<Record<number, ReactionSummary>>;
        reactToTarget: (
            payload: {
                account_id: number;
                app: string;
                emoji: string;
                target_id: number;
                target_table: string;
            },
        ) => Promise<unknown>;
        searchAccounts: (
            query: AccountSearchQuery,
        ) => Promise<{ nextCursor: number | null; rows: Account[] }>;
        unblockAccount: (
            input: {
                app: string;
                blocked_account_id: number;
                blocker_account_id: number;
            },
        ) => Promise<unknown>;
        unfollowAccount: (
            input: {
                app: string;
                followee_account_id: number;
                follower_account_id: number;
            },
        ) => Promise<unknown>;
        unreactToTarget: (
            payload: {
                account_id: number;
                app: string;
                emoji: string;
                target_id: number;
                target_table: string;
            },
        ) => Promise<unknown>;
        updateAccount: (
            input: { id: number } & Partial<Account>,
        ) => Promise<unknown>;
    }
    admin: () => { isAdmin: Writable<boolean>; refreshAdmin: () => Promise<void> }
    appAction: (
        appId?: string,
    ) => {
        busy: Writable<boolean>;
        notify: (
            n: { message: string; title?: string; type: "error" | "success" },
        ) => string;
        run: (work: () => unknown, options?: AppActionOptions) => Promise<boolean>;
    }
    appEvents: (
        appId: string,
    ) => {
        clear: () => void;
        on: <T = Record<string, unknown>>(
            event: string,
            handler: (e: AppEvent<T>) => void,
        ) => () => void;
        onAny: (handler: (e: AppEvent) => void) => () => void;
    }
    appLevels: (
        config: AppLevelsConfig,
    ) => { back: () => void; release: () => void; title: string }
    appRegistry: () => {
        bundledAddOns: AppManifest[];
        getFirstBootTime: () => string;
        registryStore: {
            getAddOnSource: (appId: string) => Promise<string | undefined>;
            getComponent: (appId: string) => AppComponent | undefined;
            getManifest: (appId: string) => AppManifest | undefined;
            installFromCatalog: (
                entry: CatalogEntry,
            ) => Promise<{ manifest: AppManifest }>;
            isInstalled: (appId: string) => boolean;
            isKnownApp: (appId: string) => boolean;
            loadComponent: (appId: string) => Promise<AppComponent | undefined>;
            registerAddOn: (manifest: AppManifest, source?: string) => void;
            registerApp: (manifest: AppManifest, component: AppComponent) => void;
            rehydrateSavedRemoteApps: () => Promise<void>;
            subscribe: (
                this: void,
                run: Subscriber<AppManifest[]>,
                invalidate?: () => void,
            ) => Unsubscriber;
            unregisterApp: (appId: string) => void;
        };
        updateCount: Readable<number>;
        updatesStore: Readable<AppUpdate[]>;
    }
    appRegistryWrite: () => {
        fetchRemoteCatalog: () => Promise<
            | { entries: CatalogEntry[]; status: "ok" }
            | { status: "off" }
            | { status: "unavailable" },
        >;
        grantedPermissions: (
            appId: string,
        ) =>
            | readonly (
                | "messages"
                | "admin"
                | "location"
                | "camera"
                | "account"
                | "bank"
                | "call"
                | "contacts"
                | "control-center"
                | "highscores"
                | "jobs"
                | "mail"
                | "marketplace"
                | "media"
                | "notifications"
                | "reports"
                | "social"
                | "storage"
                | "app-events"
                | "app-registry"
                | "app-registry-write"
                | "clock"
                | "clock-write"
                | "devtools"
                | "display"
                | "display-write"
                | "keybinds"
                | "keybinds-write"
                | "lock-screen"
                | "lock-screen-write"
                | "music"
                | "navigation"
                | "notification-settings"
                | "notification-settings-write"
                | "system-hardware"
                | "system-hardware-write"
                | "theme"
                | "theme-write"
                | "wallpaper"
                | "wallpaper-write"
            )[]
            | undefined;
        installFromCatalog: (
            entry: CatalogEntry,
        ) => Promise<{ manifest: AppManifest }>;
        recordConsent: (
            appId: string,
            permissions: readonly (
                | "messages"
                | "admin"
                | "location"
                | "camera"
                | "account"
                | "bank"
                | "call"
                | "contacts"
                | "control-center"
                | "highscores"
                | "jobs"
                | "mail"
                | "marketplace"
                | "media"
                | "notifications"
                | "reports"
                | "social"
                | "storage"
                | "app-events"
                | "app-registry"
                | "app-registry-write"
                | "clock"
                | "clock-write"
                | "devtools"
                | "display"
                | "display-write"
                | "keybinds"
                | "keybinds-write"
                | "lock-screen"
                | "lock-screen-write"
                | "music"
                | "navigation"
                | "notification-settings"
                | "notification-settings-write"
                | "system-hardware"
                | "system-hardware-write"
                | "theme"
                | "theme-write"
                | "wallpaper"
                | "wallpaper-write"
            )[],
        ) => void;
        refreshUpdates: () => Promise<AppUpdate[]>;
        registerAddOn: (manifest: AppManifest, source?: string) => void;
        registerApp: (manifest: AppManifest, component: AppComponent) => void;
        unregisterApp: (appId: string) => void;
        updateApp: (appId: string) => Promise<AppManifest>;
    }
    appStorageBytes: (appId: string) => number
    bank: () => {
        declineInvoice: (id: number) => Promise<InvoiceActionOutcome>;
        fetchInvoices: () => Promise<void>;
        invoices: Writable<Invoice[]>;
        invoicesLoaded: Writable<boolean>;
        payInvoice: (id: number) => Promise<InvoiceActionOutcome>;
        sendMoney: (input: SendMoneyInput) => Promise<SendMoneyOutcome>;
    }
    call: () => {
        answerCall: () => Promise<void>;
        callLog: Writable<PhoneCallLogEntry[]>;
        callStore: {
            answerCall: () => Promise<void>;
            endCall: () => Promise<void>;
            setIncoming: (number: string, name?: string) => void;
            setStatus: (status: CallStatus) => void;
            startCall: (number: string, name?: string) => Promise<void>;
            subscribe: (
                this: void,
                run: Subscriber<CallState>,
                invalidate?: () => void,
            ) => Unsubscriber;
            toggleMute: () => Promise<void>;
            toggleSpeaker: () => Promise<void>;
        };
        endCall: () => Promise<void>;
        loadCallLog: () => Promise<void>;
        startCall: (number: string, name?: string) => Promise<void>;
        toggleSpeaker: () => Promise<void>;
    }
    camera: () => {
        isPreviewingPhoto: Writable<boolean>;
        isTakingPhoto: Writable<boolean>;
    }
    clearAppStorage: (appId: string) => void
    clock: () => {
        formattedTime: Readable<string>;
        is24Hour: Writable<boolean>;
        time: Writable<TimeState>;
    }
    clockWrite: () => { setIs24Hour: (value: boolean) => void }
    contacts: () => {
        addContact: (
            firstname: string,
            phone: string,
            lastname?: string,
            avatar?: string,
            favorite?: boolean,
        ) => Promise<Contact>;
        contactsStore: {
            add: (
                draft: Omit<Contact, "id" | "citizenid" | "created_at" | "updated_at">,
            ) => Promise<Contact>;
            delete: (id: number) => Promise<void>;
            getDeleted: () => Promise<Contact[]>;
            load: () => Promise<void>;
            loaded: {
                subscribe: (
                    this: void,
                    run: Subscriber<boolean>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            patch: (id: number, changes: Partial<Contact>) => void;
            restore: (id: number) => Promise<boolean>;
            set: (rows: Contact[]) => void;
            share: (
                payload: Partial<Contact> & { name?: string; phone: string },
            ) => Promise<void>;
            subscribe: (
                this: void,
                run: Subscriber<Contact[]>,
                invalidate?: () => void,
            ) => Unsubscriber;
            update: (row: Contact) => Promise<void>;
        };
        favoriteContacts: Readable<Contact[]>;
        getDeletedContacts: () => Promise<Contact[]>;
        restoreContact: (id: number) => Promise<boolean>;
        shareContact: (
            firstname: string,
            phone: string,
            lastname?: string,
        ) => Promise<void>;
    }
    controlCenter: (
        appId: string,
    ) => {
        lease: Readable<boolean>;
        registerToggle: (toggle: ControlCenterToggle) => void;
        setToggleActive: (id: string, active: boolean) => void;
        unregisterToggle: (id: string) => void;
    }

    MICA-247: an app's own switches in the control center.

    lease is held for as long as a caller wants its switches listed: the shell releases every switch an instance registered when the last subscriber to that instance's lease goes, and refuses a registration made without one. For a sandboxed frame the lease is a subscription the shell holds on the frame's behalf, so the frame's teardown drops it whether or not the frame's own code gets to run first.

    deepLink: (appId: string, handle: () => boolean) => void
    devTools: () => {
        devToolsUnlocked: Writable<boolean>;
        lock: () => void;
        unlock: () => void;
    }
    directMessages: (
        appId: string,
    ) => {
        markRead: (
            input: { account_id: number; peer_account_id: number },
        ) => Promise<boolean>;
        send: (
            input: { account_id: number; body: string; peer_account_id: number },
        ) => Promise<DirectMessage>;
        thread: (
            query: {
                account_id: number;
                cursor?: number | null;
                limit?: number;
                peer_account_id: number;
            },
        ) => Promise<{ nextCursor: number | null; rows: DirectMessage[] }>;
        threads: () => Promise<DirectMessageThread[]>;
    }

    MICA-390. One app's direct messages, on the dms service with the app as a payload field. The factory's appId is the app every member passes, pinned to the caller for a sandboxed frame (APP_SCOPED_FACETS), so no member takes one of its own.

    display: () => {
        device: Readable<"phone" | "tablet">;
        displaySize: Writable<number>;
        displaySizeDefault: number;
        frame: Readable<{ height: number; width: number }>;
        homeGridColumns: Writable<number>;
        homeGridColumnsDefault: number;
        homeGridColumnsMax: number;
        homeGridColumnsMin: number;
        homeGridRows: Writable<number>;
        homeGridRowsDefault: number;
        homeGridRowsMax: number;
        homeGridRowsMin: number;
        isSizeLimited: Readable<boolean>;
        motionPreference: Writable<MotionPreference>;
        motionPreferenceDefault: MotionPreference;
        phoneBox: Readable<{ height: number; width: number }>;
        phoneScale: Readable<number>;
        reducedMotion: Readable<boolean>;
    }
    displayWrite: () => {
        setDisplaySize: (size: number) => void;
        setFrame: (id: string) => void;
        setFrameColor: (id: string) => void;
        setHomeGridSize: (columns: number, rows: number) => void;
        setMotionPreference: (preference: MotionPreference) => void;
    }
    highscores: () => {
        getLeaderboard: (app: string) => Promise<LeaderboardEntry[]>;
        submitScore: (app: string, score: number) => Promise<void>;
    }
    jobs: () => {
        fetchJobs: () => Promise<void>;
        jobs: Writable<JobView[]>;
        jobsLoaded: Writable<boolean>;
        setActiveJob: (name: string) => Promise<JobActionOutcome>;
        setDuty: (name: string, onDuty: boolean) => Promise<JobActionOutcome>;
    }

    MICA-228. Every job the player holds, and the two things the phone may do about them. setActiveJob and setDuty answer with the re-read list on success, and the in-process facet writes it into jobs before resolving, so a caller need not fetch again. The name is only ever compared against the player's own list on the server — unknown_job is the answer for anything else.

    keybinds: () => {
        bindings: Readable<Record<string, string>>;
        findConflict: (actionId: string, key: string) => KeybindAction | undefined;
        groups: Readable<KeybindGroup[]>;
        onKeybind: (
            actionId: string,
            handler: () => void,
            appId?: string,
        ) => () => void;
    }
    keybindsWrite: () => {
        resetBindings: () => void;
        setBinding: (actionId: string, key: string) => void;
    }
    lifecycle: (
        appId: string,
    ) => {
        consumeDeepLink: () => void;
        currentApp: Writable<RunningApp>;
        goHome: () => void;
        onBack: (handler: () => void) => () => void;
        visible: Readable<boolean>;
    }
    locale: (
        appId?: string,
    ) => {
        catalogs: Readable<
            Readonly<
                Record<
                    string,
                    Readonly<Record<string, Readonly<Record<string, string>>>>,
                >,
            >,
        >;
        locale: Readable<string>;
        setLocale: (next: string) => void;
    }

    MICA-61: the active locale, and the one way to change it. setLocale is for Settings; an add-on may read the locale and never set it (FACET_MEMBERS).

    MICA-235: catalogs is what the shell's registry holds for appId's own namespace and ui, along the active locale's fallback chain (its base language, then en), as { [namespace]: { [locale]: messages } } — how an owner's locales/ files reach a sandboxed frame, which merges it with registerMessages. For an add-on the shell pins appId to the caller, so no frame can ask for another app's strings; with no appId it is empty, since an in-process app already shares the shell's registry.

    location: () => {
        setWaypoint: (x: number, y: number) => Promise<void>;
        shareLocation: () => Promise<{ id: number; media: MediaPreview }>;
    }
    lockScreen: () => {
        autoLockPolicy: Writable<AutoLockPolicy>;
        autoLockPolicyChoices: Readable<readonly AutoLockPolicyChoice[]>;
        hasPasscode: Writable<boolean>;
    }
    lockScreenWrite: () => {
        clearPasscode: () => Promise<void>;
        setAutoLockPolicy: (policy: AutoLockPolicy) => void;
        setPasscode: (digits: string) => Promise<void>;
    }
    mail: () => {
        addReceivedMail: (newMail: Mail) => void;
        archiveMail: (id: number, archiveState?: boolean) => Promise<void>;
        deleteMail: (id: number) => Promise<void>;
        mailStore: {
            add: (draft: Omit<Mail, "id">) => Promise<Mail>;
            addReceivedMail: (incoming: Mail) => void;
            archive: (id: number, archive?: boolean) => Promise<void>;
            delete: (id: number) => Promise<void>;
            load: () => Promise<void>;
            loaded: {
                subscribe: (
                    this: void,
                    run: Subscriber<boolean>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            markAsRead: (id: number) => Promise<void>;
            patch: (id: number, changes: Partial<Mail>) => void;
            set: (rows: Mail[]) => void;
            subscribe: (
                this: void,
                run: Subscriber<Mail[]>,
                invalidate?: () => void,
            ) => Unsubscriber;
            update: (row: Mail) => Promise<void>;
        };
        markAsRead: (id: number) => Promise<void>;
        unreadMailCount: Readable<number>;
    }
    marketplace: () => {
        feedStore: Writable<ListingPage>;
        loadFeed: () => Promise<void>;
        loadMine: () => Promise<void>;
        markSold: (id: number) => Promise<boolean>;
        mineStore: Writable<ListingPage>;
        postListing: (input: CreateListingInput) => Promise<Listing>;
        removeListing: (id: number) => Promise<boolean>;
        searchListings: (q: string) => Promise<ListingPage>;
        viewListing: (
            id: number,
        ) => Promise<
            Listing & { contactPhone: string | null; isOwn: boolean }
            | null,
        >;
    }
    media: () => {
        capturePhoto: (data: string, thumbnail?: string) => Promise<MediaItem>;
        deletePhoto: (id: number) => Promise<void>;
        dropNearby: (mediaId: number) => Promise<{ count: number }>;
        fullMedia: (mediaId: number) => Promise<MediaItem>;
        getDeletedMedia: () => Promise<DeletedMediaItem[]>;
        media: {
            add: (
                draft: Omit<
                    MediaItem,
                    "id"
                    | "citizenid"
                    | "created_at"
                    | "updated_at",
                >,
            ) => Promise<MediaItem>;
            delete: (mediaId: number) => Promise<void>;
            dropNearby: (mediaId: number) => Promise<{ count: number }>;
            full: (mediaId: number) => Promise<MediaItem>;
            getDeleted: () => Promise<DeletedMediaItem[]>;
            hasMore: Readable<boolean>;
            hydrate: (item: MediaPreview) => void;
            load: (filter?: Record<string, unknown>) => Promise<void>;
            loaded: Readable<boolean>;
            receive: (mediaId?: number) => Promise<void>;
            restore: (mediaId: number) => Promise<boolean>;
            setThumbnail: (mediaId: number, thumbnail: string) => Promise<boolean>;
            setWaypoint: (x: number, y: number) => Promise<void>;
            shareLocation: () => Promise<{ id: number; media: MediaPreview }>;
            loadMore(): Promise<boolean>;
            prepend(row: MediaItem): void;
            remove(id: number): void;
            replace(row: MediaItem): void;
            subscribe(
                this: void,
                run: Subscriber<MediaItem[]>,
                invalidate?: () => void,
            ): Unsubscriber;
        };
        restoreMedia: (mediaId: number) => Promise<boolean>;
    }
    messages: () => {
        addReceivedMessage: (message: IncomingMessage) => void;
        conversationsStore: {
            activeConversationId: {
                subscribe: (
                    this: void,
                    run: Subscriber<number | null>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            addReceivedMessage: (incoming: IncomingMessage) => void;
            archiveConversation: (
                conversationId: number,
                archive?: boolean,
            ) => Promise<void>;
            deleteConversation: (conversationId: number) => Promise<void>;
            deleteMessage: (
                conversationId: number,
                messageId: number,
            ) => Promise<boolean>;
            editMessage: (
                conversationId: number,
                messageId: number,
                message: string,
            ) => Promise<{ edited?: boolean; message: string } | null>;
            hasMore: {
                subscribe: (
                    this: void,
                    run: Subscriber<boolean>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            hasOlderMessages: {
                subscribe: (
                    this: void,
                    run: Subscriber<Record<number, boolean>>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            loadConversations: () => Promise<void>;
            loaded: {
                subscribe: (
                    this: void,
                    run: Subscriber<boolean>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            loadMessages: (conversationId: number) => Promise<void>;
            loadMoreConversations: () => Promise<boolean>;
            loadOlderMessages: (conversationId: number) => Promise<boolean>;
            markAsRead: (conversationId: number) => Promise<void>;
            messages: {
                subscribe: (
                    this: void,
                    run: Subscriber<Record<number, UIMessage[]>>,
                    invalidate?: () => void,
                ) => Unsubscriber;
            };
            renameConversation: (
                conversationId: number,
                name: string,
            ) => Promise<void>;
            sendMessage: (
                conversationId: number,
                message: string,
                attachments?: { attachment?: string; photo_id: number }[],
                replyToId?: number | null,
            ) => Promise<Message | null>;
            setActiveConversationId: (id: number | null) => void;
            startConversation: (
                phone: string,
                isGroup?: boolean,
            ) => Promise<UIConversation | null>;
            subscribe: (
                this: void,
                run: Subscriber<UIConversation[]>,
                invalidate?: () => void,
            ) => Unsubscriber;
        };
        loadMessageReactions: (ids: number[]) => Promise<void>;
        messageReactions: ReactionStore;
        sendMessage: (
            conversationId: number,
            text: string,
        ) => Promise<Message | null>;
        startText: (phone: string) => void;
        toggleMessageReaction: (messageId: number, emoji: string) => Promise<void>;
        unreadMessagesCount: Readable<number>;
    }
    music: () => {
        audibleBroadcasts: Readable<AudibleBroadcast[]>;
        canPlay: (input: string) => boolean;
        clearMutedBroadcasters: () => void;
        clearQueue: () => void;
        cycleRepeat: () => void;
        describeMusicError: (
            reason: MusicErrorReason,
            translate?: Translate,
        ) => string;
        enqueue: (input: string) => void;
        maxAudibleBroadcasts: number;
        musicError: Readable<MusicError | null>;
        musicHasNext: Readable<boolean>;
        musicHasPrevious: Readable<boolean>;
        musicIndex: Readable<number>;
        musicMuted: Writable<boolean>;
        musicNowPlaying: Readable<MusicNowPlaying | null>;
        musicPosition: Readable<MusicPosition>;
        musicQueue: Readable<QueueEntry[]>;
        musicRepeat: Readable<MusicRepeat>;
        musicShuffle: Readable<boolean>;
        musicSource: Readable<MusicSource | null>;
        musicStatus: Readable<MusicStatus>;
        musicVolume: Writable<number>;
        muteAllNearby: Readable<boolean>;
        muteBroadcaster: (token: string) => void;
        mutedBroadcasters: Readable<string[]>;
        nearbyBroadcasts: Readable<NearbyBroadcast[]>;
        nextTrack: () => void;
        pauseMusic: () => void;
        playQueueIndex: (index: number) => void;
        playSource: (input: string) => void;
        previousTrack: () => void;
        removeFromQueue: (key: string) => void;
        resumeMusic: () => void;
        seekMusic: (to: number) => void;
        setMusicMuted: (muted: boolean) => void;
        setMusicVolume: (value: number) => void;
        setMuteAllNearby: (on: boolean) => void;
        setRepeat: (mode: MusicRepeat) => void;
        stopMusic: () => void;
        thumbnailUrlFor: (videoId: string | null | undefined) => string | null;
        toggleBroadcasterMute: (token: string) => void;
        toggleMusicMute: () => void;
        toggleMuteAllNearby: () => void;
        toggleShuffle: () => void;
        unmuteBroadcaster: (token: string) => void;
    }
    navigation: () => {
        closePhone: () => void;
        currentApp: Writable<RunningApp>;
        goHome: () => void;
        openApp: (appName: string, props?: Record<string, unknown>) => void;
    }
    notifications: (
        appId?: string,
    ) => {
        clear: (ids: number[]) => Promise<void>;
        clearAll: (targetAppId?: string) => Promise<void>;
        load: () => Promise<void>;
        loaded: Writable<boolean>;
        markRead: (ids: number[]) => Promise<void>;
        notificationsStore: Readable<NotificationItem[]>;
        totalUnread: Readable<number>;
        unreadCount: Readable<number>;
    }
    notificationSettings: () => {
        appNotificationPolicies: Writable<
            { [x: string]: AppNotificationPolicy },
        >;
        appPolicyStore: (appId: string) => Readable<AppNotificationPolicy>;
        badgesEnabled: Writable<boolean>;
        customisedNotificationApps: Readable<string[]>;
        dndEnabled: Writable<boolean>;
        notificationSoundEnabled: Writable<boolean>;
        toastsEnabled: Writable<boolean>;
    }
    notificationSettingsWrite: () => {
        clearAppNotificationPolicy: (appId: string) => void;
        setAppNotificationPolicy: (
            appId: string,
            patch: Partial<AppNotificationPolicy>,
        ) => void;
        setBadgesEnabled: (value: boolean) => void;
        setDndEnabled: (value: boolean) => void;
        setNotificationSoundEnabled: (value: boolean) => void;
        setToastsEnabled: (value: boolean) => void;
    }
    onAppForeground: (appId: string, handler: () => void) => () => void
    onAppUnmount: (handler: () => void) => void
    persisted: <T>(
        appId: string,
        key: string,
        initial: T,
        options?: PersistedOptions<T>,
    ) => Writable<T>
    phoneNotification: () => {
        dismissNotification: (id: string) => void;
        sendNotification: (options: SendNotificationOptions) => string;
        toast: {
            archive: (id: string) => Promise<void>;
            clear: () => void;
            dismiss: (id: string) => void;
            pauseDismiss: (id: string) => void;
            resumeDismiss: (id: string, delay?: number) => void;
            show: (
                options: Partial<ToastMessage> & {
                    message: string;
                    soundHandled?: boolean;
                },
            ) => string;
            showCall: (
                options: {
                    breakThrough?: boolean;
                    name?: string;
                    number: string;
                    onAccept: () => void | Promise<void>;
                    onDecline?: () => void | Promise<void>;
                    onExpire?: () => void | Promise<void>;
                },
            ) => string;
            showContactShare: (
                options: {
                    avatar?: string;
                    name: string;
                    onAccept: () => void | Promise<void>;
                    onClick?: () => void | Promise<void>;
                    onDecline?: () => void | Promise<void>;
                    phone: string;
                    senderLabel: string;
                },
            ) => string;
            showIncomingMessage: (
                options: {
                    avatar?: string;
                    message: string;
                    onClick?: () => void | Promise<void>;
                    onReply: (replyText: string) => void | Promise<void>;
                    sender: string;
                },
            ) => string;
            showMail: (
                options: { onClick?: () => void; sender: string; subject: string },
            ) => string;
            subscribe: (
                this: void,
                run: Subscriber<ToastMessage[]>,
                invalidate?: () => void,
            ) => Unsubscriber;
        };
    }
    report: () => { submit: (input: SubmitReportInput) => Promise<void> }
    reports: () => {
        loadPendingReports: () => Promise<void>;
        loadReportHistory: () => Promise<void>;
        pendingReportCount: Readable<number>;
        pendingReports: Writable<Report[]>;
        reopenReport: (id: number) => Promise<void>;
        resolvedReports: Writable<Report[]>;
        resolveReport: (
            id: number,
            action: "moderate" | "dismiss",
        ) => Promise<void>;
    }
    searchProvider: (
        appId: string,
    ) => {
        publish: (needle: string, hits: readonly ProvidedHit[]) => void;
        query: Readable<string>;
    }

    MICA-286: an app's own rows in the phone's search, without the app handing a function across the add-on seam.

    The seam carries data one way — the shell pushes store values into a frame and answers calls out of it — and has no shell-to-frame call direction at all, so SearchProvider.search (web/src/shell/state/searchResults.ts) is not something a sandboxed add-on could ever be. This inverts it into the two shapes the seam already has: query is a store the shell pushes, publish is a call the frame makes.

    query is the current needle, already trimmed and lower-cased — the same string searchEverything hands a provider, so an app matches against one spelling rather than inventing its own. It is '' whenever nothing is being searched.

    publish states which needle the hits answer, so the shell can drop a reply for a query the player has already typed past. The app searches its own rows inside its own process; nothing but the hits it chose crosses the seam.

    service: (
        serviceId: string,
    ) => {
        call: <T = unknown>(
            action: string,
            data?: unknown,
            defaultValue?: T,
        ) => Promise<T>;
        id: string;
    }
    sound: () => { play: (effect: SoundEffect) => void }
    sourceUrl: () => {
        refreshSourceUrl: () => Promise<void>;
        sourceUrl: Writable<string>;
    }

    MICA-192: the AGPL §13 source address this server reports, and a way to re-ask.

    storage: (
        appId: string,
    ) => {
        clear: () => void;
        getItem: <T = unknown>(key: string, defaultValue?: T) => T | null;
        markUnsynced: (key: string) => void;
        removeItem: (key: string) => void;
        setItem: <T = unknown>(key: string, value: T) => void;
    }
    streamerMode: () => {
        revealGeneration: Readable<number>;
        setStreamerMode: (on: boolean) => void;
        streamerMode: Readable<boolean>;
    }

    MICA-249: streamer mode. streamerMode is the player's choice — blur every player-supplied picture until it is tapped; revealGeneration bumps whenever a revealed picture should hide again (the flag flips, the foreground app changes, the device closes). setStreamerMode is for Settings; an add-on reads both stores and never sets the flag (FACET_MEMBERS).

    systemHardware: () => {
        bluetoothEnabled: Writable<boolean>;
        cellServiceEnabled: Writable<boolean>;
        charge: Writable<number>;
        isBluetoothDiscoverable: Readable<boolean>;
        notificationTone: Writable<string>;
        notificationToneChoices: Readable<readonly RingtoneOption[]>;
        ringMode: Writable<RingMode>;
        ringModeChoices: Readable<readonly RingModeChoice[]>;
        ringtone: Writable<string>;
        ringtoneChoices: Readable<readonly RingtoneOption[]>;
        signalLevel: Writable<number>;
        soundMuted: Writable<boolean>;
        soundVolume: Writable<number>;
        volumeStep: Writable<number>;
        volumeStepChoices: readonly [1, 2, 5, 10, 20];
    }
    systemHardwareWrite: () => {
        previewNotificationTone: (id: string) => void;
        previewRingtone: (id: string) => void;
        setCharge: (level: number) => void;
        setNotificationTone: (id: string) => void;
        setRingMode: (mode: RingMode) => void;
        setRingtone: (id: string) => void;
        setSignal: (level: number) => void;
        setVolume: (val: number) => void;
        setVolumeStep: (percent: number) => void;
        toggleBluetooth: () => void;
        toggleCellService: () => void;
        toggleMute: () => void;
    }
    theme: () => {
        defaultTheme: ThemeState;
        isLightMode: Readable<boolean>;
        sanitizeSeed: (value: unknown) => string;
        schemeStore: Readable<M3Tokens>;
        seedFromRgbString: (value: string) => string | null;
        themeStore: Writable<ThemeState>;
    }
    themeWrite: () => {
        resetTheme: () => void;
        setThemeMode: (mode: ThemeMode) => void;
        setThemeSeed: (seed: string) => void;
    }
    timer: () => {
        after: (ms: number, handler: () => void) => CancelTimer;
        clearAll: () => void;
        every: (ms: number, handler: () => void) => CancelTimer;
    }
    wallpaper: () => {
        activeSeed: Readable<string>;
        backgroundForSeed: (seed: string, mode: ThemeMode) => string;
        defaultWallpaper: WallpaperState;
        presets: readonly WallpaperPreset[];
        seedFromImage: (source: string) => Promise<string | null>;
        wallpaperBackground: Readable<string>;
        wallpaperNeedsContrast: Readable<boolean>;
        wallpaperStore: Writable<WallpaperState>;
    }
    wallpaperWrite: () => {
        resetWallpaper: () => void;
        setPresetWallpaper: (preset: WallpaperPreset) => void;
        setWallpaperImage: (image: string, seed?: string) => void;
        setWallpaperSeed: (seed: string) => void;
    }