feat(session-title): integrate queries ACP and TUI
This commit is contained in:
@@ -7,6 +7,8 @@
|
||||
import { Context, Service } from 'cordis'
|
||||
import z from 'schemastery'
|
||||
import type { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import { foldSessionTitle } from '@deepseek-ai/dsh-session-title'
|
||||
import type { SessionTitleSnapshot } from '@deepseek-ai/dsh-session-title'
|
||||
import type {
|
||||
SessionEventReadRequest,
|
||||
SessionEventRecord,
|
||||
@@ -64,6 +66,16 @@ export class SessionQueryService extends Service {
|
||||
return this._corpus.listSessions()
|
||||
}
|
||||
|
||||
/**
|
||||
* Fold the latest log-backed title from one live-preferred logical session.
|
||||
* @param sessionId - live or persisted session id to read.
|
||||
* @returns latest title snapshot, or `undefined` when the log has no title event.
|
||||
*/
|
||||
async readTitle(sessionId: SessionId): Promise<SessionTitleSnapshot | undefined> {
|
||||
const loaded = await this._corpus.load(sessionId)
|
||||
return foldSessionTitle(loaded.events)
|
||||
}
|
||||
|
||||
/**
|
||||
* List lightweight raw-log event records for one logical session.
|
||||
* @param sessionId - live-preferred session id to read.
|
||||
|
||||
@@ -6,6 +6,7 @@ import SessionPersistence from '@deepseek-ai/dsh-session-persistence'
|
||||
import SessionQueryService, {
|
||||
type SessionQueryErrorCode,
|
||||
} from '@deepseek-ai/dsh-session-query'
|
||||
import { SessionTitleProviderId } from '@deepseek-ai/dsh-session-title'
|
||||
|
||||
function header(id: string, createdAt = 1, extra: Partial<SessionHeader> = {}): SessionHeader {
|
||||
return { version: SESSION_FORMAT_VERSION, id: SessionId(id), createdAt, ...extra }
|
||||
@@ -85,6 +86,58 @@ function rejectUnknown<T>(reason: unknown): Promise<T> {
|
||||
}
|
||||
|
||||
describe('session-query exact reads', () => {
|
||||
it('reads the latest title from one live-preferred or persisted log without widening listSessions', async () => {
|
||||
const persistedHeader = header('persisted-title', 2)
|
||||
const sharedHeader = header('shared-title', 3)
|
||||
TestPersistence.reset([
|
||||
{
|
||||
meta: persistedHeader,
|
||||
events: [{
|
||||
type: 'session/title',
|
||||
seq: 0,
|
||||
time: 20,
|
||||
data: {
|
||||
title: 'Persisted title',
|
||||
messageSeqs: [4],
|
||||
source: { kind: 'fallback' },
|
||||
},
|
||||
}],
|
||||
},
|
||||
{
|
||||
meta: sharedHeader,
|
||||
events: [{
|
||||
type: 'session/title',
|
||||
seq: 0,
|
||||
time: 30,
|
||||
data: {
|
||||
title: 'Stale durable title',
|
||||
messageSeqs: [1],
|
||||
source: { kind: 'fallback' },
|
||||
},
|
||||
}],
|
||||
},
|
||||
])
|
||||
const ctx = await liveContext()
|
||||
const shared = ctx.sessions.create(sharedHeader.id, { meta: { createdAt: 3 } })
|
||||
shared.append('session/title', {
|
||||
title: 'Live title',
|
||||
messageSeqs: [7],
|
||||
source: {
|
||||
kind: 'provider',
|
||||
provider: SessionTitleProviderId('query-test'),
|
||||
},
|
||||
})
|
||||
await ctx.plugin(TestPersistence)
|
||||
|
||||
await expect(ctx.sessionQuery.readTitle(persistedHeader.id)).resolves.toMatchObject({
|
||||
title: 'Persisted title', eventSeq: 0, updatedAt: 20,
|
||||
})
|
||||
await expect(ctx.sessionQuery.readTitle(shared.id)).resolves.toMatchObject({
|
||||
title: 'Live title', eventSeq: 0,
|
||||
})
|
||||
expect(Object.keys((await ctx.sessionQuery.listSessions())[0]!)).toEqual(['header', 'live', 'persisted'])
|
||||
})
|
||||
|
||||
it('lists live sessions deterministically and returns detached headers', async () => {
|
||||
const ctx = await liveContext()
|
||||
const older = ctx.sessions.create(SessionId('older'), { meta: { createdAt: 1 } })
|
||||
|
||||
Reference in New Issue
Block a user