Skip to content

[ProcessContainer] Add caller-controlled PTY support - #1381

Draft
Branden Bonaby (bbonaby) wants to merge 14 commits into
mainfrom
user/bbonaby/add-process-container-pty
Draft

Branden Bonaby (bbonaby) wants to merge 14 commits into
mainfrom
user/bbonaby/add-process-container-pty

Conversation

@bbonaby

@bbonaby Branden Bonaby (bbonaby) commented Oct 3, 2026 •

Copy link
Copy Markdown
Collaborator

📖 Description

  • Adds caller-controlled PTY execution for Windows ProcessContainer through spawnWithPty.
  • Preserves normal ProcessContainer tier selection, including BaseContainer/PSEC when it can enforce the request.
  • Keeps DACL guard lifetime, policy validation, timeout, and process-tree cleanup intact.
  • Supports terminal input, merged output, resize, wait, timeout, and process-tree termination.
  • Drains unclaimed ConPTY output before teardown so ClosePseudoConsole cannot block on a full pipe.

Limitations

  • PTY output is a single merged terminal stream rather than separate stdout and stderr.
  • BaseContainer/PSEC currently creates the child successfully but does not attach its input or output to ConPTY on the tested Windows host. The public SDK integration test therefore times out with an empty terminal transcript; this remains under investigation.

Architecture

flowchart LR
    Caller[SDK caller<br/>Rust / Node / .NET] --> Engine[mxc_engine<br/>ProcessContainer dispatch]
    Engine --> Select[Normal ProcessContainer tier selection]
    Select --> Base[BaseContainer / PSEC]
    Select --> App[AppContainer tier]
    Base --> ConPTY[ConPTY]
    App --> ConPTY[ConPTY]
    ConPTY --> Pty[MxcPty<br/>input / merged output / resize / wait]
    Pty --> Caller
Loading

🔗 References

🔍 Validation

  • cargo fmt --all -- --check — passed.
  • cargo test -p process_container_common host_process_uses_pseudo_console_transport -- --nocapture — passed.
  • cargo test -p process_container_common fallback_detector::tests — passed (26 tests).
  • cargo test -p process_container_common dispatcher::tests — passed (28 tests).
  • cargo test -p mxc-sdk --test streaming_processcontainer processcontainer_pty_supports_io_resize_and_wait -- --ignored --nocapture — currently fails on BaseContainer: TimedOut with an empty terminal transcript.
  • cargo clippy -p process_container_common -p mxc-sdk --all-targets -- -D warnings — passed.
  • npm run build && npm test from sdk/node — passed (406 tests; 20 skipped).
  • dotnet run --project Microsoft.Mxc.Sdk.Tests/Microsoft.Mxc.Sdk.Tests.csproj --no-build --no-restore -- -class Microsoft.Mxc.Sdk.Tests.MxcPtyProcessTests from sdk/dotnet — passed (9 tests).
  • cargo test -p process_container_common — 323 passed; the unrelated host-loopback proxy test timed out with WinSock error 10060 on this host.

✅ Checklist

📋 Issue Type

  • Bug fix
  • Feature
  • Task

GitHub Actions runs the PR validation build automatically. The ADO pipeline
(MXC-PR-Build) is the Azure version of the PR pipeline, kept in parity with the GitHub
Actions build; it runs on merge to main, and Microsoft reviewers with write access can trigger it
on a PR with /azp run. See docs/pull-requests.md.

If the dependency-feed-check check fails on a new dependency, the crate must be added to
the feed before the PR can pass. See docs/pull-requests.md
for the steps.

Microsoft Reviewers: Open in CodeFlow

@bbonaby
Branden Bonaby (bbonaby) requested a review from a team as a code owner October 3, 2026 00:22
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Base automatically changed from user/bbonaby/add-pty-api-through-rust-and-all-sdks to main October 3, 2026 04:38
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
@bbonaby
Branden Bonaby (bbonaby) force-pushed the user/bbonaby/add-process-container-pty branch from defefa9 to bc9aa9c Compare October 3, 2026 05:00
Copilot AI balanced review requested due to automatic review settings October 3, 2026 05:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

A stale validator makes ProcessContainer PTY spawning unreachable, and timeout enforcement is incomplete for Rust SDK PTY waits.

Review effort: Balanced
Findings: 4 High severity

Open (4)
What changed in this PR

Adds caller-controlled ConPTY execution to Windows ProcessContainer while retaining backend fallback and policy enforcement.

Changes:

  • Adds PTY creation, I/O, resize, wait, and teardown support.
  • Integrates PTYs into AppContainer, BaseContainer/PSEC, and DACL wrappers.
  • Updates SDK documentation to list ProcessContainer support.
