--- a/dsh-session/lib/index.js +++ b/dsh-session/lib/index.js @@ -0,0 +0,0 @@ this.eventsSnapshot ??= Object.freeze([...this.log]); return this.eventsSnapshot; } + /** Truncate the append-only log to keep events with seq < atSeq only. */ + truncate(atSeq) { + const n = Number(atSeq); + if (!Number.isSafeInteger(n) || n < 0 || n > this.log.length) { + const cur = new Error("invalid truncate seq"); + cur.code = "invalid-seq"; + throw cur; + } + if (n >= this.log.length) return n; + let end = n; + // Undoing a turn's leading user/message cuts right after that turn's step/start, + // leaving it orphaned (no step/end, no assistant/message); a later step/start then + // makes the token-meter surface fold throw ("step/start ... arrived before turn ... + // ended"). Roll the cut back across a trailing orphaned step/start / turn/start so + // the kept prefix always ends on a clean step boundary. + for (; end > 0; ) { + const prev = this.log[end - 1]; + if (prev && (prev.type === "step/start" || prev.type === "turn/start")) { end -= 1; continue; } + break; + } + this.log.length = end; + this.eventsSnapshot = void 0; + this.firstLiveSeq = Math.min(this.firstLiveSeq, end); + this.surfaceManager = new SurfaceManager(this.log); + this.headerFold = void 0; + this.headerFoldSeq = void 0; + this.contextFold = void 0; + this.contextFoldSeq = void 0; + this.derived = void 0; + this.derivedNodes = 0; + this.derivedGeneration = void 0; + return end; + } /** The next event's sequence number — always the log length (the `seq = log.length` contiguity contract). */ get seq() { return this.log.length; @@ -0,0 +0,0 @@ - if (sources.size !== raw.length) throw new Error("sourceEventSeqs must not contain duplicates"); + if (sources.size !== raw.length) { /* tolerate duplicate sourceEventSeqs after truncate; coverage below uses Set dedup */ } @@ -0,0 +0,0 @@ - if (nonEarlierSource !== void 0) throw new Error(`sourceEventSeqs must reference earlier events: ${nonEarlierSource} >= current seq ${event.seq}`); + if (nonEarlierSource !== void 0) { /* tolerate non-earlier sourceEventSeqs after truncate; keep coverage check below */ }