import type { AbstractLevel } from 'abstract-level';
import type { GenericMessage } from '@enbox/dwn-sdk-js';
import type { EnboxPlatformAgent } from './types/agent.js';
import type { DeadLetterCategory, DeadLetterEntry, StartSyncParams, SyncConnectivityState, SyncEngine, SyncEventListener, SyncHealthSummary, SyncIdentityOptions } from './types/sync.js';
export type SyncEngineLevelParams = {
    agent?: EnboxPlatformAgent;
    dataPath?: string;
    db?: AbstractLevel<string | Buffer | Uint8Array>;
};
export declare class SyncEngineLevel implements SyncEngine {
    /**
     * Holds the instance of a `EnboxPlatformAgent` that represents the current execution context for
     * the `SyncEngineLevel`. This agent is used to interact with other Enbox agent components. It's
     * vital to ensure this instance is set to correctly contextualize operations within the broader
     * Enbox Agent framework.
     */
    private _agent?;
    /**
     * An instance of the `AgentPermissionsApi` that is used to interact with permissions grants used during sync
     */
    private _permissionsApi;
    private readonly _db;
    private _syncIntervalId?;
    private _syncLock;
    /**
     * Durable replication ledger — persists per-link checkpoint state.
     * Used by live sync to track pull progression per link.
     * Lazily initialized on first use to avoid sublevel() calls on mock dbs.
     */
    private _ledger?;
    /**
     * In-memory cache of active links, keyed by `{did}^{dwnUrl}^{protocol}`.
     * Populated from the ledger on `startLiveSync`, used by subscription handlers
     * to avoid async ledger lookups on every event.
     */
    private readonly _activeLinks;
    /**
     * Per-link in-memory delivery-order tracking for the pull path. Keyed by
     * the same link key as `_activeLinks`. Not persisted — on crash, replay
     * restarts from `contiguousAppliedToken` and idempotent apply handles
     * re-delivered events.
     */
    private readonly _linkRuntimes;
    /**
     * Hex-encoded default hashes for empty subtrees at each depth, keyed by depth.
     * Lazily initialized on first use. Used by `walkTreeDiff` to detect empty subtrees
     * and short-circuit the recursive walk instead of descending all the way to MAX_DIFF_DEPTH.
     */
    private _defaultHashHex?;
    /** Current sync mode, set by `startSync`. Reset to `undefined` by `stopSync`/`clear`. */
    private _syncMode;
    /**
     * Monotonic session generation counter. Incremented on every teardown.
     * Async operations (repair, retry timers) capture the generation at start
     * and bail if it has changed — this prevents stale work from mutating
     * state after teardown or mode switch.
     */
    private _engineGeneration;
    /** Active live pull subscriptions (remote -> local via MessagesSubscribe). */
    private _liveSubscriptions;
    /** Active local EventLog subscriptions for push-on-write (local -> remote). */
    private _localSubscriptions;
    /** Connectivity state derived from subscription health. */
    private _connectivityState;
    /** Registered event listeners for observability. */
    private readonly _eventListeners;
    /** Per-link push runtime: queue, debounce timer, retry state. */
    private readonly _pushRuntimes;
    /**
     * CIDs recently received via pull subscription, keyed by `cid|dwnUrl` to
     * scope suppression per remote endpoint. A message pulled from Provider A
     * is only suppressed for push back to Provider A — it still fans out to
     * Provider B and C. TTL: 60 seconds. Cap: 10,000 entries.
     */
    private readonly _recentlyPulledCids;
    /** TTL for echo-loop suppression entries (60 seconds). */
    private static readonly ECHO_SUPPRESS_TTL_MS;
    /**
     * Per-tenant closure evaluation contexts for the current live sync session.
     * Caches ProtocolsConfigure and grant lookups across events for the same
     * tenant. Keyed by tenantDid to prevent cross-tenant cache pollution.
     */
    private readonly _closureContexts;
    /** Maximum entries in the echo-loop suppression cache. */
    private static readonly ECHO_SUPPRESS_MAX_ENTRIES;
    /** Validate `SyncIdentityOptions` for `registerIdentity` and `updateIdentityOptions`. */
    private static validateSyncIdentityOptions;
    private buildSyncTargetsForEndpoint;
    private buildSyncTargetResolutions;
    /**
     * Cached sync targets result from the last {@link getSyncTargets} call.
     * Invalidated on identity registration/unregistration/update.
     * TTL-based: cleared after 30 seconds to pick up DID document changes.
     */
    private _syncTargetsCache?;
    /**
     * Monotonic generation counter for sync target cache invalidation.
     * Bumped on every invalidation (register/unregister/update/clear/close/stopSync).
     * An in-flight `getSyncTargets()` captures the generation before awaiting
     * and only writes to the cache if it hasn't changed, preventing a
     * concurrent mutation from being masked by stale data.
     */
    private _syncTargetsCacheGeneration;
    /** TTL for the sync targets cache (30 seconds). */
    private static readonly SYNC_TARGETS_CACHE_TTL_MS;
    /** Backoff schedule for recently published did:dht records. */
    private static readonly DID_RESOLUTION_RETRY_BACKOFF_MS;
    /** Count of consecutive SMT sync failures (for backoff in poll mode). */
    private _consecutiveFailures;
    /** Maximum consecutive failures before entering backoff. */
    private static readonly MAX_CONSECUTIVE_FAILURES;
    /** Backoff multiplier for consecutive failures (caps at 4x the configured interval). */
    private static readonly MAX_BACKOFF_MULTIPLIER;
    /**
     * Bound browser event handlers so they can be added and removed.
     * Set in `startBrowserConnectivityListeners`, cleared in `stopBrowserConnectivityListeners`.
     */
    private _onOnline?;
    private _onOffline?;
    private _onVisibilityChange?;
    constructor({ agent, dataPath, db }: SyncEngineLevelParams);
    /** Lazy accessor for the replication ledger. */
    private get ledger();
    /** LevelDB sublevel for permanently failed messages (dead letters). */
    private get _deadLetters();
    /**
     * Retrieves the `EnboxPlatformAgent` execution context.
     *
     * @returns The `EnboxPlatformAgent` instance that represents the current execution context.
     * @throws Will throw an error if the `agent` instance property is undefined.
     */
    get agent(): EnboxPlatformAgent;
    set agent(agent: EnboxPlatformAgent);
    get hasActiveSubscriptions(): boolean;
    get connectivityState(): SyncConnectivityState;
    on(listener: SyncEventListener): () => void;
    /** Emit a sync event to all registered listeners. */
    private emitEvent;
    clear(): Promise<void>;
    close(): Promise<void>;
    registerIdentity({ did, options }: {
        did: string;
        options: SyncIdentityOptions;
    }): Promise<void>;
    unregisterIdentity(did: string): Promise<void>;
    getIdentityOptions(did: string): Promise<SyncIdentityOptions | undefined>;
    updateIdentityOptions({ did, options }: {
        did: string;
        options: SyncIdentityOptions;
    }): Promise<void>;
    sync(direction?: 'push' | 'pull'): Promise<void>;
    startSync(params: StartSyncParams): Promise<void>;
    /**
     * stopSync awaits the completion of the current sync operation before stopping the sync interval
     * and tearing down any live subscriptions.
     *
     * @param timeout - Maximum milliseconds to wait for an in-progress
     *   sync cycle to finish. Non-finite values (`NaN`, `Infinity`) are
     *   coerced to the default to avoid a tight poll loop or never-exit
     *   condition.
     */
    stopSync(timeout?: number): Promise<void>;
    private startPollSync;
    /**
     * Starts live sync:
     * 1. Performs an initial SMT reconciliation to catch up.
     * 2. Opens MessagesSubscribe subscriptions to each remote DWN for real-time pull.
     * 3. Subscribes to the local EventLog for push-on-write.
     * 4. Schedules an infrequent SMT integrity check at `interval`.
     */
    private startLiveSync;
    /**
     * Get or create the runtime state for a link.
     */
    private getOrCreateRuntime;
    /**
     * Drain contiguously committed ordinals from the runtime state, advancing
      * the link's pull checkpoint for each drained entry. Returns the number of
     * entries drained (0 if the next ordinal is not yet committed).
     */
    private drainCommittedPull;
    /** Maximum consecutive repair attempts before falling back to degraded_poll. */
    private static readonly MAX_REPAIR_ATTEMPTS;
    /** Per-link degraded-poll interval timers. */
    private readonly _degradedPollTimers;
    /** Per-link repair attempt counters. */
    private readonly _repairAttempts;
    /** Per-link active repair promises — prevents concurrent repair for the same link. */
    private readonly _activeRepairs;
    /** Per-link retry timers for failed repairs below max attempts. */
    private readonly _repairRetryTimers;
    /** Backoff schedule for repair retries (milliseconds). */
    private static readonly REPAIR_BACKOFF_MS;
    /**
     * Per-link repair context — stores ProgressGap metadata for use during
     * repair. The `resumeToken` (from `gapInfo.latestAvailable`) is used as
     * the post-repair checkpoint so the reopened subscription replays from
     * a valid boundary instead of starting live-only.
     */
    private readonly _repairContext;
    /**
     * Central helper for transitioning a link to `repairing`. Encapsulates:
     * - status change
     * - optional gap context storage
     * - repair kick-off with retry scheduling on failure
     *
     * All code paths that set `repairing` should go through this helper to
     * guarantee a future retry path.
     */
    private transitionToRepairing;
    private transitionToTerminalIncomplete;
    private setLinkOfflineStatus;
    private clearLinkRuntimeInflight;
    /**
     * Schedule a retry for a failed repair. Uses exponential backoff.
     * No-op if the link is already in `degraded_poll` (timer loop owns retries)
     * or if a retry is already scheduled.
     */
    private scheduleRepairRetry;
    /**
     * Repair a single link. Deduplicates concurrent calls via `_activeRepairs`.
     * If repair is already running for this link, returns the existing promise.
     */
    private repairLink;
    /**
     * Internal repair implementation. Runs SMT set reconciliation for a single
     * link, then attempts to re-establish live subscriptions. If repair succeeds,
     * transitions to `live`. If it fails, throws so callers (degraded_poll timer,
     * startup) can handle retry scheduling.
     */
    private doRepairLink;
    /**
     * Close pull and push subscriptions for a specific link.
     */
    private closeLinkSubscriptions;
    private closeLiveSubscription;
    private closeLocalSubscription;
    /**
     * Transition a link to `degraded_poll` and start a per-link polling timer.
     * The timer runs SMT reconciliation at a reduced frequency (30s with jitter)
     * and attempts to re-establish live subscriptions after each successful repair.
     */
    private enterDegradedPoll;
    /**
     * Tears down all live subscriptions and push listeners.
     */
    /**
     * Registers browser `online`, `offline`, and `visibilitychange` event
     * listeners to detect connectivity changes that WebSocket `close` events
     * miss (NAT timeout, network switch, sleep/wake). Safe to call in Node —
     * the guards skip registration when browser APIs are unavailable.
     */
    private startBrowserConnectivityListeners;
    /** Removes browser connectivity listeners if they were registered. */
    private stopBrowserConnectivityListeners;
    private teardownLiveSync;
    /**
     * Initialize a single replication link target: create or resume the durable
     * link, open pull + push subscriptions, and transition the link to `'live'`.
     */
    private initializeLinkTarget;
    private getOrCreateReplicationLink;
    private getReplicationLinkKey;
    private openLinkSubscriptions;
    private static supportsLiveSubscriptions;
    private markLinkLive;
    private markLinkPolling;
    private handleInitializeLinkTargetError;
    private createActiveLinkInitializationResult;
    private cleanupFailedLinkInitialization;
    /**
     * Wrapper around {@link initializeLinkTarget} that retries on DID
     * resolution failures. Newly published `did:dht` DIDs take a few
     * seconds to propagate through the DHT network. During this window,
     * the remote DWN can't resolve the DID to verify request signatures,
     * causing a 401. Retrying with exponential backoff lets the
     * propagation settle before giving up.
     */
    private initializeLinkTargetWithRetry;
    private isDidResolutionFailure;
    /** Check whether a link key belongs to a given DID. */
    private isLinkKeyForDid;
    /** Check whether this DID has any active links. */
    private hasActiveLinksForDid;
    /** Hot-add a single identity to the active live sync session. */
    private addIdentityToLiveSync;
    /** Hot-remove a single identity from the active live sync session. */
    private removeIdentityFromLiveSync;
    private tryPruneSupersededDurableLinksForRegisteredIdentity;
    private getDurableLinkIdentityKeysForRegisteredIdentity;
    private pruneSupersededDurableLinksForIdentity;
    /**
     * Opens a MessagesSubscribe WebSocket subscription to a remote DWN.
     * Incoming events are processed locally as they arrive.
     */
    private openLivePullSubscription;
    private getInitialPullCursor;
    private isValidProgressToken;
    private createLinkStalePredicate;
    private handleLivePullMessage;
    private handleLivePullEose;
    private markPullLinkOnline;
    private handleLivePullSubscriptionError;
    private handleLivePullEvent;
    private shouldSkipLivePullEvent;
    private skipOutOfScopePullEvent;
    private startPullDelivery;
    private processLivePullEvent;
    private getLivePullDataStream;
    private invalidateClosureCacheForMessage;
    private ensureClosureComplete;
    private recordClosureFailure;
    private trackRecentlyPulledMessage;
    private commitPullDelivery;
    private emitPullCheckpointAdvance;
    private handleLivePullProcessingError;
    private recordPullProcessingFailure;
    /**
     * Subscribes to the local DWN's EventLog so that writes by the user are
     * immediately pushed to the remote DWN instead of waiting for the next poll.
     */
    private openLocalPushSubscription;
    /**
     * Flushes accumulated push CIDs to remote DWNs.
     */
    private flushPendingPushes;
    private flushPendingPushesForLink;
    private takePushFlushBatch;
    private handlePushBatchResult;
    private clearSucceededPushFailures;
    private recordPermanentPushFailures;
    private requeueFailedPushes;
    private cleanupSuccessfulPushRuntime;
    private finishPushFlush;
    /** Push retry backoff schedule: immediate, 250ms, 1s, 2s, then give up. */
    private static readonly PUSH_RETRY_BACKOFF_MS;
    private static readonly ROOT_CONVERGENCE_CLEARABLE_DEAD_LETTER_CATEGORIES;
    /**
     * Re-queues a failed push batch for retry, or marks the link
     * `needsReconcile` if retries are exhausted. Bounded to prevent
     * infinite retry loops.
     */
    private requeueOrReconcile;
    private markLinkNeedsReconcile;
    private createLinkReconciler;
    private getReconcileProtocols;
    private getAuthorizationGrantIds;
    private reconcileProjectionTarget;
    private reconcileRecordsProjectionTarget;
    private reconcileProtocolSetProjectionTarget;
    private collectProtocolSetDiffPlan;
    private getProtocolRoots;
    private applyProtocolSetDiffPlan;
    private applyProjectedDiff;
    private pullProjectedRemoteDiff;
    private pushProjectedLocalDiff;
    private pullRemoteDiffEntries;
    private pullProjectedDependencyHints;
    private verifyProjectedDependencies;
    private verifyProjectedProtocolConfigDependencies;
    private static indexEntriesWithMessage;
    private static recordsInitialWritesFromVerifiedDependencies;
    private collectProjectedRecordsInitialWriteDependencies;
    private verifyRecordsInitialWriteCandidate;
    private toAuthenticatedRecordsInitialWriteDependency;
    private collectProjectedProtocolConfigCandidates;
    private verifyProtocolConfigCandidate;
    private verifyProtocolConfigCandidateMessage;
    private toAuthenticatedProtocolConfigDependency;
    private static projectedDependencyCidsMatch;
    private static recordsWriteProtocol;
    private static recordsDeleteRecordId;
    private static protocolConfigRootRecordsWrite;
    private static protocolsConfigureProtocol;
    private static protocolsConfigureProtocolFromGenericMessage;
    private static protocolsConfigureIsNotNewerThanRecordsWrite;
    private static protocolsConfigureUses;
    private static hasMessage;
    private static isRecordsWriteProtocolMessage;
    private static isRecordsWriteMessage;
    private static isRecordsDeleteMessage;
    private static isProtocolsConfigureDefinitionMessage;
    private static hasProtocolsConfigureDefinition;
    private static isProtocolsConfigureDefinition;
    private static filterProtocolConfigClosure;
    private static groupGoverningProtocolConfigCandidatesByProtocol;
    private static isProtocolConfigCandidateAtLeastAsNew;
    private static takeNextUnvisitedProtocol;
    private static acceptProtocolConfigCandidates;
    private static queueUnvisitedProtocols;
    private static protocolSetScopeForProjectedDependencies;
    private static projectedDependencyProtocols;
    private static hasDependencyPayloadBytes;
    private static dedupeDependencyEntries;
    private pushLocalDiffEntries;
    private verifyProtocolSetConvergence;
    private static dedupeRemoteEntries;
    private static dedupeStrings;
    private clearRootConvergenceDeadLettersForScope;
    /** Active reconcile timers, keyed by link key. */
    private readonly _reconcileTimers;
    /** Active reconcile operations, keyed by link key (dedup). */
    private readonly _reconcileInFlight;
    /**
     * Schedule a per-link reconciliation after a short debounce. Coalesces
     * repeated requests for the same link.
     */
    private scheduleReconcile;
    /**
     * Run SMT reconciliation for a single link. Deduplicates concurrent calls.
     * On success, clears `needsReconcile`. On failure, schedules retry.
     */
    private reconcileLink;
    /**
     * Internal reconciliation implementation for a single link. Runs the
     * same SMT diff + pull/push that `sync()` does, but scoped to one link.
     */
    private doReconcileLink;
    private getOrCreatePushRuntime;
    /**
     * Build the runtime key for a replication link.
     *
     * Live-mode subscription methods (`openLivePullSubscription`,
     * `openLocalPushSubscription`) receive `linkKey` directly and never
     * call this. The remaining callers are poll-mode `sync()` and the
     * live-mode startup/error paths that already have a projection ID and
     * authorization epoch.
     */
    private buildLinkKey;
    /**
     * Extracts a ReadableStream from a MessageEvent if it contains a
     * RecordsWrite with data — either as an inline `encodedData` field
     * (for records <= 30 KB) or as a pre-existing data stream.
     */
    private extractDataStream;
    /**
     * Returns the hex-encoded default (empty-subtree) hash for a given depth.
     * Lazily initializes the cache on first call.
     */
    private getDefaultHashHex;
    /**
     * Parse a bit prefix string (e.g. "0110101") into a boolean array
     * for the StateIndex API. Each '1' maps to `true` (right child),
     * each '0' maps to `false` (left child).
     */
    private static parseBitPrefix;
    /**
     * Access the local DWN's StateIndex directly, bypassing the `processMessage`
     * pipeline. The sync engine runs in the same process as the local DWN, so
     * there is no need for message signing, schema validation, or authentication
     * when querying our own state.
     *
     * Returns `undefined` in remote mode (no in-process DWN). The local methods
     * fall back to `processRequest` in that case, routing through RPC to the
     * local DWN server.
     */
    private get stateIndex();
    /**
     * Get the SMT root hash from the local DWN.
     *
     * In local mode: queries the StateIndex directly (fast, no processMessage overhead).
     * In remote mode: constructs a signed MessagesSync message and routes through RPC.
     *
     * Returns a hex-encoded root hash string.
     */
    private getLocalRoot;
    /**
     * Get the SMT root hash from a remote DWN via a MessagesSync 'root' action.
     * Returns a hex-encoded root hash string.
     */
    private getRemoteRoot;
    private getLocalProjectedRoot;
    private getRemoteProjectedRoot;
    /**
     * Compute the diff between local and remote in a single HTTP round-trip.
     *
     * 1. Walk the local SMT directly (no processMessage) to collect subtree
     *    hashes at `MAX_DIFF_DEPTH`.
     * 2. Send a single `MessagesSync action:'diff'` to the remote with all
     *    non-empty subtree hashes.
     * 3. The remote compares and returns `onlyRemote` (with inline messages)
     *    and `onlyLocal` prefixes.
     * 4. Enumerate local leaves for the `onlyLocal` prefixes directly.
     *
     * This replaces `walkTreeDiff()` which required one HTTP call per tree node.
     */
    private diffWithRemote;
    private diffProjectedWithRemote;
    private diffRemoteMessages;
    /**
     * Walk the local SMT to a given depth and collect non-empty subtree hashes.
     * Returns a `{ prefix: hexHash }` map. Empty subtrees (matching the default
     * hash) are omitted.
     *
     * Uses direct StateIndex access in local mode. In remote mode, falls back
     * to `getLocalSubtreeHash` which routes through RPC.
     */
    private collectLocalSubtreeHashes;
    private collectLocalProjectedSubtreeHashes;
    /**
     * Get the subtree hash at a given bit prefix from the local DWN.
     *
     * In local mode: queries the StateIndex directly.
     * In remote mode: constructs a signed MessagesSync message and routes through RPC.
     */
    private getLocalSubtreeHash;
    private getLocalProjectedSubtreeHash;
    /**
     * Get all leaf messageCids under a given prefix from the local DWN.
     *
     * In local mode: queries the StateIndex directly.
     * In remote mode: constructs a signed MessagesSync message and routes through RPC.
     */
    private getLocalLeaves;
    private getLocalProjectedLeaves;
    /**
     * Fetches missing messages from the remote DWN and processes them locally
     * in dependency order (topological sort).
     *
     * When prefetched entries are provided (from the batched diff response),
     * they are processed directly without additional HTTP round-trips.
     * Only `messageCids` that were NOT prefetched are fetched individually.
     */
    private pullMessages;
    private acceptPulledSyncEntry;
    private resolvePulledDeleteInitialWrite;
    private static findInitialWriteByRecordId;
    private findInitialWriteInPullBatch;
    /**
     * Evicts expired entries from the echo-loop suppression cache.
     * Also enforces the size cap by evicting oldest entries first.
     */
    private evictExpiredEchoEntries;
    /**
     * Checks whether a CID was recently pulled from a specific remote endpoint
     * and should not be pushed back to that same endpoint (echo-loop suppression).
     * Does not suppress pushes to other endpoints — multi-provider fan-out works.
     */
    private isRecentlyPulled;
    /**
     * Reads missing messages from the local DWN and pushes them to the remote DWN
     * in dependency order (topological sort).
     */
    private pushMessages;
    /**
     * Delegate to the standalone `topologicalSort` function.
     * Tests call `SyncEngineLevel.topologicalSort(...)` so this static method must remain.
     */
    static topologicalSort<T extends {
        message: GenericMessage;
    }>(messages: T[]): T[];
    /**
     * Clear dead letter entries scoped to a specific sync link. Matches on
     * (tenantDid, remoteEndpoint, protocol) so that repairing protocol A
     * does not erase still-valid failures for protocol B on the same remote.
     * When `protocol` is undefined (full-tenant link), clears entries that
     * also have no protocol.
     */
    private clearDeadLettersForLink;
    private clearRootConvergenceDeadLetters;
    /**
     * Build a compound dead letter key. Different remotes can fail the same CID
     * for different reasons, so the key includes the remote endpoint.
     */
    private static deadLetterKey;
    recordDeadLetter(params: {
        messageCid: string;
        tenantDid: string;
        remoteEndpoint?: string;
        protocol?: string;
        category: DeadLetterCategory;
        errorCode?: string;
        errorDetail: string;
    }): Promise<void>;
    getFailedMessages(tenantDid?: string): Promise<DeadLetterEntry[]>;
    clearFailedMessage(messageCid: string, remoteEndpoint?: string): Promise<boolean>;
    clearAllFailedMessages(tenantDid?: string): Promise<void>;
    getSyncHealth(): Promise<SyncHealthSummary>;
    private getCurrentDurableLinkIdentityKeys;
    private getDurableLinkIdentityKey;
    private static durableLinkIdentityKey;
    private static isUnhealthyLinkStatus;
    /**
     * Returns the list of sync targets: one canonical projection target per
     * registered DID and resolved DWN endpoint.
     * Results are cached for up to 30 seconds to avoid redundant DID resolution
     * on every sync tick. The cache is invalidated when identities are registered,
     * unregistered, or updated.
     */
    private getSyncTargets;
}
//# sourceMappingURL=sync-engine-level.d.ts.map