(key, ttlMs, statePath)
| 430 | // and returns false. Best-effort: a state read/write failure just means no |
| 431 | // throttling (fail open). Entries older than 24h are pruned on write. |
| 432 | function throttled(key, ttlMs, statePath) { |
| 433 | let state = {}; |
| 434 | try { |
| 435 | if (fs.existsSync(statePath)) { |
| 436 | state = JSON.parse(fs.readFileSync(statePath, 'utf8')) || {}; |
| 437 | } |
| 438 | } catch { state = {}; } |
| 439 | |
| 440 | const now = Date.now(); |
| 441 | const last = state[key]; |
| 442 | if (typeof last === 'number' && now - last < ttlMs) return true; |
| 443 | |
| 444 | state[key] = now; |
| 445 | try { |
| 446 | for (const k of Object.keys(state)) { |
| 447 | if (typeof state[k] !== 'number' || now - state[k] > 24 * 60 * 60 * 1000) { |
| 448 | delete state[k]; |
| 449 | } |
| 450 | } |
| 451 | const tmp = statePath + '.tmp'; |
| 452 | fs.writeFileSync(tmp, JSON.stringify(state), 'utf8'); |
| 453 | fs.renameSync(tmp, statePath); |
| 454 | } catch { /* best-effort */ } |
| 455 | return false; |
| 456 | } |
| 457 | |
| 458 | function shouldSkipInjection() { |
| 459 | // Only apply dedup when explicitly enabled (set by Kiro adapter) OR when |
no test coverage detected