File Description
src/​core/​wxc_common/​src/​process_util.rs Adds Windows handle conversions.
src/​core/​mxc-sdk/​src/​lib.rs Documents ProcessContainer PTY support.
src/​core/​mxc_engine/​src/​dispatch.rs Routes ProcessContainer PTY requests.
src/​backends/​process_container/​common/​src/​secenv.rs Adds the PSEC pseudo-console attribute.
src/​backends/​process_container/​common/​src/​pseudo_console.rs Implements ConPTY ownership and I/O.
src/​backends/​process_container/​common/​src/​lib.rs Registers the PTY module.
src/​backends/​process_container/​common/​src/​dispatcher.rs Forwards PTY operations through DACL guards.
src/​backends/​process_container/​common/​src/​base_container_runner.rs Integrates PTYs with BaseContainer.
src/​backends/​process_container/​common/​src/​appcontainer_runner.rs Integrates PTYs with AppContainer.
src/​backends/​process_container/​common/​examples/​lm_capture.rs Updates startup-info construction.
sdk/​node/​README.md Documents Node.js support.
sdk/​dotnet/​README.md Documents .NET support.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/backends/process_container/common/src/appcontainer_runner.rs
Comment thread src/backends/process_container/common/src/base_container_runner.rs
Comment thread src/core/mxc_engine/src/dispatch.rs
Comment thread src/core/wxc_common/src/process_util.rs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Copilot AI balanced review requested due to automatic review settings October 3, 2026 05:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Existing ProcessContainer validation still rejects every PTY request before either new launch path runs.

Review effort: Balanced
Findings: 4 High severity · 1 Medium severity · 1 Low severity

Open (6)

Comment thread src/backends/process_container/common/src/pseudo_console.rs
Comment thread src/core/mxc-sdk/src/lib.rs
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Allow PTY launch through both ProcessContainer fallback paths and enforce finite script timeouts while PTY waits poll try_wait.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Reject null and invalid handles before constructing BorrowedHandle in the safe cloning helper.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Add ignored Windows integration tests for PTY input, merged output, resize, wait, and finite timeout behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Clarify that one-shot PTY spawning supports Windows ProcessContainer while existing-container PTY spawning remains an IsolationSession API.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Copilot AI balanced review requested due to automatic review settings October 3, 2026 05:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread src/backends/process_container/common/src/appcontainer_runner.rs Outdated
Comment thread src/backends/process_container/common/src/base_container_runner.rs Outdated
Poll the process handle before applying the elapsed timeout so a process already observed as complete is not killed or reclassified.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Copilot AI balanced review requested due to automatic review settings October 3, 2026 06:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Inheritable ConPTY pipe handles can leak and block teardown, while blocking and polling waits currently apply inconsistent timeout deadlines.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
Resolved since last review (5)
Previously missed (1)

In code that hasn't changed since last review

Low severity Rename fallible handle conversion to try_into_std_owned_handle

src/​core/​wxc_common/​src/​process_util.rs:331

This public fallible conversion is named like an infallible Into operation, unlike the adjacent OwnedHandle::try_into_std_owned_handle API. Rename it to try_into_std_owned_handle and update its ProcessContainer call sites so callers can recognize that it may fail.

Comment thread src/backends/process_container/common/src/pseudo_console.rs Outdated
Comment thread src/backends/process_container/common/src/lib.rs Outdated
Use non-inheritable local ConPTY transport pipes and calculate blocking waits from the original execution deadline so wait and try_wait enforce the same timeout.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Copilot AI balanced review requested due to automatic review settings October 3, 2026 19:37

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Low-level ConPTY lifecycle and teardown behavior still requires the noted real ProcessContainer workload validation.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
Resolved since last review (1)

Keep ConPTY transport handles alive through suspended child startup, prevent redirected host stdio inheritance, and route PTY launches to the AppContainer tier when PSEC cannot attach the pseudoconsole.

Add real Rust, Node, and .NET public SDK round-trip coverage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Copilot AI balanced review requested due to automatic review settings October 3, 2026 20:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Integration coverage does not currently verify timeout termination or merged stderr behavior.

Review effort: Balanced
Findings: 2 Low severity

Open (2)
Resolved since last review (1)

Comment on lines +2148 to +2149
WAIT_TIMEOUT if crate::process_timeout_elapsed(self.started_at, self.timeout_ms) => {
self.kill_for_timeout()?;
Comment on lines +61 to +63
let request = processcontainer_request(
"cmd.exe /d /q /c \"set /p value= & echo MXC_PROCESSCONTAINER_PTY_OK\"",
);
Remove the PTY-specific AppContainer fallback so ProcessContainer keeps its normal enforcement-based tier selection.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 433e5b70-c2bd-496d-9b8d-bd2085bb2706
Copilot AI balanced review requested due to automatic review settings October 3, 2026 20:53

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Normal tier selection can choose the acknowledged nonfunctional BaseContainer PTY path, causing empty-output timeouts.

Review effort: Balanced
Findings: 1 High severity · 2 Low severity

Open (3)
Previously missed (1)

In code that hasn't changed since last review

Low severity Correct startup comment about PTY standard handle flags

src/​backends/​process_container/​common/​src/​appcontainer_runner.rs:1084

This launch description contradicts the startup info above: PTY mode sets STARTF_USESTDHANDLES at line 1007, with null standard handles, while only bInheritHandles is false. Correct the comment so future changes do not remove a flag this implementation intentionally relies on.

Comment on lines +102 to +104
ContainmentBackend::ProcessContainer => {
spawn_process_container(request, logger, StdioMode::Pty(size))
}
@bbonaby
Branden Bonaby (bbonaby) marked this pull request as draft October 4, 2026 00:18
@bbonaby

Copy link
Copy Markdown
Collaborator Author

Moving this one to draft. Looks like the CreateProcessSecurityEnvironment API flow doesn't currently support creating in PTYs yet.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants