/** The ONLY event names. */ export type TelemetryEvent = "cli_command" | "project_initialized" | "study_finished"; export interface TelemetryProperties { command?: string; /** A starter lab id, or "custom" — never an adopter's own lab id. */ lab?: string; mode?: "dry-run" | "live"; outcome?: string; /** Bucketed, not exact: a duration is a fingerprint at full precision. */ durationBucket?: string; /** Which brain ran it, as a route name — never a model id the adopter configured. */ brain?: "provider-key" | "local-agent" | "none"; ok?: boolean; exitCode?: number; /** One of humanish's OWN error codes (`HUMANISH_*`), never a message. Which failure ends a * first run is the question the funnel exists to answer. */ errorCode?: string; /** Finite result categories and recorded stop causes; never raw errors or lane details. */ diagnosticCategory?: string; stopCause?: string; } export interface TelemetryState { enabled: boolean; /** Random, generated locally, tied to nothing. */ anonymousId: string; /** Whether the first-run notice has been shown. */ noticed: boolean; } export declare function telemetryStatePath(env?: NodeJS.ProcessEnv, home?: string): string; /** * Off means off, from any of the three switches people already expect: the cross-tool * `DO_NOT_TRACK` standard, our own env var, and the persisted opt-out. */ export declare function disabledByEnvironment(env: NodeJS.ProcessEnv): boolean; /** * True when this process is running from a humanish SOURCE CHECKOUT rather than an install. * * Checked by walking up from cwd for a package.json whose name is `humanish` AND which carries * this repo's private marker. An adopter with a dependency named humanish in node_modules is not * matched: node_modules copies are skipped, and a consumer's own package.json has a different * name. Falls back to "not a checkout" on any read error, because the failure direction that * loses one event is better than the one that silently disables real telemetry. */ export declare function inHumanishCheckout(startDir: string, readFileSyncFn: (p: string) => string): boolean; /** * Is this command one of OUR runs, whichever directory it was started from? Two walks: from the * cwd (catches `pnpm humanish ...` inside the repo) and from the running CLI's own directory * (catches a test, TUI smoke or release-dogfood host that spawns dist/cli.js into a temp * directory with a constructed env). The second walk is the one the 0.63.0 fix lacked: one * development box put 1,251 events into the adopter metric between 2026-08-31 and 09-03 through * exactly those spawns. An installed copy lives under node_modules and is never matched, so an * adopter running from their own project still reports. */ export declare function isOwnCheckoutRun(cwd: string, cliDir: string, readFileSyncFn: (p: string) => string): boolean; export declare function readTelemetryState(env?: NodeJS.ProcessEnv, home?: string): Promise; export declare function writeTelemetryState(state: TelemetryState, env?: NodeJS.ProcessEnv, home?: string): Promise; /** Coarse enough that a duration cannot fingerprint a session. */ export declare function durationBucket(ms: number): string; /** A lab id only if it is one of ours. An adopter's lab id can name an unannounced product. */ export declare function safeLabId(lab: string | undefined): string | undefined; export interface TelemetryPayload { event: TelemetryEvent; distinct_id: string; properties: Record; } /** * The exact document that would be sent. Built separately from sending so that * `HUMANISH_TELEMETRY_DEBUG=1` can show it and so tests can assert on it — "you can read exactly * what we collect" is the part of this convention that makes it honest rather than merely legal. */ export declare function buildPayload(args: { event: TelemetryEvent; anonymousId: string; version: string; properties?: TelemetryProperties; env?: NodeJS.ProcessEnv; platform?: string; nodeVersion?: string; }): TelemetryPayload; /** * Read the study facts off a command's result document: mode, outcome, starter lab, brain route, * and our own error code. Duck-typed over every result shape the CLI writes, because the point is * ONE derivation at the one seam every command already passes through (writeResult), so that a * backend added later cannot forget to report. Returns {} for results that carry no study. * * The first two days of 0.62.0 data had 1,359 `run`/`lab run` events and not one carried a mode * or an outcome — the vocabulary existed, nothing populated it — so the one question telemetry * was added to answer ("does anyone get to a working LIVE first run") had no answer in the data. */ export declare function deriveStudyFacts(result: unknown): TelemetryProperties; /** The notice, shown once, before anything is sent. */ export declare const TELEMETRY_NOTICE: string; export interface SendDeps { fetchFn?: typeof fetch; now?: () => number; } /** * Fire and forget, bounded, and incapable of affecting the command that triggered it. A tool whose * telemetry can slow down or fail a run has made its users pay for its metrics. */ export declare function sendTelemetry(payload: TelemetryPayload, deps?: SendDeps): Promise;