commonswarm 0.1.57 → 0.1.58
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/cswarm.cjs +665 -24
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -27126,6 +27126,34 @@ function describeRenewalGrant(grant) {
|
|
|
27126
27126
|
return lines;
|
|
27127
27127
|
}
|
|
27128
27128
|
|
|
27129
|
+
// src/cloud/wake.ts
|
|
27130
|
+
var WAKE_EVENT = "wake";
|
|
27131
|
+
var WAKE_TOPIC_RE = /^cswarm-wake:[A-Za-z0-9_-]{43}$/;
|
|
27132
|
+
function isWakeTopic(value) {
|
|
27133
|
+
return WAKE_TOPIC_RE.test(value);
|
|
27134
|
+
}
|
|
27135
|
+
var WakeHintError = class extends Error {
|
|
27136
|
+
code = "malformed_wake";
|
|
27137
|
+
constructor(message) {
|
|
27138
|
+
super(message);
|
|
27139
|
+
this.name = "WakeHintError";
|
|
27140
|
+
}
|
|
27141
|
+
};
|
|
27142
|
+
function parseOptionalWakeHint(value) {
|
|
27143
|
+
if (value === void 0 || value === null) return void 0;
|
|
27144
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
27145
|
+
throw new WakeHintError("wake hint must be an object");
|
|
27146
|
+
}
|
|
27147
|
+
const row = value;
|
|
27148
|
+
if (typeof row.topic !== "string" || !isWakeTopic(row.topic)) {
|
|
27149
|
+
throw new WakeHintError("wake hint topic is malformed");
|
|
27150
|
+
}
|
|
27151
|
+
if (row.event !== WAKE_EVENT) {
|
|
27152
|
+
throw new WakeHintError("wake hint event is malformed");
|
|
27153
|
+
}
|
|
27154
|
+
return { topic: row.topic, event: WAKE_EVENT };
|
|
27155
|
+
}
|
|
27156
|
+
|
|
27129
27157
|
// src/cloud/renewal.ts
|
|
27130
27158
|
var AGENT_TOKEN_DEFAULT_TTL_MS2 = 60 * 60 * 1e3;
|
|
27131
27159
|
var AGENT_TOKEN_MAX_TTL_MS2 = 8 * 60 * 60 * 1e3;
|
|
@@ -27427,6 +27455,16 @@ async function requestSuccessor(options) {
|
|
|
27427
27455
|
"The deployment issued a successor credential that lasts longer than eight hours. cswarm refused to store it. Agent credentials stay short on purpose; renewal is what makes that survivable."
|
|
27428
27456
|
);
|
|
27429
27457
|
}
|
|
27458
|
+
let wake;
|
|
27459
|
+
try {
|
|
27460
|
+
wake = parseOptionalWakeHint(body.wake);
|
|
27461
|
+
} catch {
|
|
27462
|
+
throw new RenewalRefused(
|
|
27463
|
+
response.status,
|
|
27464
|
+
"malformed_wake",
|
|
27465
|
+
"The deployment returned a successor credential with a malformed wake hint. It was not stored."
|
|
27466
|
+
);
|
|
27467
|
+
}
|
|
27430
27468
|
return {
|
|
27431
27469
|
token,
|
|
27432
27470
|
tokenId: tokenId.toLowerCase(),
|
|
@@ -27435,7 +27473,8 @@ async function requestSuccessor(options) {
|
|
|
27435
27473
|
issuedAt,
|
|
27436
27474
|
expiresAt,
|
|
27437
27475
|
horizonExpiresAt: timestamp(body.horizon_expires_at),
|
|
27438
|
-
successorsRemaining: count(body.successors_remaining)
|
|
27476
|
+
successorsRemaining: count(body.successors_remaining),
|
|
27477
|
+
...wake === void 0 ? {} : { wake }
|
|
27439
27478
|
};
|
|
27440
27479
|
}
|
|
27441
27480
|
var AgentCredentialSession = class _AgentCredentialSession {
|
|
@@ -30010,6 +30049,12 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
|
|
|
30010
30049
|
const rawRows = body.signals;
|
|
30011
30050
|
const parsedRows = parseSignalRows(rawRows, parseOptions2);
|
|
30012
30051
|
const ascending = query.ascending === true || query.after !== void 0;
|
|
30052
|
+
let wake;
|
|
30053
|
+
try {
|
|
30054
|
+
wake = parseOptionalWakeHint(body.wake);
|
|
30055
|
+
} catch {
|
|
30056
|
+
throw plainMalformedError("signal read returned a malformed wake hint");
|
|
30057
|
+
}
|
|
30013
30058
|
return {
|
|
30014
30059
|
signals: sortSignals(
|
|
30015
30060
|
rowsAfterCursor(parsedRows.signals, query.after),
|
|
@@ -30020,7 +30065,8 @@ async function agentSignalPage(target2, credential, query, options, allowLegacyC
|
|
|
30020
30065
|
rawCount: rawRows.length,
|
|
30021
30066
|
nextCursor: rawRows.length === 0 ? null : cursorFromUnknown(rawRows[rawRows.length - 1]),
|
|
30022
30067
|
malformedRows: parsedRows.malformedRows,
|
|
30023
|
-
pendingDeliveryCount
|
|
30068
|
+
pendingDeliveryCount,
|
|
30069
|
+
...wake === void 0 ? {} : { wake }
|
|
30024
30070
|
};
|
|
30025
30071
|
}
|
|
30026
30072
|
function parseAgentMemberRow(value) {
|
|
@@ -32076,11 +32122,18 @@ function parseClaimSuccess(body, expected, now) {
|
|
|
32076
32122
|
"delivery claim response returned more deliveries than its pending count"
|
|
32077
32123
|
);
|
|
32078
32124
|
}
|
|
32125
|
+
let wake;
|
|
32126
|
+
try {
|
|
32127
|
+
wake = parseOptionalWakeHint(row.wake);
|
|
32128
|
+
} catch {
|
|
32129
|
+
throw new DeliveryProtocolError("delivery claim response wake field is malformed");
|
|
32130
|
+
}
|
|
32079
32131
|
return {
|
|
32080
32132
|
capabilities,
|
|
32081
32133
|
deliveries,
|
|
32082
32134
|
pendingDeliveryCount,
|
|
32083
|
-
terminalDeliveryFailureCount
|
|
32135
|
+
terminalDeliveryFailureCount,
|
|
32136
|
+
...wake === void 0 ? {} : { wake }
|
|
32084
32137
|
};
|
|
32085
32138
|
}
|
|
32086
32139
|
function parseAckSuccess(body, expected) {
|
|
@@ -32294,7 +32347,8 @@ var DeliveryCommandClient = class {
|
|
|
32294
32347
|
capabilities: parsed.capabilities,
|
|
32295
32348
|
deliveries: parsed.deliveries,
|
|
32296
32349
|
pendingDeliveryCount: parsed.pendingDeliveryCount,
|
|
32297
|
-
terminalDeliveryFailureCount: parsed.terminalDeliveryFailureCount
|
|
32350
|
+
terminalDeliveryFailureCount: parsed.terminalDeliveryFailureCount,
|
|
32351
|
+
...parsed.wake === void 0 ? {} : { wake: parsed.wake }
|
|
32298
32352
|
};
|
|
32299
32353
|
}
|
|
32300
32354
|
/** Acknowledge one leased delivery with an exact terminal outcome. */
|
|
@@ -37412,6 +37466,346 @@ function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
|
|
|
37412
37466
|
}, true);
|
|
37413
37467
|
}
|
|
37414
37468
|
|
|
37469
|
+
// src/listener/wake.ts
|
|
37470
|
+
var LISTENER_RECONCILE_POLL_MS = 3e5;
|
|
37471
|
+
var WAKE_COALESCE_MS = 1e3;
|
|
37472
|
+
var WAKE_CLAIMS_PER_MINUTE_BUDGET = 50;
|
|
37473
|
+
var WAKE_RATE_LIMIT_POLL_MS = 6e4;
|
|
37474
|
+
var REALTIME_SUBSCRIBE_STATUS = {
|
|
37475
|
+
SUBSCRIBED: "SUBSCRIBED",
|
|
37476
|
+
CHANNEL_ERROR: "CHANNEL_ERROR",
|
|
37477
|
+
CLOSED: "CLOSED",
|
|
37478
|
+
TIMED_OUT: "TIMED_OUT"
|
|
37479
|
+
};
|
|
37480
|
+
var LISTENER_WAKE_MODES = ["push", "poll"];
|
|
37481
|
+
var LISTENER_WAKE_MODE_PUSH = LISTENER_WAKE_MODES[0];
|
|
37482
|
+
var LISTENER_WAKE_MODE_POLL = LISTENER_WAKE_MODES[1];
|
|
37483
|
+
var LISTENER_WAKE_MODE_SET = new Set(
|
|
37484
|
+
LISTENER_WAKE_MODES
|
|
37485
|
+
);
|
|
37486
|
+
var WAKE_ERROR_CODES = [
|
|
37487
|
+
"channel_error",
|
|
37488
|
+
"closed",
|
|
37489
|
+
"timed_out",
|
|
37490
|
+
"rate_limited",
|
|
37491
|
+
"wake_budget"
|
|
37492
|
+
];
|
|
37493
|
+
var WAKE_ERROR_CODE_SET = new Set(WAKE_ERROR_CODES);
|
|
37494
|
+
var WAKE_ERROR_CODE_WAKE_BUDGET = WAKE_ERROR_CODES.find(
|
|
37495
|
+
(code) => code === "wake_budget"
|
|
37496
|
+
);
|
|
37497
|
+
var LISTENER_WAKE_STATUS_KEYS = [
|
|
37498
|
+
"mode",
|
|
37499
|
+
"subscribedAt",
|
|
37500
|
+
"reconnects",
|
|
37501
|
+
"lastWakeAt",
|
|
37502
|
+
"lastReconcileAt",
|
|
37503
|
+
"errorCode",
|
|
37504
|
+
"topicRotatedAt",
|
|
37505
|
+
"rateLimited"
|
|
37506
|
+
];
|
|
37507
|
+
var LISTENER_WAKE_SENSITIVE_KEYS = [
|
|
37508
|
+
"topic",
|
|
37509
|
+
"wakeTopic",
|
|
37510
|
+
"wake_topic"
|
|
37511
|
+
];
|
|
37512
|
+
function defaultRealtime(target2) {
|
|
37513
|
+
const client = createClient(target2.url, target2.anonKey, {
|
|
37514
|
+
auth: { persistSession: false, autoRefreshToken: false }
|
|
37515
|
+
});
|
|
37516
|
+
return client.realtime;
|
|
37517
|
+
}
|
|
37518
|
+
function isSubscribeStatus(value) {
|
|
37519
|
+
return value === REALTIME_SUBSCRIBE_STATUS.SUBSCRIBED || value === REALTIME_SUBSCRIBE_STATUS.CHANNEL_ERROR || value === REALTIME_SUBSCRIBE_STATUS.CLOSED || value === REALTIME_SUBSCRIBE_STATUS.TIMED_OUT;
|
|
37520
|
+
}
|
|
37521
|
+
function wakeErrorCodeFromSubscribeStatus(status) {
|
|
37522
|
+
switch (status) {
|
|
37523
|
+
case REALTIME_SUBSCRIBE_STATUS.CHANNEL_ERROR:
|
|
37524
|
+
return "channel_error";
|
|
37525
|
+
case REALTIME_SUBSCRIBE_STATUS.CLOSED:
|
|
37526
|
+
return "closed";
|
|
37527
|
+
case REALTIME_SUBSCRIBE_STATUS.TIMED_OUT:
|
|
37528
|
+
return "timed_out";
|
|
37529
|
+
default:
|
|
37530
|
+
return null;
|
|
37531
|
+
}
|
|
37532
|
+
}
|
|
37533
|
+
function emptyListenerWakeStatus() {
|
|
37534
|
+
return {
|
|
37535
|
+
mode: LISTENER_WAKE_MODE_POLL,
|
|
37536
|
+
subscribedAt: null,
|
|
37537
|
+
reconnects: 0,
|
|
37538
|
+
lastWakeAt: null,
|
|
37539
|
+
lastReconcileAt: null,
|
|
37540
|
+
errorCode: null,
|
|
37541
|
+
topicRotatedAt: null,
|
|
37542
|
+
rateLimited: false
|
|
37543
|
+
};
|
|
37544
|
+
}
|
|
37545
|
+
function listenerWakePersistWorthy(previous, next, lastPersistMs, nowMs) {
|
|
37546
|
+
if (previous === void 0) return true;
|
|
37547
|
+
if (previous.mode !== next.mode || previous.rateLimited !== next.rateLimited || previous.errorCode !== next.errorCode || previous.reconnects !== next.reconnects || previous.subscribedAt !== next.subscribedAt || previous.topicRotatedAt !== next.topicRotatedAt || previous.lastReconcileAt !== next.lastReconcileAt) {
|
|
37548
|
+
return true;
|
|
37549
|
+
}
|
|
37550
|
+
if (previous.lastWakeAt !== next.lastWakeAt) {
|
|
37551
|
+
return nowMs - lastPersistMs >= WAKE_COALESCE_MS;
|
|
37552
|
+
}
|
|
37553
|
+
return false;
|
|
37554
|
+
}
|
|
37555
|
+
function listenerWakeStatusSentence(wake, pollIntervalMs, lastWakeLabel) {
|
|
37556
|
+
if (wake.mode === LISTENER_WAKE_MODE_PUSH) {
|
|
37557
|
+
const last = lastWakeLabel === null ? "no wake yet" : `last wake ${lastWakeLabel}`;
|
|
37558
|
+
return `${LISTENER_WAKE_MODE_PUSH} (Realtime), ${last}, reconcile every ${formatIdlePollDuration(LISTENER_RECONCILE_POLL_MS)}.`;
|
|
37559
|
+
}
|
|
37560
|
+
if (wake.errorCode === WAKE_ERROR_CODE_WAKE_BUDGET) {
|
|
37561
|
+
return `Subscribed; claims paused until the minute clears (${WAKE_ERROR_CODE_WAKE_BUDGET}); polling every ${formatIdlePollDuration(pollIntervalMs)} meanwhile.`;
|
|
37562
|
+
}
|
|
37563
|
+
const code = wake.errorCode ?? "disconnected";
|
|
37564
|
+
return `${LISTENER_WAKE_MODE_POLL} every ${formatIdlePollDuration(pollIntervalMs)}. Realtime not connected (${code}).`;
|
|
37565
|
+
}
|
|
37566
|
+
var WakeSubscriber = class {
|
|
37567
|
+
now;
|
|
37568
|
+
target;
|
|
37569
|
+
createRealtime;
|
|
37570
|
+
realtime = null;
|
|
37571
|
+
channel = null;
|
|
37572
|
+
topic = null;
|
|
37573
|
+
waiter = null;
|
|
37574
|
+
pending = null;
|
|
37575
|
+
connectionState = "disconnected";
|
|
37576
|
+
subscribedAt = null;
|
|
37577
|
+
reconnects = 0;
|
|
37578
|
+
lastWakeAt = null;
|
|
37579
|
+
lastReconcileAt = null;
|
|
37580
|
+
lastErrorCode = null;
|
|
37581
|
+
topicRotatedAt = null;
|
|
37582
|
+
rateLimitedUntil = 0;
|
|
37583
|
+
lastClaimAt = 0;
|
|
37584
|
+
wakeClaimTimes = [];
|
|
37585
|
+
closed = false;
|
|
37586
|
+
everSubscribed = false;
|
|
37587
|
+
constructor(options) {
|
|
37588
|
+
this.target = options.target;
|
|
37589
|
+
this.now = options.now ?? Date.now;
|
|
37590
|
+
this.createRealtime = options.createRealtime ?? defaultRealtime;
|
|
37591
|
+
}
|
|
37592
|
+
get state() {
|
|
37593
|
+
return this.connectionState;
|
|
37594
|
+
}
|
|
37595
|
+
get hasTopic() {
|
|
37596
|
+
return this.topic !== null;
|
|
37597
|
+
}
|
|
37598
|
+
snapshot(nowMs = this.now()) {
|
|
37599
|
+
const serverLimited = nowMs < this.rateLimitedUntil;
|
|
37600
|
+
const overBudget = this.overWakeBudget(nowMs);
|
|
37601
|
+
const rateLimited = serverLimited || overBudget;
|
|
37602
|
+
const mode3 = this.connectionState === "subscribed" && !rateLimited ? LISTENER_WAKE_MODE_PUSH : LISTENER_WAKE_MODE_POLL;
|
|
37603
|
+
const errorCode = serverLimited ? "rate_limited" : overBudget ? WAKE_ERROR_CODE_WAKE_BUDGET : this.lastErrorCode;
|
|
37604
|
+
return {
|
|
37605
|
+
mode: mode3,
|
|
37606
|
+
subscribedAt: this.subscribedAt,
|
|
37607
|
+
reconnects: this.reconnects,
|
|
37608
|
+
lastWakeAt: this.lastWakeAt,
|
|
37609
|
+
lastReconcileAt: this.lastReconcileAt,
|
|
37610
|
+
errorCode,
|
|
37611
|
+
topicRotatedAt: this.topicRotatedAt,
|
|
37612
|
+
rateLimited
|
|
37613
|
+
};
|
|
37614
|
+
}
|
|
37615
|
+
noteReconcile(nowMs = this.now()) {
|
|
37616
|
+
this.lastReconcileAt = new Date(nowMs).toISOString();
|
|
37617
|
+
}
|
|
37618
|
+
noteClaim(nowMs = this.now()) {
|
|
37619
|
+
this.lastClaimAt = nowMs;
|
|
37620
|
+
}
|
|
37621
|
+
noteWakeClaim(nowMs = this.now()) {
|
|
37622
|
+
this.noteClaim(nowMs);
|
|
37623
|
+
this.wakeClaimTimes.push(nowMs);
|
|
37624
|
+
this.trimWakeClaims(nowMs);
|
|
37625
|
+
}
|
|
37626
|
+
coalescingRemainingMs(nowMs = this.now()) {
|
|
37627
|
+
if (this.lastClaimAt <= 0) return 0;
|
|
37628
|
+
return Math.max(0, this.lastClaimAt + WAKE_COALESCE_MS - nowMs);
|
|
37629
|
+
}
|
|
37630
|
+
overWakeBudget(nowMs = this.now()) {
|
|
37631
|
+
this.trimWakeClaims(nowMs);
|
|
37632
|
+
return this.wakeClaimTimes.length >= WAKE_CLAIMS_PER_MINUTE_BUDGET;
|
|
37633
|
+
}
|
|
37634
|
+
canClaimOnWake(nowMs = this.now()) {
|
|
37635
|
+
if (nowMs < this.rateLimitedUntil) return false;
|
|
37636
|
+
if (this.coalescingRemainingMs(nowMs) > 0) return false;
|
|
37637
|
+
return !this.overWakeBudget(nowMs);
|
|
37638
|
+
}
|
|
37639
|
+
markRateLimited(nowMs = this.now()) {
|
|
37640
|
+
this.rateLimitedUntil = nowMs + WAKE_RATE_LIMIT_POLL_MS;
|
|
37641
|
+
this.lastErrorCode = "rate_limited";
|
|
37642
|
+
this.emitPending("state");
|
|
37643
|
+
}
|
|
37644
|
+
setTopic(topic) {
|
|
37645
|
+
if (this.closed) return;
|
|
37646
|
+
if (!isWakeTopic(topic)) {
|
|
37647
|
+
throw new Error("wake topic is malformed");
|
|
37648
|
+
}
|
|
37649
|
+
if (this.topic === topic) return;
|
|
37650
|
+
const rotated = this.topic !== null;
|
|
37651
|
+
void this.detachChannel();
|
|
37652
|
+
this.topic = topic;
|
|
37653
|
+
if (rotated) {
|
|
37654
|
+
this.topicRotatedAt = new Date(this.now()).toISOString();
|
|
37655
|
+
}
|
|
37656
|
+
this.connect();
|
|
37657
|
+
}
|
|
37658
|
+
next(options) {
|
|
37659
|
+
if (this.waiter !== null) {
|
|
37660
|
+
throw new Error("wake next() already has a waiter");
|
|
37661
|
+
}
|
|
37662
|
+
if (options.signal?.aborted) {
|
|
37663
|
+
return Promise.resolve("deadline");
|
|
37664
|
+
}
|
|
37665
|
+
const nowMs = this.now();
|
|
37666
|
+
if (this.pending !== null) {
|
|
37667
|
+
if (!(this.pending === "wake" && this.wakeClaimPaused(nowMs))) {
|
|
37668
|
+
const reason = this.pending;
|
|
37669
|
+
this.pending = null;
|
|
37670
|
+
return Promise.resolve(reason);
|
|
37671
|
+
}
|
|
37672
|
+
}
|
|
37673
|
+
if (nowMs >= options.until) {
|
|
37674
|
+
return Promise.resolve("deadline");
|
|
37675
|
+
}
|
|
37676
|
+
return new Promise((resolve3) => {
|
|
37677
|
+
const finish = (reason) => {
|
|
37678
|
+
if (this.waiter === null) return;
|
|
37679
|
+
const current = this.waiter;
|
|
37680
|
+
this.waiter = null;
|
|
37681
|
+
if (current.timer !== null) clearTimeout(current.timer);
|
|
37682
|
+
if (current.signal && current.onAbort) {
|
|
37683
|
+
current.signal.removeEventListener("abort", current.onAbort);
|
|
37684
|
+
}
|
|
37685
|
+
resolve3(reason);
|
|
37686
|
+
};
|
|
37687
|
+
const delay2 = Math.max(0, options.until - this.now());
|
|
37688
|
+
const timer2 = setTimeout(() => finish("deadline"), delay2);
|
|
37689
|
+
const onAbort = () => finish("deadline");
|
|
37690
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
37691
|
+
this.waiter = {
|
|
37692
|
+
resolve: finish,
|
|
37693
|
+
timer: timer2,
|
|
37694
|
+
onAbort,
|
|
37695
|
+
signal: options.signal
|
|
37696
|
+
};
|
|
37697
|
+
});
|
|
37698
|
+
}
|
|
37699
|
+
async close() {
|
|
37700
|
+
this.closed = true;
|
|
37701
|
+
this.topic = null;
|
|
37702
|
+
this.finishWait("deadline");
|
|
37703
|
+
await this.detachChannel();
|
|
37704
|
+
try {
|
|
37705
|
+
this.realtime?.disconnect?.();
|
|
37706
|
+
} catch {
|
|
37707
|
+
}
|
|
37708
|
+
this.realtime = null;
|
|
37709
|
+
this.connectionState = "disconnected";
|
|
37710
|
+
}
|
|
37711
|
+
trimWakeClaims(nowMs) {
|
|
37712
|
+
const minuteStart = Math.floor(nowMs / 6e4) * 6e4;
|
|
37713
|
+
this.wakeClaimTimes = this.wakeClaimTimes.filter((ts) => ts >= minuteStart);
|
|
37714
|
+
}
|
|
37715
|
+
/** Client budget or a server 429: do not claim on wake; poll covers the window. */
|
|
37716
|
+
wakeClaimPaused(nowMs) {
|
|
37717
|
+
return nowMs < this.rateLimitedUntil || this.overWakeBudget(nowMs);
|
|
37718
|
+
}
|
|
37719
|
+
emitPending(reason) {
|
|
37720
|
+
if (this.waiter !== null) {
|
|
37721
|
+
if (reason === "wake" && this.wakeClaimPaused(this.now())) {
|
|
37722
|
+
this.pending = "wake";
|
|
37723
|
+
return;
|
|
37724
|
+
}
|
|
37725
|
+
this.finishWait(reason);
|
|
37726
|
+
return;
|
|
37727
|
+
}
|
|
37728
|
+
if (reason === "wake" || this.pending !== "wake") {
|
|
37729
|
+
this.pending = reason;
|
|
37730
|
+
}
|
|
37731
|
+
}
|
|
37732
|
+
finishWait(reason) {
|
|
37733
|
+
const waiter = this.waiter;
|
|
37734
|
+
if (waiter === null) return;
|
|
37735
|
+
this.waiter = null;
|
|
37736
|
+
if (waiter.timer !== null) clearTimeout(waiter.timer);
|
|
37737
|
+
if (waiter.signal && waiter.onAbort) {
|
|
37738
|
+
waiter.signal.removeEventListener("abort", waiter.onAbort);
|
|
37739
|
+
}
|
|
37740
|
+
waiter.resolve(reason);
|
|
37741
|
+
}
|
|
37742
|
+
connect() {
|
|
37743
|
+
if (this.closed || this.topic === null) return;
|
|
37744
|
+
if (this.realtime === null) {
|
|
37745
|
+
this.realtime = this.createRealtime(this.target);
|
|
37746
|
+
void this.realtime.setAuth(this.target.anonKey);
|
|
37747
|
+
}
|
|
37748
|
+
const topic = this.topic;
|
|
37749
|
+
this.connectionState = "connecting";
|
|
37750
|
+
this.lastErrorCode = null;
|
|
37751
|
+
const channel = this.realtime.channel(topic, {
|
|
37752
|
+
config: { private: true }
|
|
37753
|
+
});
|
|
37754
|
+
this.channel = channel;
|
|
37755
|
+
channel.on("broadcast", { event: WAKE_EVENT }, () => {
|
|
37756
|
+
this.lastWakeAt = new Date(this.now()).toISOString();
|
|
37757
|
+
this.emitPending("wake");
|
|
37758
|
+
});
|
|
37759
|
+
channel.subscribe((status) => {
|
|
37760
|
+
this.onSubscribeStatus(status);
|
|
37761
|
+
});
|
|
37762
|
+
}
|
|
37763
|
+
onSubscribeStatus(status) {
|
|
37764
|
+
if (this.closed) return;
|
|
37765
|
+
if (!isSubscribeStatus(status)) return;
|
|
37766
|
+
const wasSubscribed = this.connectionState === "subscribed";
|
|
37767
|
+
if (status === REALTIME_SUBSCRIBE_STATUS.SUBSCRIBED) {
|
|
37768
|
+
this.connectionState = "subscribed";
|
|
37769
|
+
this.subscribedAt = new Date(this.now()).toISOString();
|
|
37770
|
+
this.lastErrorCode = null;
|
|
37771
|
+
if (this.everSubscribed && !wasSubscribed) this.reconnects += 1;
|
|
37772
|
+
this.everSubscribed = true;
|
|
37773
|
+
if (!wasSubscribed) this.emitPending("state");
|
|
37774
|
+
return;
|
|
37775
|
+
}
|
|
37776
|
+
const code = wakeErrorCodeFromSubscribeStatus(status);
|
|
37777
|
+
if (status === REALTIME_SUBSCRIBE_STATUS.CLOSED) {
|
|
37778
|
+
this.connectionState = "disconnected";
|
|
37779
|
+
} else {
|
|
37780
|
+
this.connectionState = "errored";
|
|
37781
|
+
}
|
|
37782
|
+
this.lastErrorCode = code;
|
|
37783
|
+
this.subscribedAt = null;
|
|
37784
|
+
if (wasSubscribed) this.emitPending("state");
|
|
37785
|
+
}
|
|
37786
|
+
async detachChannel() {
|
|
37787
|
+
const channel = this.channel;
|
|
37788
|
+
this.channel = null;
|
|
37789
|
+
if (channel === null) return;
|
|
37790
|
+
try {
|
|
37791
|
+
await channel.unsubscribe();
|
|
37792
|
+
} catch {
|
|
37793
|
+
}
|
|
37794
|
+
try {
|
|
37795
|
+
await this.realtime?.removeChannel?.(channel);
|
|
37796
|
+
} catch {
|
|
37797
|
+
}
|
|
37798
|
+
if (this.channel !== null) return;
|
|
37799
|
+
if (this.connectionState === "subscribed") {
|
|
37800
|
+
this.connectionState = "disconnected";
|
|
37801
|
+
this.subscribedAt = null;
|
|
37802
|
+
}
|
|
37803
|
+
}
|
|
37804
|
+
};
|
|
37805
|
+
function createWakeSubscriber(options) {
|
|
37806
|
+
return new WakeSubscriber(options);
|
|
37807
|
+
}
|
|
37808
|
+
|
|
37415
37809
|
// src/listener/runtime.ts
|
|
37416
37810
|
var LISTENER_PAGE_LIMIT = 100;
|
|
37417
37811
|
var LISTENER_IDLE_POLL_MS = IDLE_POLL_DEFAULT_MS;
|
|
@@ -37901,6 +38295,35 @@ async function runListenerRuntime(options) {
|
|
|
37901
38295
|
abort.addEventListener("abort", onAbort);
|
|
37902
38296
|
}
|
|
37903
38297
|
let stop;
|
|
38298
|
+
let wakeSubscriber = options.wake ?? null;
|
|
38299
|
+
let reconcileDueAt = now();
|
|
38300
|
+
const ensureWake = () => {
|
|
38301
|
+
if (wakeSubscriber === null) {
|
|
38302
|
+
wakeSubscriber = options.createWake ? options.createWake(options.target) : createWakeSubscriber({ target: options.target, now });
|
|
38303
|
+
}
|
|
38304
|
+
return wakeSubscriber;
|
|
38305
|
+
};
|
|
38306
|
+
const applyWakeHint = (hint) => {
|
|
38307
|
+
if (hint === void 0) return;
|
|
38308
|
+
try {
|
|
38309
|
+
ensureWake().setTopic(hint.topic);
|
|
38310
|
+
} catch {
|
|
38311
|
+
}
|
|
38312
|
+
};
|
|
38313
|
+
const emitWake = () => {
|
|
38314
|
+
if (wakeSubscriber === null) return;
|
|
38315
|
+
options.onEvent?.({
|
|
38316
|
+
type: "wake",
|
|
38317
|
+
wake: wakeSubscriber.snapshot(now()),
|
|
38318
|
+
ts: eventTime(now)
|
|
38319
|
+
});
|
|
38320
|
+
};
|
|
38321
|
+
const waitCapMs = () => {
|
|
38322
|
+
if (wakeSubscriber !== null && wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
|
|
38323
|
+
return LISTENER_RECONCILE_POLL_MS;
|
|
38324
|
+
}
|
|
38325
|
+
return nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
|
|
38326
|
+
};
|
|
37904
38327
|
const sendPreparedAck = async (active) => {
|
|
37905
38328
|
if (active.phase !== "ack_pending" || active.signalId === null || active.leaseId === null || active.leasedUntil === null || active.ack === null) {
|
|
37906
38329
|
return {
|
|
@@ -37967,8 +38390,33 @@ async function runListenerRuntime(options) {
|
|
|
37967
38390
|
stop = { reason: "cancelled" };
|
|
37968
38391
|
break;
|
|
37969
38392
|
}
|
|
37970
|
-
let
|
|
37971
|
-
|
|
38393
|
+
let skipRead = false;
|
|
38394
|
+
if (ready && deliveryMode === "durable_claim" && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
|
|
38395
|
+
const until = Math.min(reconcileDueAt, now() + waitCapMs());
|
|
38396
|
+
const reason = await wakeSubscriber.next({
|
|
38397
|
+
until,
|
|
38398
|
+
...abort ? { signal: abort } : {}
|
|
38399
|
+
});
|
|
38400
|
+
emitWake();
|
|
38401
|
+
if (abort?.aborted) {
|
|
38402
|
+
stop = { reason: "cancelled" };
|
|
38403
|
+
break;
|
|
38404
|
+
}
|
|
38405
|
+
if (reason === "wake" && wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
|
|
38406
|
+
const coalesceMs = wakeSubscriber.coalescingRemainingMs(now());
|
|
38407
|
+
if (coalesceMs > 0) await sleep2(coalesceMs, abort);
|
|
38408
|
+
if (abort?.aborted) {
|
|
38409
|
+
stop = { reason: "cancelled" };
|
|
38410
|
+
break;
|
|
38411
|
+
}
|
|
38412
|
+
if (wakeSubscriber.snapshot(now()).mode === LISTENER_WAKE_MODE_PUSH) {
|
|
38413
|
+
skipRead = true;
|
|
38414
|
+
}
|
|
38415
|
+
}
|
|
38416
|
+
}
|
|
38417
|
+
let page = null;
|
|
38418
|
+
if (skipRead) {
|
|
38419
|
+
} else try {
|
|
37972
38420
|
const token = await options.credentialSession.bearer();
|
|
37973
38421
|
page = await readPage({
|
|
37974
38422
|
token,
|
|
@@ -37986,6 +38434,8 @@ async function runListenerRuntime(options) {
|
|
|
37986
38434
|
}
|
|
37987
38435
|
});
|
|
37988
38436
|
requireCapabilities(page);
|
|
38437
|
+
applyWakeHint(page.wake);
|
|
38438
|
+
emitWake();
|
|
37989
38439
|
if (ready && readEpisodeStartedAtMs !== null) {
|
|
37990
38440
|
const recoveredAtMs = now();
|
|
37991
38441
|
options.onEvent?.({
|
|
@@ -38113,7 +38563,7 @@ async function runListenerRuntime(options) {
|
|
|
38113
38563
|
break;
|
|
38114
38564
|
}
|
|
38115
38565
|
}
|
|
38116
|
-
if (page
|
|
38566
|
+
if ((page?.capabilities.deliveryAck === true || skipRead) && now() < horizon && !preparedNeedsMainRoute) {
|
|
38117
38567
|
const ackStop = await sendPreparedAck(recovery);
|
|
38118
38568
|
if (ackStop !== null) {
|
|
38119
38569
|
stop = ackStop;
|
|
@@ -38143,7 +38593,7 @@ async function runListenerRuntime(options) {
|
|
|
38143
38593
|
continue;
|
|
38144
38594
|
}
|
|
38145
38595
|
if (recovery?.phase === "leased") {
|
|
38146
|
-
if (page
|
|
38596
|
+
if (page?.capabilities.deliveryAck === true || skipRead) {
|
|
38147
38597
|
let terminal = null;
|
|
38148
38598
|
if (recovery.signalId !== null) {
|
|
38149
38599
|
try {
|
|
@@ -38253,6 +38703,10 @@ async function runListenerRuntime(options) {
|
|
|
38253
38703
|
stop = { reason: "credential", error: asError2(error) };
|
|
38254
38704
|
break;
|
|
38255
38705
|
}
|
|
38706
|
+
if (error instanceof DeliveryHttpError && error.code === "rate_limited") {
|
|
38707
|
+
wakeSubscriber?.markRateLimited(now());
|
|
38708
|
+
emitWake();
|
|
38709
|
+
}
|
|
38256
38710
|
if (!isRetryableDeliveryError(error)) {
|
|
38257
38711
|
stop = { reason: "fatal", error: asError2(error) };
|
|
38258
38712
|
break;
|
|
@@ -38271,6 +38725,14 @@ async function runListenerRuntime(options) {
|
|
|
38271
38725
|
stop = { reason: "fatal", error: new Error("delivery claim did not settle") };
|
|
38272
38726
|
break;
|
|
38273
38727
|
}
|
|
38728
|
+
applyWakeHint(result.wake);
|
|
38729
|
+
if (!skipRead && wakeSubscriber !== null && wakeSubscriber.hasTopic) {
|
|
38730
|
+
wakeSubscriber.noteReconcile(now());
|
|
38731
|
+
reconcileDueAt = now() + LISTENER_RECONCILE_POLL_MS;
|
|
38732
|
+
}
|
|
38733
|
+
if (skipRead) wakeSubscriber?.noteWakeClaim(now());
|
|
38734
|
+
else wakeSubscriber?.noteClaim(now());
|
|
38735
|
+
emitWake();
|
|
38274
38736
|
const claimed = result.deliveries[0] ?? null;
|
|
38275
38737
|
options.onEvent?.({
|
|
38276
38738
|
type: "delivery_claim",
|
|
@@ -38301,6 +38763,19 @@ async function runListenerRuntime(options) {
|
|
|
38301
38763
|
stop = { reason: "fatal", error: asError2(error) };
|
|
38302
38764
|
break;
|
|
38303
38765
|
}
|
|
38766
|
+
if (wakeSubscriber !== null && wakeSubscriber.hasTopic) {
|
|
38767
|
+
const snap = wakeSubscriber.snapshot(now());
|
|
38768
|
+
const intervalMs = snap.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : nextIdlePollMs(pollMs, emptyIdleStreak, LISTENER_IDLE_POLL_MAX_MS);
|
|
38769
|
+
if (snap.mode === LISTENER_WAKE_MODE_PUSH) emptyIdleStreak = 0;
|
|
38770
|
+
else emptyIdleStreak += 1;
|
|
38771
|
+
options.onEvent?.({
|
|
38772
|
+
type: "idle_poll",
|
|
38773
|
+
intervalMs,
|
|
38774
|
+
ts: eventTime(now)
|
|
38775
|
+
});
|
|
38776
|
+
emitWake();
|
|
38777
|
+
continue;
|
|
38778
|
+
}
|
|
38304
38779
|
await idleSleep(false);
|
|
38305
38780
|
continue;
|
|
38306
38781
|
}
|
|
@@ -38495,6 +38970,7 @@ async function runListenerRuntime(options) {
|
|
|
38495
38970
|
}
|
|
38496
38971
|
continue;
|
|
38497
38972
|
}
|
|
38973
|
+
if (page === null) continue;
|
|
38498
38974
|
for (const signal of page.signals) {
|
|
38499
38975
|
if (abort?.aborted) {
|
|
38500
38976
|
stop = { reason: "cancelled" };
|
|
@@ -38587,6 +39063,10 @@ async function runListenerRuntime(options) {
|
|
|
38587
39063
|
} finally {
|
|
38588
39064
|
abort?.removeEventListener("abort", onAbort);
|
|
38589
39065
|
options.model.cancel();
|
|
39066
|
+
try {
|
|
39067
|
+
await wakeSubscriber?.close();
|
|
39068
|
+
} catch {
|
|
39069
|
+
}
|
|
38590
39070
|
try {
|
|
38591
39071
|
await options.model.close();
|
|
38592
39072
|
} catch (error) {
|
|
@@ -38604,6 +39084,7 @@ var LISTENER_READ_RETRY_HOUR_CAP = 25;
|
|
|
38604
39084
|
var LISTENER_READ_RETRY_MINUTE_CAP = 61;
|
|
38605
39085
|
var LISTENER_CLAIM_HOUR_CAP = 25;
|
|
38606
39086
|
var LISTENER_THROUGHPUT_LAPSE_RATIO = 0.5;
|
|
39087
|
+
var LISTENER_MODE_CHANGE_SKIP_MAX = 1;
|
|
38607
39088
|
var FAILURE_CODES = /* @__PURE__ */ new Set([
|
|
38608
39089
|
"http_status",
|
|
38609
39090
|
"no_response",
|
|
@@ -38700,9 +39181,20 @@ function recordListenerReadRecovery(health, input) {
|
|
|
38700
39181
|
retryHours: trimNewest(retryHours, LISTENER_READ_RETRY_HOUR_CAP)
|
|
38701
39182
|
};
|
|
38702
39183
|
}
|
|
39184
|
+
function freezeClosedHourExpectedClaims(rows3, currentHourStart) {
|
|
39185
|
+
return rows3.map((row) => {
|
|
39186
|
+
if (row.hourStart === currentHourStart) return row;
|
|
39187
|
+
if (row.expectedClaims !== void 0) return row;
|
|
39188
|
+
if (row.cadenceMs === void 0 || row.cadenceMs < 1) return row;
|
|
39189
|
+
return { ...row, expectedClaims: HOUR_MS / row.cadenceMs };
|
|
39190
|
+
});
|
|
39191
|
+
}
|
|
38703
39192
|
function recordListenerClaimCadence(health, cadenceMs, ts) {
|
|
38704
39193
|
const hourStart = bucketStart(ts, HOUR_MS);
|
|
38705
|
-
const claimHours =
|
|
39194
|
+
const claimHours = freezeClosedHourExpectedClaims(
|
|
39195
|
+
health.claimHours.map((row) => ({ ...row })),
|
|
39196
|
+
hourStart
|
|
39197
|
+
);
|
|
38706
39198
|
const hour = claimHours.find((row) => row.hourStart === hourStart);
|
|
38707
39199
|
if (hour) {
|
|
38708
39200
|
hour.cadenceMs = hour.cadenceMs === void 0 ? cadenceMs : Math.max(hour.cadenceMs, cadenceMs);
|
|
@@ -38715,9 +39207,26 @@ function recordListenerClaimCadence(health, cadenceMs, ts) {
|
|
|
38715
39207
|
claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
|
|
38716
39208
|
};
|
|
38717
39209
|
}
|
|
39210
|
+
function recordListenerWakeModeChange(health, ts) {
|
|
39211
|
+
const hourStart = bucketStart(ts, HOUR_MS);
|
|
39212
|
+
const claimHours = freezeClosedHourExpectedClaims(
|
|
39213
|
+
health.claimHours.map((row) => ({ ...row })),
|
|
39214
|
+
hourStart
|
|
39215
|
+
);
|
|
39216
|
+
const hour = claimHours.find((row) => row.hourStart === hourStart);
|
|
39217
|
+
if (hour) hour.modeChanged = true;
|
|
39218
|
+
else claimHours.push({ hourStart, claims: 0, modeChanged: true });
|
|
39219
|
+
return {
|
|
39220
|
+
...health,
|
|
39221
|
+
claimHours: trimNewest(claimHours, LISTENER_CLAIM_HOUR_CAP)
|
|
39222
|
+
};
|
|
39223
|
+
}
|
|
38718
39224
|
function recordListenerClaim(health, ts) {
|
|
38719
39225
|
const hourStart = bucketStart(ts, HOUR_MS);
|
|
38720
|
-
const claimHours =
|
|
39226
|
+
const claimHours = freezeClosedHourExpectedClaims(
|
|
39227
|
+
health.claimHours.map((row) => ({ ...row })),
|
|
39228
|
+
hourStart
|
|
39229
|
+
);
|
|
38721
39230
|
const hour = claimHours.find((row) => row.hourStart === hourStart);
|
|
38722
39231
|
if (hour) hour.claims += 1;
|
|
38723
39232
|
else claimHours.push({ hourStart, claims: 1 });
|
|
@@ -38774,9 +39283,13 @@ function parseListenerReadHealth(value, rejectUnknownKeys = false) {
|
|
|
38774
39283
|
const hour = value2;
|
|
38775
39284
|
if (!hasExpectedKeys(hour, ["hourStart", "claims"], false) || !validTimestamp(hour.hourStart) || !validCount(hour.claims)) return null;
|
|
38776
39285
|
if (rejectUnknownKeys && Object.keys(hour).some(
|
|
38777
|
-
(key2) => key2 !== "hourStart" && key2 !== "claims" && key2 !== "cadenceMs"
|
|
39286
|
+
(key2) => key2 !== "hourStart" && key2 !== "claims" && key2 !== "cadenceMs" && key2 !== "expectedClaims" && key2 !== "modeChanged"
|
|
38778
39287
|
)) return null;
|
|
38779
39288
|
if (hour.cadenceMs !== void 0 && !(typeof hour.cadenceMs === "number" && Number.isSafeInteger(hour.cadenceMs) && hour.cadenceMs >= 1)) return null;
|
|
39289
|
+
if (hour.expectedClaims !== void 0 && !(typeof hour.expectedClaims === "number" && Number.isFinite(hour.expectedClaims) && hour.expectedClaims > 0)) return null;
|
|
39290
|
+
if (hour.modeChanged !== void 0 && typeof hour.modeChanged !== "boolean") {
|
|
39291
|
+
return null;
|
|
39292
|
+
}
|
|
38780
39293
|
}
|
|
38781
39294
|
return {
|
|
38782
39295
|
currentEpisodeStartedAt: row.currentEpisodeStartedAt,
|
|
@@ -38798,10 +39311,14 @@ function parseListenerReadHealth(value, rejectUnknownKeys = false) {
|
|
|
38798
39311
|
claimCadenceMs: row.claimCadenceMs,
|
|
38799
39312
|
claimHours: row.claimHours.map((hour) => {
|
|
38800
39313
|
const cadenceMs = hour.cadenceMs;
|
|
39314
|
+
const expectedClaims = hour.expectedClaims;
|
|
39315
|
+
const modeChanged = hour.modeChanged;
|
|
38801
39316
|
return {
|
|
38802
39317
|
hourStart: hour.hourStart,
|
|
38803
39318
|
claims: hour.claims,
|
|
38804
|
-
...typeof cadenceMs === "number" ? { cadenceMs } : {}
|
|
39319
|
+
...typeof cadenceMs === "number" ? { cadenceMs } : {},
|
|
39320
|
+
...typeof expectedClaims === "number" ? { expectedClaims } : {},
|
|
39321
|
+
...modeChanged === true ? { modeChanged: true } : {}
|
|
38805
39322
|
};
|
|
38806
39323
|
})
|
|
38807
39324
|
};
|
|
@@ -38838,14 +39355,18 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
|
|
|
38838
39355
|
health.claimHours.map((row) => [row.hourStart, row.claims])
|
|
38839
39356
|
);
|
|
38840
39357
|
const cadenceByHour = /* @__PURE__ */ new Map();
|
|
39358
|
+
const expectedByHour = /* @__PURE__ */ new Map();
|
|
38841
39359
|
for (const row of health.claimHours) {
|
|
38842
39360
|
if (row.cadenceMs !== void 0) cadenceByHour.set(row.hourStart, row.cadenceMs);
|
|
39361
|
+
if (row.expectedClaims !== void 0) {
|
|
39362
|
+
expectedByHour.set(row.hourStart, row.expectedClaims);
|
|
39363
|
+
}
|
|
38843
39364
|
}
|
|
38844
39365
|
for (let hour = first; hour < currentHour; hour += HOUR_MS) {
|
|
38845
39366
|
const hourStart = new Date(hour).toISOString();
|
|
38846
39367
|
const claims = claimsByHour.get(hourStart) ?? 0;
|
|
38847
39368
|
const cadenceMs = cadenceByHour.get(hourStart) ?? health.claimCadenceMs;
|
|
38848
|
-
const expectedClaims = HOUR_MS / cadenceMs;
|
|
39369
|
+
const expectedClaims = expectedByHour.get(hourStart) ?? HOUR_MS / cadenceMs;
|
|
38849
39370
|
claimThroughputHours.push({
|
|
38850
39371
|
hourStart,
|
|
38851
39372
|
claims,
|
|
@@ -38854,9 +39375,25 @@ function summarizeListenerReadHealth(health, readyAt, nowMs) {
|
|
|
38854
39375
|
});
|
|
38855
39376
|
}
|
|
38856
39377
|
}
|
|
38857
|
-
const
|
|
38858
|
-
|
|
38859
|
-
|
|
39378
|
+
const consecutiveModeChangedHours = (hourStart) => {
|
|
39379
|
+
let count2 = 0;
|
|
39380
|
+
let t = Date.parse(hourStart);
|
|
39381
|
+
if (!Number.isFinite(t)) return 0;
|
|
39382
|
+
while (true) {
|
|
39383
|
+
const start = new Date(t).toISOString();
|
|
39384
|
+
const row = health.claimHours.find((hour) => hour.hourStart === start);
|
|
39385
|
+
if (row?.modeChanged !== true) break;
|
|
39386
|
+
count2 += 1;
|
|
39387
|
+
t -= HOUR_MS;
|
|
39388
|
+
}
|
|
39389
|
+
return count2;
|
|
39390
|
+
};
|
|
39391
|
+
const throughputLapseHours = claimThroughputHours.filter((hour) => {
|
|
39392
|
+
if (hour.ratio >= LISTENER_THROUGHPUT_LAPSE_RATIO) return false;
|
|
39393
|
+
const consecutive = consecutiveModeChangedHours(hour.hourStart);
|
|
39394
|
+
if (consecutive === 0) return true;
|
|
39395
|
+
return consecutive > LISTENER_MODE_CHANGE_SKIP_MAX;
|
|
39396
|
+
});
|
|
38860
39397
|
return {
|
|
38861
39398
|
currentEpisodeDurationMs,
|
|
38862
39399
|
episodesLast24h,
|
|
@@ -38960,7 +39497,8 @@ var STATUS_ALLOWED_KEYS = /* @__PURE__ */ new Set([
|
|
|
38960
39497
|
"connectionReuseRatio",
|
|
38961
39498
|
"activityPublishFailures",
|
|
38962
39499
|
"activityLastErrorCode",
|
|
38963
|
-
"idlePollMs"
|
|
39500
|
+
"idlePollMs",
|
|
39501
|
+
"wake"
|
|
38964
39502
|
]);
|
|
38965
39503
|
var STATUS_ACTIVITY_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
38966
39504
|
"activity_credential_failed",
|
|
@@ -38983,7 +39521,10 @@ var STATUS_SENSITIVE_KEYS = /* @__PURE__ */ new Set([
|
|
|
38983
39521
|
"reply",
|
|
38984
39522
|
"owner",
|
|
38985
39523
|
"ownerId",
|
|
38986
|
-
"owner_id"
|
|
39524
|
+
"owner_id",
|
|
39525
|
+
"topic",
|
|
39526
|
+
"wakeTopic",
|
|
39527
|
+
"wake_topic"
|
|
38987
39528
|
]);
|
|
38988
39529
|
var STATUS_DELIVERY_KEYS = [
|
|
38989
39530
|
"deliveryMode",
|
|
@@ -39020,6 +39561,37 @@ function parseHeldBackDeliveries(value) {
|
|
|
39020
39561
|
}
|
|
39021
39562
|
return parsed;
|
|
39022
39563
|
}
|
|
39564
|
+
var WAKE_STATUS_KEY_SET = new Set(LISTENER_WAKE_STATUS_KEYS);
|
|
39565
|
+
var WAKE_SENSITIVE_KEY_SET = new Set(LISTENER_WAKE_SENSITIVE_KEYS);
|
|
39566
|
+
function nullableIso(value) {
|
|
39567
|
+
return value === null || typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
39568
|
+
}
|
|
39569
|
+
function parseListenerWake(value, rejectUnknownKeys = false) {
|
|
39570
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
39571
|
+
const row = value;
|
|
39572
|
+
for (const key2 of Object.keys(row)) {
|
|
39573
|
+
if (WAKE_SENSITIVE_KEY_SET.has(key2)) return null;
|
|
39574
|
+
if (rejectUnknownKeys && !WAKE_STATUS_KEY_SET.has(key2)) return null;
|
|
39575
|
+
}
|
|
39576
|
+
for (const key2 of LISTENER_WAKE_STATUS_KEYS) {
|
|
39577
|
+
if (!(key2 in row)) return null;
|
|
39578
|
+
}
|
|
39579
|
+
const mode3 = LISTENER_WAKE_MODES.find((item) => item === row.mode);
|
|
39580
|
+
if (mode3 === void 0 || !nullableIso(row.subscribedAt) || !(typeof row.reconnects === "number" && Number.isSafeInteger(row.reconnects) && row.reconnects >= 0) || !nullableIso(row.lastWakeAt) || !nullableIso(row.lastReconcileAt) || !(row.errorCode === null || typeof row.errorCode === "string" && WAKE_ERROR_CODE_SET.has(row.errorCode)) || !nullableIso(row.topicRotatedAt) || typeof row.rateLimited !== "boolean") {
|
|
39581
|
+
return null;
|
|
39582
|
+
}
|
|
39583
|
+
if (mode3 === LISTENER_WAKE_MODE_PUSH && row.subscribedAt === null) return null;
|
|
39584
|
+
return {
|
|
39585
|
+
mode: mode3,
|
|
39586
|
+
subscribedAt: row.subscribedAt,
|
|
39587
|
+
reconnects: row.reconnects,
|
|
39588
|
+
lastWakeAt: row.lastWakeAt,
|
|
39589
|
+
lastReconcileAt: row.lastReconcileAt,
|
|
39590
|
+
errorCode: row.errorCode,
|
|
39591
|
+
topicRotatedAt: row.topicRotatedAt,
|
|
39592
|
+
rateLimited: row.rateLimited
|
|
39593
|
+
};
|
|
39594
|
+
}
|
|
39023
39595
|
function parseStatus(raw, rejectUnknownKeys = false) {
|
|
39024
39596
|
let value;
|
|
39025
39597
|
try {
|
|
@@ -39044,7 +39616,8 @@ function parseStatus(raw, rejectUnknownKeys = false) {
|
|
|
39044
39616
|
const nullableTimestamp3 = (candidate) => candidate === null || typeof candidate === "string" && Number.isFinite(Date.parse(candidate));
|
|
39045
39617
|
const readHealth = row.readHealth === void 0 ? void 0 : parseListenerReadHealth(row.readHealth, rejectUnknownKeys);
|
|
39046
39618
|
const heldBackDeliveries = row.heldBackDeliveries === void 0 ? void 0 : parseHeldBackDeliveries(row.heldBackDeliveries);
|
|
39047
|
-
|
|
39619
|
+
const wake = row.wake === void 0 ? void 0 : parseListenerWake(row.wake, rejectUnknownKeys);
|
|
39620
|
+
if (row.version !== 1 || typeof row.instanceId !== "string" || !UUID_RE18.test(row.instanceId) || row.provider !== "grok" && row.provider !== "opencode" && row.provider !== "claude" && row.provider !== "codex" || typeof row.profileId !== "string" || typeof row.workspaceId !== "string" || !UUID_RE18.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE18.test(row.principalId) || !Number.isSafeInteger(row.pid) || row.pid < 1 || typeof row.state !== "string" || !["starting", "ready", "stopping", "stopped", "failed"].includes(row.state) || typeof row.startedAt !== "string" || !Number.isFinite(Date.parse(row.startedAt)) || !(row.readyAt === null || typeof row.readyAt === "string" && Number.isFinite(Date.parse(row.readyAt))) || typeof row.updatedAt !== "string" || !Number.isFinite(Date.parse(row.updatedAt)) || !(row.stoppedAt === null || typeof row.stoppedAt === "string" && Number.isFinite(Date.parse(row.stoppedAt))) || !nullableUuid3(row.lastSignalId) || !(row.lastErrorCode === null || typeof row.lastErrorCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorCode)) || !(row.lastErrorDetail === void 0 || row.lastErrorDetail === null || typeof row.lastErrorDetail === "string" && row.lastErrorDetail.length > 0 && row.lastErrorDetail.length <= 2048 && !SECRET_SHAPE_RE.test(row.lastErrorDetail)) || !(row.lastErrorReasonCode === void 0 || row.lastErrorReasonCode === null || typeof row.lastErrorReasonCode === "string" && /^[a-z0-9_-]{1,96}$/.test(row.lastErrorReasonCode)) || !(row.providerExecutable === void 0 || row.providerExecutable === null || typeof row.providerExecutable === "string" && (0, import_node_path16.isAbsolute)(row.providerExecutable)) || !(row.providerVersion === void 0 || row.providerVersion === null || typeof row.providerVersion === "string" && SEMVER_RE2.test(row.providerVersion)) || !(row.providerLastMeasuredVersion === void 0 || row.providerLastMeasuredVersion === null || typeof row.providerLastMeasuredVersion === "string" && SEMVER_RE2.test(row.providerLastMeasuredVersion)) || !(row.providerBundledAgentSdkVersion === void 0 || row.providerBundledAgentSdkVersion === null || typeof row.providerBundledAgentSdkVersion === "string" && SEMVER_RE2.test(row.providerBundledAgentSdkVersion)) || !(row.providerBundledClaudeCodeVersion === void 0 || row.providerBundledClaudeCodeVersion === null || typeof row.providerBundledClaudeCodeVersion === "string" && SEMVER_RE2.test(row.providerBundledClaudeCodeVersion)) || !(row.providerMinimumRequiredVersion === void 0 || row.providerMinimumRequiredVersion === null || typeof row.providerMinimumRequiredVersion === "string" && SEMVER_RE2.test(row.providerMinimumRequiredVersion)) || !(row.cswarmVersion === void 0 || row.cswarmVersion === null || typeof row.cswarmVersion === "string" && SEMVER_RE2.test(row.cswarmVersion)) || (row.providerVersion === null || row.providerVersion === void 0) !== (row.providerLastMeasuredVersion === null || row.providerLastMeasuredVersion === void 0) || !(row.lastWorkerStderrTail === void 0 || row.lastWorkerStderrTail === null || typeof row.lastWorkerStderrTail === "string" && row.lastWorkerStderrTail.length > 0 && row.lastWorkerStderrTail.length <= 2048 && !SECRET_SHAPE_RE.test(row.lastWorkerStderrTail)) || typeof row.logPath !== "string" || !(0, import_node_path16.isAbsolute)(row.logPath) || !(row.deliveryMode === void 0 || row.deliveryMode === null || typeof row.deliveryMode === "string" && STATUS_DELIVERY_MODES.has(row.deliveryMode)) || !(row.pendingDeliveryCount === void 0 || nullableCount(row.pendingDeliveryCount)) || !(row.lastTerminalDeliveryFailureCount === void 0 || nullableCount(row.lastTerminalDeliveryFailureCount)) || !(row.lastTerminalDeliveryFailureAt === void 0 || nullableTimestamp3(row.lastTerminalDeliveryFailureAt)) || !(row.lastClaimAt === void 0 || nullableTimestamp3(row.lastClaimAt)) || !(row.lastAckAt === void 0 || nullableTimestamp3(row.lastAckAt)) || !(row.lastAckOutcome === void 0 || row.lastAckOutcome === null || typeof row.lastAckOutcome === "string" && deliveryOutcomes.has(row.lastAckOutcome)) || !(row.consecutiveAckFailureCount === void 0 || nullableCount(row.consecutiveAckFailureCount)) || !(row.lastAckSignalId === void 0 || row.lastAckSignalId === null || typeof row.lastAckSignalId === "string" && UUID_RE18.test(row.lastAckSignalId)) || !(row.currentDeliverySignalId === void 0 || row.currentDeliverySignalId === null || typeof row.currentDeliverySignalId === "string" && UUID_RE18.test(row.currentDeliverySignalId)) || !(row.currentDeliverySince === void 0 || nullableTimestamp3(row.currentDeliverySince)) || heldBackDeliveries === null || !(row.pendingDeliveryCountAt === void 0 || nullableTimestamp3(row.pendingDeliveryCountAt)) || !(row.routeMode === void 0 || row.routeMode === "worker" || row.routeMode === "main" || row.routeMode === "split") || !(row.deferOverChars === void 0 || row.deferOverChars === null || typeof row.deferOverChars === "number" && Number.isSafeInteger(row.deferOverChars) && row.deferOverChars >= 1 && row.deferOverChars <= 1e4) || !(row.pendingForMainCount === void 0 || typeof row.pendingForMainCount === "number" && Number.isSafeInteger(row.pendingForMainCount) && row.pendingForMainCount >= 0) || !(row.droppedForMainCount === void 0 || typeof row.droppedForMainCount === "number" && Number.isSafeInteger(row.droppedForMainCount) && row.droppedForMainCount >= 0) || readHealth === null || wake === null || !(row.connectionsOpened === void 0 || typeof row.connectionsOpened === "number" && Number.isSafeInteger(row.connectionsOpened) && row.connectionsOpened >= 0) || !(row.connectionReuseRatio === void 0 || typeof row.connectionReuseRatio === "number" && Number.isFinite(row.connectionReuseRatio) && row.connectionReuseRatio >= 0) || !(row.activityPublishFailures === void 0 || typeof row.activityPublishFailures === "number" && Number.isSafeInteger(row.activityPublishFailures) && row.activityPublishFailures >= 0) || !(row.activityLastErrorCode === void 0 || row.activityLastErrorCode === null || typeof row.activityLastErrorCode === "string" && STATUS_ACTIVITY_ERROR_CODES.has(
|
|
39048
39621
|
row.activityLastErrorCode
|
|
39049
39622
|
)) || !(row.idlePollMs === void 0 || row.idlePollMs === null || typeof row.idlePollMs === "number" && Number.isSafeInteger(row.idlePollMs) && row.idlePollMs >= 0)) {
|
|
39050
39623
|
throw new Error("stored listener status is malformed");
|
|
@@ -39090,7 +39663,8 @@ function parseStatus(raw, rejectUnknownKeys = false) {
|
|
|
39090
39663
|
pendingForMainCount: row.pendingForMainCount ?? 0,
|
|
39091
39664
|
droppedForMainCount: row.droppedForMainCount ?? 0,
|
|
39092
39665
|
...readHealth === void 0 ? {} : { readHealth },
|
|
39093
|
-
...row.idlePollMs === void 0 ? {} : { idlePollMs: row.idlePollMs ?? null }
|
|
39666
|
+
...row.idlePollMs === void 0 ? {} : { idlePollMs: row.idlePollMs ?? null },
|
|
39667
|
+
...wake === void 0 ? {} : { wake }
|
|
39094
39668
|
};
|
|
39095
39669
|
}
|
|
39096
39670
|
async function writeListenerStatus(paths, status) {
|
|
@@ -39161,7 +39735,11 @@ async function appendListenerEvent(paths, event) {
|
|
|
39161
39735
|
// How long one delivery held the worker seat, and why it gave it back.
|
|
39162
39736
|
"held_ms",
|
|
39163
39737
|
"release_reason",
|
|
39164
|
-
"idle_poll_ms"
|
|
39738
|
+
"idle_poll_ms",
|
|
39739
|
+
"wake_mode",
|
|
39740
|
+
"wake_error_code",
|
|
39741
|
+
"wake_reconnects",
|
|
39742
|
+
"rate_limited"
|
|
39165
39743
|
]);
|
|
39166
39744
|
const deliveryModes = /* @__PURE__ */ new Set(["durable_claim", "cursor_fallback"]);
|
|
39167
39745
|
const routeModes = /* @__PURE__ */ new Set(["worker", "main", "split"]);
|
|
@@ -39229,6 +39807,18 @@ async function appendListenerEvent(paths, event) {
|
|
|
39229
39807
|
if (key2 === "idle_poll_ms" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
39230
39808
|
throw new Error("listener event idle poll interval is not allowed");
|
|
39231
39809
|
}
|
|
39810
|
+
if (key2 === "wake_mode" && !(typeof value === "string" && LISTENER_WAKE_MODE_SET.has(value))) {
|
|
39811
|
+
throw new Error("listener event wake mode is not allowed");
|
|
39812
|
+
}
|
|
39813
|
+
if (key2 === "wake_error_code" && !(value === null || typeof value === "string" && WAKE_ERROR_CODE_SET.has(value))) {
|
|
39814
|
+
throw new Error("listener event wake error code is not allowed");
|
|
39815
|
+
}
|
|
39816
|
+
if (key2 === "wake_reconnects" && !(typeof value === "number" && Number.isSafeInteger(value) && value >= 0)) {
|
|
39817
|
+
throw new Error("listener event wake reconnect count is not allowed");
|
|
39818
|
+
}
|
|
39819
|
+
if (key2 === "rate_limited" && typeof value !== "boolean") {
|
|
39820
|
+
throw new Error("listener event rate-limited flag is not allowed");
|
|
39821
|
+
}
|
|
39232
39822
|
if (key2 === "release_reason" && !(typeof value === "string" && LISTENER_DELIVERY_HOLD_RELEASE_REASONS.includes(
|
|
39233
39823
|
value
|
|
39234
39824
|
))) {
|
|
@@ -39640,6 +40230,7 @@ async function runListenerSupervisor(options) {
|
|
|
39640
40230
|
activityPublishFailures: 0,
|
|
39641
40231
|
activityLastErrorCode: null,
|
|
39642
40232
|
idlePollMs: null,
|
|
40233
|
+
wake: emptyListenerWakeStatus(),
|
|
39643
40234
|
logPath: options.paths.logPath
|
|
39644
40235
|
};
|
|
39645
40236
|
let writes = Promise.resolve();
|
|
@@ -39712,7 +40303,50 @@ async function runListenerSupervisor(options) {
|
|
|
39712
40303
|
const fitted = fitWorkerStderrTailForLog(tail);
|
|
39713
40304
|
return fitted.length > 0 ? fitted : null;
|
|
39714
40305
|
};
|
|
40306
|
+
let lastWakePersistMs = 0;
|
|
39715
40307
|
const onEvent = (event) => {
|
|
40308
|
+
if (event.type === "wake") {
|
|
40309
|
+
const previousWake = status.wake;
|
|
40310
|
+
const previous = previousWake?.mode;
|
|
40311
|
+
let readHealth = status.readHealth ?? emptyListenerReadHealth();
|
|
40312
|
+
if (previous !== void 0 && previous !== event.wake.mode) {
|
|
40313
|
+
readHealth = recordListenerWakeModeChange(readHealth, event.ts);
|
|
40314
|
+
}
|
|
40315
|
+
const cadenceMs = event.wake.mode === LISTENER_WAKE_MODE_PUSH ? LISTENER_RECONCILE_POLL_MS : status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : null;
|
|
40316
|
+
if (cadenceMs !== null) {
|
|
40317
|
+
readHealth = recordListenerClaimCadence(
|
|
40318
|
+
readHealth,
|
|
40319
|
+
cadenceMs,
|
|
40320
|
+
event.ts
|
|
40321
|
+
);
|
|
40322
|
+
}
|
|
40323
|
+
status = {
|
|
40324
|
+
...status,
|
|
40325
|
+
wake: event.wake,
|
|
40326
|
+
readHealth,
|
|
40327
|
+
updatedAt: event.ts
|
|
40328
|
+
};
|
|
40329
|
+
const eventMs = Date.parse(event.ts);
|
|
40330
|
+
const nowMs = Number.isFinite(eventMs) ? eventMs : Date.now();
|
|
40331
|
+
if (listenerWakePersistWorthy(
|
|
40332
|
+
previousWake,
|
|
40333
|
+
event.wake,
|
|
40334
|
+
lastWakePersistMs,
|
|
40335
|
+
nowMs
|
|
40336
|
+
)) {
|
|
40337
|
+
lastWakePersistMs = nowMs;
|
|
40338
|
+
persist();
|
|
40339
|
+
log({
|
|
40340
|
+
ts: event.ts,
|
|
40341
|
+
event: "listener_wake",
|
|
40342
|
+
wake_mode: event.wake.mode,
|
|
40343
|
+
wake_error_code: event.wake.errorCode,
|
|
40344
|
+
wake_reconnects: event.wake.reconnects,
|
|
40345
|
+
rate_limited: event.wake.rateLimited
|
|
40346
|
+
});
|
|
40347
|
+
}
|
|
40348
|
+
return;
|
|
40349
|
+
}
|
|
39716
40350
|
if (event.type === "idle_poll") {
|
|
39717
40351
|
status = {
|
|
39718
40352
|
...status,
|
|
@@ -43028,8 +43662,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
43028
43662
|
]);
|
|
43029
43663
|
var UUID_RE23 = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
43030
43664
|
function packageVersion() {
|
|
43031
|
-
if ("0.1.
|
|
43032
|
-
return "0.1.
|
|
43665
|
+
if ("0.1.58".length > 0) {
|
|
43666
|
+
return "0.1.58";
|
|
43033
43667
|
}
|
|
43034
43668
|
try {
|
|
43035
43669
|
const value = JSON.parse(
|
|
@@ -46368,6 +47002,8 @@ function listenerStatusJson(status, permissionMode, evidence = {
|
|
|
46368
47002
|
claimCadenceMs: readHealth.claimCadenceMs,
|
|
46369
47003
|
idlePollMs: status.idlePollMs ?? null,
|
|
46370
47004
|
idlePollSentence: status.idlePollMs === void 0 || status.idlePollMs === null ? null : idlePollStatusSentence(status.idlePollMs),
|
|
47005
|
+
wake: status.wake ?? emptyListenerWakeStatus(),
|
|
47006
|
+
mode: (status.wake ?? emptyListenerWakeStatus()).mode,
|
|
46371
47007
|
claimThroughputHours: readSummary.claimThroughputHours,
|
|
46372
47008
|
listenerLapse: lapseNotices.length > 0,
|
|
46373
47009
|
listenerLapseCodes: lapseNotices.map((notice) => notice.code),
|
|
@@ -46439,7 +47075,12 @@ function renderListenerStatus(status, evidence = {
|
|
|
46439
47075
|
readSummary.longestEpisodeAttemptsLast24h === 0 ? "Longest read retry episode in the last 24h: none recorded." : `Longest read retry episode in the last 24h: ${readSummary.longestEpisodeAttemptsLast24h} attempts over ${Math.floor(readSummary.longestEpisodeDurationMsLast24h / 1e3)}s.`,
|
|
46440
47076
|
readSummary.retryHours.length === 0 ? "Read retries by hour in the last 24h: none." : `Read retries by hour in the last 24h: ${readSummary.retryHours.map((hour) => `${hour.hourStart}=${hour.retries}`).join("; ")}.`,
|
|
46441
47077
|
readSummary.claimThroughputHours.length === 0 ? "Claim throughput by full hour: no complete listener hour is available yet." : `Claim throughput by full hour: ${readSummary.claimThroughputHours.map((hour) => `${hour.hourStart} ${hour.claims}/${Math.round(hour.expectedClaims)} (${hour.ratio.toFixed(3)})`).join("; ")}.`,
|
|
46442
|
-
status.idlePollMs === void 0 || status.idlePollMs === null ? "Current idle poll interval has not been reported yet." : idlePollStatusSentence(status.idlePollMs)
|
|
47078
|
+
status.idlePollMs === void 0 || status.idlePollMs === null ? "Current idle poll interval has not been reported yet." : idlePollStatusSentence(status.idlePollMs),
|
|
47079
|
+
listenerWakeStatusSentence(
|
|
47080
|
+
status.wake ?? emptyListenerWakeStatus(),
|
|
47081
|
+
status.idlePollMs && status.idlePollMs > 0 ? status.idlePollMs : IDLE_POLL_DEFAULT_MS,
|
|
47082
|
+
status.wake?.lastWakeAt ? relativeAge(status.wake.lastWakeAt, nowMs) : null
|
|
47083
|
+
)
|
|
46443
47084
|
];
|
|
46444
47085
|
for (const notice of lapseNotices) {
|
|
46445
47086
|
lines.push(`WARNING [${notice.code}]: ${notice.message}`);
|