/**
 * adapters/hosting/agentcore — AWS Bedrock **AgentCore Runtime** adapters for
 * the two hosting ports.
 *
 *   import { agentCoreRuntimeHost, agentCoreSessions } from 'agentfootprint/hosting';
 *   import { standingAgent } from 'agentfootprint/hosting';
 *
 *   const handle = await standingAgent({
 *     agent,
 *     host: agentCoreRuntimeHost(),
 *     sessions: agentCoreSessions({ store: 'session-storage' }),
 *   });
 *
 * ── What this file actually is ───────────────────────────────────────────────
 * Vendor paths, a header name, and two JSON body shapes. That is the whole
 * adapter, and it is the claim the hosting ports were designed to make: a
 * container runtime's contract is a CONFIGURATION of HTTP work that already
 * exists, not a second implementation of it. Nothing here reaches into the
 * ports, and nothing here needed the ports to change.
 *
 * AgentCore Runtime is a **container contract**: an ARM64 image serving HTTP on
 * `0.0.0.0:8080` —
 *
 *   POST /invocations   JSON `{ "prompt": "..." }` → JSON `{ "response", "status" }`
 *   GET  /ping          → `{ "status": "Healthy", "time_of_last_update": <unix seconds> }`
 *   GET  /ws            a bidirectional WebSocket, on the SAME port
 *
 * and the caller's conversation arrives in the
 * `X-Amzn-Bedrock-AgentCore-Runtime-Session-Id` header rather than in the body,
 * which is the one thing paths-and-bodies configuration alone could not
 * express before this release. Its sibling
 * `X-Amzn-Bedrock-AgentCore-Runtime-User-Id` carries WHO is calling (9.12.0) —
 * two headers, two different facts, and the second is why a served run can name
 * an actor in its audit trail without anybody configuring one.
 *
 * ── The second door ──────────────────────────────────────────────────────────
 * `/ws` is this runtime's answer for a caller that cannot host an inbound
 * endpoint — a browser, most obviously — and so dials out and parks a channel
 * instead. It is the same container and the same port, which is exactly why the
 * two doors here share one socket.
 *
 * Its wire facts are ADAPTER facts and live nowhere but this file: 32KB frames,
 * a 15-minute idle ceiling, the bearer credential carried as a
 * `Sec-WebSocket-Protocol` offer because a browser's WebSocket API cannot set a
 * header, and session affinity that has to be readable from the query string
 * for the same reason. The port knows none of it — it is handed a session id
 * and a header bag, the same two things every other transport hands it.
 *
 * ── Verification status, stated plainly ──────────────────────────────────────
 * `agentCoreRuntimeHost` is **plain HTTP and is really verified**: it runs the
 * same host conformance suite as `nodeHost`, over a real socket, in
 * `test/hosting/host-contract.test.ts`. There is no AWS SDK on its path.
 *
 * `agentCoreSessions({ store: 'memory' })` is **contract-mapped and
 * injection-tested**: its AgentCore Memory calls are exercised through the
 * `_client` seam, never against AWS. Confirm the command and field names
 * against your installed `@aws-sdk/client-bedrock-agentcore` before you rely
 * on it.
 *
 * Both modes have now been exercised against the real service by a production
 * integration, and the `'memory'` mode came back with a defect no injected fake
 * could have shown: given an OBJECT as an event's blob, the service stores its
 * own host language's `toString()` of it and returns a string that is not JSON
 * and cannot be decoded. This shim writes JSON text for exactly that reason.
 * Envelopes written by any build before 7.22.1 are unrecoverable — the mangling
 * is lossy, so there is nothing to migrate, and the honest response is a loud
 * refusal rather than a silent fresh start.
 *
 * Pattern: Adapter (GoF). Role: outer ring. The file-backed session store uses
 * `node:fs` and nothing else; the event-backed one lazy-loads the AWS SDK, so
 * importing this module costs zero peer-dep load.
 */
