# Agent Note: pwsh 工具与 bash 对齐

Status: implemented

[English](2026-08-02-pwsh-tool-bash-parity.md) | 中文

## 问题

首个 Windows 原生基础交付的 `dsh-tool-pwsh` 是刻意最小的画像——仅前台、无后台任务、受管环境只有三个硬编码 `DSH_*` 键、以及一个未声明就偏离 bash 工具的 marker 故事（"恒打 `[exit code: N]`"）。模型可见约定曾与实现脱节：描述承诺了渲染器从未执行的 spill 路径报告，README 宣称了不存在的导出与工具未做的渲染，工具自己的测试还钉死了有损行为。最小画像还让 `DSH_*` contributor seam 因缺席而重复：向 `ctx.bashEnv` 贡献环境事实的插件对 pwsh 调用毫无作用。

## 决策

`dsh-tool-pwsh` 现在逐调用镜像 `dsh-tool-bash`，其模型可见文本精确描述这一行为：

- **渲染完全采用 bash 故事**：stdout、带标记的 `[stderr]` 段、带 spill 路径的截断通知、空体渲染 `(no output)`、退出 marker 仅限非零退出——干净退出不产生 marker。描述与 `tool:pwsh` prompt section 精确陈述这一点（"Non-zero exits are reported as `[exit code: N]` markers"），刻意不复制 bash prompt 中与其自身渲染矛盾的 "every result" 措辞。
- **`run_in_background` 经通用任务运行时接线**，与 bash 工具完全一致：预检、owner 注册、`task_output`/`task_kill` 控制与相同的结果映射。其背后是 `pwsh-local` 早已镜像好的 `start()` 句柄。
- **`DSH_*` 环境共享而非复制**：`BashEnvRegistry` 从 `dsh-tool-bash` 迁入新的工具无关包 `@deepseek-ai/dsh-bash-env`（`ctx.bashEnv` + 内置事实 + session-persistence contributor），两个 shell 工具都注入它。contributor 对 pwsh 调用与 bash 调用一视同仁；因此，共享环境的所有权不属于任何一个面向模型的 shell 工具。
- **Windows 现实在 bash 无对应处钉死**：每条命令都在 UTF-8 输出 preamble 下运行，使 Windows PowerShell 5.1 兜底无法经 UTF-8 解码的 collector 破坏非 ASCII 输出；prompt 教授 Windows 强制终止以无 signal 的 exit 1 结算。
- **范围外，不变**：持久 PTY shell（后端仅限 Linux/macOS；ConPTY 属路线图）。sandbox 升级随 [Windows ACL sandbox 决策](2026-08-08-windows-acl-restricted-token-sandbox.md) 稍后交付——pwsh 工具现在携带 sandbox 拒绝渲染与同轮次 `sandbox_permissions` 升级面，外加其描述中的 Windows ConstrainedLanguage 契约。带退出 pill 的 pwsh 专属 terminal 卡已随 [pwsh UI 呈现与 bash 对齐](2026-08-05-pwsh-ui-bash-parity.md) 决策另行交付。

## 备选方案

**保留最小画像，只修声明。** 否决：从 bash 复制的文本约定在缺少对应实现时会漂移；最小工具加准确声明仍让 pwsh 调用没有后台执行、没有 contributor 对等、并留下一个必须永远重新辩护的偏离 marker 故事。

**在加载时拒绝不匹配的执行器方言。** 合并前尝试过并撤回：在 `BashExecutor` 上加 `ShellDialect` 标记（`bash` | `powershell`），两个 shell 工具在挂载的执行器说另一种方言时抛错。它迫使每个执行器实现——包括每个测试与示例的 fake——都要声明 dialect，为一道仓内及合理部署中都没有目标可拦的护栏（交付组合总是把 tool-pwsh 配 `dsh-pwsh-local`、tool-bash 配 `dsh-bash-local`）给每个 shell 工具测试添噪。配对约定改由各工具 README 记录。

**提取完全共享的工具实现基座（抽象 shell 方言，两个薄叶子）。** 考虑后推迟：bash-env 提取与结构镜像（`render.ts`/`background.ts` 孪生）是它要立足的基础；在出现第三种方言或持久 PTY 孪生、让抽象的形态可观察之前，不做完整基座。

## 后果

- bash 与 pwsh 工具在前台、后台与沙箱化 shell 工作上行为可互换（sandbox 面随 Windows ACL sandbox 决策到来），pwsh 的 prompt/描述句每句都有渲染器背书——reviewer 的“拿代码 grep 对证”检查通过。
- 对齐也反向发生过一次：pwsh 工具的结构化前台中止（`HarnessError('tool call aborted', TOOL_ABORTED)`，name 为 `AbortError`）被回移到 bash 工具，取代其无码的 `Error('command aborted')`——这是模型可见/入日志的变更，由两侧的精确形状测试与 cancel-tool-calls fixture 钉住。
- `@deepseek-ai/dsh-bash-env` 成为新的交付包；`dsh-tool-bash` 的 `dshHome` 配置迁往那里，因此挂载 shell 工具的组合也必须挂载 `bash-env`（spine bundle 已如此）。
- Windows 专属语义（CRLF 归一化、强制终止 exit-1/signal-null、仅 POSIX 的自信号）一如既往由测试钉住。
- pwsh 工具的 per-file 覆盖门禁由可脚本化的 fake-executor 套件（`tests/tools.spec.ts`）承担；真实 pwsh 的集成与 Loader 组合套件在无 `pwsh` 的宿主自跳过，与 bash 套件的分工一致。
- 路线图提案的 parity 阶段已交付；terminal 卡呈现阶段随 [pwsh UI 呈现与 bash 对齐](2026-08-05-pwsh-ui-bash-parity.md) 决策交付（TUI 本身已移除），剩余阶段是 Windows 默认组合。
