/**
 * memoryBankStore — the `MemoryStore` port over Vertex AI **Memory Bank**.
 *
 *   import { memoryBankStore } from 'agentfootprint/memory';
 *
 *   const store = memoryBankStore({
 *     project: 'my-project',
 *     location: 'us-central1',
 *     reasoningEngine: '1234567890',
 *   });
 *
 * ── Read this before you write anything into it ─────────────────────────────
 * Memory Bank is a **natural-language** memory service, not a key-value store
 * and not a vector database. A `Memory` is a `fact` string plus an immutable
 * `scope`; retrieval takes a QUESTION IN WORDS, embeds it on Google's side and
 * ranks there. Three consequences, each of which has a silent-failure mode
 * that this adapter turns into something you can see:
 *
 *  1. **It never ranks the vectors you wrote.** `supportsVectorSearch` is
 *     `false` and `ranksBy` is `'server-text'`, so the corpus builders
 *     (`indexCorpus`, `indexFolder`, `indexDocuments`) refuse this store by
 *     name instead of embedding a whole corpus, reporting success, and leaving
 *     it unreachable forever. An `embedding` on an entry handed to `put()` is
 *     **not stored** — there is nowhere to put it and nothing that would read
 *     it — and the two declarations above are how this adapter says so before
 *     you spend anything.
 *
 *  2. **The retrieval score is a DISTANCE, and smaller is closer.** The port's
 *     `ScoredEntry.score` is a cosine similarity, where HIGHER is closer.
 *     Passed through unconverted, `search()` would return the LEAST relevant
 *     memories first with a confident-looking number in the right range —
 *     which no threshold and no eyeball can separate from a working search.
 *     See {@link MemoryBankStore.search} for exactly what this adapter does
 *     instead, and why it refuses `minScore` rather than reinterpreting it.
 *
 *  3. **`scope` is an exact match and immutable once written.** A retrieval
 *     whose scope is a subset of a memory's scope returns NOTHING — not a
 *     superset, not a partial match, nothing. So the scope convention has to
 *     be right before the first write, because it cannot be changed after it
 *     and a bank written under the wrong one is poisoned permanently. See
 *     {@link MemoryBankStoreOptions.scopeFor}.
 *
 * ── How a memory is ADDRESSED, and why it is not just the entry id ──────────
 * A memory's resource name is `<engine>/memories/<resource id>`, and that
 * resource id is composed from **the resolved scope and the entry id
 * together** — never the entry id alone.
 *
 * The reason is that entry ids in this library are deliberately deterministic
 * and identity-free: `msg-<turn>-<index>`, `fact:<key>`, `snap-<turn>`. Two
 * people talking to the same agent produce the SAME entry ids, so an address
 * built from the id alone is one row for both of them — and since a resource
 * name addresses a row directly, the second writer's `fact` lands on the first
 * writer's row while the immutable `scope` stays the first writer's. What comes
 * back is one tenant reading another tenant's private fact, one tenant's own
 * write invisible to them, and `forget()` finding nothing to erase. Every
 * sibling store in this library namespaces by identity (`s3Vectors` keys on
 * `<namespace>#<id>`, `pgVector` on `(namespace, id)`, `InMemoryStore` on a map
 * per namespace); this one does the same thing, keyed on the SCOPE rather than
 * the raw identity so that a widened `scopeFor` widens sharing exactly as much
 * as it widens retrieval, and not one row more.
 *
 * The composed address is a partition, not the boundary itself. The boundary is
 * the stored `scope`, which is re-checked on the way back from every read AND
 * before every overwrite — so even an address collision is refused rather than
 * written through.
 *
 * ── The retrieved NAME is not the name you wrote ────────────────────────────
 * A live field trial on the raw service found this and it is worth stating,
 * because it is exactly the assumption an adapter is tempted to make: `create`
 * and `list` came back with the caller-chosen memory ids, while SIMILARITY
 * RETRIEVAL answered with generated numeric resource names for the very same
 * facts (FINDINGS "Agent Runtime Memory Bank"). Anything reading an entry id
 * out of `memory.name` would therefore work perfectly on `list()` and hand back
 * unusable ids from `search()` — ids that no `get()` or `delete()` could find.
 *
 * This adapter never does that: `toEntry` reads the id from the metadata this
 * library wrote, and the resource name is carried only as
 * `entry.metadata.resourceName`, for looking at. The same trial also confirmed
 * exact-match scope semantics (a partial `{tenant}` scope retrieved nothing)
 * and the distance-not-similarity ranking that {@link MemoryBankStore.search}
 * converts.
 *
 * ── Writes are long-running operations ──────────────────────────────────────
 * `create`, `patch` and `delete` all answer with an Operation rather than the
 * resource — verified against the installed SDK's own return types. Every
 * write here waits for `done` before returning, because a `put` that came back
 * early followed by a `get` is a race whose failure mode is "no data", and
 * nobody can tell that from a memory that was never written.
 *
 * ── What has no primitive here, and is therefore refused ────────────────────
 * `putIfVersion`, `seen`, `recordSignature`, `feedback` and `getFeedback` have
 * no counterpart in this service: a `Memory` carries no etag and there is no
 * dedup or feedback surface. The sibling AgentCore adapter emulates them
 * in-process; this one refuses them by name, and the difference is deliberate.
 * A store you reach for BECAUSE it is shared across a fleet is the worst place
 * for a per-process shadow: `seen()` would answer `false` in the second
 * container for a signature the first one recorded, and an emulated
 * `putIfVersion` across two writers is a lost-update generator that reports
 * `{ applied: true }` to both. A refusal you read once beats a correctness bug
 * you never find.
 *
 * Pattern: Adapter (GoF) — `MemoryStore` onto `reasoningEngines.memories`,
 * through the shared REST client in `adapters/google/aiPlatform.ts`.
 */
