Skip to content

fix(cli): close non-terminal exec stdin by default - #6394

Closed
chengjiew wants to merge 1 commit into
mainfrom
fix/6319_exec-stdin-signed
Closed

fix(cli): close non-terminal exec stdin by default#6394
chengjiew wants to merge 1 commit into
mainfrom
fix/6319_exec-stdin-signed

Conversation

@chengjiew

@chengjiew chengjiew commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Close non-terminal or unavailable stdin by default for nemoclaw <sandbox> exec, preventing live pipes/SSH stdin from hanging one-shot commands.
  • Add explicit --stdin / --no-stdin controls so intentional piped scripts still work.
  • Update command docs and focused CLI/action tests for the new stdin policy.

Related Issue

Fixes #6319

Verification

  • npx vitest run --project cli src/commands/sandbox/exec.test.ts src/lib/actions/sandbox/exec-stdio.test.ts src/lib/actions/sandbox/exec.multiline-guard.test.ts src/lib/actions/sandbox/exec-openclaw-permission-cleanup.test.ts — 4 files / 47 tests passed.
  • npm run build:cli — passed.
  • npm run typecheck:cli — passed.
  • npm run docs:check-agent-variants — passed.
  • git diff --cached --check — passed before commit.
  • Commit hooks passed: format/lint/repository checks, gitleaks, env-var docs gate, test file size budget, commitlint.
  • Push hooks passed: CLI typecheck and package/tag version sync.
  • Commit signature verified by GitHub for c8f0cf7503191de39358c98a081f6a856127d1b5.

Linux Reproduction

Validated on aits@aits-log-worker-6 with Ready sandbox repro-6182-live.

Baseline nemoclaw v0.0.74-13-gf0d25491c:

/dev/null control:
ok
control_rc=0 elapsed=2s

held-open stdin:
live_rc=124 elapsed=25s

Fixed PR head nemoclaw v0.0.74-37-gd73686641:

/dev/null control:
ok
fixed_control_rc=0 elapsed=1s

held-open stdin default:
ok
fixed_live_rc=0 elapsed=1s

--stdin held-open stdin:
fixed_explicit_stdin_rc=124 elapsed=12s

Signed-off-by: Chengjie Wang chengjiew@nvidia.com

Summary by CodeRabbit

  • New Features

    • Added --stdin and --no-stdin options to sandbox exec commands.
    • Stdin is now forwarded only when requested or when running from a terminal; otherwise it is closed to avoid blocking.
    • Updated command usage and examples to show piping input into exec commands.
  • Documentation

    • Clarified exec command behavior and default stdin handling in the reference docs.

Fixes #6319

Signed-off-by: Chengjie Wang <chengjiew@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Adds stdin inheritance control to sandbox exec: a new module decides whether stdin should be inherited based on TTY detection or explicit request, exec.ts wires this into spawning, a new --stdin/--no-stdin CLI flag is added, related tests are updated, and documentation is revised accordingly.

Changes

Stdin forwarding feature

Layer / File(s) Summary
Stdin inheritance decision module
src/lib/actions/sandbox/exec-stdio.ts, src/lib/actions/sandbox/exec-stdio.test.ts
New shouldInheritSandboxExecStdin and buildSandboxExecStdio functions decide stdio config from explicit flags or TTY detection, with tests covering both paths.
exec.ts spawner and options wiring
src/lib/actions/sandbox/exec.ts
SandboxExecOptions gains stdin, SandboxExecSpawner/runSandboxExecChild accept and forward options, defaultSandboxExecSpawner uses buildSandboxExecStdio, usage string and deps.run wiring updated, and error hint comment/example revised.
Multiline guard test updates for stdio
src/lib/actions/sandbox/exec.multiline-guard.test.ts
Guidance message assertions updated for --stdin, and a parameterized test matrix verifies spawn stdio shapes for enabled/disabled stdin.
CLI --stdin flag
src/commands/sandbox/exec.ts, src/commands/sandbox/exec.test.ts
Adds boolean stdin flag with allowNo, forwards it into execSandbox options, updates description/usage/examples, and updates tests for stdin: true/false/undefined.
Permission cleanup test signature update
src/lib/actions/sandbox/exec-openclaw-permission-cleanup.test.ts
Updates runSandboxExecChild stub calls to include an extra options argument in SIGTERM/SIGINT tests.
Documentation updates
docs/reference/commands-nemohermes.mdx, docs/reference/commands.mdx
Documents stdin inheritance defaults and --stdin/--no-stdin flags with examples and updated flag tables.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant execSandbox
  participant runSandboxExecChild
  participant defaultSandboxExecSpawner
  participant buildSandboxExecStdio

  CLI->>execSandbox: run(options with stdin flag)
  execSandbox->>runSandboxExecChild: spawnChild(binary, args, options)
  runSandboxExecChild->>defaultSandboxExecSpawner: options
  defaultSandboxExecSpawner->>buildSandboxExecStdio: options, stdinIsTty
  buildSandboxExecStdio-->>defaultSandboxExecSpawner: "inherit" or ["ignore","inherit","inherit"]
  defaultSandboxExecSpawner-->>execSandbox: spawned child with resolved stdio
