import { CreateEventInput, CreateSessionInput, UpdateSessionInput, UpdateEventStatusInput, Event, Session, EventSource, DeviceKey, GrantSessionKeyInput } from '../types';
/**
 * Download URL response
 */
export interface DownloadUrlResponse {
    downloadUrl: string;
    expiresAt: string;
}
/**
 * AppSync GraphQL client with WebSocket subscriptions
 */
export declare class AppSyncClient {
    private authenticated;
    private currentUserId;
    private currentEmail;
    private tokens;
    private activeSubscriptions;
    private pendingRefresh;
    private lastRefreshFailureAt;
    private static readonly REFRESH_BACKOFF_MS;
    private deviceKeyWatcher;
    private environment;
    constructor();
    /**
     * Get the current authenticated user ID
     */
    getCurrentUserId(): string;
    /**
     * Get the current authenticated user email
     */
    getCurrentUserEmail(): string | null;
    /**
     * Authenticate using stored OAuth tokens from keychain
     */
    authenticateWithStoredTokens(): Promise<boolean>;
    /**
     * Refresh expired tokens
     */
    private refreshTokens;
    /**
     * Do the work of refreshing tokens. Tries the caller-supplied tokens
     * first; on failure, re-reads from storage and retries once with a
     * potentially-fresher refresh token. This is the self-healing path
     * that lets `codevibe login` (which writes new tokens to the
     * keychain) recover running daemons without requiring a restart:
     * the in-memory copy the daemon cached at boot may be invalid, but
     * whatever the user just wrote from the login flow is still valid.
     *
     * Splitting the two attempts into a pure-network helper keeps the
     * orchestration readable without duplicating the fetch + body-shape
     * plumbing.
     */
    private performRefresh;
    /**
     * POST to Cognito's /oauth2/token with a refresh_token grant.
     * Returns the parsed body on 200, or null on any failure (network
     * error, non-2xx response, JSON parse failure). Caller decides how
     * to proceed — this helper is side-effect-free beyond logging.
     */
    private callCognitoRefresh;
    /**
     * Apply a successful refresh response: update in-memory cache first,
     * clear the backoff sentinel, then persist to storage. Success here
     * is defined as "the process has usable fresh tokens in memory" —
     * storage persistence is degraded-success, not a failure mode.
     *
     * Ordering matters. The API call to Cognito already succeeded, which
     * means we hold valid access/id tokens right now. If we delayed the
     * in-memory update until after persistence and the keychain write
     * threw (keychain locked, disk full, file-backend permission error),
     * we'd be stuck with stale-and-known-dead tokens in memory while
     * holding valid fresh tokens in local scope that vanish at the end
     * of this function. That would re-break both guarantees this hotfix
     * makes: no-restart recovery becomes "restart required to escape
     * the keychain-lock window," and backoff stays unarmed so the
     * caller hot-loops against a working Cognito endpoint — R1's MEDIUM
     * on round 1 of this review. Persistence-failure is loud-logged
     * so operators can see degraded durability without losing availability.
     */
    private applyRefreshedTokens;
    /**
     * Check if authenticated
     */
    isAuthenticated(): boolean;
    /**
     * Sign out
     */
    signOut(): void;
    /**
     * Make a GraphQL request
     */
    private graphqlRequest;
    /**
     * Create a session
     */
    createSession(input: CreateSessionInput): Promise<Session>;
    /**
     * Update a session
     */
    updateSession(input: UpdateSessionInput): Promise<Session>;
    /**
     * Get a session
     */
    getSession(sessionId: string): Promise<Session | null>;
    /**
     * Create an event
     */
    createEvent(input: CreateEventInput): Promise<Event>;
    /**
     * Update event status
     */
    updateEventStatus(input: UpdateEventStatusInput): Promise<Event>;
    /**
     * List events for a session
     */
    listEvents(sessionId: string, source?: EventSource, limit?: number): Promise<Event[]>;
    /**
     * List the authenticated user's sessions. Paginates automatically
     * via nextToken so callers always get the complete set.
     */
    listSessions(limit?: number): Promise<Array<{
        sessionId: string;
        agentType: string;
        status: string;
        lastHeartbeatAt: string | null;
    }>>;
    /**
     * Mark stale ACTIVE sessions of a given agentType INACTIVE so they
     * stop appearing in the mobile app's session list. Called at daemon
     * startup to clean up after daemons that died without running their
     * graceful shutdown (crash, auth-loop death, force-kill, power loss).
     *
     * Staleness rule: lastHeartbeatAt is older than `staleThresholdMs`
     * (default 15 min — a conservative ~7.5× the 2-min heartbeat
     * interval, giving legitimately-active daemons on other machines
     * ample margin before we consider their session abandoned).
     *
     * Safety:
     *   - Only sessions with status === 'ACTIVE' are candidates.
     *   - Sessions explicitly listed in `excludeSessionIds` are skipped
     *     (caller can pass the session the daemon is about to attach to
     *     if the ID is known before the sweep).
     *   - Absent `lastHeartbeatAt` (never-heartbeated sessions — should
     *     only happen for rows created within the last few seconds)
     *     treats the session as fresh and skips.
     *   - updateSession failures are logged as warnings and don't abort
     *     the sweep — best-effort cleanup.
     *
     * Returns the number of sessions actually marked INACTIVE.
     */
    sweepOrphanSessions(opts: {
        agentType: string;
        staleThresholdMs?: number;
        excludeSessionIds?: string[];
    }): Promise<number>;
    /**
     * List user device keys
     */
    listUserDeviceKeys(): Promise<DeviceKey[]>;
    /**
     * Register device key
     */
    registerDeviceKey(deviceId: string, publicKey: string, platform: string, deviceName: string): Promise<void>;
    /**
     * Grant a session key to an additional device by appending a new
     * per-device wrapped key blob to the session's encryptedKeys array.
     *
     * This is the client-side entry point for the session re-key flow:
     * when a new device (typically a mobile app) registers for a user who
     * already has active sessions, an already-enrolled device (typically
     * the desktop plugin) wraps the session key for the new device's
     * public key and calls this mutation to append it. The backend VTL
     * resolver enforces that the caller owns the session.
     */
    grantSessionKey(input: GrantSessionKeyInput): Promise<void>;
    /**
     * Get attachment download URL
     */
    getAttachmentDownloadUrl(s3Key: string): Promise<DownloadUrlResponse>;
    /**
     * Subscribe to events for a session
     */
    subscribeToEvents(sessionId: string, onEvent: (event: Event) => void, onError?: (error: Error) => void): () => void;
    /**
     * Build WebSocket URL
     */
    private buildRealtimeUrl;
    /**
     * Create WebSocket subscription
     */
    private createSubscription;
    /**
     * Send subscription start message
     */
    private sendSubscriptionStart;
    /**
     * Reset keep-alive timer
     */
    private resetKeepAliveTimer;
    /**
     * Handle subscription error with two-phase reconnection:
     * Phase 1 (urgent): Exponential backoff for first N attempts
     * Phase 2 (persistent): Fixed interval retry indefinitely
     */
    private handleSubscriptionError;
    /**
     * Cleanup subscription state
     */
    private cleanupSubscriptionState;
    /**
     * Subscribe to device key registrations for the caller's own userId.
     * Returns a cleanup function that stops the subscription.
     */
    subscribeToDeviceKeyRegistered(userId: string, onNewDevice: (device: DeviceKey) => void, onReconnect?: () => void, onError?: (error: Error) => void): () => void;
    /**
     * Public method to stop the device key watcher. Used at plugin shutdown.
     */
    stopDeviceKeyWatcher(): void;
    private stopDeviceKeyWatcherInternal;
    private createDeviceKeyWatcherConnection;
    private sendDeviceKeyWatcherStart;
    private resetDeviceKeyWatcherKeepAlive;
    private handleDeviceKeyWatcherError;
    private heartbeatTimers;
    /**
     * Start periodic heartbeat for a session.
     * Updates lastHeartbeatAt on the session every intervalMs (default 2 minutes).
     */
    startHeartbeat(sessionId: string, intervalMs?: number): void;
    /**
     * Stop heartbeat for a session.
     */
    stopHeartbeat(sessionId: string): void;
    /**
     * Send a single heartbeat update.
     */
    private sendHeartbeat;
    /**
     * Cleanup all subscriptions and heartbeats
     */
    cleanupSubscriptions(): void;
}
