import { type MemorySyncCursor } from "./sync.js"; import type { MemoryBackend, MemoryEntry, PatchReport } from "./types.js"; /** The injected HTTP seam — core never bundles a fetch. The deployment maps `path` (e.g. * `/v1/memory/sync/user%3Aalice`) onto its server base URL, attaches auth, and returns the * DECODED JSON body of a 2xx response; any non-2xx / network failure should THROW (the round then * aborts with zero local side effects — the transport runs before any local write). */ export type MemorySyncTransport = (path: string, body: unknown) => Promise; /** The wire REQUEST of one round (the server's `parseMemorySyncRequest` twin — [671]①): the push * half as computed against the client's own cursor. `deletes` ⊆ `Object.keys(baseRevs)` by * construction (a delete is CAS'd on its baseline; an id never synced cannot be delete-propagated). */ export interface MemorySyncRequestBody { peer: string; /** id → rev at the last completed round (the cursor's baseRevs; empty = first round). */ baseRevs: Record; /** Full state of every entry new/changed here since the baseline. */ entries: MemoryEntry[]; /** Ids deleted here since the baseline (tombstoned or gone from the local plane). */ deletes: string[]; } /** The wire RESPONSE of one round (server `MemorySyncResponse` twin — [671]①: one RTT, both * directions; `serverDeletes` is the delete-propagation leg [codex B4 — without it a central * removal never crosses = livelock]; `cursor` is the server-computed next baseline with the * retention discipline ALREADY applied to the pull half). */ export interface MemorySyncResponseBody { /** Ops the server actually landed from our push (empty on an idempotent replay). */ applied: PatchReport["applied"]; /** Server-side rejections/divergences: inbound-gate rejects, rev lies, CAS losses, both-sides * divergence (resolve via the §3 ladder — loser minted as a sibling, never dropped). */ conflicts: Array<{ id: string; reason: string; baseRev?: string; currentRev?: string; }>; /** The pull half: entries new/changed centrally since our baseline. */ serverEntries: MemoryEntry[]; /** The pull half's delete leg: centrally deleted, unchanged here since baseline (CAS baseRev). */ serverDeletes: Array<{ id: string; baseRev: string; }>; /** The next baseline to persist AFTER the pull half lands (the client cannot compute it — it * does not hold the central set). Pull-half ids are retained at the OLD baseline inside; the * client advances the LANDED ones to their landed state before persisting (see ⑤ — otherwise * every landed pull echoes back next round and a central delete can be resurrected). */ cursor: { peer: string; baseRevs: Record; updatedAtMs: number; }; } /** One aggregated conflict: `server` = reported by the central half (its gate/CAS/divergence), * `local` = produced landing the pull half here (our CAS refusals, inbound-gate rejects, rev * lies). Nothing is hidden; the caller resolves via the §3 ladder (loser-as-sibling). */ export interface MemorySyncClientConflict { side: "server" | "local"; id: string; reason: string; baseRev?: string; currentRev?: string; } export interface SyncMemoryScopeOptions { /** The LOCAL authority plane (TOC: the File backend). */ backend: MemoryBackend; /** The scope this round syncs (one round = one scope, mirroring the server route). */ scope: string; /** The injected HTTP seam. */ transport: MemorySyncTransport; /** The persisted cursor from the last completed round with this peer; absent = first round. */ cursor?: MemorySyncCursor; /** Peer name for the cursor partition (defaults to the cursor's peer, else "central"). */ peer?: string; /** Caller-injected clock (engine precedent) — stamps the returned cursor's updatedAtMs. */ now?: () => number; } export interface MemorySyncClientResult { /** True ⇔ the round was fully clean: cursor advanced AND zero conflicts on either side. */ ok: boolean; /** What the push half contained (ids only — the bodies already went over the wire). */ pushed: { entries: string[]; deletes: string[]; }; /** Ops the server landed from our push (response `applied` verbatim). */ serverApplied: PatchReport["applied"]; /** Ops we landed locally from the pull half. */ localApplied: PatchReport["applied"]; /** Aggregated server-reported + local conflicts (never hidden). */ conflicts: MemorySyncClientConflict[]; /** True ⇔ every pull-half item landed cleanly, so `cursor` is the NEW baseline to persist. */ cursorAdvanced: boolean; /** The cursor the caller should now persist: advanced on full success, otherwise the INPUT * cursor unchanged (undefined on a failed first round) — the retention discipline. */ cursor: MemorySyncCursor | undefined; } /** * One full CLIENT sync round for one (scope, peer) against the [671]① central sync API — see the * module header for the ①-⑤ flow and the cursor retention discipline. Pure orchestration: all I/O * rides the injected backend/transport; the caller persists the returned cursor. */ export declare function syncMemoryScope(opts: SyncMemoryScopeOptions): Promise; //# sourceMappingURL=sync-client.d.ts.map