Parcourir la source

fix(session): resolve packed default without schema

Tianyi Cui il y a 2 mois
Parent
commit
4a336ba8d7

+ 1 - 1
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`
 

+ 4 - 4
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<Config> = 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<Config>).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<JsonlTornMarker>(this.ctx, this)

+ 26 - 1
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 () => {