import type { MemoryEntry } from '../../memory/entry/index.js';
import type { MemoryIdentity } from '../../memory/identity/index.js';
import { type MemoryWriteOptions } from '../../memory/store/writeOptions.js';
import type { ListOptions, ListResult, MemoryStore, PutIfVersionResult, ScoredEntry, SearchOptions } from '../../memory/store/types.js';
import { type AiPlatformConnection } from '../google/aiPlatform.js';
/**
 * The most JSON one carried field may be, in characters.
 *
 * Memory Bank metadata values are typed scalars and this column has NOT
 * measured the service's own ceiling on a string one, so the bound is this
 * adapter's own and says so where it refuses. It is a refusal rather than a
 * truncation on purpose: provenance that came back shortened would be
 * provenance nobody could tell was shortened.
 */
export declare const MAX_CARRIED_JSON = 8192;
/** The scope map an identity resolves to. Keys and values are both strings. */
export type MemoryScope = Readonly<Record<string, string>>;
/** Options for {@link memoryBankStore}. */
export interface MemoryBankStoreOptions extends AiPlatformConnection {
    /**
     * Map this library's identity tuple onto Memory Bank's `scope` — **the one
     * decision that cannot be taken back.**
     *
     * The default is the full tuple:
     *
     * ```
     *   { tenant: '<tenant|_>', principal: '<principal|_>', conversation: '<conversationId>' }
     * ```
     *
     * which is the same isolation every other store in this library enforces
     * (`identityNamespace` composes exactly these three), so agent code behaves
     * identically whichever column it runs on. That consistency is why it is the
     * default even though it is the NARROWEST useful choice.
     *
     * **What it costs, stated plainly.** Because scope matching is exact, a
     * memory written under a conversation is retrievable only within that
     * conversation. If what you want from a memory bank is "remember this person
     * across their conversations" — which is usually the point — widen it here:
     *
     * ```ts
     *   import { encodeIdentityField } from 'agentfootprint/memory';
     *
     *   scopeFor: (identity) => ({
     *     tenant: encodeIdentityField(identity.tenant),
     *     principal: encodeIdentityField(identity.principal),
     *   })
     * ```
     *
     * Encode the fields rather than writing `identity.tenant ?? '_'`: a raw `_`
     * is what "no tenant" spells, so an un-encoded widening hands the anonymous
     * scope to anyone whose tenant is literally `_`. The encoder returns
     * ordinary ids byte for byte and only escapes the ones that would collide.
     *
     * **And why it is worth getting right the first time.** `Memory.scope` is
     * immutable. Memories already written keep the scope they were written with,
     * and a later retrieval under a different convention will not find them —
     * not with a warning, not with a partial match, but with an empty result
     * that looks exactly like "this person has told us nothing". Changing the
     * convention on a live bank means re-writing every memory in it.
     *
     * Values may not contain `*`; this adapter replaces any it is handed and
     * refuses an empty scope outright, because a scope of `{}` is the one value
     * that matches every other empty-scoped memory in the bank regardless of who
     * wrote it.
     */
    readonly scopeFor?: (identity: MemoryIdentity) => MemoryScope;
    /**
     * How long a memory lives, as a duration string the API accepts (`'86400s'`).
     * Omit and memories do not expire.
     *
     * A `MemoryEntry.ttl` is a unix TIMESTAMP and this is a DURATION; the two
     * are different quantities and the entry's own is honoured per write, so
     * this is only the default for entries that name none.
     */
    readonly ttl?: string;
    /**
     * How long a write waits for its long-running operation before refusing.
     * Default {@link DEFAULT_OPERATION_TIMEOUT_MS} (30s).
     */
    readonly operationTimeoutMs?: number;
    /**
     * How many rows a `list()` page carries when the caller names no limit.
     * Default 20. The service's own ceiling is 100 and it silently coerces
     * anything larger, so this adapter clamps rather than letting a request for
     * 500 come back as 100 with no explanation.
     */
    readonly pageSize?: number;
}
/** The service's own ceiling on a page or a top-k. Larger values are coerced. */
export declare const MAX_PAGE_SIZE = 100;
/**
 * A `MemoryStore` over Vertex AI Memory Bank.
 *
 * **Status: field-validated on the data plane (2026-08-14).** An independent
 * trial ran THIS class against a real Memory Bank and every one of these
 * answered a live request: the honest `supportsVectorSearch: false` /
 * `ranksBy: 'server-text'` declarations; cross-conversation recall under a
 * widened `scopeFor`; two identities using the SAME entry id without collision
 * or disclosure; string and structured values; opaque pagination cursors; tier
 * filtering; text similarity that found the right marker, excluded the other
 * identity, returned finite distances and converted them to correctly ORDERED
 * scores; overwrite advancing value and version; a scoped delete leaving the
 * other identity intact; `forget()` erasing across the widened scope; and the
 * five unsupported operations refusing by name rather than pretending.
 *
 * The same trial found the one thing that was NOT faithful — `source` and
 * caller `metadata` were accepted and silently dropped — which 9.30.0 fixes by
 * carrying them (see `toMemory`). That fix is tested here and has not itself
 * been re-run in a live project.
 */
