/** * Binding store — v2.16.0 multi-Claude-instance sentinel layer. * * Pre-v2.16.0 binding schema was a single-session record: * { sessionId, createdAt, projectRoot } * * v2.16.0 schema is a multi-instance registry with conflict-detection * sentinel fields (ownerHint, pid, lastHeartbeat, instances Map). * * The store keeps a one-shot migration from the legacy schema: * legacy single-session binding → v2.16.0 instances Map with one entry, * ownerHint sourced from env (PEAKS_OUTER_SESSION_ID fallback * CLAUDE_CODE_SESSION_ID fallback 'unknown'). * * Each write produces an atomic writeFileSync — partial writes from * crashed processes are acceptable because re-read will re-validate * with the zod schema and either re-migrate or rebuild empty. */ import { z } from 'zod'; export declare const InstanceRecordSchema: z.ZodObject<{ startedAt: z.ZodString; roles: z.ZodDefault>; callerId: z.ZodString; lastHeartbeat: z.ZodString; }, z.core.$strip>; export declare const BindingSchema: z.ZodObject<{ ownerHint: z.ZodString; pid: z.ZodNumber; lastHeartbeat: z.ZodString; scope: z.ZodString; instances: z.ZodRecord>; callerId: z.ZodString; lastHeartbeat: z.ZodString; }, z.core.$strip>>; }, z.core.$strip>; export type InstanceRecord = z.infer; export type Binding = z.infer; /** * Read the project-level binding. * * Returns null if no binding exists. Returns a migrated v2.16.0 binding * when legacy schema is detected (one-shot auto-migration; original * legacy file is NOT deleted — caller decides retention policy). * * On schema violation (corrupt JSON or missing required field), the file * is backed up as `.bak.` and null is returned. The next * write call will recreate the binding from scratch. */ export declare function readBinding(projectRoot: string): Binding | null; /** * Persist the binding atomically. Caller passes the full binding object; * the store does NOT merge — pass the result of `readBinding` (or * `withInstance`) and modify in place. */ export declare function writeBinding(projectRoot: string, binding: Binding): void; /** * Register a Claude instance by `callerId`. Idempotent: if an instance * with the given callerId already exists, refresh its `lastHeartbeat` * and return its sid. Otherwise pick the next free sid slot (creating * a new session id under `instances`) and return it. * * This is the D2 Claude-instance-level binding primitive — a single * Claude instance keeps one sid across multiple peaks-* skill * activations; binding.instances[].roles accumulates the skill * names (peaks-code → peaks-rd → peaks-qa in the same Claude run). * * The caller is responsible for actually creating the session dir via * `initWorkspace` and storing per-session meta via `setSessionMeta`. */ export declare function registerInstance(projectRoot: string, opts: { callerId: string; roles?: string[]; existingSid?: string; }): { binding: Binding; sid: string; }; /** * Touch the `lastHeartbeat` for a specific instance. Used by the * gate. */ export declare function heartbeat(projectRoot: string, sid: string): Binding | null; /** * Drop an instance entry from the binding. Used when the last peaks-* * no record). Returns the updated binding (or null if no instances * remain and the caller wants to delete the binding entirely). */ export declare function dropInstance(projectRoot: string, sid: string): Binding | null; /** * Drop stale instances (lastHeartbeat older than `ttlMs`). Returns * the list of dropped sid values so the caller can log them. Used */ export declare function dropStale(projectRoot: string, ttlMs: number): { binding: Binding | null; dropped: string[]; }; /** * Look up the active sid for a Claude caller. Returns null if this * caller has never registered or its instance was dropped. * * The caller is typically the outer-session-id (PEAKS_OUTER_SESSION_ID * with CLAUDE_CODE_SESSION_ID fallback) — same string used as * `ownerHint` and `callerId`. */ export declare function findSidByCaller(projectRoot: string, callerId: string): string | null; export type RebuildResult = { rewritten: number; preserved: number; errors: string[]; /** True when no legacy entries were present and the file is unchanged. */ noop: boolean; }; export declare function rebuildBindingFromLegacy(projectRoot: string): RebuildResult;