Merge remote-tracking branch 'origin/session-query' into session-query-trace-filter

# Conflicts:
#	docs/config-catalog.md
#	docs/core-data-structures/core.md
#	docs/rfc/INDEX.md
#	packages/support/invariants/src/index.ts
This commit is contained in:
Hypatia May
2026-07-13 14:55:25 +08:00
242 changed files with 14575 additions and 4832 deletions

View File

@@ -90,7 +90,7 @@ describe('session-query exact reads', () => {
const records = await ctx.sessionQuery.listSessions()
expect(records.map(record => record.header.id)).toEqual([SessionId('a'), SessionId('z'), older.id])
expect(records.every(record => record.live && !record.persisted)).toBe(true)
records[2]!.header.createdAt = 99
Object.assign(records[2]!.header, { createdAt: 99 })
expect(older.header.createdAt).toBe(1)
})
@@ -131,7 +131,7 @@ describe('session-query exact reads', () => {
const result = await ctx.sessionQuery.readEvent({ sessionId: session.id, seq: 1, before: 1, after: 1 })
expect([result.startSeq, result.endSeq, result.target.seq]).toEqual([0, 2, 1])
expect(result.session).toEqual(session.header)
result.session.createdAt = -1
Object.assign(result.session, { createdAt: -1 })
if (result.events[0]?.type !== 'user/message') throw new Error('expected user message')
result.events[0].data.content = []
expect(session.header.createdAt).not.toBe(-1)
@@ -172,7 +172,8 @@ describe('session-query exact reads', () => {
await expect(ctx.sessionQuery.readEvent({ sessionId: durable.id, seq: 0 }))
.resolves.toMatchObject({ session: durable })
TestPersistence.entries.get(shared.id)!.meta.cwd = '/conflict'
const sharedEntry = TestPersistence.entries.get(shared.id)!
sharedEntry.meta = { ...sharedEntry.meta, cwd: '/conflict' }
await expect(ctx.sessionQuery.listSessions()).rejects.toThrow(expectCode('SESSION_QUERY_SOURCE_CONFLICT'))
await persistence.dispose()
await expect(ctx.sessionQuery.listSessions()).resolves.toEqual([
@@ -213,9 +214,11 @@ describe('session-query exact reads', () => {
await expect(ctx.sessionQuery.listEvents(durable.id))
.rejects.toThrow(expectCode('SESSION_QUERY_PERSISTENCE_FAILED'))
TestPersistence.loadFailure = undefined
TestPersistence.entries.get(durable.id)!.meta.cwd = '/changed-after-list'
const durableEntry = TestPersistence.entries.get(durable.id)!
durableEntry.meta = { ...durableEntry.meta, cwd: '/changed-after-list' }
TestPersistence.afterList = () => {
TestPersistence.entries.get(durable.id)!.meta.cwd = '/changed-during-read'
const listedEntry = TestPersistence.entries.get(durable.id)!
listedEntry.meta = { ...listedEntry.meta, cwd: '/changed-during-read' }
}
await expect(ctx.sessionQuery.listEvents(durable.id))
.rejects.toThrow(expectCode('SESSION_QUERY_SOURCE_CONFLICT'))

View File

@@ -5,6 +5,13 @@ import type { Session, SessionEvent, SessionHeader, SessionId as SessionIdType }
import SessionPersistence from '@deepseek-ai/dsh-session-persistence'
import SessionQueryService, { type SessionQueryErrorCode } from '@deepseek-ai/dsh-session-query'
type MutableSessionHeader = { -readonly [K in keyof SessionHeader]: SessionHeader[K] }
/** Test-only mutable view used to verify detached returned metadata. */
function mutableHeader(value: SessionHeader): MutableSessionHeader {
return value
}
function header(id: string, createdAt = 1, extra: Partial<SessionHeader> = {}): SessionHeader {
return { version: SESSION_FORMAT_VERSION, id: SessionId(id), createdAt, ...extra }
}
@@ -134,10 +141,10 @@ describe('session lineage tracing', () => {
expect(trace.descendants[1]?.descendants.map(node => node.session.header.id))
.toEqual([SessionId('grandchild')])
trace.target.header.createdAt = 99
trace.ancestors[0]!.header.createdAt = 99
trace.root.header.createdAt = 99
trace.descendants[0]!.session.header.createdAt = 99
mutableHeader(trace.target.header).createdAt = 99
mutableHeader(trace.ancestors[0]!.header).createdAt = 99
mutableHeader(trace.root.header).createdAt = 99
mutableHeader(trace.descendants[0]!.session.header).createdAt = 99
const repeated = await ctx.sessionQuery.traceSession(target.id)
expect(repeated.target.header.createdAt).toBe(2)
expect(repeated.ancestors[0]?.header.createdAt).toBe(1)
@@ -315,7 +322,7 @@ describe('session event tracing', () => {
.rejects.toThrow(expectCode('SESSION_QUERY_PERSISTENCE_FAILED'))
TracePersistence.loadFailure = undefined
TracePersistence.afterList = () => {
TracePersistence.entries.get(durable.id)!.meta.cwd = '/changed'
mutableHeader(TracePersistence.entries.get(durable.id)!.meta).cwd = '/changed'
}
await expect(failedCtx.sessionQuery.traceEvent({ sessionId: durable.id, seq: 0 }))
.rejects.toThrow(expectCode('SESSION_QUERY_SOURCE_CONFLICT'))