import type { ResidencyPolicy } from '../types/residency.js'; /** One region's agent server: its base URL, or a handler in this process, such as `server.handle`. */ export interface RegionTarget { /** The server's base URL, such as `https://agents.eu-west.example.com`. */ url?: string; /** A handler in this process instead, given every request as the server would receive it. */ handle?: (request: Request) => Promise; } /** How a gateway checks each region. */ export interface RegionHealthOptions { /** How often `start()` checks every region, in milliseconds. Defaults to 10 seconds. */ everyMs?: number; /** How long a check may take before it counts as a failure, in milliseconds. Defaults to 2 seconds. */ timeoutMs?: number; /** Failures in a row, of checks and of forwarded requests, before a region is down. Defaults to 1. */ failuresToDown?: number; /** The path checked. Defaults to `/health`, which a draining server answers with 503. */ path?: string; } /** Options for `regionalGateway()`. */ export interface RegionalGatewayOptions { /** Each region's agent server, by region. Its threads and runs carry the region in their ids. */ regions: Readonly>; /** Which regions each tenant may reach. */ residency: ResidencyPolicy; /** * The tenant a request is for, such as from a verified token. A request with none follows the * policy's `untenanted` rule. */ tenantOf: (request: Request) => string | undefined | Promise; /** How each region is checked. */ health?: RegionHealthOptions; /** Replaces the global `fetch` for regions given by URL. */ fetch?: typeof globalThis.fetch; /** * How `GET /threads` and `GET /runs` are answered: `merged`, the default, asks every region the * tenant may reach and merges their answers, newest first; `home` asks the home region alone, as * before 2.18. */ lists?: 'merged' | 'home'; /** Hears each failure to reach a region. */ onError?: (error: unknown, region: string) => void; /** Replaces the system clock, for tests. */ now?: () => Date; } /** Where a region stands, as the gateway sees it. */ export interface RegionHealth { /** Whether new work is sent to it. */ up: boolean; /** Failures in a row. */ failures: number; /** ISO-8601 time it was last checked or reached. */ checkedAt?: string; /** Why it is down, when it is. */ reason?: string; /** True when an operator marked it, which only an operator undoes. */ manual?: boolean; } /** A gateway in front of an agent server per region. */ export interface RegionalGateway { /** Answers one request: forwarded to a region, or refused. */ handle(request: Request): Promise; /** Checks every region now, and returns where each stands. */ check(): Promise>; /** Where each region stands. */ status(): Record; /** Takes a region out, as an operator does before maintenance, until `markUp()`. */ markDown(region: string, reason?: string): void; /** Puts a region back, clearing its failures. */ markUp(region: string): void; /** Checks every region on a timer until `stop()`. */ start(): void; /** Stops `start()`. */ stop(): void; } /** A region a merged list could not ask, and why. */ export interface RegionListGap { /** The region. */ region: string; /** Why it is missing: down, unreachable, or what it answered. */ reason: string; } /** * A gateway in front of one agent server per region. A request for a thread or a run, whose id names * the region that holds its data, goes to that region only: refused with a 403 when the tenant may not * reach it, and answered 503 while the region is down, since its data is there and nowhere else. A * request for new work goes to the tenant's home region, the first region it may reach, and, when that * is down or does not answer, to the next one it may reach, never to another. A tenant that may reach * no region the gateway serves is refused with a 403 naming the rule. A list of the tenant's threads or * runs is gathered from every region it may reach, newest first, naming any that could not answer. * * ```ts * const gateway = regionalGateway({ * regions: { 'eu-west': { url: 'https://eu-west.agents.internal' }, 'us-east': { url: 'https://us-east.agents.internal' } }, * residency: policy, * tenantOf: (request) => verify(request).tenantId, * }); * gateway.start(); * serve({ fetch: gateway.handle }); * ``` */ export declare function regionalGateway(options: RegionalGatewayOptions): RegionalGateway;