UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

127 lines (126 loc) • 6.13 kB
import { M as resolveNonNegativeIntegerOption, g as isFutureDateTimestampMs, s as asFiniteNumber } from "./number-coercion-CkhMiRw8.mjs"; import "./session-key-DY3MOX4N.mjs"; import { n as normalizeAccountId, r as normalizeOptionalAccountId } from "./account-id-ifCOO7_9.mjs"; import { r as resolveChannelAccountEntry } from "./account-lookup-BAaS-Jak.mjs"; //#region src/gateway/channel-health-policy.ts function resolveAccountHealthMonitorOverride(channelConfig, channelId, accountId) { if (!channelConfig?.accounts) return; const direct = resolveChannelAccountEntry(channelConfig.accounts, accountId, channelId); if (typeof direct?.healthMonitor?.enabled === "boolean") return direct.healthMonitor.enabled; const normalizedAccountId = normalizeOptionalAccountId(accountId); if (!normalizedAccountId) return; const match = resolveChannelAccountEntry(channelConfig.accounts, normalizedAccountId, channelId, normalizeAccountId); if (typeof match?.healthMonitor?.enabled !== "boolean") return; return match.healthMonitor.enabled; } function resolveChannelHealthMonitorEnabled(cfg, channelId, accountId) { const channelConfig = cfg.channels?.[channelId]; const accountOverride = resolveAccountHealthMonitorOverride(channelConfig, channelId, accountId); const channelOverride = channelConfig?.healthMonitor?.enabled; return accountOverride ?? (typeof channelOverride === "boolean" ? channelOverride : true); } /** Keep channel-authored terminal detail above the shared unhealthy projection. */ function resolveChannelHealthState(snapshot, policy) { const evaluation = evaluateChannelHealth(snapshot, policy); return !evaluation.healthy && !(snapshot.lifecycle === "blocked" && snapshot.healthState) ? evaluation.reason : snapshot.healthState; } function resolveObservedChannelTimestamp(value, now) { return typeof value === "number" && Number.isFinite(value) && !isFutureDateTimestampMs(value, { nowMs: now }) ? value : null; } const BUSY_ACTIVITY_STALE_THRESHOLD_MS = 15e5; const CHANNEL_RECONNECT_GRACE_MS = 12e4; const DEFAULT_CHANNEL_STALE_EVENT_THRESHOLD_MS = 18e5; const DEFAULT_CHANNEL_CONNECT_GRACE_MS = 12e4; function evaluateChannelHealth(snapshot, policy) { if (snapshot.enabled === false || snapshot.configured === false) return { healthy: true, reason: "unmanaged" }; if (!snapshot.running && snapshot.terminalDisconnect) return { healthy: false, reason: "terminal-disconnect" }; if (snapshot.lifecycle === "blocked") return { healthy: false, reason: "blocked" }; if (snapshot.ingressUnavailable === true) return { healthy: false, reason: "ingress-unavailable" }; if (snapshot.linked === false) return { healthy: true, reason: "unmanaged" }; const lastStartAt = asFiniteNumber(snapshot.lastStartAt) ?? null; const currentLifecycleStarted = lastStartAt !== null && !isFutureDateTimestampMs(lastStartAt, { nowMs: policy.now }); if ((snapshot.lifecycle === "starting" || snapshot.lifecycle === "recovering") && currentLifecycleStarted && policy.now - lastStartAt < policy.channelConnectGraceMs) return { healthy: true, reason: "startup-connect-grace" }; if (snapshot.lifecycle === "stopped" || !snapshot.running) return { healthy: false, reason: "not-running" }; const activeRuns = resolveNonNegativeIntegerOption(snapshot.activeRuns, 0); const isBusy = snapshot.busy === true || activeRuns > 0; const lastRunActivityAt = resolveObservedChannelTimestamp(snapshot.lastRunActivityAt, policy.now); const activeRunStartedAt = resolveObservedChannelTimestamp(snapshot.activeRunStartedAt, policy.now); const lastTransportActivityAt = resolveObservedChannelTimestamp(snapshot.lastTransportActivityAt, policy.now); if (isBusy && (lastStartAt == null || lastRunActivityAt != null && lastRunActivityAt >= lastStartAt)) { const runActivityAge = lastRunActivityAt == null ? Number.POSITIVE_INFINITY : Math.max(0, policy.now - lastRunActivityAt); const disconnectedRunStartAge = snapshot.connected === false && activeRunStartedAt != null ? Math.max(0, policy.now - activeRunStartedAt) : 0; if (Math.max(runActivityAge, disconnectedRunStartAge) < BUSY_ACTIVITY_STALE_THRESHOLD_MS) return { healthy: true, reason: "busy" }; return { healthy: false, reason: "stuck" }; } if (snapshot.lifecycle === void 0 && currentLifecycleStarted) { if (policy.now - lastStartAt < policy.channelConnectGraceMs) return { healthy: true, reason: "startup-connect-grace" }; } if (snapshot.connected === false) { const lastDisconnectAt = snapshot.lastDisconnect && typeof snapshot.lastDisconnect !== "string" ? resolveObservedChannelTimestamp(snapshot.lastDisconnect.at, policy.now) : null; if (lastDisconnectAt != null && (lastStartAt == null || lastDisconnectAt >= lastStartAt) && Math.max(0, policy.now - lastDisconnectAt) < CHANNEL_RECONNECT_GRACE_MS) return { healthy: true, reason: "reconnect-grace" }; return { healthy: false, reason: "disconnected" }; } if (snapshot.connected === true && lastTransportActivityAt != null) { if (lastStartAt != null && lastTransportActivityAt < lastStartAt) { if (currentLifecycleStarted && policy.now - lastStartAt <= policy.staleEventThresholdMs) return { healthy: true, reason: "healthy" }; return { healthy: false, reason: "stale-socket" }; } if (policy.now - lastTransportActivityAt > policy.staleEventThresholdMs) return { healthy: false, reason: "stale-socket" }; } return { healthy: true, reason: "healthy" }; } function resolveChannelRestartReason(snapshot, evaluation) { if (evaluation.reason === "stale-socket" || evaluation.reason === "ingress-unavailable" || evaluation.reason === "disconnected") return evaluation.reason; if (evaluation.reason === "not-running") return snapshot.reconnectAttempts && snapshot.reconnectAttempts >= 10 ? "gave-up" : "stopped"; return "stuck"; } //#endregion export { resolveChannelHealthState as a, resolveChannelHealthMonitorEnabled as i, DEFAULT_CHANNEL_STALE_EVENT_THRESHOLD_MS as n, resolveChannelRestartReason as o, evaluateChannelHealth as r, DEFAULT_CHANNEL_CONNECT_GRACE_MS as t };