/**
 * Redis Utilities for NeuroLink
 * Helper functions for Redis storage operations
 */
import type { ChatMessage, RedisClient, RedisConversationObject, RedisStorageConfig } from "../types/index.js";
/**
 * Redact the userinfo of every `scheme://user:pass@host` in `text`.
 *
 * Deliberately not a regex. `/:\/\/[^@]+@/` and its relatives backtrack
 * quadratically on input with many `://` and no `@` (CodeQL
 * js/polynomial-redos), and the URL here can come straight from a caller —
 * `RedisStorageConfig.url` reaches this through `RedisArtifactStore` and the
 * conversation memory manager. One forward pass, every character visited a
 * bounded number of times.
 *
 * Redacts everything before the LAST `@` of the authority, so a password
 * containing `@` is still hidden. A URL with only a user name is redacted
 * too; the old regexes let that through.
 */
export declare function redactUrlCredentials(text: string): string;
/**
 * Get a pooled Redis connection. Multiple callers with the same host:port:db
 * share a single connection, reducing connection count.
 */
export declare function getPooledRedisClient(config: Required<RedisStorageConfig>): Promise<RedisClient>;
/**
 * Release a pooled Redis connection. Only closes when refCount reaches 0.
 */
export declare function releasePooledRedisClient(config: Required<RedisStorageConfig>): Promise<void>;
/**
 * Get stats about the connection pool
 */
export declare function getPoolStats(): Array<{
    key: string;
    refCount: number;
    isOpen: boolean;
}>;
/**
 * Creates a Redis client with the provided configuration
 */
export declare function createRedisClient(config: Required<RedisStorageConfig>): Promise<RedisClient>;
/**
 * Generates a Redis key for session messages
 */
export declare function getSessionKey(config: Required<RedisStorageConfig>, sessionId: string, userId?: string): string;
/**
 * Generates a Redis key for user sessions mapping
 */
export declare function getUserSessionsKey(config: Required<RedisStorageConfig>, userId: string): string;
/**
 * Serializes conversation object for Redis storage
 */
export declare function serializeConversation(conversation: RedisConversationObject): string;
/**
 * True for a complete `ChatMessage` — `id` included, because callers keying
 * summary pointers and condensation groups off it are entitled to find one.
 */
export declare function isStoredChatMessage(value: unknown): value is ChatMessage;
/**
 * Coerce a stored entry into a complete `ChatMessage`, or `undefined` when its
 * shape is unusable.
 *
 * Shared by BOTH read paths on purpose. Inline blobs have always been
 * validated; once messages moved into the companion LIST the split read
 * bypassed that check, so `null`, a number or a bare string survived
 * `JSON.parse` and reached callers that dereference `.role`, `.content` and
 * `.metadata`. The two storage formats must offer the same read guarantee.
 *
 * `id` is a separate matter: it is required on `ChatMessage`, but no read path
 * has ever enforced it, so history written before it existed carries none.
 * Rejecting those records would empty otherwise-healthy sessions, so a missing
 * id is backfilled rather than fatal — prefixed, so a synthesized id is never
 * mistaken for one an existing pointer could reference.
 */
export declare function normalizeStoredMessage(value: unknown): ChatMessage | undefined;
/**
 * Deserializes conversation object from Redis storage
 */
export declare function deserializeConversation(data: string | Buffer | null): RedisConversationObject | null;
/**
 * Checks if Redis client is healthy
 */
export declare function isRedisHealthy(client: RedisClient): Promise<boolean>;
/**
 * Scan Redis keys matching a pattern without blocking the server
 * This is a non-blocking alternative to the KEYS command
 *
 * @param client Redis client
 * @param pattern Pattern to match keys (e.g. "prefix:*")
 * @param batchSize Number of keys to scan in each iteration (default: 100)
 * @returns Array of keys matching the pattern
 */
export declare function scanKeys(client: RedisClient, pattern: string, batchSize?: number): Promise<string[]>;
/**
 * Get normalized Redis configuration with defaults
 */
export declare function getNormalizedConfig(config: RedisStorageConfig): Required<RedisStorageConfig>;
/**
 * Suffix of the companion LIST key holding a session's messages.
 *
 * Every `storeConversationTurn` used to re-serialize and SET the ENTIRE
 * conversation, so per-turn write cost grew with history size. Measured
 * against local Redis: 200 turns of 2KB messages stayed flat at 2ms, but 400
 * turns of 20KB messages (~16MB blob) went 2ms -> 61ms, a 30x degradation on
 * exactly the agentic tool-output profile. Splitting messages into an
 * append-only LIST makes the per-turn write O(1) in conversation size.
 */
export declare const MESSAGES_KEY_SUFFIX = ":msgs";
/**
 * Marker on a stored blob meaning "messages live in the companion LIST".
 * A blob WITHOUT it is a legacy record whose inline `messages` array is
 * authoritative — it is read as-is and converted on its next write, which is
 * what makes this migration backward compatible.
 */
export declare const MESSAGES_IN_LIST_MARKER = "__nlMessagesInList";
/** Redis key holding a session's messages as an append-only LIST. */
export declare function getSessionMessagesKey(config: Required<RedisStorageConfig>, sessionId: string, userId?: string): string;
/**
 * True for a companion messages LIST key.
 *
 * Scan-then-GET paths (`getStats`, session listing) match `${keyPrefix}*`, so
 * they now also see these LIST keys — and `GET` on a LIST raises WRONGTYPE.
 * They must filter with this, exactly as they already skip `:sessions` index
 * keys.
 */
export declare function isSessionMessagesKey(key: string): boolean;
/** Serialize the conversation WITHOUT its messages, flagged for split reads. */
export declare function serializeConversationMetadata(conversation: RedisConversationObject): string;
/** True when a deserialized blob's messages live in the companion LIST. */
export declare function usesSplitMessageStorage(conversation: RedisConversationObject | null | undefined): boolean;
/** Parse LRANGE entries, skipping any single entry that is not usable. */
export declare function parseStoredMessages(entries: string[]): ChatMessage[];
/** Encode messages for RPUSH. */
export declare function encodeStoredMessages(messages: ChatMessage[]): string[];
