/** * Annotation state on disk + composed agent message. No pi imports — pure node, * so `smoke.mjs` can drive it directly against `dist/`. */ export interface TextQuoteAnchor { exact: string; prefix?: string; suffix?: string; } export interface Annotation { id: string; quote?: TextQuoteAnchor; element?: { selector: string; label: string; }; intent?: 'comment' | 'keep' | 'question' | 'decision'; decisionId?: string; decisionValues?: string[]; comment: string; createdAt: string; sentAt?: string; reply?: string; } /** Validate persisted and browser-provided records before they enter the review flow. */ export declare function validAnnotation(value: unknown): value is Annotation; /** Read the annotation list for a slug; [] when missing or slug is unsafe. */ export declare function readAnnotations(slug: string): Annotation[]; /** Replace the annotation list for a slug. Throws (surfaced as 500) on write failure. */ export declare function writeAnnotations(slug: string, list: Annotation[]): void; /** Revision tokens prevent an old browser tab from overwriting a newer review. */ export declare function annotationState(slug: string): { annotations: Annotation[]; revision: string; }; /** Add an answer to a sent question without touching the artifact itself. */ export declare function answerQuestion(slug: string, id: string, reply: string): void; /** Remove a sidecar explicitly. No-op if absent. */ export declare function deleteAnnotations(slug: string): void; /** * Whitespace-normalized visible text of the current artifact, for anchoring * checks. Strips comments/doctype/script/style and all tags, decodes the 5 basic * entities. Naive by design — this checks quote presence, not structure. */ export declare function artifactText(slug: string): string | null; /** * Stale = the quote is not findable in the current artifact text. When `exact` * occurs more than once, prefix/suffix (when provided) must match around at * least one occurrence for the anchor to count as found. */ export declare function isStale(ann: Annotation, text: string): boolean; /** * Baked share render: the artifact with its annotations embedded and the layer * in static (read-only) mode. Null when there's nothing to bake — callers fall * back to the clean stored file. */ export declare function bakeAnnotations(slug: string): { html: string; count: number; } | null; export interface ShareResult { /** comments included/visible in the share (0 = none) */ count: number; /** gist only: the created URL (also copied to the system clipboard) */ url?: string; /** copy only: bytes placed on the clipboard */ bytes?: number; /** image/pdf only: the written file */ path?: string; /** image/pdf only: whether the file landed on the clipboard */ copied?: boolean; } /** * Share an artifact, baking comments in when present (unless bake: false). * `copy` puts the self-contained HTML on the system clipboard; `gist` uploads * via `gh gist create` from a temp file (the stored artifact stays clean). * Shared by the `artifact` tool and the in-page Share button (POST /api/share). */ export declare function shareBaked(slug: string, title: string, method: 'copy' | 'gist' | 'image' | 'pdf', opts?: { public?: boolean; bake?: boolean; baseUrl?: string; width?: number; height?: number; }): Promise; /** * 1-based line of the quote in .md, or undefined (no mirror / not found). * Plain normalized substring search; quotes crossing markdown formatting (**) * simply return undefined and the ref is omitted. */ export declare function sourceLine(ann: Annotation, slug: string): number | undefined; /** * Compose the markdown feedback message from the sidecar annotations plus the * server-computed stale flags and source-line refs. */ export declare function composeFeedback(slug: string, url: string, anns: Annotation[], staleFlags: boolean[], lines: (number | undefined)[]): string;