import { AssistantConversationProcessing, AssistantConversationStatus, AssistantMessageMetadata, AssistantSandboxRuntime, AssistantSandboxStatus, AssistantTurnStage, Conversation, Message } from './conversation'; import { AssistantMode } from './types'; interface AssistantState { conversations: Conversation[]; activeConversationId: string | null; mode: AssistantMode; processingByConversationId: Record; conversationListOpen: boolean; /** Active sandboxes keyed by assistant mode */ sandboxesByMode: Partial>; /** * A prompt queued up for auto-submit by the chat area. Used by the * `GetHelpButton` / `useOpenAssistantHelp` hook so pages can prompt the * assistant contextually from an empty state button without coupling to * the chat area internals. */ pendingPrompt: string | null; /** * The write-capable modes whose consent dialog this profile has accepted, by * mode id (BOFF-7308). Persisted per-profile so the dialog shows once per * MODE, not on every switch. * * ★★ Per mode. It replaced one `apiCallsAcknowledged` flag that covered every * write-capable mode, so accepting the dialog for one skipped it for all the * others. A stored flag is carried over to `api-calls` only — see * `restoreAcknowledgedWriteModes`. */ acknowledgedWriteModes: readonly AssistantMode[]; /** * Read every assistant reply aloud as it settles (BOFF-6290). * * Persisted per-profile and **false by default**: speech is opt-in, and the * assistant must never start talking without an explicit prior choice. A * one-off "read this message" needs no preference at all — it is the speak * button on the message. */ autoSpeakEnabled: boolean; /** * The language the user speaks TO the assistant, independent of the interface * language (BOFF-7108). `null` means "follow the app locale", which is what * every user gets until they choose otherwise — and is exactly the behaviour * that existed before this preference did. */ assistantLanguage: string | null; /** * The language the assistant ANSWERS in. `null` means "match my language" — * i.e. follow `assistantLanguage` — which is the default and what shipped * before the two were separated. */ replyLanguage: string | null; /** * The voice the user picked, keyed by language code (BOFF-7110). * * Per language, because a voice only reads one: picking "Google US English" * says nothing about which Tamil voice to use. Stored by NAME because that is * the only identifier stable across page loads — voice objects are recreated * each time and `voiceURI` is not reliably unique across platforms. * * A name saved on one device will not resolve on another; that is handled by * falling back to the ranking rather than by trying to sync it. */ preferredVoices: Record; createConversation: (title?: string, mode?: AssistantMode) => string; deleteConversation: (id: string) => void; renameConversation: (id: string, title: string) => void; setActiveConversation: (id: string | null) => void; setMode: (mode: AssistantMode) => void; setConversationMode: (conversationId: string, mode: AssistantMode) => void; addMessage: (conversationId: string, message: Message) => void; updateMessage: (conversationId: string, messageId: string, content: string) => void; patchMessage: (conversationId: string, messageId: string, patch: Partial) => void; /** * MERGE into a message's metadata, leaving untouched keys alone. * * ★ The counterpart to `patchMessage`, which REPLACES `metadata` wholesale * (`{...m, ...patch}`). That has already shipped one defect: a write which * rebuilt the object dropped `voice`, so a spoken reply silently became a * typed one. Anything adding a SINGLE metadata key should use this, or it * takes `lang` and `voice` down with it. */ mergeMessageMetadata: (conversationId: string, messageId: string, patch: Partial) => void; /** * Replace a conversation's processing entry. * * `stage` is the step an in-flight turn is on (see `AssistantTurnStage`). It * lands in the SAME set as the status, so no render can pair a new status with * the previous step's copy. Omitting it clears it, which is what every settled * status wants. */ setConversationProcessing: (conversationId: string, status: AssistantConversationStatus, messageId?: string | null, error?: string | null, stage?: AssistantTurnStage | null) => void; clearConversationProcessing: (conversationId: string) => void; toggleConversationList: () => void; setConversationListOpen: (open: boolean) => void; clearConversation: (id: string) => void; setBackendSessionId: (conversationId: string, sessionId: string) => void; clearBackendSessionId: (conversationId: string) => void; setSandbox: (mode: AssistantMode, sandboxId: string | null, status: AssistantSandboxStatus) => void; clearSandbox: (mode: AssistantMode) => void; resetStore: () => void; /** Queue a prompt for the chat area to auto-send on next render. */ submitPrompt: (text: string) => void; /** Returns and clears any queued prompt. */ consumePendingPrompt: () => string | null; /** Record that the write consent for `mode` was accepted (persisted). */ acknowledgeWriteMode: (mode: AssistantMode) => void; /** Turn automatic read-aloud of new replies on or off (persisted). */ setAutoSpeak: (enabled: boolean) => void; /** Choose the language spoken to the assistant; `null` follows the app locale (persisted). */ setAssistantLanguage: (code: string | null) => void; setReplyLanguage: (code: string | null) => void; /** `null` clears the pick for that language, restoring the ranked default. */ setPreferredVoice: (languageCode: string, voiceName: string | null) => void; } /** The sandbox a request addresses, with the mode it was provisioned for. */ export interface AssistantModeSandboxTarget { sandboxId: string; mode: AssistantMode; } /** * The sandbox of the panel's CURRENT mode, paired with that mode. * * ★★ BOFF-7316: the two are returned together so a caller cannot pass one * sandbox and a different mode. The mode decides whether the user's own tokens * are forwarded (`forwardsUserTokensToSandbox`); a sandbox id taken from * anywhere else — a message's metadata, another mode's slot — would pair an Ask * mode with a Build sandbox, or the reverse, and send the tokens to a sandbox * that must never hold them. `null` when that mode has no sandbox yet. */ export declare function resolveActiveModeSandbox(state: { mode: AssistantMode; sandboxesByMode: Partial>; }): AssistantModeSandboxTarget | null; /** * Fold what storage holds into the live state — zustand's own default (a shallow * spread, persisted over current), plus the write-consent migration. * * ★★ A `merge`, not a `version` + `migrate`. Bumping the persist version would * make every OLDER build — a product rolled back to an earlier fe-libs pin — * find a version it has no `migrate` for, and zustand then hydrates NOTHING: the * person's saved conversations would be dropped on the next write. This reads * old and new shapes alike and leaves the version where it is. * * ★ The legacy flag is removed from the live state, not merely ignored, so it * can never be written back (`partialize` would not write it anyway) nor read * by mistake as a second source of truth. */ export declare function mergePersistedAssistantState(persisted: unknown, current: S): S; export declare const useAssistantStore: import('zustand').UseBoundStore, "setState" | "persist"> & { setState(partial: AssistantState | Partial | ((state: AssistantState) => AssistantState | Partial), replace?: false | undefined): unknown; setState(state: AssistantState | ((state: AssistantState) => AssistantState), replace: true): unknown; persist: { setOptions: (options: Partial>) => void; clearStorage: () => void; rehydrate: () => Promise | void; hasHydrated: () => boolean; onHydrate: (fn: (state: AssistantState) => void) => () => void; onFinishHydration: (fn: (state: AssistantState) => void) => () => void; getOptions: () => Partial>; }; }>; export {}; //# sourceMappingURL=store.d.ts.map