5f97b2416e
Teammate spawn (`agent` tool, `task_create`) failed on Windows with exit
127 and "command not found" against a backslash-mangled Python path,
even though the same `bash -lc "..."` command worked interactively.
Root cause: `subprocess_backend.spawn` built a single shell command
string with a `KEY='val' python ...` env-prefix, then routed through
`bash -lc` via `asyncio.create_subprocess_exec`. Two failures stacked:
1. The env-prefix string used Python `repr()` for values, and Git
Bash's `-lc` argument parser consumed backslashes inside the
quoted segments as escape sequences (`\U` -> `U`).
2. Even with proper `shlex.quote` of every command part — keeping
backslashes intact — Git Bash launched via
`asyncio.create_subprocess_exec` could not exec a Windows-pathed
Python interpreter, returning `command not found` for the same
argv that interactive bash exec'd fine.
Fix: don't use a shell at all for teammate spawn.
* Add `argv: list[str] | None` to `TaskRecord` plus matching kwargs
on `BackgroundTaskManager.create_shell_task` /
`create_agent_task`. Either `command` (shell) or `argv` (direct
exec) must be supplied; both is rejected.
* `_start_process` runs the argv path via
`asyncio.create_subprocess_exec(*argv, env=...)` directly, with no
bash wrapper.
* `subprocess_backend.spawn` builds an argv list (via
`get_teammate_command()` plus inherited CLI flags) and hands it to
`create_agent_task(argv=..., env=...)`. Inherited env vars now
flow through `env=` rather than being embedded in the command
string.
* The legacy shell-evaluated `command=` path is preserved verbatim
for callers (e.g. `BashTool`) that legitimately want shell
semantics — only teammate spawn migrates to direct exec.
Tests: 5 new unit tests in `tests/test_swarm/test_subprocess_backend.py`
and 3 in `tests/test_tasks/test_manager.py` lock in the contract — env
plumbed via `env=` kwarg, argv list preserves Windows backslashed
paths, command/argv mutual exclusion, direct-exec round-trip.
Validated end-to-end on Windows 11 against a real CEO/Lead workflow:
CEO successfully calls `agent` tool, Lead spawns at depth=1 and
issues 29 tool calls. Same path was producing exit-127 task logs
before this change.