fix(session): repaired torn JSONL appends

Rolled partial file appends back to their pre-write size and marked malformed resumed sessions for an atomic rewrite.

Retried transient persistence failures from in-memory state and surfaced the first failure in the interactive TUI.

Fixes #8596
This commit is contained in:
roboomp
2026-08-15 00:33:48 +00:00
parent ffd53ff92a
commit 9ae089d71b
10 changed files with 233 additions and 75 deletions
+8 -2
View File
@@ -446,12 +446,13 @@ export async function* readSseEvents(
* Uses `Bun.JSONL.parseChunk` internally. On parse errors, the malformed
* region is skipped up to the next newline and parsing continues.
*
* @param options.onMalformedRecord Called once for every skipped JSONL record.
* @example
* ```ts
* const entries = parseJsonlLenient<MyType>(fileContents);
* ```
*/
export function parseJsonlLenient<T>(buffer: string): T[] {
export function parseJsonlLenient<T>(buffer: string, options: { onMalformedRecord?: () => void } = {}): T[] {
let entries: T[] | undefined;
while (buffer.length > 0) {
@@ -466,11 +467,16 @@ export function parseJsonlLenient<T>(buffer: string): T[] {
}
if (error) {
const nextNewline = buffer.indexOf("\n", read);
const malformedEnd = nextNewline === -1 ? buffer.length : nextNewline;
if (buffer.substring(read, malformedEnd).trim().length > 0) options.onMalformedRecord?.();
if (nextNewline === -1) break;
buffer = buffer.substring(nextNewline + 1);
continue;
}
if (read === 0) break;
if (read === 0) {
if (buffer.trim().length > 0) options.onMalformedRecord?.();
break;
}
buffer = buffer.substring(read);
if (done) break;
}