Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

.NET: Python: [Bug]: Persistent PowerShell LocalShellTool returns empty stdout for commands that output objects

クローズ
#9,042 コメント 1 件 リアクション 0 件 担当者 1 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

@eavanvalkenburg がすでに取り組んでいます。

2026年10月4日 から。

  • #9043 @Aditya-XR による — オープン

評価

この issue はまだ評価されていません。

説明

.NET python reproduced skills
Description

LocalShellTool in its default mode="persistent" returns an empty stdout for any PowerShell command whose output gets formatted as a table or list. That includes Get-Location / pwd, anything piped through Select-Object, Get-Process, and [pscustomobject]. The command succeeds with exit_code=0, but the agent gets nothing back. Once an object has been written, plain strings that come after it in the same command are lost too. mode="stateless" returns all of this output.

This happens on Windows, where PowerShell is the default shell, and with pwsh on Linux, so it isn't Windows-specific.

Cause: ShellSession._build_script runs the command as & { try { Invoke-Expression $__af_cmd; ... } finally { [Console]::WriteLine('<sentinel>_' + $__af_rc) } } and leaves formatting to the host. The host's Out-Default only flushes table/list formatting when the outer pipeline ends, which is after the script block has returned. By then finally has already written the sentinel straight to [Console]. So the formatted output lands after the sentinel in the stdout buffer, and _run_locked throws it away, since it keeps only the bytes before the sentinel and then clears the buffer. Plain strings survive only because Out-Default writes them immediately.

The .NET ShellSession already handles this. BuildScript in dotnet/src/Microsoft.Agents.AI.Tools.Shell/ShellSession.cs formats the output inside the try, with the comment: "Without this, pwsh defers Out-Default formatting until the script block returns and the sentinel races ahead of the user's output in the byte stream."

Expected: persistent mode returns the same stdout as stateless mode.

Code Sample
import asyncio
import re
import shutil

from agent_framework.tools import LocalShellTool

POWERSHELL = shutil.which("pwsh") or shutil.which("powershell")
SHELLS = {
    # the argv LocalShellTool uses by default on Windows
    "persistent": [POWERSHELL, "-NoLogo", "-NoProfile", "-NonInteractive", "-Command", "-"],
    "stateless": [POWERSHELL],
}
COMMANDS = [
    "Get-Location",
    "Get-Item $PSHOME | Select-Object Name",
    "[pscustomobject]@{ Name = 'an object' }; Write-Output 'a string after it'",
    "Write-Output 'just a string'",
]


async def main() -> None:
    for mode, shell in SHELLS.items():
        async with LocalShellTool(mode=mode, shell=shell) as tool:
            for command in COMMANDS:
                result = await tool.run(command)
                stdout = re.sub(r"\x1b\[[0-9;?]*[A-Za-z]", "", result.stdout).strip()  # drop terminal escape codes
                print(f"{mode:<10} exit_code={result.exit_code} stdout={stdout!r}")
                print(f"{'':<10} ^ {command}")


asyncio.run(main())

Output on main with Linux (Debian 12, PowerShell 7.6.6):

persistent exit_code=0 stdout=''
           ^ Get-Location
persistent exit_code=0 stdout=''
           ^ Get-Item $PSHOME | Select-Object Name
persistent exit_code=0 stdout=''
           ^ [pscustomobject]@{ Name = 'an object' }; Write-Output 'a string after it'
persistent exit_code=0 stdout='just a string'
           ^ Write-Output 'just a string'
stateless  exit_code=0 stdout='Path\n----\n/tmp'
           ^ Get-Location
stateless  exit_code=0 stdout='Name\n----\n7'
           ^ Get-Item $PSHOME | Select-Object Name
stateless  exit_code=0 stdout='Name\n----\nan object\na string after it'
           ^ [pscustomobject]@{ Name = 'an object' }; Write-Output 'a string after it'
stateless  exit_code=0 stdout='just a string'
           ^ Write-Output 'just a string'

I get the same result on Windows 11 with PowerShell 7.6.6 and with Windows PowerShell 5.1, and the same with the default LocalShellTool() there, without passing shell=.

Error Messages / Stack Traces

None. The empty stdout comes back with exit_code=0.

Package Versions

agent-framework-tools: 1.0.0b261002, agent-framework-core: 1.20.0 (source checkout of main at e9857bd75)

Python Version

Python 3.13

Additional Context
  • The persistent PowerShell tests in python/packages/tools/tests/test_local_shell_tool.py only check plain strings and exit codes, so none of them covers output PowerShell formats as a table. The re-anchor test, for example, reads (Get-Location).Path, which returns a string and so doesn't hit this. Plain Get-Location would.
  • Fix: format the output inside the try, before finally writes the sentinel: Invoke-Expression $__af_cmd | Out-Default;. Unlike a per-object Out-String, this keeps PowerShell's normal formatting (one table per command). Exit codes for native failures, cmdlet errors, throw, and $LASTEXITCODE readback stay the same. I'll open a PR with this and a regression test.
主要言語
Python
スター
13.9k
フォーク
2.4k
平均マージ
1日 16時間
マージ済み PR(30日)
432

環境構築

Codespaces で開く

このプロジェクトの開発コンテナを、あなたの GitHub アカウントでブラウザ上に起動します。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

microsoft/agent-framework のほかの issue

microsoft/agent-framework の issue をすべて見る

似ている issue

Python の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。