import { writeRawStdout } from "../../core/output-guard.js";
import { INTERACTIVE_ENGINE_MAX_FRAME_BYTES } from "../interactive-engine/protocol.js";
const MAX_STRING_BYTES = 64 * 1024;
const MAX_ARRAY_ITEMS = 128;
function boundedValue(value, depth = 0) {
    if (typeof value === "string") {
        if (Buffer.byteLength(value, "utf8") <= MAX_STRING_BYTES)
            return value;
        return `${Buffer.from(value).subarray(0, MAX_STRING_BYTES).toString("utf8")}\n[RPC payload truncated]`;
    }
    if (value === null || typeof value !== "object")
        return value;
    if (depth >= 8)
        return "[RPC payload depth truncated]";
    if (Array.isArray(value))
        return value.slice(0, MAX_ARRAY_ITEMS).map((entry) => boundedValue(entry, depth + 1));
    const result = {};
    for (const [key, entry] of Object.entries(value)) {
        if (entry !== undefined)
            result[key] = boundedValue(entry, depth + 1);
    }
    return result;
}
function serializeBounded(record) {
    let line = JSON.stringify(record);
    if (Buffer.byteLength(line, "utf8") <= INTERACTIVE_ENGINE_MAX_FRAME_BYTES)
        return `${line}\n`;
    line = JSON.stringify(boundedValue(record));
    if (Buffer.byteLength(line, "utf8") <= INTERACTIVE_ENGINE_MAX_FRAME_BYTES)
        return `${line}\n`;
    const identity = record;
    return `${JSON.stringify({
        type: identity.type ?? "transport_error",
        ...(identity.id ? { id: identity.id } : {}),
        ...(identity.command ? { command: identity.command } : {}),
        success: false,
        error: "RPC record exceeded the 1 MiB transport limit",
    })}\n`;
}
export class RpcOutputBuffer {
    constructor() {
        this.updates = new Map();
        this.output = (record) => this.enqueue(record);
    }
    dispose() { this.flush(); }
    enqueue(record) {
        const event = record;
        const key = event.type === "message_update"
            ? "message"
            : event.type === "tool_execution_update" && event.toolCallId
                ? `tool:${event.toolCallId}`
                : undefined;
        if (key) {
            this.updates.set(key, record);
            this.timer ??= setTimeout(() => this.flush(), 16);
            return;
        }
        this.flush();
        writeRawStdout(serializeBounded(record));
    }
    flush() {
        if (this.timer)
            clearTimeout(this.timer);
        this.timer = undefined;
        for (const record of this.updates.values())
            writeRawStdout(serializeBounded(record));
        this.updates.clear();
    }
}
//# sourceMappingURL=rpc-output-buffer.js.map