import type { PlatformAdapter } from '../platform/index.js'; import { type ChannelConversation, type LedgerTurn } from '../store/conversation-ledger-repo.js'; import { type Session } from '../store/session-registry-repo.js'; import * as sessionBackup from '../domain/sessions/session-backup.js'; import type { AttachmentMeta } from '../domain/ui-service/types.js'; export type RewindResult = { ok: true; } | { ok: false; reason: 'running' | 'not-found'; }; /** Injectable dependency surface (unit tests). Defaults bind the production singletons. */ export interface RewindDeps { activeAgents: { hasChannel(channel: string): boolean; }; snapshotPending(channel: string): boolean; tryAcquireMutation(channel: string): (() => void) | null; ledger: { getConversation(channel: string): Promise; rollbackTo(channel: string, turnIndex: number): Promise<{ supersededTurns: LedgerTurn[]; conversation: ChannelConversation; } | null>; truncateTurns(channel: string, fromIndex: number): Promise; }; history: { truncateFromTurn(sessionId: string, turnIndex: number): Promise<{ text: string; ts: string; attachments?: AttachmentMeta[]; } | null>; appendEditMarker(sessionId: string, opts: { originalText: string; originalTs: string; }): Promise; }; sessionStore: { getById(sessionId: string): Promise; updateSession(name: string, updates: { backendSessionId: string | null; }): Promise; }; backup: Pick; resolveBackend: (channel: string) => string; registerPISessionPath: (sessionId: string, sessionPath: string) => void; closePooledSession: (channel: string, backend: string) => void; send: (opts: { channel: string; text: string; attachments?: AttachmentMeta[]; adapter: PlatformAdapter; mutationRelease?: () => void; }) => void; publishRewound: (payload: { sessionId: string; channel: string; turnIndex: number; }) => void; } /** * Edit a previously-sent user message and rewind the conversation to it: every turn from * `turnIndex` onward is rolled back (ledger + backend session jsonl + display history), then the * edited `text` is re-sent as a genuine user turn (original attachments preserved). Fire-and-forget * on the regeneration — the new reply streams over `session.message` as usual. * * Rejected while a run is live on the channel ('running' — the UI greys the edit action out too) * and when the session/turn cannot be found ('not-found'). */ export declare function rewindWebSession(opts: { sessionId: string; channel: string; turnIndex: number; text: string; adapter: PlatformAdapter; }, deps?: RewindDeps): Promise;