import type { EventBus } from '../kernel/events.js'; /** * L2-B: AnnotationsStore — sidecar storage for collaboration annotations * (Phase 2 of idea #13 from IDEAS.md). * * Why a sidecar file, not the session JSONL? * * The session log is an event-sourced append-only journal * (`packages/core/src/types/session.ts` invariant: events are * append-only, with `truncateToCheckpoint` only as an explicit * rewind). Mixing in human-typed annotations would break that * invariant — the user's note about "this rm looks dangerous" * is not part of the agent's event history; it is meta-commentary * on the history. * * So we keep annotations in a sibling file: one JSON document per * session, resolved with `sessionScopedPath(sessionDir, sessionId, * '.annotations.json')`. The shape is a simple versioned array, * written atomically. * * Concurrency model: * * The store uses a per-session Promise chain to serialize writes. * Multiple annotators adding notes at the same time will queue, * not race. The atomic write itself is the second line of * defense (in case the chain is bypassed — e.g. two processes * pointing at the same dir). */ /** Wire/storage shape for one annotation. */ export interface Annotation { /** Stable id (UUIDv4-ish). Referenced by resolve/delete. */ id: string; /** Session this annotation belongs to. */ sessionId: string; /** Index into the session event log the annotation refers to. */ atEventIndex: number; /** Participant id of the annotator (matches WSCollabParticipantJoined.participantId). */ authorId: string; /** Human-readable role label snapshot for display (e.g. "annotator"). */ authorRole: 'annotator'; /** The note itself. Trimmed, capped at `MAX_TEXT_LENGTH` on add. */ text: string; /** ISO timestamp of creation. */ createdAt: string; /** Resolved state. Annotations start unresolved. */ resolved: boolean; /** ISO timestamp when resolved (if resolved). */ resolvedAt?: string | undefined; /** Participant id of the resolver (if resolved). */ resolvedBy?: string | undefined; } export interface AnnotationsStoreOptions { /** Root sessions directory used with `sessionScopedPath(..., '.annotations.json')`. */ dir: string; events?: EventBus; traceId?: string; /** * Override the max annotations per session (default 1000). * Exposed for tests to avoid 1001 sequential disk writes on every eviction test. */ maxAnnotations?: number; } export declare class AnnotationsStore { private readonly dir; private readonly events; private readonly traceId; private readonly maxAnnotations; /** Per-session write queue. Created lazily on first add. */ private readonly writeChains; constructor(opts: AnnotationsStoreOptions); /** * Return all annotations for `sessionId` in insertion order * (oldest first). Returns an empty array when no file exists * yet (the normal case for a fresh session) and also degrades * gracefully to `[]` on a read error (permissions, corruption) — * the failure is still surfaced via a `storage.read` event so it * never silently hides I/O problems from observers. */ list(sessionId: string): Promise; /** * Convenience: only unresolved annotations, newest first — the * common UI rendering for "what still needs attention?". */ listOpen(sessionId: string): Promise; /** * Add a new annotation. Returns the persisted record (with id * and timestamps filled in). Throws when `text` is empty or * exceeds `MAX_TEXT_LENGTH`. */ add(input: { sessionId: string; atEventIndex: number; authorId: string; text: string; }): Promise; /** * Mark an annotation as resolved. Returns the updated record, or * `null` if no annotation with that id exists in this session. * Idempotent: resolving an already-resolved annotation refreshes * `resolvedAt` / `resolvedBy` to the latest call. */ resolve(input: { sessionId: string; annotationId: string; resolvedBy: string; }): Promise; private filePath; private readFile; private writeFile; /** * Serialize writes per-sessionId. We chain promises instead of * using a Mutex class so the contract is obvious from the * call-site: `enqueue(sid, fn)` runs `fn` after every prior * enqueue for `sid` has settled. */ private enqueue; } //# sourceMappingURL=annotations-store.d.ts.map