/// <reference types="node" />
import { type CrossSiteOptions } from '../../hosting/doorGuard.js';
import type { ConversationHandshake, HandshakeFacts, HttpHost, HttpWire } from '../../hosting/httpHost.js';
import type { CheckpointEnvelope, SessionLifecycle } from '../../hosting/types.js';
/**
 * Options for {@link agentCoreRuntimeHost}.
 *
 * The three cross-site fields are the door guard every `httpHost` keeps, with
 * this adapter's defaults INVERTED — off unless set. The runtime's front door
 * is the only way to this port and demands a credential no web page holds (a
 * signed request, or the bearer a browser offers on `/ws`), and whether that
 * front door forwards `Origin` to the container at all is still unverified —
 * so a default Origin rule could refuse the documented browser path while
 * protecting nothing (see `platformFrontedDoor` in `src/hosting/doorGuard.ts`).
 * Set any of them to enforce it, exactly as on every other door. On a
 * LOOPBACK `hostname` (a laptop — no platform fronts it) every plain-host
 * default applies instead, the loopback-only `allowedHosts` included; with the
 * rules off the host says so once at boot. The session-id bound applies
 * regardless: the runtime's own session header is bounded like any other.
 */
export interface AgentCoreRuntimeHostOptions extends CrossSiteOptions {
    /**
     * Port to bind. Default `8080` — the port the container contract specifies.
     * Pass `0` in tests to take an ephemeral one.
     */
    readonly port?: number;
    /**
     * Interface to bind. Default `'0.0.0.0'`, which the contract requires: bind
     * to loopback inside the container and the runtime's health probe cannot
     * reach you.
     */
    readonly hostname?: string;
    /**
     * Report `'HealthyBusy'` instead of `'Healthy'` on the health path.
     *
     * A function, not a flag, because busy is a live fact about the process, not
     * a setting: the runtime reads it on every probe to decide whether to send
     * you more work. Omit it and the host reports `'Healthy'`, which is the
     * honest answer for an agent that answers synchronously.
     */
    readonly busy?: () => boolean;
    /**
     * A `node:http` server **you** own, already listening. Given one, this
     * adapter attaches `/invocations` and `/ping` to it instead of binding a
     * socket of its own — which is the only way to serve something else on the
     * same port, and a container gets one port.
     *
     * The case this exists for: a container that must also answer a WebSocket
     * upgrade beside the runtime's two routes. Add your `'upgrade'` listener to
     * the server, listen on 8080 yourself, and attach the agent to it.
     *
     * Every law is `httpHost`'s: unmatched paths are YOURS (this adapter writes
     * no 404 on a server it does not own), and `close()` detaches and drains
     * without closing your socket. `port` and `hostname` are refused alongside
     * it — a server you own already has an address.
     *
     * @example
     *   const server = createServer();
     *   server.on('upgrade', handleWebSocket);
     *   await new Promise<void>((r) => server.listen(8080, '0.0.0.0', r));
     *   const handle = await standingAgent({
     *     agent,
     *     host: agentCoreRuntimeHost({ server }),
     *     sessions: agentCoreSessions({ store: 'session-storage' }),
     *   });
     */
    readonly server?: import('node:http').Server;
    /**
     * Path that takes a conversation upgrade. Default `'/ws'` — the runtime's
     * own second door, beside `/invocations` on the same port.
     *
     * You do not need `server` for this: both doors share one socket by
     * construction, which is what the single-port container required.
     */
    readonly conversationPath?: string;
    /**
     * Answer a request whose path this adapter does not own — your code, on this
     * host's socket, instead of its 404.
     *
     * The inverse of `server`, and the cheaper half of it when all you need is a
     * route of your own: the host binds the container's one port as usual and
     * hands you everything it does not answer. The runtime's own three paths
     * never reach it, a throw inside it is that request's 500, and passing it
     * beside `server` is refused by name — there, unmatched paths already reach
     * your own listeners.
     *
     * @example  A diagnostic route inside the container, on the one port it has
     *   agentCoreRuntimeHost({
     *     onUnhandled: (req, res) => {
     *       res.writeHead(req.url === '/debug/trace' ? 200 : 404, {
     *         'content-type': 'application/json',
     *       });
     *       res.end(JSON.stringify(req.url === '/debug/trace' ? lastTrace : { error: 'no route' }));
     *     },
     *   });
     */
    readonly onUnhandled?: (req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse) => void;
}
/**
 * The AgentCore Runtime contract as an {@link HttpWire}.
 *
 * Exported so the body shapes are inspectable and testable without binding a
 * socket, and so a deployment that must serve the same bodies from somewhere
 * else can reuse them by name.
 */
