sentinelayer-cli 0.30.8 → 0.30.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/session.js +4 -3
- package/src/session/listeners.js +41 -10
package/package.json
CHANGED
package/src/commands/session.js
CHANGED
|
@@ -4324,8 +4324,9 @@ export function registerSessionCommand(program) {
|
|
|
4324
4324
|
const durablePresenceEnabled = options.presence !== false;
|
|
4325
4325
|
const publishPresence = durablePresenceEnabled && canPublishListenerPresence(agentId);
|
|
4326
4326
|
const presenceIntervalMs = Math.max(1, presenceIntervalSeconds) * 1000;
|
|
4327
|
-
const
|
|
4328
|
-
Math.max(presenceIntervalSeconds, presenceKeepaliveSeconds, 1)
|
|
4327
|
+
const effectivePresenceKeepaliveSeconds =
|
|
4328
|
+
Math.max(presenceIntervalSeconds, presenceKeepaliveSeconds, intervalSeconds, 1);
|
|
4329
|
+
const presenceKeepaliveMs = effectivePresenceKeepaliveSeconds * 1000;
|
|
4329
4330
|
let lastPresenceHeartbeatMs = 0;
|
|
4330
4331
|
let lastPresenceHeartbeatFingerprint = "";
|
|
4331
4332
|
|
|
@@ -4512,7 +4513,7 @@ export function registerSessionCommand(program) {
|
|
|
4512
4513
|
const publishLifecycle = {
|
|
4513
4514
|
...lifecycle,
|
|
4514
4515
|
presenceIntervalSeconds,
|
|
4515
|
-
presenceKeepaliveSeconds:
|
|
4516
|
+
presenceKeepaliveSeconds: effectivePresenceKeepaliveSeconds,
|
|
4516
4517
|
};
|
|
4517
4518
|
if (lifecycleType === "heartbeat") {
|
|
4518
4519
|
const nowMs = Date.now();
|
package/src/session/listeners.js
CHANGED
|
@@ -9,6 +9,7 @@ const LISTENER_EVENT_TYPES = new Set([
|
|
|
9
9
|
// A heartbeat older than this (and not explicitly stopped) means the
|
|
10
10
|
// listener likely died without a clean stop — show it as stale, not live.
|
|
11
11
|
const DEFAULT_STALE_AFTER_MS = 180_000;
|
|
12
|
+
const MAX_STALE_GRACE_MS = 60_000;
|
|
12
13
|
|
|
13
14
|
function normalizeString(value) {
|
|
14
15
|
return String(value || "").trim();
|
|
@@ -29,6 +30,38 @@ function positiveInt(value) {
|
|
|
29
30
|
return Number.isFinite(n) && n > 0 ? Math.floor(n) : null;
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function listenerStaleAfterMs({
|
|
34
|
+
staleAfterMs,
|
|
35
|
+
presenceKeepaliveSeconds,
|
|
36
|
+
presenceIntervalSeconds,
|
|
37
|
+
cadenceSeconds,
|
|
38
|
+
idleIntervalSeconds,
|
|
39
|
+
activeIntervalSeconds,
|
|
40
|
+
} = {}) {
|
|
41
|
+
const fallbackMs = Math.max(1, Number(staleAfterMs) || DEFAULT_STALE_AFTER_MS);
|
|
42
|
+
const keepaliveMs = presenceKeepaliveSeconds ? presenceKeepaliveSeconds * 1000 : null;
|
|
43
|
+
const expectedIntervalSeconds =
|
|
44
|
+
cadenceSeconds ||
|
|
45
|
+
presenceIntervalSeconds ||
|
|
46
|
+
idleIntervalSeconds ||
|
|
47
|
+
activeIntervalSeconds ||
|
|
48
|
+
null;
|
|
49
|
+
const expectedIntervalMs = expectedIntervalSeconds ? expectedIntervalSeconds * 1000 : 0;
|
|
50
|
+
|
|
51
|
+
if (keepaliveMs) {
|
|
52
|
+
// The listener can only publish on a poll tick. Allow one bounded poll
|
|
53
|
+
// interval after the advertised keepalive, not the old 2.5x keepalive
|
|
54
|
+
// window that made dead listeners look live for several extra minutes.
|
|
55
|
+
return keepaliveMs + Math.min(expectedIntervalMs || 0, MAX_STALE_GRACE_MS);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (expectedIntervalMs) {
|
|
59
|
+
return Math.max(fallbackMs, Math.round(expectedIntervalMs * 2.5));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return fallbackMs;
|
|
63
|
+
}
|
|
64
|
+
|
|
32
65
|
/**
|
|
33
66
|
* Reduce a stream of session_listener_* events into one row per agent: who is
|
|
34
67
|
* listening, at what cadence, and whether they're currently active or idle.
|
|
@@ -66,17 +99,14 @@ export function summarizeListeners(events = [], { nowMs = Date.now(), staleAfter
|
|
|
66
99
|
const cadenceSeconds = active
|
|
67
100
|
? activeIntervalSeconds || nextPollSeconds
|
|
68
101
|
: idleIntervalSeconds || nextPollSeconds;
|
|
69
|
-
const
|
|
70
|
-
presenceKeepaliveSeconds ||
|
|
71
|
-
presenceIntervalSeconds ||
|
|
72
|
-
cadenceSeconds ||
|
|
73
|
-
idleIntervalSeconds ||
|
|
74
|
-
activeIntervalSeconds ||
|
|
75
|
-
null;
|
|
76
|
-
const staleAfterForRowMs = Math.max(
|
|
102
|
+
const staleAfterForRowMs = listenerStaleAfterMs({
|
|
77
103
|
staleAfterMs,
|
|
78
|
-
|
|
79
|
-
|
|
104
|
+
presenceKeepaliveSeconds,
|
|
105
|
+
presenceIntervalSeconds,
|
|
106
|
+
cadenceSeconds,
|
|
107
|
+
idleIntervalSeconds,
|
|
108
|
+
activeIntervalSeconds,
|
|
109
|
+
});
|
|
80
110
|
let status;
|
|
81
111
|
if (stopped) status = "stopped";
|
|
82
112
|
else if (ageMs !== null && ageMs > staleAfterForRowMs) status = "stale";
|
|
@@ -94,6 +124,7 @@ export function summarizeListeners(events = [], { nowMs = Date.now(), staleAfter
|
|
|
94
124
|
presenceIntervalSeconds,
|
|
95
125
|
presenceKeepaliveSeconds,
|
|
96
126
|
nextPollSeconds,
|
|
127
|
+
staleAfterSeconds: Math.round(staleAfterForRowMs / 1000),
|
|
97
128
|
lastSeenAt: epoch ? new Date(epoch).toISOString() : null,
|
|
98
129
|
lastSeenAgoSeconds: ageMs !== null ? Math.round(ageMs / 1000) : null,
|
|
99
130
|
lastHumanActivityAt: normalizeString(payload.lastHumanActivityAt) || null,
|