import type { MemoryId } from '../../types/ids/index.js'; import type { CreateMemoryParams, MemoryContent, MemoryIndexEntry, MemoryRecord, MemorySearchParams, MemorySearchResult, MemoryStore, MemoryType, UpdateMemoryParams } from '../../types/memory/index.js'; import { type Logger } from '../../utils/logger.js'; import { type RenderedMemoryIndex } from './index-file.js'; /** The generated index. Never a memory, never read back as one. */ export declare const MEMORY_INDEX_FILE = "MEMORY.md"; /** Largest memory file this store will read. A memory is a paragraph, not a document. */ export declare const MEMORY_FILE_MAX_BYTES: number; export interface MarkdownMemoryStoreConfig { /** * The exact directory holding one `.md` per memory and the generated * `MEMORY.md`. Created private (0700) when absent. The host owns isolation: * bind it to the project or tenant the memories belong to. */ readonly directory: string; readonly logger?: Logger; /** Maximum wait for another process's operation; default 10 seconds. Never breaks stale locks. */ readonly lockTimeoutMs?: number; } /** What {@link MarkdownMemoryStore.importRecord} did with one record. */ export type MemoryImportOutcome = 'imported' | 'present'; /** * One Markdown file per memory, in a directory the operator can read, grep * and edit by hand, with a generated `MEMORY.md` index beside them. * * Implements the same {@link MemoryStore} contract as `DiskMemoryStore` and * keeps its guarantees: every operation — reads included — runs under the * directory's exclusive operation lock and reloads what is on disk, so no * process acts on another's stale snapshot; writes are atomic renames of * private (0600) files; a file that does not parse, a name that does not match * its file, a symlink, two files claiming one id with the same `updatedAt`, or * a record stamped by a newer build is refused with the file named, never * skipped. Two files claiming one id are what an interrupted rename leaves * when both state their own, different `updatedAt`, or when one is a * hand-written file with neither an `id` nor an `updatedAt` — its id derived * from its name, its time its mtime — and the other states that id and a * different `updatedAt`: only a rename of the hand-written file writes its * derived id under another name. The newer is read, and the next write moves * the older aside to `.md.superseded`. Any other pair — an undated file * that states its id, or equal times — is refused like a copy. Content the loader * would refuse — a NUL character in the body, title, summary, description or * tags, a file over {@link MEMORY_FILE_MAX_BYTES} — is refused with * {@link MemoryContentRejectedError} before it is written. A store that * quietly dropped the one file it could not read would present the model an * incomplete memory as a complete one. * * Names are unique. {@link create} refuses an explicit name another memory * holds with {@link MemoryNameConflictError}, and derives a free one from the * title when none is given. */ export declare class MarkdownMemoryStore implements MemoryStore { private readonly directory; private readonly log; private readonly lockTimeoutMs; private canonical?; constructor(config: MarkdownMemoryStoreConfig); /** The directory this store reads and writes, as configured. */ get path(): string; private location; private memoryPath; private load; private withLoaded; /** {@link withLoaded} for an operation that writes. */ private mutate; private setAside; private writeMemory; /** Rewrite `MEMORY.md` from `records` when its text would change. */ private writeIndex; private entries; /** Every loaded record, with `changed` put in place of (or beside) its id's. */ private records; create(params: CreateMemoryParams): Promise<{ entry: MemoryIndexEntry; content: MemoryContent; }>; get(id: MemoryId): Promise; getRecord(id: MemoryId): Promise; /** The current record held under `name`, archived included, or `undefined`. */ getByName(name: string): Promise; update(id: MemoryId, updates: UpdateMemoryParams): Promise; delete(id: MemoryId): Promise; list(params?: MemorySearchParams): Promise; /** * The index a prompt carries: one line per active memory someone chose to * keep — never a record the runtime derived from a turn — operator * `feedback` and `user` memories first, capped at `maxLines` (default 200) * with a note pointing to search for the rest. Rendered from the memory * files under the lock, so it is current even when a file was edited by * hand since the last write. See {@link renderMemoryIndex}. `derived: true` * lists the records the runtime derived instead, newest first — for an * operator's inspection, never for a prompt. */ readIndex(options?: { readonly maxLines?: number; readonly derived?: boolean; }): Promise; /** * Bring in a record from another store, keeping its id, timestamps, * status and metadata. Idempotent by id: a record already here is left * alone and reported `present`, so a migration interrupted halfway can * simply run again. A name another memory holds is suffixed rather than * refused, because an import that stopped on a clash would strand the * records after it. */ importRecord(record: MemoryRecord, defaults?: { readonly type?: MemoryType; }): Promise; } //# sourceMappingURL=markdown.d.ts.map