export declare function agentCoreRuntimeWire(busy?: () => boolean): HttpWire;
/**
 * The `/ws` handshake, in this runtime's spelling: **header-or-query session
 * affinity, and the bearer subprotocol mapped into headers.**
 *
 * ── Why the query string is read at all ──────────────────────────────────────
 * The same header carries the session on `/invocations`, and it is preferred
 * here too. But the caller this door exists for is a browser, and the browser
 * WebSocket API cannot set a header — so a session id has nowhere to travel
 * except the URL. Both the runtime's header name and the plain `sessionId` are
 * accepted as query parameters, case-insensitively; **the header wins** when
 * both arrive, so a caller that sets both is never surprised by which one the
 * server preferred. That is the same precedence rule the request dialect uses.
 *
 * ── Why the credential becomes a header ──────────────────────────────────────
 * A bearer token offered as a subprotocol is this runtime's spelling of
 * `Authorization`, and a port field spelled the way one vendor spells it is how
 * a port stops being one. So it lands in `headers.authorization` as
 * `Bearer <token>` — the vocabulary every other transport already uses, with
 * the vendor's base64url wrapper already undone — and the raw
 * `sec-websocket-protocol` header is left in place, so an application that
 * reads the offer itself still can. **Nothing here authenticates anything**:
 * the port never proves who is calling, and a token that arrived is a claim,
 * exactly like the session id beside it.
 *
 * ── When it refuses ──────────────────────────────────────────────────────────
 * Two shapes throw rather than degrade, and both throws end this one upgrade
 * with a message naming the reason: a dotted value that is not valid base64url
 * (see {@link BEARER_SUBPROTOCOL}), and a dotted value offered without the
 * sentinel beside it. The alternative — mapping a credential nobody can read,
 * or echoing the token back to the client — is the failure shape this adapter
 * is built to refuse.
 *
 * Exported by name so the mapping is inspectable and testable without binding a
 * socket, the same way the body shapes are.
 */
export declare function readAgentCoreConversation(facts: HandshakeFacts): ConversationHandshake;
/**
 * An `AgentHost` that speaks AgentCore Runtime's container contract.
 *
 * Passes the same conformance suite as `nodeHost` — it is the same HTTP host
 * with this runtime's two paths, its header, and its two body shapes.
 *
 * @example  The container's entry point
 *   const handle = await standingAgent({
 *     agent,
 *     host: agentCoreRuntimeHost(),
 *     sessions: agentCoreSessions({ store: 'session-storage' }),
 *   });
 *   process.on('SIGTERM', () => void handle.close());
 */
export declare function agentCoreRuntimeHost(options?: AgentCoreRuntimeHostOptions): HttpHost;
/**
 * Where {@link agentCoreSessions} keeps a conversation between requests.
 *
 *  - `'session-storage'` — a JSON file under the container's own storage. The
 *    runtime keeps that storage for the life of a session, INCLUDING across a
 *    stop/resume of the container, so a conversation survives the thing most
 *    likely to interrupt it. It does not survive the session ending.
 *  - `'memory'` — one AgentCore Memory event per persist. Outlives the session,
 *    the container and the deployment; costs an API call per turn and the
 *    `@aws-sdk/client-bedrock-agentcore` peer dependency.
 *
 * Chosen at construction, never per call: a store that silently changed where
 * it wrote would be a store you cannot reason about after an incident.
 */
export type AgentCoreSessionStore = 'session-storage' | 'memory';
/** The default file the `'session-storage'` mode writes to. */
export declare const DEFAULT_SESSION_STORAGE_PATH = "/tmp/agentcore-session";
/** Options for the file-backed mode. */
export interface AgentCoreFileSessionsOptions {
    readonly store: 'session-storage';
    /**
     * Where to write. Default {@link DEFAULT_SESSION_STORAGE_PATH}. One file
     * holds every session this container has seen, keyed by session id — the
     * runtime already gives each session its own storage, so the keying is
     * belt-and-braces for the case where it does not.
     */
    readonly path?: string;
}
/** One AgentCore Memory event, as this adapter cares about it. */
export interface AgentCoreSessionEvent {
    /** Server-assigned event id. */
    readonly eventId: string;
    /**
     * The envelope decoded from the event's blob payload.
     *
     * `null` means the event carried **no blob at all** — nothing here ever
     * claimed to be a session, which is an absence and hydrates as "no
     * conversation".
     *
     * A blob that IS present but could not be decoded travels here **as-is**, so
     * the shared reading law refuses it by name rather than this adapter quietly
     * calling a conversation that exists an absent one. Those are different facts
     * and only one of them is safe to answer with a fresh start.
     */
    readonly envelope: unknown;
}
/**
 * The minimal AgentCore Memory surface the session store calls. The real SDK is
 * adapted to this shape in one function below; tests inject a fake via
 * `_client` and never touch AWS.
 */
