/**
 * hosting/nodeHost — the plain HTTP adapter, built on `node:http` and nothing
 * else.
 *
 *   const host = nodeHost({ port: 8080 });
 *   const handle = await host.serve(async (request, reply) => {
 *     reply.complete(await answer(request.input));
 *   });
 *
 * Two routes: `POST /invoke` takes `{ input, sessionId?, decision? }` and
 * answers `{ output }` — or `{ awaiting }` with a **202** when the run stopped
 * to ask a person something, which a later `POST` carrying `decision` continues;
 * `GET /health` answers `{ status: 'ok' }`. The same `POST` also carries the
 * artifact wire operations (9.23.0): `{ op: 'artifact-head', ref, sessionId }`
 * answers `{ artifact: { ref, meta } }` and `{ op: 'artifact-get', … }` adds
 * `data` — a screen redeeming a claim ticket under the same session identity
 * the conversation's own requests present. Both paths are
 * options, because the paths are the part most likely to be dictated to you by
 * whatever is in front of the process — a load balancer, a container contract,
 * a colleague's convention. A path is a deployment detail, so it is a knob
 * here and absent from the port entirely.
 *
 * **The socket can be yours.** Pass `server` — a `node:http` server you
 * created and listened on — and this adapter attaches its two routes to it
 * instead of binding one, so a WebSocket upgrade or your own routes can share
 * the port. `close()` then detaches and drains, and your socket stays up.
 *
 * **Or the socket can be the host's and the routes yours.** Pass `onUnhandled`
 * and every path this adapter does not own is handed to your code instead of
 * collecting its 404 — the same one port, bound by the host rather than by you.
 * The two are opposite ends of one seam and are refused together.
 *
 * **There is a third door.** `serveConversations(handler)` takes WebSocket
 * upgrades on `/conversation` and hands each one to your handler as a
 * `HostConversation` — a channel that stays open, for the callers that cannot
 * host an inbound endpoint and dial out instead. It shares this host's socket
 * with `/invoke` and `/health`, needs nothing installed, and declares what it
 * caps.
 *
 * **Streaming is the caller's choice, not the server's.** Send
 * `Accept: text/event-stream` and the reply is Server-Sent Events, one `chunk`
 * event per `reply.emit(...)` then a final `complete`. Send anything else and
 * the same handler produces one JSON body — it emits into a buffer that the
 * completion settles. The handler cannot tell the difference and does not need
 * to, which is the property `capabilities` exists to describe.
 *
 * Pattern: Adapter. What is specific to THIS adapter is its two paths and its
 * JSON dialect (`jsonWire` below) — nothing else. The HTTP work itself lives in
 * `httpHost.ts` and is shared with every other HTTP adapter, so two of them can
 * never quietly drift apart on what `close()` drains or what a handler that
 * throws does. `types.ts` knows none of it either way.
 */
/// <reference types="node" />
import type { HostSignInOptions } from './signin/types.js';
import type { CrossSiteOptions } from './doorGuard.js';
import { type HttpHost, type HttpHostHandle, type HttpWire } from './httpHost.js';
import type { ConversationLimits } from './types.js';
/**
 * Options for {@link nodeHost}.
 *
 * The three cross-site fields — `allowedOrigins`, `allowedHosts`,
 * `requireJsonContentType` — are `httpHost`'s own door guard, on both of this
 * adapter's doors, with the safe defaults: a request that changes something
 * must say it is JSON, a browser may drive the door only from its own host,
 * and with `allowedHosts` unset the host says once at boot that it answers any
 * name. Behind a proxy, list every name the door is reached by — the library
 * never redirects a short name to a canonical one; that is the proxy's job.
 *
 * @example  A door on a company network, reached by one name
 *   nodeHost({ port: 8080, allowedHosts: ['bot.corp.example'] });
 */
