win32 hosts booting a shipped profile now get pwsh-local as the ctx.bash executor and tool-pwsh as the shell tool through the base bundle's new windows.cordis.patch.yml platform layer, injected by the launcher between the bundle layers and the user layers on win32. bash-sandbox, tool-bash, permission, and ui-permission are disabled there: the POSIX-only executor cannot run on Windows, and dsh-permission requires a confining executor. Overriding the default is a composition decision through the user's cordis.patch.yml; there is no environment override channel. apps/cli and dsh-base re-declare dsh-pwsh-local/dsh-tool-pwsh so the profile module fallback links them for cold starts (the profiles rework had dropped them from the CLI closure). Promotes the windows-pwsh-default Agent Note from proposed to implemented and documents the platform layer in the base bundle README.
4.6 KiB
Agent Note: PowerShell executor and pwsh tool
Status: implemented
English | 中文
Problem
The harness spoke one shell dialect on every platform: bash. Windows hosts could run it only through WSL or Git-Bash shims, and the shipped dsh-bash-local executor is POSIX-only (bash hardcoded, process-group semantics POSIX). The Windows roadmap — defaulting hosts to pwsh, later pwsh TUI/GUI rendering — had no execution foundation: there was no PowerShell implementation of the bash executor seam and no model-facing tool that taught the PowerShell dialect. The bash tool itself is also far larger than a Windows-first profile needs: background tasks, sandbox escalation, and the persistent-PTY twin are all bash-shaped surface that a minimal pwsh tool should not carry.
Decision
Two new packages under packages/bash/:
@deepseek-ai/dsh-pwsh-local— a local implementation of thectx.bashexecutor seam overctx.subprocess, mirroringdsh-bash-localcall-for-call:resolve()defaults and caps from config,run()fuses the config-clamped timeout with the caller's signal through one deadline,start()returns a consuming background handle whose processes belong to the subprocess service. The command string rides as ONE argv element topwsh -NoLogo -NoProfile -NonInteractive -Command, so PowerShell parses it and no shell-quoting layer exists. Executable resolution (resolvePwshPath) is a pure function of(configured, env, platform): explicit config first, then Windows probes PowerShell 7's install, PATH entries (quotes stripped), and Windows PowerShell 5.1, else a barepwshvia PATH.@deepseek-ai/dsh-tool-pwsh— the model-facing tool overctx.bash, PowerShell-dialect by contract, mirroringdsh-tool-bashcall-for-call minus the sandbox surface: foreground andrun_in_backgroundexecution through the generic task runtime, managedDSH_*environment through the shareddsh-bash-envregistry, and the bash marker/truncation rendering story (a clean exit produces no marker). The parity decision supersedes this note's minimal-profile tool description.
Windows vitest coverage is deliberately NOT part of this change: the repo's Windows CI lane owns build/static gates, and unit coverage runs on Linux, where both packages' suites run against a real pwsh (preinstalled on the GitHub-hosted runners) or self-skip when absent. The vitest windowsUnsupportedPackages exclusion narrows from packages/bash/* to the bash-requiring packages so the pwsh suites can also run natively on Windows dev machines.
The roadmap beyond this decision — defaulting Windows hosts to pwsh (bash off), and pwsh TUI/GUI rendering — is recorded separately as the Windows pwsh default decision.
Alternatives considered
Extend dsh-bash-local with a pwsh mode. Rejected: the executor's identity is the shell it spawns; a second dialect inside one package doubles its config surface (shell switches) and its test matrix, and the two dialects' quirks (signal facts on Windows, quoting domains) belong to their own packages' documentation.
Extend dsh-tool-bash with a dialect parameter. Rejected: the bash tool's background/sandbox surface is bash-shaped; a pwsh mode would either hide it (conditional schema churn) or inherit it (surface the minimal profile explicitly rejects). The minimal twin keeps the model contract honest.
Wire the pwsh tool into the shipped CLI compositions now. Rejected: mounting tool-pwsh + pwsh-local in base.cordis.yml would change the shipped roster before the Windows-default decision lands; this change ships the capability and its wiring points (apps/cli dependencies, tsconfig projects) without switching any default.
Consequences
- The bash executor seam gains a second, Windows-native implementation with an identical request/spec contract, so model-facing consumers beyond
tool-pwsh(hooks bridges, in-process plugins) can run PowerShell without dialect shims. tool-pwshis the model-visible Windows-first shell tool: behaviorally interchangeable with the bash tool for foreground and background work (minus sandbox), with prompt guidance that states the marker contract precisely.- Windows semantics differ where the platform differs: forced termination reports exit 1 with no signal (so
signal/killedstatus facts are POSIX-only), and PowerShell writes CRLF, which tests normalize. - The CLI gains two workspace dependencies and two tsconfig projects without mounting either plugin — the composition decision stays with the Windows-default proposal.