import type { AssertAllKeysHandled } from "./ask-origin.js";
/**
 * The closed set of failure classes ({@link McpFailure.kind}):
 *  · `connect_refused`   — a connect-phase errno (ECONNREFUSED / ENOTFOUND / EAI_AGAIN / EHOSTUNREACH /
 *                          ENETUNREACH / UND_ERR_CONNECT_TIMEOUT): the request provably never left;
 *  · `connection_failed` — any other errno / undici code on the cause chain (a reset or a timeout
 *                          mid-exchange): the request may have run;
 *  · `connection_closed` — the SDK reported the transport closed (`McpError` `ConnectionClosed`): in
 *                          flight the fate is unknowable, and for a server already known dead the
 *                          request was never attempted — the two are told apart by `delivered`;
 *  · `http_status`       — the HTTP endpoint answered with a status instead of an MCP response
 *                          (`httpStatus` carries the number: 401/403 = re-authorize, 5xx = down);
 *  · `not_mcp_response`  — the HTTP endpoint answered something that is not an MCP payload and no status;
 *  · `spawn_failed`      — a stdio server process could not be started (a missing/unexecutable command);
 *  · `timeout`           — the request timed out (the SDK's per-call timer, the total ceiling, or the
 *                          idle watchdog — `details` names which);
 *  · `protocol`          — the server ANSWERED with an MCP/JSON-RPC error (a version mismatch, a rejected
 *                          capability, an ordinary `-32603`): the exchange happened;
 *  · `invalid_config`    — the declaration itself could not be dialed (a malformed `url`): a fault to
 *                          fix in the spec, never an outage to wait out;
 *  · `unknown`           — the thrown value carried no structure this table reads.
 */
export declare const MCP_FAILURE_KINDS: readonly ["connect_refused", "connection_failed", "connection_closed", "http_status", "not_mcp_response", "spawn_failed", "timeout", "protocol", "invalid_config", "unknown"];
export type McpFailureKind = (typeof MCP_FAILURE_KINDS)[number];
/** The closed set of delivery verdicts ({@link McpFailure.delivered}). */
export declare const MCP_DELIVERY_VERDICTS: readonly ["yes", "no", "unknown"];
export type McpDelivered = (typeof MCP_DELIVERY_VERDICTS)[number];
/**
 * The delivery verdict each KIND carries when the failure happened on an established connection
 * (`phase: "request"`) — the disposition table over {@link MCP_FAILURE_KINDS}. The classifier's rows
 * read the thrown value's STRUCTURE and mint a class; what that class means for delivery is decided
 * here, once, so no two rows can disagree about the same word and a new kind cannot arrive without
 * saying what it means for delivery (the fence below reds `tsc` with the missing word spelled out).
 */
export declare const DELIVERED_BY_KIND: {
    readonly connect_refused: "no";
    readonly connection_failed: "unknown";
    readonly connection_closed: "unknown";
    readonly http_status: "unknown";
    readonly not_mcp_response: "unknown";
    readonly spawn_failed: "no";
    readonly timeout: "unknown";
    readonly protocol: "yes";
    readonly invalid_config: "no";
    readonly unknown: "unknown";
};
/** The fence over the delivery table: `never` while every kind has a verdict. */
export type DeliveryTableCoversEveryMcpFailureKind = AssertAllKeysHandled<Exclude<McpFailureKind, keyof typeof DELIVERED_BY_KIND>>;
/**
 * The closed set of {@link McpServerLiveness.state} — the engine's answer to ONE question, "can this
 * client complete an exchange with this server":
 *  · `reachable`   — an exchange COMPLETED (the dial handshaked and listed, a re-dial succeeded, or the
 *                    server itself answered — an MCP-level rejection is an answer, so it proves reach
 *                    even though it failed);
 *  · `unreachable` — an exchange was attempted and lost at the transport or the clock (refused, reset,
 *                    closed, unspawnable, timed out);
 *  · `unknown`     — an exchange was attempted and what came back does not answer the question (a status
 *                    a gateway in front of the server may have written, a non-MCP payload, a thrown value
 *                    with no structure this table reads).
 * ABSENCE is the fourth reading and is NOT one of these words: no observation exists at all (the leg
 * never reached this server, or the declaration could not be dialed). A consumer must not fold an absent
 * record into either `unknown` or a health verdict — absent means indeterminate, and `unknown` means
 * "we looked and could not tell".
 */
