@@ -29,18 +29,18 @@ import {
2929 createKernel ,
3030 allowAllChildProcess ,
3131 allowAllEnv ,
32- } from '../../../kernel /src/index.ts' ;
32+ } from '../../../secure-exec-core /src/kernel /index.ts' ;
3333import type {
3434 Kernel ,
3535 RuntimeDriver ,
3636 KernelInterface ,
3737 DriverProcess ,
3838 ProcessContext ,
39- } from '../../../kernel /src/index.ts' ;
40- import type { VirtualFileSystem } from '../../../kernel /src/vfs.ts' ;
41- import { TerminalHarness } from '../../../kernel /test/terminal-harness.ts' ;
42- import { InMemoryFileSystem } from '../../../os/ browser/src/index .ts' ;
43- import { createNodeRuntime } from '../../../runtime/node/ src/index .ts' ;
39+ } from '../../../secure-exec-core /src/kernel /index.ts' ;
40+ import type { VirtualFileSystem } from '../../../secure-exec-core /src/kernel /vfs.ts' ;
41+ import { TerminalHarness } from '../../../secure-exec-core /test/kernel /terminal-harness.ts' ;
42+ import { InMemoryFileSystem } from '../../../secure-exec- browser/src/os-filesystem .ts' ;
43+ import { createNodeRuntime } from '../../../secure-exec-nodejs/ src/kernel-runtime .ts' ;
4444import {
4545 createMockLlmServer ,
4646 type MockLlmServerHandle ,
@@ -685,6 +685,88 @@ describe.skipIf(skipReason)('Claude Code interactive PTY E2E (sandbox)', () => {
685685 45_000 ,
686686 ) ;
687687
688+ it (
689+ 'Tool use UI — tool execution renders on screen when LLM requests a tool' ,
690+ async ( { skip } ) => {
691+ if ( sandboxSkip ) skip ( ) ;
692+
693+ // Queue: tool_use (Bash) → text result after tool execution
694+ mockServer . reset ( [
695+ { type : 'tool_use' , name : 'Bash' , input : { command : 'echo TOOL_CANARY_42' } } ,
696+ { type : 'text' , text : 'The tool ran successfully.' } ,
697+ { type : 'text' , text : 'The tool ran successfully.' } ,
698+ ] ) ;
699+
700+ harness = createClaudeHarness ( ) ;
701+ await waitForClaudeBoot ( harness ) ;
702+ await harness . waitFor ( '❯' , 1 , 5_000 ) ;
703+
704+ // Submit a prompt that will trigger a tool use
705+ await harness . type ( 'run echo\r' ) ;
706+
707+ // Wait for tool-related UI to appear — Claude shows tool name or output
708+ // With --dangerously-skip-permissions, tools auto-execute and show results
709+ await harness . waitFor ( 'Bash' , 1 , 30_000 ) ;
710+
711+ const screen = harness . screenshotTrimmed ( ) ;
712+ // Tool name should appear in the TUI output
713+ expect ( screen ) . toMatch ( / B a s h | e c h o | T O O L _ C A N A R Y / i) ;
714+ } ,
715+ 60_000 ,
716+ ) ;
717+
718+ it (
719+ 'PTY resize — Ink re-renders for new dimensions' ,
720+ async ( { skip } ) => {
721+ if ( sandboxSkip ) skip ( ) ;
722+
723+ mockServer . reset ( [ { type : 'text' , text : 'resize placeholder' } ] ) ;
724+
725+ harness = createClaudeHarness ( ) ;
726+ await waitForClaudeBoot ( harness ) ;
727+ await harness . waitFor ( '❯' , 1 , 5_000 ) ;
728+
729+ // Resize PTY to wider terminal and resize xterm to match
730+ harness . shell . resize ( 120 , 40 ) ;
731+ harness . term . resize ( 120 , 40 ) ;
732+
733+ // Wait for Claude's Ink TUI to process SIGWINCH and re-render
734+ await new Promise ( ( r ) => setTimeout ( r , 1_500 ) ) ;
735+
736+ const screenAfter = harness . screenshotTrimmed ( ) ;
737+ // Claude TUI should still show its UI elements after resize
738+ expect ( screenAfter ) . toMatch ( / ❯ | H a i k u | W e l c o m e / ) ;
739+ // Screen should not be blank/garbled
740+ expect ( screenAfter . length ) . toBeGreaterThan ( 0 ) ;
741+ } ,
742+ 45_000 ,
743+ ) ;
744+
745+ it (
746+ '/help renders help text — help command output visible on screen' ,
747+ async ( { skip } ) => {
748+ if ( sandboxSkip ) skip ( ) ;
749+
750+ mockServer . reset ( [ ] ) ;
751+
752+ harness = createClaudeHarness ( ) ;
753+ await waitForClaudeBoot ( harness ) ;
754+ await harness . waitFor ( '❯' , 1 , 5_000 ) ;
755+
756+ // Type /help and submit
757+ await harness . type ( '/help\r' ) ;
758+
759+ // Wait for help content to render — Claude shows slash commands list
760+ // Help output includes common commands like /exit, /clear, /help
761+ await harness . waitFor ( 'exit' , 1 , 15_000 ) ;
762+
763+ const screen = harness . screenshotTrimmed ( ) ;
764+ // Help text should list available commands
765+ expect ( screen ) . toMatch ( / e x i t | c l e a r | h e l p / i) ;
766+ } ,
767+ 45_000 ,
768+ ) ;
769+
688770 it (
689771 'Exit cleanly — /exit causes Claude to exit' ,
690772 async ( { skip } ) => {
0 commit comments