Loading

Suggested labels: bug-fix

Suggested reviewers: ericksoa, cjagwani

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: closing non-terminal exec stdin by default.
Linked Issues check ✅ Passed The PR implements the requested stdin-default fix and adds explicit --stdin/--no-stdin support, matching the linked issue.
Out of Scope Changes check ✅ Passed The added docs, tests, helper module, and related exec wiring all support the stdin-handling fix; no unrelated changes stand out.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6319_exec-stdin-signed

Comment @coderabbitai help to get the list of available commands.

@github-code-quality

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the fix/6319_exec-stdin-... branch is 96%. Coverage data for the main branch is not yet available.

Show a code coverage summary of the most covered files.
File main fix/6319_exec-stdin-... c8f0cf7 +/-
nemoclaw/src/se...cret-scanner.ts 100%
nemoclaw/src/commands/slash.ts 100%
nemoclaw/src/li...bprocess-env.ts 100%
nemoclaw/src/bl...eprint/state.ts 98%
nemoclaw/src/onboard/config.ts 98%
nemoclaw/src/bl...int/snapshot.ts 97%
nemoclaw/src/bl...print/runner.ts 95%
nemoclaw/src/co...ration-state.ts 94%
nemoclaw/src/bl...ate-networks.ts 94%
nemoclaw/src/index.ts 94%

Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

@chengjiew chengjiew closed this Jul 7, 2026
@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — Changes requested

Merge posture: Do not merge yet
Primary next action: Resolve or justify PRA-1: Resolve duplicate active PR overlap for the same issue and files.
Open items: 0 required · 1 warning · 0 suggestions · 3 test follow-ups
Top item: Resolve duplicate active PR overlap for #6319

Action checklist

  • PRA-1 Resolve or justify: Resolve duplicate active PR overlap for the same issue and files
  • PRA-T1 Add or justify test follow-up: Runtime validation
  • PRA-T2 Add or justify test follow-up: Runtime validation
  • PRA-T3 Add or justify test follow-up: Runtime validation

Findings index

ID Severity Category Location Required action
PRA-1 Resolve/justify scope Coordinate which PR is the intended source of truth for the #6319 fix, then close/supersede or update the duplicate so only one active branch carries this change.
Review findings by urgency: 0 required fixes, 1 item to resolve/justify, 0 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

PRA-1 Resolve/justify — Resolve duplicate active PR overlap for the same issue and files

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

  • None.
Test follow-ups to resolve or justify

If these cover changed behavior, prefer adding them in this PR; otherwise state why existing coverage is enough or link the follow-up.

  • PRA-T1 Runtime validation — Add or identify a test that exercises the production default runner with no `--stdin` flag while stdin detection is non-terminal or unavailable, and asserts `spawn` receives `stdio: ["ignore", "inherit", "inherit"]`.. Unit and parser coverage are strong for the local stdin policy, including pure helper behavior and explicit production spawn modes. Because the bug manifests at the runtime child-process/OpenShell boundary with a held-open pipe, one targeted runtime or integration validation would provide additional confidence without relying on external E2E status.
  • PRA-T2 Runtime validation — Add or identify a runtime/integration validation where `nemoclaw <sandbox> exec -- echo ok` is invoked with a held-open non-EOF pipe and completes without waiting for EOF.. Unit and parser coverage are strong for the local stdin policy, including pure helper behavior and explicit production spawn modes. Because the bug manifests at the runtime child-process/OpenShell boundary with a held-open pipe, one targeted runtime or integration validation would provide additional confidence without relying on external E2E status.
  • PRA-T3 Runtime validation — Add or identify a paired runtime/integration validation where `nemoclaw <sandbox> exec --stdin -- cat` receives intentional piped input, proving the opt-in pipe path still works.. Unit and parser coverage are strong for the local stdin policy, including pure helper behavior and explicit production spawn modes. Because the bug manifests at the runtime child-process/OpenShell boundary with a held-open pipe, one targeted runtime or integration validation would provide additional confidence without relying on external E2E status.

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Consider PRA-1: Default parameter isStdinTty() evaluated at call time — verified correct but worth documenting.
Open items: 0 required · 0 warnings · 4 suggestions · 0 test follow-ups
Since last review: 0 prior items resolved · 0 still apply · 3 new items found

