import { type EventFrame, SessionEventStream } from "./events"; import { ReverseLeaseRuntime } from "./reverse-leases"; import type { BrokerIndexWriter, HostEndpointAdapters, SdkFrame } from "./types"; export type SdkRequestObserver = (kind: "control" | "query", connectionId: string, frame: SdkFrame) => void; /** * When a session publishes its replayable readiness signal. * * `immediate` is the stock contract: `start()` publishes `session_ready` at * once, so a chat daemon that attaches (or replays late) surfaces the session * and creates its stock root. `deferred` prepares the session instead: the * session id, endpoint, and broker registration become discoverable authority, * but no readiness exists for any consumer to act on until `activate()` runs. * That ordering is what lets an operator adopt an existing chat root before the * daemon would otherwise have published one of its own. */ export type SessionReadinessMode = "immediate" | "deferred"; /** * Replayable signal that a deferred session is fully initialized and holding * endpoint authority while its readiness stays withheld. * * It is deliberately not `session_ready`: a broker lifecycle wait can * authenticate it as the child's semantic completion receipt, while every * readiness consumer — chat daemons above all — keeps ignoring it and publishes * no root until the session is explicitly activated. */ export declare const SESSION_PREPARED_EVENT = "session_prepared"; /** Every terminal answer an activation attempt can produce. */ export type SessionActivationOutcome = "activated" | "already" | "not_prepared" | "generation_changed" | "not_authorized" | "authority_unavailable"; /** Proves that a prepared session may publish readiness at this exact generation. */ export type SessionActivationGate = (input: { sessionId: string; generation: number; }) => boolean | Promise; export interface SessionSdkHostOptions extends HostEndpointAdapters { control?: (connectionId: string, frame: SdkFrame) => unknown | Promise; query?: (connectionId: string, frame: SdkFrame) => unknown | Promise; /** Test/lifecycle seam invoked synchronously before fire-and-forget dispatch. */ onFrameAdmitted?: (connectionId: string, frame: SdkFrame) => void; /** Best-effort diagnostic observation of accepted control/query frames. */ onRequest?: SdkRequestObserver; /** Runs before a control response is sent; identity transitions use sendTerminal. */ beforeControlResponse?: (connectionId: string, request: SdkFrame, response: SdkFrame, sendTerminal: () => Promise) => void | Promise; /** Runs only after a successful control response has been sent to the client. */ afterControlResponse?: (connectionId: string, request: SdkFrame, response: SdkFrame) => void | Promise; /** * Classifies the awaited control-response write exactly once: `written` * (sent), `rejected` (the write threw), or `dropped` (the send adapter * deliberately skipped delivery). `afterControlResponse` runs only * on `written`. Used to persist monotonic response-state transitions. */ onControlResponseDelivery?: (connectionId: string, request: SdkFrame, response: SdkFrame, outcome: "written" | "rejected" | "dropped") => void | Promise; installProviderDefinitions?: (capability: string, definitions: unknown) => void; onProviderDefinitionsRemoved?: (capability: string) => void; onReverseCancel?: (requestId: string, reason: "provider_disconnected" | "lease_released") => void; /** Best-effort capabilities mirrored from the native transport for out-of-band consumers. */ connectionCapabilities?: (connectionId: string) => ReadonlySet | undefined; /** Readiness publication mode; defaults to the stock immediate contract. */ readiness?: SessionReadinessMode; /** * Authorization for a deferred activation. It is consulted on every attempt * that would publish readiness and never on an idempotent replay, and a gate * that fails is never read as authorization. */ activationGate?: SessionActivationGate; } /** Shared by the replay filter and transport live broadcasts: a connection * must see the same capability-gated event kinds on both legs, or live and * replay delivery diverge for the same subscriber. */ export declare const TOOL_ACTIVITY_CAPABILITY = "tool_activity_v2"; export declare const CAP_GATED_FRAME_KINDS: ReadonlySet; /** SDK hosting is independent of notification configuration. Only root sessions host an endpoint. */ export declare function shouldHostSdk(_settings: unknown, isTopLevel: boolean, env?: NodeJS.ProcessEnv): boolean; /** Adapter-based session host; bus wiring owns NotificationServer creation and transport framing. */ export declare class SessionSdkHost { #private; readonly events: SessionEventStream; readonly reverse: ReverseLeaseRuntime; constructor(options: SessionSdkHostOptions); get started(): boolean; get generation(): number; /** True while the session holds endpoint authority but has published no readiness. */ get prepared(): boolean; /** True once readiness for the current generation has been published. */ get ready(): boolean; /** Current installed definitions for a live provider capability. */ getProviderDefinitions(capability: string): unknown | undefined; /** Release reverse leases after the transport reports a WebSocket disconnect. */ handleDisconnect(connectionId: string): void; /** Route malformed transport bytes through the host's structured protocol-error seam. */ handleMalformedFrame(connectionId: string, message: string): void; /** Adds an event to the resumable event ring. Transport delivery is owned by bus wiring. */ emitEvent(frame: SdkFrame): EventFrame; emitAutoroutingInactiveNotice(): EventFrame; start(): Promise<"started" | "already">; /** * Publish the readiness signal a prepared session withheld. * * The attempt is refused unless the session is still started at exactly * `expectedGeneration` (when supplied) and the activation gate authorizes it * at that same generation. Authority is re-proved after the gate resolves, * because a stop or an endpoint roll can land while it is in flight, and an * exact retry after a successful activation is answered `already` instead of * publishing a second readiness signal. */ activate(expectedGeneration?: number): Promise; stop(): Promise<"stopped" | "already">; registerWithBroker(writer: BrokerIndexWriter): Promise; }