import { SessionStore, SessionStoreOptions, GetSnapshotOptions, SnapshotMutator } from './session.mjs';
import { SessionSnapshot } from './agent-types.mjs';
import '@genkit-ai/core';
import '@genkit-ai/core/async';
import '@genkit-ai/core/registry';
import './model-types.mjs';
import './parts.mjs';

/**
 * Copyright 2026 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * In-memory implementation of persistent Session Store.
 */
declare class InMemorySessionStore<S = unknown> implements SessionStore<S> {
    private snapshots;
    private listeners;
    private rejectBranchingSessions;
    /**
     * @param options.rejectBranchingSessions When `true`, a `sessionId` lookup
     *   that resolves to a branched history (more than one leaf) throws
     *   `FAILED_PRECONDITION` instead of returning the latest leaf. Defaults to
     *   `false`; opt in (e.g. in dev) to surface accidental branching early.
     */
    constructor(options?: {
        rejectBranchingSessions?: boolean;
    });
    getSnapshot(opts: GetSnapshotOptions): Promise<SessionSnapshot<S> | undefined>;
    saveSnapshot(snapshotId: string | undefined, mutator: SnapshotMutator<S>, options?: SessionStoreOptions): Promise<string | null>;
    onSnapshotStateChange(snapshotId: string, callback: (snapshot: SessionSnapshot<S>) => void, options?: SessionStoreOptions): void | (() => void);
}
/**
 * A Node.js file-system backed session snapshot store.
 *
 * Snapshots are stored as flat JSON files keyed by their `snapshotId`, under an
 * optional per-tenant sub-directory `prefix`:
 *
 * File layout: `dirPath/<prefix>/<snapshotId>.json`
 *
 * `getSnapshot({ sessionId })` resolves the session's current leaf via a tiny
 * per-session pointer file (`<prefix>/.pointers/<sessionId>.json`, see
 * {@link PointerDoc}) - one pointer read plus one snapshot read. When the
 * pointer is missing (e.g. a legacy store) or stale it transparently falls back
 * to scanning the prefix directory and selecting the single leaf whose
 * `sessionId` matches, then rewrites the pointer so subsequent lookups are fast
 * again.
 */
