feat(tui): add /status session diagnostics

This commit is contained in:
ZiyaZhang
2026-07-22 03:10:14 -07:00
parent 988dd70e7c
commit 280207c824
13 changed files with 316 additions and 42 deletions
+9 -5
View File
@@ -24,6 +24,8 @@ interface FakeAgent extends Agent {
export interface TuiHarnessOptions {
status?: AgentStatus
config?: Config
/** Leave the session event log empty instead of seeding one turn and step. */
omitInitialLifecycle?: boolean
/** Omit the harness's default `welcome`, exercising the banner sweep-reveal path. */
omitWelcome?: boolean
tools?: Record<string, ToolDefinition>
@@ -120,11 +122,13 @@ export async function createTuiTestHarness<TerminalType extends Terminal, Exit e
sessionId,
options.cwd === null ? undefined : { meta: { cwd: options.cwd ?? '/workspace' } },
)
session.append('turn/start', {
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
session.append('step/start', { turn: 1, step: 1 })
if (options.omitInitialLifecycle !== true) {
session.append('turn/start', {
turn: 1,
trigger: { kind: 'message', source: { kind: 'user' } },
})
session.append('step/start', { turn: 1, step: 1 })
}
options.beforeMount?.(session)
const sent: ContentBlock[][] = []
const steered: ContentBlock[][] = []
@@ -1,7 +1,7 @@
terminal 92x32 buffer=normal length=32 base=0 viewport=0
lifecycle started=1 stopped=1 progress=inactive
title "DSH snapshot"
cursor visible column=0 viewportRow=30 bufferRow=30
cursor visible column=0 viewportRow=31 bufferRow=31
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-blue bold
@@ -36,26 +36,28 @@ buffer
style 1-88 fg=bright-black
16| " /resume — List this workspace's resumable sessions "
style 1-50 fg=bright-black
17| " /tools — Expand or collapse all tool cards "
17| " /status — Show detailed session diagnostics "
style 1-43 fg=bright-black
18| " /tools — Expand or collapse all tool cards "
style 1-42 fg=bright-black
18| " /skill:<name> [instructions] — load a skill into the conversation "
19| " /skill:<name> [instructions] — load a skill into the conversation "
style 1-65 fg=bright-black
19| <blank>
20| " provider stream failed after partial output "
20| <blank>
21| " provider stream failed after partial output "
style 1-43 fg=red
21| <blank>
22| " The previous process ended during this turn. "
22| <blank>
23| " The previous process ended during this turn. "
style 1-44 fg=yellow
23| <blank>
24| " Unknown command: /unknown-advanced-command "
24| <blank>
25| " Unknown command: /unknown-advanced-command "
style 1-42 fg=yellow
25| "────────────────────────────────────────────────────────────────────────────────────────────"
26| "────────────────────────────────────────────────────────────────────────────────────────────"
style 0-91 dim
26| " "
27| " "
style 1-1 inverse
27| "────────────────────────────────────────────────────────────────────────────────────────────"
28| "────────────────────────────────────────────────────────────────────────────────────────────"
style 0-91 dim
28| "deepseek-v4-flash /workspace/project ↑0 ↓0 0% context tools:collapsed"
29| "deepseek-v4-flash /workspace/project ↑0 ↓0 0% context tools:collapsed"
style 0-43 dim
style 65-91 dim
29-31| <blank>
30-31| <blank>
@@ -1,7 +1,7 @@
terminal 92x32 buffer=normal length=32 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "DSH snapshot"
cursor hidden column=1 viewportRow=26 bufferRow=26
cursor hidden column=1 viewportRow=27 bufferRow=27
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-blue bold
@@ -36,26 +36,28 @@ buffer
style 1-88 fg=bright-black
16| " /resume — List this workspace's resumable sessions "
style 1-50 fg=bright-black
17| " /tools — Expand or collapse all tool cards "
17| " /status — Show detailed session diagnostics "
style 1-43 fg=bright-black
18| " /tools — Expand or collapse all tool cards "
style 1-42 fg=bright-black
18| " /skill:<name> [instructions] — load a skill into the conversation "
19| " /skill:<name> [instructions] — load a skill into the conversation "
style 1-65 fg=bright-black
19| <blank>
20| " provider stream failed after partial output "
20| <blank>
21| " provider stream failed after partial output "
style 1-43 fg=red
21| <blank>
22| " The previous process ended during this turn. "
22| <blank>
23| " The previous process ended during this turn. "
style 1-44 fg=yellow
23| <blank>
24| " Unknown command: /unknown-advanced-command "
24| <blank>
25| " Unknown command: /unknown-advanced-command "
style 1-42 fg=yellow
25| "────────────────────────────────────────────────────────────────────────────────────────────"
26| "────────────────────────────────────────────────────────────────────────────────────────────"
style 0-91 dim
26| " "
27| " "
style 1-1 inverse
27| "────────────────────────────────────────────────────────────────────────────────────────────"
28| "────────────────────────────────────────────────────────────────────────────────────────────"
style 0-91 dim
28| "deepseek-v4-flash /workspace/project ↑0 ↓0 0% context tools:collapsed"
29| "deepseek-v4-flash /workspace/project ↑0 ↓0 0% context tools:collapsed"
style 0-43 dim
style 65-91 dim
29-31| <blank>
30-31| <blank>
@@ -0,0 +1,79 @@
terminal 92x32 buffer=normal length=32 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "Inspect session diagnostics — DSH snapshot"
cursor hidden column=1 viewportRow=27 bufferRow=27
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-blue bold
style 10-16 bold
1| " Inspect session diagnostics"
style 1-27 fg=bright-black
2| " deepseek-v4-pro • main-session"
style 1-32 dim
3| <blank>
4| "▌ "
style 0-0 fg=bright-blue
5| "▌ You "
style 0-0 fg=bright-blue
style 2-4 fg=bright-blue bold
6| "▌ inspect this session "
style 0-0 fg=bright-blue
7| "▌ "
style 0-0 fg=bright-blue
8| <blank>
9| " Assistant "
style 1-9 fg=bright-magenta bold
10| " Session inspected. "
11| <blank>
12| "▌ Session diagnostics "
style 0-0 fg=bright-blue
style 2-20 fg=bright-blue bold
13| "▌ Session main-session "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
14| "▌ Title Inspect session diagnostics "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
15| "▌ Working dir /workspace/project "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
16| "▌ Model deepseek/deepseek-v4-pro "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
17| "▌ Reasoning view shown "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
18| "▌ Agent idle "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
19| "▌ Activity events 6 · turns 1 · steps 1 · tool calls 1 "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
20| "▌ Tokens input 1,250 · output 340 "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
21| "▌ Cache tokens read 3,000 · write 250 "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
22| "▌ KV cache hit 67% "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
23| "▌ Context 42,000 / 128,000 (33%) "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
24| "▌ Created 2026-07-22T09:10:11.000Z "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
25| "▌ Last active 2026-07-22T09:10:11.000Z "
style 0-0 fg=bright-blue
style 2-15 fg=bright-black
26| "────────────────────────────────────────────────────────────────────────────────────────────"
style 0-91 dim
27| " "
style 1-1 inverse
28| "────────────────────────────────────────────────────────────────────────────────────────────"
style 0-91 dim
29| "deepseek-v4-pro /workspace/project ↑1.3k ↓340 cache 67% 33% context tools:collapsed"
style 0-57 dim
style 64-91 dim
30-31| <blank>
+39 -1
View File
@@ -1,7 +1,7 @@
import { mkdir, readdir, writeFile } from 'node:fs/promises'
import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterAll, describe, expect, it } from 'vitest'
import { afterAll, describe, expect, it, vi } from 'vitest'
import type { Context } from 'cordis'
import { agentEvents } from '@deepseek-ai/dsh-agent'
import { CallId, type ContentBlock } from '@deepseek-ai/dsh-llm'
@@ -48,6 +48,7 @@ const CHECKPOINTS = [
'errors-and-help',
'disposed-terminal',
'resume-sessions',
'status-diagnostics',
] as const
type Checkpoint = typeof CHECKPOINTS[number]
@@ -637,6 +638,43 @@ describe('TUI terminal-state snapshots', () => {
await checkpoint('resume-sessions', harness.terminal, { includeScrollback: true })
await disposeSnapshot(harness)
})
it('pins the detailed session diagnostics card', async () => {
const dateNow = vi.spyOn(Date, 'now').mockReturnValue(Date.parse('2026-07-22T09:10:11.000Z'))
const harness = await setupSnapshot({
contextWindow: 128_000,
contextTokens: 42_000,
agentOptions: { provider: 'deepseek', model: 'deepseek-v4-pro' },
beforeMount(session) {
appendUser(session, 'inspect this session')
appendAssistant(session, [{ type: 'text', text: 'Session inspected.' }], {
inputTokens: 1_250,
outputTokens: 340,
cacheReadTokens: 3_000,
cacheWriteTokens: 250,
})
session.append('tool/call', {
turn: 1,
step: 1,
callId: CallId('status-call'),
name: 'read',
arguments: '{"path":"README.md"}',
})
session.append('session/title', {
title: 'Inspect session diagnostics',
messageSeqs: [1],
source: { kind: 'fallback' },
})
},
}, { columns: 92, rows: 32 })
await renderAfter(harness, () => {
harness.terminal.send('/status')
harness.terminal.send('\r')
})
await checkpoint('status-diagnostics', harness.terminal, { includeScrollback: true })
await disposeSnapshot(harness)
dateNow.mockRestore()
})
})
afterAll(async () => {
+86
View File
@@ -845,6 +845,92 @@ describe('pi-tui chat lifecycle and transcript', () => {
await dispose(result)
})
it('shows detailed session diagnostics while the agent is running', async () => {
const timestamp = Date.parse('2026-07-22T09:10:11.000Z')
const dateNow = vi.spyOn(Date, 'now').mockReturnValue(timestamp)
const result = await setup({
cwd: '/workspace/status',
contextWindow: 128_000,
contextTokens: 42_000,
config: { showReasoning: false },
agentOptions: { provider: 'deepseek', model: 'deepseek-v4-pro' },
beforeMount(session) {
session.append('session/title', {
title: 'Inspect status \u001B]2;unsafe\u0007',
messageSeqs: [1],
source: { kind: 'fallback' },
})
appendAssistant(session, [{ type: 'text', text: 'measured' }], {
inputTokens: 1_250,
outputTokens: 340,
cacheReadTokens: 3_000,
cacheWriteTokens: 250,
})
session.append('tool/call', {
turn: 1, step: 1, callId: 'status-call-1' as never, name: 'read', arguments: '{}',
})
session.append('tool/call', {
turn: 1, step: 1, callId: 'status-call-2' as never, name: 'write', arguments: '{}',
})
},
})
result.agent.status = 'running'
agentEvents(result.ctx, result.agent).emit('agent/status', 'running')
result.terminal.send('/status')
result.terminal.send('\r')
await tick()
expect(result.terminal.output).toContain('Session diagnostics')
expect(result.terminal.output).toContain('Session main-session')
expect(result.terminal.output).toContain('Title Inspect status \\x1b]2;unsafe\\x07')
expect(result.terminal.output).toContain('Working dir /workspace/status')
expect(result.terminal.output).toContain('Model deepseek/deepseek-v4-pro')
expect(result.terminal.output).toContain('Reasoning view hidden')
expect(result.terminal.output).toContain('Agent running')
expect(result.terminal.output).toContain('Activity events 6 · turns 1 · steps 1 · tool calls 2')
expect(result.terminal.output).toContain('Tokens input 1,250 · output 340')
expect(result.terminal.output).toContain('Cache tokens read 3,000 · write 250')
expect(result.terminal.output).toContain('KV cache hit 67%')
expect(result.terminal.output).toContain('Context 42,000 / 128,000 (33%)')
expect(result.terminal.output).toContain('Created 2026-07-22T09:10:11.000Z')
expect(result.terminal.output).toContain('Last active 2026-07-22T09:10:11.000Z')
expect(result.terminal.output).not.toContain('\u001B]2;unsafe\u0007')
await dispose(result)
dateNow.mockRestore()
})
it('labels unavailable status diagnostics without inventing values', async () => {
const timestamp = Date.parse('2026-07-22T10:11:12.000Z')
const dateNow = vi.spyOn(Date, 'now').mockReturnValue(timestamp)
const result = await setup({
cwd: null,
omitInitialLifecycle: true,
contextTokens: 7,
agentOptions: {},
catalog: {
providers: [],
models: [],
resolveModelContext: () => Promise.resolve(undefined),
},
})
result.terminal.send('/status')
result.terminal.send('\r')
await tick()
expect(result.terminal.output).toContain('Title untitled')
expect(result.terminal.output).toContain('Model unset')
expect(result.terminal.output).toContain('Reasoning view shown')
expect(result.terminal.output).toContain('Agent idle')
expect(result.terminal.output).toContain('Activity events 0 · turns 0 · steps 0 · tool calls 0')
expect(result.terminal.output).toContain('KV cache hit n/a')
expect(result.terminal.output).toContain('Context 7 used · capacity unknown')
expect(result.terminal.output).toContain('Created 2026-07-22T10:11:12.000Z')
expect(result.terminal.output).toContain('Last active 2026-07-22T10:11:12.000Z')
await dispose(result)
dateNow.mockRestore()
})
it('sends, steers, handles commands, global keys, and disposed-agent input', async () => {
const result = await setup()