/** * writeSnapshot — write-side stage for Causal memory. * * Captures the current run's `(query, finalContent)` pair from * `scope.newMessages` (populated by the Agent's PrepareFinal stage), * embeds the query for retrieval, and persists a `SnapshotEntry` to * the store. Future turns can match new questions against past * queries via cosine similarity to replay decision evidence. * * Reads from scope: `identity`, `turnNumber`, `newMessages` * Writes to store: one `SnapshotEntry` per call, id = `snap-{turn}` * * Why per-turn (not per-iteration)? * Causal memory captures TURN outcomes — "user asked X, agent said Y." * Mid-iteration state isn't useful for cross-run replay. * * Turn derivation — collisions are impossible by construction: * The effective turn is `max(scope.turnNumber, maxStoredTurn + 1)` where * `maxStoredTurn` is the highest `snap-{n}` already live in THIS * conversation's namespace (`identityNamespace(identity)` — the durable * conversation anchor across `run()` calls, Agent instances, and * processes). Rationale: * - Hosts that track `turnNumber` correctly keep their numbering * (`turnNumber: 5` → `snap-5`, gaps preserved). * - Hosts with a stale counter (the Agent seeds `turnNumber = 1` on * every run) still get a fresh, ordered id — turn 2 of the same * conversation lands `snap-2` instead of silently replacing * `snap-1`. * Causal snapshots are decision evidence (audit/replay data): when * "stale counter" and "deliberate same-turn rewrite" are * indistinguishable, never destroying a prior turn's evidence wins. * TTL-expired snapshots are ignored by the scan (same as every read). * * Empty-newMessages handling: * When `newMessages` is empty (no final answer produced — e.g. * pause-resume mid-flight), the stage no-ops. Re-runs after resume * capture the snapshot then. * * @see ./types.ts for the SnapshotEntry shape this writes * @see ./loadSnapshot.ts for the read-side counterpart */ import type { TypedScope } from 'footprintjs'; import type { MemoryStore } from '../store/index.js'; import type { Embedder } from '../embedding/index.js'; import type { MemoryState } from '../stages/index.js'; export interface WriteSnapshotConfig { /** The store to persist the snapshot to. */ readonly store: MemoryStore; /** * Embedder used to vectorize the query for later cosine-search. * Required — Causal memory's value comes from semantic retrieval * across past runs. */ readonly embedder: Embedder; /** * Stable id for the embedder. Stored on the entry so a later * embedder swap doesn't cross-pollute similarity scores. * Default: `'unknown-embedder'` — pass an explicit id when you * may swap embedder instances over time. */ readonly embedderId?: string; /** * TTL in milliseconds — drop snapshots after this duration. Useful * for compliance ("delete causal trace after 30 days"). */ readonly ttlMs?: number; /** * Tier to tag the snapshot with — typical: `'hot'` for current, * `'warm'`/`'cold'` for archived. Read stages can filter by tier. */ readonly tier?: 'hot' | 'warm' | 'cold'; } export declare function writeSnapshot(config: WriteSnapshotConfig): (scope: TypedScope) => Promise; //# sourceMappingURL=writeSnapshot.d.ts.map