Action checklist

  • PRA-1 In-scope improvement: Default parameter isStdinTty() evaluated at call time — verified correct but worth documenting in src/lib/actions/sandbox/exec-stdio.ts:18
  • PRA-2 In-scope improvement: Fail-closed stdin default eliminates hang vector in CI/SSH/automation in src/lib/actions/sandbox/exec-stdio.ts:1
  • PRA-3 In-scope improvement: CLI flag plumbing correctly threads stdin option to action layer in src/commands/sandbox/exec.ts:20
  • PRA-4 In-scope improvement: Documentation updated for both command variants with new flags and pipe syntax in docs/reference/commands.mdx:708

Findings index

ID Severity Category Location Required action
PRA-1 Improvement correctness src/lib/actions/sandbox/exec-stdio.ts:18 Add a one-line comment: `// Default param is late-bound; evaluated at call time in execSandbox`
PRA-2 Improvement security src/lib/actions/sandbox/exec-stdio.ts:1 No action needed; this is the intended security hardening.
PRA-3 Improvement security src/commands/sandbox/exec.ts:20 No action needed.
PRA-4 Improvement acceptance docs/reference/commands.mdx:708 No action needed.
Review findings by urgency: 0 required fixes, 0 items to resolve/justify, 4 in-scope improvements

⚠️ Resolve or justify before merge

Investigate these in the current review; either fix them, explain why they are not applicable, or document the accepted risk.

  • None.

💡 In-scope improvements

These are lower-risk, not throwaway. Prefer fixing them in this PR when they are local to changed code; defer only with rationale or a linked follow-up.

PRA-1 Improvement — Default parameter isStdinTty() evaluated at call time — verified correct but worth documenting

  • Location: src/lib/actions/sandbox/exec-stdio.ts:18
  • Category: correctness
  • Problem: Default parameter `stdinIsTty: boolean | undefined = isStdinTty()` uses JavaScript late-binding semantics, so `isStdinTty()` (which calls `tty.isatty(0)`) executes at call time inside `execSandbox`, not at module load. This is correct for runtime TTY detection. Adding a comment clarifying the late-binding behavior would aid future maintainers.
  • Impact: Low — current behavior is correct; only a maintainability note.
  • Suggested action: Add a one-line comment: `// Default param is late-bound; evaluated at call time in execSandbox`
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check that execSandbox calls buildSandboxExecStdio(options) without second argument; the default is evaluated during that call.
  • Missing regression test: Unit test confirming isStdinTty() not invoked during module import (only during execSandbox call)
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: exec.ts:410 calls buildSandboxExecStdio(options) — default param evaluated here, not at module load

PRA-2 Improvement — Fail-closed stdin default eliminates hang vector in CI/SSH/automation

  • Location: src/lib/actions/sandbox/exec-stdio.ts:1
  • Category: security
  • Problem: Changed default stdin behavior from 'inherit' to closed (`['ignore', 'inherit', 'inherit']`) for non-TTY input. Uses kernel-level `tty.isatty(0)` for TTY detection. Explicit `--stdin` flag required for intentional pipe workflows. This is a security improvement (least privilege) that also fixes the availability bug from [WSL2][CLI&UX] nemoclaw exec blocks indefinitely when stdin is a live (non-EOF) pipe — breaks pipelines/CI; stdin from /dev/null works #6319.
  • Impact: Prevents indefinite hangs when exec inherits live non-EOF stdin (pipelines, SSH sessions). Reduces attack surface from host→sandbox input forwarding.
  • Suggested action: No action needed; this is the intended security hardening.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Run: printf 'test' | nemoclaw sandbox exec alpha -- cat (should exit promptly, not hang); printf 'test' | nemoclaw sandbox exec alpha --stdin -- cat (should forward stdin to cat)
  • Missing regression test: exec-stdio.test.ts covers all 6 flag×TTY combos; exec.multiline-guard.test.ts it.each verifies --stdin/--no-stdin wiring to spawn stdio
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: buildSandboxExecStdio returns 'inherit' only when stdinIsTty === true or options.stdin === true; otherwise ['ignore','inherit','inherit']

