import { LIMITS, type Warning } from './types.js'; function boundedMessage(message: string, suffix: string): string { const bytes = Buffer.from(message); let end = Math.min(bytes.length, LIMITS.MAX_ERROR_MESSAGE_BYTES - Buffer.byteLength(suffix)); while (end < bytes.length && (bytes[end] & 0xc0) === 0x80) end--; return bytes.subarray(0, end).toString('utf8') + suffix; } /** Keep repeated warnings and overflow visible without extending the wire shape. */ export class WarningCollector { private entries: Warning[] = []; private groups = new Map(); private overflow = 0; add(warning: Warning): void { const key = JSON.stringify([warning.code, warning.message, warning.tool_call_id, warning.name]); const existing = this.groups.get(key); if (existing) { existing.count++; existing.warning.message = boundedMessage(existing.message, ` (Repeated ${existing.count} times.)`); return; } if (!this.overflow && this.entries.length < LIMITS.MAX_WARNINGS) { const copy = { ...warning }; this.entries.push(copy); this.groups.set(key, { warning: copy, message: copy.message, count: 1 }); return; } if (!this.overflow) { const replaced = this.entries.pop()!; const removed = [...this.groups.entries()].find(([, group]) => group.warning === replaced)!; this.overflow = removed[1].count; this.groups.delete(removed[0]); this.entries.push({ code: 'warnings_aggregated', message: '' }); } this.overflow++; this.entries.at(-1)!.message = `${this.overflow} additional warnings were aggregated after the warning limit.`; } snapshot(): Warning[] { return this.entries.map(w => ({ ...w })); } }