/** In-memory ring buffer of multi-agent guidance-injection / effort-cap log lines. * * Injection debug lines were previously console-only, so the GUI had an "Injection log" * toggle with nothing to display. This buffer mirrors the provider debug buffer so the * management API and GUI can tail injection lines the same way. Callers keep their own * `isInjectionDebugEnabled()` guard; this module only stores what it is given. */ import type { DebugLogEntry } from "./debug-log-buffer"; import { retainedUtf8Bytes, truncateRetainedUtf8 } from "./admission"; import { enforceAppOwnedMemoryBudget } from "./app-owned-memory"; const MAX_LINES = 2_000; const MAX_DEBUG_LINE_BYTES = 16 * 1024; const buffer: DebugLogEntry[] = []; let nextSeq = 1; let bufferBytes = 0; function removeOldestEntry(): number { const entry = buffer.shift(); if (!entry) return 0; const bytes = retainedUtf8Bytes(entry.line); bufferBytes -= bytes; return bytes; } /** Append a line to the injection buffer and echo it to the server console. */ export function injectionDebugLog(line: string): void { const retainedLine = truncateRetainedUtf8(line, MAX_DEBUG_LINE_BYTES); const entry: DebugLogEntry = { seq: nextSeq++, at: Date.now(), line: retainedLine }; buffer.push(entry); bufferBytes += retainedUtf8Bytes(retainedLine); while (buffer.length > MAX_LINES) removeOldestEntry(); enforceAppOwnedMemoryBudget(); console.log(line); } export function getInjectionDebugLogEntries(options?: { after?: number; limit?: number }): DebugLogEntry[] { const after = options?.after ?? 0; const limit = options?.limit ?? 500; const filtered = after > 0 ? buffer.filter(entry => entry.seq > after) : buffer; if (filtered.length <= limit) return filtered; return filtered.slice(-limit); } export function injectionBufferMetrics(): { entries: number; bytes: number; oldestAt: number | null } { return { entries: buffer.length, bytes: bufferBytes, oldestAt: buffer[0]?.at ?? null }; } export function evictOldestInjectionEntryForBudget(): number { return removeOldestEntry(); } /** Test isolation. */ export function resetInjectionDebugLogBufferForTests(): void { buffer.length = 0; bufferBytes = 0; nextSeq = 1; }