import { type SessionIndex } from "../broker/session-index"; import { type SdkDispatchContext, type SdkDispatchHandler } from "../client/client"; import { type ActivatedPreparedSession } from "../session-activation"; /** * Exact identity of one attached SDK session endpoint. Providers persist it next to * their conversation state and re-prove it before every resume, so it must be derived * in exactly one place: a caller that recomputes the digest by hand silently stops * matching the moment the bound fields change. */ export declare function sessionAttachmentAuthorityId(input: { sessionId: string; generation: number; pid: number; endpointMtimeMs: number | undefined; url: string; token: string; }): string; /** The only capability a provider may retain for an attached SDK session. */ export interface SessionAttachment { readonly sessionId: string; readonly authorityId?: string; /** Current Router-owned transport identity for this exact attachment's reverse leases. */ readonly connectionId?: string; readonly generation: number; isCurrent(): boolean; send(frame: Record): unknown; /** * Idempotent provider-lease heartbeat send that skips the pre-send authority * reconcile (#4689). * * Optional so existing exported-capability implementations (including * consumer-provided `resolveAttachment` callbacks on the Discord/Slack daemon * options) stay source- and runtime-compatible (#4730 review). It is NOT a * fail-open fallback: a caller that finds it absent must fail closed rather * than route the heartbeat through `send()`, which would restore the 5s * heartbeat-forced locked rescan this fix exists to remove. Router-owned * attachments always provide it. */ sendMaintenance?(leaseId: string): unknown; /** Revoke this exact capability after provider admission or replay fails closed. */ retire?(): Promise; } /** * Provider-local notification capability. This is deliberately not an * attachment lease: it carries no endpoint, connection, generation, or * authority identity and its cancellation can only stop this subscription. */ export interface NotificationSubscription { readonly sessionId: string; readonly subscriptionId: string; readonly cursor: { readonly generation: number; readonly seq: number; }; readonly isActive: () => boolean; readonly send: (frame: Record) => unknown; readonly advanceCursor: (generation: number, seq: number) => void; readonly cancel: (reason?: string) => void; } export type NotificationCleanupState = "pending" | "failed" | "completed"; export interface NotificationCleanupReceipt { readonly subscriptionId: string; readonly sessionId: string; readonly state: NotificationCleanupState; readonly reason?: string; } export interface SessionGenerationEvidence { readonly source: "session_index"; readonly observedIndexSeq: number; readonly evidenceIndexSeq: number; } export interface SessionGenerationUnknownEvidence { readonly source: "session_index"; readonly observedIndexSeq: number; } export type SessionGenerationStatus = { readonly status: "current"; readonly evidence: SessionGenerationEvidence; } | { readonly status: "retired"; readonly evidence: SessionGenerationEvidence & { readonly event: "host_unregistered" | "session_closed" | "session_deleted"; }; } | { readonly status: "replaced"; readonly currentGeneration: number; readonly evidence: SessionGenerationEvidence; } | { readonly status: "unknown"; readonly reason: "invalid_generation" | "index_unavailable" | "index_incomplete" | "session_not_observed" | "generation_not_observed" | "generation_reused" | "ambiguous_authority" | "proof_expired" | "reconciliation_incomplete"; readonly evidence?: SessionGenerationUnknownEvidence; }; /** The transport surface Router keeps private behind its attachment capabilities. */ export interface SessionRouterClient { onFrame(handler: (frame: Record) => void): () => void; onReconnect?(handler: () => void): () => void; connect?(): Promise; request(frame: Record, options?: { timeoutMs?: number; /** Synchronous pre-send observer; a throw aborts the dispatch before the wire. */ beforeDispatch?: (context: SdkDispatchContext) => void; /** Synchronous post-send boundary observer for transport-close-aware consumers. */ onDispatch?: (context: SdkDispatchContext) => void; }): Promise>; /** Current private transport connection identity, surfaced only through its exact attachment. */ readonly connectionId?: string; close(): Promise; send(frame: Record): void; } /** One frame after the caller's envelope/payload identity correlation. */ export interface SessionRouterFrame { readonly body: Record; readonly name: string | undefined; readonly sessionId: string | undefined; readonly generation: number | undefined; readonly commandId?: string; readonly turnId?: string; readonly publicationId?: string; readonly seq?: number; } export type SessionRouterFrameCorrelator = (frame: Record) => SessionRouterFrame | undefined; export interface SessionRouterDeps { createClient?: (authority: { readonly sessionId: string; readonly generation: number; readonly pid: number; readonly endpointMtimeMs: number; }) => Promise; createIndex?: (agentDir: string) => SessionIndex; createBrokerClient?: () => Promise; /** Receives only an opaque capability and correlated provider-neutral frames. */ onFrame?: (attachment: SessionAttachment, frame: SessionRouterFrame) => Promise | void; /** Test/runtime observer invoked after a frame's delivery and cursor update settle. */ onFrameSettled?: (attachment: SessionAttachment, frame: SessionRouterFrame) => void; onAttachment?: (attachment: SessionAttachment) => Promise | void; /** Called only after the opaque capability becomes externally current. */ onAttachmentReady?: (attachment: SessionAttachment) => Promise | void; /** Called when the Broker index no longer reports an attached session as live. */ onSessionRemoved?: (attachment: SessionAttachment, reason?: "removed" | "replaced" | "replaced_same_generation") => Promise | void; /** Narrow provider surface for notification consumers such as Telegram. */ onNotificationSubscription?: (subscription: NotificationSubscription) => Promise | void; onNotificationSubscriptionReady?: (subscription: NotificationSubscription) => Promise | void; onNotificationFrame?: (subscription: NotificationSubscription, frame: SessionRouterFrame) => Promise | void; onNotificationSubscriptionRemoved?: (subscription: NotificationSubscription, reason?: "removed" | "replaced" | "replaced_same_generation" | "cancelled") => Promise | void; onReconciled?: () => void; setInterval?: typeof setInterval; clearInterval?: typeof clearInterval; setTimeout?: typeof setTimeout; clearTimeout?: typeof clearTimeout; /** Test seam for the idle liveness-sweep cadence (#4689). */ idleSweepMs?: number; /** Test seam for the bounded initial attach pass. */ startupAttachBudgetMs?: number; } export type SessionRouterProviderDeps = Pick; export interface SessionRouterOptions { agentDir: string; deps?: SessionRouterDeps; /** Runtime-specific identity validation; Router supplies a conservative fallback. */ correlateFrame?: SessionRouterFrameCorrelator; } export type SessionRouterErrorPhase = "pre_send" | "ambiguous"; export declare class SessionRouterError extends Error { readonly phase: SessionRouterErrorPhase; constructor(phase: SessionRouterErrorPhase, message?: string); } /** * Broker-index-backed SDK attachment authority. Providers receive only opaque * attachment capabilities; endpoint records and SDK clients remain here. */ export declare class SessionRouter { #private; constructor(options: SessionRouterOptions); /** Provider-local cleanup outcomes; core authority never depends on these. */ notificationCleanupReceipts(): NotificationCleanupReceipt[]; isReady(): boolean; /** Starts reconciliation and the index watcher. */ start(): Promise; /** Exposed for deterministic callers and reconciliation tests. */ reconcile(options?: { waitForReplay?: boolean; }): Promise; /** Ingests a credential-bearing Broker lifecycle result directly into Router custody. */ adoptLifecycleResult(value: unknown, fallback: { sessionId: string; cwd: string; }): Promise; stop(): Promise; /** Returns an opaque lease only while the exact attachment generation is live. */ attachment(sessionId: string, expectedGeneration?: number): SessionAttachment | null; /** Sends an SDK command through the current attachment without exposing its client. */ request(sessionId: string, frame: Record, expectedGeneration?: number, expectedAttachment?: SessionAttachment, options?: { timeoutMs?: number; beforeDispatch?: (context: SdkDispatchContext) => void; onDispatch?: SdkDispatchHandler; }): Promise>; /** Resolves the exact provider-neutral binding authority for operator adoption. */ bindingAuthority(sessionId: string): Promise<{ sessionId: string; endpointGeneration: number; } | undefined>; /** * Reconciles one exact endpoint generation without exposing endpoint or process * credentials. Retirement is returned only from a retained positive terminal * index event; absence, corruption, incomplete reconciliation, and generation * reuse remain explicitly unknown. */ generationStatus(sessionId: string, endpointGeneration: number): Promise; /** Activates a prepared session through one Router-owned, one-shot SDK client. */ activatePreparedSession(sessionId: string): Promise; /** Lists saved sessions through Router-owned Broker discovery without exposing credentials or mutation authority. */ listBrokerSessions(input: Record, idempotencyKey: string): Promise>; }