orca/src/shared/daemon-lifecycle-telemetry.ts

48 lines
1.8 KiB
TypeScript

// Enums + bucketing for the `daemon_lifecycle` telemetry event (STA-2376).
// The daemon's own retirement cause (pam-rejections/probe-timeouts) lives in the
// subprocess and never crosses into the app, so every reason here is one the app
// itself decides: a startup replace, or an observed death→respawn.
// Startup launcher replaced a still-connectable daemon (each maps 1:1 to a `daemon-init.ts` decision).
export const DAEMON_REPLACE_REASONS = [
'unhealthy_resolver',
'stale_bundle',
'different_app_path',
'failed_health_check'
] as const
export type DaemonReplaceReason = (typeof DAEMON_REPLACE_REASONS)[number]
// Adapter observed the daemon die and forked a replacement.
export const DAEMON_RETIRE_REASONS = ['died_respawn'] as const
export type DaemonRetireReason = (typeof DAEMON_RETIRE_REASONS)[number]
export const DAEMON_LIFECYCLE_TRANSITIONS = ['replaced', 'retired'] as const
export type DaemonLifecycleTransition = (typeof DAEMON_LIFECYCLE_TRANSITIONS)[number]
export const DAEMON_LIFECYCLE_REASONS = [
...DAEMON_REPLACE_REASONS,
...DAEMON_RETIRE_REASONS
] as const
export type DaemonLifecycleReason = (typeof DAEMON_LIFECYCLE_REASONS)[number]
// Bucketed, never raw: exact live-session counts could fingerprint heavy users. `unknown` when
// the count couldn't be verified (null) — e.g. a wedged daemon or an already-dead respawn target.
export const DAEMON_LIFECYCLE_SESSION_BUCKETS = ['0', '1', '2-5', '6+', 'unknown'] as const
export type DaemonLifecycleSessionBucket = (typeof DAEMON_LIFECYCLE_SESSION_BUCKETS)[number]
export function bucketDaemonLiveSessionCount(count: number | null): DaemonLifecycleSessionBucket {
if (count === null) {
return 'unknown'
}
if (count <= 0) {
return '0'
}
if (count === 1) {
return '1'
}
if (count <= 5) {
return '2-5'
}
return '6+'
}