export declare class MemoryBankStore implements MemoryStore {
    /**
     * **No.** `search()` exists here, but it is Memory Bank's own retrieval:
     * Google embeds and ranks on its side, over the `fact` strings this store
     * wrote, and never over an `embedding` handed to `put()`. Embeddings are not
     * stored at all.
     *
     * Declared because a method's presence could not say that — and because the
     * sibling column already paid for the lesson once, with a corpus that
     * indexed, billed, reported success, and was unreachable forever.
     */
    readonly supportsVectorSearch = false;
    /**
     * The query form this store takes: **words, not a vector.** `search()` reads
     * {@link SearchOptions.text} and refuses by name without it. A retriever
     * built over this store therefore needs no `Embedder`, and wiring one would
     * be spend on a vector discarded on arrival.
     */
    readonly ranksBy: "server-text";
    private readonly memories;
    private readonly scope;
    private readonly scopeFor;
    private readonly operationTimeoutMs;
    private readonly pageSize;
    private readonly defaultTtl;
    private closed;
    constructor(options: MemoryBankStoreOptions);
    /**
     * One memory by id.
     *
     * Two independent things keep this from reading somebody else's memory, and
     * both are deliberate. The **address** carries the scope, so another
     * identity's row for the same entry id is a different resource name that
     * simply is not there. And the **stored scope is re-checked on the way
     * back**, because a resource name addresses a memory directly and a `get`
     * alone would happily read another tenant's row for anyone who could guess a
     * name. A memory whose scope is not this identity's answers `null` — the same
     * `null` a missing one answers, because "exists but not yours" is an oracle
     * for which ids are real.
     */
    get<T = unknown>(identity: MemoryIdentity, id: string): Promise<MemoryEntry<T> | null>;
    /**
     * A page of this identity's memories, in no particular order.
     *
     * It rides `retrieve` with `simpleRetrievalParams` rather than `memories.list`
     * — deliberately. `list` filters with AIP-160 over the resource's own fields,
     * and whether that filter language can express an exact scope match is not
     * something this adapter is willing to guess at for the call that decides
     * which memories a caller can see. `retrieve` takes the scope as a structured
     * field with semantics the SDK states outright, so the isolation is the
     * service's rather than a filter string's.
     *
     * `tiers` filtering is applied to what comes back, since a tier is this
     * library's own metadata and not something the service ranks on.
     */
    list<T = unknown>(identity: MemoryIdentity, options?: ListOptions): Promise<ListResult<T>>;
    /**
     * Memory Bank's own semantic retrieval — **text in, and the ranking trap
     * handled rather than passed on.**
     *
     * ── It takes WORDS, not the vector ───────────────────────────────────────
     * Google embeds and ranks server-side, so the `query` vector this method is
     * handed cannot be sent anywhere. The query it needs travels in
     * {@link SearchOptions.text}, and omitting it is refused by name — returning
     * `[]` would read as "no matches" when it means "wrong query form".
     *
     * ── The score, and what this adapter refuses to pretend ──────────────────
     * The service reports a **distance**, in its own words "smaller values
     * indicate more similar memories". The port's score is a cosine similarity,
     * where higher is closer. Two things follow, and both are decisions:
     *
     *   • The distance is **converted**, never forwarded: `score = 1 / (1 + d)`,
     *     which is strictly decreasing in `d`, so **the ordering is right** —
     *     the closest memory has the highest score, which is the whole point.
     *     The raw distance is carried on `entry.metadata.distance` so nothing is
     *     hidden and a caller who knows the metric can do better.
     *
     *   • **`minScore` is REFUSED by name**, because that number is calibrated
     *     for a cosine similarity and this scale is not one. Silently applying a
     *     cosine threshold to a converted distance is precisely the failure this
     *     library refuses elsewhere: a number that READS like a similarity, in
     *     the right range, that no threshold and no eyeball can separate from a
     *     real one. The sibling S3 Vectors adapter refuses a non-cosine index
     *     for the same reason; here the metric is Google's and cannot be
     *     changed, so the threshold is what goes rather than the store.
     *
     * ── One more thing the service requires ──────────────────────────────────
     * Similarity search only works if the reasoning engine was configured with a
     * similarity-search config. Without it the service refuses the call, and
     * that refusal is passed through with its status — it is a setup fact, not a
     * bug in the query.
     *
     * @throws when `options.text` is absent, or `options.minScore` is present.
     */
    search<T = unknown>(identity: MemoryIdentity, query: readonly number[], options?: SearchOptions): Promise<readonly ScoredEntry<T>[]>;
    /**
     * Write one memory, waiting for the service to say it landed.
     *
     * ── Read-then-write, and what the read is FOR ────────────────────────────
     * This costs one `get` before the write, and that read is not an
     * optimisation — it is the tenant check. A `patch` names a resource
     * directly and the service does not ask whose it is, so a patch sent
     * without looking is a write this adapter cannot promise landed on its own
     * row. The address already carries the scope (see the module header), so
     * the row under this name is ours in every ordinary run; the read is what
     * turns "ordinary" into "checked", and what makes the one case where it is
     * NOT ours a refusal you can read instead of a fact one tenant wrote into
     * another tenant's memory.
     *
     * What the read finds decides the rest: an existing row of ours is patched,
     * an absent one is created, and a row that is somebody else's is refused by
     * name. The `create` race — two writers, neither of whom saw a row — is
     * caught as `ALREADY EXISTS` and folded back into the same checked patch.
     */
    put<T = unknown>(identity: MemoryIdentity, entry: MemoryEntry<T>, options?: MemoryWriteOptions): Promise<void>;
    /**
     * Sequential, not batched: the service has no batch-write operation, and
     * each write is a long-running operation that has to be waited on
     * individually. An empty batch is a no-op and costs no round trip, which
     * callers rely on.
     */
    putMany<T = unknown>(identity: MemoryIdentity, entries: readonly MemoryEntry<T>[], options?: MemoryWriteOptions): Promise<void>;
    /**
     * Remove one memory.
     *
     * Scope-checked first, for the reason {@link get} spells out: a resource
     * name addresses a row directly, and a delete that skipped the check would
     * let anyone who could guess an id remove another tenant's memory. A memory
     * that is not this identity's is left alone and reported as nothing to do —
     * the same answer a missing one gets.
     */
    delete(identity: MemoryIdentity, id: string): Promise<void>;
    /**
     * GDPR — every memory for this identity, gone.
     *
     * Paginated retrieve-then-delete, and **deliberately not `memories.purge`**,
     * for two reasons that are both about not being silently wrong on the one
     * operation where that matters most:
     *
     *  1. `PurgeMemoriesRequest.force` defaults to **false**, which the service
     *     documents as "the purge request will be validated but not executed".
     *     A forget built on it and written without that flag would report
     *     success and delete nothing — a compliance failure that looks exactly
     *     like a working erasure.
     *  2. Purge selects rows with an AIP-160 filter STRING, and whether that
     *     language can express an exact scope match is not verified. A filter
     *     that under-matches leaves data behind; one that over-matches deletes
     *     somebody else's. Neither is a guess worth making here.
     *
     * The scoped retrieve has semantics the SDK states outright, so that is what
     * this uses. It costs one delete per memory, which is the right price.
     */
    forget(identity: MemoryIdentity): Promise<void>;
    /**
     * @throws always — this service has no compare-and-set.
     * @see the module header for why this refuses where the sibling adapter
     *      emulates.
     */
    putIfVersion<T = unknown>(_identity: MemoryIdentity, entry: MemoryEntry<T>, expectedVersion: number): Promise<PutIfVersionResult>;
    /** @throws always — this service has no recognition set. */
    seen(_identity: MemoryIdentity, signature: string): Promise<boolean>;
    /** @throws always — the write side of a recognition set this service does not have. */
    recordSignature(_identity: MemoryIdentity, signature: string): Promise<void>;
    /** @throws always — this service has no feedback primitive. */
    feedback(_identity: MemoryIdentity, id: string, _usefulness: number): Promise<void>;
    /** @throws always — the read side of feedback this service does not record. */
    getFeedback(_identity: MemoryIdentity, id: string): Promise<{
        average: number;
        count: number;
    } | null>;
    /**
     * Stop using this store. Idempotent and final. Nothing is torn down on
     * Google's side — the memories outlive this process, which is the point.
     */
    close(): Promise<void>;
    /** Where this identity's copy of `id` lives. See the module header. */
    private nameOf;
    /** One memory by resource name, or `undefined` when there is none. */
    private fetch;
    /**
     * Patch the row at `name` if it exists AND is this scope's; answer `false`
     * when there is nothing there to patch.
     *
     * A row that exists under somebody else's scope is REFUSED rather than
     * written. It should be unreachable — the address carries the scope — so
     * reaching it means the fingerprint collided or the bank was written by
     * another tool under a name of ours, and both of those are facts an operator
     * has to be told rather than have resolved in favour of the last writer.
     */
    private overwrite;
    private ensureOpen;
    private wait;
    /** Keep an already-sanitized refusal; sanitize anything else. */
    private asFailure;
    /** The identity's scope, checked for the two values that would break isolation. */
    private resolveScope;
}
/**
 * A `MemoryStore` over Vertex AI Memory Bank.
 *
 * @example  Per-person memory that survives a conversation ending
 *   const store = memoryBankStore({
 *     project: 'my-project',
 *     location: 'us-central1',
 *     reasoningEngine: '1234567890',
 *     scopeFor: (id) => ({
 *       tenant: encodeIdentityField(id.tenant),
 *       principal: encodeIdentityField(id.principal),
 *     }),
 *   });
 *
 *   const hits = await store.search(identity, [], { text: 'what does she prefer?', k: 5 });
 */
export declare function memoryBankStore(options: MemoryBankStoreOptions): MemoryBankStore;
/**
 * Distance → a score whose ORDER is right.
 *
 * `1 / (1 + d)` is strictly decreasing on `d >= 0`, lands in `(0, 1]`, and is
 * exactly 1 at distance 0. It is **not** a cosine similarity and this adapter
 * never says it is — see {@link MemoryBankStore.search} for why `minScore` is
 * refused rather than measured against it.
 *
 * A row with no distance is a simple retrieval, which does no ranking at all;
 * `0` is the honest score for "this was not ranked", and it sorts last.
 */
export declare function scoreFromDistance(distance: number | null | undefined): number;
