import type { AgentId } from '../types.js';
import type { DuplicateVersionHook } from '../hooks/install.js';
import type { AgentsBinaryShadow } from '../binary-shadow.js';
import type { RcSecretFinding } from '../secrets/rc-hygiene.js';
import type { OwnerSinkStatus } from '../channels/owner-sink.js';
import { type WindowsSshEnrollmentAudit } from './windows-ssh-enrollment.js';
import type { SyncStatusRow, OrphanRow } from '../drift.js';
import type { FetchStatusMarker } from '../auto-pull.js';
import { type VersionResourceReport } from '../doctor-diff.js';
import type { FleetDivergence, FleetHookRuntimeState, FleetVersionSignIn } from './fleet-divergence.js';
export type FindingSeverity = 'critical' | 'warning';
/** A machine-stable class for a finding — drives {@link remediationFor} and lets
 *  the JSON consumer group by kind. */
/** Every finding class. Severity is NOT annotated here — {@link FINDING_SEVERITY}
 *  below owns it, and a second copy in these comments is a fourth place to drift. */
export declare const ALL_FINDING_KINDS: readonly ["logged-out", "logout-unprovable", "missing-hook", "missing-plugin", "unwired-hook", "hook-runtime-broken", "hook-runtime-visibility-unavailable", "cli-missing", "missing-resource", "content-drift", "never-synced", "stale", "repo-behind", "repo-drift", "fleet-resource-gap", "host-cli-missing", "host-cli-invalid", "version-skew", "orphan", "duplicate-hook", "duplicate-hook-drift", "rc-secret-export", "env-secret-export", "auth-bundle-wrong-backend", "exec-policy", "ssh-key-enrollment", "stale-cli", "binary-shadow", "owner-sink-unreachable"];
/**
 * The severity each kind is emitted with - the SINGLE source of truth, read by
 * the builders below and asserted against both prose rubrics by
 * `doctor-findings.test.ts`.
 *
 * It exists because severity drifted: the rubrics claimed `never-synced` and
 * `duplicate-hook-drift` were critical for three days after RUSH-2162 downgraded
 * them, and the exhaustiveness test missed it because it only checked that a kind
 * was *named* in the rubric, not which bucket it sat in. Change a severity HERE
 * and the test names the docs that must move with it.
 */
export declare const FINDING_SEVERITY: Record<FindingKind, FindingSeverity>;
/** A machine-stable class for a finding. Derived from the runtime list above so
 *  the rubric test can enumerate every kind. */
export type FindingKind = typeof ALL_FINDING_KINDS[number];
/** One prioritized finding, attributed to a device (and, when relevant, an agent
 *  version + account). `remediation` is the exact command/hint to fix it. */
export interface DoctorFinding {
    severity: FindingSeverity;
    kind: FindingKind;
    /** The device this finding is about. */
    device: string;
    /** Agent id, when the finding is about a specific agent (else undefined). */
    agent?: AgentId;
    /** Version id, when about a specific installed version. Absent on a finding
     *  collapsed across versions — read {@link DoctorFinding.versions} instead. */
    version?: string;
    /** Set only on a finding collapsed across several versions of one agent (the
     *  same problem on each). The row renders `<agent> (N versions)` and the
     *  remediation widens to the agent-wide sweep. */
    versions?: string[];
    /** Human account label (email/org/opaque id), when known. */
    account?: string | null;
    /** One-line plain-English description of the problem. */
    message: string;
    /** Exact remediation command / hint. */
    remediation: string;
}
/**
 * The exact remediation for a finding. Login fixes are harness-native
 * (`loginHint`); a per-version login is offered ONLY for agents that isolate the
 * credential per home (`agents run <agent>@<version>` then log in) — for
 * gemini/antigravity/droid the login is shared, so we say so instead of
 * faking a per-version fix. (Cursor now isolates its token per version home via
 * XDG_CONFIG_HOME — see buildExecEnv — so it gets the per-version run.) Every
 * other kind maps to its canonical command.
 */
