/**
 * Telegram Lifeline — minimal persistent process that owns the Telegram connection.
 *
 * Architecture:
 *   Lifeline (this process)
 *     ├── Telegram Bot polling (always running)
 *     ├── Message queue (persisted to disk)
 *     └── Server Supervisor (manages full Instar server as child)
 *
 * The lifeline is intentionally minimal — it only handles:
 *   1. Telegram message polling
 *   2. Forwarding messages to the server
 *   3. Queuing messages when server is down
 *   4. Replaying queued messages when server recovers
 *   5. Responding to /lifeline commands directly
 *   6. Supervising the server process
 *
 * This ensures the user always has a communication channel even when
 * the full server crashes, runs out of memory, or gets stuck.
 *
 * RULE 3: EXEMPT — this module is the legitimate Telegram Bot API
 * client, not a state detector that parses ambient external state.
 * The `fetch()` calls to api.telegram.org implement the documented
 * Bot API contract (getUpdates, sendMessage, etc.); upstream evolution
 * here is governed by Telegram's API versioning, not by silent
 * format drift that Rule 3 canaries are designed to catch. The
 * Telegram-side health is observed via the polling-alive scenario
 * (.instar/scenarios/v1.0.0/11-telegram-polling-alive.scenario.json).
 */
export declare class TelegramLifeline {
    private config;
    private projectConfig;
    private queue;
    private supervisor;
    private polling;
    private lastUpdateId;
    private pollTimeout;
    private offsetPath;
    private stopHeartbeat;
    private replayInterval;
    private restartSignalInterval;
    private sessionRecoveryInterval;
    private lifelineTopicId;
    private lockPath;
    private consecutive409s;
    private consecutive429s;
    private pollBackoffMs;
    private activeDoctorSession;
    private activeDoctorSecret;
    private doctorSessionTimeout;
    constructor(projectDir?: string);
    /**
     * Start the lifeline — begins Telegram polling and server supervision.
     */
    start(): Promise<void>;
    /**
     * Stop all in-flight / scheduled mutation sources so the queue snapshot
     * is consistent when persisted.
     */
    private quiesceEverything;
    /**
     * Install the restart orchestrator and watchdog. Called from start().
     *
     * The orchestrator owns the process.exit call. The watchdog requests
     * restarts via the orchestrator on threshold crossings, subject to
     * rate-limit state on disk.
     */
    private installOrchestratorAndWatchdog;
    /**
     * Read drift-promoter config from .instar/config.json, falling back to
     * defaults. Per the Migration Parity Standard (CLAUDE.md), defaults are
     * also installed into existing agents' config files by PostUpdateMigrator.
     */
    private loadDriftPromoterConfig;
    /**
     * Clean-window predicate for the drift promoter:
     *   - no in-flight forwards (`consecutiveForwardFailures` reflects current
     *     in-flight state; we also require `lastForwardSuccessAt` to be at
     *     least 90s old so we don't restart mid-conversation)
     *   - no queued messages waiting to flush
     *
     * Conservative bias: when in doubt, defer rather than restart.
     */
    private isCleanRestartWindow;
    /**
     * Write a marker file the post-restart lifeline boot will read to send a
     * one-shot user-facing note. Atomic write via rename so the marker can't
     * exist in a partial state.
     */
    private writeDriftRestartPendingMarker;
    /**
     * On boot, check for a drift-restart-pending marker. If present, send the
     * post-restart user notice and delete the marker. Idempotent: a corrupt or
     * missing file is silently treated as "no pending notice."
     */
    private consumeDriftRestartPendingMarker;
    /**
     * Observe the patch-drift header on a successful forward response and
     * forward the signal to the drift promoter. Called from forwardToServer.
     * Never throws.
     */
    private observeForwardResponseDriftHeader;
    /** Extract oldest queue item's enqueue timestamp as ms, if any. */
    private oldestQueueItemEnqueuedAt;
    /** Read config overrides for watchdog thresholds. */
    private loadThresholdOverrides;
    /** Persist rate-limit state. Safe to call during orchestrator persist. */
    private persistRateLimitSafe;
    /**
     * Unified restart initiator: checks rate limit, writes history, then
     * calls the orchestrator. Used by both the watchdog tick (bucket=watchdog)
     * and the version-skew handler (bucket=versionSkew).
     */
    private initiateRestart;
    /**
     * Flush stale Telegram connections on startup.
     * After a hard kill or sleep/wake, a previous long-poll getUpdates call may
     * still be active on Telegram's side. This causes 409 Conflict errors until
     * the old connection times out (~30s). We claim the polling slot immediately
     * with a non-blocking getUpdates call (timeout=0), which invalidates any
     * stale long-poll connection.
     */
    private flushStaleConnection;
    private poll;
    private processUpdate;
    /**
     * Handle an incoming photo message: download it and forward/queue with [image:path] content.
     */
    private handlePhotoMessage;
    /**
     * Download a photo from Telegram and save it to the state directory.
     */
    private downloadPhoto;
    /**
     * Download a document from Telegram and save it to the state directory.
     * Preserves the original filename when available.
     */
    private downloadDocument;
    /**
     * Handle an incoming document message: download it and forward/queue with [document:path] content.
     */
    private handleDocumentMessage;
    /**
     * Forward an inline keyboard callback query to the server for processing.
     * Prompt Gate relay buttons generate these when the user taps a button.
     */
    private forwardCallbackQuery;
    /**
     * Forward a message to the Instar server's Telegram webhook.
     *
     * Attempts up to FORWARD_ATTEMPTS times with exponential backoff
     * (1s, 2s base). A single 10s-timeout fetch per attempt. Returns true
     * on the first success, false after all attempts fail. Giving the
     * handoff a real chance to succeed closes the silent-drop window that
     * the caller's queue-and-retry path papered over.
     */
    private static readonly FORWARD_ATTEMPTS;
    private static readonly FORWARD_BACKOFF_BASE_MS;
    /**
     * `legacyStrict` — if a pre-Stage-B server strictly validates JSON and
     * rejects the unknown `lifelineVersion` field with 400, the lifeline
     * falls back to omitting it and pins this flag for the session.
     */
    private legacyStrictServer;
    /** Full semver of this lifeline, read once at construction. */
    private readonly lifelineVersion;
    /**
     * Agent hard-sleep wake trigger. When the ServerSupervisor has intentionally
     * slept the server (a `state/slept-marker.json` is present), write
     * `state/wake-requested.json` so the supervisor respawns it. Idempotent + cheap:
     * the supervisor consumes the flag and removes the marker on wake, so steady-state
     * (awake) this is a single `existsSync` no-op per forward.
     */
    private requestWakeIfSlept;
    /**
     * Boolean façade kept for the inbound-handler callers that only care whether
     * the forward landed. Replay uses {@link forwardToServerClassified} so it can
     * tell a message-specific rejection (poison) from a transient outage.
     */
    private forwardToServer;
    /**
     * Forward a message to the server and classify the result so replay can
     * budget correctly: only a genuine HTTP-400 rejection ('poison') burns the
     * drop budget; timeout / 5xx / 503-boot / network refusal are 'transient'
     * (never drop a real message), and 426 is 'skew' (coordinated restart).
     */
    private forwardToServerClassified;
    /**
     * Resolve the destination topic for update-class alerts (version-skew,
     * delivery-health). Reads `agent-updates-topic` from server state. If
     * unset, falls back to the Lifeline topic — Lifeline is the always-
     * reachable channel for delivery-health alerts, so a misconfigured
     * Updates topic must never *prevent* the alert from being seen.
     *
     * Returns null only if neither topic is configured (e.g. early boot
     * before Lifeline topic is ensured). Caller should drop the alert in
     * that case rather than picking an arbitrary topic.
     */
    private resolveUpdatesAlertTopic;
    /**
     * Handle a 426 response from the server. Validates the response body's
     * `serverVersion` differs from this lifeline's, then requests restart
     * through the orchestrator. If the body is malformed or the versions
     * match (loopback impostor), treat as transient.
     *
     * The `inboundTopicId` parameter is kept for diagnostics/logging only;
     * the destination for the user-visible alert is resolved via
     * resolveUpdatesAlertTopic() — never the inbound topic.
     */
    private handleVersionSkew;
    /**
     * Per-tick check of the coordinated-restart signal file.
     *
     * Three independent writers (AutoUpdater on major.minor crossing; the
     * server on a 426 forward; PostUpdateMigrator's one-time stale-lifeline
     * bootstrap) drop a signal at `state/lifeline-restart-requested.json`.
     * We read it, validate, delete it (so a respawn doesn't re-fire), and
     * call initiateRestart on the `plannedUpgrade` bucket which bypasses
     * the watchdog cooldown.
     *
     * Spec: docs/specs/auto-updater-lifeline-coordination.md
     */
    private checkLifelineRestartSignal;
    /** Watchdog-tracked counters/state. */
    private consecutiveForwardFailures;
    private lastForwardSuccessAt;
    private conflict409StartedAt;
    /**
     * Version-skew episode state. While a server/lifeline major-minor
     * mismatch is active, ingress is paused until the lifeline restarts
     * onto the matching version. We track this so the replay loop can
     * SKIP the drop-after-3-failures policy for messages that failed
     * specifically due to version skew — retrying is useless against a
     * hard incompatibility, but DROPPING is worse (silent data loss).
     * The flag is set in handleVersionSkew and cleared on the next
     * successful forward (or process restart).
     */
    private versionSkewActive;
    /** Wall-clock of the user-visible "ingress paused" alert for the
     * current skew episode. Used to dedupe — one alert per topic per
     * episode, not one per dropped message. */
    private versionSkewAlertSentAt;
    private orchestrator;
    private driftPromoter;
    private watchdog;
    private handleLifelineCommand;
    private replayQueue;
    /** Whether we've already notified for the current outage. Reset on recovery. */
    private hasNotifiedServerDown;
    /** Suppressed "server down" count during current outage. */
    private suppressedServerDownCount;
    /** Timestamp of last "server down" notification sent (for cross-outage rate limiting). */
    private lastServerDownNotifyAt;
    /** Minimum interval between "server down" notifications, even across separate outages (30 min). */
    private static readonly SERVER_DOWN_COOLDOWN_MS;
    /** Per-topic timestamps for rate-limiting queue acknowledgment messages. */
    private lastQueueAckAt;
    /** Minimum interval between "your message has been queued" acks per topic (2 minutes). */
    private static readonly QUEUE_ACK_RATE_LIMIT_MS;
    /** Queue size threshold above which ack messages are suppressed entirely. */
    private static readonly QUEUE_ACK_SUPPRESS_THRESHOLD;
    /**
     * Load persisted rate limit state from disk.
     * Before v0.12.10, this was in-memory only — every process restart
     * reset the counter, causing "server went down" spam during update loops.
     */
    private loadRateLimitState;
    private saveRateLimitState;
    /**
     * Check if a queue acknowledgment should be sent for this topic.
     * Rate-limits acks to prevent Telegram spam during restart loops.
     */
    private shouldSendQueueAck;
    private notifyServerDown;
    private notifyCircuitBroken;
    /**
     * One-per-episode escalation for the slow-retry Eternal Sentinel (P19
     * condition 4). The supervisor keeps retrying after this — the message
     * exists so "never give up" can never again mean "flail silently for days."
     */
    private notifySentinelStalled;
    /**
     * Handle `/lifeline doctor` — spawn a Claude Code diagnostic session.
     */
    private handleDoctorCommand;
    /**
     * Sanitize log content by stripping ANSI codes and redacting secrets.
     */
    private sanitizeLogContent;
    /**
     * Write sanitized diagnostic context to a file for the doctor session.
     */
    private writeDiagnosticContext;
    /**
     * Spawn a diagnostic session in tmux to triage a server crash.
     *
     * Framework-aware (provider-portability v1.0.0): currently routes
     * only Claude Code agents — the diagnostic session uses Claude's
     * `--message -` stdin prompt + `--allowedTools` tool-set restriction
     * which have no direct Codex equivalent (Codex's `exec` takes a
     * positional prompt; tool restriction is per-sandbox-mode, not a
     * tool list). For Codex agents the recovery falls back to the
     * existing circuit-breaker reset path without spawning a doctor.
     *
     * Returns the session name and HMAC secret for restart authentication.
     */
    private spawnDoctorSession;
    /**
     * Read the last N lines from a file, using seek-based reading for large files.
     */
    private readTailStream;
    /**
     * Find an existing doctor tmux session for this project.
     */
    private findExistingDoctorSession;
    /**
     * Check if `--allowedTools` is supported by the installed Claude Code version.
     */
    private supportsAllowedTools;
    /**
     * Log a doctor session to the audit trail.
     */
    private logDoctorSession;
    /**
     * Kill a doctor tmux session and notify via Telegram.
     */
    private killDoctorSession;
    /**
     * Check if OS-level autostart is installed for this project.
     */
    private isAutostartInstalled;
    /**
     * Self-heal the launchd plist if it uses a hardcoded Node path instead of the boot wrapper.
     *
     * Older agents (pre-boot-wrapper) had plists pointing directly to a Node binary path like
     * /Users/x/.asdf/installs/nodejs/24.13.1/bin/instar. When Node versions change (asdf, nvm),
     * the path breaks and the agent becomes unrecoverable after a reboot or restart.
     *
     * The boot wrapper pattern resolves the shadow install at runtime — immune to Node version changes.
     * If the plist doesn't use the boot wrapper, regenerate both the wrapper and the plist.
     */
    /**
     * Validate .claude/settings.json is parseable JSON.
     * Unresolved git merge conflicts (<<<<<<< markers) are the #1 cause of
     * "agent is alive but every session dies instantly" — Claude Code crashes
     * on startup when it can't parse its settings, but the lifeline/server
     * show no errors because the crash happens in the spawned tmux session.
     */
    private selfHealSettingsJson;
    private selfHealPlist;
    /**
     * Ensure the Lifeline topic exists. Recreates if deleted.
     */
    private ensureLifelineTopic;
    /**
     * Persist the Lifeline topic ID to config.json.
     */
    private persistLifelineTopicId;
    private sendToTopic;
    private getUpdates;
    /**
     * Resolve the current format mode from the loaded project config. Read on
     * each call so hot-reload works without restart.
     * Multi-machine: this machine is the SENDER, so its own config is authoritative
     * (per spec "Multi-machine send-side-only"). We ignore any upstream envelope
     * `alreadyFormatted` flag.
     */
    private currentFormatMode;
    private apiCall;
    private loadOffset;
    private saveOffset;
}
//# sourceMappingURL=TelegramLifeline.d.ts.map