/** * Persistent 1:1 binding between one dsh session and one durable Codex * thread (C3/Q4). Single local JSON file, atomic replace on write; survives * dsh restarts so a bound session reconnects to the same Codex thread. * * Invariants enforced here: * - one dsh session binds at most one Codex thread (Q4 first half); * - one Codex thread is owned by at most one dsh session (Q4 second half). * * @module dsh-subagent-codex-plus/gateway/binding */ /** One durable session↔thread binding. */ export interface GatewayBinding { /** Durable Codex thread id (`thread/resume` target). */ readonly codexThreadId: string; /** Unix ms when the binding was established. */ readonly boundAt: number; } export interface GatewayBindingState { readonly version: 1; /** dsh session id → binding. */ readonly bindings: Record; } /** * File-backed binding store. Reads once at construction and writes * atomically (temp file + rename) on every mutation. */ export declare class GatewayBindingStore { private readonly file; private state; constructor(file: string); /** Resolve one session's binding, if any. */ get(sessionId: string): GatewayBinding | undefined; /** Which dsh session owns the given Codex thread, if any. */ threadOwner(codexThreadId: string): string | undefined; /** Snapshot of every live binding, session-id order stable by key. */ list(): readonly (readonly [string, GatewayBinding])[]; /** * Bind one session to one thread. Refuses both halves of the 1:1 * invariant: a session already bound, or a thread already owned by * another session. * @returns the recorded binding. */ bind(sessionId: string, codexThreadId: string): GatewayBinding; /** Remove one session's binding. Idempotent. */ unbind(sessionId: string): boolean; /** Raw file path (diagnostics). */ get filePath(): string; private load; private save; }