feat(tui): session resume — /resume command, exit hint, and dsh --resume <id>
Squashes feat/tui-resume-command, fix/tui-resume-desc, and feat/tui-resume-flag.
This commit is contained in:
@@ -7,6 +7,7 @@ import { agentEvents } from '@deepseek-ai/dsh-agent'
|
||||
import { CallId, type ContentBlock } from '@deepseek-ai/dsh-llm'
|
||||
import type {} from '@deepseek-ai/dsh-llm-retry'
|
||||
import type { Session } from '@deepseek-ai/dsh-session'
|
||||
import { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import SystemPrompt from '@deepseek-ai/dsh-system-prompt'
|
||||
import ToolRegistry, { type ToolDefinition, type ToolResultView } from '@deepseek-ai/dsh-tools'
|
||||
import * as ToolCordis from '@deepseek-ai/dsh-tool-cordis'
|
||||
@@ -30,6 +31,7 @@ const CHECKPOINTS = [
|
||||
'retry-recovered',
|
||||
'retry-cancelled',
|
||||
'retry-exhausted',
|
||||
'banner-gradient',
|
||||
'code-mode-pending',
|
||||
'dynamic-workflow-pending',
|
||||
'cordis-tools-pending',
|
||||
@@ -45,6 +47,7 @@ const CHECKPOINTS = [
|
||||
'model-switching',
|
||||
'errors-and-help',
|
||||
'disposed-terminal',
|
||||
'resume-sessions',
|
||||
] as const
|
||||
|
||||
type Checkpoint = typeof CHECKPOINTS[number]
|
||||
@@ -56,9 +59,23 @@ async function checkpoint(
|
||||
name: Checkpoint,
|
||||
terminal: HeadlessTerminal,
|
||||
options: TerminalSnapshotOptions = {},
|
||||
bannerGradient = false,
|
||||
): Promise<void> {
|
||||
observedCheckpoints.add(name)
|
||||
expect(terminal.themeViolations(), `${name} must remain theme-agnostic`).toEqual([])
|
||||
const violations = terminal.themeViolations()
|
||||
if (bannerGradient) {
|
||||
// The banner paints its product name in the DeepSeek brand gradient with
|
||||
// 24-bit foreground codes: the sole sanctioned truecolor. Require it to be
|
||||
// present and to never leak a background or extended-palette color into the
|
||||
// otherwise theme-agnostic UI.
|
||||
expect(violations, `${name} must render the banner gradient`).not.toEqual([])
|
||||
expect(
|
||||
violations.every(entry => entry.endsWith('rgb-fg')),
|
||||
`${name} must confine truecolor to the banner foreground`,
|
||||
).toBe(true)
|
||||
} else {
|
||||
expect(violations, `${name} must remain theme-agnostic`).toEqual([])
|
||||
}
|
||||
const snapshot = await terminal.snapshot(options)
|
||||
const path = join(SNAPSHOTS_DIR, `${name}.expected.txt`)
|
||||
if (REFRESHING) {
|
||||
@@ -308,6 +325,12 @@ describe('TUI terminal-state snapshots', () => {
|
||||
await disposeSnapshot(harness)
|
||||
})
|
||||
|
||||
it('paints the startup banner product name in the DeepSeek brand gradient on truecolor terminals', async () => {
|
||||
const harness = await setupSnapshot({ config: { truecolor: true } })
|
||||
await checkpoint('banner-gradient', harness.terminal, {}, true)
|
||||
await disposeSnapshot(harness)
|
||||
})
|
||||
|
||||
it('pins Code Mode run_code with its production presenter', async () => {
|
||||
const harness = await setupSnapshot({ configureContext: configureAdvancedTools })
|
||||
const call = {
|
||||
@@ -596,6 +619,24 @@ describe('TUI terminal-state snapshots', () => {
|
||||
await checkpoint('model-switching', harness.terminal, { includeScrollback: true })
|
||||
await disposeSnapshot(harness)
|
||||
})
|
||||
|
||||
it('lists this workspace\'s resumable sessions with their commands', async () => {
|
||||
const harness = await setupSnapshot({
|
||||
config: { resumeCommand: 'RESUME_SESSION_ID={session} dsh' },
|
||||
sessionPersistence: { list: async () => [
|
||||
{ version: 0, id: SessionId('main-session'), createdAt: Date.parse('2024-01-02T03:04:00Z'), cwd: '/workspace/project' },
|
||||
{ version: 0, id: SessionId('earlier-session'), createdAt: Date.parse('2024-01-01T00:00:00Z'), cwd: '/workspace/project' },
|
||||
] },
|
||||
}, { columns: 92, rows: 32 })
|
||||
harness.terminal.send('/resume')
|
||||
harness.terminal.send('\r')
|
||||
// `/resume` scans persistence asynchronously, so the listing renders a tick
|
||||
// after submit (the unit suite waits the same way); settle, then flush.
|
||||
await new Promise(resolve => setTimeout(resolve, 60))
|
||||
await harness.terminal.flush()
|
||||
await checkpoint('resume-sessions', harness.terminal, { includeScrollback: true })
|
||||
await disposeSnapshot(harness)
|
||||
})
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
|
||||
Reference in New Issue
Block a user