export interface AgentCoreSessionClientLike {
    /**
     * Append one envelope as an event (the server assigns the event id).
     *
     * The envelope arrives as an OBJECT; how it reaches the wire is the
     * implementation's business. The shipped shim writes it as JSON text, because
     * this service returns an object blob back as its own host language's
     * `toString()` of it — see `createSessionClient`.
     */
    createEvent(input: {
        memoryId: string;
        actorId: string;
        sessionId: string;
        envelope: CheckpointEnvelope;
    }): Promise<void>;
    /** The session's events, newest first — the adapter reads only the newest. */
    listEvents(input: {
        memoryId: string;
        actorId: string;
        sessionId: string;
        maxResults?: number;
    }): Promise<{
        events: readonly AgentCoreSessionEvent[];
    }>;
}
/** Options for the event-backed mode. */
export interface AgentCoreMemorySessionsOptions {
    readonly store: 'memory';
    /** AgentCore Memory ARN or id. Required. */
    readonly memoryId: string;
    /** AWS region, when the adapter constructs the SDK client itself. */
    readonly region?: string;
    /**
     * The AgentCore `actorId` these conversations belong to. Default
     * `'afp-standing-agent'`. One actor per deployed agent is the usual shape.
     */
    readonly actorId?: string;
    /** Pre-built client, to share one SDK config across the host app. */
    readonly client?: AgentCoreSessionClientLike;
    /** @internal Test injection — skips the SDK require entirely. */
    readonly _client?: AgentCoreSessionClientLike;
    /** @internal Test injection — the AWS SDK module, to exercise the real shim with a fake SDK. */
    readonly _sdk?: BedrockAgentCoreSessionSdkModule;
}
/** Options for {@link agentCoreSessions}. */
export type AgentCoreSessionsOptions = AgentCoreFileSessionsOptions | AgentCoreMemorySessionsOptions;
/**
 * A `SessionLifecycle` backed by AgentCore, with the checkpoint's home chosen
 * at construction.
 *
 * Both modes store the SAME `CheckpointEnvelope` the port defines — a
 * conversation or a paused run — and both refuse an unknown `format` by name
 * through the shared `checkEnvelope`: a session written by a newer runtime is
 * refused, never half-restored. That law is inherited, not re-implemented, and
 * so is its other half: a stored session that is present but **unreadable** is
 * refused by name too (`UnreadableEnvelopeError`), never answered with a fresh
 * start. Only a session that was never written hydrates as `undefined`.
 *
 * **The two modes differ on retention (9.42.0), and the difference is
 * reported rather than smoothed over.** `'session-storage'` owns its file, so
 * it implements the port's optional `retention()` as a sweep you call.
 * `'memory'` appends events to a service whose expiry belongs to the memory
 * resource an operator configured, and this shim has no delete on its surface
 * — so it implements no retention member at all, and `sessionRetention()`
 * refuses BY NAME rather than reporting a sweep that would delete nothing.
 *
 * @example  Survive a stop/resume, no AWS SDK required
 *   agentCoreSessions({ store: 'session-storage' });
 *
 * @example  Outlive the session entirely
 *   agentCoreSessions({ store: 'memory', memoryId: process.env.MEMORY_ID!, region: 'us-west-2' });
 */
export declare function agentCoreSessions(options: AgentCoreSessionsOptions): SessionLifecycle;
/** The slice of `@aws-sdk/client-bedrock-agentcore` this shim touches. */
export interface BedrockAgentCoreSessionSdkModule {
    readonly BedrockAgentCoreClient?: new (config: {
        region?: string;
    }) => {
        send(cmd: unknown): Promise<unknown>;
    };
    readonly CreateEventCommand?: new (input: unknown) => unknown;
    readonly ListEventsCommand?: new (input: unknown) => unknown;
}
