Co-authored-by: jason.woltje <jason@diversecanvas.com> Co-committed-by: jason.woltje <jason@diversecanvas.com>
64 lines
1.9 KiB
TypeScript
64 lines
1.9 KiB
TypeScript
/**
|
|
* Deterministic liveness computation (RFC-001 §4.5).
|
|
*
|
|
* The authoritative liveness signal is the `mosaic.presence` heartbeat, NOT
|
|
* native Matrix presence. Given the age of an agent's last heartbeat and a
|
|
* policy, these pure functions classify online/away/offline the same way every
|
|
* time — which is exactly what makes the A3 "hard-killed agent flips to
|
|
* offline within dark_threshold" guarantee deterministic and testable without
|
|
* standing up a homeserver.
|
|
*/
|
|
|
|
import type {
|
|
AgentLiveness,
|
|
HeartbeatObservation,
|
|
LivenessPolicy,
|
|
PresenceStatus,
|
|
} from './types.js';
|
|
|
|
/**
|
|
* Classify a single agent from the age (ms) of its last heartbeat.
|
|
*
|
|
* - `age <= heartbeatIntervalMs * missTolerance` → **online**
|
|
* - `age < darkThresholdMs` → **away**
|
|
* - otherwise (or non-finite age) → **offline / dark**
|
|
*
|
|
* A non-finite age (never seen / NaN) fails safe to `offline`: we never assert
|
|
* a liveness we cannot substantiate.
|
|
*/
|
|
export function classifyLiveness(ageMs: number, policy: LivenessPolicy): PresenceStatus {
|
|
if (!Number.isFinite(ageMs)) {
|
|
return 'offline';
|
|
}
|
|
const onlineWindowMs = policy.heartbeatIntervalMs * policy.missTolerance;
|
|
if (ageMs <= onlineWindowMs) {
|
|
return 'online';
|
|
}
|
|
if (ageMs < policy.darkThresholdMs) {
|
|
return 'away';
|
|
}
|
|
return 'offline';
|
|
}
|
|
|
|
/**
|
|
* Compute liveness for every observed agent at wall-clock `nowMs`.
|
|
* The result order mirrors the input order (stable for display).
|
|
*/
|
|
export function computeFleetLiveness(
|
|
observations: readonly HeartbeatObservation[],
|
|
nowMs: number,
|
|
policy: LivenessPolicy,
|
|
): AgentLiveness[] {
|
|
return observations.map((o) => {
|
|
const ageMs = nowMs - o.lastSeenTs;
|
|
return {
|
|
slug: o.slug,
|
|
mxid: o.mxid,
|
|
status: classifyLiveness(ageMs, policy),
|
|
lastSeenTs: o.lastSeenTs,
|
|
ageMs,
|
|
lastSeq: o.lastSeq,
|
|
};
|
|
});
|
|
}
|