patchcord 0.5.59 → 0.5.60
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/scripts/subscribe.mjs +21 -0
package/package.json
CHANGED
package/scripts/subscribe.mjs
CHANGED
|
@@ -22,6 +22,11 @@ const RECONNECT_BACKOFF_MS = [1000, 2000, 4000, 8000, 15_000, 30_000];
|
|
|
22
22
|
// gap means three missed heartbeats. Force a reconnect via the outer loop.
|
|
23
23
|
const FRESHNESS_CHECK_INTERVAL_MS = 30_000;
|
|
24
24
|
const FRESHNESS_STALE_MS = 90_000;
|
|
25
|
+
// Re-emit pending count every minute as a safety net. The Realtime push
|
|
26
|
+
// + Monitor pipeline can drop notifications when an agent is mid-tool-call
|
|
27
|
+
// (especially for long-running work like vector-DB searches). Re-emitting
|
|
28
|
+
// gives Monitor multiple chances to surface the line on a later idle tick.
|
|
29
|
+
const PENDING_HEARTBEAT_MS = 60_000;
|
|
25
30
|
|
|
26
31
|
// Short HH:MM:SS prefix so the Monitor output can be scanned at a glance.
|
|
27
32
|
// Local time — Monitor's reader is always a human looking at one machine.
|
|
@@ -243,6 +248,7 @@ function runOnce(ticket, baseUrl, token, refreshTicket) {
|
|
|
243
248
|
let heartbeatTimer = null;
|
|
244
249
|
let refreshTimer = null;
|
|
245
250
|
let freshnessTimer = null;
|
|
251
|
+
let pendingHeartbeatTimer = null;
|
|
246
252
|
let lastEventAt = Date.now();
|
|
247
253
|
let currentJwt = ticket.jwt;
|
|
248
254
|
let settled = false;
|
|
@@ -253,6 +259,7 @@ function runOnce(ticket, baseUrl, token, refreshTicket) {
|
|
|
253
259
|
if (heartbeatTimer) clearInterval(heartbeatTimer);
|
|
254
260
|
if (refreshTimer) clearTimeout(refreshTimer);
|
|
255
261
|
if (freshnessTimer) clearInterval(freshnessTimer);
|
|
262
|
+
if (pendingHeartbeatTimer) clearInterval(pendingHeartbeatTimer);
|
|
256
263
|
try {
|
|
257
264
|
ws.close();
|
|
258
265
|
} catch (_) {}
|
|
@@ -311,6 +318,20 @@ function runOnce(ticket, baseUrl, token, refreshTicket) {
|
|
|
311
318
|
}
|
|
312
319
|
}, FRESHNESS_CHECK_INTERVAL_MS);
|
|
313
320
|
|
|
321
|
+
// Pending-inbox heartbeat. The Realtime INSERT push that triggers the
|
|
322
|
+
// initial "PATCHCORD: 1 new from <sender>" notification can be lost
|
|
323
|
+
// by the Monitor → agent-context surface layer if the agent happens
|
|
324
|
+
// to be mid-tool-call when the line is emitted (heavy agents with
|
|
325
|
+
// long tool calls — vector-DB search, CrossRef lookups — are most
|
|
326
|
+
// exposed). Re-emit the pending count once a minute as long as the
|
|
327
|
+
// inbox is non-empty so a later idle tick has another chance to
|
|
328
|
+
// wake the agent. drainQueueOnce stays silent if pending_count == 0.
|
|
329
|
+
pendingHeartbeatTimer = setInterval(() => {
|
|
330
|
+
drainQueueOnce(baseUrl, token).catch((e) => {
|
|
331
|
+
logErr(`subscribe: pending heartbeat failed: ${e.message}`);
|
|
332
|
+
});
|
|
333
|
+
}, PENDING_HEARTBEAT_MS);
|
|
334
|
+
|
|
314
335
|
const scheduleRefresh = (ttlSec) => {
|
|
315
336
|
const refreshIn = Math.max((ttlSec - JWT_REFRESH_SAFETY_MARGIN_SEC) * 1000, 30_000);
|
|
316
337
|
refreshTimer = setTimeout(doRefresh, refreshIn);
|