feat(tui): /details command sets card visibility and reasoning display

/details reports the transcript detail state bare, jumps tool cards to
collapsed|expanded|hidden, and sets or toggles reasoning blocks, sharing
the closure state behind Ctrl+O/Ctrl+R via setToolsVisibility/setReasoning.

Also fixes a replay defect the reasoning rebuild exposed: rebuildTranscript
reused a settled StreamingAssistantComponent for a later assistant/message
of the same step, overwriting the earlier content; the settled check now
lives in renderEvent for both live and replay paths (untrusted-controls
re-recorded with the previously dropped content present).
This commit is contained in:
Turtle
2026-07-30 18:26:26 +08:00
parent 651417e01c
commit 3899a26c87
12 changed files with 316 additions and 90 deletions
@@ -0,0 +1,45 @@
terminal 100x40 buffer=normal length=40 base=0 viewport=0
lifecycle started=1 stopped=0 progress=inactive
title "DSH snapshot"
cursor hidden column=7 viewportRow=19 bufferRow=19
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
style 10-16 bold
1| " Snapshot agent ready."
style 1-21 dim
2| " main-session"
style 1-12 dim
3| <blank>
4| "Assistant "
style 0-8 fg=bright-magenta bold underline
5| "Running the check now. "
6| "Model wait 0.0s "
style 0-14 dim
7| <blank>
8| "You "
style 0-2 fg=bright-magenta bold underline
9| "Inspect the renderer. "
10| "Model wait 0.0s · Completed 2026-07-30 18:00:00 "
style 0-46 dim
11| <blank>
12| "Reasoning blocks hidden. "
style 0-23 dim
13| <blank>
14| "Tool cards hidden. "
style 0-17 dim
15| <blank>
16| "Tool and context cards hidden; reasoning blocks hidden. "
style 0-54 dim
17| <blank>
18| "/workspace/project (tui-staging) deepseek-v4-flash ↑0 ↓0 0% context"
style 0-17 fg=bright-magenta bold
style 18-31 dim
style 34-50 dim
style 53-57 dim
style 60-69 dim
19| " dsh > "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
20-39| <blank>
@@ -1,7 +1,7 @@
terminal 92x32 buffer=normal length=37 base=5 viewport=5
terminal 92x32 buffer=normal length=39 base=7 viewport=7
lifecycle started=1 stopped=1 progress=inactive
title "DSH snapshot"
cursor visible column=0 viewportRow=31 bufferRow=36
cursor visible column=0 viewportRow=31 bufferRow=38
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
@@ -29,48 +29,52 @@ buffer
12| " "
13| "/clear — Clear the transcript view (session history is unchanged) "
style 0-64 dim
14| "/exit — Exit after the active turn reaches idle "
14| "/details [collapsed|expanded|hidden] [reasoning [on|off]] — Show or set tool-card visibility"
style 0-91 dim
15| "and reasoning display "
style 0-20 dim
16| "/exit — Exit after the active turn reaches idle "
style 0-46 dim
15| "/help — Show keyboard shortcuts and commands "
17| "/help — Show keyboard shortcuts and commands "
style 0-43 dim
16| "/model [[provider/]model] — Show or switch this session's model "
18| "/model [[provider/]model] — Show or switch this session's model "
style 0-62 dim
17| "/palette — Show every color and attribute role this terminal renders "
19| "/palette — Show every color and attribute role this terminal renders "
style 0-67 dim
18| "/quit — Exit after the active turn reaches idle "
20| "/quit — Exit after the active turn reaches idle "
style 0-46 dim
19| "/reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
21| "/reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
style 0-87 dim
20| "/resume — List this workspace's resumable sessions "
22| "/resume — List this workspace's resumable sessions "
style 0-49 dim
21| "/status — Show session diagnostics, system prompt, and registered tools "
23| "/status — Show session diagnostics, system prompt, and registered tools "
style 0-70 dim
22| "/skill:<name> [instructions] — load a skill into the conversation "
24| "/skill:<name> [instructions] — load a skill into the conversation "
style 0-64 dim
23| <blank>
24| "provider stream failed after partial output "
style 0-42 fg=red
25| <blank>
26| "The previous process ended during this turn. "
style 0-43 fg=yellow
26| "provider stream failed after partial output "
style 0-42 fg=red
27| <blank>
28| "Turn stopped: the agent was disposed. "
style 0-36 fg=yellow
28| "The previous process ended during this turn. "
style 0-43 fg=yellow
29| <blank>
30| "Turn ended: plugin-policy. "
style 0-25 fg=yellow
30| "Turn stopped: the agent was disposed. "
style 0-36 fg=yellow
31| <blank>
32| "Unknown command: /unknown-advanced-command "
style 0-41 fg=yellow
32| "Turn ended: plugin-policy. "
style 0-25 fg=yellow
33| <blank>
34| "/workspace/project (tui-staging) deepseek-v4-flash ↑0 ↓0 0% context"
34| "Unknown command: /unknown-advanced-command "
style 0-41 fg=yellow
35| <blank>
36| "/workspace/project (tui-staging) deepseek-v4-flash ↑0 ↓0 0% context"
style 0-17 fg=bright-magenta bold
style 18-31 dim
style 34-50 dim
style 53-57 dim
style 60-69 dim
35| " dsh > "
37| " dsh > "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
36| <blank>
38| <blank>
@@ -1,7 +1,7 @@
terminal 92x32 buffer=normal length=36 base=4 viewport=4
terminal 92x32 buffer=normal length=38 base=6 viewport=6
lifecycle started=1 stopped=0 progress=inactive
title "DSH snapshot"
cursor hidden column=7 viewportRow=31 bufferRow=35
cursor hidden column=7 viewportRow=31 bufferRow=37
buffer
0| " DEEPSEEK HARNESS"
style 1-8 fg=bright-magenta bold
@@ -29,47 +29,51 @@ buffer
12| " "
13| "/clear — Clear the transcript view (session history is unchanged) "
style 0-64 dim
14| "/exit — Exit after the active turn reaches idle "
14| "/details [collapsed|expanded|hidden] [reasoning [on|off]] — Show or set tool-card visibility"
style 0-91 dim
15| "and reasoning display "
style 0-20 dim
16| "/exit — Exit after the active turn reaches idle "
style 0-46 dim
15| "/help — Show keyboard shortcuts and commands "
17| "/help — Show keyboard shortcuts and commands "
style 0-43 dim
16| "/model [[provider/]model] — Show or switch this session's model "
18| "/model [[provider/]model] — Show or switch this session's model "
style 0-62 dim
17| "/palette — Show every color and attribute role this terminal renders "
19| "/palette — Show every color and attribute role this terminal renders "
style 0-67 dim
18| "/quit — Exit after the active turn reaches idle "
20| "/quit — Exit after the active turn reaches idle "
style 0-46 dim
19| "/reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
21| "/reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
style 0-87 dim
20| "/resume — List this workspace's resumable sessions "
22| "/resume — List this workspace's resumable sessions "
style 0-49 dim
21| "/status — Show session diagnostics, system prompt, and registered tools "
23| "/status — Show session diagnostics, system prompt, and registered tools "
style 0-70 dim
22| "/skill:<name> [instructions] — load a skill into the conversation "
24| "/skill:<name> [instructions] — load a skill into the conversation "
style 0-64 dim
23| <blank>
24| "provider stream failed after partial output "
style 0-42 fg=red
25| <blank>
26| "The previous process ended during this turn. "
style 0-43 fg=yellow
26| "provider stream failed after partial output "
style 0-42 fg=red
27| <blank>
28| "Turn stopped: the agent was disposed. "
style 0-36 fg=yellow
28| "The previous process ended during this turn. "
style 0-43 fg=yellow
29| <blank>
30| "Turn ended: plugin-policy. "
style 0-25 fg=yellow
30| "Turn stopped: the agent was disposed. "
style 0-36 fg=yellow
31| <blank>
32| "Unknown command: /unknown-advanced-command "
style 0-41 fg=yellow
32| "Turn ended: plugin-policy. "
style 0-25 fg=yellow
33| <blank>
34| "/workspace/project (tui-staging) deepseek-v4-flash ↑0 ↓0 0% context"
34| "Unknown command: /unknown-advanced-command "
style 0-41 fg=yellow
35| <blank>
36| "/workspace/project (tui-staging) deepseek-v4-flash ↑0 ↓0 0% context"
style 0-17 fg=bright-magenta bold
style 18-31 dim
style 34-50 dim
style 53-57 dim
style 60-69 dim
35| " dsh > "
37| " dsh > "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
@@ -13,46 +13,41 @@ buffer
3| <blank>
4| "Assistant "
style 0-8 fg=bright-magenta bold underline
5| <blank>
6| "You "
5| "Reasoning "
style 0-8 dim italic
6| "Unsafe reasoning \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
style 0-61 dim italic
7| "Unsafe assistant \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
8| "Model wait 0.0s "
style 0-14 dim
9| <blank>
10| "You "
style 0-2 fg=bright-magenta bold underline
7| "Unsafe user \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
8| <blank>
9| "● Tool / unsafe / Unsafe description \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m"
11| "Unsafe user \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
12| <blank>
13| "Assistant "
style 0-8 fg=bright-magenta bold underline
14| <blank>
15| "● Tool / unsafe / Unsafe description \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m"
style 0-81 fg=green
10| "$ Unsafe title \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
16| "$ Unsafe title \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
style 0-59 dim
11| "/unsafe/\\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
17| "/unsafe/\\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
style 0-52 dim
12| "Unsafe output \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
18| "Unsafe output \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
style 0-58 dim
13| "[signal SIG\\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m] "
19| "[signal SIG\\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m] "
style 0-56 fg=red
14| "Model wait 0.0s · Completed 2026-07-21 15:00:00 "
20| "Model wait 0.0s · Completed 2026-07-21 15:00:00 "
style 0-46 dim
15| <blank>
16| "Context · unsafe-\\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m"
21| <blank>
22| "Context · unsafe-\\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m"
style 0-61 dim
17| "Unsafe context \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
23| "Unsafe context \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
style 0-59 dim
18| <blank>
19| "Unsafe turn error \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
24| <blank>
25| "Unsafe turn error \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
style 0-62 fg=red
20-21| <blank>
22| "Plan"
style 0-3 fg=bright-magenta bold
23| " ● Unsafe todo \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m"
style 2-2 fg=yellow
24| "/workspace/project (tui-staging) deepseek-v4-flash ↑0 ↓0 0% context"
style 0-17 fg=bright-magenta bold
style 18-31 dim
style 34-50 dim
style 53-57 dim
style 60-69 dim
25| " dsh > "
style 1-3 fg=bright-magenta bold
style 5-6 dim
style 7-7 inverse
26| " "
27| " Question 1/1 (1 unanswered) · Unsafe header \\x1b]2;snapshot-controlled\\x07\\x09\\x7f\\x9b31m "
style 2-90 dim
+33
View File
@@ -45,6 +45,7 @@ const CHECKPOINTS = [
'advanced-cards-collapsed',
'advanced-cards-expanded',
'tool-cards-hidden-folded',
'details-command',
'untrusted-controls',
'question-dialog',
'question-dialog-single-option',
@@ -637,6 +638,38 @@ describe('TUI terminal-state snapshots', () => {
await disposeSnapshot(harness)
})
it('pins /details jumping card visibility and reasoning display to named states', async () => {
const nowSpy = vi.spyOn(Date, 'now').mockReturnValue(new Date(2026, 6, 30, 18, 0, 0).getTime())
const harness = await setupSnapshot({
tools: ADVANCED_CARD_TOOLS,
config: { maxToolOutputLines: 3 },
}, { columns: 100, rows: 40 })
await renderAfter(harness, () => {
appendUser(harness.session, 'Inspect the renderer.')
appendAssistant(harness.session, [
{ type: 'reasoning', text: 'The tool card and this block vanish under /details hidden reasoning off.' },
{ type: 'text', text: 'Running the check now.' },
])
appendToolCalls(harness.session, [
{ id: 'details-1', name: 'bash', arguments: { command: 'pnpm run test' } },
])
appendToolResult(harness.session, 'details-1', [{ type: 'text', text: 'all tests pass' }])
harness.session.append('step/end', { turn: 1, step: 1 })
harness.session.append('turn/end', { turn: 1, reason: { kind: 'completed' } })
})
await renderAfter(harness, () => {
harness.terminal.send('/details hidden reasoning off')
harness.terminal.send('\r')
})
await renderAfter(harness, () => {
harness.terminal.send('/details')
harness.terminal.send('\r')
})
await checkpoint('details-command', harness.terminal, { includeScrollback: true })
nowSpy.mockRestore()
await disposeSnapshot(harness)
})
it('renders terminal controls as inert text across transcripts, tools, dialogs, diagnostics, and title', async () => {
const nowSpy = vi.spyOn(Date, 'now').mockReturnValue(new Date(2026, 6, 21, 15, 0, 0).getTime())
const tools = {
+33
View File
@@ -2510,6 +2510,39 @@ describe('pi-tui chat lifecycle and transcript', () => {
await dispose(result)
})
it('/details reports and sets card visibility and reasoning display', async () => {
const result = await setup()
const run = async (line: string): Promise<void> => {
result.terminal.send(line)
result.terminal.send('\r')
await tick()
}
await run('/details')
expect(result.terminal.output).toContain('Tool and context cards collapsed; reasoning blocks shown.')
await run('/details hidden')
expect(result.terminal.output).toContain('Tool cards hidden.')
await run('/details expanded reasoning off')
expect(result.terminal.output).toContain('Tool and context cards expanded.')
expect(result.terminal.output).toContain('Reasoning blocks hidden.')
await run('/details reasoning on')
expect(result.terminal.output).toContain('Reasoning blocks shown.')
// Bare `reasoning` toggles: shown -> hidden, confirmed by the status line.
await run('/details reasoning')
await run('/details collapsed')
await run('/details')
expect(result.terminal.output).toContain('Tool and context cards collapsed; reasoning blocks hidden.')
await run('/details bogus')
expect(result.terminal.output).toContain('Unknown /details argument "bogus"')
await dispose(result)
})
it('sends, steers, handles commands, global keys, and disposed-agent input', async () => {
const result = await setup()