/** * Soul — local .fafm model (TS mirror of claude-fafm-sdk 1.0 Soul). * INTEROP: load/save fidelity, residual preserve, recall SoT. * * A soul loaded from a file keeps that file's text. `save` writes only what * changed since the load (or the last save) into it — through the YAML * Document, so comments, key order, quoting, the `version`, a missing * `profile`, a hand-kept `index` and every unknown key stay as they were. * Known keys faf does not model in the shape the file has them (a `facts` or * `index` written as a mapping, `sessions` as a mapping, …) are kept verbatim; * a change that would have to rewrite one is refused, with nothing written. */ import { type Fact, type SoulDoc } from './types.js'; export declare function utcNow(): string; export declare function canonicalPriority(p: string | null | undefined): string; export declare function factFromObj(obj: unknown): Fact; export declare function factToObj(f: Fact): unknown; /** Filters for {@link Soul.recall}. */ export interface RecallOptions { tags?: string[]; type?: string; minPriority?: string; limit?: number | null; } export declare class Soul { namepoint: string; profile: string; retention: string; created: string; last_etched: string; private _facts; private _byId; private _index; private _sessions; private _preferences; private _custom; private _extra; private _memoryExtra; private _version; /** The file the soul was loaded from or last saved to. Undefined while the * soul was made with no file (in memory, not saved yet): its first save * then refuses a file already at the path — one that appeared since the * caller found none — unless `replace`. */ private _origin; /** Each loaded fact's item position in the file's memory.facts. */ private _factAt; /** The index was faf-derived when the soul was loaded (or made, or last * saved) — the default save keeps it in step with the facts. */ private _indexDerived; /** The index as it was then: an index changed in memory since is the * caller's, and the default save leaves it as it is. */ private _indexAtRecord; constructor(namepoint: string, opts?: { profile?: string; facts?: Fact[]; retention?: string; created?: string | null; index?: string[]; sessions?: unknown[]; preferences?: Record; custom?: Record; extra?: Record; memoryExtra?: Record; }); get facts(): Fact[]; get index(): string[]; get sessions(): unknown[]; get preferences(): Record; get custom(): Record; get extra(): Record; get memoryExtra(): Record; /** * Load a soul. The root must be a mapping (anything else is refused). Known * keys in a shape faf does not model — `memory.facts` or `index` written as * a mapping, `memory.sessions` as a mapping, `memory.preferences` as a list, * `memory` itself as a list — load as empty and stay in the file verbatim; * so do list items in `memory.facts` that are not facts. */ static load(path: string): Soul; private static fromText; /** Note whether the index is faf-derived now (at load, and after a save). */ private recordIndex; /** Take a fact read from item `i` of the file's memory.facts. */ private adopt; static fromFile(path: string): Soul; toDoc(): SoulDoc; toYaml(): string; /** The index faf derives from the facts (INTEROP §5 formula, * `${id ?? '?'} — ${text[:width]}` per fact) — without changing the soul. */ derivedIndex(width?: number): string[]; /** True when the stored index is exactly the one faf derives from the facts * (so faf wrote it, and may rewrite it). A hand-kept index is not. */ indexIsDerived(width?: number): boolean; rebuildIndex(width?: number): string[]; /** * Write the soul — atomically (a failure leaves the original as it was) * and never through a link that leaves the folder or dangles. * * A soul loaded from a file is written as that file's text with only what * changed since the load (or the last save) edited into it; a save that * changes nothing writes nothing. * * The index: `reindex: true` rebuilds it from the facts, `reindex: false` * keeps it as it is. Left out, the index is rebuilt only when it was * faf-derived — {@link indexIsDerived} when the soul was loaded or last * saved; a new soul counts as derived unless it was given an index of its * own — and has not been changed in memory since. A hand-kept index is * never touched. * Refused, with nothing written: a change to a known key the file holds in * a shape faf does not model (adding a fact to `memory.facts` written as a * mapping, say); a save over the file the soul was loaded from (or last * saved to) when that file changed on disk since — SafePathError `changed`, * so an edit made meanwhile is never written over; and a save of a soul * made with no file (or to a path it was not loaded from) when a file is * already there — the same `changed` refusal, so a soul.fafm that appeared * after the caller found none is kept. `replace: true` is the explicit * overwrite of such a file (`faf memory convert --force`). */ save(path: string, opts?: { reindex?: boolean; replace?: boolean; }): string; toFile(path: string, opts?: { reindex?: boolean; replace?: boolean; }): string; /** After a save: the written text (at `real`) is the new origin. */ private settle; /** The soul's modelled state as plain values. */ private state; /** Refuse a change that would rewrite a key kept verbatim. */ private static refuse; /** Edit the loaded Document with what changed since `origin.base`; returns * each fact's item position in memory.facts afterwards. */ private applyTo; private applyMemory; /** Which facts are still at their place in the file, and which are new. * Kept facts must be in the file's order, new ones after them. */ private sortFacts; /** Facts: changed facts are edited where they are (only the fields that * changed), deleted ones removed, new ones appended. List items that are * not facts are never touched. */ private applyFacts; /** Insert or overwrite a Fact by id, preserving every field it carries (its * timestamp included) — the INTEROP merge primitive: on an existing id the * whole Fact is replaced. `etch` merges instead. */ add(fact: Fact): Fact; /** * Write a fact. With an `id` that is already in the soul, the fact is * updated in place: the text and timestamp are new, and of the other fields * only those passed here change — links, source, tags, type, priority and * any extra fields the fact carries are kept (a priority is never lowered * unless one is passed). Otherwise the fact is appended. */ etch(text: string, opts?: { id?: string; type?: string; priority?: string; tags?: string[]; links?: string[]; source?: string; }): Fact; /** * Deterministic recall (INTEROP §6). Call it as `recall(query, filters)` or * `recall({ query, ...filters })`. Bare-string facts are facts: their text * is matched and returned like any other. */ recall(query?: string | null | (RecallOptions & { query?: string | null; }), opts?: RecallOptions): Fact[]; getFact(id: string): Fact | null; deleteFact(id: string): boolean; }