Merge remote-tracking branch 'origin/master' into worktree/fix-sqlite-file-permissions

# Conflicts:
#	docs/config-catalog.md
This commit is contained in:
Yichen Jiang
2026-07-17 23:06:20 +08:00
678 files changed
+37644 -8578

No files matched your search

@@ -153,6 +153,48 @@ describe('scanRows', () => {
})
describe('SessionPersistenceSqlite: durability and crash semantics', () => {
it('rejects a stored v0 log containing a legacy request/header-delta event', async () => {
const path = await freshDbPath()
const m = meta('legacy-header-delta', '/legacy')
const db = openDatabase(path, 'wal')
db.prepare('INSERT INTO sessions (id, version, created_at, cwd, parent_session, seed_length) VALUES (?, ?, ?, ?, NULL, NULL)')
.run(m.id, m.version, m.createdAt, m.cwd ?? null)
const insert = db.prepare('INSERT INTO events (session_id, seq, type, time, data) VALUES (?, ?, ?, ?, ?)')
insert.run(m.id, 0, 'turn/start', 1, JSON.stringify({ turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } }))
insert.run(m.id, 1, 'request/header-delta', 2, JSON.stringify({ config: { model: 'legacy' } }))
insert.run(m.id, 2, 'turn/end', 3, JSON.stringify({ turn: 1, reason: { kind: 'completed' } }))
db.close()
const mounted = await backend(path)
await expect(mounted.ctx.sessionPersistence.load(m.id)).rejects.toThrow(/unsupported legacy request\/header-delta event at seq 1/)
await mounted.dispose()
})
it('rejects a stored v0 full header carrying the legacy fallback reason', async () => {
const path = await freshDbPath()
const m = meta('legacy-header-fallback', '/legacy')
const db = openDatabase(path, 'wal')
db.prepare('INSERT INTO sessions (id, version, created_at, cwd, parent_session, seed_length) VALUES (?, ?, ?, ?, NULL, NULL)')
.run(m.id, m.version, m.createdAt, m.cwd ?? null)
db.prepare('INSERT INTO events (session_id, seq, type, time, data) VALUES (?, ?, ?, ?, ?)')
.run(m.id, 0, 'request/header', 1, JSON.stringify({
header: { config: { model: 'legacy' } },
reason: 'fallback',
}))
db.close()
const mounted = await backend(path)
await expect(mounted.ctx.sessionPersistence.load(m.id))
.rejects.toThrow(/unsupported legacy request\/header reason "fallback" at seq 0/)
await mounted.dispose()
})
it('has no independent per-session log location', async () => {
const { ctx, dispose } = await backend()
expect(ctx.sessionPersistence.locate(meta('sqlite-location'))).toBeUndefined()
await dispose()
})
it('an interrupted turn (rows after the last turn/end) is PRESERVED and closed during load', async () => {
const path = await freshDbPath()
const m = meta('crash')
@@ -510,7 +552,7 @@ describe('surface field round-trip', () => {
const session = ctx.sessions.create(SessionId('roundtrip-surface'))
session.append('turn/start', { turn: 1, trigger: { kind: 'message', source: { kind: 'user' } } })
session.append('user/message', { content: [{ type: 'text', text: 'hi' }], source: { kind: 'user' } }, { surfaceOp: 'append' })
session.append('assistant/message', { 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: [0] })
session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
await ctx.parallel('session/flush', session)
const loaded = await ctx.sessionPersistence.load(SessionId('roundtrip-surface'))