import type { CronRecord, Principal, RunRecord, ScalingSnapshot, ServerAssistant, ServerDeployments, ServerStateStore, ThreadRecord } from '../types/server.js'; import { CronScheduler, type CronSchedulerOptions } from './cron.js'; import { type DrainResult, RunManager, type RunManagerOptions } from './runs.js'; /** Options for `createAgentServer()`. */ export interface AgentServerOptions extends Omit { /** The assistants the server exposes, by id. A compiled graph satisfies the contract as it is. */ assistants: Record; /** Path the routes are mounted under, such as `/api`. Defaults to the root. */ basePath?: string; /** * Identifies the caller. Returning a `Response` answers the request itself, which is how a custom * challenge or redirect is returned; returning nothing refuses the request as unauthenticated. */ authenticate?: (request: Request) => Promise | Principal | Response | undefined; /** Serves requests without authentication when no hook is configured. Defaults to true. */ allowAnonymous?: boolean; /** * Scopes a principal must carry, by route group. The deployment and replica routes always need * `admin`, which defaults to a scope named `admin`, whenever an `authenticate` hook is set. */ scopes?: { read?: string; write?: string; admin?: string; }; /** * Serves `GET /metrics` in Prometheus text and `GET /scaling` as JSON, for dashboards and * autoscalers. On by default, behind `authenticate` like any read; `{ public: true }` answers them * without it, for a scraper inside the cluster. `false` turns both off. */ metrics?: boolean | { public?: boolean; }; /** * The deployments registry, which adds the `/deployments` and `/replicas` routes and heartbeats * this replica. See `Deployments` in `nexus-ai-pro/server/deployments`. */ deployments?: ServerDeployments; /** What this replica reports about itself besides its runs, such as its host or image tag. */ replicaMetadata?: Record; /** Schedules runs. Cron jobs given here exist from startup; the API can add more. */ cron?: CronSchedulerOptions & { jobs?: ReadonlyArray & { id?: string; }>; }; /** How often a live event stream sends a comment to keep the connection open, in milliseconds. Defaults to 15 seconds. */ heartbeatMs?: number; } /** The server: one handler, with the pieces behind it available for tests and custom routes. */ export interface AgentServer { /** Answers one request. This is the whole HTTP surface. */ handle(request: Request): Promise; /** Runs, threads, and the event log. */ readonly runs: RunManager; /** The cron scheduler, when one is configured. */ readonly cron?: CronScheduler; /** * Starts the scheduler, re-claims runs abandoned by a crashed worker, starts the worker loop when * there is a queue, and starts heartbeating when there is a deployments registry. */ start(): Promise; /** Stops the scheduler, the worker loop, and the heartbeat. Runs in flight are left to finish. */ stop(): Promise; /** * Prepares this replica to shut down: `GET /health` answers `503` so the load balancer stops * sending, new work goes to the queue, and runs in flight finish or, after `timeoutMs`, are handed * to another worker. Call it on `SIGTERM`, then `stop()`. */ drain(options?: { timeoutMs?: number; }): Promise; /** The numbers an autoscaler reads, as `GET /scaling` reports them. */ scaling(): Promise; } /** * A self-hosted agent server: assistants, threads, runs, cron jobs, and resumable event streams. * * The handler takes a `Request` and returns a `Response`, so it runs on Node's `http` through * `toNodeListener()`, and equally under Express, Fastify, Nest, or any runtime with `fetch` types. * Nothing is held in the handler itself: runs are durable operations and threads are records in a * store, so a second replica pointed at the same Redis or Postgres serves the same threads, finishes * a run whose worker died, and streams events a client started reading somewhere else. */ export declare function createAgentServer(options: AgentServerOptions): AgentServer; export type { ThreadRecord, RunRecord, ServerStateStore };