Skip to content

Recording from a host with redirected stdio (opencode TUI shell, VS Code, CI) produces an empty cast but reports success #8

Closed

Description

@soroshsabz

Summary

When Start-InteractiveTranscript runs in a host whose standard handles are redirected (pipes) - CI jobs, VS Code script runners, and notably the opencode TUI's built-in shell - the recorded child process's output bypasses the pseudo console entirely and goes straight to the caller's redirected stdout. The resulting cast file contains no program output at all (only two ConPTY housekeeping events), yet the cmdlet reports success and returns a normal result object. The existing warning ("output may bypass the pseudo-console") understates it: in my tests capture was 100% bypassed, producing a useless but valid-looking recording.

This is likely a large part of the "recording from inside opencode produces empty/broken transcripts" experience, since agent/terminal hosts like opencode run their shells with redirected stdio.

Environment

  • Bsn.Host 0.1.0 (PowerShell Gallery)
  • Windows 11 10.0.26300
  • Caller: any host with redirected stdout (tested with pwsh -Command ... | Out-File and with a scripted harness)

Repro

pwsh -NoProfile -Command "Import-Module Bsn.Host; Start-InteractiveTranscript -Command 'echo hello-redirected' -Path .\x.cast -Force" 3>$null | Out-File .\out.txt

Result:

  • x.cast is 348 bytes with exactly 2 events - the ConPTY session open (ESC[?9001h ESC[?1004h) and close (ESC[?25l ESC[2J ...) sequences. No hello-redirected anywhere.
  • out.txt does contain hello-redirected: the child wrote directly into the caller's stdout pipe, around the pseudo console.
  • The cmdlet emits the redirected-stdio warning, then returns ExitCode=0, a cast path, and a duration - i.e. it looks successful.

The same happens for -Command 'echo hello' from my agent harness (cast = 354 bytes, 2 housekeeping events, program output in the harness's captured stdout).

For contrast: the identical command recorded from a non-redirected console session captures everything correctly, so the wiring difference is purely the caller's redirected standard handles.

Root cause / notes

On Windows, when the process hosting the ConPTY has pipe standard handles, the child created with PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE + null STARTUPINFO handles ends up with the parent's redirected handles for its standard streams, so its writes never pass through the pseudo console and are never teed into the cast. The engine (PtyRecorder.Run) cannot see any of it - from its perspective the child simply produced no VT output.

Suggested fix

Since silent-but-empty recordings are the worst outcome for an audit feature, consider:

  1. Promoting the check from a warning to a terminating error when both stdin and stdout are redirected (there is no meaningful interactive capture in that shape anyway), with guidance to run from a real console; or
  2. Investigating the handle wiring so the child's std streams are forced through the pseudo console even when the caller is redirected (e.g. explicitly detaching the child from the caller's handles) - if Windows permits it on this build.

Either way, the current behavior (warning + success-shaped result + empty cast) is easy to mistake for a working recording.

Activity

  1. soroshsabz commented on Nov 6, 2026

    @soroshsabz
    MemberAuthor

    Evidence artifacts: https://gist.github.com/soroshsabz/58fcb241ebb37d55a61918e69196bc47

    • J_redirected.cast - 348 bytes, only the two ConPTY housekeeping events, no program output
    • redirected_out.txt - the caller captured stdout, containing the hello-redirected output that bypassed the pseudo console
  2. self-assigned this
    on Nov 6, 2026
  3. added theissue type on Nov 6, 2026
  4. added 2 commits that reference this issue on Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions