/** * Runtime integrity checkpoint — public types. * * A checkpoint re-verifies the live environment against the state that * passed preflight and reports what has drifted. It is detect-and-emit: it * never pauses, locks, or fails the session — the host decides what to do * with the result. See `checkpoint.ts` for the logic and `probes.ts` for the * browser-facing detectors. */ /** The media roles a change can point at. */ export type CheckpointDeviceKind = "microphone" | "camera" | "speaker"; /** * Every drift signal a checkpoint can raise. * * Tier 1 — configuration drift (the setup changed): * - permission-lost a granted mic/camera permission is now denied * or requires a fresh browser prompt * - device-unavailable the selected device is no longer enumerated * - external-monitor-connected preflight passed single-display, now extended * * Tier 2 — capture liveness (the setup is no longer delivering): * - camera-track-ended video track readyState === "ended" * - camera-black frame is all-black / zero-variance * - camera-frozen frames are not advancing (held last frame) * - mic-track-ended audio track readyState === "ended" * - mic-muted audio track.muted === true (browser-owned) */ export type CheckpointChangeKind = "permission-lost" | "device-unavailable" | "external-monitor-connected" | "camera-track-ended" | "camera-black" | "camera-frozen" | "mic-track-ended" | "mic-muted"; export interface CheckpointChange { /** 1 = configuration drift, 2 = capture liveness. */ tier: 1 | 2; kind: CheckpointChangeKind; /** Which device the change is about, when applicable. */ device?: CheckpointDeviceKind; /** Human label for the device, when known (e.g. "AirPods Pro"). */ label?: string; /** * Every shipping signal is `hard` — a fact the browser reported (a denied * permission, an ended/muted track, a device missing from enumeration) or * a deterministic frame verdict. The field exists so a future inferred * signal (e.g. baseline-relative "mic-silent") can be marked `soft`. */ confidence: "hard"; } export interface CheckpointResult { /** True when nothing drifted from the passed-preflight baseline. */ ok: boolean; /** Epoch ms the checkpoint ran. */ at: number; changes: CheckpointChange[]; } /** Per-call options for `client.checkpoint(options)`. */ export interface CheckpointOptions { /** * Run Tier 2 (capture liveness) in addition to Tier 1 (configuration * drift). Default true. `false` = a cheap Tier-1-only check with no stream * reads. */ liveness?: boolean; /** * Route a FAILING result through the configured `onDrift` callback, so a * manual `client.checkpoint()` can gate exactly like a scheduled run — e.g. * a host firing a checkpoint at a section boundary that should raise the * `` integrity hold on drift. Default false (manual * runs stay detect-only). Action only ever on failure: a passing result * fires nothing, and `onCheckpoint` (the scheduled auto-recovery hook) is * never called on a manual run regardless of this flag. */ notifyDrift?: boolean; } /** * Which moments the SDK re-runs a checkpoint on, on its own schedule. * - visibility the tab is refocused (`visibilitychange` → visible) * - interval a heartbeat timer (`intervalMs`) * - devicechange a media device was added/removed (`navigator.mediaDevices * .devicechange`) — the event-driven catch for a mic/speaker * (e.g. a Bluetooth headset) disconnecting mid-exam, which no * continuous track-`ended` can see. Debounced by the SDK. */ export type CheckpointTrigger = "visibility" | "interval" | "devicechange"; /** The `checkpoint` block on the client options. */ export interface CheckpointConfig { /** Default `liveness` for scheduled runs. Default true. */ liveness?: boolean; /** * SDK-owned scheduled triggers. Omit for a purely manual checkpoint * (call `client.checkpoint()` yourself). The SDK owns these listeners / * timers and their teardown. */ on?: CheckpointTrigger[]; /** Heartbeat period; used only when `on` includes "interval". Default 60s. */ intervalMs?: number; /** * Opt-in active microphone-liveness probe. When true, and only when no live * audio track is already available (no in-flight clip) and the mic * permission is granted, a checkpoint briefly acquires `getUserMedia({ * audio: true })`, reads `readyState`/`muted`, and releases it — giving * mic-liveness coverage every run at the cost of a momentary mic-indicator * flicker. Off by default; without it, mic liveness only runs while a clip * is recording (piggyback). Never prompts (gated on a granted permission). */ micProbe?: boolean; /** Fires on every scheduled run — the full result, ok or not. */ onCheckpoint?: (result: CheckpointResult) => void; /** * Fires when `result.ok === false`: on every scheduled run that drifts, and * on a manual `checkpoint({ notifyDrift: true })` run that drifts. Manual * runs without that flag never fire it (they stay detect-only). */ onDrift?: (result: CheckpointResult) => void; } /** Tri-state permission snapshot (`unknown` = browser can't answer). */ export type PermissionSnapshot = "granted" | "denied" | "unknown"; /** Tri-state display snapshot (`unknown` = `screen.isExtended` unsupported). */ export type DisplaySnapshot = "single" | "extended" | "unknown"; /** Deterministic verdict from sampling a live video track. */ export type FrameVerdict = "ok" | "black" | "frozen" | "unknown"; /** The environment as it was when the session started (the comparison point). */ export interface CheckpointBaseline { /** * Only the permissions the session actually DEPENDS on are tracked. Camera * is tracked when the camera is captured; microphone is tracked when the mic * is captured OR the speaker is used (browsers require a mic grant to * enumerate/route audio outputs). A permission absent here is never checked * — so a policy that uses neither (e.g. camera-only) can't false-flag a lost * mic permission it never needed. */ permissions: Partial>; /** The selected device per role, keyed by the id we compare against. */ devices: Partial>; display: DisplaySnapshot; } /** * The injectable detectors the checkpoint composes. The client wires real * browser implementations (see `probes.ts`); tests pass deterministic fakes. */ export interface CheckpointProbes { queryPermission: (name: "microphone" | "camera") => Promise; /** Current device ids grouped by kind, or null when enumeration is unavailable. */ listDeviceIds: () => Promise> | null>; probeDisplay: () => DisplaySnapshot; /** The live capture tracks the session holds, or null when none is live. */ getVideoTrack: () => MediaStreamTrack | null; getAudioTrack: () => MediaStreamTrack | null; /** Sample a live video track for black/frozen. Returns "unknown" if it can't. */ sampleVideoFrame: (track: MediaStreamTrack) => Promise; now: () => number; } //# sourceMappingURL=types.d.ts.map