PRA-3 Improvement — CLI flag plumbing correctly threads stdin option to action layer

  • Location: src/commands/sandbox/exec.ts:20
  • Category: security
  • Problem: oclif boolean flag with `allowNo: true` creates mutually exclusive `--stdin`/`--no-stdin` pair. Parsed value passed through to `execSandbox` options object. No parsing gaps.
  • Impact: Ensures user intent (explicit stdin forwarding or closure) reaches the spawn logic.
  • Suggested action: No action needed.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Run `nemoclaw sandbox exec --help` and verify --stdin/--no-stdin appear; test parsing with explicit flags in exec.test.ts
  • Missing regression test: exec.test.ts has 4 tests: parses --stdin, parses --no-stdin, leaves undefined for auto-detect, combines with other flags
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: exec.ts:43 passes flags.stdin through to execSandbox; flag definition at line 20-25

PRA-4 Improvement — Documentation updated for both command variants with new flags and pipe syntax

  • Location: docs/reference/commands.mdx:708
  • Category: acceptance
  • Problem: Both commands.mdx (nemoclaw) and commands-nemohermes.mdx updated: usage strings, flag tables, examples, and pipe workaround syntax now show `--stdin` requirement. No drift between variants.
  • Impact: Users discover new flags and updated pipe workflow (`printf ... | exec --stdin -- bash`) from docs.
  • Suggested action: No action needed.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: grep --stdin docs/reference/commands.mdx docs/reference/commands-nemohermes.mdx — verify flags, examples, and pipe workaround syntax updated
  • Missing regression test: docs:check-agent-variants passes; manual doc review sufficient
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Both files show --stdin/--no-stdin in usage, flag table, examples, and updated pipe workaround
Since last review details

Current findings, using the urgency labels above:

PRA-1 Improvement — Default parameter isStdinTty() evaluated at call time — verified correct but worth documenting

  • Location: src/lib/actions/sandbox/exec-stdio.ts:18
  • Category: correctness
  • Problem: Default parameter `stdinIsTty: boolean | undefined = isStdinTty()` uses JavaScript late-binding semantics, so `isStdinTty()` (which calls `tty.isatty(0)`) executes at call time inside `execSandbox`, not at module load. This is correct for runtime TTY detection. Adding a comment clarifying the late-binding behavior would aid future maintainers.
  • Impact: Low — current behavior is correct; only a maintainability note.
  • Suggested action: Add a one-line comment: `// Default param is late-bound; evaluated at call time in execSandbox`
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Check that execSandbox calls buildSandboxExecStdio(options) without second argument; the default is evaluated during that call.
  • Missing regression test: Unit test confirming isStdinTty() not invoked during module import (only during execSandbox call)
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: exec.ts:410 calls buildSandboxExecStdio(options) — default param evaluated here, not at module load

PRA-2 Improvement — Fail-closed stdin default eliminates hang vector in CI/SSH/automation

  • Location: src/lib/actions/sandbox/exec-stdio.ts:1
  • Category: security
  • Problem: Changed default stdin behavior from 'inherit' to closed (`['ignore', 'inherit', 'inherit']`) for non-TTY input. Uses kernel-level `tty.isatty(0)` for TTY detection. Explicit `--stdin` flag required for intentional pipe workflows. This is a security improvement (least privilege) that also fixes the availability bug from [WSL2][CLI&UX] nemoclaw exec blocks indefinitely when stdin is a live (non-EOF) pipe — breaks pipelines/CI; stdin from /dev/null works #6319.
  • Impact: Prevents indefinite hangs when exec inherits live non-EOF stdin (pipelines, SSH sessions). Reduces attack surface from host→sandbox input forwarding.
  • Suggested action: No action needed; this is the intended security hardening.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Run: printf 'test' | nemoclaw sandbox exec alpha -- cat (should exit promptly, not hang); printf 'test' | nemoclaw sandbox exec alpha --stdin -- cat (should forward stdin to cat)
  • Missing regression test: exec-stdio.test.ts covers all 6 flag×TTY combos; exec.multiline-guard.test.ts it.each verifies --stdin/--no-stdin wiring to spawn stdio
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: buildSandboxExecStdio returns 'inherit' only when stdinIsTty === true or options.stdin === true; otherwise ['ignore','inherit','inherit']

