.NET: [Bug]: Shell="cmd" echoes commands containing quotes instead of executing them and reports exit 0

未关闭
#9,914 0 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
3/5
预计耗时
半天
新手友好度
50/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
csharp, shell
领域
cli

调研方向

Start in ShellResolver.StatelessArgvForCommand, which builds the cmd /d /c argv, and in the Cmd branch of LocalShellExecutor.RunStatelessAsync, where the elements are added through ProcessStartInfo.ArgumentList. Run the 25-line reproduction on Windows to confirm that quoted commands are echoed with exit 0. Done means quoted commands such as echo "quoted value" return the correct stdout with the right exit code on Windows. Wait for maintainer agreement on the /s approach before you start.

由索引模型根据 Issue 内容生成。

描述

.NET needs-maintainer-triage python triage
Observed Behavior

LocalShellExecutor in stateless mode with Shell = "cmd" does not execute commands that contain double quotes. It prints the command text as if it were the program output and reports ExitCode == 0, so the caller has no signal that nothing ran.

LocalShellExecutor.RunAsync("powershell -NoProfile -Command \"Write-Output hello\"")
  stdout: Write-Output hello        ExitCode: 0     <-- command echoed, never executed

LocalShellExecutor.RunAsync("echo \"quoted value\"")
  stdout: \"quoted value\"          ExitCode: 0     <-- quotes arrived escaped as \" and were printed

The second case shows the mechanism: the child process receives the quote as literal \". For commands that do run despite this, their arguments are corrupted the same way.

This is the shape models emit all the time, for example powershell -NoProfile -Command "...", git commit -m "message", or echo "text". On Windows the tool silently returns a success with wrong output, which is worse than an error because the model has no reason to retry.

Expected Behavior

Quoted commands execute normally:

RunAsync("powershell -NoProfile -Command \"Write-Output hello\"")  -> stdout: hello           ExitCode: 0
RunAsync("echo \"quoted value\"")                                  -> stdout: "quoted value"   ExitCode: 0
Steps to Reproduce
  1. On Windows, create a console project targeting net10.0 and add Microsoft.Agents.AI.Tools.Shell (prerelease).
  2. Construct LocalShellExecutor in ShellMode.Stateless with Shell = "cmd".
  3. RunAsync a command containing a double quote, for example powershell -NoProfile -Command "Write-Output hello".
  4. Observe Stdout == "Write-Output hello" with ExitCode == 0: the command was echoed, not executed.
Minimal Reproduction
using Microsoft.Agents.AI.Tools.Shell;

await using var shell = new LocalShellExecutor(new LocalShellExecutorOptions
{
    Mode = ShellMode.Stateless,
    Shell = "cmd",
    AcknowledgeUnsafe = true, // unrelated to this bug: only disables the approval wrapper
    WorkingDirectory = Directory.GetCurrentDirectory(),
    Timeout = TimeSpan.FromSeconds(30),
});

var nested = await shell.RunAsync("powershell -NoProfile -Command \"Write-Output hello\"", default);
Console.WriteLine($"stdout=[{nested.Stdout.Trim()}] exit={nested.ExitCode}");
// actual:   stdout=[Write-Output hello] exit=0
// expected: stdout=[hello] exit=0

var quoted = await shell.RunAsync("echo \"quoted value\"", default);
Console.WriteLine($"stdout=[{quoted.Stdout.Trim()}] exit={quoted.ExitCode}");
// actual:   stdout=[\"quoted value\"] exit=0
// expected: stdout=["quoted value"] exit=0
Error Messages and Stack Traces

None. That is part of the problem: no exception, no stderr, exit code 0.

Package Versions
  • Microsoft.Agents.AI.Tools.Shell 1.27.0-preview.261030.1
  • (transitive) Microsoft.Agents.AI.Abstractions 1.27.0, Microsoft.Extensions.AI 10.10.0
.NET Version

.NET 10.0.402 SDK, runtime 10.0.9.

Operating System

Windows

Regression

Unknown

Additional Context

Cause. ShellResolver.StatelessArgvForCommand builds cmd /d /c <command> and LocalShellExecutor.RunStatelessAsync adds each element through ProcessStartInfo.ArgumentList. .NET escapes " as \" when it flattens ArgumentList into a command line, and cmd.exe has no notion of an escaped quote, so it terminates the /c argument at the first \" and treats the rest as text to print. cmd.exe documents /s as the way to pass a full command line: with /s, the outer pair of quotes is stripped and the remainder is used verbatim.

Suggested fix. For ShellKind.Cmd, bypass ArgumentList and set the command line as a single Arguments string:

startInfo.Arguments = $"/d /s /c \"{command}\"";

That is the same approach powershell/bash do not need, so it can stay inside the Cmd branch of RunStatelessAsync.

The persistent path (ShellSession) uses the same ArgumentList for its launch argv, but since persistent mode is not supported for cmd.exe, only stateless is affected.

Workaround. Build the command line manually for cmd instead of going through ArgumentList (what a downstream consumer does today), or avoid cmd by using Shell = "pwsh", which changes how bash-style commands in model output behave.

AI assistance. The analysis, the reproduction program and this report were prepared with an AI coding assistant. Every command output quoted here was produced by actually running the reproduction; none of it is predicted or paraphrased. Package versions, API names and source references were checked against the published package and the repository's main branch.

Verification. Reproduced locally before filing:

  • OS: Windows 10.0.26300.9711
  • SDK: .NET SDK 10.0.402, runtime 10.0.9
  • Package: Microsoft.Agents.AI.Tools.Shell 1.27.0-preview.261030.1
  • How: dotnet run on the 25-line console project in Minimal Reproduction above
  • Output: stdout=[Write-Output hello] exit=0, stdout=[\"quoted value\"] exit=0
Acknowledgements
  • I searched existing issues and did not find a duplicate (searched cmd quote, ArgumentList, LocalShellExecutor, Tools.Shell, and open issues/PRs with shell in the title; every existing shell issue targets the other SDK implementation, none target the .NET one).
  • I personally verified this behavior and the reproduction details are authentic.
  • I will wait for explicit maintainer agreement before starting implementation of a non-trivial change.
主要语言
Python
星标
14.5k
派生
2.6k
平均合并
2 天 13 小时
30 天内合并 PR
335

环境准备

在 Codespaces 中打开

在浏览器里用你自己的 GitHub 账号启动这个项目的开发容器。

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

microsoft/agent-framework 的其他 Issue

查看 microsoft/agent-framework 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。