Merge commit 'refs/codex/unblock/master-current' into HEAD

# Conflicts:
#	docs/config-catalog.md
#	packages/session-persistence/session-persistence-jsonl/README.md
#	packages/session-persistence/session-persistence-jsonl/src/index.ts
#	packages/session-persistence/session-persistence-jsonl/tests/jsonl.spec.ts
This commit is contained in:
Tianyi Cui
2026-07-21 20:28:30 +08:00
1144 changed files with 42128 additions and 8470 deletions

View File

@@ -11,17 +11,23 @@
"types": "./lib/types/index.d.ts",
"default": "./lib/index.js"
},
"./invariant": {
"types": "./lib/types/invariant.d.ts",
"default": "./lib/invariant.js"
},
"./src/*": "./src/*",
"./package.json": "./package.json"
},
"files": [
"lib/index.js",
"lib/invariant.js",
"lib/types/**/*.d.ts",
"lib/types/**/*.d.ts.map",
"src"
],
"license": "BSD-3-Clause",
"peerDependencies": {
"@deepseek-ai/dsh-invariants": "^0.0.1",
"@deepseek-ai/dsh-session": "^0.0.1",
"@deepseek-ai/dsh-session-persistence": "^0.0.1",
"cordis": "^4.0.0-rc.7"
@@ -30,6 +36,7 @@
"schemastery": "^3.18.0"
},
"devDependencies": {
"@deepseek-ai/dsh-invariants": "workspace:^",
"@deepseek-ai/dsh-session": "workspace:^",
"@deepseek-ai/dsh-session-persistence": "workspace:^",
"cordis": "^4.0.0-rc.7"

View File

@@ -248,14 +248,15 @@ export class SessionPersistenceSqlite extends SessionPersistence implements Pers
*/
private writeRow(meta: SessionHeader): void {
this.db.prepare(`
INSERT INTO sessions (id, version, created_at, cwd, parent_session, seed_length)
VALUES (?, ?, ?, ?, ?, ?)
INSERT INTO sessions (id, version, created_at, cwd, parent_session, seed_length, delegation_depth)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
version = excluded.version,
created_at = excluded.created_at,
cwd = excluded.cwd,
parent_session = excluded.parent_session,
seed_length = excluded.seed_length
seed_length = excluded.seed_length,
delegation_depth = excluded.delegation_depth
`).run(
meta.id,
meta.version,
@@ -263,6 +264,7 @@ export class SessionPersistenceSqlite extends SessionPersistence implements Pers
meta.cwd ?? null,
meta.parentSession ?? null,
meta.seedLength ?? null,
meta.delegationDepth ?? null,
)
}
}

View File

@@ -0,0 +1,30 @@
/**
* Package-owned invariant companion for `@deepseek-ai/dsh-session-persistence-sqlite`.
* @module @deepseek-ai/dsh-session-persistence-sqlite/invariant
*/
/* jscpd:ignore-start */
import type { Context } from 'cordis'
import type { InvariantInstaller } from '@deepseek-ai/dsh-invariants'
const PACKAGE_NAME = '@deepseek-ai/dsh-session-persistence-sqlite'
/** Cordis companion plugin name. */
export const name = 'session-persistence-sqlite-invariant'
/** Service required before the companion can reserve package ownership. */
export const inject = ['invariants']
/**
* No runtime invariant: persistence correctness requires backend round-trip and crash-tail tests;
* this package exposes no continuously observable in-process relation.
*/
const install: InvariantInstaller = () => {}
/**
* Register this package's invariant companion.
* @param ctx - Cordis context carrying the invariant service.
* @returns the installed registration's disposer after setup succeeds.
*/
export const apply = (ctx: Context): Promise<() => void> =>
Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install))
/* jscpd:ignore-end */

View File

@@ -15,7 +15,7 @@ import type { SessionEvent, SessionId, SessionHeader, SurfaceOp } from '@deepsee
* layout; orthogonal to a session's own `version` (which versions the EVENT
* vocabulary, stored per session in the `sessions` row).
*/
export const SCHEMA_VERSION = 4
export const SCHEMA_VERSION = 5
/**
* A row of the `sessions` table — the out-of-log metadata ({@link SessionHeader}).
@@ -31,6 +31,7 @@ export interface SessionRow {
cwd: string | null
parent_session: string | null
seed_length: number | null
delegation_depth: number | null
}
/** An `events` table row: one `SessionEvent` mapped 1:1 (`data` is JSON text). */
@@ -83,9 +84,10 @@ export function openDatabase(path: string, journalMode: JournalMode): DatabaseSy
id TEXT PRIMARY KEY,
version INTEGER NOT NULL,
created_at INTEGER NOT NULL,
cwd TEXT,
parent_session TEXT,
seed_length INTEGER
cwd TEXT,
parent_session TEXT,
seed_length INTEGER,
delegation_depth INTEGER
) STRICT
`)
db.exec(`
@@ -116,6 +118,7 @@ export function rowToMeta(row: SessionRow): SessionHeader {
...row.cwd !== null ? { cwd: row.cwd } : {},
...row.parent_session !== null ? { parentSession: row.parent_session as SessionId } : {},
...row.seed_length !== null ? { seedLength: row.seed_length } : {},
...row.delegation_depth !== null ? { delegationDepth: row.delegation_depth } : {},
}
}

View File

@@ -14,17 +14,15 @@ import { runCoordinatorContract, type CoordinatorFixture } from '../../session-p
const dirs: string[] = []
afterEach(async () => { for (const d of dirs.splice(0)) await rm(d, { recursive: true, force: true }) })
async function expectParallelFlushError(promise: Promise<unknown>, message: RegExp): Promise<void> {
async function expectFlushError(promise: Promise<unknown>, message: RegExp): Promise<void> {
try {
await promise
} catch (error) {
expect(error).toBeInstanceOf(AggregateError)
const [cause] = (error as AggregateError).errors as unknown[]
expect(cause).toBeInstanceOf(Error)
expect((cause as Error).message).toMatch(message)
expect(error).toBeInstanceOf(Error)
expect((error as Error).message).toMatch(message)
return
}
throw new Error('expected parallel flush to reject')
throw new Error('expected flush to reject')
}
async function freshDbPath(): Promise<string> {
@@ -385,7 +383,7 @@ describe('SessionPersistenceSqlite: durability and crash semantics', () => {
})
it('exposes the schema version constant', () => {
expect(SCHEMA_VERSION).toBe(4)
expect(SCHEMA_VERSION).toBe(5)
})
})
@@ -502,7 +500,7 @@ describe('SessionPersistenceSqlite: edge cases', () => {
const b1 = await backend(path)
const s1 = b1.ctx.sessions.create(SessionId('hmr-collide'))
appendLog(s1, oneTurnLog())
await b1.ctx.parallel('session/flush', s1)
await b1.ctx.sessions.flush(s1)
await b1.dispose()
// A fresh context with an UNRELATED live session reusing the id meets a
@@ -513,9 +511,9 @@ describe('SessionPersistenceSqlite: edge cases', () => {
await ctx.plugin(Object.assign((inner: Context) => {
session = inner.sessions.create(SessionId('hmr-collide'))
}, { inject: ['sessions'] }))
session.append('turn/start', { turn: 9, trigger: { kind: 'message', source: { kind: 'user' } } })
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
await ctx.plugin(SessionPersistenceSqlite, { path })
await expectParallelFlushError(ctx.parallel('session/flush', session), /id collision/)
await expectFlushError(ctx.sessions.flush(session), /id collision/)
await ctx.fiber.dispose()
})
})
@@ -567,18 +565,20 @@ describe('surface field round-trip', () => {
const fiber = await ctx.plugin(SessionPersistenceSqlite, { path: ':memory:' })
const session = ctx.sessions.create(SessionId('roundtrip-surface'))
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
session.append('step/start', { turn: 1, step: 1 })
session.append('user/message', { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } }, { surfaceOp: 'append' })
session.append('assistant/message', { provenance: { provider: 'mock', model: 'mock' }, turn: 1, step: 1, content: [] }, { surfaceOp: 'append', sourceEventSeqs: [0] })
session.append('assistant/message', { provenance: { provider: 'mock', model: 'mock' }, turn: 1, step: 1, content: [] }, { surfaceOp: 'append', sourceEventSeqs: [2] })
session.append('step/end', { turn: 1, step: 1 })
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
await ctx.parallel('session/flush', session)
await ctx.sessions.flush(session)
const loaded = await ctx.sessionPersistence.load(SessionId('roundtrip-surface'))
expect(loaded.events).toHaveLength(4)
const um = loaded.events[1]!
expect(loaded.events).toHaveLength(6)
const um = loaded.events[2]!
expect((um as SurfaceEvent).surfaceOp).toBe('append')
expect((um as SurfaceEvent).sourceEventSeqs).toBeUndefined()
const am = loaded.events[2]!
const am = loaded.events[3]!
expect((am as SurfaceEvent).surfaceOp).toBe('append')
expect((am as SurfaceEvent).sourceEventSeqs).toEqual([0])
expect((am as SurfaceEvent).sourceEventSeqs).toEqual([2])
await fiber.dispose()
})
@@ -590,7 +590,7 @@ describe('surface field round-trip', () => {
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
session.append('steering/message', { turn: 1, content: [], source: { kind: 'user' } }, { surfaceOp: 'append' })
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
await ctx.parallel('session/flush', session)
await ctx.sessions.flush(session)
const loaded = await ctx.sessionPersistence.load(SessionId('surface-noseq'))
expect((loaded.events[1]! as SurfaceEvent).surfaceOp).toBe('append')
expect((loaded.events[1]! as SurfaceEvent).sourceEventSeqs).toBeUndefined()

View File

@@ -22,6 +22,9 @@
},
{
"path": "../../session-persistence/session-persistence"
},
{
"path": "../../support/invariants"
}
]
}