namespace Kbx.Shared.Providers; public sealed class KisRequestPacer { private readonly SemaphoreSlim _gate = new(1,1); private DateTimeOffset _last = DateTimeOffset.MinValue; public async ValueTask WaitAsync(bool sandbox, CancellationToken ct) { await _gate.WaitAsync(ct); try { var spacing = TimeSpan.FromMilliseconds(sandbox ? 1000 : 100); // KBX conservative default; official production max is higher. var remaining = spacing - (DateTimeOffset.UtcNow - _last); if (remaining > TimeSpan.Zero) await Task.Delay(remaining, ct); _last = DateTimeOffset.UtcNow; } finally { _gate.Release(); } } }