@clawling/clawchat-plugin-openclaw 2026.6.30-2 → 2026.6.30-3
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/dist/src/runtime.js +84 -4
- package/package.json +1 -1
- package/src/runtime.ts +81 -4
package/dist/src/runtime.js
CHANGED
|
@@ -33,6 +33,34 @@ export { setOpenclawClawlingRuntime, getOpenclawClawlingRuntime };
|
|
|
33
33
|
const activeClients = new Map();
|
|
34
34
|
const CLAWCHAT_PLUGIN_SLASH_COMMANDS = new Set(["clawchat-activate", "clawchat-output"]);
|
|
35
35
|
const ACTIVATION_CREDENTIAL_POLL_INTERVAL_MS = 2_000;
|
|
36
|
+
// Activation-bootstrap retry on the live connection. The bootstrap greeting is
|
|
37
|
+
// otherwise only (re)dispatched on a fresh `connected` edge, so if its send is
|
|
38
|
+
// cancelled mid-turn (e.g. a proactive token-refresh reconnect closed the
|
|
39
|
+
// transport) or a reconnect's claim no-op'd while a stale in-flight dispatch
|
|
40
|
+
// still held the claim, the greeting would stay pending forever on a now-stable
|
|
41
|
+
// connection. These bound an in-connection retry that re-attempts until the
|
|
42
|
+
// greeting lands or the per-connection budget is spent (then it falls back to
|
|
43
|
+
// the next reconnect, as before).
|
|
44
|
+
const ACTIVATION_BOOTSTRAP_RETRY_DELAY_MS = 5_000;
|
|
45
|
+
const ACTIVATION_BOOTSTRAP_MAX_RETRIES = 6;
|
|
46
|
+
// Count of in-flight activation-bootstrap dispatches per account, shared across
|
|
47
|
+
// gateway re-enters so a fresh connection can tell "nothing pending" (don't
|
|
48
|
+
// retry) apart from "a prior dispatch still holds the claim" (retry once it
|
|
49
|
+
// releases).
|
|
50
|
+
const activationBootstrapInFlight = new Map();
|
|
51
|
+
function incrementActivationBootstrapInFlight(accountId) {
|
|
52
|
+
activationBootstrapInFlight.set(accountId, (activationBootstrapInFlight.get(accountId) ?? 0) + 1);
|
|
53
|
+
}
|
|
54
|
+
function decrementActivationBootstrapInFlight(accountId) {
|
|
55
|
+
const next = (activationBootstrapInFlight.get(accountId) ?? 0) - 1;
|
|
56
|
+
if (next <= 0)
|
|
57
|
+
activationBootstrapInFlight.delete(accountId);
|
|
58
|
+
else
|
|
59
|
+
activationBootstrapInFlight.set(accountId, next);
|
|
60
|
+
}
|
|
61
|
+
function activationBootstrapInFlightCount(accountId) {
|
|
62
|
+
return activationBootstrapInFlight.get(accountId) ?? 0;
|
|
63
|
+
}
|
|
36
64
|
// Upper bound (ms) on how long group ingest waits for the FIRST group-settings
|
|
37
65
|
// pull to settle before proceeding anyway. Best-effort governance must never
|
|
38
66
|
// permanently disable group traffic, so a stalled initial GET (endpoint accepts
|
|
@@ -1450,6 +1478,33 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1450
1478
|
// outbound message (the greeting). The bootstrap dispatcher reads it to decide
|
|
1451
1479
|
// whether to mark the bootstrap sent or leave it pending for a later retry.
|
|
1452
1480
|
let bootstrapProducedFinal = false;
|
|
1481
|
+
// Per-connection retry for a pending activation bootstrap (see the module-level
|
|
1482
|
+
// constants). Re-attempts on the live connection while `wsReady`, bounded by
|
|
1483
|
+
// ACTIVATION_BOOTSTRAP_MAX_RETRIES; the budget resets on each fresh connect.
|
|
1484
|
+
let bootstrapRetryTimer;
|
|
1485
|
+
let bootstrapRetryAttempts = 0;
|
|
1486
|
+
const bootstrapRetryDelayMs = Math.max(1, params.activationBootstrapRetryDelayMs ?? ACTIVATION_BOOTSTRAP_RETRY_DELAY_MS);
|
|
1487
|
+
const clearBootstrapRetry = () => {
|
|
1488
|
+
if (bootstrapRetryTimer !== undefined) {
|
|
1489
|
+
clearTimeout(bootstrapRetryTimer);
|
|
1490
|
+
bootstrapRetryTimer = undefined;
|
|
1491
|
+
}
|
|
1492
|
+
};
|
|
1493
|
+
const armBootstrapRetry = () => {
|
|
1494
|
+
if (bootstrapRetryTimer !== undefined)
|
|
1495
|
+
return;
|
|
1496
|
+
if (!wsReady)
|
|
1497
|
+
return;
|
|
1498
|
+
if (bootstrapRetryAttempts >= ACTIVATION_BOOTSTRAP_MAX_RETRIES)
|
|
1499
|
+
return;
|
|
1500
|
+
bootstrapRetryTimer = setTimeout(() => {
|
|
1501
|
+
bootstrapRetryTimer = undefined;
|
|
1502
|
+
if (!wsReady)
|
|
1503
|
+
return;
|
|
1504
|
+
bootstrapRetryAttempts += 1;
|
|
1505
|
+
void dispatchActivationBootstrap();
|
|
1506
|
+
}, bootstrapRetryDelayMs);
|
|
1507
|
+
};
|
|
1453
1508
|
client.on("state", ({ from, to }) => {
|
|
1454
1509
|
log?.info?.(`[${accountId}] clawchat-plugin-openclaw state ${from} -> ${to}`);
|
|
1455
1510
|
wsReady = to === "connected";
|
|
@@ -1522,6 +1577,9 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1522
1577
|
// The queue keeps the failed frame at the head and will retry after the next reconnect.
|
|
1523
1578
|
}
|
|
1524
1579
|
void refreshConversationCacheAfterReady();
|
|
1580
|
+
// Fresh connection → fresh retry budget; cancel any stale timer first.
|
|
1581
|
+
bootstrapRetryAttempts = 0;
|
|
1582
|
+
clearBootstrapRetry();
|
|
1525
1583
|
void dispatchActivationBootstrap();
|
|
1526
1584
|
// Re-pull per-group agent settings on every successful (re)connect so the
|
|
1527
1585
|
// cache stays current after a network interruption or token refresh.
|
|
@@ -1533,6 +1591,8 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
1533
1591
|
}
|
|
1534
1592
|
else if (to === "disconnected") {
|
|
1535
1593
|
reconnectTracker.markClosed();
|
|
1594
|
+
// Stop the bootstrap retry while down; it re-arms on the next ready.
|
|
1595
|
+
clearBootstrapRetry();
|
|
1536
1596
|
// §A.1 — clear the proactive timer on disconnect; it re-arms on the next
|
|
1537
1597
|
// ready, or the gateway re-enter arms a fresh one.
|
|
1538
1598
|
if (!reconnectWithRefreshedToken)
|
|
@@ -2389,6 +2449,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2389
2449
|
if (!store?.claimPendingActivationBootstrap || !store.markActivationBootstrapSent)
|
|
2390
2450
|
return;
|
|
2391
2451
|
let bootstrap;
|
|
2452
|
+
let claimedInFlight = false;
|
|
2392
2453
|
const releaseBootstrap = () => {
|
|
2393
2454
|
if (!bootstrap || !store.releaseActivationBootstrapClaim)
|
|
2394
2455
|
return;
|
|
@@ -2401,23 +2462,34 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2401
2462
|
};
|
|
2402
2463
|
try {
|
|
2403
2464
|
bootstrap = recordConnection("activation bootstrap claim", () => store.claimPendingActivationBootstrap?.({ platform: "openclaw", accountId }));
|
|
2404
|
-
if (!bootstrap)
|
|
2465
|
+
if (!bootstrap) {
|
|
2466
|
+
// Nothing claimable right now. If another dispatch (e.g. a pre-reconnect
|
|
2467
|
+
// one that was about to fail) still holds the claim, keep retrying on
|
|
2468
|
+
// this live connection so the greeting isn't stranded once it releases.
|
|
2469
|
+
if (activationBootstrapInFlightCount(accountId) > 0)
|
|
2470
|
+
armBootstrapRetry();
|
|
2405
2471
|
return;
|
|
2472
|
+
}
|
|
2406
2473
|
const claimedBootstrap = bootstrap;
|
|
2474
|
+
claimedInFlight = true;
|
|
2475
|
+
incrementActivationBootstrapInFlight(accountId);
|
|
2407
2476
|
bootstrapProducedFinal = false;
|
|
2408
2477
|
const result = await handleInboundEnvelope(buildActivationBootstrapEnvelope({ account, conversationId: claimedBootstrap.conversationId }));
|
|
2409
2478
|
if (result !== "submitted") {
|
|
2410
2479
|
releaseBootstrap();
|
|
2480
|
+
armBootstrapRetry();
|
|
2411
2481
|
return;
|
|
2412
2482
|
}
|
|
2413
2483
|
// Only mark the bootstrap done once the agent has actually sent a greeting
|
|
2414
2484
|
// (a final outbound message). If the turn was handed off but produced no
|
|
2415
|
-
// final reply — e.g. the
|
|
2416
|
-
//
|
|
2417
|
-
//
|
|
2485
|
+
// final reply — e.g. the greeting send was cancelled by a reconnect, or the
|
|
2486
|
+
// agent went off to write a file/note instead of replying — leave the
|
|
2487
|
+
// bootstrap pending (release the claim) and retry on the live connection,
|
|
2488
|
+
// rather than silently consuming it forever or waiting for the next reconnect.
|
|
2418
2489
|
if (!bootstrapProducedFinal) {
|
|
2419
2490
|
log?.info?.(`[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but produced no final reply; leaving pending for retry`);
|
|
2420
2491
|
releaseBootstrap();
|
|
2492
|
+
armBootstrapRetry();
|
|
2421
2493
|
return;
|
|
2422
2494
|
}
|
|
2423
2495
|
recordConnection("activation bootstrap sent", () => store.markActivationBootstrapSent?.({
|
|
@@ -2425,11 +2497,18 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2425
2497
|
accountId,
|
|
2426
2498
|
conversationId: claimedBootstrap.conversationId,
|
|
2427
2499
|
}));
|
|
2500
|
+
// Delivered — stop any pending retry.
|
|
2501
|
+
clearBootstrapRetry();
|
|
2428
2502
|
}
|
|
2429
2503
|
catch (err) {
|
|
2430
2504
|
releaseBootstrap();
|
|
2505
|
+
armBootstrapRetry();
|
|
2431
2506
|
log?.error?.(`[${accountId}] clawchat-plugin-openclaw activation bootstrap failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
2432
2507
|
}
|
|
2508
|
+
finally {
|
|
2509
|
+
if (claimedInFlight)
|
|
2510
|
+
decrementActivationBootstrapInFlight(accountId);
|
|
2511
|
+
}
|
|
2433
2512
|
};
|
|
2434
2513
|
client.on("message", (env) => {
|
|
2435
2514
|
void (async () => {
|
|
@@ -2541,6 +2620,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
2541
2620
|
log?.info?.(`[${accountId}] clawchat-plugin-openclaw runtime abort received; closing client`);
|
|
2542
2621
|
activeClients.delete(accountId);
|
|
2543
2622
|
closingForAbort = true;
|
|
2623
|
+
clearBootstrapRetry();
|
|
2544
2624
|
// §A.1 — stop the proactive refresh timer on shutdown.
|
|
2545
2625
|
refreshManager.stop();
|
|
2546
2626
|
groupCoalescer.cancelAll();
|
package/package.json
CHANGED
package/src/runtime.ts
CHANGED
|
@@ -120,6 +120,32 @@ export { setOpenclawClawlingRuntime, getOpenclawClawlingRuntime };
|
|
|
120
120
|
const activeClients = new Map<string, ClawlingChatClient>();
|
|
121
121
|
const CLAWCHAT_PLUGIN_SLASH_COMMANDS = new Set(["clawchat-activate", "clawchat-output"]);
|
|
122
122
|
const ACTIVATION_CREDENTIAL_POLL_INTERVAL_MS = 2_000;
|
|
123
|
+
// Activation-bootstrap retry on the live connection. The bootstrap greeting is
|
|
124
|
+
// otherwise only (re)dispatched on a fresh `connected` edge, so if its send is
|
|
125
|
+
// cancelled mid-turn (e.g. a proactive token-refresh reconnect closed the
|
|
126
|
+
// transport) or a reconnect's claim no-op'd while a stale in-flight dispatch
|
|
127
|
+
// still held the claim, the greeting would stay pending forever on a now-stable
|
|
128
|
+
// connection. These bound an in-connection retry that re-attempts until the
|
|
129
|
+
// greeting lands or the per-connection budget is spent (then it falls back to
|
|
130
|
+
// the next reconnect, as before).
|
|
131
|
+
const ACTIVATION_BOOTSTRAP_RETRY_DELAY_MS = 5_000;
|
|
132
|
+
const ACTIVATION_BOOTSTRAP_MAX_RETRIES = 6;
|
|
133
|
+
// Count of in-flight activation-bootstrap dispatches per account, shared across
|
|
134
|
+
// gateway re-enters so a fresh connection can tell "nothing pending" (don't
|
|
135
|
+
// retry) apart from "a prior dispatch still holds the claim" (retry once it
|
|
136
|
+
// releases).
|
|
137
|
+
const activationBootstrapInFlight = new Map<string, number>();
|
|
138
|
+
function incrementActivationBootstrapInFlight(accountId: string): void {
|
|
139
|
+
activationBootstrapInFlight.set(accountId, (activationBootstrapInFlight.get(accountId) ?? 0) + 1);
|
|
140
|
+
}
|
|
141
|
+
function decrementActivationBootstrapInFlight(accountId: string): void {
|
|
142
|
+
const next = (activationBootstrapInFlight.get(accountId) ?? 0) - 1;
|
|
143
|
+
if (next <= 0) activationBootstrapInFlight.delete(accountId);
|
|
144
|
+
else activationBootstrapInFlight.set(accountId, next);
|
|
145
|
+
}
|
|
146
|
+
function activationBootstrapInFlightCount(accountId: string): number {
|
|
147
|
+
return activationBootstrapInFlight.get(accountId) ?? 0;
|
|
148
|
+
}
|
|
123
149
|
// Upper bound (ms) on how long group ingest waits for the FIRST group-settings
|
|
124
150
|
// pull to settle before proceeding anyway. Best-effort governance must never
|
|
125
151
|
// permanently disable group traffic, so a stalled initial GET (endpoint accepts
|
|
@@ -543,6 +569,8 @@ export interface StartGatewayParams {
|
|
|
543
569
|
backoffTimer?: (cb: () => void, ms: number) => void;
|
|
544
570
|
/** Test hook only — fixed transport-backoff delay (ms) for re-enter. */
|
|
545
571
|
transportBackoffDelayMs?: number;
|
|
572
|
+
/** Test hook only — retry delay (ms) for re-dispatching a pending activation bootstrap. */
|
|
573
|
+
activationBootstrapRetryDelayMs?: number;
|
|
546
574
|
}
|
|
547
575
|
|
|
548
576
|
/**
|
|
@@ -1826,6 +1854,32 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1826
1854
|
// outbound message (the greeting). The bootstrap dispatcher reads it to decide
|
|
1827
1855
|
// whether to mark the bootstrap sent or leave it pending for a later retry.
|
|
1828
1856
|
let bootstrapProducedFinal = false;
|
|
1857
|
+
// Per-connection retry for a pending activation bootstrap (see the module-level
|
|
1858
|
+
// constants). Re-attempts on the live connection while `wsReady`, bounded by
|
|
1859
|
+
// ACTIVATION_BOOTSTRAP_MAX_RETRIES; the budget resets on each fresh connect.
|
|
1860
|
+
let bootstrapRetryTimer: ReturnType<typeof setTimeout> | undefined;
|
|
1861
|
+
let bootstrapRetryAttempts = 0;
|
|
1862
|
+
const bootstrapRetryDelayMs = Math.max(
|
|
1863
|
+
1,
|
|
1864
|
+
params.activationBootstrapRetryDelayMs ?? ACTIVATION_BOOTSTRAP_RETRY_DELAY_MS,
|
|
1865
|
+
);
|
|
1866
|
+
const clearBootstrapRetry = () => {
|
|
1867
|
+
if (bootstrapRetryTimer !== undefined) {
|
|
1868
|
+
clearTimeout(bootstrapRetryTimer);
|
|
1869
|
+
bootstrapRetryTimer = undefined;
|
|
1870
|
+
}
|
|
1871
|
+
};
|
|
1872
|
+
const armBootstrapRetry = () => {
|
|
1873
|
+
if (bootstrapRetryTimer !== undefined) return;
|
|
1874
|
+
if (!wsReady) return;
|
|
1875
|
+
if (bootstrapRetryAttempts >= ACTIVATION_BOOTSTRAP_MAX_RETRIES) return;
|
|
1876
|
+
bootstrapRetryTimer = setTimeout(() => {
|
|
1877
|
+
bootstrapRetryTimer = undefined;
|
|
1878
|
+
if (!wsReady) return;
|
|
1879
|
+
bootstrapRetryAttempts += 1;
|
|
1880
|
+
void dispatchActivationBootstrap();
|
|
1881
|
+
}, bootstrapRetryDelayMs);
|
|
1882
|
+
};
|
|
1829
1883
|
|
|
1830
1884
|
client.on("state", ({ from, to }) => {
|
|
1831
1885
|
log?.info?.(`[${accountId}] clawchat-plugin-openclaw state ${from} -> ${to}`);
|
|
@@ -1900,6 +1954,9 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1900
1954
|
// The queue keeps the failed frame at the head and will retry after the next reconnect.
|
|
1901
1955
|
}
|
|
1902
1956
|
void refreshConversationCacheAfterReady();
|
|
1957
|
+
// Fresh connection → fresh retry budget; cancel any stale timer first.
|
|
1958
|
+
bootstrapRetryAttempts = 0;
|
|
1959
|
+
clearBootstrapRetry();
|
|
1903
1960
|
void dispatchActivationBootstrap();
|
|
1904
1961
|
// Re-pull per-group agent settings on every successful (re)connect so the
|
|
1905
1962
|
// cache stays current after a network interruption or token refresh.
|
|
@@ -1910,6 +1967,8 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1910
1967
|
refreshManager.armProactiveTimer(activatedAtMs);
|
|
1911
1968
|
} else if (to === "disconnected") {
|
|
1912
1969
|
reconnectTracker.markClosed();
|
|
1970
|
+
// Stop the bootstrap retry while down; it re-arms on the next ready.
|
|
1971
|
+
clearBootstrapRetry();
|
|
1913
1972
|
// §A.1 — clear the proactive timer on disconnect; it re-arms on the next
|
|
1914
1973
|
// ready, or the gateway re-enter arms a fresh one.
|
|
1915
1974
|
if (!reconnectWithRefreshedToken) refreshManager.disarmProactiveTimer();
|
|
@@ -2876,6 +2935,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
2876
2935
|
dispatchActivationBootstrap = async (): Promise<void> => {
|
|
2877
2936
|
if (!store?.claimPendingActivationBootstrap || !store.markActivationBootstrapSent) return;
|
|
2878
2937
|
let bootstrap: { conversationId: string } | null | undefined;
|
|
2938
|
+
let claimedInFlight = false;
|
|
2879
2939
|
const releaseBootstrap = () => {
|
|
2880
2940
|
if (!bootstrap || !store.releaseActivationBootstrapClaim) return;
|
|
2881
2941
|
const claimedBootstrap = bootstrap;
|
|
@@ -2891,26 +2951,37 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
2891
2951
|
bootstrap = recordConnection("activation bootstrap claim", () =>
|
|
2892
2952
|
store.claimPendingActivationBootstrap?.({ platform: "openclaw", accountId }),
|
|
2893
2953
|
);
|
|
2894
|
-
if (!bootstrap)
|
|
2954
|
+
if (!bootstrap) {
|
|
2955
|
+
// Nothing claimable right now. If another dispatch (e.g. a pre-reconnect
|
|
2956
|
+
// one that was about to fail) still holds the claim, keep retrying on
|
|
2957
|
+
// this live connection so the greeting isn't stranded once it releases.
|
|
2958
|
+
if (activationBootstrapInFlightCount(accountId) > 0) armBootstrapRetry();
|
|
2959
|
+
return;
|
|
2960
|
+
}
|
|
2895
2961
|
const claimedBootstrap = bootstrap;
|
|
2962
|
+
claimedInFlight = true;
|
|
2963
|
+
incrementActivationBootstrapInFlight(accountId);
|
|
2896
2964
|
bootstrapProducedFinal = false;
|
|
2897
2965
|
const result = await handleInboundEnvelope(
|
|
2898
2966
|
buildActivationBootstrapEnvelope({ account, conversationId: claimedBootstrap.conversationId }),
|
|
2899
2967
|
);
|
|
2900
2968
|
if (result !== "submitted") {
|
|
2901
2969
|
releaseBootstrap();
|
|
2970
|
+
armBootstrapRetry();
|
|
2902
2971
|
return;
|
|
2903
2972
|
}
|
|
2904
2973
|
// Only mark the bootstrap done once the agent has actually sent a greeting
|
|
2905
2974
|
// (a final outbound message). If the turn was handed off but produced no
|
|
2906
|
-
// final reply — e.g. the
|
|
2907
|
-
//
|
|
2908
|
-
//
|
|
2975
|
+
// final reply — e.g. the greeting send was cancelled by a reconnect, or the
|
|
2976
|
+
// agent went off to write a file/note instead of replying — leave the
|
|
2977
|
+
// bootstrap pending (release the claim) and retry on the live connection,
|
|
2978
|
+
// rather than silently consuming it forever or waiting for the next reconnect.
|
|
2909
2979
|
if (!bootstrapProducedFinal) {
|
|
2910
2980
|
log?.info?.(
|
|
2911
2981
|
`[${accountId}] clawchat-plugin-openclaw activation bootstrap dispatched but produced no final reply; leaving pending for retry`,
|
|
2912
2982
|
);
|
|
2913
2983
|
releaseBootstrap();
|
|
2984
|
+
armBootstrapRetry();
|
|
2914
2985
|
return;
|
|
2915
2986
|
}
|
|
2916
2987
|
recordConnection("activation bootstrap sent", () =>
|
|
@@ -2920,11 +2991,16 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
2920
2991
|
conversationId: claimedBootstrap.conversationId,
|
|
2921
2992
|
}),
|
|
2922
2993
|
);
|
|
2994
|
+
// Delivered — stop any pending retry.
|
|
2995
|
+
clearBootstrapRetry();
|
|
2923
2996
|
} catch (err) {
|
|
2924
2997
|
releaseBootstrap();
|
|
2998
|
+
armBootstrapRetry();
|
|
2925
2999
|
log?.error?.(
|
|
2926
3000
|
`[${accountId}] clawchat-plugin-openclaw activation bootstrap failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
2927
3001
|
);
|
|
3002
|
+
} finally {
|
|
3003
|
+
if (claimedInFlight) decrementActivationBootstrapInFlight(accountId);
|
|
2928
3004
|
}
|
|
2929
3005
|
};
|
|
2930
3006
|
|
|
@@ -3049,6 +3125,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
3049
3125
|
log?.info?.(`[${accountId}] clawchat-plugin-openclaw runtime abort received; closing client`);
|
|
3050
3126
|
activeClients.delete(accountId);
|
|
3051
3127
|
closingForAbort = true;
|
|
3128
|
+
clearBootstrapRetry();
|
|
3052
3129
|
// §A.1 — stop the proactive refresh timer on shutdown.
|
|
3053
3130
|
refreshManager.stop();
|
|
3054
3131
|
groupCoalescer.cancelAll();
|