import { DocxDocument, type NormalizationResult, type ParagraphRevision, type RevisionContext, type RevisionIdState } from '@usejunior/docx-core'; export type SaveFormat = 'clean' | 'tracked' | 'both'; export type TrackedChangesStats = { insertions: number; deletions: number; modifications: number; }; export type SaveCacheEntry = { cacheKey: string; revision: number; format: SaveFormat; cleanBookmarks: boolean; trackedAuthor: string; revisedBuffer: Buffer; trackedBuffer: Buffer | null; trackedStats: TrackedChangesStats | null; bookmarksRemoved: number; blocksRestored: number; exportedAtUtc: string; cachedAtIso: string; }; export type ExtractionCacheEntry = { revision: number; changes: ParagraphRevision[]; }; export type SelectiveRevisionAction = { tool: 'accept_ai_edits' | 'reject_ai_edits'; selector: 'revision_ids' | 'author'; selectedRevisionIds: string[]; editRevision: number; }; /** * A package-level (non-revision) mutation recorded during a session. * * Per #122, AI-attributed writes in the *revisionable* surface must land as * native OOXML tracked-change markup. Writes in the *package-mutation* surface * (side-story parts, relationships, content types — things OOXML has no native * revision wrapper for) cannot be tracked, so instead of being emitted silently * they are recorded here and surfaced in the save report. This keeps the * "every AI mutation is accounted for" invariant honest even where the mutation * is not, and cannot be, a tracked change. * * @see packages/docx-core/SUPPORT.md (Table B) for the ratified classification. */ export type NonRevisionChange = { /** MCP tool that produced the change (e.g. `add_comment`). */ tool: string; /** Session edit revision at which the change was recorded. */ editRevision: number; /** Package parts mutated without tracked-change markup. */ parts: string[]; /** Human-readable summary of what was mutated and why it is untracked. */ description: string; }; export type DocxSession = { provider: 'docx'; sessionId: string; filename: string; tmpPath: string; originalPath: string; originalBuffer: Buffer; /** * Post-normalization + bookmark-cleaned buffer used as comparison baseline for tracked output. * Comparing against this instead of originalBuffer prevents normalization artifacts from * appearing as false tracked changes. Lazily generated on first save/compare via ensureBaselines(). */ comparisonBaseline: Buffer | null; /** * Post-normalization buffer WITH bookmarks, used as comparison baseline for * compare_documents tool (which uses cleanBookmarks: false). * Lazily generated on first save/compare via ensureBaselines(). */ comparisonBaselineWithBookmarks: Buffer | null; doc: DocxDocument; aiAuthor: string | null; revisionIdState: RevisionIdState | null; editCount: number; editRevision: number; saveCache: Map; extractionCache: ExtractionCacheEntry | null; /** * Non-revision (package-mutation) changes recorded this session, in order. * Surfaced in the save report so package-level mutations that have no native * OOXML revision wrapper are still accounted for (#122). */ nonRevisionManifest: NonRevisionChange[]; /** * Most recent selective revision disposition in this in-memory session. * A later clean save uses this marker to prevent automatically accepting * remaining revisions that the caller deliberately left unresolved. */ selectiveRevisionAction: SelectiveRevisionAction | null; createdAt: Date; lastAccessedAt: Date; expiresAt: Date; normalizationStats: NormalizationResult | null; }; export type GDocsSession = { provider: 'gdocs'; sessionId: string; docId: string; doc: any; editCount: number; editRevision: number; createdAt: Date; lastAccessedAt: Date; expiresAt: Date; }; export type OdfSession = { provider: 'odf'; sessionId: string; filename: string; tmpPath: string; originalPath: string; originalBuffer: Buffer; archive: any; doc: any; editCount: number; editRevision: number; createdAt: Date; lastAccessedAt: Date; expiresAt: Date; }; export type Session = DocxSession | GDocsSession | OdfSession; /** * Compute a starting `RevisionIdState` whose first allocated `w:id` is * higher than any existing revision id found in the supplied documents. * * Only `w:id` attributes on revision-bearing elements * (`REVISION_ID_ELEMENT_LOCAL_NAMES`) are considered. Non-revision IDs such * as `` or `` share the attribute name but * occupy a different ID space and must not influence the counter. * * Callers should pass every available story/metadata part that can contain * package-wide revision attributes, not just `document.xml`. */ export declare function inferStartingRevisionIdState(...docs: Document[]): RevisionIdState; export declare function getSidePartRevisionSeedDocs(buffer: Buffer): Promise; export declare function getRevisionContextForSession(session: DocxSession): Promise; export declare function isDocxSession(s: Session): s is DocxSession; export declare function isGDocsSession(s: Session): s is GDocsSession; export declare function isOdfSession(s: Session): s is OdfSession; export declare class SessionManager { /** Sessions keyed by canonical file path (realpath). */ private sessions; private ttlMs; private defaultAiAuthor; /** Concurrency guard: prevents double-generation of baselines for the same session. */ private baselinePromises; constructor(opts?: { ttlMs?: number; defaultAiAuthor?: string | null; }); private expandPath; normalizePath(inputPath: string): string; /** Canonicalize path using realpath (resolves symlinks, case). */ canonicalizePath(inputPath: string): Promise; private newSessionId; createSession(documentContent: Buffer, filename: string, originalPath: string): Promise; /** * Create an ODF session from an already-loaded `archive` + `doc`. The caller (the * lazily-reached ODF resolver / open path) loads these via the optional odf-core * provider — this method does NOT import odf-core, keeping the always-loaded * SessionManager free of a hard dependency on the private package. */ createOdfSession(documentContent: Buffer, filename: string, originalPath: string, archive: any, doc: any): Promise; /** * Finalize a newly created session by normalizing the document and inserting * paragraph bookmarks. Baselines are lazily generated on first save/compare. * * INVARIANT: All production session creation paths must call * `finalizeNewSession` before returning a session. `createSession` alone * leaves baselines null and is incomplete for tool use. */ finalizeNewSession(session: DocxSession, opts?: { skipNormalization?: boolean; }): Promise<{ normalizationStats: NormalizationResult | null; paragraphCount: number; }>; /** * Lazily generate comparison baselines from the immutable originalBuffer. * Safe to call multiple times — returns immediately if baselines already exist. * Uses a concurrency guard to prevent double-generation from parallel calls. */ ensureBaselines(session: DocxSession): Promise; private _generateBaselines; /** Get session by file path (auto-canonicalizes). */ getSessionByFilePath(filePath: string): Promise; /** Get session by canonical file path. Returns null if not found or expired. */ getSessionByPath(canonicalPath: string): Session | null; /** * @deprecated Use getSessionByPath instead. Kept only for backward compatibility during migration. */ getSession(sessionId: string): Session; private cleanupSessionArtifacts; clearSessionByPath(filePath: string): Promise; clearAllSessions(): Promise; touch(session: Session): void; markEdited(session: Session): void; /** * Record a package-level (non-revision) mutation for later surfacing in the * save report (#122). Call this after a successful mutation whose effect is * not, and cannot be, captured by OOXML tracked-change markup — e.g. creating * `word/comments.xml`, rewriting relationships, or editing side-story parts. */ recordNonRevisionChange(session: DocxSession, change: Omit): void; recordSelectiveRevisionAction(session: DocxSession, action: Omit): void; getSaveCache(session: DocxSession, cacheKey: string): SaveCacheEntry | null; setSaveCache(session: DocxSession, entry: SaveCacheEntry): void; getExtractionCache(session: DocxSession): ExtractionCacheEntry | null; setExtractionCache(session: DocxSession, changes: ParagraphRevision[]): void; /** * Create a Google Docs session. The `doc` is an already-loaded * GoogleDocsDocument instance (created via dynamic import in the handler layer). */ createGDocsSession(docId: string, doc: any): GDocsSession; saveTo(session: DocxSession, savePath: string, opts?: { cleanBookmarks?: boolean; }): Promise; saveOdfTo(session: OdfSession, savePath: string): Promise; } //# sourceMappingURL=manager.d.ts.map