export declare function remediationFor(finding: DoctorFinding): string;
export interface LocalFindingInputs {
    device: string;
    syncRows: SyncStatusRow[];
    orphanRows: OrphanRow[];
    repoBehind: FetchStatusMarker[];
    /** Per-version resource reports (one per installed version) — the source of the
     *  missing-hook / missing-plugin / missing-resource / content-drift / unwired
     *  criticals+warnings. */
    reports: VersionResourceReport[];
    /** Per-version sign-in per agent id. */
    signIn: Record<string, FleetVersionSignIn[]>;
    /** Managed agents (installed versions) whose binary won't resolve. */
    cliMissing?: AgentId[];
    /** Host CLIs declared in a DotAgents repo's `cli/`: their install state on this
     *  box, plus any manifest the loader could not parse. Host-global (installed to
     *  PATH, never synced into a version home), so they are a machine-level
     *  finding, not a per-version one. */
    hostClis?: {
        statuses: Array<{
            name: string;
            installed: boolean;
        }>;
        errors: Array<{
            file: string;
            reason: string;
        }>;
    };
    /** Hooks materialized into several version homes at once — identical copies are
     *  installation noise, differing ones are drift a stale gate can act on. */
    duplicateHooks?: DuplicateVersionHook[];
    /** Credential-shaped exports found in the user's shell rc files (RUSH-1968). */
    rcSecrets?: RcSecretFinding[];
    /** True when the file-store master key is live in THIS process's environment.
     *  Distinct from `rcSecrets`, which scans FILES: a value inherited by a
     *  long-lived process survives deleting the rc line that set it, so the rc
     *  scan reports clean while the leak is still in flight (RUSH-1968). Never the
     *  value — only whether it is set. */
    masterPassphraseInEnv?: boolean;
    /** True when the reserved `auth` bundle exists on a non-file backend
     *  (SEC-GAP-3): usage/probe ignores the setup-tokens and falls through to
     *  Touch ID. Collected by `inspectReservedAuthBundle`. */
    authBundleWrongBackend?: boolean;
    /** The effective PowerShell execution policy and the platform it was read on.
     *  Only `win32` yields a finding — the `agents.ps1` launcher is Windows-only. */
    execPolicy?: {
        platform: NodeJS.Platform;
        policy: string | null;
    };
    /** Read-only Windows OpenSSH AuthorizedKeysFile/content/ACL audit. */
    windowsSshEnrollment?: WindowsSshEnrollmentAudit | null;
    /** `<agent>@<version>` keys whose home is an isolated copy. Their findings are
     *  never collapsed across versions: the agent-wide `agents sync <agent>@all`
     *  sweep deliberately skips isolated copies, so a collapsed row would print a
     *  remediation that does not fix them. */
    isolatedVersions?: string[];
    /** Whether the feed/notify owner-delivery lane can reach the owner from this box
     *  (RUSH-2262). Collected by `probeOwnerSink` in the command (it spawns the real
     *  `rush` transport, so it stays out of this pure module). Absent → no probe ran
     *  → no finding. */
    ownerSink?: OwnerSinkStatus;
    /** `agents` binaries that shadow the currently running CLI (RUSH-2431). */
    binaryShadows?: AgentsBinaryShadow[];
}
/**
 * Fold this machine's signals into findings. Missing hooks/plugins and unwired
 * hooks are CRITICAL; provable logouts are CRITICAL and unprovable ones WARNING;
 * everything else (other missing kinds, drift, stale/never-synced, repo-behind,
 * orphans) is a WARNING. Pure.
 */