export interface NodeHostOptions extends CrossSiteOptions {
    /** Port to bind. Default `8080`. Pass `0` for an ephemeral port. Refused alongside `server`. */
    readonly port?: number;
    /** Interface to bind. Default `'0.0.0.0'`. Refused alongside `server`. */
    readonly hostname?: string;
    /** Path that takes a request. Default `'/invoke'`. */
    readonly invokePath?: string;
    /** Path that answers a health probe. Default `'/health'`. */
    readonly healthPath?: string;
    /**
     * Path that takes a conversation upgrade. Default `'/conversation'` — this
     * adapter's own word, chosen the way its other two paths were, and named for
     * what it carries rather than for what any runtime calls it.
     */
    readonly conversationPath?: string;
    /**
     * What the conversation door caps. Defaults to one mebibyte per frame and one
     * mebibyte held before a handler subscribes — declared, so
     * `host.conversationLimits` always reports what is really enforced. This
     * adapter declares no `idleMs`: it does not idle a conversation out, and
     * reporting a ceiling it neither imposes nor sits behind would be inventing
     * a fact.
     */
    readonly conversationLimits?: ConversationLimits;
    /**
     * A `node:http` server **you** own, already listening. Given one, this
     * adapter attaches its two routes to it instead of binding a socket of its
     * own — the only way to serve something else (a WebSocket upgrade, routes
     * that were there first) on the same port, which matters when the thing
     * running your process gives it exactly one.
     *
     * You keep `listen()` and you keep the shutdown; `close()` detaches this
     * host and drains what it was already serving. On a server you own the host
     * never writes a 404 either: a path it does not own is yours to answer. The
     * full set of laws is on `httpHost`'s own `server` option, which this is.
     */
    readonly server?: import('node:http').Server;
    /**
     * Carry a sign-in cookie — see `HttpHostOptions.signIn`. The cookie is
     * stripped from every header bag handed onward and only its key goes on;
     * conversation handshakes are verified before the 101. Absent, nothing
     * changes.
     */
    readonly signIn?: HostSignInOptions;
    /**
     * 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`: there you own the socket and lend the host two
     * paths; here the host owns the socket and lends you every path it does not
     * answer. `/invoke`, `/health` and `/conversation` never reach it, a throw
     * inside it is that request's 500, and passing it beside `server` is refused
     * by name. The full set of laws is on `httpHost`'s own `onUnhandled`, which
     * this is.
     *
     * @example  A diagnostic route beside the agent, on one port
     *   nodeHost({
     *     port: 8080,
     *     onUnhandled: (req, res) => {
     *       if (req.url === '/debug/trace') {
     *         res.writeHead(200, { 'content-type': 'application/json' });
     *         res.end(JSON.stringify(lastTrace));
     *         return;
     *       }
     *       res.writeHead(404, { 'content-type': 'application/json' });
     *       res.end('{"error":"no such route"}');
     *     },
     *   });
     */
    readonly onUnhandled?: (req: import('node:http').IncomingMessage, res: import('node:http').ServerResponse) => void;
    /**
     * Which header carries the session id. Default `'x-session-id'` — the header
     * this adapter has always read, now named so a deployment can change it.
     *
     * The pairing on the client side is one line:
     * `browserSessionId()` from the main barrel mints the id, keeps it, and hands
     * it back on every call.
     */
    readonly sessionHeader?: string;
    /**
     * Read the session from a cookie of this name, and ISSUE one (HttpOnly,
     * SameSite=Lax) when the request carried none — a durable per-browser
     * conversation with no client code at all.
     *
     * Off by default. The trade-offs are real and are stated in full on
     * {@link JsonWireOptions.sessionCookie} — read them before turning it on;
     * the short version is that a cookie is browser affinity and never identity,
     * and that no `Secure` flag is set because this host cannot know whether it
     * is behind TLS.
     */
    readonly sessionCookie?: string;
    /**
     * Ceiling on a request body, in bytes. Default {@link DEFAULT_NODE_MAX_BODY_BYTES}
     * (one mebibyte) — the same number the hosted-runtime adapters default to.
     *
     * `httpHost` itself keeps no default (a number chosen there would be
     * inherited by every adapter built on it); this adapter chooses one, because
     * without it a caller with no credentials could make the door buffer and
     * parse a body of any size before a single rule about it — the session-id
     * bound included — could say no. A body over the line is refused with
     * `ERR_REQUEST_TOO_LARGE` (413) at the byte that crossed it. One mebibyte is
     * about a quarter of a million tokens of input, more than any context window
     * serves in one message; raise it if this deployment genuinely carries more.
     */
    readonly maxBodyBytes?: number;
}
/** {@link NodeHostOptions.maxBodyBytes}'s default: one mebibyte. */
export declare const DEFAULT_NODE_MAX_BODY_BYTES = 1048576;
/**
 * What {@link nodeHost}'s `serve()` resolves to — a `HostHandle` that also
 * reports the `url` and `port` it actually bound, which is the only way to find
 * out when you asked for port `0`.
 */
