From 4a336ba8d753f23d7e0d8d16cd6096181bcc7178 Mon Sep 17 00:00:00 2001 From: Tianyi Cui <53024+tianyicui@users.noreply.github.com> Date: Mon, 27 Jul 2026 03:54:03 +0800 Subject: [PATCH] fix(session): resolve packed default without schema --- docs/config-catalog.md | 2 +- .../session-persistence-jsonl/src/index.ts | 8 +++--- .../tests/zstd.spec.ts | 27 ++++++++++++++++++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/docs/config-catalog.md b/docs/config-catalog.md index 4cb713ad92..4a6f3669e2 100644 --- a/docs/config-catalog.md +++ b/docs/config-catalog.md @@ -1003,7 +1003,7 @@ export interface Config { export type JsonlCompression = 'zstd' | 'none' ``` -Source: [`packages/session-persistence/session-persistence-jsonl/src/index.ts:39`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) +Source: [`packages/session-persistence/session-persistence-jsonl/src/index.ts:40`](../packages/session-persistence/session-persistence-jsonl/src/index.ts) ## `@deepseek-ai/dsh-session-persistence-sqlite` diff --git a/packages/session-persistence/session-persistence-jsonl/src/index.ts b/packages/session-persistence/session-persistence-jsonl/src/index.ts index f452fb986c..a312b94a40 100644 --- a/packages/session-persistence/session-persistence-jsonl/src/index.ts +++ b/packages/session-persistence/session-persistence-jsonl/src/index.ts @@ -27,6 +27,7 @@ import { ensureDurableDirectoryWin32, publishNewFileWin32 } from './win32.ts' export type { JsonlCompression } from './format.ts' +const DEFAULT_PACK_CHUNKS = true const DEFAULT_COMPRESSION: JsonlCompression = 'zstd' /** Loader schema for the JSONL artifact's physical encoding. */ @@ -79,7 +80,7 @@ export class SessionPersistenceJsonl extends SessionPersistence implements Persi static Config: z = z.object({ root: z.string().required(), - packChunks: z.boolean().default(true), + packChunks: z.boolean().default(DEFAULT_PACK_CHUNKS), compression: JsonlCompressionSchema, }) @@ -100,9 +101,8 @@ export class SessionPersistenceJsonl extends SessionPersistence implements Persi super(ctx) // Resolve once so later process.cwd() changes cannot split one backend across roots. this.root = resolve(config.root) - // schemastery (static Config) applied the default before construction; - // the cast records that runtime fact for exactOptionalPropertyTypes. - this.packChunks = (config as Required).packChunks + // Programmatic wrappers may construct the backend without Schemastery normalization. + this.packChunks = config.packChunks ?? DEFAULT_PACK_CHUNKS this.compression = config.compression ?? DEFAULT_COMPRESSION this.assertUsableRoot() this.coordinator = new PersistenceCoordinator(this.ctx, this) diff --git a/packages/session-persistence/session-persistence-jsonl/tests/zstd.spec.ts b/packages/session-persistence/session-persistence-jsonl/tests/zstd.spec.ts index 9283d51918..cef1ff71e5 100644 --- a/packages/session-persistence/session-persistence-jsonl/tests/zstd.spec.ts +++ b/packages/session-persistence/session-persistence-jsonl/tests/zstd.spec.ts @@ -245,10 +245,35 @@ describe('SessionPersistenceJsonl: default Zstandard encoding', () => { backend = new SessionPersistenceJsonl(inner, { root }) }, { inject: ['sessions'] })) const header = meta('direct-default') + const path = logPath(root, header.cwd, header.id, 'zstd') expect(backend.locate(header)).toEqual({ kind: 'jsonl', - path: logPath(root, header.cwd, header.id, 'zstd'), + path, }) + + const base = oneTurnLog() + const events: SessionEvent[] = [ + ...base.slice(0, 3), + ...Array.from({ length: 3 }, (_, index): SessionEvent => ({ + type: 'assistant/chunk', + seq: 3 + index, + time: 4 + index, + data: { turn: 1, step: 1, chunk: { type: 'text-delta', index: 0, text: `part-${index}` } }, + })), + ...base.slice(3).map((event): SessionEvent => ({ + ...event, + seq: event.seq + 3, + time: event.time + 3, + })), + ] + await backend.create(header) + await backend.append(header.id, events) + + const plaintext = (await decodeCompleteFrames(await readFile(path))).toString() + const recordTypes = plaintext.trimEnd().split('\n') + .map(line => (JSON.parse(line) as { type: string }).type) + expect(recordTypes).toContain('text-chunks') + expect((await backend.load(header.id)).events).toEqual(events) }) it('appends one frame per durable batch without rewriting prior bytes', async () => {