feat(schedule): add durable after reminders

This commit is contained in:
pku-xht
2026-08-06 03:47:37 +08:00
committed by Tianyi Cui
parent f7e7851e3f
commit 8b69252664
41 changed files with 354 additions and 146 deletions

View File

@@ -45,7 +45,6 @@ function reminderEvent(seq: number, id: string): SessionEvent {
function reminderView(id: string, prompt = '检查日志') {
return {
for: 'event' as const,
presentationKey: 'schedule/reminder',
view: { id, prompt },
}
}
@@ -127,7 +126,7 @@ describe('late event views', () => {
type: 'session/event', sessionId: SID, event, view: reminderView('schedule-1'),
})
expect(session.getSnapshot().nodes).toMatchObject([{
kind: 'presented-event', seq: 0, presentationKey: 'schedule/reminder',
kind: 'presented-event', seq: 0, eventType: 'schedule/change',
view: { id: 'schedule-1', prompt: '检查日志' },
}])
@@ -707,6 +706,27 @@ describe('paging', () => {
expect(snapshot.nodes.map(n => n.seq)).toEqual([1, 3, 7, 9])
})
it('keeps a concurrent live tail in the current window before prepending the older page', async () => {
const older = plainTurn(0, 0, '旧问', '旧答')
const newer = plainTurn(6, 1, '新问', '新答')
const page = deferred<Awaited<ReturnType<FakeApiClient['onHistory']>>>()
const { api, session } = makeSession()
api.onHistory = payload => payload.beforeSeq === undefined
? histResponse(newer, true)
: page.promise
await session.open()
const loading = session.loadOlder()
session.handleMuxEnvelope('live-tail' as never, {
type: 'session/event', sessionId: SID, event: ev.user(12, '并发尾部'),
})
expect(session.getSnapshot().nodes.map(node => node.seq)).toEqual([7, 9, 12])
page.resolve(await histResponse(older, false))
await loading
expect(session.getSnapshot().nodes.map(node => node.seq)).toEqual([1, 3, 7, 9, 12])
})
it('renders a page whose checkpoint shadows seqs below the window head, logging nothing', async () => {
// Pagination no longer spends maxMessages quota on replacement copies, so a
// page can carry a compaction checkpoint whose surfaceOp.start lies outside

View File

@@ -440,23 +440,21 @@ describe('TranscriptAdapter', () => {
const adapter = new TranscriptAdapter()
adapter.reset([replayed], [{
for: 'event',
presentationKey: 'schedule/reminder',
view: { id: 'schedule-1', prompt: '检查日志' },
}])
adapter.append(live, {
for: 'event',
presentationKey: 'schedule/reminder',
view: { id: 'schedule-2', prompt: '检查发布' },
})
expect(adapter.nodes()).toEqual([
{
kind: 'presented-event', seq: 0, time: 1_700_000_000_000,
presentationKey: 'schedule/reminder',
eventType: 'schedule/change',
view: { id: 'schedule-1', prompt: '检查日志' },
},
{
kind: 'presented-event', seq: 1, time: 1_700_000_000_001,
presentationKey: 'schedule/reminder',
eventType: 'schedule/change',
view: { id: 'schedule-2', prompt: '检查发布' },
},
])