/**
 * Daemon self-update service (PHNX-3695, "Fix 2").
 *
 * The daemon (`agents __daemon-run`) is a long-running background process that
 * historically opted OUT of the CLI's interactive auto-update: `bootstrap.ts`
 * force-sets `AGENTS_CLI_DISABLE_AUTO_UPDATE=1` for `__daemon-run`, so a
 * running daemon never picked up new agents-cli code until a human ran
 * `agents daemon restart`. R5 in the root CLAUDE.md ("An installed CLI and its
 * installed helpers auto-update from the public channel") is binding for the
 * interactive CLI path (`self-update.ts` / `agents upgrade`) but was silently
 * NOT held for the one process that runs unattended for days. This service
 * closes that gap for the daemon specifically, reusing the exact same
 * verified-install primitives `agents upgrade` already uses — it does not
 * fork or reimplement them.
 *
 * Model: verify-then-exit, not swap-in-place. A daemon cannot safely hot-swap
 * its own loaded JS mid-process (in-flight ticks, open sockets, a live
 * ServiceSupervisor). Instead this tick installs + BYTE-VERIFIES the new
 * package on disk, and only once that succeeds does it `process.exit(0)` —
 * the OS supervisor (launchd `KeepAlive` / systemd `Restart=always`, see
 * `daemon/AGENTS.md`'s crash-recovery model) relaunches the daemon, which
 * then boots the new code. Clients do not need to be told anything: browser
 * IPC clients re-probe the socket via `waitForBrowserService`
 * (`browser/ipc.ts`), and the scheduler's atomic `(routine, scheduledFor)`
 * claim (see `docs/specifications.md` §Scheduling & execution singularity)
 * means a routine mid-fire at the moment of exit is deduped safely across the
 * restart rather than double-fired.
 *
 * Fail-closed is the whole point: every step below that can fail — the
 * registry check, the install, the post-install verify — leaves the OLD
 * daemon running untouched and logs a WARN/ERROR for the next tick to retry.
 * The daemon must never exit into code it has not proven is the real,
 * verified, requested version; an unverified exit would let the OS supervisor
 * relaunch-loop on a broken install.
 */
import { BasePeriodicService, type DaemonContext } from './service.js';
import type { DaemonServiceId } from '../daemon-services.js';
/**
 * Hard cap per tick: a real download + npm/bun install + verify can
 * legitimately take minutes on a slow link. 15 minutes matches the task's
 * stated budget and is short relative to the ~75min cadence. Exported so the
 * on-demand `request-self-update` IPC handler (`browser/ipc.ts`) can bound
 * its own `AbortController` on the SAME budget the periodic tick runs under —
 * one deadline, not two independently-tuned numbers that could drift apart.
 */
export declare const SELF_UPDATE_DEADLINE_MS: number;
export interface SelfUpdateOutcome {
    updated: boolean;
    reason?: string;
}
interface NpmLatestMetadata {
    version: string;
    integrity: string;
    tarball: string;
}
/**
 * Dependency seam so `self-update-service.test.ts` can drive a REAL install
 * against a fixture npm prefix/tarball without touching the real npm
 * registry or the real running install. Production code always uses
 * {@link defaultSelfUpdateDeps} (the default parameter below) — no test-only
 * branch exists in the exported logic itself.
 */
export interface SelfUpdateDeps {
    currentVersion(): string;
    isDevBuild(): boolean;
    detectShadow(): boolean;
    packageRoot(): string;
    fetchLatestMetadata(signal: AbortSignal): Promise<NpmLatestMetadata>;
    installAndVerify(metadata: NpmLatestMetadata, packageRoot: string, signal: AbortSignal): Promise<void>;
    syncSystemRepo(): Promise<void>;
    syncLocal(): Promise<void>;
}
export declare function fetchLatestNpmMetadata(signal: AbortSignal): Promise<NpmLatestMetadata>;
/**
 * Install + byte-verify `metadata` into `packageRoot`'s install, exactly the
 * sequence `bootstrap.ts`'s `installResolvedPackage` runs for `agents
 * upgrade` (download+integrity-verify -> sweep stale staging -> package-
 * manager install -> verify installed version -> refresh alias shims).
 * `bootstrap.ts` cannot be imported here — it runs side-effecting top-level
 * code (argv parsing, command registration) on import, which the daemon must
 * never trigger — so this is the same primitives from `self-update.ts`
 * composed directly, not a fork of the upgrade logic.
 *
 * `signal` is threaded into EVERY step's own `signal` option
 * (`downloadVerifiedTarball` / `installPackageIntoPrefix` /
 * `installPackageWithBun`, all in `self-update.ts`) rather than raced against
 * from the outside: those primitives kill the underlying fetch/child process
 * on abort and their promise rejects only once that real cancellation has
 * happened. A wrapper that merely stopped AWAITING an unkillable operation
 * (the prior approach here) left an orphaned `npm install -g`/`bun add -g`
 * writing into the shared global prefix — which the very next tick's fresh
 * install (started as soon as the supervisor's backoff fires, seconds later)
 * would then race into the same directory. Real cancellation is what makes
 * `attemptSelfUpdateAndExit`'s `inFlightAttempt` dedupe (below) an actual
 * guarantee instead of a guard whose lifetime is shorter than the operation
 * it's guarding (found in review, PHNX-3695).
 */
