/** * Server side of the shared ledger. Two jobs the `shadok-ledger` skill's own * core (context/ledger-skill/ledger-core.mjs) does NOT do: * * 1. Locate the PER-INSTANCE ledger file. The skill writes it, the server reads * it and hands agents its path; both key it by the launch dir, like channels * and crons. (The skill runs inside an agent whose cwd is a worktree, so it * cannot derive the launch dir itself — the server passes SHADOK_LEDGER_FILE * at spawn.) * 2. Build the DELTA block pushed ahead of each human prompt: the rows changed * since this agent last saw the ledger, so sibling agents learn what was * resolved/decided in near-real-time without having to `check`. The block is * prepended to the submitted text (like the ⟦platform⟧ prompt-meta header) * and stripped from the display on replay — the agent sees it, the chat * doesn't. Because a delta is usually empty, most prompts carry nothing. */ export interface LedgerRow { id?: string; entity: string; status: string; note?: string; source?: string; updatedAt?: number; } /** The per-instance ledger file, keyed by the launch dir (same encoding as * channels/crons). Distinct launch dirs → distinct ledgers. */ export declare function ledgerFileFor(cwd: string): string; /** The legacy single-file ledger, before it was scoped per instance. */ export declare function legacyLedgerFile(): string; /** A short id: 4 hex chars, not already taken. The ledger is tiny per instance, * so 16 bits is ample; a durable, quotable handle for update-by-id. */ export declare function mintLedgerId(taken: ReadonlySet): string; /** * Make sure THIS instance has its own ledger file, and that every row carries an * id (the durable handle). Called at boot. Seeds the scoped file from the legacy * single-file ledger the first time (so nothing already recorded is lost), then * backfills any id-less rows. Idempotent and best-effort — a failure here must * never break the boot. */ export declare function ensureLedgerFile(cwd: string): void; /** Read the rows of a ledger file (empty on any error). */ export declare function loadLedger(file: string): LedgerRow[]; /** The rows changed since `watermark`, newest-first, capped at `cap`. `total` is * the full count of changed rows (may exceed `rows.length` when capped). */ export declare function deltaSince(rows: LedgerRow[], watermark: number, cap: number): { rows: LedgerRow[]; total: number; }; /** The delta block the agent sees ahead of a human prompt. English (it is * written into the transcript — repo side). One line per row: `• [id] entity — * status (source)`; an overflow line when capped. */ export declare function formatLedgerBlock(rows: LedgerRow[], total: number): string; /** Prepend the block as its own leading lines. Idempotent. */ export declare function markLedgerBlock(text: string, block: string): string; /** Does this text open with a pushed ledger block? */ export declare function hasLedgerBlock(text: string): boolean; /** Remove a leading ledger block — its header line and the contiguous `• ` bullet * lines that follow it — leaving whatever came after (e.g. the ⟦platform⟧ header * and the user's message). A plain message is returned untouched. */ export declare function stripLedgerBlock(text: string): string; /** Where this instance stores its agents' ledger watermarks. Beside the table, * same launch-dir encoding, distinct name — writing one must never clobber the * other. */ export declare function ledgerSeenFileFor(cwd: string): string; /** * The moment this agent last saw the ledger, or `undefined` if it never has. * The two are DIFFERENT answers and the caller acts on the difference: no * record means a fresh agent, which anchors to now rather than replaying the * whole table (0 would do exactly that). */ export declare function seenFor(file: string, sessionId: string): number | undefined; /** * Keep the map bounded: drop agents that no longer exist (`keep` is the live * channel list), never drop the one being written — a spawn can race its own * channel upsert, and losing that entry would re-anchor it to now — and cap the * rest newest-first as a backstop when there is no list to compare against. * Pure. */ export declare function pruneSeen(map: Record, keep: ReadonlySet | undefined, current: string, cap: number): Record; /** * Write this agent's watermark. Best-effort and atomic: a lost write costs a * duplicated block on the next prompt, never a swallowed one, so it must never * be able to break a turn. */ export declare function recordSeen(file: string, sessionId: string, at: number, keep?: ReadonlySet): void;