import { type McpElicitRequest, type McpElicitResponse } from "@sema-agent/core"; /** A live elicitation frame delivered to whoever is tailing this run's stream. `type` IS the SSE event name * (mirrors the named-event convention: the payload also carries `type` so a proxy that strips event names still * works). The shell renders the `elicitation` frame as a dialog and dismisses on `elicitation_complete`. */ export interface ElicitationFrame { type: "elicitation" | "elicitation_complete"; elicitationId: string; mcpServerName: string; /** "elicitation" only: the FENCED + secret-redacted human-facing prompt. UNTRUSTED — never re-feed to a model. */ message?: string; /** "elicitation" only: the server's requested input schema. OPAQUE passthrough — never interpreted/validated; * still server-controlled UNTRUSTED, so the shell must fence it on display. */ requestedSchema?: unknown; /** "elicitation" only: always "form" in v1 (url-mode is rejected upstream). */ mode?: "form"; /** "elicitation_complete" only: how it resolved. */ action?: McpElicitResponse["action"]; /** Server-signed emit timestamp (additive, [ref]§五→[ref]). These side-frames are SERVER-minted — they have * no core-stream anchor, so no `eventId` (absence is honest, not an omission); this is the frame's own time * coordinate instead, same posture as the stream-approval frames' `serverNowMs`. */ serverNowMs?: number; } /** The per-run context `onElicit` recovers via ALS. `emit` delivers a frame to the run's live stream; * `abortSignal` (the run's own cancel signal) lets a run-abort promptly cancel an elicitation parked awaiting a * human, since the MCP per-request signal may not be chained to the run abort. */ export interface ElicitationRunContext { taskId: string; owner: string | null; emit: (frame: ElicitationFrame) => void | Promise; abortSignal?: AbortSignal; } export interface ElicitationThrottle { /** Max concurrent in-flight elicitations per run leg (parallel tool calls can each elicit). Breach ⇒ decline. */ maxConcurrentPerRun: number; /** Max total elicitations one run leg will surface to the human. Breach ⇒ decline (flood defense). */ maxTotalPerRun: number; /** Min gap (ms) between two elicitations from the SAME server within a run. Within ⇒ decline (burst guard). */ minIntervalMsPerServer: number; /** An unanswered elicitation auto-declines after this (ms) — the human walked away; don't hold core forever. */ ttlMs: number; } export declare const DEFAULT_ELICITATION_THROTTLE: ElicitationThrottle; /** Validate the respond body = core's `McpElicitResponse`. `content` is accept-only OPAQUE passthrough (core never * validates it against `requestedSchema`); we validate only the outer shape (a flat record of scalars / string[]), * never the schema. A non-accept drops content (MCP convention). */ export declare function parseElicitationResponse(body: unknown): { ok: true; value: McpElicitResponse; } | { ok: false; error: string; }; /** * Coordinates inbound MCP elicitations for one server instance. Process-local + same-replica (the pending map is in * memory, like `steerableRuns`): a respond that lands on another replica finds nothing → 404. Present (passed into * `RunnerDeps.onElicit` + the respond route) ONLY when `MCP_ELICITATION_ENABLED` — absent ⇒ core advertises no * elicitation capability to any server (doubly fail-closed with the per-server `McpServerSpec.elicitation` default OFF). */ export declare class ElicitationCoordinator { private readonly als; private readonly pending; private readonly counters; private readonly throttle; private readonly now; constructor(throttle?: ElicitationThrottle, now?: () => number); /** Run `fn` with the per-run elicitation context ambient. On exit, cancel any still-pending elicitation for this * run (a live-only elicitation cannot outlive its leg) and drop the run's counters (no leak). The cancel covers * the case where the leg ends/throws while an elicitation is parked but the leg's own loop has already drained. */ runWithContext(ctx: ElicitationRunContext, fn: () => Promise): Promise; /** `RunnerDeps.onElicit`. Core calls this when an opted-in server elicits the END USER mid-tool-call; the resolved * value is sent back to the server on the still-open connection. Fail-closed (`decline`/`cancel`) in every * uncertain case. Arrow property so it can be passed as `onElicit: coordinator.elicit` with `this` bound. */ elicit: (req: McpElicitRequest, signal?: AbortSignal) => Promise; /** `POST /v1/elicitations/:id/respond` — resolve a parked elicitation with the shell's answer. Owner-gated with a * 404 (no existence oracle): a non-owner AND an unknown id (answered / expired / wrong replica) both get 404. * Returns the HTTP {status, body}; the HTTP layer owns auth (gatedPrincipal + REQUIRE_PRINCIPAL) before calling. */ respond(id: string, principal: string | undefined, body: unknown): { status: number; body: unknown; }; /** Test/observability hook: number of currently-parked elicitations. */ pendingCount(): number; private countersFor; } //# sourceMappingURL=elicitation.d.ts.map