export declare const MCP_LIVENESS_STATES: readonly ["reachable", "unreachable", "unknown"];
export type McpLivenessState = (typeof MCP_LIVENESS_STATES)[number];
/**
 * One liveness OBSERVATION about one server (see {@link MCP_LIVENESS_STATES} for the words). It answers
 * what the engine last LEARNED, at `observedAt` (epoch ms) — never what is true now, and never a poll:
 * the engine makes no probe of its own, so every record here is a by-product of work the task already
 * did (the dial, a re-dial, a transport close).
 *
 * `errorCode` rides `unreachable` ONLY, and names the failure class that decided it (a `spawn_failed` is
 * a missing binary, a `connect_refused` a closed port). It is deliberately absent on `unknown`: the whole
 * point of that word is that the failure did NOT decide reachability, so carrying its class there would
 * invite a consumer to key a reach action off evidence that says nothing about reach. The connect-time
 * class is still reported in its own right beside this record (the status / manifest row's `errorCode`).
 */
export interface McpServerLiveness {
    state: McpLivenessState;
    observedAt: number;
    errorCode?: McpFailureKind;
}
/**
 * What each failure CLASS says about reachability — the second disposition table over
 * {@link MCP_FAILURE_KINDS}, beside {@link DELIVERED_BY_KIND}, and deliberately NOT derived from it:
 * delivery asks "did the caller's request run", reachability asks "did an exchange complete", and the two
 * part company on both edges — a `timeout` leaves delivery `unknown` while the exchange provably did not
 * complete, and a `protocol` rejection is a failure the caller must handle while being the strongest
 * proof of reach there is.
 *
 * `invalid_config` maps to `undefined` — the one class that is NOT an observation: a declaration that
 * could not be dialed means no transport was ever attempted, so the honest record is no record (see the
 * absence reading in {@link MCP_LIVENESS_STATES}), not a third-hand `unknown`.
 */
export declare const LIVENESS_BY_FAILURE_KIND: {
    readonly connect_refused: "unreachable";
    readonly connection_failed: "unreachable";
    readonly connection_closed: "unreachable";
    readonly http_status: "unknown";
    readonly not_mcp_response: "unknown";
    readonly spawn_failed: "unreachable";
    readonly timeout: "unreachable";
    readonly protocol: "reachable";
    readonly invalid_config: undefined;
    readonly unknown: "unknown";
};
/** The fence over the liveness table: `never` while every kind has a verdict. */
export type LivenessTableCoversEveryMcpFailureKind = AssertAllKeysHandled<Exclude<McpFailureKind, keyof typeof LIVENESS_BY_FAILURE_KIND>>;
/**
 * What ONE exchange with a server tells the liveness record: the CLASS of the failure it ended in, or
 * `"completed"` when it finished. Every round trip the engine makes — a dial, a tool call, a resource
 * read, a listing, a refresh — reports exactly this, so the engine needs no second vocabulary and no
 * per-site judgment about which failures "count".
 */
export type McpExchangeOutcome = McpFailureKind | "completed";
/** The liveness observation an exchange supports, or `undefined` when the outcome is not an observation
 *  at all (see {@link LIVENESS_BY_FAILURE_KIND}: a declaration that could never be dialed). THE single
 *  mint of the record — no two sites can disagree about the word, about which words may carry
 *  `errorCode`, or about what a completed exchange proves. */
export declare function livenessOf(outcome: McpExchangeOutcome, observedAt: number): McpServerLiveness | undefined;
/** Where the failure was judged: at the dial (connect + initial listing), where an errno on a stdio
 *  transport is the child process failing to spawn, or on a request over an established connection. */
export type McpFailureSite = {
    phase: "connect";
    transport: "stdio" | "http" | undefined;
} | {
    phase: "request";
};
/**
 * The one MCP failure record. Both fields are always present; `httpStatus` rides only with
 * `kind === "http_status"`.
 */
