import { type Database, type MachineMetricsSample } from "@opengeni/db"; import { type AttachedBrowserInventorySnapshot as AttachedBrowserInventoryContractValue } from "@opengeni/contracts"; import { type EventBus } from "@opengeni/events"; import type { Observability } from "@opengeni/observability"; import { Hello, type AttachedBrowserInventorySnapshot as WireAttachedBrowserInventorySnapshot, type MetricsSample } from "@opengeni/agent-proto"; /** The wildcard subject the agent event plane publishes heartbeats on. */ export declare const AGENT_EVENTS_SUBJECT = "agent.*.*.connection.*.events"; /** The wildcard subject the agent publishes its connect Hello on. */ export declare const AGENT_HELLO_SUBJECT = "agent.*.*.connection.*.hello"; export type AgentEventIngestionScheduler = { enqueue: (payload: Uint8Array, subject: string) => void; whenIdle: () => Promise; close: () => void; }; /** * Drain agent events without making every Connected Machine wait behind one * deployment-wide serial DB queue. Each exact process subject retains FIFO order; * separate subjects drain concurrently. Consecutive pending heartbeats for one * subject are latest-wins because they are point-in-time liveness/metrics samples, * while GoingOffline and update-progress events remain ordered barriers. */ export declare function createAgentEventIngestionScheduler(handler: (payload: Uint8Array, subject: string) => void | Promise, options?: { maxConcurrentSubjects?: number; onHeartbeatCoalesced?: () => void; }): AgentEventIngestionScheduler; /** Parse the exact process events subject (heartbeat plane). */ export declare function parseAgentEventSubject(subject: string): { workspaceId: string; agentId: string; connectionInstanceId: string; } | null; /** Parse the exact process hello subject (connect plane). */ export declare function parseAgentHelloSubject(subject: string): { workspaceId: string; agentId: string; connectionInstanceId: string; } | null; /** * Project a wire `MetricsSample` (proto, ms-stamped, GPU as a repeated list) to * the DB `MachineMetricsSample`. The proto byte/count fields are protobuf-encoded * as decimal strings (uint64) on the TS side (ts-proto `string`); coerce to * numbers. The DB carries a single `gpuUtilPercent` + `gpuMemUsedBytes`/Total — * we take the FIRST GPU (the dashboard surfaces the primary accelerator); absent * GPUs stay null (the not-reported contract). A zero on a non-GPU field is the * agent's "not reported" (we keep it null-friendly via `nullIfZero` only for the * GPU plane; cpu/mem/disk 0 is a legitimate reading the dashboard shows as 0). */ export declare function wireSampleToDbSample(wire: MetricsSample): MachineMetricsSample; /** Validate and project one wire inventory before it reaches durable browser * discovery. Unknown enum values and unsafe uint64 counters reject the whole * authoritative snapshot instead of partially disconnecting good endpoints. */ export declare function wireAttachedBrowserInventoryToContract(wire: WireAttachedBrowserInventorySnapshot): AttachedBrowserInventoryContractValue; /** * Decode a raw `AgentEvent` payload + ingest it (the per-message handler). A * heartbeat carrying a metrics sample is ingested; a going-offline records the * machine-plane marker + fans out the link-plane session events. Decode failures * are reported + swallowed. `bus` (when present) enables the session-event * fan-out; the live consumer always supplies it, pure unit tests may omit it. */ export declare function handleAgentEventPayload(db: Database, observability: Observability | undefined, payload: Uint8Array, subject: string, bus?: EventBus): Promise; /** * Start the metrics-ingestion consumer: subscribe exact process events and ingest * every heartbeat. Gated by sandboxSelfhostedEnabled (the caller checks the flag; * a disabled deployment never starts the consumer). Returns the unsubscribe fn. */ export declare function startMetricsIngestion(deps: { db: Database; bus: EventBus; observability?: Observability; }): () => void; /** * The LIVE display presence the agent's Hello reports: a desktop framebuffer is * available (`capabilities.desktop`, which the agent sets true only when a display * probes AND it can stream it) OR a `Display` detail is present. An unset * Capabilities (or a headless machine) → false. This is what `has_display` should * track, replacing the enroll-time snapshot. */ export declare function helloReportsDisplay(hello: Hello): boolean; /** * The human, actionable reason a display is present but UNUSABLE (macOS Screen * Recording / TCC not granted), or null when capture is permitted / the machine is * headless. Normalizes the proto's non-optional "" empty string to null so the DB * carries a clean tri-state (a real reason vs. no reason) — the Machines dashboard * shows "display: capture not granted" only when this is non-null. */ export declare function helloDesktopUnavailableReason(hello: Hello): string | null; /** Whether the runner's current Hello advertises the op-stream engine. */ export declare function helloReportsOpStream(hello: Hello): boolean; export declare function helloRuntimeCapabilities(hello: Hello): Record; /** * Reconcile `enrollments.has_display` (+ the capture-blocked reason) to what a Hello * reports. Resolves the enrollment (the accountId is the RLS principal + the * existence check + the current values). A no-change Hello short-circuits BEFORE * issuing any write (and the DB writer is itself change-guarded on BOTH fields as a * backstop), so a steady state never churns. An unknown/cross-workspace agentId is a * no-op. */ export declare function refreshEnrollmentDisplay(db: Database, input: { workspaceId: string; agentId: string; hasDisplay: boolean; desktopUnavailableReason?: string | null; connectionInstanceId?: string; }): Promise<{ updated: boolean; }>; /** * Reconcile `enrollments.op_stream` to what a Hello reports. Resolves the * enrollment first so the accountId remains the RLS principal and so a no-change * Hello short-circuits BEFORE issuing any write (the DB writer is itself * change-guarded as a backstop). An unknown/cross-workspace agentId is a no-op. */ export declare function refreshEnrollmentOpStream(db: Database, input: { workspaceId: string; agentId: string; opStream: boolean; connectionInstanceId?: string; }): Promise<{ updated: boolean; }>; /** * Decode a raw `Hello` payload + refresh the enrollment's display cursor + clear * any pending clean going-offline marker and, when the reconnect actually cleared * one, fan out machine.link.restored to the sessions with an active op on the * machine (the per-message handler for the hello plane). Decode failures + write * failures are reported + swallowed — a Hello must NEVER break the agent's connect. * `bus` (when present) enables the link.restored fan-out. */ export declare function handleHelloPayload(db: Database, observability: Observability | undefined, payload: Uint8Array, subject: string, bus?: EventBus): Promise; /** * Start the Hello display-refresh consumer: subscribe exact process hellos and * reconcile `has_display` to the live capability the agent reports on every * connect. Gated by sandboxSelfhostedEnabled (the caller checks the flag). Returns * the unsubscribe fn. */ export declare function startHelloIngestion(deps: { db: Database; bus: EventBus; observability?: Observability; }): () => void;