PRA-3 Improvement — CLI flag plumbing correctly threads stdin option to action layer

  • Location: src/commands/sandbox/exec.ts:20
  • Category: security
  • Problem: oclif boolean flag with `allowNo: true` creates mutually exclusive `--stdin`/`--no-stdin` pair. Parsed value passed through to `execSandbox` options object. No parsing gaps.
  • Impact: Ensures user intent (explicit stdin forwarding or closure) reaches the spawn logic.
  • Suggested action: No action needed.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Run `nemoclaw sandbox exec --help` and verify --stdin/--no-stdin appear; test parsing with explicit flags in exec.test.ts
  • Missing regression test: exec.test.ts has 4 tests: parses --stdin, parses --no-stdin, leaves undefined for auto-detect, combines with other flags
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: exec.ts:43 passes flags.stdin through to execSandbox; flag definition at line 20-25

PRA-4 Improvement — Documentation updated for both command variants with new flags and pipe syntax

  • Location: docs/reference/commands.mdx:708
  • Category: acceptance
  • Problem: Both commands.mdx (nemoclaw) and commands-nemohermes.mdx updated: usage strings, flag tables, examples, and pipe workaround syntax now show `--stdin` requirement. No drift between variants.
  • Impact: Users discover new flags and updated pipe workflow (`printf ... | exec --stdin -- bash`) from docs.
  • Suggested action: No action needed.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: grep --stdin docs/reference/commands.mdx docs/reference/commands-nemohermes.mdx — verify flags, examples, and pipe workaround syntax updated
  • Missing regression test: docs:check-agent-variants passes; manual doc review sufficient
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Both files show --stdin/--no-stdin in usage, flag table, examples, and updated pipe workaround

Workflow run details

This is an automated, non-binding review; it still expects maintainers and agents to respond to each required or warning item. Treat suggestions as current-PR improvements when they touch changed code; defer only with maintainer rationale or a linked follow-up. A human maintainer must make the final merge decision.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: sandbox-operations, shields-config
Optional E2E: sessions-agents-cli

Dispatch hint: sandbox-operations,shields-config

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • sandbox-operations (high): Closest existing live coverage for sandbox lifecycle and host-side sandbox operation commands. This PR changes the core exec transport used by sandbox command flows, so the live sandbox operations lane should run.
  • shields-config (high): Covers the documented nemoclaw <name> exec -- ... path plus OpenClaw mutable config permission cleanup after exec. The PR changes the exec runner and stdio path that must not regress cleanup or config safety.

Optional E2E

  • sessions-agents-cli (high): Useful adjacent confidence because it drives host-side nemoclaw <name> exec -- openclaw ... agent/device commands through a live sandbox, but it is not as directly targeted at stdio behavior as the required lanes.

New E2E recommendations

  • sandbox exec stdin transport (high): Existing live coverage notes that piped stdin coverage was retired from sandbox-operations. This PR specifically changes stdin forwarding/closure semantics, so a dedicated live test should assert: default non-TTY stdin closes without hanging, --stdin forwards a pipe to cat/bash, and --no-stdin closes terminal stdin.
    • Suggested test: Add a live sandbox-exec-stdio E2E job or extend sandbox-operations with explicit nemoclaw <name> exec --stdin -- cat and default non-terminal stdin no-hang assertions.

Dispatch hint

  • Workflow: .github/workflows/e2e.yaml
  • jobs input: sandbox-operations,shields-config

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: ubuntu-repo-cloud-openclaw
Optional E2E targets: ubuntu-repo-cloud-langchain-deepagents-code