export declare function installAndVerifyDefault(metadata: NpmLatestMetadata, packageRoot: string, signal: AbortSignal): Promise<void>;
export declare function defaultSelfUpdateDeps(): SelfUpdateDeps;
/**
 * Core self-update decision + action, shared by the periodic tick
 * ({@link SelfUpdateService.onTick}) and the on-demand IPC path
 * (`request-self-update`, `browser/ipc.ts`) — one implementation, so a
 * version-skew client asking "update now" runs exactly the same fail-closed
 * logic as the scheduled sweep. Returns rather than throws so callers decide
 * their own exit timing (the periodic service exits immediately; the IPC
 * handler must respond to the client on the socket BEFORE exiting, or the
 * client hangs on a socket that is closing mid-write). Concurrent callers
 * share one in-flight attempt rather than racing separate installs.
 */
export declare function attemptSelfUpdateAndExit(ctx: DaemonContext, signal: AbortSignal, deps?: SelfUpdateDeps): Promise<SelfUpdateOutcome>;
/**
 * Schedule the process exit for a verified self-update, exactly once, no
 * matter how many callers observe `outcome.updated` on the shared
 * `inFlightAttempt` promise. The periodic tick and an on-demand
 * `request-self-update` IPC call (`browser/ipc.ts`) can both be awaiting that
 * SAME promise — if the tick's continuation ran an immediate `process.exit(0)`
 * while the IPC handler's continuation had not yet reached `socket.write`,
 * the tick's exit could win the race and the client would see a closed socket
 * before any response (found in review, PHNX-3695). Routing every caller
 * through this one guarded, always-delayed scheduling point means the delay
 * protects EVERY caller's in-flight response, not just the IPC handler's own.
 */
export declare function scheduleSelfUpdateExit(): void;
/**
 * Fire the on-demand self-update in the BACKGROUND and return its (bounded)
 * promise WITHOUT the caller having to await it. This is what keeps the
 * `request-self-update` IPC handler (`browser/ipc.ts`) from parking a
 * version-skewed `agents browser` verb behind the full
 * check→download→install→verify: that handler routes through
 * `reconcileDaemonVersion` on every version-skewed call, so awaiting the whole
 * install there reintroduces exactly the client-stall PHNX-3605 was written to
 * prevent (tens of seconds, worst case ~15 min). The handler instead responds
 * "triggered" immediately and lets this run in the background — the daemon does
 * install→verify→exit(0) on its own, the OS supervisor relaunches it, and the
 * browser reconnects.
 *
 * The work still shares the module-level {@link attemptSelfUpdateAndExit}
 * `inFlightAttempt` guard, so a concurrent trigger (or the periodic tick) can't
 * race a second install into the same prefix. It is bounded by
 * {@link SELF_UPDATE_DEADLINE_MS} via an `AbortController` nobody awaits (the
 * timer is `unref`'d so it never keeps the daemon alive on its own and never
 * dangles in a test). Fail-closed is preserved end to end:
 * `runSelfUpdateAttempt` already turns an install/verify failure into a
 * not-updated outcome that leaves the running daemon untouched. The caller
 * schedules the one decoupled {@link scheduleSelfUpdateExit} off the returned
 * promise once `updated` is true, so the exit still fires after the IPC
 * response has flushed.
 */
export declare function triggerSelfUpdateInBackground(ctx: DaemonContext, deps?: SelfUpdateDeps): Promise<SelfUpdateOutcome>;
/**
 * The subset of self-update decline checks that are INSTANT and network-free —
 * a dev build, or a shadowed install. The on-demand IPC handler
 * (`request-self-update`) runs these SYNCHRONOUSLY so a version-skewed browser
 * client gets the PHNX-3605 "nothing changed, not evicting" advisory
 * immediately, instead of a "triggered" it would never act on. The remaining
 * checks (registry probe, already-current, install/verify) stay inside the
 * backgrounded {@link attemptSelfUpdateAndExit} so the handler never blocks on
 * the network or the install. This is the single source of the two instant
 * decline reasons — {@link runSelfUpdateAttempt} calls it too, so the on-demand
 * decline text can never drift from the periodic tick's. Returns the decline
 * reason, or `null` to proceed to the (backgrounded) install path.
 */
export declare function selfUpdateSyncDeclineReason(deps?: SelfUpdateDeps): string | null;
export declare class SelfUpdateService extends BasePeriodicService {
    readonly id: DaemonServiceId;
    readonly intervalMs: number;
    readonly deadlineMs: number;
    readonly startupDelayMs: number;
    protected onStart(_ctx: DaemonContext): Promise<void>;
    protected onStop(): Promise<void>;
    protected onTick(ctx: DaemonContext, signal: AbortSignal): Promise<void>;
}
export {};
