Skip to content

fix(exec): reject multi-line exec args with recovery guidance (#5980)#5991

Merged
apurvvkumaria merged 17 commits into
mainfrom
fix/5980-exec-newline-guidance
Jul 1, 2026
Merged

fix(exec): reject multi-line exec args with recovery guidance (#5980)#5991
apurvvkumaria merged 17 commits into
mainfrom
fix/5980-exec-newline-guidance

Conversation

@yimoj

@yimoj yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

nemoclaw <name> exec forwards user argv straight to the OpenShell exec endpoint, which rejects any argument containing a newline or carriage return. Multi-line commands (for example a bash heredoc) therefore failed with a low-level InvalidArgument error and no NemoClaw-specific recovery guidance. This PR validates exec command argv before dispatch and fails with a clear, actionable NemoClaw error.

Related Issue

Fixes #5980

Changes

  • Add findMultilineExecArg() and multilineExecMessage() to src/lib/actions/sandbox/exec.ts, and wire them into execSandbox() so a newline/carriage-return command argument is rejected before spawning OpenShell.
  • The error names the offending 1-based argument position and points to the supported workarounds: semicolon-joined statements, piping a script into the sandbox shell over stdin, or running a script file. Exit status is 2, consistent with the existing usage-error path.
  • Security: the error describes the offending argument neutrally (length + line count) and never echoes its contents, so an accidentally-pasted secret cannot leak into terminal/CI logs.
  • Normal exec argv is left unchanged — we reject only what OpenShell would reject anyway, with better guidance, rather than silently rewriting shell semantics. A normal single-line command still reaches dispatch with unchanged argv.
  • Document the limitation and workarounds under nemoclaw <name> exec in docs/reference/commands.mdx, and regenerate the commands-nemohermes.mdx agent variant.

Type of Change

  • Code change with doc updates

Quality Gates

  • Tests added or updated for changed behavior
  • Existing tests cover changed behavior — justification:
  • Tests not applicable — justification:
  • Docs updated for user-facing behavior changes
  • Docs not applicable — justification:
  • Sensitive paths changed (security, policy, credentials, preflight, onboarding, inference, runner, sandbox, or messaging)
  • Sensitive-path review completed or maintainer-approved waiver recorded — reviewer/approval link/justification: sandbox exec argv path; change only adds pre-dispatch validation, never rewrites or weakens the command sent to OpenShell, and avoids echoing argument contents to prevent secret leakage.
  • Non-success, skipped, or missing CI check accepted by maintainer — check name, approval link, and follow-up issue:

Verification

  • PR description includes the DCO sign-off declaration and every commit appears as Verified in GitHub
  • Git hooks passed during commit and push, or npx prek run --from-ref main --to-ref HEAD passes
  • Targeted tests pass for changed behavior
  • Full npm test passes (broad runtime changes only)
  • Quality Gates section completed with required justifications or waivers
  • No secrets, API keys, or credentials committed
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Targeted tests: vitest run --project cli src/lib/actions/sandbox/exec.test.ts (29 passed, including the newline-guard regression tests, the secret-not-echoed test, and a dispatch-unchanged test). Full CLI project run with coverage: 4797 passed. npm run checks, npm run docs:check-agent-variants, tsc -p tsconfig.cli.json, and Biome all pass.

Real worktree-CLI E2E (reporter workflow)

Ran the compiled CLI (node ./bin/nemoclaw.js) from the worktree. The newline guard fires at the top of execSandbox, before any OpenShell dispatch or sandbox lookup, so this reproduces the reporter's exact failure deterministically.

Reporter's failing scenario — multi-line bash heredoc (now a clear NemoClaw error, exit 2, with no argument contents echoed):

$ node ./bin/nemoclaw.js sandbox exec bug5980test -- bash -lc $'cat <<EOF\nline1\nline2\nEOF'
error: command argument 3 (25 characters spanning 4 lines) contains a newline or carriage return, which OpenShell exec does not accept.
Multi-line commands (for example heredocs) cannot be passed through exec argv. Instead:
  - join statements with semicolons: nemoclaw bug5980test exec -- bash -lc "cmd1; cmd2"
  - pipe the script into the sandbox shell over stdin: printf 'cmd1\ncmd2\n' | nemoclaw bug5980test exec -- bash
  - or write the script to a file in the sandbox and run it: nemoclaw bug5980test exec -- bash <script-path>
---> exit code: 2

Previously this surfaced the bare OpenShell × status: InvalidArgument, message: "command argument 2 contains newline or carriage return characters" with no recovery guidance.

Reporter's confirmed single-line workaround still passes the guard and proceeds to OpenShell dispatch unchanged (only fails here because bug5980test is not a real sandbox in this environment):

$ node ./bin/nemoclaw.js sandbox exec bug5980test -- bash -lc "echo line1; echo line2"
Error:   × status: NotFound, message: "sandbox not found" ...
---> exit code: 1

This dispatch-unchanged behavior is also pinned by unit tests: findMultilineExecArg returns -1 for the single-line form and buildOpenshellExecArgs builds the forwarded argv ["sandbox","exec","--name","bug5980test","--","bash","-lc","echo line1; echo line2"] unchanged. The execSandbox rejection test injects a SandboxExecRunner and asserts it is never called when a multi-line argument is rejected, proving the guard fires before any OpenShell dispatch. (execSandbox resolves the OpenShell binary through a process-exiting lookup that is not present on CI runners, so the spawn itself is validated by the real worktree-CLI transcript above rather than a brittle node-builtin mock.)


Signed-off-by: Yimo Jiang yimoj@nvidia.com

Summary by CodeRabbit

  • Bug Fixes
    • Sandbox exec now rejects inner command arguments containing newline (\n) or carriage return (\r) before execution begins (Unicode line separators are allowed).
    • Error output pinpoints the exact 1-based offending argument position, provides safe guidance without echoing the payload, and exits with status 2 before other checks (like --workdir).
  • Documentation
    • Updated nemohermes <name> exec and $$nemoclaw <name> exec to document the restriction and the supported alternatives for multi-line behavior (semicolon one-liners, stdin-piped scripts, or executing a script file).
  • Tests
    • Added/expanded coverage for newline/carriage-return detection, messaging, guard ordering, and correct argument forwarding.

yimoj and others added 2 commits June 29, 2026 10:29
OpenShell's `sandbox exec` rejects any argv element containing a newline
or carriage return, so multi-line commands (heredocs) fail with a
low-level `InvalidArgument` error and no NemoClaw-specific recovery path.

Detect offending arguments before dispatch and fail with a clear,
non-zero NemoClaw error that names the 1-based argument position and
points to supported workarounds (semicolon-joined statements or running
a script file). Normal exec argv semantics are left unchanged; we only
reject what OpenShell would reject anyway, with better guidance.

Fixes #5980

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Document that `nemoclaw <name> exec` rejects arguments containing a
newline or carriage return and exits with status 2, with the
semicolon and script-file workarounds. Regenerate the nemohermes
variant page from the shared source.

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds client-side multiline argument rejection to sandbox exec before OpenShell dispatch, with injectable execution seams for tests. Related command tests, helper tests, and reference docs cover the new behavior and workaround guidance.

Changes

Multi-line exec argument guard

Layer / File(s) Summary
Detection and dispatch guard
src/lib/actions/sandbox/exec.ts
Defines multiline-argument detection and message formatting, adds injectable exec and probe dependencies, and exits with code 2 before binary resolution and workdir validation when a multiline argument is found.
Command forwarding tests
src/commands/sandbox/exec.test.ts
Adds assertions for forwarding heredoc, semicolon-joined, and workdir-bearing exec commands into execSandbox with the expected typed options.
Multiline guard tests
src/lib/actions/sandbox/exec.multiline-guard.test.ts, src/lib/actions/sandbox/exec.test.ts
Covers multiline detection, error message text, guard ordering, workaround forwarding, and the default spawn-based runner behavior.
Reference docs updates
docs/reference/commands.mdx, docs/reference/commands-nemohermes.mdx
exec reference pages for both CLI entrypoints describe multiline rejection, the reported argument position, exit status 2, and the supported workaround forms.

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

Suggested reviewers: brandonpelfrey, ericksoa

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: rejecting multiline exec args and adding recovery guidance for #5980.
Linked Issues check ✅ Passed The changes address #5980 by rejecting multiline argv, preserving single-line/semicolon behavior, and adding the requested workaround guidance.
Out of Scope Changes check ✅ Passed The extra docs, tests, and dependency-injection helpers appear to support the multiline guard and do not introduce unrelated scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/5980-exec-newline-guidance

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

@github-actions

Copy link
Copy Markdown
Contributor

@github-code-quality

github-code-quality Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Overview

Languages: TypeScript

TypeScript / code-coverage/plugin

The overall coverage in the branch is 96%. Coverage data for the branch is not yet available.

Show a code coverage summary of the most covered files.
File 3f88185 +/-
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%

TypeScript / code-coverage/cli

The overall coverage in the branch is 68%. Coverage data for the branch is not yet available.

Show a code coverage summary of the most covered files.
File 3f88185 +/-
src/lib/shields...nsition-lock.ts 86%
src/lib/actions...dbox/rebuild.ts 80%
src/lib/actions...all/run-plan.ts 80%
src/lib/state/o...oard-session.ts 80%
src/lib/state/sandbox.ts 72%
src/lib/shields/index.ts 69%
src/lib/onboard/preflight.ts 69%
src/lib/onboard...er-gpu-patch.ts 59%
src/lib/actions...licy-channel.ts 58%
src/lib/onboard.ts 20%

Updated July 01, 2026 21:50 UTC
Code Coverage is in Public Preview. Learn more and provide us with your feedback.

@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Real worktree-CLI E2E evidence (#5980)

Ran the compiled CLI (node ./bin/nemoclaw.js) from the worktree. The newline guard fires at the top of execSandbox, before any OpenShell dispatch or sandbox lookup, so this reproduces the reporter's exact failure without needing a live sandbox.

1. Reporter's failing scenario — multi-line bash heredoc:

$ node ./bin/nemoclaw.js sandbox exec bug5980test -- bash -lc $'cat <<EOF\nline1\nline2\nEOF'
error: command argument 3 contains a newline or carriage return, which OpenShell exec does not accept:
  cat <<EOF\nline1\nline2\nEOF
Multi-line commands (for example heredocs) cannot be passed through exec argv. Instead:
  - join statements with semicolons: nemoclaw bug5980test exec -- bash -lc "cmd1; cmd2"
  - or write the script to a file in the sandbox and run it: nemoclaw bug5980test exec -- bash <script-path>
---> exit code: 2

Previously this surfaced the bare OpenShell × status: InvalidArgument, message: "command argument 2 contains newline or carriage return characters" with no recovery guidance.

2. Reporter's confirmed workaround still passes the guard (reaches OpenShell dispatch unchanged):

$ node ./bin/nemoclaw.js sandbox exec bug5980test -- bash -lc "echo line1; echo line2"
Error:   × status: NotFound, message: "sandbox not found" ...
---> exit code: 1

The single-line command is not intercepted by the new guard — it proceeds to dispatch (and only fails here because bug5980test is not a real sandbox in this environment). Normal exec semantics are unchanged.

3. Carriage-return-only argument is also rejected:

$ node ./bin/nemoclaw.js sandbox exec bug5980test -- printf $'a\rb'
error: command argument 2 contains a newline or carriage return, which OpenShell exec does not accept:
  a\rb
...
---> exit code: 2

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor (Nemotron Ultra) — Changes requested

Merge posture: Do not merge yet
Primary next action: Fix PRA-1: Missing integration tests for three documented exec workarounds; then add or justify PRA-T1.
Open items: 4 required · 3 warnings · 1 suggestion · 7 test follow-ups
Since last review: 0 prior items resolved · 8 still apply · 0 new items found

Action checklist

  • PRA-1 Fix: Missing integration tests for three documented exec workarounds in test/e2e/live/:1
  • PRA-2 Fix: Security Category 8 FAIL — Missing integration validation for user-facing workarounds in test/e2e/live/:1
  • PRA-3 Fix: Acceptance clause unmet — Workarounds not verified end-to-end against real sandbox in test/e2e/live/:1
  • PRA-4 Fix: Monolith growth — exec.ts grew by 90 lines (exceeds 20-line threshold) in src/lib/actions/sandbox/exec.ts:1
  • PRA-5 Resolve or justify: Identical warning block duplicated across two command reference files in docs/reference/commands.mdx:578
  • PRA-6 Resolve or justify: Unicode line separator dispatch path not tested at command layer in src/commands/sandbox/exec.test.ts:57
  • PRA-7 Resolve or justify: Multi-line guard early exit bypasses OpenClaw cleanup — not documented as intentional in src/lib/actions/sandbox/exec.ts:385
  • 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
  • PRA-T4 Add or justify test follow-up: Runtime validation
  • PRA-T5 Add or justify test follow-up: Missing integration tests for three documented exec workarounds
  • PRA-T6 Add or justify test follow-up: Unicode line separator dispatch path not tested at command layer
  • PRA-T7 Add or justify test follow-up: Acceptance clause
  • PRA-8 In-scope improvement: No TODO comment linking duplicated warning blocks in docs/reference/commands.mdx:578

Findings index

ID Severity Category Location Required action
PRA-1 Required tests test/e2e/live/:1 Add focused Vitest integration tests in test/e2e/live/exec-workarounds.test.ts that: (1) start a test sandbox, (2) run `nemoclaw exec` with semicolon-joined command and verify success and combined output, (3) test stdin pipe variant, (4) test script file variant. Use existing SandboxClient.exec() and execShell() fixtures from test/e2e/fixtures/clients/sandbox.ts.
PRA-2 Required security test/e2e/live/:1 Add integration test coverage for the three workarounds as described in PRA-1. This satisfies both the test gap and the security testing category requirement.
PRA-3 Required acceptance test/e2e/live/:1 Add integration tests in test/e2e/live/ covering all three workarounds as described in PRA-1.
PRA-4 Required architecture src/lib/actions/sandbox/exec.ts:1 Extract the multi-line guard logic to a new module src/lib/actions/sandbox/exec-multiline-guard.ts exporting findMultilineExecArg, multilineExecMessage, describeMultilineArg, MULTILINE_ARG_PATTERN. Import and use from exec.ts. This offsets the growth and keeps exec.ts focused on dispatch orchestration.
PRA-5 Resolve/justify docs docs/reference/commands.mdx:578 If MDX/Nextra supports shared includes/snippets, factor the warning block into a common component. Otherwise, add a TODO comment in both files noting the duplication and that both files must be updated together.
PRA-6 Resolve/justify tests src/commands/sandbox/exec.test.ts:57 Add a command-layer test in src/commands/sandbox/exec.test.ts mirroring the action-layer Unicode dispatch test: verify that argv containing U+2028 is forwarded unchanged to execSandboxMock.
PRA-7 Resolve/justify security src/lib/actions/sandbox/exec.ts:385 Add a comment at the guard exit point clarifying that the multi-line guard exits before any dispatch or cleanup because the command never reaches OpenShell. The existing test confirms this behavior.
PRA-8 Improvement docs docs/reference/commands.mdx:578 Add `<!-- TODO: Keep in sync with commands-nemohermes.mdx (and vice versa) — multi-line exec warning block -->` above the warning block in both files.

🚨 Required before merge

Address these before merging unless a maintainer explicitly overrides the advisor with rationale.

PRA-1 Required — Missing integration tests for three documented exec workarounds

  • Location: test/e2e/live/:1
  • Category: tests
  • Problem: Three user-facing workarounds (semicolon join, stdin pipe, script file) are documented in commands.mdx and commands-nemohermes.mdx but NO integration test in test/e2e/live/ exercises real `nemoclaw exec` or `nemohermes exec` against a live sandbox to verify they work end-to-end.
  • Impact: Runtime validation gap: a regression in OpenShell argv handling, stdin forwarding, or script execution could silently break the documented guidance without CI detection. Users would encounter the original low-level OpenShell InvalidArgument error instead of NemoClaw's actionable guidance.
  • Required action: Add focused Vitest integration tests in test/e2e/live/exec-workarounds.test.ts that: (1) start a test sandbox, (2) run `nemoclaw exec` with semicolon-joined command and verify success and combined output, (3) test stdin pipe variant, (4) test script file variant. Use existing SandboxClient.exec() and execShell() fixtures from test/e2e/fixtures/clients/sandbox.ts.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Search test/e2e/live/ for exec workaround tests — none exist. Run `grep -r 'exec.*semicolon\|exec.*stdin\|exec.*script' test/e2e/live/` — no matches for the documented workarounds via the NemoClaw CLI.
  • Missing regression test: Integration test: execSandbox with 'bash -lc "cmd1; cmd2"' executes in sandbox and returns combined output; stdin pipe variant works; script file variant works
  • Done when: The required change is committed and verification passes: Search test/e2e/live/ for exec workaround tests — none exist. Run `grep -r 'exec.*semicolon\|exec.*stdin\|exec.*script' test/e2e/live/` — no matches for the documented workarounds via the NemoClaw CLI.
  • Evidence: No exec workaround tests found in test/e2e/live/ via file listing and grep. SandboxClient.exec() and execShell() fixtures exist and are used by sandbox-operations.test.ts.

PRA-2 Required — Security Category 8 FAIL — Missing integration validation for user-facing workarounds

  • Location: test/e2e/live/:1
  • Category: security
  • Problem: Security Category 8 (Security Testing) requires tests covering security edge cases and negative test cases verifying forbidden actions are denied. The three documented workarounds are the 'allowed' path for multi-line content but lack integration validation against a real sandbox.
  • Impact: If OpenShell changes its argv handling or NemoClaw's arg forwarding regresses, the workarounds could break without test failure. Users would receive the original low-level OpenShell error instead of NemoClaw's actionable guidance.
  • Required action: Add integration test coverage for the three workarounds as described in PRA-1. This satisfies both the test gap and the security testing category requirement.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Run `vitest run --project cli src/lib/actions/sandbox/exec.multiline-guard.test.ts` — all unit tests pass but mock the runner. No test/e2e/live exec workaround tests exist.
  • Missing regression test: Integration test covering all three documented workarounds against a real sandbox
  • Done when: The required change is committed and verification passes: Run `vitest run --project cli src/lib/actions/sandbox/exec.multiline-guard.test.ts` — all unit tests pass but mock the runner. No test/e2e/live exec workaround tests exist.
  • Evidence: Security rubric Category 8 requires tests covering security edge cases. The workarounds are the 'allowed' path for multi-line content and should be validated end-to-end. Unit tests only mock the runner.

PRA-3 Required — Acceptance clause unmet — Workarounds not verified end-to-end against real sandbox

PRA-4 Required — Monolith growth — exec.ts grew by 90 lines (exceeds 20-line threshold)

  • Location: src/lib/actions/sandbox/exec.ts:1
  • Category: architecture
  • Problem: src/lib/actions/sandbox/exec.ts grew from 329 to 419 lines (+90). The multi-line guard logic (findMultilineExecArg, multilineExecMessage, describeMultilineArg, MULTILINE_ARG_PATTERN, ExecSandboxDeps) adds significant surface area to an already large file.
  • Impact: Increased cognitive load, harder to review future changes, higher risk of accidental coupling. Rubric flags this as blocker severity for monolith growth.
  • Required action: Extract the multi-line guard logic to a new module src/lib/actions/sandbox/exec-multiline-guard.ts exporting findMultilineExecArg, multilineExecMessage, describeMultilineArg, MULTILINE_ARG_PATTERN. Import and use from exec.ts. This offsets the growth and keeps exec.ts focused on dispatch orchestration.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Count lines in src/lib/actions/sandbox/exec.ts — currently 419. After extraction, should return to ~330.
  • Missing regression test: All existing tests pass after extraction; no behavior change
  • Done when: The required change is committed and verification passes: Count lines in src/lib/actions/sandbox/exec.ts — currently 419. After extraction, should return to ~330.
  • Evidence: Diff shows +98/-4 net +90 lines. Monolith delta flagged in drift evidence.
Review findings by urgency: 4 required fixes, 3 items to resolve/justify, 1 in-scope improvement

⚠️ 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-5 Resolve/justify — Identical warning block duplicated across two command reference files

  • Location: docs/reference/commands.mdx:578
  • Category: docs
  • Problem: Identical warning block (multi-line exec guidance) duplicated across commands.mdx and commands-nemohermes.mdx with only CLI name differences (nemoclaw vs nemohermes).
  • Impact: Maintenance burden: any future update to the guidance must be applied to both files. Risk of divergence.
  • Recommended action: If MDX/Nextra supports shared includes/snippets, factor the warning block into a common component. Otherwise, add a TODO comment in both files noting the duplication and that both files must be updated together.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare the exec sections in both files — the 7-line warning block is nearly identical except for CLI name references.
  • Missing regression test: N/A - documentation maintenance issue
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare the exec sections in both files — the 7-line warning block is nearly identical except for CLI name references.
  • Evidence: Both files contain the same 7-line block starting with 'The OpenShell exec endpoint rejects any command argument...' with only nemoclaw/nemohermes differences.

PRA-6 Resolve/justify — Unicode line separator dispatch path not tested at command layer

  • Location: src/commands/sandbox/exec.test.ts:57
  • Category: tests
  • Problem: Unicode line separator (U+2028/U+2029) dispatch path is tested at action layer (exec.multiline-guard.test.ts) but not at command layer. The command layer should verify argv containing U+2028 is forwarded unchanged to execSandboxMock.
  • Impact: Gap in command-layer test coverage for a documented behavior (Unicode separators are valid argv that OpenShell accepts).
  • Recommended action: Add a command-layer test in src/commands/sandbox/exec.test.ts mirroring the action-layer Unicode dispatch test: verify that argv containing U+2028 is forwarded unchanged to execSandboxMock.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search src/commands/sandbox/exec.test.ts for U+2028 or Unicode — no matches.
  • Missing regression test: Command-layer test: argv with U+2028 forwarded to execSandboxMock unchanged
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search src/commands/sandbox/exec.test.ts for U+2028 or Unicode — no matches.
  • Evidence: Action layer test 'forwards a Unicode line-separator argument through dispatch' exists; command layer has heredoc and semicolon tests but no Unicode separator test.

PRA-7 Resolve/justify — Multi-line guard early exit bypasses OpenClaw cleanup — not documented as intentional

  • Location: src/lib/actions/sandbox/exec.ts:385
  • Category: security
  • Problem: Multi-line guard exits at line ~389 (process.exit(2)) before any OpenClaw cleanup runs. The test 'rejects a multi-line command before probing --workdir (guard runs first)' confirms this behavior, but there is no code comment documenting this as intentional.
  • Impact: Future maintainers may incorrectly assume cleanup should run and add it, or may not understand why the guard exits early. The early exit is correct (command never reaches OpenShell) but should be explicit.
  • Recommended action: Add a comment at the guard exit point clarifying that the multi-line guard exits before any dispatch or cleanup because the command never reaches OpenShell. The existing test confirms this behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read exec.ts lines 385-395 — the guard exits with process.exit(2) before binary resolution, workdir probe, or runSandboxExecCommand (which calls cleanupOpenClawAfterExec).
  • Missing regression test: N/A - documentation of intentional behavior
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read exec.ts lines 385-395 — the guard exits with process.exit(2) before binary resolution, workdir probe, or runSandboxExecCommand (which calls cleanupOpenClawAfterExec).
  • Evidence: exec.ts line 387-389: multilineIndex check → console.error → process.exit(2). No comment explaining cleanup is intentionally skipped.

💡 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-8 Improvement — No TODO comment linking duplicated warning blocks

  • Location: docs/reference/commands.mdx:578
  • Category: docs
  • Problem: No TODO comment linking the duplicated warning blocks in commands.mdx and commands-nemohermes.mdx.
  • Impact: Without a visible linkage, future editors may update one file and forget the other.
  • Suggested action: Add `<!-- TODO: Keep in sync with commands-nemohermes.mdx (and vice versa) — multi-line exec warning block -->` above the warning block in both files.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Search both files for TODO comment referencing the other file — none found.
  • Missing regression test: N/A - documentation maintenance improvement
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Grep for 'TODO.*sync' or 'TODO.*commands-nemohermes' in docs/reference/ returns no matches.
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 — test/e2e/live/exec-workarounds.test.ts: semicolon join executes and returns combined stdout. Unit tests for guard logic and command parsing are comprehensive (27 tests across 3 test files). However, the three documented workarounds (semicolon join, stdin pipe, script file) are user-facing 'allowed paths' that require integration validation against a real sandbox to satisfy Security Category 8 and the linked issue acceptance clause.
  • PRA-T2 Runtime validation — test/e2e/live/exec-workarounds.test.ts: piped script executes via inherited stdio. Unit tests for guard logic and command parsing are comprehensive (27 tests across 3 test files). However, the three documented workarounds (semicolon join, stdin pipe, script file) are user-facing 'allowed paths' that require integration validation against a real sandbox to satisfy Security Category 8 and the linked issue acceptance clause.
  • PRA-T3 Runtime validation — test/e2e/live/exec-workarounds.test.ts: bash <script-path> executes sandbox file. Unit tests for guard logic and command parsing are comprehensive (27 tests across 3 test files). However, the three documented workarounds (semicolon join, stdin pipe, script file) are user-facing 'allowed paths' that require integration validation against a real sandbox to satisfy Security Category 8 and the linked issue acceptance clause.
  • PRA-T4 Runtime validation — src/commands/sandbox/exec.test.ts: argv with U+2028 forwarded to execSandboxMock unchanged. Unit tests for guard logic and command parsing are comprehensive (27 tests across 3 test files). However, the three documented workarounds (semicolon join, stdin pipe, script file) are user-facing 'allowed paths' that require integration validation against a real sandbox to satisfy Security Category 8 and the linked issue acceptance clause.
  • PRA-T5 Missing integration tests for three documented exec workarounds — Add focused Vitest integration tests in test/e2e/live/exec-workarounds.test.ts that: (1) start a test sandbox, (2) run `nemoclaw exec` with semicolon-joined command and verify success and combined output, (3) test stdin pipe variant, (4) test script file variant. Use existing SandboxClient.exec() and execShell() fixtures from test/e2e/fixtures/clients/sandbox.ts.
  • PRA-T6 Unicode line separator dispatch path not tested at command layer — Add a command-layer test in src/commands/sandbox/exec.test.ts mirroring the action-layer Unicode dispatch test: verify that argv containing U+2028 is forwarded unchanged to execSandboxMock.
  • PRA-T7 Acceptance clause — Workarounds verified end-to-end against real sandbox — add test evidence or identify existing coverage. No tests in test/e2e/live/ for any workaround; issue [Linux][CLI&UX] nemoclaw <sb> exec rejects multi-line bash heredocs with "command argument contains newline" — single-line / semicolon workaround works #5980 explicitly requires this
Since last review details

Current findings, using the urgency labels above:

PRA-1 Required — Missing integration tests for three documented exec workarounds

  • Location: test/e2e/live/:1
  • Category: tests
  • Problem: Three user-facing workarounds (semicolon join, stdin pipe, script file) are documented in commands.mdx and commands-nemohermes.mdx but NO integration test in test/e2e/live/ exercises real `nemoclaw exec` or `nemohermes exec` against a live sandbox to verify they work end-to-end.
  • Impact: Runtime validation gap: a regression in OpenShell argv handling, stdin forwarding, or script execution could silently break the documented guidance without CI detection. Users would encounter the original low-level OpenShell InvalidArgument error instead of NemoClaw's actionable guidance.
  • Required action: Add focused Vitest integration tests in test/e2e/live/exec-workarounds.test.ts that: (1) start a test sandbox, (2) run `nemoclaw exec` with semicolon-joined command and verify success and combined output, (3) test stdin pipe variant, (4) test script file variant. Use existing SandboxClient.exec() and execShell() fixtures from test/e2e/fixtures/clients/sandbox.ts.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Search test/e2e/live/ for exec workaround tests — none exist. Run `grep -r 'exec.*semicolon\|exec.*stdin\|exec.*script' test/e2e/live/` — no matches for the documented workarounds via the NemoClaw CLI.
  • Missing regression test: Integration test: execSandbox with 'bash -lc "cmd1; cmd2"' executes in sandbox and returns combined output; stdin pipe variant works; script file variant works
  • Done when: The required change is committed and verification passes: Search test/e2e/live/ for exec workaround tests — none exist. Run `grep -r 'exec.*semicolon\|exec.*stdin\|exec.*script' test/e2e/live/` — no matches for the documented workarounds via the NemoClaw CLI.
  • Evidence: No exec workaround tests found in test/e2e/live/ via file listing and grep. SandboxClient.exec() and execShell() fixtures exist and are used by sandbox-operations.test.ts.

PRA-2 Required — Security Category 8 FAIL — Missing integration validation for user-facing workarounds

  • Location: test/e2e/live/:1
  • Category: security
  • Problem: Security Category 8 (Security Testing) requires tests covering security edge cases and negative test cases verifying forbidden actions are denied. The three documented workarounds are the 'allowed' path for multi-line content but lack integration validation against a real sandbox.
  • Impact: If OpenShell changes its argv handling or NemoClaw's arg forwarding regresses, the workarounds could break without test failure. Users would receive the original low-level OpenShell error instead of NemoClaw's actionable guidance.
  • Required action: Add integration test coverage for the three workarounds as described in PRA-1. This satisfies both the test gap and the security testing category requirement.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Run `vitest run --project cli src/lib/actions/sandbox/exec.multiline-guard.test.ts` — all unit tests pass but mock the runner. No test/e2e/live exec workaround tests exist.
  • Missing regression test: Integration test covering all three documented workarounds against a real sandbox
  • Done when: The required change is committed and verification passes: Run `vitest run --project cli src/lib/actions/sandbox/exec.multiline-guard.test.ts` — all unit tests pass but mock the runner. No test/e2e/live exec workaround tests exist.
  • Evidence: Security rubric Category 8 requires tests covering security edge cases. The workarounds are the 'allowed' path for multi-line content and should be validated end-to-end. Unit tests only mock the runner.

PRA-3 Required — Acceptance clause unmet — Workarounds not verified end-to-end against real sandbox

PRA-4 Required — Monolith growth — exec.ts grew by 90 lines (exceeds 20-line threshold)

  • Location: src/lib/actions/sandbox/exec.ts:1
  • Category: architecture
  • Problem: src/lib/actions/sandbox/exec.ts grew from 329 to 419 lines (+90). The multi-line guard logic (findMultilineExecArg, multilineExecMessage, describeMultilineArg, MULTILINE_ARG_PATTERN, ExecSandboxDeps) adds significant surface area to an already large file.
  • Impact: Increased cognitive load, harder to review future changes, higher risk of accidental coupling. Rubric flags this as blocker severity for monolith growth.
  • Required action: Extract the multi-line guard logic to a new module src/lib/actions/sandbox/exec-multiline-guard.ts exporting findMultilineExecArg, multilineExecMessage, describeMultilineArg, MULTILINE_ARG_PATTERN. Import and use from exec.ts. This offsets the growth and keeps exec.ts focused on dispatch orchestration.
  • Expected follow-up: Fix before merge or get explicit maintainer override.
  • Verification: Count lines in src/lib/actions/sandbox/exec.ts — currently 419. After extraction, should return to ~330.
  • Missing regression test: All existing tests pass after extraction; no behavior change
  • Done when: The required change is committed and verification passes: Count lines in src/lib/actions/sandbox/exec.ts — currently 419. After extraction, should return to ~330.
  • Evidence: Diff shows +98/-4 net +90 lines. Monolith delta flagged in drift evidence.

PRA-5 Resolve/justify — Identical warning block duplicated across two command reference files

  • Location: docs/reference/commands.mdx:578
  • Category: docs
  • Problem: Identical warning block (multi-line exec guidance) duplicated across commands.mdx and commands-nemohermes.mdx with only CLI name differences (nemoclaw vs nemohermes).
  • Impact: Maintenance burden: any future update to the guidance must be applied to both files. Risk of divergence.
  • Recommended action: If MDX/Nextra supports shared includes/snippets, factor the warning block into a common component. Otherwise, add a TODO comment in both files noting the duplication and that both files must be updated together.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Compare the exec sections in both files — the 7-line warning block is nearly identical except for CLI name references.
  • Missing regression test: N/A - documentation maintenance issue
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Compare the exec sections in both files — the 7-line warning block is nearly identical except for CLI name references.
  • Evidence: Both files contain the same 7-line block starting with 'The OpenShell exec endpoint rejects any command argument...' with only nemoclaw/nemohermes differences.

PRA-6 Resolve/justify — Unicode line separator dispatch path not tested at command layer

  • Location: src/commands/sandbox/exec.test.ts:57
  • Category: tests
  • Problem: Unicode line separator (U+2028/U+2029) dispatch path is tested at action layer (exec.multiline-guard.test.ts) but not at command layer. The command layer should verify argv containing U+2028 is forwarded unchanged to execSandboxMock.
  • Impact: Gap in command-layer test coverage for a documented behavior (Unicode separators are valid argv that OpenShell accepts).
  • Recommended action: Add a command-layer test in src/commands/sandbox/exec.test.ts mirroring the action-layer Unicode dispatch test: verify that argv containing U+2028 is forwarded unchanged to execSandboxMock.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Search src/commands/sandbox/exec.test.ts for U+2028 or Unicode — no matches.
  • Missing regression test: Command-layer test: argv with U+2028 forwarded to execSandboxMock unchanged
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Search src/commands/sandbox/exec.test.ts for U+2028 or Unicode — no matches.
  • Evidence: Action layer test 'forwards a Unicode line-separator argument through dispatch' exists; command layer has heredoc and semicolon tests but no Unicode separator test.

PRA-7 Resolve/justify — Multi-line guard early exit bypasses OpenClaw cleanup — not documented as intentional

  • Location: src/lib/actions/sandbox/exec.ts:385
  • Category: security
  • Problem: Multi-line guard exits at line ~389 (process.exit(2)) before any OpenClaw cleanup runs. The test 'rejects a multi-line command before probing --workdir (guard runs first)' confirms this behavior, but there is no code comment documenting this as intentional.
  • Impact: Future maintainers may incorrectly assume cleanup should run and add it, or may not understand why the guard exits early. The early exit is correct (command never reaches OpenShell) but should be explicit.
  • Recommended action: Add a comment at the guard exit point clarifying that the multi-line guard exits before any dispatch or cleanup because the command never reaches OpenShell. The existing test confirms this behavior.
  • Expected follow-up: Resolve in this PR or explain why the risk is acceptable.
  • Verification: Read exec.ts lines 385-395 — the guard exits with process.exit(2) before binary resolution, workdir probe, or runSandboxExecCommand (which calls cleanupOpenClawAfterExec).
  • Missing regression test: N/A - documentation of intentional behavior
  • Done when: The risk is fixed or explicitly justified in the PR. Verification: Read exec.ts lines 385-395 — the guard exits with process.exit(2) before binary resolution, workdir probe, or runSandboxExecCommand (which calls cleanupOpenClawAfterExec).
  • Evidence: exec.ts line 387-389: multilineIndex check → console.error → process.exit(2). No comment explaining cleanup is intentionally skipped.

PRA-8 Improvement — No TODO comment linking duplicated warning blocks

  • Location: docs/reference/commands.mdx:578
  • Category: docs
  • Problem: No TODO comment linking the duplicated warning blocks in commands.mdx and commands-nemohermes.mdx.
  • Impact: Without a visible linkage, future editors may update one file and forget the other.
  • Suggested action: Add `<!-- TODO: Keep in sync with commands-nemohermes.mdx (and vice versa) — multi-line exec warning block -->` above the warning block in both files.
  • Expected follow-up: Prefer a current-PR fix when local to changed code; defer only with rationale or linked follow-up.
  • Verification: Search both files for TODO comment referencing the other file — none found.
  • Missing regression test: N/A - documentation maintenance improvement
  • Done when: The local improvement is applied, or the PR notes why it should be deferred.
  • Evidence: Grep for 'TODO.*sync' or 'TODO.*commands-nemohermes' in docs/reference/ returns no matches.

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 Jun 29, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: shields-config
Optional E2E: cron-preflight-inference-local

Dispatch hint: shields-config

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • shields-config (about 45 minutes; hosted Linux live sandbox with hosted inference): This is the focused existing live job that exercises the public nemoclaw <sandbox> exec -- ... path against an onboarded OpenClaw sandbox, including the post-exec mutable config permission cleanup contract that is implemented in the changed action file. It gives merge-blocking confidence that successful single-line exec dispatch still works after the new multiline guard and dependency seams.

Optional E2E

  • cron-preflight-inference-local (about 45 minutes; hosted Linux live sandbox with hosted inference): Useful adjacent confidence because it runs a live installed sandbox and invokes nemoclaw <sandbox> exec -- sh -c ... for an inference.local availability probe. It is not merge-blocking here because shields-config is the more direct existing coverage for the public exec command and cleanup behavior.

New E2E recommendations

  • sandbox exec multiline argv guard (medium): Existing live E2E jobs cover successful single-line nemoclaw exec dispatch, but none appears to assert the new failure mode: a multiline argv element is rejected locally with exit 2 before OpenShell dispatch, without echoing the payload, and documented stdin/script-file workarounds still succeed in a live sandbox.
    • Suggested test: Extend an existing live exec-oriented job such as shields-config, or add a focused live job, to onboard a sandbox and assert: nemoclaw <sandbox> exec -- bash -lc <argument containing LF or CR> exits 2 with the actionable multiline message and no payload leak; the command is not dispatched; printf 'cmd1\ncmd2\n' | nemoclaw <sandbox> exec -- bash succeeds via inherited stdin; and the same branded message path is covered for nemohermes if a Hermes sandbox is available.

Dispatch hint

  • Workflow: e2e.yaml
  • jobs input: shields-config

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Scenario Recommendation

Required Vitest E2E scenarios: cron-preflight-inference-local-vitest
Optional Vitest E2E scenarios: sessions-agents-cli-vitest

Dispatch required Vitest E2E scenarios:

  • gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=cron-preflight-inference-local-vitest

Workflow run

Full Vitest E2E advisor summary

Vitest E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required Vitest E2E scenarios

  • cron-preflight-inference-local-vitest: The PR changes the host-side nemoclaw <sandbox> exec action by adding a newline/carriage-return guard before OpenShell dispatch. The cron preflight live Vitest job onboards a real OpenClaw sandbox and invokes nemoclaw <sandbox> exec -- sh -c ..., exercising the changed exec wrapper against a real OpenShell sandbox while validating the single-line script workaround still dispatches successfully.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=cron-preflight-inference-local-vitest

Optional Vitest E2E scenarios

  • sessions-agents-cli-vitest: Additional adjacent coverage for nemoclaw <sandbox> exec argument forwarding through OpenClaw device and agent CLI commands in a live sandbox, useful if maintainers want broader confidence in the exec argv path beyond the cron preflight probe.
    • Dispatch: gh workflow run e2e-vitest-scenarios.yaml --ref <pr-head-ref> --field jobs=sessions-agents-cli-vitest

Relevant changed files

  • src/lib/actions/sandbox/exec.ts

@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor — No blocking findings

Merge posture: No blocking advisor findings
Primary next action: Add or justify PRA-T1 and any related test follow-ups.
Open items: 0 required · 0 warnings · 0 suggestions · 4 test follow-ups
Since last review: 0 prior items resolved · 0 still apply · 0 new items found

Action checklist

  • 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
  • PRA-T4 Add or justify test follow-up: Runtime validation
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 — Public CLI fake-OpenShell validation: `nemoclaw sandbox exec alpha -- bash -lc $'cat <<EOF\nline1\nEOF'` exits 2, prints the redacted CR/LF guidance, and does not invoke the fake OpenShell binary.. Checked-in unit/action/command tests are strong for the local contract, but this is a public CLI-to-OpenShell dispatch boundary. A small checked-in public CLI/fake-OpenShell validation would add confidence without relying on PR-provided transcripts or external E2E status.
  • PRA-T2 Runtime validation — Public CLI fake-OpenShell validation: `nemoclaw sandbox exec alpha -- bash -lc "echo line1; echo line2"` invokes fake OpenShell with argv after `--` unchanged.. Checked-in unit/action/command tests are strong for the local contract, but this is a public CLI-to-OpenShell dispatch boundary. A small checked-in public CLI/fake-OpenShell validation would add confidence without relying on PR-provided transcripts or external E2E status.
  • PRA-T3 Runtime validation — Public CLI fake-OpenShell validation: `nemoclaw sandbox exec alpha -- bash -lc "echo single-line-test"` remains outside the newline guard and reaches dispatch unchanged.. Checked-in unit/action/command tests are strong for the local contract, but this is a public CLI-to-OpenShell dispatch boundary. A small checked-in public CLI/fake-OpenShell validation would add confidence without relying on PR-provided transcripts or external E2E status.
  • PRA-T4 Runtime validation — If a lightweight fake-shell fixture already exists, validate that `printf 'cmd1\ncmd2\n' | nemoclaw sandbox exec alpha -- bash` preserves inherited stdin/stdio to the dispatched child.. Checked-in unit/action/command tests are strong for the local contract, but this is a public CLI-to-OpenShell dispatch boundary. A small checked-in public CLI/fake-OpenShell validation would add confidence without relying on PR-provided transcripts or 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.

@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`:
- Line 472: The documentation in the NemoHermes commands page should use one
sentence per line and the literal `nemohermes` CLI name instead of the shorthand
`exec -- ...` fragments. Update the affected prose so each sentence stands on
its own line, and rewrite the workaround examples to show full `nemohermes`
command invocations; use the relevant command examples in this section to keep
the wording consistent with the page’s single-agent naming convention.

In `@docs/reference/commands.mdx`:
- Line 580: The paragraph in the shared command reference should be rewritten to
match the shared-doc example style by placing one sentence per source line in
the MDX content. Update the inline host CLI examples to use $$nemoclaw instead
of bare exec -- fragments, and keep the existing guidance about semicolon-joined
commands or running a script file inside the sandbox. Use the surrounding
commands/docs wording and the command example formatting pattern in this section
to locate and revise the text.
🪄 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: 7fb91386-47f8-4773-8057-27a33e71554b

📥 Commits

Reviewing files that changed from the base of the PR and between c6113be and 845c7d0.

📒 Files selected for processing (4)
  • docs/reference/commands-nemohermes.mdx
  • docs/reference/commands.mdx
  • src/lib/actions/sandbox/exec.test.ts
  • src/lib/actions/sandbox/exec.ts

Comment thread docs/reference/commands-nemohermes.mdx Outdated
Comment thread docs/reference/commands.mdx Outdated
Address PR review feedback on the multi-line exec guard:

- Security: stop echoing the offending argument's contents in the
  error. A multi-line value can carry pasted secrets, env files, or
  key material, and printing even a truncated preview risks leaking it
  into terminal/CI logs. Describe it neutrally (length + line count)
  instead; the 1-based position is enough to locate it.
- Acceptance (#5980): add the issue's pipe-into-shell workaround. exec
  inherits stdin, so `printf '...' | nemoclaw <sb> exec -- bash` works.
- Correctness: narrow the docs from "any argument" to "any command
  argument (the values after `--`)" so the promise matches what is
  validated; flag values such as --workdir are out of scope.
- Docs: one sentence per source line and literal CLI names
  ($$nemoclaw / nemohermes) per the docs style guide.

Tests: assert the secret payload is never printed, the pipe workaround
is surfaced, and a normal single-line command still reaches OpenShell
dispatch with unchanged argv.

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Response to PR Review Advisor

PRA-2 (security — avoid echoing argument contents): Fixed in 3594786. multilineExecMessage() no longer interpolates the argument; it now reports a neutral size description (N characters spanning M lines) plus the 1-based position. Added a regression test that passes a -----BEGIN PRIVATE KEY------shaped multi-line value and asserts the payload is never printed.

PRA-4 (acceptance — pipe-into-shell workaround): Fixed in 3594786. exec runs with stdio: "inherit", so stdin is forwarded to the in-sandbox process. The message and both docs pages now surface the issue's pipe option — printf 'cmd1\ncmd2\n' | nemoclaw <sb> exec -- bash — alongside the semicolon and script-file workarounds. A unit test asserts the pipe guidance is present.

PRA-3 (correctness — "any argument" vs command argv): Resolved by narrowing the docs to "any command argument (the values after --)", matching exactly what findMultilineExecArg(command) validates. This intentionally keeps --workdir and other flag values out of scope: the reported flow is command argv (heredocs), and the existing --workdir path already has its own pre-dispatch validation (validateWorkdirOrFail, exit 1). A newline-bearing --workdir would still surface OpenShell's lower-level error, which is an out-of-scope edge case not covered by #5980; I preferred narrowing the promise over widening this PR.

PRA-1 (architecture — source-of-truth review):

  • Invalid state: a user exec command argument containing \n/\r, which OpenShell's gRPC API rejects with InvalidArgument.
  • Source boundary: OpenShell owns the exec argv validation; NemoClaw cannot relax it. The actionable layer is the NemoClaw CLI boundary, which is where the user gets the error.
  • Source-fix constraint: accepting/forwarding multi-line argv would require OpenShell to change its API contract; that is not ours to change, and silently base64-wrapping the command (as the internal wrapSandboxShellScript helper does) would alter shell/signal semantics for a user-supplied command, so it is deliberately not done on the generic exec path.
  • Regression test: findMultilineExecArg, multilineExecMessage, and execSandbox (rejects heredoc before spawn; forwards single-line argv unchanged) — plus a real worktree-CLI transcript in the PR body.
  • Removal condition: if OpenShell's exec endpoint ever accepts newline-bearing argv, this guard (and the docs note) can be removed.

PRA-T1–T8 (runtime/test follow-ups): Covered by (a) a hermetic execSandbox test that mocks the OpenShell runtime and asserts a single-line command reaches dispatch with unchanged argv and propagates the remote exit code (T1/T2/T3/T6/T7), and (b) the real worktree-CLI transcript in the PR body running the reporter's exact heredoc and semicolon forms (T3/T5/T6/T7). T4 (newline in --workdir) is intentionally not added per PRA-3 above. T8 (accept-and-forward multi-line argv) is the explicitly rejected design alternative — #5980's expected result allows either accepting heredocs or returning actionable guidance, and this PR chooses the latter without an OpenShell change.

Fix two CI failures introduced by the previous commit:

- detect-private-key (static-checks): the redaction test used a literal
  `-----BEGIN PRIVATE KEY-----` fixture, which the secret scanner
  flagged. Use a neutral multi-line sentinel that still proves the
  argument contents are never echoed.
- cli-test-shards: the dispatch test mocked `node:child_process` and the
  OpenShell runtime via `vi.mock`, which does not intercept the dynamic
  `require()` calls in `execSandbox`; on CI (no openshell on PATH)
  `getOpenshellBinary()` called `process.exit(1)`, so the assertion saw
  exit 1 instead of 0. Inject a `SandboxExecRunner` (mirroring the
  existing `WorkdirProbeRunner` seam) so the rejection test can assert no
  dispatch occurs without spawning, and cover the single-line
  pass-through via the pure argv builder instead of a brittle runtime
  mock. The real dispatch path stays covered by the worktree-CLI
  transcript on the PR.

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Follow-up on PRA-T1 / PRA-T2 (runtime validation)

These are now addressed by the regression tests on the current head (78dde86):

  • PRA-T1 — rejects a heredoc before spawning OpenShell: execSandbox is given an injected SandboxExecRunner and the test asserts the runner is never called when a multi-line command argument is supplied, while process.exit(2) fires. This proves the guard short-circuits before any OpenShell dispatch (and before the process-exiting getOpenshellBinary() lookup).
  • PRA-T2 — forwards a normal single-line command unchanged: covered by findMultilineExecArg(["bash","-lc","echo line1; echo line2"]) === -1 plus buildOpenshellExecArgs(...) asserting the forwarded argv is byte-for-byte unchanged. The end-to-end spawn is demonstrated by the real worktree-CLI transcript in the PR description (the single-line form passes the guard and reaches OpenShell dispatch).

I intentionally did not unit-test the spawn itself through execSandbox: it resolves the OpenShell binary via getOpenshellBinary(), which calls process.exit(1) when openshell is absent (as on CI runners), and vi.mock cannot intercept the dynamic require() it uses. A node-builtin spawn mock there was brittle (it caused the initial cli-test-shards failure), so the spawn path is validated by the worktree-CLI transcript instead.

@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

Re: "PR review advisor unavailable" (low-confidence fallback) + PRA-T1

This comment is the degraded fallback from a model that failed to load. The companion advisor run on this same head (78dde86) completed successfully and reports "No blocking advisor findings" (0 required · 0 warnings · 0 suggestions), so a manual re-review is not required.

PRA-T1 (runtime validation): the changed behavior in src/lib/actions/sandbox/exec.ts is covered by:

  • a unit test that injects a SandboxExecRunner into execSandbox and asserts no dispatch occurs when a multi-line command argument is rejected (exit 2 before the OpenShell binary lookup);
  • findMultilineExecArg / buildOpenshellExecArgs unit tests proving a single-line command passes the guard with unchanged argv;
  • a real worktree-CLI transcript in the PR description running the reporter's exact heredoc (exit 2 with guidance) and single-line (-- reaches dispatch) forms.

The docs changes (commands.mdx, commands-nemohermes.mdx) are prose-only and validated by npm run docs:check-agent-variants and the green preview check.

Address PR Review Advisor PRA-T1/PRA-T2: add parser/command-level tests
on SandboxExecCommand for the exact scenarios the advisor named —
forwarding a multi-line heredoc verbatim to the action guard, and
preserving --workdir while forwarding a single-line command unchanged.
The real exit-2-before-dispatch guard remains asserted at the action
level (execSandbox rejection test).

Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@yimoj

yimoj commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator Author

PRA-T1 / PRA-T2 — command/parser-level tests added (045b3df)

Added the exact command-level scenarios you named, in src/commands/sandbox/exec.test.ts:

  • PRA-T1: SandboxExecCommand.run(["alpha", "--", "bash", "-lc", "cat <<EOF\nline1\nline2\nEOF"]) is asserted to forward the heredoc argv verbatim to the action. The real exit-2-before-dispatch guard (with the matching 1-based argument position) is asserted directly at the action level in src/lib/actions/sandbox/exec.test.ts ("rejects a multi-line command argument before dispatch", which also asserts the injected runner is never invoked).
  • PRA-T2: SandboxExecCommand.run(["alpha", "--workdir", "/sandbox", "--", "bash", "-lc", "echo line1; echo line2"]) is asserted to preserve --workdir and forward the single-line command argv unchanged.

Together with the action-level guard/redaction/argv tests, this covers both the negative (heredoc rejected) and positive (single-line forwarded) command paths.

@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression labels Jun 29, 2026
@wscurran

Copy link
Copy Markdown
Contributor

Address non-blocking PR Review Advisor items on #5991:

- PRA-2: mark findMultilineExecArg @internal (test-only export).
- PRA-3: replace the inert vi.mock of the OpenShell binary lookup (the
  dynamic require defeats vi.mock) with an injectable resolveBinary seam on
  execSandbox, and add a test that dispatches the single-line semicolon
  workaround through execSandbox, asserting the forwarded argv and exit code.
- PRA-4: thread the existing workdir probe runner through execSandbox and add
  a test proving the workdir probe still runs after the multi-line guard for a
  single-line command (surfaces the workdir error and exits 1, not the
  multi-line error).

The 4th execSandbox parameter (added earlier in this PR, unreleased) becomes an
optional ExecSandboxDeps seam object; production callers pass three args and
are unaffected.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@yimoj

yimoj commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

Response to PR Review Advisor (Nemotron Ultra)

All items were non-blocking (merge_as_is, 0 required). Addressed on 929ca228b:

PRA-2 (findMultilineExecArg not marked internal) — fixed. Added a /** @internal */ JSDoc tag; it remains exported only for unit testing.

PRA-3 (semicolon workaround test only validated argv-build, not dispatch) — fixed. Root cause: the prior vi.mock("../../adapters/openshell/runtime") was inertexecSandbox resolves the binary via a dynamic require(), which vi.mock does not intercept (verified empirically: the mock returned the real on-disk path). I replaced it with an injectable resolveBinary seam and added a test that dispatches bash -lc "echo line1; echo line2" through execSandbox, asserting the forwarded argv is byte-for-byte correct and the inner exit code (0) propagates. This closes PRA-T1 at the unit level for the semicolon workaround.

PRA-4 / PRA-T6 (workdir validation after guard not tested) — fixed. Threaded the existing workdir probe runner through execSandbox and added a test: a valid single-line command with a missing --workdir now asserts exit 1 with the workdir error (and explicitly not the multi-line error), proving guard ordering (multi-line check → workdir probe) and that the probe still runs and dispatch is skipped.

PRA-1 / PRA-T1–T3, T5 (live-sandbox integration test for the three workarounds) — justified, not added. A test/e2e-scenario test that boots a real sandbox is disproportionate for a host-side guard that fires before any OpenShell dispatch, and live E2E is opt-in in this repo. The three workarounds are standard POSIX/shell behavior, not NemoClaw logic. Argv forwarding is now covered hermetically (PRA-3 dispatch test + byte-for-byte buildOpenshellExecArgs assertions), and the E2E Advisor already maps end-to-end coverage of nemoclaw <name> exec to the existing sessions-agents-cli-e2e scenario. The PR also carries a real worktree-CLI transcript.

PRA-5 (duplicate doc block) — justified, not changed. The two blocks are not identical: commands.mdx uses the $$nemoclaw Fern variable and commands-nemohermes.mdx uses the literal nemohermes command name (the regenerated per-CLI-name variant). A shared Fern include cannot carry that per-page CLI-name substitution, so the advisor's own fallback ("acceptable for two files") applies.

CLI type-check clean; src/lib/actions/sandbox (626) and src/commands/sandbox (55) test lanes green.

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

E2E Target Recommendation

Required E2E targets: shields-config
Optional E2E targets: None

Dispatch required E2E targets:

  • gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=shields-config

Workflow run

Full E2E target advisor summary

E2E Target Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E targets

  • shields-config: The PR changes the public nemoclaw <sandbox> exec action path. The shields-config free-standing live E2E job exercises a live OpenClaw sandbox and the documented nemoclaw <name> exec -- ... path, making it the smallest wired live E2E dispatch that covers this surface.
    • Dispatch: gh workflow run e2e.yaml --ref <pr-head-ref> --field jobs=shields-config

Optional E2E targets

  • None.

Relevant changed files

  • 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.

🧹 Nitpick comments (1)
src/lib/actions/sandbox/exec.test.ts (1)

222-250: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a multiline + --workdir ordering assertion.

Lines 222-250 only prove that single-line argv still reaches probeWorkdir. They do not prove the new guarantee that a multiline argv exits with code 2 before probeWorkdir runs when --workdir is also present, so that ordering could regress without this suite noticing.

Suggested test shape
+  it("rejects a multi-line command before probing --workdir", async () => {
+    const exitSpy = vi.spyOn(process, "exit").mockImplementation(((code?: number) => {
+      throw new Error(`exit:${code}`);
+    }) as never);
+    const errSpy = vi.spyOn(console, "error").mockImplementation(() => {});
+    const run = vi.fn(() => ({ status: 0 }));
+    const probeWorkdir = vi.fn(() => ({ status: 0 }));
+
+    await expect(
+      execSandbox(
+        "alpha",
+        ["bash", "-lc", "printf 'a\nb'"],
+        { workdir: "/workspace" },
+        { run, resolveBinary: () => "openshell", probeWorkdir },
+      ),
+    ).rejects.toThrow("exit:2");
+
+    expect(probeWorkdir).not.toHaveBeenCalled();
+    expect(run).not.toHaveBeenCalled();
+    expect(exitSpy).toHaveBeenCalledWith(2);
+    expect(errSpy.mock.calls.map((call) => String(call[0])).join("\n")).toContain(
+      "contains a newline or carriage return",
+    );
+  });

As per path instructions, "Review tests for behavioral confidence rather than implementation lock-in."

🤖 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 `@src/lib/actions/sandbox/exec.test.ts` around lines 222 - 250, The current
execSandbox test only covers the single-line argv path, so add a new assertion
in exec.test.ts that a multiline argv with --workdir exits with code 2 before
probeWorkdir is called. Extend the existing execSandbox coverage by spying on
probeWorkdir and run, then invoke execSandbox with a newline-containing command
and a workdir option, and assert the process exits with the multiline error,
probeWorkdir is never reached, and the command is not dispatched. Use
execSandbox, probeWorkdir, and the existing exitSpy/errSpy setup to keep the
check focused on ordering.

Source: Path instructions

🤖 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.

Nitpick comments:
In `@src/lib/actions/sandbox/exec.test.ts`:
- Around line 222-250: The current execSandbox test only covers the single-line
argv path, so add a new assertion in exec.test.ts that a multiline argv with
--workdir exits with code 2 before probeWorkdir is called. Extend the existing
execSandbox coverage by spying on probeWorkdir and run, then invoke execSandbox
with a newline-containing command and a workdir option, and assert the process
exits with the multiline error, probeWorkdir is never reached, and the command
is not dispatched. Use execSandbox, probeWorkdir, and the existing
exitSpy/errSpy setup to keep the check focused on ordering.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 539fb00e-1856-41fd-b71e-408e0a925a23

📥 Commits

Reviewing files that changed from the base of the PR and between 045b3df and 929ca22.

📒 Files selected for processing (2)
  • src/lib/actions/sandbox/exec.test.ts
  • src/lib/actions/sandbox/exec.ts

Address the re-run PR Review Advisor and CodeRabbit items on #5991:

- PRA-4: add dispatch tests for the two remaining documented workarounds —
  stdin-pipe (`printf ... | exec -- bash`, script over stdin so argv is just
  `bash`) and script-file (`exec -- bash <script-path>`) — each injecting a
  success runner and asserting the forwarded argv and exit code. Together with
  the existing semicolon dispatch test, all three documented workarounds now
  have hermetic unit-level runtime validation.
- CodeRabbit (review 4597559898): add an ordering assertion that a multi-line
  argv with --workdir exits 2 before the workdir probe runs (probe and runner
  never called), locking the guard-before-probe ordering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
@yimoj

yimoj commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

Response to re-run PR Review Advisor (Nemotron Ultra) + CodeRabbit (head 9fbca4592)

PRA-4 (add stdin-pipe and script-file workaround dispatch tests) — done. Added two cases to the execSandbox multi-line guard block, each injecting a success runner and asserting the forwarded argv + exit code:

  • stdin-pipe: execSandbox("sb", ["bash"], …) — the multi-line script travels over stdin (stdio: "inherit"), so the argv is just bash and dispatches.
  • script-file: execSandbox("sb", ["bash", "/sandbox/run.sh"], …).

All three documented workarounds (semicolon, stdin-pipe, script-file) now have hermetic unit-level runtime validation of the exact argv each produces.

CodeRabbit review 4597559898 (ordering assertion) — done. Added rejects a multi-line command before probing --workdir (guard runs first): a multi-line argv with --workdir exits 2, and both probeWorkdir and the runner are asserted never-called — locking the guard-before-probe ordering.

PRA-3 (doc duplication) — justified, no change. docs/reference/commands-nemohermes.mdx is not hand-maintained: it is generated from commands.mdx by scripts/sync-agent-variant-docs.ts (carries a "generated … Do not edit by hand" banner) and a sync-check fails CI if the two drift (commands-nemohermes.mdx is out of sync. Run npm run docs:sync-agent-variants). The advisor's suggested TODO ("both files must be updated together") would contradict the real workflow — only commands.mdx is edited, then the variant is regenerated. The duplication is machine-enforced, so a shared include is unnecessary.

PRA-1 (Required) / PRA-2 (Security Cat. 8) / PRA-T1–T5 (live-sandbox integration test in test/e2e-scenario/ for the three workarounds) — justified, not added. Concrete evidence:

  • The newline/CR guard fires host-side, before any OpenShell dispatch (execSandbox exits 2 prior to resolveBinary()), so the reporter's bug never reaches a sandbox — a live-sandbox test cannot exercise the fixed code path.
  • The three workarounds are standard POSIX/OpenShell behavior (bash -lc "a; b", stdin pipe, bash <file>), not NemoClaw logic; a live test would validate bash/OpenShell, not this PR.
  • test/e2e-scenario live scenarios are opt-in (ephemeral Brev, mutate real state) and do not run in required PR CI, so such a test would not "catch regressions in CI" as the finding intends.
  • End-to-end coverage of nemoclaw <name> exec is already mapped by the E2E Advisor to the existing sessions-agents-cli-e2e scenario.
  • The argv each workaround produces is now asserted byte-for-byte by hermetic unit dispatch tests (this commit + 929ca228b), and the PR carries a real worktree-CLI transcript.
  • On the security framing (PRA-2): the security-relevant surface here is not echoing argument contents (potential secret leak), which is unit-tested (describes the argument by size without echoing its contents); the workarounds themselves are not a security boundary.

Tests: src/lib/actions/sandbox/exec.test.ts → 34 passed; tsc -p tsconfig.cli.json + biome clean.

@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: 1

🤖 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 `@src/lib/actions/sandbox/exec.test.ts`:
- Around line 280-303: The current execSandbox test only asserts argv forwarding
via the run stub, so it does not verify the stdin-pipe workaround behavior.
Update the coverage around execSandbox and its CLI path to assert an observable
outcome at the public boundary: either add a higher-level CLI test for the
stdin-over-pipe flow, or make the existing test verify that the runner seam
preserves inherited stdin behavior rather than just mock-call arguments. Keep
the focus on execSandbox and the run/resolveBinary wiring so the workaround
cannot regress silently.
🪄 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: 244e6629-f50c-4ddb-8dc1-4788556eb14e

📥 Commits

Reviewing files that changed from the base of the PR and between 929ca22 and 9fbca45.

📒 Files selected for processing (1)
  • src/lib/actions/sandbox/exec.test.ts

Comment thread src/lib/actions/sandbox/exec.test.ts Outdated
yimoj and others added 6 commits June 30, 2026 07:35
…ce-of-truth

Address review follow-ups on the multi-line exec guard:
- Add a dispatch test that exercises the default runner and asserts spawnSync
  inherits stdio, so a regression dropping inherited stdin (which the documented
  pipe workaround relies on) fails instead of passing on argv shape alone.
- Reframe the stdin-pipe test name/comment to reflect it pins argv shape only.
- Rename the semicolon workaround test to match the newer workaround tests.
- Document the guard's invalid state, source boundary, regression coverage, and
  removal condition in exec.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
The guard regex intentionally covers \r and \n only because OpenShell rejects
exactly "newline or carriage return characters". Unicode line separators
(U+2028/U+2029) are valid argv OpenShell accepts, so broadening the pattern
would reject commands OpenShell would otherwise run. Record this assumption so
the scope is an explicit decision, not an oversight.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
- Add a findMultilineExecArg case asserting U+2028/U+2029 pass the guard,
  locking in the documented decision that the guard mirrors OpenShell's
  CR/LF-only rejection rather than a general line-break notion.
- Comment describeMultilineArg's split so the CRLF/CR/LF handling and the
  "bare CR spans two lines" behavior are explicit for future maintainers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
…olon test

- Move findMultilineExecArg, multilineExecMessage, and the execSandbox
  multi-line guard (#5980) suites into exec.multiline-guard.test.ts so the
  feature's coverage is self-contained and exec.test.ts stays focused on argv
  construction and the workdir probe.
- Add a command-layer test that mirrors the action-layer semicolon workaround
  test, asserting the single-line semicolon command is forwarded to the action
  guard unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
Rename the command-layer semicolon workaround test to "forwards the semicolon
workaround to dispatch" so it mirrors the action-layer test name exactly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
…count

- Assert a U+2028-bearing argument passes the guard and dispatches unchanged at
  the execSandbox boundary, confirming the CR/LF-only assumption end-to-end on
  the dispatch path (not just findMultilineExecArg).
- Assert a trailing newline counts as a second (empty) line in the size
  description, pinning the documented line-count behavior.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Yimo Jiang <yimoj@nvidia.com>
cjagwani and others added 4 commits July 1, 2026 13:47
Merge origin/main into fix/5980-exec-newline-guidance. Resolved:
- src/lib/actions/sandbox/exec.ts: keep main's async spawn dispatch
  (runSandboxExecChild + signal forwarding + OpenClaw permission cleanup,
  #6060); re-apply the PR's pre-dispatch multiline-argv guard on top, threaded
  through main's deps seam. The guard exits 2 before dispatch and never echoes
  argument contents.
- docs/reference/commands.mdx: place the PR's agent-agnostic multiline guidance
  after main's AgentOnly exit-code blocks.
- src/lib/actions/sandbox/exec.multiline-guard.test.ts: update the
  default-runner stdio-inherit test to assert main's async spawn
  (spawn(..., { stdio: 'inherit' })) instead of the retired spawnSync default.

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…removal trigger (#5991)

Signed-off-by: Charan Jagwani <cjagwani@nvidia.com>

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Vitest E2E Target Results — ✅ All requested jobs passed

Run: 28547848169
Workflow ref: fix/5980-exec-newline-guidance
Requested targets: (default — all supported)
Requested jobs: sessions-agents-cli,cron-preflight-inference-local
Summary: 2 passed, 0 failed, 0 cancelled, 0 skipped

Job Result
cron-preflight-inference-local ✅ success
sessions-agents-cli ✅ success

@cjagwani cjagwani 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.

Approving.

The exec multiline-argv guard correctly resolves #5980 via the issue's option (b): a clear NemoClaw error that names the offending 1-based argument position and the three workarounds (semicolon-join / stdin-pipe / script-file), exits 2 before any OpenShell dispatch, and never echoes the argument contents (secret-safe) — verified against the live CLI. It only rejects what OpenShell already rejects (\r/\n); single-line argv passes through unchanged.

  • Security review: clean (no content leak, exit-before-dispatch, no side effects).
  • build:cli / typecheck:cli / biome clean; 51 exec unit tests pass.
  • Recommended live E2E passed against a real sandbox — sessions-agents-cli + cron-preflight-inference-local (run 28547848169).
  • The main-merge conflicts were resolved onto main's async-spawn dispatch (#6060), with the guard re-applied on top and the default-runner stdio-inherit test updated accordingly.

Note: the currently-red checks are run cancellations from unrelated concurrent main-merge activity on the branch (build-typecheck logged "operation was canceled"), not failures in this PR. A clean re-run on a settled head should go green.

@apurvvkumaria apurvvkumaria merged commit 845dd41 into main Jul 1, 2026
46 of 56 checks passed
@apurvvkumaria apurvvkumaria deleted the fix/5980-exec-newline-guidance branch July 1, 2026 21:57
ericksoa pushed a commit that referenced this pull request Jul 2, 2026
## Summary
- Add the `v0.0.72` release-note section with links to the deeper docs
pages for installer recovery, command diagnostics, inference, policy,
and sandbox repair changes.
- Document the custom preset `allowed_ips` guard for user-authored
policy files.

## Related Issue
None.

## Source summary
- #6132 -> `docs/about/release-notes.mdx`: Summarizes installer and
upgrade recovery before generic onboarding, with links to quickstart and
lifecycle docs.
- #6087 -> `docs/network-policy/customize-network-policy.mdx`: Documents
that user-authored custom presets reject `allowed_ips` for ordinary
endpoints; also summarized in release notes.
- #5975 -> `docs/about/release-notes.mdx`: Summarizes safer curl-based
inference probes that keep API keys out of process arguments.
- #6044 -> `docs/about/release-notes.mdx`: Summarizes compact `channels
status` configuration reporting.
- #6096 -> `docs/about/release-notes.mdx`: Summarizes OpenClaw EC2
metadata discovery disablement and links to security guidance.
- #5980 and #5991 -> `docs/about/release-notes.mdx`: Summarizes `exec`
multiline argument rejection and recovery guidance.
- #6023 -> `docs/about/release-notes.mdx`: Summarizes
registered-provider diagnostics for `inference set` failures.
- #6074 -> `docs/about/release-notes.mdx`: Summarizes the refreshed
NVIDIA Endpoints featured-model selection behavior.
- #5969 -> `docs/about/release-notes.mdx`: Summarizes `credentials add`
provider credential registration.
- #6060 -> `docs/about/release-notes.mdx`: Summarizes mutable OpenClaw
config permission restoration after `exec`.
- #6134 -> `docs/about/release-notes.mdx`: Summarizes restored Tavily
access for managed Python workflows.
- #6089 -> `docs/about/release-notes.mdx`: Summarizes Hermes runtime
version-scheme comparison during upgrade checks.
- #6131 -> `docs/about/release-notes.mdx`: Summarizes OpenClaw gateway
watchdog recovery behavior.
- #5976 and #5990 -> `docs/about/release-notes.mdx`: Summarizes prompt
stdin EOF cancellation behavior during onboarding.
- #5540 -> `docs/about/release-notes.mdx`: Summarizes clarified
host-level and per-sandbox status command scope.
- #5978 and #6018 -> `docs/about/release-notes.mdx`: Summarizes
policy-denial log breadcrumbs in connect shells.

## Testing
- `npm run docs:sync-agent-variants`
- `npm run docs`
- Commit hooks passed during `git commit`, including commitlint and
gitleaks.
- Pre-push hook passed during `git push`, including TypeScript CLI and
package/tag version sync.

## Checklist
- [x] Documentation updated.
- [x] `npm run docs` completed with 0 errors and 1 existing Fern
warning.
- [x] No source code or generated build artifacts committed.

Signed-off-by: Miyoung Choi <miyoungc@nvidia.com>

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Documentation**
* Added release notes for v0.0.72 covering improved installer recovery,
clearer CLI diagnostics, safer inference setup and provider switching,
better credential handling, stronger policy boundaries, and more robust
runtime repair behavior.
* Updated network policy guidance to clarify when `allowed_ips` can be
used, including a specific exception for the sandbox-to-host bridge
endpoint.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression v0.0.72 Release target

Projects

None yet

4 participants