Dispatch required E2E targets:

  • gh workflow run e2e.yaml --ref <pr-head-ref> --field targets=ubuntu-repo-cloud-openclaw

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • ubuntu-repo-cloud-openclaw: The PR changes the host-side sandbox exec command and runner stdio behavior, including stdin forwarding/closure and OpenClaw post-exec cleanup integration. The live-supported ubuntu-repo-cloud-openclaw target is the smallest registry target that onboards a real OpenClaw sandbox on Ubuntu/Docker and exercises the shared OpenShell sandbox exec boundary used by this surface.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field targets=ubuntu-repo-cloud-openclaw

Optional E2E targets

  • ubuntu-repo-cloud-langchain-deepagents-code: Optional adjacent coverage for the same shared sandbox exec transport against the live-supported terminal-agent onboarding path. This is not the primary target because the changed cleanup behavior is OpenClaw-specific and the Ubuntu OpenClaw target is the smallest required live surface.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field targets=ubuntu-repo-cloud-langchain-deepagents-code

Relevant changed files

  • src/commands/sandbox/exec.ts
  • src/lib/actions/sandbox/exec-policy-hint-emission.ts
  • src/lib/actions/sandbox/exec-stdio.ts
  • src/lib/actions/sandbox/exec.ts

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/reference/commands-nemohermes.mdx`:
- Around line 576-596: The stdin flag description is imprecise: `--no-stdin`
does not just “close terminal stdin explicitly,” it always forces stdin off via
`ExecOptions`/`stdin: false` and maps to the ignore/inherit/inherit behavior
regardless of terminal state. Update the wording in the `commands-nemohermes`
docs near the `--stdin` / `--no-stdin` entry and the introductory paragraph to
say `--stdin` forwards an intentional pipe, while `--no-stdin` unconditionally
closes stdin. Keep the existing default behavior description for inherited
terminal stdin and non-terminal stdin closure.

In `@docs/reference/commands.mdx`:
- Around line 706-726: Update the stdin flag description in commands.mdx to
match shouldInheritSandboxExecStdin in src/lib/actions/sandbox/exec-stdio.ts:
explicit --no-stdin always closes stdin, not just terminal stdin. Adjust the
wording in the exec docs table and the surrounding prose so it says default
behavior only inherits terminal stdin, while --no-stdin forces stdin closure
regardless of TTY state; this source should then regenerate the Hermes variant
correctly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5cfb61fb-8a3a-4655-aa72-153f0efec0b2

📥 Commits

Reviewing files that changed from the base of the PR and between 5a6f489 and c8f0cf7.

📒 Files selected for processing (10)
  • docs/reference/commands-nemohermes.mdx
  • docs/reference/commands.mdx
  • src/commands/sandbox/exec.test.ts
  • src/commands/sandbox/exec.ts
  • src/lib/actions/sandbox/exec-openclaw-permission-cleanup.test.ts
  • src/lib/actions/sandbox/exec-policy-hint-emission.ts
  • src/lib/actions/sandbox/exec-stdio.test.ts
  • src/lib/actions/sandbox/exec-stdio.ts
  • src/lib/actions/sandbox/exec.multiline-guard.test.ts
  • src/lib/actions/sandbox/exec.ts

Comment on lines +576 to +596
By default, NemoClaw inherits caller stdin only when it is a terminal.
Non-terminal or unavailable stdin is closed so SSH, CI, and other one-shot commands cannot wait on an inherited pipe.
Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close terminal stdin explicitly.

```bash
printf 'hello\n' | nemohermes my-assistant exec --stdin -- cat
ssh dgx-spark 'nemohermes my-assistant exec --no-stdin -- pwd'
```

The OpenShell exec endpoint rejects any command argument (the values after `--`) that contains a newline or carriage return, so multi-line commands such as a `bash` heredoc cannot be passed through `exec`.
NemoClaw detects this before dispatch, names the offending argument position, and exits with status `2` instead of surfacing the lower-level OpenShell `InvalidArgument` error.
Join the statements with semicolons (`nemohermes <name> exec -- bash -lc "cmd1; cmd2"`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | nemohermes <name> exec -- bash`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | nemohermes <name> exec --stdin -- bash`).
Or write the script to a file in the sandbox and run it (`nemohermes <name> exec -- bash <script-path>`).

| Flag | Description |
|------|-------------|
| `--workdir <dir>` | Working directory inside the sandbox. The directory is checked before the command runs; if it does not exist, NemoClaw reports `error: --workdir: <dir> does not exist inside the sandbox` and exits with status `1` without invoking the inner command. |
| `--tty` / `--no-tty` | Allocate a pseudo-terminal; defaults to auto-detection (on when stdin and stdout are terminals) |
| `--timeout <seconds>` | Timeout in seconds (`0` means no timeout) |
| `--stdin` / `--no-stdin` | Force caller stdin forwarding or closure (default: inherit terminal stdin; close non-terminal or unavailable stdin). |

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Imprecise wording: "close terminal stdin explicitly."

--no-stdin forces stdin: false and always maps to ["ignore", "inherit", "inherit"] regardless of whether stdin is a terminal — it closes stdin unconditionally, not just terminal stdin. The current phrasing could mislead users into thinking --no-stdin is a no-op for piped/non-terminal stdin.

📝 Proposed wording fix
-Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close terminal stdin explicitly.
+Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close stdin explicitly.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
By default, NemoClaw inherits caller stdin only when it is a terminal.
Non-terminal or unavailable stdin is closed so SSH, CI, and other one-shot commands cannot wait on an inherited pipe.
Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close terminal stdin explicitly.
```bash
printf 'hello\n' | nemohermes my-assistant exec --stdin -- cat
ssh dgx-spark 'nemohermes my-assistant exec --no-stdin -- pwd'
```
The OpenShell exec endpoint rejects any command argument (the values after `--`) that contains a newline or carriage return, so multi-line commands such as a `bash` heredoc cannot be passed through `exec`.
NemoClaw detects this before dispatch, names the offending argument position, and exits with status `2` instead of surfacing the lower-level OpenShell `InvalidArgument` error.
Join the statements with semicolons (`nemohermes <name> exec -- bash -lc "cmd1; cmd2"`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | nemohermes <name> exec -- bash`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | nemohermes <name> exec --stdin -- bash`).
Or write the script to a file in the sandbox and run it (`nemohermes <name> exec -- bash <script-path>`).
| Flag | Description |
|------|-------------|
| `--workdir <dir>` | Working directory inside the sandbox. The directory is checked before the command runs; if it does not exist, NemoClaw reports `error: --workdir: <dir> does not exist inside the sandbox` and exits with status `1` without invoking the inner command. |
| `--tty` / `--no-tty` | Allocate a pseudo-terminal; defaults to auto-detection (on when stdin and stdout are terminals) |
| `--timeout <seconds>` | Timeout in seconds (`0` means no timeout) |
| `--stdin` / `--no-stdin` | Force caller stdin forwarding or closure (default: inherit terminal stdin; close non-terminal or unavailable stdin). |
By default, NemoClaw inherits caller stdin only when it is a terminal.
Non-terminal or unavailable stdin is closed so SSH, CI, and other one-shot commands cannot wait on an inherited pipe.
Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close stdin explicitly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/commands-nemohermes.mdx` around lines 576 - 596, The stdin
flag description is imprecise: `--no-stdin` does not just “close terminal stdin
explicitly,” it always forces stdin off via `ExecOptions`/`stdin: false` and
maps to the ignore/inherit/inherit behavior regardless of terminal state. Update
the wording in the `commands-nemohermes` docs near the `--stdin` / `--no-stdin`
entry and the introductory paragraph to say `--stdin` forwards an intentional
pipe, while `--no-stdin` unconditionally closes stdin. Keep the existing default
behavior description for inherited terminal stdin and non-terminal stdin
closure.

