feat(schedule): add durable after reminders

This commit is contained in:
pku-xht
2026-08-05 19:00:02 +08:00
committed by Tianyi Cui
parent a229b42e24
commit f7e7851e3f
102 changed files with 2619 additions and 122 deletions

View File

@@ -53,7 +53,7 @@ describe('apply wiring', () => {
await b.runtime.dispose()
})
it('registers the chat view as the first ring entry, declaring the whole-Tool seat', async () => {
it('registers the chat view as the first ring entry with Tool and event seats', async () => {
const b = await bench()
const entries = b.slots.entries('conversation.view')
expect(entries.map(e => e.options.id)).toEqual(['chat'])
@@ -63,6 +63,7 @@ describe('apply wiring', () => {
// Declaring is claiming: the chat entry's registration put the hole on
// the ledger with the contract's kind/scope.
expect(b.slots.spec('conversation.chat.tool')).toEqual({ kind: 'single', scope: 'session' })
expect(b.slots.spec('conversation.chat.eventview')).toEqual({ kind: 'keyed', scope: 'session' })
await b.runtime.dispose()
})
@@ -110,6 +111,8 @@ describe('apply wiring', () => {
expect(b.slots.entries('conversation.view')).toHaveLength(0)
expect(b.slots.entries('conversation.chat.tool')).toHaveLength(0)
expect(b.slots.spec('conversation.chat.tool')).toBeUndefined()
expect(b.slots.entries('conversation.chat.eventview')).toHaveLength(0)
expect(b.slots.spec('conversation.chat.eventview')).toBeUndefined()
expect(b.slots.entries('details')).toHaveLength(0)
expect(b.slots.entries('settings.general.item')).toHaveLength(0)
expect(b.runtime.ctx.get('conversation')).toBeUndefined()

View File

@@ -8,7 +8,7 @@ import { Profiler } from 'react'
import { act, cleanup, fireEvent, render, within } from '@testing-library/react'
import type {
AssistantMessageNode, CommandNode, CompactionSummaryNode, ConversationNode, ConversationSnapshot,
ModelRetryNode, RunningToolCall, SessionId, SessionListState, ToolResultNode, TurnErrorNode,
ModelRetryNode, PresentedEventNode, RunningToolCall, SessionId, SessionListState, ToolResultNode, TurnErrorNode,
UserMessageNode, WorkspaceListState,
} from '@deepseek-ai/dsh-client-runtime/client'
import { bindSnapshotSelector } from '@deepseek-ai/dsh-client-web-react'
@@ -106,6 +106,13 @@ const compaction = (over: Partial<CompactionSummaryNode> = {}): CompactionSummar
shadowedTokenCount: 11_309,
...over,
})
const presentedEvent = (seq: number): PresentedEventNode => ({
kind: 'presented-event',
seq,
time: seq * 1_000,
presentationKey: 'schedule/reminder',
view: { prompt: 'check logs', scheduleId: 'schedule-1' },
})
/** Empty sessions-list hook for the global standard-kit seat. */
function emptySessions() {
@@ -953,6 +960,21 @@ describe('ChatView', () => {
expect(calls[0]?.entryKey).toBeUndefined()
})
it('dispatches presented events by key and keeps a visible JSON fallback', () => {
const node = presentedEvent(3)
const h = makeHarness({ nodes: [node] })
const calls: { key: string; entryKey?: string }[] = []
h.props.renderSlot = ((key: string, _owner: object, opts?: { entryKey?: string; fallback?: React.ReactNode }) => {
calls.push({ key, ...(opts?.entryKey !== undefined ? { entryKey: opts.entryKey } : {}) })
return opts?.fallback ?? null
})
const view = render(<h.ChatView {...h.props} />)
expect(calls).toEqual([{ key: 'conversation.chat.eventview', entryKey: 'schedule/reminder' }])
fireEvent.click(view.getByText('事件:schedule/reminder'))
expect(view.getByText(/"prompt": "check logs"/)).toBeTruthy()
expect(view.getByText(/"scheduleId": "schedule-1"/)).toBeTruthy()
})
it('prepend preserves a semantic row; a trailing user node force-scrolls', () => {
const h = makeHarness({ nodes: [user(5, 'later'), assistant(6, 'a')], hasMore: true })
const view = render(<h.ChatView {...h.props} />)