import { type GenericNotificationSessionSource, type NotificationConfig } from "./config"; /** Minimal session-manager surface shared by extension, TUI, and headless hosts. */ export interface NotificationSessionContext { sessionManager: { getCwd(): string; getSessionId(): string; }; } /** A snapshot of the current session, resolved from the session manager per operation. */ export interface BoundNotificationSession { readonly context: Context; readonly cwd: string; readonly sessionId: string; unbind(): void; } export type NotificationEndpointStartResult = "started" | "already" | "disabled" | "failed"; /** * Outcome of the Telegram daemon preflight. * * `pending` means ownership acquisition is in flight and was deliberately NOT * awaited: chat daemons are optional notification adapters, never session * authority, so reconciliation on an awaited session-lifecycle path must never * block on one. A pending preflight neither stops the runtime nor marks it * blocked — the runtime starts, adapters stay withheld while the owner state is * not ready, and the ensure's settle callback re-reconciles. */ export type TelegramDaemonPreflightResult = "ready" | "pending" | "blocked_identity" | "failed"; /** * The endpoint implementation is deliberately injected. The controller owns * policy and session-local state while the extension continues to own its * concrete NotificationServer resources. */ export interface NotificationSessionRuntime { isRunning(binding: BoundNotificationSession): boolean; start(binding: BoundNotificationSession): Promise; stop(binding: BoundNotificationSession): Promise; /** * Provider-neutral ownership re-proof, called for ANY effective chat * provider before the Telegram-specific preflight. * * A credential, destination, or actor-authorization change invalidates the * ownership proof that authorized this runtime's adapters. The runtime must * withhold its LOCAL adapters until the new identity is proved. It does NOT * revoke an already-attached external chat daemon's SessionRouter * attachment, so such a daemon can still observe host events and reach the * host's inbound/control paths during the window; closing that requires an * authenticated chat-attachment authority boundary at the Router layer. * Implementations must not await the daemon ensure itself. */ reproveOwnership?(binding: BoundNotificationSession): Promise; /** * Proves the complete Telegram owner identity before a generic endpoint can * emit a frame. `blocked_identity` is fail-closed and starts nothing. */ ensureTelegramDaemon?(binding: BoundNotificationSession): Promise; /** Rotates a running/default endpoint into Telegram-isolated chat scope. */ isolateTelegram?(binding: BoundNotificationSession): Promise; /** Refresh mutable delivery policy from the same configuration snapshot used for reconciliation. */ refreshPolicy?(binding: BoundNotificationSession, policy: NotificationRuntimePolicy): void; /** Enables delivery only after the controller has committed a stable policy. */ activate?(binding: BoundNotificationSession): void; } export interface NotificationSessionStatus { eligible: boolean; locallyEnabled: boolean; genericSessionEnabled: boolean; genericEligibilitySource: GenericNotificationSessionSource; running: boolean; } export interface NotificationSessionReconcileResult { outcome: NotificationEndpointStartResult | "stopped"; status: NotificationSessionStatus; } export interface NotificationRuntimePolicy { redact: boolean; verbosity: NotificationConfig["verbosity"]; /** Generic live-frame delivery policy; never proof of provider effectiveness. */ stream: boolean; mode: "provisional" | "committed"; } export interface NotificationSessionControllerOptions { /** Gate A result, resolved once by the SDK from the canonical host predicate. */ eligible: boolean; /** Reads the global-only, schema-default-resolved notification configuration. */ getConfig(): NotificationConfig; /** Kept as a reference so test and embedding hosts can supply their own environment. */ env?: NodeJS.ProcessEnv; /** This process was launched by a marked GJC child spawn site. */ spawnedByGjc?: boolean; } export declare class NotificationSessionController { #private; constructor(options: NotificationSessionControllerOptions); /** Attach the concrete generic endpoint implementation used by this host. */ attachRuntime(runtime: NotificationSessionRuntime): () => void; /** * Bind a fresh session snapshot. Callers should not cache it: cwd and session * id may change on `/new`, fork, or resume. */ bind(context: Context): BoundNotificationSession; /** Preserve session-local safety state and pending-operation ownership across a session rekey. */ rekeySession(previousSessionId: string, nextSessionId: string): void; query(context: Context): NotificationSessionStatus; /** Stop the current endpoint during host shutdown without changing local preference. */ stopCurrentSession(context: Context): Promise; /** * Hold this session's endpoint inactive after a foreign-daemon identity race. * The block remains until an explicit same-identity reconnect or CAS restore clears it. */ enterBlockedRuntime(context: Context): Promise; /** Clear a block only after the caller has verified a safe same-identity reconnect or restore. */ clearBlockedRuntime(context: Context): Promise; setLocalEnabled(context: Context, enabled: boolean): Promise; reconcileCurrentSession(context: Context): Promise; }