Comment on lines +706 to +726
By default, NemoClaw inherits caller stdin only when it is a terminal.
Non-terminal or unavailable stdin is closed so SSH, CI, and other one-shot commands cannot wait on an inherited pipe.
Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close terminal stdin explicitly.

```bash
printf 'hello\n' | $$nemoclaw my-assistant exec --stdin -- cat
ssh dgx-spark '$$nemoclaw my-assistant exec --no-stdin -- pwd'
```

The OpenShell exec endpoint rejects any command argument (the values after `--`) that contains a newline or carriage return, so multi-line commands such as a `bash` heredoc cannot be passed through `exec`.
NemoClaw detects this before dispatch, names the offending argument position, and exits with status `2` instead of surfacing the lower-level OpenShell `InvalidArgument` error.
Join the statements with semicolons (`$$nemoclaw <name> exec -- bash -lc "cmd1; cmd2"`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | $$nemoclaw <name> exec -- bash`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | $$nemoclaw <name> exec --stdin -- bash`).
Or write the script to a file in the sandbox and run it (`$$nemoclaw <name> exec -- bash <script-path>`).

| Flag | Description |
|------|-------------|
| `--workdir <dir>` | Working directory inside the sandbox. The directory is checked before the command runs; if it does not exist, NemoClaw reports `error: --workdir: <dir> does not exist inside the sandbox` and exits with status `1` without invoking the inner command. |
| `--tty` / `--no-tty` | Allocate a pseudo-terminal; defaults to auto-detection (on when stdin and stdout are terminals) |
| `--timeout <seconds>` | Timeout in seconds (`0` means no timeout) |
| `--stdin` / `--no-stdin` | Force caller stdin forwarding or closure (default: inherit terminal stdin; close non-terminal or unavailable stdin). |

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Same imprecise wording: "close terminal stdin explicitly."

