import type * as native from "@gajae-code/natives"; import { type ManagedDirectoryRoot } from "./internal/managed-session-storage"; export interface SessionStorageStat { dev: bigint; ino: bigint; nlink?: bigint; size: number; mtimeMs: number; mtimeNs: bigint; ctimeNs: bigint; mtime: Date; isFile: boolean; } /** Exact bytes and identity captured from one opened regular-file descriptor. */ export interface SessionStorageSnapshot { bytes: Uint8Array; stat: SessionStorageStat; } export interface SessionStorageExactReplacementExpectation { readonly stat: SessionStorageStat; readonly sha256: string; } /** Upper bound for one descriptor-validated recorded range read. */ export declare const SESSION_RANGE_READ_MAX_BYTES: number; /** * One bounded recorded-length read validated against a single opened descriptor. * `bytes` is exactly `length` bytes from `[start, start + length)` of the same * regular-file object; `stat` is the fresh post-read descriptor snapshot so the * caller can compare dev/ino/nlink against the pathname before committing an index. */ export interface SessionStorageRangeSnapshot { stat: SessionStorageStat; bytes: Uint8Array; } /** * Four-state writer close lifecycle. Only a successful underlying close confirms * `closed`. A failure certified to have happened BEFORE the OS close was dispatched * is `close_failed_retryable` (ownership of the numeric fd is still proven, so a * later retry/finalizer close is safe). Any exception from an actually dispatched * close call is terminal `close_unknown`: the numeric fd cannot be safely retried * or finalizer-closed, and the writer blocks strict deletion. */ export type SessionStorageWriterCloseState = "open" | "close_failed_retryable" | "close_unknown" | "closed"; /** * Thrown by a {@link SessionStorageWriterCloseAdapter} to certify that a close * failure occurred BEFORE the real OS close (`fs.closeSync`-equivalent) was ever * dispatched. Because no OS close ran, the numeric fd is still owned and a retry * is safe. Any other thrown value is treated as a dispatched close failure * (`close_unknown`) and forbids retry/finalizer close of that fd. */ export declare class SessionStorageWriterRetryableCloseError extends Error { readonly name = "SessionStorageWriterRetryableCloseError"; constructor(message?: string, options?: ErrorOptions); } /** * Injectable dispatcher for the numeric-fd OS close. The default implementation * calls `fs.closeSync(fd)`. Tests inject adapters that throw * {@link SessionStorageWriterRetryableCloseError} to certify a pre-dispatch * failure, or that call the real close and throw to simulate a dispatched * failure (`close_unknown`). */ export interface SessionStorageWriterCloseAdapter { close(fd: number): void; } /** Bounded buffered-writer capacity limits. */ export declare const SESSION_STORAGE_BUFFERED_WRITER_MIN_BYTES: number; export declare const SESSION_STORAGE_BUFFERED_WRITER_MAX_BYTES: number; export declare const SESSION_STORAGE_BUFFERED_WRITER_DEFAULT_BYTES: number; /** * Counters for a synchronous buffered sidecar writer. `bytesWritten` and * `writeCalls` count operations against the backend, not calls accepted into * the in-process buffer. */ export interface SessionStorageBufferedWriterInstrumentation { readonly bytesSubmitted: number; readonly bytesWritten: number; readonly writeCalls: number; readonly flushCalls: number; readonly bufferedBytes: number; } /** Options for opening a {@link SessionStorageWriter}. */ export interface SessionStorageWriterOpenOptions { flags?: "a" | "w"; onError?: (err: Error) => void; /** Injectable OS-close dispatcher; defaults to `fs.closeSync`. */ closeAdapter?: SessionStorageWriterCloseAdapter; /** Opaque authority for default-computed managed destinations only. */ securityContext?: SessionStorageSecurityContext; /** Enable bounded synchronous buffering for disposable sidecar writes. */ bufferSize?: number; } /** * Immutable authority attached only to a computed managed session destination. * A caller-supplied pathname never receives this capability, even when it * happens to equal the current default session directory. */ export interface ManagedSessionSecurityContext { readonly kind: "managed"; readonly agentDir: string; /** Logical profile root for process-local caches; distinct from managed pathname authority. */ readonly profileAgentDir: string; readonly sessionsRoot: string; readonly sessionDir: string; readonly rootAuthority: ManagedDirectoryRoot; readonly retainedAuthority?: native.RecoveryFsRoot; } /** @internal Create the only accepted managed writer authority object. */ export declare function createManagedSessionSecurityContext(input: { agentDir: string; /** Optional for compatibility with existing authority-only callers. */ profileAgentDir?: string; sessionsRoot: string; sessionDir: string; rootAuthority: ManagedDirectoryRoot; retainedAuthority?: native.RecoveryFsRoot; }): ManagedSessionSecurityContext; export type SessionStorageSecurityContext = ManagedSessionSecurityContext | undefined; export interface SessionStorageWriter { writeLine(line: string): Promise; /** * Synchronously append a single line. Returns once the bytes are handed to the kernel * (page cache), so the data survives a non-graceful process death (OOM, SIGKILL, etc.) * even though it has not yet been fsynced to the underlying disk. * * `line` MUST already include the trailing newline. Throws synchronously on I/O error. */ writeLineSync(line: string): void; flush(): Promise; fsync(): Promise; /** Synchronously fsync all prior writes when the backend supports durable sidecar publication. */ fsyncSync?(): void; /** Descriptor-bound identity captured from the still-open writer after fsync. */ statSync?(): SessionStorageStat; close(): Promise; /** * Synchronously close the underlying descriptor. The certainty-aware close * state is updated synchronously and any close failure throws before this * returns, so sync callers (atomic rewrite) can observe a close failure * before proceeding to rename. Mirrors {@link close} semantics exactly. */ closeSync(): void; getError(): Error | undefined; /** Current certainty-aware close lifecycle state. */ getCloseState(): SessionStorageWriterCloseState; /** Stored error for non-success close states (`close_failed_retryable`/`close_unknown`). */ getCloseError(): Error | undefined; } /** Synchronous byte-oriented writer with bounded buffering for disposable sidecars. */ export interface SessionStorageBufferedWriter extends SessionStorageWriter { /** Append already-serialized bytes; the caller's view may be reused on return. */ writeBytesSync(bytes: Uint8Array): void; /** Flush pending bytes to the backend without synchronizing them to stable storage. */ flushSync(): void; /** Flush pending bytes, then synchronize the backend. */ fsyncSync(): void; /** Flush pending bytes, then close the backend descriptor. */ closeSync(): void; /** Snapshot backend-write counters and current pending capacity. */ getInstrumentation(): SessionStorageBufferedWriterInstrumentation; } /** Upper bound for one staged line (excluding the trailing newline). */ export declare const STAGED_WRITER_LINE_MAX_BYTES: number; /** Upper bound for aggregated different-length patches buffered for the publish-time overlay pass. */ export declare const STAGED_WRITER_PATCH_LIMIT_BYTES: number; export declare const STAGED_WRITER_PATCH_MAX_COUNT = 65536; export declare const STAGED_MEMORY_WRITER_MAX_BYTES: number; export declare const STAGED_MEMORY_WRITER_MAX_LINES: number; /** * Bounded staged streaming writer for one immutable one-shot destination (fork / * capture). Lines are streamed to a sibling staging file; {@link publishNoReplace} * atomically publishes the staged file only while the destination is still absent, * so publication never materializes the whole file in memory. Different-length * {@link patchLine} replacements are buffered (bounded) and applied by a second * bounded streaming pass at publish time. * * `publishNoReplace` is reserved for immutable one-shot destinations and must never * be used for the mutable `.spill.commit` marker (checked create/replace helpers * exist for that path). */ export interface StagedStreamingWriter { /** Append one complete line; the writer adds the trailing newline. */ writeLine(bytes: Uint8Array): void; /** Move the line cursor to `ordinal` (0-based) so a later patchLine targets it. */ seekToLine(ordinal: number): void; /** * Replace the line at `ordinal` with `bytes`. Same-length replacements are * applied in place; different-length replacements are buffered (bounded) and * applied by the publish-time overlay pass. */ patchLine(ordinal: number, bytes: Uint8Array): void; /** Hand buffered writes to the kernel (the staged descriptor is unbuffered). */ flush(): void; /** Synchronize the staged file. */ fsync(): void; /** Close the staged descriptor; required before {@link publishNoReplace}. */ closeSync(): void; /** Atomically publish the staged file at the destination only while it is absent. */ publishNoReplace(): void; } export interface SessionStorageExclusiveLock { releaseSync(): void; } export interface SessionStorage { ensureDirSync(dir: string): void; existsSync(path: string): boolean; writeTextSync(path: string, content: string): void; readTextSync(path: string): string; /** Exact on-disk bytes for strict read-only session inspection. */ readBytesSync?(path: string): Uint8Array; /** Exact bytes and descriptor-bound identity captured from one opened regular file. */ readSnapshotSync?(path: string): SessionStorageSnapshot; statSync(path: string): SessionStorageStat; listFilesSync(dir: string, pattern: string): string[]; /** List matching files with mtimes without issuing one JavaScript stat call per path. */ listFilesByMtime?(dir: string, pattern: string): Promise>; /** * Strict directory scan that never suppresses scan/root errors. Used by strict * authorization inventory; the forgiving {@link listFilesSync} stays display-only. */ listFilesStrictSync?(dir: string, pattern: string): string[]; exists(path: string): Promise; readText(path: string): Promise; readTextPrefix(path: string, maxBytes: number): Promise; writeText(path: string, content: string): Promise; rename(path: string, nextPath: string): Promise; renameSync(path: string, nextPath: string): void; /** Replace only while the destination still has the expected exact identity and bytes. */ replaceExactSync?(sourcePath: string, destinationPath: string, expected: SessionStorageExactReplacementExpectation): boolean; unlink(path: string): Promise; unlinkSync(path: string): void; deleteSessionWithArtifacts(sessionPath: string): Promise; /** * Verified hard delete bound to exact identity evidence. Removes the verified * artifact directory first, revalidates, and unlinks the transcript last. Returns * typed partial-cleanup evidence for exact-identity retry; never returns success * for a partial deletion. */ deleteSessionVerified?(target: VerifiedSessionDeleteTarget): Promise; openWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageWriter; /** Open a bounded synchronous byte-oriented writer for disposable sidecars. */ openBufferedWriter?(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageBufferedWriter; /** Bounded recorded-length read with descriptor identity validation (additive). */ readRangeSync?(path: string, start: number, length: number): SessionStorageRangeSnapshot; /** Async bounded recorded-length read with descriptor identity validation (additive). */ readRange?(path: string, start: number, length: number): Promise; /** Open a staged streaming writer for one immutable one-shot destination (additive). */ openStagedWriter?(path: string, options?: SessionStorageWriterOpenOptions): StagedStreamingWriter; /** Acquire an owner-bound exclusive lock; returns undefined while another owner holds it. */ acquireExclusiveLockSync?(path: string, options?: { securityContext?: SessionStorageSecurityContext; }): SessionStorageExclusiveLock | undefined; } /** Exact authorization evidence for a transcript or artifact path. */ export interface SessionStorageFileIdentity { dev: bigint; ino: bigint; nlink?: bigint; size: number; mtimeNs: bigint; sha256: string; } /** Kind of verification failure surfaced by {@link deleteSessionVerified}. */ export type VerifiedDeleteFailureKind = "containment" | "symlink" | "stat" | "identity" | "header" | "cwd" | "artifacts"; /** * Thrown by {@link deleteSessionVerified} when canonical containment, transcript * non-symlink/identity, header id/cwd, parent identity, or artifact identity * verification fails. These are visible, sanitized failures: they never mutate * the transcript or artifacts and grant zero authority. */ export declare class SessionDeleteVerificationError extends Error { readonly kind: VerifiedDeleteFailureKind; constructor(kind: VerifiedDeleteFailureKind, message: string, options?: ErrorOptions); } /** * Exact identity evidence a verified hard delete binds to. All fields are captured * at authorization time; delete revalidates each one before any mutation. Retry * after a partial cleanup supplies the recorded artifact identity via * {@link expectedArtifactsIdentity}. */ export interface VerifiedSessionDeleteTarget { /** Canonical sessions root; the transcript must be contained within it. */ sessionsRoot: string; /** Canonical transcript path (absolute `*.jsonl`). */ transcriptPath: string; /** Expected session id parsed from the header. */ sessionId: string; /** Expected canonical cwd parsed from the header. */ cwd: string; /** Expected transcript file `(dev, ino)` captured at authorization. */ transcriptIdentity: SessionStorageFileIdentity; transcriptParentIdentity?: { dev: bigint; ino: bigint; }; /** * For retry after an `artifacts` `cleanup_pending`: the recorded artifact * directory identity to re-accept. A replacement/different artifact directory * fails closed. Omit on first attempt or to accept recorded absence. */ expectedArtifactsIdentity?: SessionStorageFileIdentity; artifactsAbsentAtAuthorization?: true; /** Stable native recursive-tree evidence captured before artifact detachment. */ expectedArtifactsTree?: NativeDirectoryTreeSnapshot; /** Identity-bound quarantine path retained when recursive artifact cleanup failed. */ detachedArtifactsPath?: string; /** Identity-bound quarantine path retained when transcript unlink deferred cleanup. */ detachedTranscriptPath?: string; /** Native-retained publisher successor observed during transcript cleanup. */ retainedTranscriptSuccessorPath?: string; /** Native-retained exchange placeholder observed during transcript cleanup. */ retainedTranscriptPlaceholderPath?: string; /** Native-retained transcript entry whose identity could not be verified. */ retainedTranscriptUnknownPath?: string; /** Native-retained publisher successor observed during cleanup. */ retainedArtifactsSuccessorPath?: string; /** Native-retained exchange placeholder observed during cleanup. */ retainedArtifactsPlaceholderPath?: string; /** Native-retained entry whose identity could not be verified. */ retainedArtifactsUnknownPath?: string; /** Caller-published, no-replace quarantine pathname for the next artifact detach. */ plannedArtifactsPath?: string; /** Caller-published, no-replace quarantine pathname for the next transcript detach. */ plannedTranscriptPath?: string; /** Set only after a durable caller receipt records successful artifact removal. */ artifactsRemoved?: true; } /** * Outcome of a verified hard delete. Artifact removal happens first; only after * revalidation is the transcript unlinked last. A partial deletion returns * `cleanup_pending` with exact evidence for same-connection retry — never * `deleted` and never `{}`. */ export type VerifiedSessionDeleteResult = { kind: "artifacts_removed"; phase: "artifacts"; transcriptIdentity: SessionStorageFileIdentity; } | { kind: "deleted"; } | { kind: "cleanup_pending"; phase: "artifacts"; error: Error; /** Artifact directory identity at failure time; undefined when absent. */ artifactsIdentity: SessionStorageFileIdentity | undefined; /** Identity-bound quarantine path retained when recursive cleanup failed. */ detachedArtifactsPath: string; artifactsPayloadDurable?: true; /** Native snapshot required for an identity-bound recursive retry. */ artifactsTree: NativeDirectoryTreeSnapshot; /** Transcript identity (unchanged) for retry binding. */ transcriptIdentity: SessionStorageFileIdentity; retainedSuccessorPath?: string; retainedPlaceholderPath?: string; retainedUnknownPath?: string; } | { kind: "cleanup_pending"; phase: "transcript"; error: Error; /** Transcript identity at failure time for retry binding. */ transcriptIdentity: SessionStorageFileIdentity; /** Optional identity-bound transcript quarantine path for restart cleanup. */ detachedTranscriptPath?: string; transcriptPayloadDurable?: true; retainedSuccessorPath?: string; retainedPlaceholderPath?: string; retainedUnknownPath?: string; }; type NativeDirectoryTreeEntry = { relativePath: string; kind: string; dev: string; ino: string; nlink: string; size: string; mtimeNs: string; ctimeNs: string; sha256?: string; }; export type NativeDirectoryTreeSnapshot = { rootDev: string; rootIno: string; entries: NativeDirectoryTreeEntry[]; }; /** Physically observed commit-marker state; corrupt JSON is still `present`. */ export type SessionCommitMarkerState = { kind: "missing"; } | { kind: "present"; rawBytesSha256: string; stat: SessionStorageStat; }; /** Exact `present` expectation for one checked commit-marker replacement. */ export interface SessionCommitMarkerPresentExpectation { /** SHA-256 of the exact raw marker bytes physically on disk (corrupt JSON included). */ rawBytesSha256: string; /** Descriptor snapshot of the marker object expected to be replaced. */ descriptorIdentity: SessionStorageStat; } /** Snapshot one commit marker's physical state without granting write authority. */ export declare function readSessionCommitMarkerSync(storage: SessionStorage, markerPath: string): SessionCommitMarkerState; /** * Checked commit-marker create: publishes only while the marker is still `missing`. * Temp + fsync + atomic create-if-absent + directory fsync (file backend); the * in-memory backend mirrors the same missing-expectation abort. Leftover temps are * removed on any failure. Runs inside the caller's persistence fence. */ export declare function createSessionCommitMarkerCheckedSync(storage: SessionStorage, markerPath: string, bytes: Uint8Array, options?: { securityContext?: SessionStorageSecurityContext; }): void; /** * Checked commit-marker replace: replaces only on an exact `present` raw/hash + * descriptor identity match (corrupt-present included). Temp + fsync + checked * atomic rename + directory fsync (file backend); any mismatch aborts with the * current marker untouched. The in-memory backend mirrors the same aborts. Runs * inside the caller's persistence fence. */ export declare function replaceSessionCommitMarkerCheckedSync(storage: SessionStorage, markerPath: string, bytes: Uint8Array, expected: SessionCommitMarkerPresentExpectation, options?: { securityContext?: SessionStorageSecurityContext; }): void; export declare class FileSessionStorage implements SessionStorage { #private; acquireExclusiveLockSync(lockPath: string, options?: { securityContext?: SessionStorageSecurityContext; }): SessionStorageExclusiveLock | undefined; ensureDirSync(dir: string): void; existsSync(path: string): boolean; writeTextSync(fpath: string, content: string): void; readTextSync(fpath: string): string; readBytesSync(fpath: string): Uint8Array; readSnapshotSync(fpath: string): SessionStorageSnapshot; /** * Bounded recorded-length read with descriptor identity validation: opens one * no-follow descriptor, verifies the requested range is fully present, reads * exactly `length` bytes, and revalidates dev/ino/nlink on the same descriptor * plus the pathname (append-only size growth is tolerated; an object swap is * rejected). No path-based Bun Blob reads for managed authority. */ readRangeSync(fpath: string, start: number, length: number): SessionStorageRangeSnapshot; readRange(fpath: string, start: number, length: number): Promise; statSync(path: string): SessionStorageStat; listFilesSync(dir: string, pattern: string): string[]; listFilesByMtime(dir: string, pattern: string): Promise>; listFilesStrictSync(dir: string, pattern: string): string[]; exists(path: string): Promise; readText(path: string): Promise; readTextPrefix(path: string, maxBytes: number): Promise; writeText(path: string, content: string): Promise; rename(path: string, nextPath: string): Promise; renameSync(path: string, nextPath: string): void; replaceExactSync(sourcePath: string, destinationPath: string, expected: SessionStorageExactReplacementExpectation): boolean; unlink(path: string): Promise; unlinkSync(path: string): void; openWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageWriter; openBufferedWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageBufferedWriter; openStagedWriter(path: string, options?: SessionStorageWriterOpenOptions): StagedStreamingWriter; /** * Delete a session and sibling artifacts in an operator-selected explicit directory. * Default managed roots use deleteSessionVerified and never call this path. */ deleteSessionWithArtifacts(sessionPath: string): Promise; /** * Verified hard delete bound to exact identity evidence. Artifact directory first, * revalidate, transcript last. Partial deletion returns typed cleanup_pending * evidence; identity/symlink/containment/header/cwd mismatch throws. */ deleteSessionVerified(target: VerifiedSessionDeleteTarget): Promise; } export declare class MemorySessionStorage implements SessionStorage { #private; acquireExclusiveLockSync(lockPath: string, _options?: { securityContext?: SessionStorageSecurityContext; }): SessionStorageExclusiveLock | undefined; ensureDirSync(_dir: string): void; existsSync(path: string): boolean; writeBytesOwnedSync(path: string, content: Buffer): void; writeTextSync(path: string, content: string): void; readTextSync(path: string): string; readBytesSync(path: string): Uint8Array; readSnapshotSync(path: string): SessionStorageSnapshot; /** * Bounded recorded-length read with descriptor identity validation: mirrors the * file backend's contract (dev/ino/nlink identity, exact `length` bytes present) * against the in-memory file model so parity tests can compare backends. */ readRangeSync(path: string, start: number, length: number): SessionStorageRangeSnapshot; readRange(path: string, start: number, length: number): Promise; statSync(path: string): SessionStorageStat; listFilesSync(dir: string, pattern: string): string[]; listFilesByMtime(dir: string, pattern: string): Promise>; listFilesStrictSync(dir: string, pattern: string): string[]; exists(path: string): Promise; readText(path: string): Promise; readTextPrefix(path: string, maxBytes: number): Promise; writeText(path: string, content: string): Promise; rename(path: string, nextPath: string): Promise; renameSync(path: string, nextPath: string): void; replaceExactSync(sourcePath: string, destinationPath: string, expected: SessionStorageExactReplacementExpectation): boolean; unlink(path: string): Promise; unlinkSync(path: string): void; deleteSessionWithArtifacts(sessionPath: string): Promise; deleteSessionVerified(target: VerifiedSessionDeleteTarget): Promise; openWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageWriter; openBufferedWriter(path: string, options?: SessionStorageWriterOpenOptions): SessionStorageBufferedWriter; openStagedWriter(path: string): StagedStreamingWriter; } /** * Outcome of a disk-retention retirement attempt. * * `kept` means the transcript survived and nothing of the session was * destroyed, with the exact reason so `gjc gc --disk` can report it. * `cleanup_pending` means the delete authority already detached or removed the * session's artifact tree (or quarantined the transcript) before it stopped: * the record survives, the session does not, and a caller must NOT report that * as an ordinary keep. */ export type SessionRetirementOutcome = { kind: "retired"; } | { kind: "kept"; reason: string; } | { kind: "cleanup_pending"; reason: string; }; /** * Non-mutating projection of {@link retireSessionTranscript}: would the * retention pass's own preconditions let this transcript be retired at all? * * `gjc gc --disk` runs it so a dry run reports the verdict a prune would reach * instead of promising bytes the delete authority will refuse to release. The * authority's verdict (containment, identity, artifact tree) is deliberately * not predicted here — it is re-derived against live state at delete time. */ export declare function probeSessionRetirement(storage: FileSessionStorage, sessionsRoot: string, transcriptPath: string): { kind: "retirable"; } | { kind: "kept"; reason: string; }; /** * Retire one managed session transcript through the verified hard-delete * authority. * * This is the only supported way for a retention pass to remove a transcript: * identity, containment, header id/cwd, artifact tree and parent directory are * all re-verified inside {@link FileSessionStorage.deleteSessionVerified}, and * anything ambiguous fails closed as `kept` rather than deleting bytes. The * caller owns the retention policy; this function owns nothing but the delete * authority. */ export declare function retireSessionTranscript(storage: FileSessionStorage, sessionsRoot: string, transcriptPath: string): Promise; export {};