/**
 * agentEngineSessions — conversations in Vertex AI's own session service, so a
 * fleet shares them.
 *
 * `memorySessions()` loses everything on restart and says so. `sqliteSessions()`
 * survives a restart on ONE machine and says so. The row above both — *many
 * containers, one conversation* — is where a managed session service belongs,
 * and on this column that service is the `sessions` collection under a
 * reasoning engine.
 *
 * ── The name, said once ─────────────────────────────────────────────────────
 * The product was **Agent Engine**, is now **Agent Runtime**, and the API
 * resource is still spelled `reasoningEngines`. This factory keeps the name it
 * was designed under; where the product name and the API disagree, the API is
 * the one that has not moved.
 *
 * ── The fit, and the one thing it cost ──────────────────────────────────────
 * `Session.sessionState` is an arbitrary JSON `Struct`. A `CheckpointEnvelope`
 * is arbitrary JSON. So the envelope goes in whole, under one key, and comes
 * back whole — no event log to fold, no blob encoding to get wrong. What it
 * cost is the WRITE VERB, and that took a field trial to learn: state goes in
 * through an appended EVENT, never through a patch. See fact 2b below.
 *
 * ── The five facts that shaped the code ─────────────────────────────────────
 *  1. **`sessions.create` takes a caller-supplied `sessionId`.** So our session
 *     id IS the resource id and `hydrate` is one `get` by name. No mapping
 *     table, no listing to find a conversation.
 *  2. **`create` and `delete` answer a long-running Operation; `get` and
 *     `appendEvent` answer directly.** Every operation-shaped write here waits
 *     for `done` before it returns — a `persist` that returned early would make
 *     the very next `hydrate` a race whose failure mode is "no conversation",
 *     which nobody can tell from a new user.
 *
 *  2b. **`sessionState` CANNOT be patched. This one is field truth, and it cost
 *     a release.** 9.29.0 wrote the steady state as
 *     `sessions.patch({ updateMask: 'sessionState,ttl' })`, which every
 *     injected-client test accepted because a double will patch anything. An
 *     independent field trial ran it against the live service (2026-08-14) and
 *     found that turn one stored and **every later turn failed**:
 *
 *       HTTP 400 — "Can't update the session state for session …, you can only
 *                   update it by appending an event."
 *
 *     A store that keeps the first turn of a conversation and refuses the
 *     second is not a session store, so the verb changed rather than the
 *     documentation: `persist` now appends a `SessionEvent` whose
 *     `actions.stateDelta` carries the envelope, which the same trial verified
 *     end to end (append accepted, `GET` returned the new state).
 *
 *     Two consequences worth saying out loud, because they are the difference
 *     between this fix and a plausible-looking one:
 *
 *       • **A state delta MERGES by top-level key.** This store writes exactly
 *         two, both namespaced to this library — {@link SESSION_STATE_KEY} and
 *         {@link SESSION_ID_KEY} — so "merge the delta" and "replace our keys"
 *         are the same outcome for us, and another guest's keys under the same
 *         session are left alone either way. The second arrived in 9.45.0: the
 *         resource id is a lossy fold, so the listing has to carry the id its
 *         CALLER would recognise rather than the composed one. Answering with
 *         the composed id, and making the fold idempotent so that answer could
 *         be fed back, is what made two session ids address one conversation.
 *       • **A conversation now has an event log behind it**, one event per
 *         persisted turn, because that is the only writing surface the service
 *         offers. Nothing here READS that log — `hydrate` still reads the one
 *         envelope out of `sessionState` — so the log is the service's audit
 *         trail of our writes and never a second copy of the truth. Sessions
 *         are per-conversation and events are small; if that growth matters to
 *         you, `forget()` deletes the session and its events together.
 *  3. **`Session.userId` is required and immutable.** Our port's
 *     `persist(sessionId, envelope)` carries no user, so one has to be
 *     resolved — see {@link AgentEngineSessionsOptions.userId}. Immutable
 *     means the first write decides forever, which is the same ownership rule
 *     this library enforces in its own stores.
 *
 *     **It is metadata, not authorization, and a field trial proved it.** Two
 *     sessions were created under `alice` and `bob`; the project's ordinary ADC
 *     principal then read BOTH by name, presenting neither end-user identity
 *     (FINDINGS "Native Agent Runtime Sessions"). `userId` is what a listing
 *     filters on and what a first write pins — it is not a check the service
 *     performs on a read. So the sentence "here the service enforces it for us"
 *     would be false: anyone who can call the API with your project's
 *     credentials and can guess a session id can read that conversation.
 *
 *     Where the check belongs is above this port, and this library already has
 *     it: `envelopeOwner` records who a conversation belongs to, and the
 *     host/gateway is what must compare that to the authenticated caller
 *     before handing a session id down here. This adapter is a STORE. Nothing
 *     that only stores can tell an impostor from an owner.
 *  4. **`ttl` is input-only with a 24-hour floor**, and `expireTime` always
 *     comes back. An hour-long TTL is not available at any price.
 *
 *     **Sliding expiry is NOT claimed here, and cannot be.** A `ttl` is sent on
 *     `create`, where the service takes it; later turns append an event, and
 *     appending one was MEASURED against the real service — by a field trial,
 *     reported in issue #2 — to leave `expireTime` exactly where it was. So a
 *     conversation expires on the clock its FIRST turn started, however active
 *     it has been since, and the adapter sends the ttl once and says so because
 *     there is no second call that would refresh it.
 *     See {@link AgentEngineSessionsOptions.ttl}.
 *
 * ── The laws it inherits rather than re-implements ──────────────────────────
 * `checkEnvelope` runs on the way OUT and on the way IN, so an envelope whose
 * `format` this runtime does not know is refused by name, and a session that
 * is PRESENT but unreadable is refused by name too. Only a session that was
 * never written hydrates as `undefined`. A conversation that exists and cannot
 * be read must never be answered with a fresh start — that failure is
 * indistinguishable, from the outside, from a brand-new user.
 */