export type NodeHostHandle = HttpHostHandle;
/** `AgentHost` narrowed to this adapter's handle. */
export type NodeHost = HttpHost;
/** The header this dialect reads a session id from when nobody says otherwise. */
export declare const DEFAULT_SESSION_HEADER = "x-session-id";
/** What {@link jsonWireWith} lets a deployment re-decide about sessions. */
export interface JsonWireOptions {
    /**
     * Which header carries the session id. Default `'x-session-id'`, matched
     * case-insensitively (headers reach a wire already lower-cased).
     *
     * Change it when something in front of the process already stamps a
     * conversation id under its own name — a gateway, a CDN, a corporate proxy.
     * The JSON body's `sessionId` still wins over it, unchanged.
     */
    readonly sessionHeader?: string;
    /**
     * Read the session from a COOKIE of this name, and issue one when the request
     * did not carry it. Off by default.
     *
     * ── What it buys ─────────────────────────────────────────────────────────
     * A browser gets a durable conversation with **no client code at all**: the
     * first request comes back with `Set-Cookie`, and every later request carries
     * it automatically. Nothing to mint, nothing to store, nothing to remember to
     * send.
     *
     * ── What it costs, stated rather than discovered ─────────────────────────
     *  - **It is browser affinity, not identity.** The same laws as
     *    {@link HostRequest.sessionId}: a cookie is client data, anyone can send
     *    any value, and nothing here signs or verifies it. Authenticate the
     *    caller by your own means before serving the session they claimed.
     *  - **No `Secure` flag is set**, because this host cannot know whether it is
     *    behind TLS — it is routinely terminated at a proxy and answers plain
     *    HTTP itself. On the public internet, add `Secure` at your proxy or
     *    terminate TLS here; a session cookie on plain HTTP travels in the clear.
     *  - `HttpOnly` and `SameSite=Lax` ARE set: script on the page cannot read
     *    the id, and a cross-site POST does not carry it. `HttpOnly` also means
     *    your own front-end code cannot read it — if the page needs the id in
     *    hand, use `browserSessionId()` and the header instead.
     *  - **It follows the browser, not the person.** One profile is one
     *    conversation; two devices are two, and a shared machine is one.
     *  - A caller that already sent a session (body, header, or an existing
     *    cookie) is never issued a second one — two competing handles for one
     *    conversation is worse than none.
     */
    readonly sessionCookie?: string;
}
/**
 * Build this adapter's JSON dialect: `{ input, sessionId? }` in, `{ output }`
 * out, `{ status: 'ok', uptimeMs }` on the health path — with the session
 * plumbing a deployment gets to re-decide.
 *
 * Exported by name so a deployment that has to keep these exact bodies while
 * changing something else about the host reuses them rather than retyping them
 * and getting one field subtly wrong.
 */
export declare function jsonWireWith(options?: JsonWireOptions): HttpWire;
/**
 * This adapter's own JSON dialect with its defaults: the session on
 * `x-session-id`, no cookie.
 */
export declare const jsonWire: HttpWire;
/**
 * An HTTP host for one handler.
 *
 * @example
 *   const handle = await nodeHost({ port: 0 }).serve(handler);
 *   await fetch(`${handle.url}/invoke`, {
 *     method: 'POST',
 *     headers: { 'content-type': 'application/json' },
 *     body: JSON.stringify({ input: 'hello', sessionId: 'c-1' }),
 *   });
 *   await handle.close();
 */
export declare function nodeHost(options?: NodeHostOptions): NodeHost;
//# sourceMappingURL=nodeHost.d.ts.map