UNPKG

@sema-agent/core

Version:

Stateless, task-oriented AI agent core

76 lines (75 loc) • 2.67 kB
import { StringDecoder } from "node:string_decoder"; export const MAX_EXEC_OUTPUT_BYTES = 8 * 1024 * 1024; export class RollingTailBuffer { maxBytes; chunks = []; size = 0; headDropped = 0; sourceSkipped = 0; constructor(maxBytes = MAX_EXEC_OUTPUT_BYTES) { this.maxBytes = maxBytes; } push(chunk) { if (chunk.length === 0) return; this.chunks.push(chunk); this.size += chunk.length; while (this.size > this.maxBytes) { const over = this.size - this.maxBytes; const head = this.chunks[0]; if (over >= head.length) { this.chunks.shift(); this.size -= head.length; this.headDropped += head.length; } else { this.chunks[0] = head.subarray(over); this.size -= over; this.headDropped += over; } } } recordSkippedBytes(n) { if (n > 0) this.sourceSkipped += n; } result() { return { text: Buffer.concat(this.chunks).toString("utf8"), droppedBytes: this.headDropped + this.sourceSkipped }; } bytes() { return Buffer.concat(this.chunks); } headDroppedBytes() { return this.headDropped; } sourceSkippedBytes() { return this.sourceSkipped; } } export function newStreamCursorState(tail = new RollingTailBuffer()) { return { tail, cursorBytes: 0, acceptedBytes: 0, disclosedSkippedBytes: 0, decoder: new StringDecoder("utf8") }; } export function sliceStreamIncrement(s, terminal) { const headDropped = s.tail.headDroppedBytes(); const sourceSkipped = s.tail.sourceSkippedBytes(); const newSkipped = Math.max(0, sourceSkipped - s.disclosedSkippedBytes); s.disclosedSkippedBytes = sourceSkipped; const droppedBeforeCursor = Math.max(0, headDropped - s.cursorBytes) + newSkipped; const buf = s.tail.bytes(); const startInTail = Math.min(Math.max(s.cursorBytes, headDropped) - headDropped, buf.length); const incBuf = startInTail <= 0 ? buf : buf.subarray(startInTail); if (droppedBeforeCursor > 0) s.decoder = new StringDecoder("utf8"); let inc = s.decoder.write(incBuf); if (terminal) { inc += s.decoder.end(); s.decoder = new StringDecoder("utf8"); } s.cursorBytes = s.acceptedBytes; return { inc, droppedBeforeCursor }; } export function markTruncated(text, droppedBytes) { return droppedBytes > 0 ? `[... ${droppedBytes} earlier byte(s) truncated; showing last ${Buffer.byteLength(text, "utf8")} ...]\n${text}` : text; }