import type { CheckpointEnvelope, SessionExpiryPolicy, SessionLifecycle } from '../../hosting/types.js';
import { type AiPlatformConnection } from '../google/aiPlatform.js';
/**
 * The `sessionState` key the envelope lives under.
 *
 * One key, namespaced, rather than spreading the envelope's own fields across
 * `sessionState`: the struct belongs to whoever owns the reasoning engine, an
 * agent framework is a guest in it, and a guest that scatters `format` and
 * `data` at the top level collides with the next guest. Namespacing also makes
 * the console readable — one entry that says whose it is.
 */
export declare const SESSION_STATE_KEY = "agentfootprint.envelope";
/**
 * The caller's OWN session id, stored beside the envelope.
 *
 * The resource id is a fold — lower-cased, punctuation replaced, long ids
 * fingerprinted — so it cannot be turned back into what the caller passed.
 * `listByUser` used to answer with the resource id and rely on
 * `safeResourceId` being idempotent, and that idempotence was a collision: it
 * made the fold's output a legal input addressing the same conversation. So the
 * raw id travels with the conversation instead, and the listing answers with
 * the id its caller would recognise.
 *
 * Sessions written before 9.45.0 do not carry it; `listByUser` falls back to
 * the resource id for those, which is what it always returned.
 */
export declare const SESSION_ID_KEY = "agentfootprint.sessionId";
/** Options for {@link agentEngineSessions}. */
export interface AgentEngineSessionsOptions extends AiPlatformConnection {
    /**
     * WHO a session belongs to — required by the service on create, and
     * **immutable** once written.
     *
     * The port hands `persist` a session id and an envelope, never a user, so
     * this is where the missing half comes from. Two spellings:
     *
     *  - a **function**, called with the session id and the envelope about to be
     *    stored. The recommended shape: return `envelopeOwner(envelope)` — the
     *    principal the conversation itself was signed with — so the service's
     *    idea of the owner and this library's own ownership index agree by
     *    construction rather than by coincidence. That is what the default does.
     *  - a **string**, when every conversation in this engine belongs to one
     *    service identity. Honest for a single-tenant deployment and wrong the
     *    moment it is not, which is why it is not the default.
     *
     * The default resolver reads the envelope's own principal and falls back to
     * {@link DEFAULT_USER_ID} for a conversation that ran anonymously. It never
     * invents a per-session user id: the service treats `userId` as the thing
     * you filter a listing by, and minting a unique one per session would make
     * every listing return exactly one row and look like it worked.
     */
    readonly userId?: string | ((sessionId: string, envelope: CheckpointEnvelope) => string);
    /**
     * How long a session lives after its last write, as a duration string the
     * API accepts (`'86400s'`). **The service's own floor is 24 hours** and it
     * rejects anything shorter, so this is a knob for keeping conversations
     * LONGER, never for expiring them sooner.
     *
     * Omit and no `ttl` is sent, which leaves the service's own default
     * expiry in charge.
     *
     * **Sent on CREATE only, and the clock does NOT restart.** Later turns append
     * an event (see the module header, fact 2b), and appending one was measured
     * against the real service — by a field trial, reported in issue #2 — to
     * leave `expireTime` where it was. So a conversation expires on the clock its
     * FIRST turn started, however active it has been since.
     *
     * That is a real operational edge: a busy conversation can disappear
     * mid-use, and nothing in this adapter can extend it, because the service
     * treats `ttl` as input-only at create. If you need a conversation to outlive
     * that window, the only lever is a `ttl` long enough at creation.
     *
     * Recorded here rather than in a changelog because it is the kind of fact
     * somebody needs at the moment they are choosing this value, and measured
     * rather than assumed — the previous version of this comment said the
     * question had not been measured, which was true until it was.
     */
    readonly ttl?: string;
    /**
     * The `author` recorded on every event this store appends. Default
     * {@link SESSION_EVENT_AUTHOR}.
     *
     * The service requires one and treats it as free text; it is who the console
     * shows as having written the turn. This is not the conversation's owner —
     * that is `userId`, which is pinned at create and immutable.
     */
    readonly eventAuthor?: string;
    /**
     * How long a write waits for its long-running operation before refusing.
     * Default {@link DEFAULT_OPERATION_TIMEOUT_MS} (30s).
     *
     * It refuses rather than returning: a `persist` that reported success on an
     * operation it never saw finish is a conversation that may or may not be
     * there next turn.
     */
    readonly operationTimeoutMs?: number;
}
/** What a conversation that named nobody is stored under. */
export declare const DEFAULT_USER_ID = "agentfootprint-anonymous";
/**
 * The `author` on every event this store appends, unless
 * {@link AgentEngineSessionsOptions.eventAuthor} names another.
 *
 * The service requires the field and treats it as free text. This name says
 * which library wrote the turn, which is the useful thing to see in the console
 * beside somebody else's agent writing to the same engine.
 */
