feat: durable command lifecycle logging in the executor (command/run + command/done)
CommandService.execute appends the log-only pair around every resolved handler — run before invocation, done at settlement, including thrown and aborted handlers (kind:'error'); admission misses log nothing. commandId is minted monotonically per instance; per-session appends serialize through a tail queue over SessionStore.appendOutOfBand (zero-step wrap on an idle log, direct join inside an open turn). The invariant companion now asserts the pairing relation (unique run ids; a done requires a prior in-log run). CommandSource is a minimal merge-extensible map (user variant only). Dependent benches mount SessionStore; TUI/e2e snapshots re-recorded for the executor's durable-append timing and the /status event counts.
This commit is contained in:
@@ -11,46 +11,46 @@ buffer
|
||||
2| " deepseek-v4-flash • main-session"
|
||||
style 1-34 dim
|
||||
3| <blank>
|
||||
4| " Keyboard shortcuts "
|
||||
style 1-18 fg=bright-blue bold
|
||||
5| " Enter send • Shift/Alt+Enter newline • Up/Down prompt history "
|
||||
style 1-61 fg=bright-black
|
||||
6| " Esc cancel active turn • Ctrl+O toggle tool cards • Ctrl+R toggle reasoning "
|
||||
style 1-75 fg=bright-black
|
||||
7| " Ctrl+C cancel while running; clear input or exit while idle • Ctrl+D exit "
|
||||
style 1-73 fg=bright-black
|
||||
8| " "
|
||||
9| " /clear — Clear the transcript view (session history is unchanged) "
|
||||
style 1-65 fg=bright-black
|
||||
10| " /exit — Exit after the active turn reaches idle "
|
||||
style 1-47 fg=bright-black
|
||||
11| " /help — Show keyboard shortcuts and commands "
|
||||
style 1-44 fg=bright-black
|
||||
12| " /model [[provider/]model] — Show or switch this session's model "
|
||||
style 1-63 fg=bright-black
|
||||
13| " /reasoning — Toggle reasoning blocks "
|
||||
style 1-36 fg=bright-black
|
||||
14| " /redraw — Invalidate components and redraw the terminal "
|
||||
style 1-55 fg=bright-black
|
||||
15| " /reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
|
||||
style 1-88 fg=bright-black
|
||||
16| " /resume — List this workspace's resumable sessions "
|
||||
style 1-50 fg=bright-black
|
||||
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
|
||||
19| " /skill:<name> [instructions] — load a skill into the conversation "
|
||||
style 1-65 fg=bright-black
|
||||
20| <blank>
|
||||
21| " provider stream failed after partial output "
|
||||
4| " provider stream failed after partial output "
|
||||
style 1-43 fg=red
|
||||
22| <blank>
|
||||
23| " The previous process ended during this turn. "
|
||||
5| <blank>
|
||||
6| " The previous process ended during this turn. "
|
||||
style 1-44 fg=yellow
|
||||
24| <blank>
|
||||
25| " Unknown command: /unknown-advanced-command "
|
||||
7| <blank>
|
||||
8| " Unknown command: /unknown-advanced-command "
|
||||
style 1-42 fg=yellow
|
||||
9| <blank>
|
||||
10| " Keyboard shortcuts "
|
||||
style 1-18 fg=bright-blue bold
|
||||
11| " Enter send • Shift/Alt+Enter newline • Up/Down prompt history "
|
||||
style 1-61 fg=bright-black
|
||||
12| " Esc cancel active turn • Ctrl+O toggle tool cards • Ctrl+R toggle reasoning "
|
||||
style 1-75 fg=bright-black
|
||||
13| " Ctrl+C cancel while running; clear input or exit while idle • Ctrl+D exit "
|
||||
style 1-73 fg=bright-black
|
||||
14| " "
|
||||
15| " /clear — Clear the transcript view (session history is unchanged) "
|
||||
style 1-65 fg=bright-black
|
||||
16| " /exit — Exit after the active turn reaches idle "
|
||||
style 1-47 fg=bright-black
|
||||
17| " /help — Show keyboard shortcuts and commands "
|
||||
style 1-44 fg=bright-black
|
||||
18| " /model [[provider/]model] — Show or switch this session's model "
|
||||
style 1-63 fg=bright-black
|
||||
19| " /reasoning — Toggle reasoning blocks "
|
||||
style 1-36 fg=bright-black
|
||||
20| " /redraw — Invalidate components and redraw the terminal "
|
||||
style 1-55 fg=bright-black
|
||||
21| " /reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
|
||||
style 1-88 fg=bright-black
|
||||
22| " /resume — List this workspace's resumable sessions "
|
||||
style 1-50 fg=bright-black
|
||||
23| " /status — Show detailed session diagnostics "
|
||||
style 1-43 fg=bright-black
|
||||
24| " /tools — Expand or collapse all tool cards "
|
||||
style 1-42 fg=bright-black
|
||||
25| " /skill:<name> [instructions] — load a skill into the conversation "
|
||||
style 1-65 fg=bright-black
|
||||
26| "────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-91 dim
|
||||
27| " "
|
||||
|
||||
@@ -11,46 +11,46 @@ buffer
|
||||
2| " deepseek-v4-flash • main-session"
|
||||
style 1-34 dim
|
||||
3| <blank>
|
||||
4| " Keyboard shortcuts "
|
||||
style 1-18 fg=bright-blue bold
|
||||
5| " Enter send • Shift/Alt+Enter newline • Up/Down prompt history "
|
||||
style 1-61 fg=bright-black
|
||||
6| " Esc cancel active turn • Ctrl+O toggle tool cards • Ctrl+R toggle reasoning "
|
||||
style 1-75 fg=bright-black
|
||||
7| " Ctrl+C cancel while running; clear input or exit while idle • Ctrl+D exit "
|
||||
style 1-73 fg=bright-black
|
||||
8| " "
|
||||
9| " /clear — Clear the transcript view (session history is unchanged) "
|
||||
style 1-65 fg=bright-black
|
||||
10| " /exit — Exit after the active turn reaches idle "
|
||||
style 1-47 fg=bright-black
|
||||
11| " /help — Show keyboard shortcuts and commands "
|
||||
style 1-44 fg=bright-black
|
||||
12| " /model [[provider/]model] — Show or switch this session's model "
|
||||
style 1-63 fg=bright-black
|
||||
13| " /reasoning — Toggle reasoning blocks "
|
||||
style 1-36 fg=bright-black
|
||||
14| " /redraw — Invalidate components and redraw the terminal "
|
||||
style 1-55 fg=bright-black
|
||||
15| " /reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
|
||||
style 1-88 fg=bright-black
|
||||
16| " /resume — List this workspace's resumable sessions "
|
||||
style 1-50 fg=bright-black
|
||||
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
|
||||
19| " /skill:<name> [instructions] — load a skill into the conversation "
|
||||
style 1-65 fg=bright-black
|
||||
20| <blank>
|
||||
21| " provider stream failed after partial output "
|
||||
4| " provider stream failed after partial output "
|
||||
style 1-43 fg=red
|
||||
22| <blank>
|
||||
23| " The previous process ended during this turn. "
|
||||
5| <blank>
|
||||
6| " The previous process ended during this turn. "
|
||||
style 1-44 fg=yellow
|
||||
24| <blank>
|
||||
25| " Unknown command: /unknown-advanced-command "
|
||||
7| <blank>
|
||||
8| " Unknown command: /unknown-advanced-command "
|
||||
style 1-42 fg=yellow
|
||||
9| <blank>
|
||||
10| " Keyboard shortcuts "
|
||||
style 1-18 fg=bright-blue bold
|
||||
11| " Enter send • Shift/Alt+Enter newline • Up/Down prompt history "
|
||||
style 1-61 fg=bright-black
|
||||
12| " Esc cancel active turn • Ctrl+O toggle tool cards • Ctrl+R toggle reasoning "
|
||||
style 1-75 fg=bright-black
|
||||
13| " Ctrl+C cancel while running; clear input or exit while idle • Ctrl+D exit "
|
||||
style 1-73 fg=bright-black
|
||||
14| " "
|
||||
15| " /clear — Clear the transcript view (session history is unchanged) "
|
||||
style 1-65 fg=bright-black
|
||||
16| " /exit — Exit after the active turn reaches idle "
|
||||
style 1-47 fg=bright-black
|
||||
17| " /help — Show keyboard shortcuts and commands "
|
||||
style 1-44 fg=bright-black
|
||||
18| " /model [[provider/]model] — Show or switch this session's model "
|
||||
style 1-63 fg=bright-black
|
||||
19| " /reasoning — Toggle reasoning blocks "
|
||||
style 1-36 fg=bright-black
|
||||
20| " /redraw — Invalidate components and redraw the terminal "
|
||||
style 1-55 fg=bright-black
|
||||
21| " /reload — EXPERIMENTAL (dev): re-read loader config files and apply the diff (idle only) "
|
||||
style 1-88 fg=bright-black
|
||||
22| " /resume — List this workspace's resumable sessions "
|
||||
style 1-50 fg=bright-black
|
||||
23| " /status — Show detailed session diagnostics "
|
||||
style 1-43 fg=bright-black
|
||||
24| " /tools — Expand or collapse all tool cards "
|
||||
style 1-42 fg=bright-black
|
||||
25| " /skill:<name> [instructions] — load a skill into the conversation "
|
||||
style 1-65 fg=bright-black
|
||||
26| "────────────────────────────────────────────────────────────────────────────────────────────"
|
||||
style 0-91 dim
|
||||
27| " "
|
||||
|
||||
@@ -52,7 +52,7 @@ buffer
|
||||
18| "│ │"
|
||||
style 0-0 dim
|
||||
style 55-55 dim
|
||||
19| "│ Agent: idle · 6 events · 1 turn · 1 step · 1 │"
|
||||
19| "│ Agent: idle · 7 events · 1 turn · 1 step · 1 │"
|
||||
style 0-0 dim
|
||||
style 3-12 fg=bright-black
|
||||
style 55-55 dim
|
||||
|
||||
@@ -49,7 +49,7 @@ buffer
|
||||
17| "│ │"
|
||||
style 0-0 dim
|
||||
style 81-81 dim
|
||||
18| "│ Agent: idle · 6 events · 1 turn · 1 step · 1 tool call │"
|
||||
18| "│ Agent: idle · 7 events · 1 turn · 1 step · 1 tool call │"
|
||||
style 0-0 dim
|
||||
style 3-12 fg=bright-black
|
||||
style 81-81 dim
|
||||
|
||||
@@ -1281,6 +1281,7 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
})
|
||||
result.terminal.send('/clear')
|
||||
result.terminal.send('\r')
|
||||
await tick() // the executor logs command/run durably before the handler clears
|
||||
appendAssistant(result.session, [{ type: 'text', text: 'answer after clear' }], undefined, { turn: 3, step: 1 })
|
||||
await tick()
|
||||
expect(result.terminal.output).toContain('answer after clear')
|
||||
@@ -1758,7 +1759,8 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
expect(result.terminal.output).toContain('/workspace/status')
|
||||
expect(result.terminal.output).toContain('deepseek/deepseek-v4-pro (effort default; reasoning blocks')
|
||||
expect(result.terminal.output).toContain('hidden)')
|
||||
expect(result.terminal.output).toContain('running · 6 events · 1 turn · 1 step · 2 tool calls')
|
||||
// 6 domain events + the /status invocation's own command/run (open turn: joined directly).
|
||||
expect(result.terminal.output).toContain('running · 7 events · 1 turn · 1 step · 2 tool calls')
|
||||
expect(result.terminal.output).toContain('1,250 input + 340 output')
|
||||
expect(result.terminal.output).toContain('[███████████░░░░░] 67% hit (3,000 read + 250 write)')
|
||||
expect(result.terminal.output).toContain('[█████░░░░░░░░░░░] 33% used (42,000 / 128,000)')
|
||||
@@ -1794,7 +1796,8 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
|
||||
expect(result.terminal.output).toContain('untitled')
|
||||
expect(result.terminal.output).toContain('unset (effort unset; reasoning blocks shown)')
|
||||
expect(result.terminal.output).toContain('idle · 0 events · 0 turns · 0 steps · 0 tool calls')
|
||||
// An empty log gains the /status invocation's zero-step wrap: turn/start + command/run + turn/end.
|
||||
expect(result.terminal.output).toContain('idle · 3 events · 1 turn · 0 steps · 0 tool calls')
|
||||
expect(result.terminal.output).toContain('n/a (0 read + 0 write)')
|
||||
expect(result.terminal.output).toContain('7 used · capacity unknown')
|
||||
expect(result.terminal.output).toContain('2026-07-22 10:11:12 UTC')
|
||||
@@ -1834,8 +1837,8 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
for (const command of ['/clear', '/wat']) {
|
||||
result.terminal.send(command)
|
||||
result.terminal.send('\r')
|
||||
await tick() // /clear's handler runs after the durable command/run append; keep it from wiping the next notice
|
||||
}
|
||||
await tick()
|
||||
result.terminal.send('draft')
|
||||
result.terminal.send('\x03')
|
||||
result.terminal.send('\x04')
|
||||
|
||||
Reference in New Issue
Block a user