Agentao 0.4.22
A Windows release. This repository had never run a test on Windows. It does
now, on Python 3.10 and 3.12. The first run to reach the whole suite failed 155
tests on 3.10 and 162 on 3.12, and refused to collect 13 modules at all — six
product defects and a long tail of tests written by someone who had only ever
seen a POSIX path separator. None of the six is a "Windows bug": each is
something POSIX absorbs quietly and Windows bills for.
On top of that, run_shell_command can use PowerShell, and the reference CLI no
longer lets a model choose how its own commands are rendered to the human
approving them.
The headline:
- PowerShell on Windows, opt-in.
"shell": {"dialect": "powershell"}in the
user-levelpermissions.jsonfindspwsh.exe, elsepowershell.exe, and
launches it directly. The Windows default is unchanged — nothing
configured still means%COMSPEC% /c, the same environment and the same
command floor. - The tool-confirmation prompt is inert. Model-authored text went straight
into a Richconsole.print. A raw escape could erase the line just printed
and reprint something else;[black on black]needs no control byte at all
and renders the tail of a command invisible. That prompt is where a human
arbitrates every shell command in the default posture. - Six Windows defects, none of them cosmetic. Every edit of a CRLF file
doubled its carriage returns. Two session saves in one clock tick destroyed
the first. Every memory read and write leaked a SQLite connection, which on
Windows means a host could not delete its own workspace. - The
slowclean-install tier now runs. Nine tests that build a wheel and
install it into fresh environments had been selected by a marker no CI job
ever passed. One of them had been failing since June.
PowerShell
The Windows shell has always been %COMSPEC% /c with a command floor written
for POSIX syntax. Now the dialect is a configuration, and four things about the
launch are worth knowing:
- The interpreter is found by walking known install locations and the absolute
directories ofPATH, nevershutil.which. On Windows that function
prepends the current directory to the search and cannot be told not to, so a
checkout containing a file namedpwsh.exewould be what agentao starts. - The command travels as
-EncodedCommand, base64 of UTF-16LE, so quotes,
backslashes, percent signs and newlines survive with no layer in between
having to agree about escaping. - A fixed trailer turns the last statement's outcome into an exit code. A
native command's own non-zero code is preserved; a cmdlet that only wrote an
error gives 1. A bareexit $LASTEXITCODEreports a stale 0 for that second
case, which is a failing command reported as success. - Windows PowerShell 5.1 wraps a redirected error stream in CLIXML, unwrapped
here by bounded text extraction rather than by an XML parser: the stream
carries the output of model-written code, and the standard library's parser
expands internal entities.
The command floor's dialect-independent half runs on every dialect, always, so
nothing it refuses today is allowed on the PowerShell path. A table of Windows
irrecoverable classes runs in addition — formatting a volume, clearing a disk,
deleting shadow copies, disabling BitLocker, a recursive Remove-Item on a
drive root — reading PowerShell's built-in aliases and abbreviated parameters,
because a table written in canonical names alone refuses one spelling of six
while reading as coverage.
This replaces a much larger design that was never released. That one
resolved the interpreter by identity: attested images, a trusted-root chain over
every ancestor directory, a closed child environment. The measurement that ended
it is that on an elevated Windows token the trust predicate is true of
everything, so the trusted set is empty, the ladder runs empty, and every shell
call is denied. Running elevated is common on Windows. Nothing about it was ever
in a release, so there is no migration; if you were tracking main, the
changelog lists what moved.
Two PowerShell 5.1 behaviours documented rather than worked around
Both were measured on a Windows runner, and neither is reachable from anything
agentao writes:
- What 5.1 pipes into a native command begins with a UTF-8 byte order mark.
Five prelude variants were tried, including no prelude at all; 5.1 emitted the
mark in every one and PowerShell 7 in none. - Cmdlets that write files default to the system ANSI code page there, so
Set-Content -Value '中文'writes??. The prelude sets the two stream
encodings and stops, because changing a cmdlet's default changes what a user's
command means — and 5.1's ownutf8is UTF-8 with a BOM.
What the Windows job found
Six product defects on the first run, and a seventh on a later one:
- Every edit of a CRLF file doubled its carriage returns.
- The state directory moved on every run when there was no home directory,
because the privacy check tested POSIX mode bits that Windows never sets. - Every memory read and write leaked a SQLite connection.
with self._connect() as connreads like a resource scope and is not. - Two session saves inside one clock tick destroyed the first. Windows' clock is
far coarser than six digits of microseconds suggest. - An atomic write could fail because someone else had the file open.
- A healthy ACP server was reported as an immediate exit, because the startup
check waited a flat 50 ms and Windows process creation routinely takes longer. - A background PowerShell command returned a process id and ran nothing. Under
DETACHED_PROCESSboth interpreters exit 0 with empty output without
executing a statement; PowerShell hosts itself in a console and that flag
leaves it none.
Also in this release
run_shell_commandpromised the modelbash -cand delivered/bin/sh -c.
It is bash now, resolved rather than assumed, so an image with no bash still
works.- A configured
shell.pathnamed an interpreter that never ran: the value
reached the resolved spec and stopped there. - The shell tool's description names the interpreter that will read the command
instead of sayingcmd /con Windows unconditionally. - The
[full]dependency baseline compares distribution names rather than
pinned versions. The churn hid a real change:distroandtqdmleft the
closure whenopenaiwent 2.x to 3.x.
Full detail, including the API surface that moved, is in the changelog under
0.4.22.