export declare const SESSION_EVENT_AUTHOR = "agentfootprint";
/**
 * A session store in Vertex AI's session service.
 *
 * It is a {@link SessionLifecycle} plus the two things a real store owns beyond
 * the port — forgetting, and closing — because the port deliberately asks for
 * two methods and leaves the rest to whoever implements it.
 */
export interface AgentEngineSessions extends SessionLifecycle {
    /** The resource these sessions live under. Useful in an incident. */
    readonly parent: string;
    /** Forget one session. A session that was never there is not an error. */
    forget(sessionId: string): Promise<void>;
    /**
     * How conversations here stop existing: **the service does it**, on the
     * `ttl` this store was built with (9.42.0).
     *
     * Narrowed from the port's optional member to a required one, and to the one
     * arm this store can be — a caller holding an `AgentEngineSessions` needs no
     * feature check, and reaching for a sweep that does not exist here is a
     * compile error rather than a surprise at 3am.
     *
     * There is nothing to call and nothing for a cron job to do. `active` says
     * whether a `ttl` was given at all; `enableWith` says what to pass and
     * repeats the two facts a caller has to plan around — the service's 24-hour
     * floor, and that the value is sent on CREATE only.
     */
    retention(): SessionExpiryPolicy;
    /**
     * Stop using this store. Idempotent, and **final** — reading or writing
     * afterwards refuses by name rather than quietly reconnecting, because a
     * store that reopened behind you would hide a shutdown-ordering bug instead
     * of surfacing it.
     *
     * Nothing is torn down on Google's side: the sessions outlive this process,
     * which is the entire reason to use a managed store.
     */
    close(): void;
}
/**
 * Conversations in Vertex AI's session service — the store that survives a
 * fleet, not just a restart.
 *
 * **Status: field-validated except the write verb, which is field-CORRECTED.**
 * An independent trial ran this adapter against a live Agent Runtime engine
 * (2026-08-14) and everything below answered a real request from Google:
 * creating first-turn sessions from real envelopes, hydrating them through a
 * fresh instance, owners preserved, paged `listByUser` filtering, `ownerOf`,
 * an unknown envelope format refused before storage, and idempotent `forget`.
 *
 * The one thing that FAILED was the second write to an existing session — the
 * core job — because 9.29.0 patched `sessionState` and the service refuses
 * that. The trial recovered the service's own words and verified the repair
 * path (`appendEvent` with `actions.stateDelta`, then a `GET` showing the new
 * state) with a raw diagnostic. This adapter now uses that verb; the repair is
 * built on measured service behaviour and tested here, but the corrected write
 * has not itself been re-run through this adapter in a live project.
 *
 * @example  A standing agent whose conversations are shared across instances
 *   import { standingAgent, nodeHost } from 'agentfootprint/hosting';
 *   import { agentEngineSessions } from 'agentfootprint/hosting';
 *
 *   const handle = await standingAgent({
 *     agentFactory: () => buildAgent(),
 *     host: nodeHost({ port: 8080 }),
 *     sessions: agentEngineSessions({
 *       project: 'my-project',
 *       location: 'us-central1',
 *       reasoningEngine: '1234567890',
 *     }),
 *   });
 */
export declare function agentEngineSessions(options: AgentEngineSessionsOptions): AgentEngineSessions;
