import { AssistantConversationStatus } from '../conversation'; import { AssistantPreviewEvent, AssistantPreviewRun } from './types'; import { AssistantToolStep } from '../ui/AssistantToolActivity'; /** * ★ Exported so the bridge can ask "is this live?" without naming any status. * `previewInvariants` forbids status literals in the bridge precisely so there * is one vocabulary for what counts as running, and this keeps that true. */ export declare function isRunningStatus(status: AssistantConversationStatus): boolean; export interface PreviewBridgeState { /** * ★★ The conversation whose baseline has been established. * * It exists to tell two indistinguishable-looking situations apart. Mounting * onto a conversation whose LAST turn already settled must NOT replay that * turn — the entry commonly still holds a `complete` status long after the * answer was read. But a turn that fails SYNCHRONOUSLY is the same shape: the * no-workspace path throws inside the same block as the `provisioning` write * (AssistantChatArea.tsx:1024 then :1492), React batches them, and the bridge * sees only `error`. Requiring a prior `start` suppressed every event for * that whole class of failure — the user saw an error in the transcript and * the preview showed nothing at all. * * ★★ PER CONVERSATION, not a single flag. Terminal processing entries * deliberately persist, so with one global flag, selecting a History row * whose entry still held `complete` re-announced that OLD settled turn and * replaced the product's preview with stale output — the very replay this * guard exists to prevent, reached by switching conversation instead of by * mounting. The first observation of EACH conversation is a baseline. */ readonly observedConversationId: string | null; /** * The run announced with `start` and not yet finished. * * ★ Clearing this IS the idempotence mechanism — a settled run is no longer * anybody's started run, so no second `end` can be derived for it. An earlier * draft also carried an `endedRunId`; a mutation test showed nothing could * distinguish its presence from its absence, because this null already did * the work. Two mechanisms for one guarantee is how they drift apart. */ readonly startedRun: AssistantPreviewRun | null; /** * ★★ The last run finished. NOT redundant with `startedRun` being null: a * settled status can be written more than once, and since a terminal run * first seen already-terminal now gets a lifecycle, "no started run" alone * cannot tell a REPEATED `complete` from a genuinely new failed run. Without * this the second `complete` tick re-announced the same run and ended it * twice. * * (An earlier revision carried this field, found nothing could distinguish * its presence from its absence, and removed it. That was correct then and is * wrong now — the terminal-first case is what gives it work to do.) */ readonly lastEndedRunId: string | null; /** The last content emitted, so an unchanged tick is not re-emitted. */ readonly lastSnapshot: string | null; } export declare const IDLE_PREVIEW_STATE: PreviewBridgeState; export interface PreviewInput { /** * The turn the store currently describes, or null when the entry is gone. * ★ null is NOT "nothing to do" — it is how an interrupted run ends. */ readonly run: AssistantPreviewRun | null; /** * ★★ The ACTIVE conversation, independent of whether it has a run. * * The baseline used to be derivable only from a run — so a panel opened on an * IDLE conversation recorded nothing, and when that conversation's first turn * then failed synchronously the terminal render was still "unbaselined" and * emitted no events at all. The very case the baseline exists to permit, * defeated by the baseline needing a run to establish itself. */ readonly conversationId: string | null; readonly status: AssistantConversationStatus; readonly content: string | null; /** * ★ The tool activity for this message, already parsed. Passed IN rather than * derived here so this function stays pure and testable without the parser. */ readonly steps: readonly AssistantToolStep[]; readonly error?: string | null; /** * Whether prose is not model output, injected so this stays pure. The bridge * passes a blank check: since BOFF-7277 a live turn's content is output or * nothing. Kept as a parameter so the rule is still applied in exactly one * place and swept by the exhaustive tests. */ readonly isFiller: (content: string) => boolean; } /** * The filler predicate the bridge injects: blank prose is not model output. * * ★ Since BOFF-7277 a live turn's content is the model's output or nothing, so * blank is the ONLY thing that is not an answer. Exported so the bridge and the * release-blocking lifecycle test use the same function rather than two copies * of it. */ export declare function isBlankProse(value: string): boolean; export declare function nextPreviewEvents(state: PreviewBridgeState, input: PreviewInput): { events: AssistantPreviewEvent[]; state: PreviewBridgeState; }; //# sourceMappingURL=previewTransitions.d.ts.map