/**
 * RedisStore — Redis-backed `MemoryStore` adapter (peer-dep `ioredis`).
 *
 * Import from the canonical subpath (the `agentfootprint/memory-redis` alias
 * was removed in 4.0.0):
 *
 *   import { RedisStore } from 'agentfootprint/memory';
 *
 *   const store = new RedisStore({ url: 'redis://localhost:6379' });
 *
 * Pattern: Adapter (GoF) — translates the `MemoryStore` interface onto
 *          Redis primitives (key/value for entries, set for signatures,
 *          hash for feedback aggregates).
 * Role:    Outer ring. Lazy-requires `ioredis`; no runtime cost when
 *          another adapter is in use.
 * Emits:   N/A (storage adapters don't emit; recorders observe the
 *          memory pipeline that calls them).
 *
 * Vector search (`search()`) is NOT implemented in this adapter — RedisSearch
 * is a separate Redis module with its own API surface. A `RedisSearchStore`
 * may ship in a future release. RAG users with v2.3 should use
 * `InMemoryStore` until the search-capable adapter lands.
 *
 * Concurrency model:
 *   - `put` / `putMany` use simple SET / pipelined SET (last-write-wins).
 *   - `putIfVersion` uses a small Lua script for atomic version compare-and-swap.
 *   - Multi-writer correctness ⇒ prefer `putIfVersion` in stage code.
 */
import type { ListOptions, ListResult, MemoryStore, PutIfVersionResult } from '../../memory/store/types.js';
import type { MemoryEntry } from '../../memory/entry/index.js';
import type { MemoryIdentity } from '../../memory/identity/index.js';
import { type MemoryWriteOptions } from '../../memory/store/writeOptions.js';
/**
 * Minimal `ioredis` client surface this adapter needs. Defined locally so
 * we don't take a hard import on `ioredis` (lazy peer-dep) and tests can
 * inject a mock implementation via `_client`.
 */
export interface RedisLikeClient {
    get(key: string): Promise<string | null>;
    set(key: string, value: string, ...args: ReadonlyArray<string | number>): Promise<unknown>;
    del(...keys: ReadonlyArray<string>): Promise<number>;
    sadd(key: string, ...members: ReadonlyArray<string>): Promise<number>;
    srem(key: string, ...members: ReadonlyArray<string>): Promise<number>;
    sismember(key: string, member: string): Promise<number>;
    smembers(key: string): Promise<readonly string[]>;
    hgetall(key: string): Promise<Record<string, string>>;
    hset(key: string, ...args: ReadonlyArray<string | number>): Promise<number>;
    scan(cursor: string, match: 'MATCH', pattern: string, count: 'COUNT', n: number): Promise<readonly [string, readonly string[]]>;
    eval(script: string, numKeys: number, ...args: ReadonlyArray<string | number>): Promise<unknown>;
    pipeline(): RedisLikePipeline;
    quit(): Promise<unknown>;
}
export interface RedisLikePipeline {
    set(key: string, value: string, ...args: ReadonlyArray<string | number>): RedisLikePipeline;
    sadd(key: string, ...members: ReadonlyArray<string>): RedisLikePipeline;
    exec(): Promise<unknown>;
}
export interface RedisStoreOptions {
    /**
     * Connection URL (e.g. `redis://default:password@host:6379/0`). Required
     * unless `_client` is supplied.
     */
    readonly url?: string;
    /**
     * Pre-built `ioredis` client. Use this when the host app already manages
     * a Redis connection pool. Adapter does NOT call `quit()` on a borrowed
     * client — caller owns the lifecycle.
     */
    readonly client?: RedisLikeClient;
    /** Key prefix for namespace isolation across apps sharing one Redis. Default `'agentfootprint'`. */
    readonly prefix?: string;
    /**
     * SCAN page size when iterating keys. Default 100. Larger = fewer
     * round-trips but more memory per response. Adapter never uses `KEYS *`
     * (which blocks Redis).
     */
    readonly scanCount?: number;
    /**
     * @internal Test injection point. When provided, skips the SDK require.
     */
    readonly _client?: RedisLikeClient;
}
/**
 * Redis-backed `MemoryStore`. Implements every method except `search()`.
 *
 * @throws when `ioredis` is not installed and no `_client` is supplied.
 */
