class ToolRateLimits { constructor(options = {}) { this.now = options.now || Date.now; this.buckets = new Map(); } consume({ actor, origin, server }, settings) { const checks = [ [`user:${actor}`, settings.per_user_per_minute], [`origin:${origin}`, settings.per_origin_per_minute], [`server:${origin}:${server}`, settings.per_server_per_minute] ]; let retryAfter = 0; for (const [key, maximum] of checks) { const result = this.inspect(key, maximum); if (!result.allowed) retryAfter = Math.max(retryAfter, result.retry_after_seconds); } if (retryAfter) return { allowed: false, retry_after_seconds: retryAfter }; for (const [key] of checks) this.record(key); return { allowed: true, retry_after_seconds: 0 }; } inspect(key, maximum) { const cutoff = this.now() - 60000; const recent = (this.buckets.get(key) || []).filter((timestamp) => timestamp > cutoff); this.buckets.set(key, recent); if (recent.length < maximum) return { allowed: true, retry_after_seconds: 0 }; return { allowed: false, retry_after_seconds: Math.max(1, Math.ceil((recent[0] + 60000 - this.now()) / 1000)) }; } record(key) { const recent = this.buckets.get(key) || []; recent.push(this.now()); this.buckets.set(key, recent); } } class ToolConcurrency { constructor(maximum = 3, maximumQueue = 20) { this.maximum = Math.max(1, Number(maximum) || 3); this.maximumQueue = Math.max(0, Number(maximumQueue) || 20); this.active = 0; this.queue = []; } run(callback) { if (this.active < this.maximum) return this.start(callback); if (this.queue.length >= this.maximumQueue) { return Promise.reject(Object.assign( new Error("Web lookup concurrency queue is full."), { code: "concurrency_limited" } )); } return new Promise((resolve, reject) => { this.queue.push({ callback, resolve, reject }); }); } async start(callback) { this.active += 1; try { return await callback(); } finally { this.active -= 1; this.drain(); } } drain() { while (this.active < this.maximum && this.queue.length) { const next = this.queue.shift(); this.start(next.callback).then(next.resolve, next.reject); } } } module.exports = { ToolConcurrency, ToolRateLimits };