/** * Thrown when a session that is going to publish events cannot say where its WAL belongs. * * A distinct type rather than a bare `Error` so a caller can tell "misconfigured launch" from * "filesystem said no" without matching on message text. */ export declare class EventsStateRootMissing extends Error { constructor(message: string); } /** * The state root for an events-enabled session, or a loud failure. * * **THE FAILURE THIS PREVENTS IS SILENT, WHICH IS WHY IT IS A THROW AND NOT A DEFAULT.** The * tempting fallback is `process.cwd()`. A WAL written there is not an error anyone sees: the emitter * starts, frames publish, and the durable state that makes a restart safe sits in whatever directory * the launch happened to begin in — a per-agent working directory that "can point at any repo". The * next start resolves the real root, finds no WAL, and reads an already-published thread as VIRGIN: * `E := 0`, a fresh epoch, a second frame claiming a sequence the stream has already seen. Nothing * reports a problem at any point, and an absence is what a clean board looks like. * * `env` is a PARAMETER and the root resolves PER CALL. A module-level read would freeze whatever the * environment was at first import, and none of what this path is keyed on — space, principal, * thread — is process-wide. */ export declare function resolveEventsStateRoot(env: { COTAL_WORKSPACE_ROOT?: string | undefined; }): string; /** The resolved locations for one `(space, principal, threadId)`. Pure: computes, touches nothing. */ export interface EventWalLocation { /** `/.cotal/events//` — the lock's directory. */ principalDir: string; /** The single-emitter-per-principal lock. One emitter per PRINCIPAL, not per thread. */ lockPath: string; /** `/subject.json` — the PRINCIPAL-scoped subject frontier, shared by every thread * of this principal because the subject is. Declared here so the layout has one owner. */ subjectPath: string; /** `/` — the directory holding exactly one `wal.json`. */ threadDir: string; /** The WAL document itself. */ walPath: string; } /** What {@link ensureEventWalDir} returns: the location, plus the lock it actually took for it. */ export interface HeldEventWalLocation extends EventWalLocation { lock: PrincipalLock; } /** * Resolve where this principal's WAL for this thread lives. Pure — no IO, no side effects. * * `workspaceRoot` is taken as given rather than defaulted. A default here would be a silent fallback * onto whatever directory the process happened to start in, which is precisely the scattering the * root exists to prevent; the caller that cannot resolve one must fail loud instead. */ export declare function eventWalLocation(opts: { workspaceRoot: string; space: string; principal: string; threadId: string; }): EventWalLocation; /** Every refusal on the lock path is one of these, so a caller never mistakes it for an I/O blip. */ export declare class PrincipalLockError extends Error { readonly path: string; readonly invariant: string; constructor(path: string, invariant: string, detail: string); } /** A HELD lock. It exists as an object only while this process owns the file. */ export interface PrincipalLock { readonly path: string; /** Close the handle and remove the file. Idempotent: releasing twice is not an error. */ release(): Promise; } /** * Take this principal's lock and HOLD IT for the life of the process. * * The handle stays open deliberately. A lock released at the end of the acquiring function is a * lock that was never held, and the layout comment above has claimed single-emitter exclusion since * this module was written while `lockPath` was only ever COMPUTED — a path in a struct standing in * for a guarantee. This is that claim made real. */ export declare function acquirePrincipalLock(lockPath: string): Promise; /** * `fsync` one directory, so its own entries are durable. * * Opened read-only: fsync on a directory handle is the portable way to flush the entries, and a * directory cannot be opened for writing anyway. `EBADF`/`EINVAL`/`EPERM` are TOLERATED rather than * fatal — some filesystems refuse to fsync a directory handle at all, and failing an emitter start * on a platform that simply does not offer the guarantee would trade a durability improvement for * an availability regression. Every other error propagates. */ export declare function fsyncDir(dir: string): Promise; /** * Create the directory chain for a thread's WAL and make it durable: **once, before the * first transition.** * * The WAL's own replace protocol fsyncs `wal.json` and the thread directory that holds it. That is * not sufficient on its own: a newly created `/` directory's NAME is an entry in its * PARENT, and `ensureDirNoSymlink` only `mkdir`s each missing component with no fsync anywhere. On a * filesystem that honours the distinction, a crash after the first transition and after the publish * can come back with the directory link itself lost — no WAL, no pending record — and boot then * reads a thread that has already published as VIRGIN: `E := 0`, a fresh epoch, and a second frame * claiming a sequence the stream has already seen. * * So every component from the workspace root down is fsynced, parents included, rather than the leaf * alone. It runs once at emitter start; the cost is a handful of directory syncs against a session * that is about to do real work. * * Returns the WAL path, so a caller cannot resolve the location by one route and create it by * another — and the HELD principal lock with it, for the same reason. Handing back a location whose * lock the caller then has to remember to take is how the lock came to be a path and nothing else. */ export declare function ensureEventWalDir(opts: { workspaceRoot: string; space: string; principal: string; threadId: string; }): Promise; //# sourceMappingURL=agui-wal-path.d.ts.map