export declare class RedisStore implements MemoryStore {
    /**
     * **No.** This adapter implements no `search()` at all — vector search over
     * Redis is RedisSearch, a separate module with its own API, and a
     * `RedisSearchStore` may ship later.
     *
     * Declared (8.19.0) rather than left to the absent method, so the corpus
     * builders refuse it at the CALL, before a corpus is embedded and paid for.
     * Without the declaration the same mistake still failed — one layer later,
     * when `defineRAG` refused the store for having no `search()`, after the
     * whole index had been built and billed.
     */
    readonly supportsVectorSearch = false;
    private readonly client;
    private readonly prefix;
    private readonly scanCount;
    private readonly ownsClient;
    private closed;
    constructor(options?: RedisStoreOptions);
    /**
     * A literal string → a `SCAN MATCH` pattern that matches only itself.
     *
     * A key is stored bytes; a MATCH pattern is a GLOB, and Redis reads it with
     * `stringmatchlen`, whose metacharacters are `*`, `?`, `[` and `\` (a `]`
     * outside a class is an ordinary character, so it needs no escape). The
     * namespace deliberately does NOT escape `*` — it is a legal character in a
     * name and escaping it in the KEY would re-key every store that has one —
     * so it has to be escaped HERE, at the one place a name is used as a
     * pattern instead of as data.
     *
     * Without this, `forget({ conversationId: '*' })` scanned `af:_/_/*:*`,
     * which glob-matches every other scope's keys — and `forget` DELETES what
     * it scans. That is cross-tenant destruction from one ordinary-looking id.
     *
     * The backslash is escaped FIRST, for the same reason the namespace encoder
     * escapes `%` first: it introduces every escape that follows, so a name
     * containing `\*` must not come out as an escaped `*`.
     */
    private static globLiteral;
    private nsKey;
    private entryKey;
    private indexKey;
    private sigKey;
    private feedbackKey;
    get<T = unknown>(identity: MemoryIdentity, id: string): Promise<MemoryEntry<T> | null>;
    /**
     * Write options: none honoured — Redis keeps the entry and derives nothing
     * from it, so `extraction` and `namespaceScope` are refused by name.
     */
    put<T = unknown>(identity: MemoryIdentity, entry: MemoryEntry<T>, options?: MemoryWriteOptions): Promise<void>;
    putMany<T = unknown>(identity: MemoryIdentity, entries: readonly MemoryEntry<T>[], options?: MemoryWriteOptions): Promise<void>;
    /**
     * Optimistic concurrency via a small Lua script — atomic
     * compare-and-swap on the JSON-encoded `version` field.
     */
    putIfVersion<T = unknown>(identity: MemoryIdentity, entry: MemoryEntry<T>, expectedVersion: number, options?: MemoryWriteOptions): Promise<PutIfVersionResult>;
    list<T = unknown>(identity: MemoryIdentity, options?: ListOptions): Promise<ListResult<T>>;
    delete(identity: MemoryIdentity, id: string): Promise<void>;
    seen(identity: MemoryIdentity, signature: string): Promise<boolean>;
    recordSignature(identity: MemoryIdentity, signature: string): Promise<void>;
    feedback(identity: MemoryIdentity, id: string, usefulness: number): Promise<void>;
    getFeedback(identity: MemoryIdentity, id: string): Promise<{
        average: number;
        count: number;
    } | null>;
    /**
     * GDPR — drop every key under this identity's namespace.
     *
     * Only the trailing `*` is a wildcard: the prefix and the namespace are
     * matched LITERALLY (see {@link RedisStore.globLiteral}), so an id that
     * happens to contain a glob character erases its own scope and nobody
     * else's.
     */
    forget(identity: MemoryIdentity): Promise<void>;
    /**
     * Close the underlying Redis connection — only when this adapter
     * owns it. Borrowed clients (passed via `client` option) are left to
     * the caller. Idempotent.
     */
    close(): Promise<void>;
    private ensureOpen;
}
//# sourceMappingURL=redis.d.ts.map