export interface McpFailure {
    /**
     * @contract mcp.failure.kind — the failure's CLASS, one of {@link MCP_FAILURE_KINDS}, decided by
     * {@link classifyMcpFailure} from the thrown value's structure alone (never from its message). It is
     * the SAME word on every face: the per-server status's and the wiring manifest's `errorCode`, the
     * thrown request error's `details.code`, and the `tool_end` frame's `errorCode`. Closed: a consumer
     * switching on it is complete; a new class is a new word for consumers, never a re-meaning of one.
     */
    kind: McpFailureKind;
    /**
     * @contract mcp.failure.delivered — whether the request REACHED the server, as a first-class fact
     * beside the class: `"yes"` = the server answered (a `protocol` rejection proves the exchange
     * happened); `"no"` = provably never sent (a connect-phase errno, a spawn failure, a malformed
     * declaration, a server already known dead — safe to retry or abandon, nothing executed); `"unknown"`
     * = the client stopped waiting or lost the pipe mid-exchange (a timeout, an in-flight close, a reset,
     * an HTTP status from a gateway in front of the server) — the request MAY have executed, so a
     * write-capable tool's side effects must be verified before a retry. Two failures with the same
     * `kind` can differ here (`connection_closed` in flight vs. against a dead server), which is why the
     * verdict is its own field and not folded into the class. It is a function of the class and the SITE,
     * never of the row that matched: {@link DELIVERED_BY_KIND} says what a class means on an established
     * connection, and at the dial the verdict is `"no"` for every class but the two minted from an answer
     * ({@link CONNECT_KEEPS_THE_TABLE_VERDICT}) — a connect failure is a server whose tools never mounted,
     * so no TOOL CALL of the caller's was dispatched, let alone executed. Read
     * {@link CONNECT_KEEPS_THE_TABLE_VERDICT} for what a dial's `"no"` does not promise (an elicitation
     * answered during the dial has already crossed the connection) and for the facts that would let the
     * rule be narrower.
     */
    delivered: McpDelivered;
    /** The HTTP status the endpoint answered with; present iff `kind === "http_status"`. */
    httpStatus?: number;
}
/**
 * The first errno-shaped / undici-shaped `code` on an error's cause chain (`E[A-Z_]+`, `UND_ERR_*`,
 * Node's `ERR_*`). `fetch` reports every network failure as a bare `TypeError: fetch failed` with the
 * real cause nested underneath (and an AggregateError when a host resolved to several addresses), so
 * the code is never on the thrown error itself. The E-prefixed alternative allows `_`: Node's
 * DNS-temporary-failure errno is literally `EAI_AGAIN`.
 */
export declare function networkErrorCode(err: unknown, depth?: number): string | undefined;
/** What a row reads OFF the thrown value: the failure's class, and the status the `http_status` class
 *  carries. A row does NOT write the delivery verdict — that follows from the kind and the SITE, in
 *  one place ({@link DELIVERED_BY_KIND} + {@link classifyMcpFailure}), so a row cannot spell it wrong. */
export type McpFailureClass = {
    kind: McpFailureKind;
    httpStatus?: number;
};
/** One row of the classifier: `when` reads structure only; `reads` names the class it saw. */
export interface McpFailureRule {
    /** The row's name, for the discrimination pins (one pin per row) and for a reader of a verdict. */
    readonly name: string;
    readonly when: (err: unknown, at: McpFailureSite) => boolean;
    readonly reads: (err: unknown) => McpFailureClass;
}
/**
 * The ORDERED classifier. First match wins; the final row matches everything. The order is part of
 * the contract — the SDK's typed errors go first because they are an answer FROM the exchange (an
 * McpError is never a transport failure, whatever its text says), then the HTTP transport's typed
 * error, then the cause-chain codes from most specific (a configuration fault, a spawn) to least
 * (a connect-phase errno, any other network code).
 */
export declare const FAILURE_CLASSIFIER: ReadonlyArray<McpFailureRule>;
/**
 * Classify one MCP failure into {@link McpFailure}: the CLASS comes from the first matching row of
 * {@link FAILURE_CLASSIFIER} (structure only), the VERDICT from {@link DELIVERED_BY_KIND} and the site
 * — one predicate for the dial ({@link CONNECT_KEEPS_THE_TABLE_VERDICT}), never a per-row spelling.
 * Total: every input maps to a record.
 */
export declare function classifyMcpFailure(err: unknown, at: McpFailureSite): McpFailure;
/**
 * What a transport-level HTTP failure can be said to be, in words for a model-facing line: the
 * condition to name, and the delivery verdict. `undefined` for every failure that is NOT of the HTTP
 * transport family (an McpError is an answer FROM the server; a bare error with no structure is
 * `unknown` and gets no transport story). Shared with the A2A client leg, which faces the SAME `fetch`
 * layer and owes the same outcome contract — one classifier, so the two protocols cannot drift on what
 * "the request never reached them" means.
 */
export interface HttpTransportFailure {
    condition: string;
    delivered: McpDelivered;
    httpStatus?: number;
}
export declare function describeHttpTransportFailure(err: unknown): HttpTransportFailure | undefined;