export declare function buildLocalFindings(input: LocalFindingInputs): DoctorFinding[];
/**
 * Fold findings that say the SAME thing about several versions of one agent into
 * a single row carrying `versions`, and widen its remediation to the agent-wide
 * sweep (`agents sync claude@all --yes` heals every non-isolated version in one
 * go). Five identical `plugin 'code' — mirror missing` rows, one per installed
 * claude, is the same fact five times.
 *
 * Three things never merge, because for each of them the widened remediation
 * would be wrong:
 *  - **Isolated copies** — the agent-wide sweep deliberately skips them, so a
 *    folded row would print a command that leaves one broken.
 *  - **Findings with no agent** (repo-behind, rc-secret-export, …) — their
 *    `version` field is an alias, not a version.
 *  - **Logouts** ({@link NEVER_COLLAPSED}) — a login is inherently per-version:
 *    the fix is `agents run <agent>@<version> -- login`, and there is no `@all`
 *    equivalent. Dropping `version` would fall back to the bare native hint,
 *    which the shim resolves to the DEFAULT version — logging into the wrong one
 *    and leaving the finding to reappear.
 *
 * Pure; input order is kept.
 */
export declare function collapseAcrossVersions(findings: DoctorFinding[], isolated: Set<string>): DoctorFinding[];
/**
 * Map a device's per-version sign-in into logout findings: a PROVABLE logout is
 * CRITICAL, an unprovable one is a hedged WARNING ("could not verify sign-in"),
 * and a signed-in version yields nothing.
 *
 * An agent with no inspectable identity never appears at all — not even as the
 * hedged warning: agents-cli knows no credential file for it, so "logged out" is
 * unknowable and silence beats a false claim. Membership is
 * `supportsAccountInspection` (`lib/agents.ts`) and is deliberately NOT listed
 * here — agents move between the sets, and a copy of the list in prose becomes a
 * lie the next time one does. Note the caller also requires
 * `CredentialPresence.knownLocation`: the inspection set and the credential-path
 * map move independently, so being inspectable is not on its own enough to call a
 * logout provable. Pure.
 */
export declare function signInToFindings(device: string, signIn: Record<string, FleetVersionSignIn[]>): DoctorFinding[];
/**
 * Rebuild remote generated-wrapper findings from the closed enum inventory
 * state. Remote paths and detector messages never cross the fleet boundary.
 */
export declare function hookRuntimeToFindings(device: string, hookRuntime: Record<string, Record<string, FleetHookRuntimeState>> | undefined): DoctorFinding[];
/**
 * Map cross-device divergence (from {@link compareFleetInventories}) into
 * warnings: an agent version present elsewhere but absent on a device is a
 * version-skew warning; a diverged config repo is a repo-drift warning; a
 * missing resource is a missing-resource warning. Baseline = the local machine.
 * Only the *lagging* box is attributed (a `*-missing-local` finding is the
 * baseline's gap). Pure.
 */
export declare function fleetDivergenceToFindings(divergences: FleetDivergence[], baseline: string): DoctorFinding[];
export interface RenderOptions {
    /** Fleet mode (`--devices`): render the `─── by computer ───` header + one
     *  block per device. Single-machine mode collapses to one `▸ <machine>` block
     *  with no fleet header. */
    fleet: boolean;
    /** The baseline (local) machine name — tagged `· this machine`. */
    baseline?: string;
    /** Header line context: device count (fleet) or the local version string. */
    header?: string;
}
/**
 * Render the two-part hybrid layout from a flat findings list. Pure — returns the
 * lines so the exact output is snapshot-tested. Criticals across ALL devices go
 * to the top section, worst-first; the per-computer section lists each device's
 * warnings + a `✗ N critical (above)` marker, worst device first.
 */
export declare function renderFindings(findings: DoctorFinding[], accounts: Record<string, Record<string, FleetVersionSignIn[]>>, opts: RenderOptions): string[];
/**
 * The compact accounts/versions line for one device: every installed version and
 * its account, grouped by agent — green ✓ signed in, red ✗ provably logged out,
 * gray ? unknown (see {@link badge}). e.g.
 *   `claude 2.1.170 ✓muqsit@gmail(Max) 2.1.999 ✓team(Team) · codex ✗ · grok ✓`
 */
export declare function renderAccountsLine(signIn: Record<string, FleetVersionSignIn[]>): string;