Per shouldInheritSandboxExecStdin in src/lib/actions/sandbox/exec-stdio.ts, an explicit --no-stdin bypasses TTY detection and always closes stdin, not just terminal stdin. Since this page is the source that commands-nemohermes.mdx is regenerated from, fixing the wording here should also propagate to the Hermes variant.

📝 Proposed wording fix
-Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close terminal stdin explicitly.
+Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close stdin explicitly.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
By default, NemoClaw inherits caller stdin only when it is a terminal.
Non-terminal or unavailable stdin is closed so SSH, CI, and other one-shot commands cannot wait on an inherited pipe.
Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close terminal stdin explicitly.
```bash
printf 'hello\n' | $$nemoclaw my-assistant exec --stdin -- cat
ssh dgx-spark '$$nemoclaw my-assistant exec --no-stdin -- pwd'
```
The OpenShell exec endpoint rejects any command argument (the values after `--`) that contains a newline or carriage return, so multi-line commands such as a `bash` heredoc cannot be passed through `exec`.
NemoClaw detects this before dispatch, names the offending argument position, and exits with status `2` instead of surfacing the lower-level OpenShell `InvalidArgument` error.
Join the statements with semicolons (`$$nemoclaw <name> exec -- bash -lc "cmd1; cmd2"`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | $$nemoclaw <name> exec -- bash`).
Pipe the script into the sandbox shell over stdin (`printf 'cmd1\ncmd2\n' | $$nemoclaw <name> exec --stdin -- bash`).
Or write the script to a file in the sandbox and run it (`$$nemoclaw <name> exec -- bash <script-path>`).
| Flag | Description |
|------|-------------|
| `--workdir <dir>` | Working directory inside the sandbox. The directory is checked before the command runs; if it does not exist, NemoClaw reports `error: --workdir: <dir> does not exist inside the sandbox` and exits with status `1` without invoking the inner command. |
| `--tty` / `--no-tty` | Allocate a pseudo-terminal; defaults to auto-detection (on when stdin and stdout are terminals) |
| `--timeout <seconds>` | Timeout in seconds (`0` means no timeout) |
| `--stdin` / `--no-stdin` | Force caller stdin forwarding or closure (default: inherit terminal stdin; close non-terminal or unavailable stdin). |
By default, NemoClaw inherits caller stdin only when it is a terminal.
Non-terminal or unavailable stdin is closed so SSH, CI, and other one-shot commands cannot wait on an inherited pipe.
Pass `--stdin` to forward an intentional pipe, or `--no-stdin` to close stdin explicitly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/reference/commands.mdx` around lines 706 - 726, Update the stdin flag
description in commands.mdx to match shouldInheritSandboxExecStdin in
src/lib/actions/sandbox/exec-stdio.ts: explicit --no-stdin always closes stdin,
not just terminal stdin. Adjust the wording in the exec docs table and the
surrounding prose so it says default behavior only inherits terminal stdin,
while --no-stdin forces stdin closure regardless of TTY state; this source
should then regenerate the Hermes variant correctly.

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.

[WSL2][CLI&UX] nemoclaw exec blocks indefinitely when stdin is a live (non-EOF) pipe — breaks pipelines/CI; stdin from /dev/null works

1 participant