declare class FileSessionStore<S = unknown> implements SessionStore<S> {
    private dirPath;
    private maxPersistedChainLength?;
    private snapshotPathPrefix?;
    private rejectBranchingSessions;
    private snapshotWatchPollIntervalMs;
    /**
     * Per-file write locks. The {@link SessionStore} contract (and the
     * abort-aware mutator that branches on `current.status`) assumes
     * read-modify-write is atomic, but on the file system a read and the
     * `writeFile` below it are not. Without a lock two concurrent saves can
     * read the same `current` and the later write clobbers the earlier one
     * (e.g. a `completed` write overwriting a concurrent `aborted`). We
     * serialize saves per resolved file path with a simple promise chain.
     */
    private writeLocks;
    /**
     * @param dirPath Directory where snapshot JSON files are stored.
     * @param options.maxPersistedChainLength When set, snapshots older than this
     *   many entries in a chain are automatically deleted on each save.
     * @param options.snapshotPathPrefix Returns a sub-directory prefix derived
     *   from the call's {@link SessionStoreOptions} (e.g. the authenticated user
     *   id from `options.context`), useful for multi-tenant isolation: all reads
     *   and writes are scoped to that prefix, so one tenant can never see
     *   another's snapshots. Defaults to `"global"`.
     * @param options.rejectBranchingSessions When `true`, a `sessionId` lookup
     *   that resolves to a branched history (more than one leaf) throws
     *   `FAILED_PRECONDITION` instead of returning the latest leaf. Defaults to
     *   `false`; opt in (e.g. in dev) to surface accidental branching early.
     * @param options.snapshotWatchPollIntervalMs Polling interval (ms) for the
     *   {@link FileSessionStore.onSnapshotStateChange} fallback that backstops
     *   `fs.watch` (which can miss events on some filesystems, e.g. network
     *   mounts). Defaults to {@link DEFAULT_SNAPSHOT_WATCH_POLL_INTERVAL_MS}.
     */
    constructor(dirPath: string, options?: {
        maxPersistedChainLength?: number;
        snapshotPathPrefix?: (options?: SessionStoreOptions) => string;
        rejectBranchingSessions?: boolean;
        snapshotWatchPollIntervalMs?: number;
    });
    private ensureDir;
    /** Resolves the (per-tenant) directory snapshots are stored under. */
    private prefixDir;
    /**
     * Resolves the file path for a given snapshotId: `<prefix>/<snapshotId>.json`.
     */
    private getFilePath;
    /** Resolves the (per-tenant) directory holding per-session pointer files. */
    private pointersDir;
    /**
     * Resolves the pointer file path for a session, validating `sessionId` is a
     * plain basename so it can never escape the pointers directory. Pure: it does
     * not create the directory, so the read path stays side-effect free. The
     * write path calls {@link ensureDir} before writing.
     */
    private getPointerPath;
    /**
     * Reads the per-session {@link PointerDoc}, or `undefined` when it is missing
     * (legacy store / not yet written) or unreadable / corrupt - callers fall
     * back to a full directory scan in that case. Best-effort: any IO/parse error
     * resolves to `undefined` so the optimization can never make a lookup (or
     * save) fail where the scan-only baseline would have succeeded. An invalid
     * `sessionId` still throws (path validation is resolved outside the try) so it
     * fails fast rather than silently being ignored.
     */
    private readPointer;
    /**
     * Atomically writes the per-session {@link PointerDoc}. Best-effort: a
     * pointer write failure is swallowed since the pointer is only an
     * optimization - `sessionId` lookups still self-heal via the full scan. An
     * invalid `sessionId` still throws (path validation is resolved outside the
     * try) so it fails fast rather than silently being ignored.
     */
    private writePointer;
    /**
     * Serializes async work per resolved file path so a read-modify-write in
     * {@link saveSnapshot} is not interleaved with a concurrent one for the same
     * snapshot (see {@link writeLocks}).
     */
    private withFileLock;
    getSnapshot(opts: GetSnapshotOptions): Promise<SessionSnapshot<S> | undefined>;
    /**
     * Loads a single snapshot file by its id (no sessionId branch). Used by
     * internal traversal (parent chains) where we always have a concrete id.
     */
    private getSnapshotById;
    /**
     * Resolves the latest (leaf) snapshot for a session.
     *
     * Fast path: read the per-session pointer file and load the leaf it names -
     * one pointer read plus one snapshot read, independent of session count /
     * length. The pointer is skipped (and the scan used) when
     * `rejectBranchingSessions` is set, since detecting branches requires seeing
     * every leaf.
     *
     * Fallback (no/stale/corrupt pointer, or branch detection): scan every
     * snapshot file in the prefix directory, keep those whose `sessionId`
     * matches, select the single leaf, and refresh the pointer so later lookups
     * take the fast path.
     *
     * Known limitation: the fast path trusts the pointer when the snapshot it
     * names still exists and belongs to the session - it does not re-verify that
     * it is the actual leaf. So if a save succeeds but the subsequent (best-effort)
     * `writePointer` does not (crash/disk error), or two new saves for the same
     * session race and the older one writes the pointer last, the pointer can
     * linger on a valid-but-older same-session snapshot and lookups return it
     * until the next save advances the pointer. This is the accepted trade-off for
     * a best-effort cache: verifying leaf-ness on every read would reintroduce the
     * full scan the pointer exists to avoid. Callers needing strict guarantees can
     * resume by `snapshotId`, or set `rejectBranchingSessions` (which always
     * scans).
     */
    private getLatestSnapshotForSession;
    saveSnapshot(snapshotId: string | undefined, mutator: SnapshotMutator<S>, options?: SessionStoreOptions): Promise<string | null>;
    private saveSnapshotUnlocked;
    /**
     * Writes `contents` to `filePath` atomically: write to a temp file in the
     * same directory, then rename over the target. `rename` is atomic on POSIX
     * and Windows, so a concurrent reader in {@link getSnapshot} never observes a
     * half-written (torn) file.
     */
    private atomicWrite;
    /**
     * Watches a single snapshot file for changes and invokes `callback` with the
     * parsed snapshot whenever it changes.
     *
     * Unlike {@link InMemorySessionStore}, file-backed snapshots are frequently
     * mutated by a *different* process (e.g. the request handler that received an
     * abort writes `status: 'aborted'`, while a detached background worker is the
     * one watching). Detecting that requires observing the filesystem rather than
     * in-process `saveSnapshot` calls.
     *
     * Reliability comes from two layers:
     * - `fs.watch` on the (per-tenant) prefix directory, filtered to the target
     *   `<snapshotId>.json`. This is low latency but can miss events on some
     *   filesystems (network mounts, certain container volumes).
     * - A polling fallback (`snapshotWatchPollIntervalMs`) that re-reads the file
     *   on an interval, backstopping any events `fs.watch` drops. Its timer is
     *   `unref`'d so it never keeps the process alive on its own.
     *
     * Callbacks are de-duplicated by serialized content, so the noisy/duplicate
     * events `fs.watch` emits collapse into one callback per real change.
     * Transient read errors (e.g. a partially written file mid-rewrite, or a
     * not-yet-created file) are swallowed; the next event/poll re-reads.
     *
     * @returns An unsubscribe function that stops watching and polling.
     */
    onSnapshotStateChange(snapshotId: string, callback: (snapshot: SessionSnapshot<S>) => void, options?: SessionStoreOptions): void | (() => void);
}

export { FileSessionStore, InMemorySessionStore };
