dsh-gitbash-shell 0.39.4 → 0.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,224 @@
1
+ # 实验性讨论:Windows 安全拒绝、typed native argv 与显式路径转换
2
+
3
+ > **状态:实验性实现讨论 / Draft,Unreleased。**
4
+ > 本文介绍实验实现的设计、接口和工程取舍。它尚未完成生产验收,不是发布候选或完整安全认证。
5
+
6
+ 关联:[验证矩阵与复现](<experimental-native-verification.md>)、[变更记录](<../CHANGELOG.md>)。
7
+
8
+ ## 1. 要讨论的决定
9
+
10
+ 本提案把三个容易混为一谈的问题拆开:
11
+
12
+ 1. **没有可靠的 Windows Git Bash 围栏时怎么办?** 当前实验选择 fail-closed,不再“标注无围栏后继续”。
13
+ 2. **必须精确保留 Windows 原生参数、明确解码原生输出时怎么办?** 新增独立 typed argv 入口,复用 Core 托管执行。
14
+ 3. **原生 Node API 如何使用模型得到的 MSYS 路径?** 新增完整值运行时转换,不再把源码字符串猜测当作可靠机制。
15
+
16
+ 另补 Agent 路由指引,让上述机制不是仅存在于 README 中的“需要用户手动教模型”的能力。
17
+
18
+ 维护者已在 [PR #26 裁决](https://github.com/KannaKuron/dsh-gitbash-shell/pull/26#issuecomment-6146363513)接受 fail-closed 方向,要求默认开启且可实时关闭的行 Config 开关。正式验收门槛仍未完成。
19
+
20
+ ## 2. 问题与非目标
21
+
22
+ ### 2.1 MSYS 参数转换
23
+
24
+ 在 Bash 启动原生程序时,MSYS 可能把 `/all`、`/c` 当作路径。单命令 EXCL、双斜杠等只适用于确定场景;EXCL 按前缀匹配,排除 `/c` 也可能影响真正的 `/c/...` 文件参数。
25
+
26
+ 实验不全局设置 `MSYS2_ARG_CONV_EXCL=*`,不重写 Bash command,不猜某个字符串究竟是 option、路径还是程序源码。
27
+
28
+ ### 2.2 输出编码
29
+
30
+ UTF-8 捕获不等于原生程序输出 UTF-8。locale 变量不保证改变其代码页;同一调用、甚至同一流可以混合编码。没有替换字符也不能证明转码正确。
31
+
32
+ 实验只支持显式已知编码。**未知同流混编码不能普遍可靠解码;损坏后的字符串不能普遍还原原始字节。**
33
+
34
+ ### 2.3 源码字面量猜测
35
+
36
+ legacy `codePaths` 无法识别 `/c/users` 是路径还是路由数据,也不能可靠处理所有动态表达式。它的默认值和既有兼容机制没有在本提案中移除。
37
+
38
+ 显式 helper 是完整值接口,不是“更聪明的全程序重写器”。处理路径状业务数据时建议关闭 `codePaths`;普通工具路径参数翻译不因此关闭。
39
+
40
+ ### 2.4 真正 Windows confinement
41
+
42
+ 本提案**没有修复 OS 围栏**。匹配研究纠正了旧泛化:现代 MSYS cygheap 使用 VirtualAlloc,signal pipe 使用显式 SD;实际 Core 已补 TokenDefaultDacl。不能据此声称“再补默认 DACL 就解决”或“所有未来方案都无解”。
43
+
44
+ IPC/Token/AppContainer/VM 路线仍需隔离环境的真实正负权限验证;这些 OS 隔离机制不属于本提案的实现范围。
45
+
46
+ ## 3. 原则与体验变化
47
+
48
+ | 项目 | 旧行为或既有原则 | 当前实验 |
49
+ |---|---|---|
50
+ | Windows 受限自有 Bash | 无围栏执行并标注 unconfined | RO/WW/缺失策略在执行前拒绝 |
51
+ | 默认 Shell | Git for Windows Bash | 保持;原生入口是显式补充,不是隐式 fallback |
52
+ | root PowerShell/provider | route B 不替换 | 保持 |
53
+ | 原生参数 | 通过 Bash/MSYS,可能转换 | typed native 入口只转换标明的路径 |
54
+ | Node 路径 | 字面量尽力重写 | 可显式转换运行时完整值;legacy 默认未改 |
55
+ | 编码 | 原有 UTF-8 捕获边界 | 新入口逐流显式编码,默认 utf8/replace |
56
+ | 设置/界面 | 现有 Config/UI | `experimentalNative` volatile 布尔,默认 true,同卡实时编辑 |
57
+ | 同装 dsh-ops 的受限会话 | 本插件 bash fail-closed;ops_bash 只发 full-access | 不是零命令工具:`ops_*` 在场时补位逻辑不遮蔽 pwsh(`inlineNeeds` 的 hidePwsh 短路,宿主 pwsh 故事保留),官方执行器带真围栏可直接跑命令;bash 要回来需切 danger-full-access 或关 experimentalNative(后者同时放开 ops_bash) |
58
+
59
+ **运行时开关:** 设置 → 插件 → dsh-gitbash-shell →「实验原生执行 / fail-closed」,权威值是 `gitbash-shell` 行 Config 的 `experimentalNative`,默认 true(包括旧配置缺键)。开启时受限/未确定 Windows Bash 拒绝,两个 companions 在能力可用且不冲突时注册。关闭时实时注销两个工具(旧 SDK 引用也拒绝),Bash 恢复旧版明确标注 `enforcement: 'unconfined'` 的受限执行并一次性日志告知;未确定策略保持旧版宿主委派。关闭不提供真实围栏,shell 文件写入限制不执行;官方文件工具策略始终独立。
60
+
61
+ 变更作用于现有会话后续调用,无需重启,不终止已开始的后台 producer。native 在准备入口、异步准备后、执行入口及最终 spawn 重读开关;已关闭则不得开始新进程。重新开启恢复工具注册与拒绝机制。`posixPaths`、`codePaths`、`subagentDialect` 独立,不改变安全开关。拒绝错误说明设置关闭路径及用户在会话访问模式控件选择 `danger-full-access` 的路径,Agent 不得自行调整。
62
+
63
+ 21 份词典均已补齐开关本地化,保留工具名、UNCONFINED 和 danger-full-access 的事实,维护者可继续审阅译文。
64
+
65
+ ## 4. Agent 应如何选择
66
+
67
+ | 意图 | 选择 | 禁止的理解 |
68
+ |---|---|---|
69
+ | Shell 脚本、管道、重定向、globbing、POSIX utilities | `bash` | 不能把权限拒绝当作改用另一个执行入口的许可 |
70
+ | Windows `.exe` 精确 literal argv,或已知逐流编码 | 可见的 `gitbash_native` | 不是 shell source,也不是通用脚本包装器 |
71
+ | 完整 MSYS 路径交给原生 Node fs/path | 可见的 `gitbash_to_native_path` | 不转换路由、搜索数据、command/code;不授文件权限 |
72
+ | 真正 PowerShell cmdlet/服务/WMI | 可见且开关允许的 `pwsh` | 不调用隐藏名称、不把 PowerShell 嵌进 Bash 绕行 |
73
+ | 权限/访问拒绝 | 停止并说明 | 不自动换模式、解释器、文件工具或 Node I/O |
74
+ | 显示/解码失败 | 核对 producer exit 与输出事实 | 不盲目重复有副作用的命令 |
75
+
76
+ [路由生成](<../src/inline-tools.js>)在[提示组装](<../src/index.js>)按实际工具面生成,不只看开关意图。自有提示块可替换、幂等;重组后不残留失效建议。方言/子代理方言关闭不关闭安全选路指引。
77
+
78
+ PTC 中直调 schema 可只有 `run_code`:该描述解释如何选择已提供 SDK binding,具体 native/path binding 的描述进入 `tools:sdk`。没有把 SDK 声明误当成可直调工具,也不编造缺失名称。
79
+
80
+ **已验证提示送达,不等于已验证 LLM 必然正确决策。** 当前没有真实模型自主选工具评测。
81
+
82
+ ## 5. 架构:不建立第二套进程系统
83
+
84
+ ```text
85
+ agent-owned tool scope
86
+ ├─ bash → private GitBashSandboxExecutor → existing Core lifecycle/provider
87
+ ├─ gitbash_to_native_path → fresh shared translator → native_path(无I/O)
88
+ └─ gitbash_native
89
+ ├─ 每调用解析真实 standing policy / 严格请求校验
90
+ ├─ prepared capability(WeakMap;不是可伪造的 policy DTO)
91
+ ├─ bounded preparation / trusted artifact owner
92
+ └─ per-operation scope(属于 agent,隔离 shell + subprocess)
93
+ └─ actual LocalBashExecutor.executeArgv
94
+ └─ private guarded provider facade
95
+ └─ actual managed provider + per-stream output adapter
96
+ ```
97
+
98
+ 对应:[执行器](<../src/native-executor.js>)、[工具/Jobs/DTO](<../src/native-tool.js>)、[输出适配](<../src/native-output.js>)。
99
+
100
+ 不改 root 服务条目,不另造 child_process runner/Jobs registry,不覆盖 private spawnSpec。新模块不静态导入宿主 peer;由既有 host resolution 通道注入真实模块。
101
+
102
+ ## 6. 权限与拒绝
103
+
104
+ ### 6.1 自有 Bash
105
+
106
+ [策略门](<../src/shell.js>)要求 Windows spec 明确为 `danger-full-access`;其他/缺失策略拒绝。工具层[预检查](<../src/bash-tool.js>)位于工作目录准备、环境收集和 Jobs admission/promotion之前。
107
+
108
+ 实际 host SandboxUnavailableError 能解析时保留结构化 SANDBOX_UNAVAILABLE;直接/旧调用者缺导出也必须拒绝,不能因此运行。非 Windows 的受限路径仍继承父后端。
109
+
110
+ ### 6.2 Native
111
+
112
+ 不以部署 defaultMode 代替逐调用策略;准备、执行、目录 callback 前后、最终 spawn seam 重验真实 standing policy。Prepared request 只能由同一个 executor 产生,复制或跨 executor 不构成能力授权。
113
+
114
+ 这只是本地 Windows danger-only 入口,**不是 native restricted-token adapter**;不支持模型传入 env、sandbox_permissions 或自动 escalation。
115
+
116
+ ### 6.3 边界
117
+
118
+ 官方文件工具有独立 policy;helper 转换不扩大权限。minimal 持久 PTY、侧栏交互终端和其他插件执行器不因本门控获得围栏。历史 unconfined 渲染仍兼容旧记录,不授权新调用降级。
119
+
120
+ ## 7. 接口
121
+
122
+ ### 7.1 `gitbash_to_native_path`
123
+
124
+ 输入 `{ msys_path: string }`,成功值 `{ native_path: string }`。复用共享 translator 与 fresh mount facts;无 fs I/O、存在性判断或 env seeding。
125
+
126
+ Windows-native/cwd-relative 值声明后可 passthrough;未知 POSIX mount、缺映射、空值、NUL、URL明确拒绝。`msys_path`/`native_path`避开旧参数/结果的自动路径字段翻译,避免转换前被改写或成功值回流。
127
+
128
+ PTC 程序示例,假设 `inputPath` 是已经取得的完整路径值:
129
+
130
+ ```typescript
131
+ const resolved = await tools.gitbash_to_native_path({ msys_path: inputPath });
132
+ const fs = await import('node:fs/promises');
133
+ return await fs.readFile(resolved.native_path, 'utf8');
134
+ ```
135
+
136
+ 这不是绕过文件拒绝的方式;已发生拒绝必须停止。
137
+
138
+ ### 7.2 `gitbash_native`
139
+
140
+ | 字段 | 契约 |
141
+ |---|---|
142
+ | description | 必填、非空、NUL-free |
143
+ | executable | 必填 `{kind:'path',value}`,可解析绝对 `.exe`;无 bare command |
144
+ | argv | 必填 typed 数组;literal 原值,path 显式转换/规范化;允许空 literal |
145
+ | cwd | 可选绝对 typed path;默认 agent workspace |
146
+ | entrypoint | native 默认;已知解释器需显式 interpreter |
147
+ | stdoutEncoding/stderrEncoding | utf8 默认,或 gbk/utf16le/raw;不支持 auto |
148
+ | errors | replace 默认,strict opt-in |
149
+ | timeoutMs | 正有限数,默认120000ms,上限600000ms |
150
+ | run_in_background | 仅能力允许时声明;与显式 timeoutMs 同时给出会拒绝 |
151
+
152
+ Native typed path 不支持相对值、变量/home展开;需要时先通过显式 helper取得可用绝对值。Windows 启动字段/path argv规范化反斜杠;literal `/all`、`/c` 等不经过 MSYS。
153
+
154
+ 拒绝未知字段、model env、path-list env、隐式脚本、`.cmd/.bat`。当前限制4096个argv及保守Windows command-line预算。已知解释器名称检查是防误门,不是二进制鉴定;重命名/custom exe可有自己的语言。
155
+
156
+ **Exact argv 指验证过的 Core/CRT/Node 语义,不承诺任意 cmd/非CRT parser 的 quoting。**
157
+
158
+ ## 8. 编码与输出事实
159
+
160
+ 只有已知 collect disposition `{maxBytes,spill?:{maxBytes}}` 转 raw pipe;未知/其他 disposition按原 provider契约委托。每 operation immutable config,stdout/stderr独立 decoder,只在 ingest解码。
161
+
162
+ - UTF8默认宽容替换,保留BOM;strict显式。
163
+ - Decoder能力在spawn前探测;unknown同流mixed选raw。
164
+ - 原字节产物与normalized UTF8产物分开;raw无可靠文本承诺。
165
+ - Reader为独立、非消费normalized UTF8 byte cursor;不是原编码byte游标,也不是Core Jobs全局游标。
166
+ - handle.done保留真实provider outcome/error身份;decode/storage问题只记facts,不伪造命令未执行。
167
+ - OutputCollector只使用公开push/snapshot/seal;不访问其私有spill字段。
168
+
169
+ | 字段 | 解释 |
170
+ |---|---|
171
+ | sourceComplete | 是否收到自然EOF;不能单独证明程序未被中止 |
172
+ | complete | 管线完整性;不承诺tail包含全文 |
173
+ | lossy/reliableText | 与尾缓存、解码、存储等损失合读,不是编码自动鉴定 |
174
+ | aborted | 取消/主动terminate/owner终止事实 |
175
+ | artifact.complete/capReached/error | 对该产物自身完整性、预算、存储错误判断 |
176
+ | rawBytes/rawCursor | 原流字节计数/游标 |
177
+ | decodedUtf8Bytes/decodedCursor | normalized UTF8计数/游标 |
178
+
179
+ Foreground DTO使用closed schemas;去undefined投影不放任unknown字段。无facts/投影失败是显式分支,不通过宽schema吞错。后台最终exit/facts进入现有Jobs文本source,不声称扩展Core Jobs结构化API。
180
+
181
+ ## 9. 生命周期与预算
182
+
183
+ 准备阶段workspace/scope/plugin mount/artifact callback受同一取消/有界期限控制,剩余预算交Core;后台/onExpiry:none只限制准备,已handoff producer不会被准备timer杀死。
184
+
185
+ operation-owned AbortController统一caller cancel、explicit kill、ownerdispose;先记录cause再terminate。正常settlement后释放scope不能反向污染已完成facts。
186
+
187
+ 输出end/error/close/abort/provider failure分别收尾;默认250ms有界drain,不无限等后代持有管道。不保证所有输出在短drain窗口内到达,未完整必须如实标注。
188
+
189
+ 默认两流raw+decoded总磁盘8MiB、自有payload16MiB;含保守tail/copy/scratch预留。**不是OS/RSS、provider buffer、caller副本或跨调用硬quota。**
190
+
191
+ trusted owner在真实policy/校验后创建自有临时目录,不从model cwd建产物目录。Adapter不创建目录、chmod、删除或授权;文件有限wx创建。完成产物保留供晚reader读取,无自动purge承诺。晚callback结果不续spawn,不武断删除可能仍被引用的目录;归trusted retention owner。
192
+
193
+ ## 10. Jobs 与动态装配
194
+
195
+ 复用registry.start/wait/read/kill/remove、processJob/processSources和owner/controller admission。Scope属于agent,不属于tool registration fiber;后台/promotion后卸载工具不提前销毁运行进程。
196
+
197
+ 没有controller时明确拒绝,不以“admission失败后再运行前台”冒二次副作用风险。没有Jobs时为前台kill deadline;有Jobs时可timeout promotion。该差异需在正式用户体验验收中检查。
198
+
199
+ Windows own-bash agent scope挂两个companions,各自按 agent 创建注册模块。初始挂载、开关更新和 native Jobs 刷新共用注册入口:先注销自身注册,再检查当前 agent 的工具所有权;已有外部全局或同 agent 同名工具就让位。实际 Core 的 knownNames 包含隐藏工具,旧宿主回退 schemas 检查;检查失败也让位。外部所有者移除后,下次刷新可恢复。注册异常只记录日志,不阻断另一个 companion 的更新。两工具必须确认 apply 已执行;scope 就绪与实际注册成功分别报告,关闭或让位时不会报告注册成功。Bash/native 共用可选目录读取,有服务走官方 ensure(错误不降级),无服务走旧 policy.workspaceRoot ?? session.header.cwd 接缝。缺native executor/OutputCollector公开能力不撤bash/path。新工具不加入minimal持久终端通道,不改变root PowerShell、market patch或preset id。
200
+
201
+ ## 11. 已发生的反证与修正
202
+
203
+ 1. 初版ownerdispose/direct kill虽quiet且无LATE,却错报completed/aborted:false/artifact.complete:true。独立固定sentinel反证后,operation cause与adapter terminate共同修正;正常cleanup反向用例确保不污染成功结果。
204
+ 2. trusted artifact callback初版可无限await;abort/deadline时0spawn但不返回。现准备阶段有界race,晚callback不恢复执行,保留资源责任明确。
205
+ 3. 初版facts空宽schema现closed DTO;跨模块termination枚举/三参callback曾不同步,已对齐并真实Core负向校验。
206
+ 4. 集成发现显式证据env未配置、路径夹具硬编码盘符/安装bootstrap;改test-only目录预载、动态盘符、宿主自身bootstrap,真实Node fs复跑通过。没有放宽产品断言掩盖失败。
207
+
208
+ 详情见[验证文档](<experimental-native-verification.md>)。失败观察用例退出0只表示复现成功,不能算修复通过。
209
+
210
+ ## 12. 合并讨论 / 未覆盖
211
+
212
+ - [x] 接受 fail-closed,新增默认 true 的 volatile 开关,关闭恢复旧行为。
213
+ - [x] Controller 缺失保持明确拒绝,不静默改前台重复执行。
214
+ - [x] Artifact ownership/quota/retention 由插件层自管;跨调用 quota/purge 尚未实现,不宣称已验收。
215
+ - [ ] **长期跟踪(不阻塞本 PR)**:Core 将来开放 native typed argv、逐流 encoding、closed DTO 等对应公开 API 时,遵循官方优先纪律整体切换并退役插件私有实现。
216
+ - [x] **同步上游**:前置 PR #24 已合并;实验已 rebase 到[上游 v0.39.4 的 main](https://github.com/KannaKuron/dsh-gitbash-shell/commit/52da331)(v0.39.4 起 `fastctxTools` 默认 OFF、新增 ops_* 互操作面),继承正式 v0.39.4 的兼容与激活验证;单装工具面在 `fastctxTools` OFF 下 companion 注册检测不受影响(`tests/runtime-preview.mjs`「companions mount on the single-install face with fastctxTools OFF」锁死设计文档 §10 语义)。
217
+ - [ ] **拆分评估**:建议两步 review:① Bash fail-closed + 行 Config/UI/拒绝引导/回退;② native/path、逐流输出与可见性路由。唯一开关在第一步建立,第二步复用同一字段。当前保留完整 Draft/RFC,尚未另开子 PR。
218
+ - [ ] **转正式的四项门槛**:真实 Agent 自主选路、生产 GUI(非 preview)、维护者 Windows 真机全量验证、非 CRT 解释器矩阵抽测。四项齐后才谈 ready for review → release。
219
+ - [ ] 真实Agent选工具评测、生产GUI/full apply、会话replay尚未验收。
220
+ - [ ] 真restricted-token/ACL/IPC/VM正负权限、PTY/SSH、任意非CRT/解释器quoting尚未验收。
221
+ - [ ] 真实disk exhaustion、吞吐/RSS峰值、跨任务DOS/自动purge尚未验收。
222
+ - [ ] 若未来Core提供受限native adapter,应如何探测启用而非偷换mode?
223
+
224
+ **当前结论:接口机制与确定性隔离用例可讨论;不能据此宣布普遍乱码解决、Windows沙箱恢复或正式发布就绪。**
@@ -0,0 +1,184 @@
1
+ # 实验性实现:验证矩阵、证据分级与复现
2
+
3
+ > **这是实验性讨论的验证记录,不是发布或安全放行。**
4
+ > 关联:[设计/接口/待讨论决定](<experimental-native-execution.md>)、[CHANGELOG](<../CHANGELOG.md>)。
5
+ > 验证日期:2026-10-14(保留此前轮次记录)。以下结果适用于所列运行环境,不代表所有宿主版本或生产场景均已通过。
6
+
7
+ ## 1. 验证基准与结果
8
+
9
+ - 当前实验基线:[上游 v0.39.4(52da331)](https://github.com/KannaKuron/dsh-gitbash-shell/commit/52da331)。前置 PR #24 已合并;2026-10-15 第一轮设计评审后由 v0.39.3(114dcde)rebase 而来,本节末尾补记本轮验证。
10
+ - 测试环境:**DSH Core 0.2.1-alpha.2**、Node 24.13.0、Windows / Git for Windows。
11
+ - 新native功能按公开能力探测启用;既有兼容版本范围之外的实际覆盖情况见后文待验收项。
12
+ - 最终普通全仓:**232 tests / 232 pass / 0 fail / 0 skip / 0 cancel,exit0**。
13
+ - 最终实际Core完整preview:**85 tests / 85 pass / 0 fail / 0 skip / 0 cancel,exit0**。
14
+ - 语法与`git diff --check`通过;npm dry-run确认新源码包含于发布文件集合。Dry-run不是已安装/发布,也不是新增生产版本。
15
+
16
+ 计数是Node test runner报告口径,包括子测试/套件;不同轮次或独立复跑有重叠,**不累加为独立覆盖数量**。232/85 是当前完整脚本结果,不意味着每项都是真生产进程场景。
17
+
18
+ ## 2. 证据等级
19
+
20
+ | 等级 | 本提案实例 | 可以证明 | 不能证明 |
21
+ |---|---|---|---|
22
+ | 静态/源码与公开契约 | 配置、SDK/types、实际安装导出、匹配MSYS因果资料 | 实现依赖与约束、提出机制假设 | 新OS沙箱可用或任意版本兼容 |
23
+ | 纯单元/受控注入 | 请求parser、预算、流异常、字段schema、路由矩阵 | 确定性输入输出与拒绝/收尾规则 | 真实磁盘故障或任意producer行为 |
24
+ | 实际Core + 禁止spawn桩 | ToolRuntime/scoped装配/提示组装 | 真Core结构化拒绝、零admission、注册/卸载/隐藏 | 生产GUI或真实native程序跑通 |
25
+ | 实际Core + 受控native程序 | LocalSubprocessRuntime/executeArgv/Jobs、固定bytes/READY/LATE | 所测环境中的真实argv、编码、kill/quiet/promotion与owner行为 | 全部原生parser、网络/SSH、真受限token |
26
+ | 实际PTC + Node/fs | ToolRuntime→NodePtcRuntime→LocalSubprocessRuntime→Node fs读自有输入 | 完整值helper子分发、动态路径、route/tag保持 | 完整插件apply/生产用户policy/模型自主选择 |
27
+ | 实际Core提示/SDK生成 | SystemPrompt.assemble普通/PTC及工具visibility | Agent将收到正确的说明与可见binding | 模型每次一定选对 |
28
+ | 未执行 | 生产GUI/full apply/LLM选择、OS权限、吞吐/RSS/retention | 仅列为待验收 | 不能作为通过项 |
29
+
30
+ ## 3. 可审阅测试清单
31
+
32
+ | 文件 | 重点 | 层次 |
33
+ |---|---|---|
34
+ | [smoke](<../tests/smoke.mjs>) | legacy与modern executor契约、Windows受限/未解析零argv、full/非Windows行为、既有护栏 | 纯回归与受控基类 |
35
+ | [runtime regressions](<../tests/runtime-regressions.mjs>) | 受限前台/后台/promotion admission前拒绝,既有Jobs动态卸载 | Cordis受控回归 |
36
+ | [compatibility regressions](<../tests/compatibility-regressions.mjs>) | 源码路径边界、live hint、选路判据、16种可见性组合、幂等、SDK-only/路径说明 | 确定性纯回归 |
37
+ | [path unit](<../tests/path-tool.mjs>) | 共享translator/fresh env、empty/NUL/URL/未知映射、schema/注册/卸载 | 纯单元 |
38
+ | [path preview](<../tests/path-preview.mjs>) | 真实PTC子分发与Node fs读中文/#自有输入、完整值动态组合、route/tag保持、catchable错误 | 实际引擎/子进程/文件读 |
39
+ | [output unit](<../tests/native-output.mjs>) | incremental/BOM、默认replace/strict、budget/cursor、异常drain、terminate完整性 | 单元含受控异常 |
40
+ | [output preview](<../tests/native-output-preview.mjs>) | 公开OutputCollector、actual raw pipe、多编码、真实exit与decodeError分离、READY后终止/quiet | 实际Core/固定producer与受控I/O异常 |
41
+ | [native unit](<../tests/native-executor.mjs>) | typed argv/path、解释器/字段/编码/env拒绝、预abort、准备/mount有界 | 单元 |
42
+ | [native preview](<../tests/native-preview.mjs>) | 实际executeArgv/ToolRuntime/Jobs、argv、多流、strict exit7、credential sentinel、timeout/背景/promotion/controller/owner | 实际Core/真实controlled Node |
43
+ | [independent regression](<../tests/native-integration-preview.mjs>) | prepared伪造/跨executor、重复policy gate、callback revocation/abort/deadline、owner/kill、自然完成后晚reader | 实际Core独立反证回归 |
44
+ | [runtime preview](<../tests/runtime-preview.mjs>) | actual拒绝、agent装配/卸载/重载、官方root保留;普通/PTC真实prompt/SDK及隐藏工具 | 实际Core,执行部分使用桩 |
45
+
46
+ 既有client/FastCtx/host/preset/桥接测试仍在完整脚本中;没有只运行新增测试冒充全仓通过。
47
+
48
+ ## 4. 特别需要看负向断言
49
+
50
+ ### 4.1 Policy不是defaultMode
51
+
52
+ - RO/WW/missing拒绝;默认full不能放宽另一会话。
53
+ - Prepared DTO复制、其他executor产生的对象拒绝。
54
+ - 重复resolve阶段与实际executeArgv的spawn seam分别测策略翻转,均零producer spawn。
55
+ - Artifact await期间撤销/取消也拒绝,不恢复已取消准备。
56
+ - 自有bash前台、显式后台及promotion不得先登记job再报告拒绝。
57
+
58
+ 这些断言验证入口门禁,**不是实际restricted-token写入正负试验**。
59
+
60
+ ### 4.2 Producer outcome与display分离
61
+
62
+ - Strict非法字节不能把真实exit0/exit7改成“命令没有执行”。
63
+ - Raw bytes保留、stdout/stderr单独解码,并发编码不能共享latest状态。
64
+ - `sourceComplete`可与`aborted:true`同时出现,不把自然pipe EOF误作自然producer完成。
65
+ - 实际ownerdispose/directkill保持range quiet且无LATE,同时status killed/aborted true/artifact incomplete。
66
+ - 自然完成后的scope cleanup不得反向污染complete;晚reader产物仍可读。
67
+
68
+ ### 4.3 准备阶段取消
69
+
70
+ - 挂起workspace、scope mount、trusted artifact callback受有界等待控制。
71
+ - 取消/期限到达时不等待late callback才返回;scope释放,零spawn。
72
+ - 晚目录结果归trusted owner保留,不冒称自动purge;不武断删除仍可能有引用的资源。
73
+
74
+ ### 4.4 输出字段严格性
75
+
76
+ 真实ToolRuntime对unknown深层facts、错误类型budget、伪造fallback分支进行负向校验。去undefined只做lossless DTO投影,不放宽closed schema;SDK不支持的minimum关键词没有强行加入,非负计数由adapter运行时校验。
77
+
78
+ ### 4.5 Agent选路说明
79
+
80
+ - Shell与native先按任务性质选,不把native当权限拒绝后的fallback。
81
+ - 不再同时承诺“所有命令ALL commands走bash”与“原生入口可用”。
82
+ - `pwsh`必须实际可见且switch允许;native/helper缺失不得推荐其名称。
83
+ - 重复组装和隐藏后更新提示,不堆叠旧块。
84
+ - PTC assembly只见run_code时解释SDK调用,实际SDK含binding自有说明。
85
+ - 委托方言关闭仍有选路/禁止绕行提示。
86
+
87
+ **没有调用真实LLM让它独立选择工具**;此处是prompt delivery,不是行为成功率统计。
88
+
89
+ ## 5. 复现方式(开发测试,不是安装教程)
90
+
91
+ 安装开发依赖并准备对应宿主后运行:
92
+
93
+ ```bash
94
+ npm test
95
+ ```
96
+
97
+ Windows实际Core测试通过环境变量指定宿主目录:
98
+
99
+ ```bash
100
+ DSH_PREVIEW_HOST_DIR='<实际宿主resources/app绝对路径>' npm run test:preview
101
+ ```
102
+
103
+ 将占位符替换为待测试宿主的 resources/app 绝对路径。
104
+
105
+ 脚本通过[host loader](<../tests/host-loader.mjs>)解析安装的Core导出,并预载[test-only evidence env](<../tests/preview-env.mjs>)创建测试证据目录。
106
+
107
+ 测试默认在临时目录下创建独立scratch并保留证据。每流artifact有预算,测试目录不会自动清理;也可提前创建目录并设置以下环境变量:
108
+
109
+ - DSH_NATIVE_OUTPUT_EVIDENCE_DIR
110
+ - DSH_NATIVE_EVIDENCE_DIR
111
+ - DSH_NATIVE_REVIEW_EVIDENCE_DIR
112
+ - DSH_PATH_EVIDENCE_DIR
113
+
114
+ 单独运行preview文件时,同时预载host-loader和preview-env,或设置该测试要求的证据目录环境变量。
115
+
116
+ ## 6. 失败记录与证据修正
117
+
118
+ | 曾出现的失败 | 纠正 | 为什么记录它 |
119
+ |---|---|---|
120
+ | Cordis bound-function直接===不同 | 比较真实service registry entry身份 | trace proxy不能被误判为root替换 |
121
+ | 夹具缺session.header.id | 补实际shellEnv要求 | 不放宽产品env契约 |
122
+ | outputFacts可选undefined | 工具边界lossless投影,closed DTO负例保留 | 不掩盖Core拒绝或producer exit |
123
+ | ownerkill只terminate | 加operation cause与adapter interruption,反向cleanup断言 | 原有quiet通过不等于完整性正确 |
124
+ | callback无限await | 准备deadline/abort race,late资源责任明确 | 零spawn仍可能无限悬挂 |
125
+ | 空宽facts schema | 枚举closed shapes与终态,真实负例 | 不能宽schema补签 |
126
+ | 最终termination/callback跨模块不同步 | 精确enum和三参signal对齐 | 防止输出校验抹真实outcome |
127
+ | 旧description还锁unconfined宣传 | 改为更强拒绝/不绕行护栏 | 行为变化不是弱化测试 |
128
+ | 完整preview缺evidence env/夹具固定盘符 | test-only预载、动态盘符、host bootstrap | 本机可重复不等于可迁移 |
129
+
130
+ 独立审查旧态观察测试即使exit0,只证明缺陷被复现。最终将这些观察转为修复后的硬回归。独立专项复跑41/55与全仓结果重叠,不另加总数。
131
+
132
+ 本文提供断言、结果计数和适用限制;完整日志与artifact由测试运行生成。以上测试文件可用于复现对应场景。
133
+
134
+ ## 7. 转正式功能前的验收清单
135
+
136
+ - [x] 维护者裁决默认开启的 volatile 开关,关闭恢复旧行为。
137
+ - [ ] 生产GUI/full plugin apply、Agent真实选择、会话replay。
138
+ - [ ] 真Windows confinement正负权限/IPC/token及可接受隔离方案。
139
+ - [ ] SSH/PTY、代表性非CRT/解释器程序的实际quoting。
140
+ - [ ] 真实disk exhaustion、吞吐/RSS、跨调用配额/retention/清理。
141
+ - [ ] 更多真实Core版本与宿主布局;能力不足不得误advertise。
142
+ - [ ] Jobs/controller缺失和promotion用户体验。
143
+ - [ ] 决定哪些接口应该成为Core公开加法契约。
144
+
145
+ 在这些项完成前,保持Draft/实验讨论定位,不称普遍编码修复或新沙箱实现。
146
+
147
+ ## PR #26 运行时开关裁决后的验证(2026-10-13 历史轮次)
148
+
149
+ 本轮普通完整回归 **226/226**,实际安装 Core 完整 preview **75/75**,均 exit0、无 skip/cancel。环境仍为 Windows、Node 24.13.0、Core 0.2.1-alpha.2。新增覆盖:
150
+
151
+ - 卡片缺键默认 ON、两向写同一行 Config 字段、21 字典键对齐(当时其余 19 语言暂用英文,本轮已本地化)。
152
+ - 实际 ToolRuntime 中现有会话 ON/OFF 反复切换、初始 OFF 后启用、工具 schema 注销/注册、受限 Bash 拒绝/旧 unconfined 结果;root shell 不变。
153
+ - 旧路径定义关闭后零翻译;native 已准备请求关闭后零 scope/adapter;artifact 异步准备期间关闭后零 spawn、scope 释放,重新开启后真实 Node producer 运行一次。
154
+ - #24 actual promotion DTO 测试以显式 OFF 验证旧 sandbox facts 提示,full-access 无警示。
155
+
156
+ 初版 tools.restrict agent-local mask 被实际 Core 拒绝;改为动态拥有者注册。artifact 负例初版检查仅在 spawn facade 内,被 Core 转为失败 handle;增加准备后检查后以明确错误拒绝,未放宽零 spawn 断言。
157
+
158
+ 仍待正式四项验收:真实 Agent 自主选路、生产 GUI(非 preview)、维护者 win32 真机全量验证、非 CRT 解释器矩阵抽测。当时 #24 未合并;本轮已完成到上游 main 的 rebase。
159
+
160
+ ## 同步 v0.39.3 后的验证(2026-10-14,所有权修复前)
161
+
162
+ 当前完整回归 231/231、实际 Core preview 79/79,exit0、无 skip/cancel。新增缺 workingDirectory 的三工具挂载/cwd/双向开关,真实 native producer 的 policy/header 默认目录;有服务 ensure 错误不降级。两个 companion 也验证真实注册后才报成功。21 份字典已本地化。
163
+
164
+ 代表性解释器抽测:真实 cmd.exe 的 literal /c echo sentinel;Windows PowerShell 的显式 EncodedCommand + UTF16 输出中文 sentinel。均通过;这些固定脚本不证明任意引号、元字符或所有非 CRT parser 的可靠性。真实 Git Bash 在 fixture RO/WW 中 OFF 执行并携带 unconfined facts,ON 再次拒绝;未确定策略在 ON 拒绝。所有进程均在隔离 Context 与临时目录中,不改变生产 Profile。
165
+
166
+ [已安装实验构建的实机报告](https://github.com/KannaKuron/dsh-gitbash-shell/pull/26#issuecomment-6149607594)覆盖 Bash/native/path 入口与 RO/WW、单次批准不粘滞。报告没有记录精确提交,且明确未覆盖开关 OFF、生产 GUI、真实 LLM 自主选路或非 CRT 矩阵;将其作为有限外部证据,不扩展为当前 rebase 构建的完整验收。
167
+
168
+ 仍需真实 LLM 自主选路、生产 GUI、维护者 Windows 全量验证、非 CRT 矩阵的正式确认;保留 Draft/RFC。
169
+
170
+ ## 独立审查后的工具所有权修复(2026-10-14)
171
+
172
+ 隔离子代理确认一项 P2:开关 OFF 期间外部注册同名工具,恢复 ON 后全局外部工具被遮蔽,同 agent 外部工具则导致 duplicate 异常并中断后续 companion 注册。共用注册入口现对每次初始挂载、开关更新及 native Jobs 刷新重新检查当前 agent 的所有权;外部所有者存在时让位,检查或注册失败记录日志并隔离异常。实际 Core 的 knownNames 包含隐藏工具,旧宿主回退 schemas 检查;旧宿主隐藏工具行为尚未独立验证。
173
+
174
+ `tests/runtime-preview.mjs` 新增六项实际 Core 回归:path/native × 全局/同 agent 四种冲突的重复 OFF→ON、外部所有者移除后的恢复、native Jobs 卸载/重载冲突,以及隐藏全局同名工具。`tests/runtime-regressions.mjs` 新增一项单个注册失败不阻断另一个工具、后续恢复和卸载清理的回归。scope 就绪与实际注册成功分别记录。
175
+
176
+ 最终普通全仓 **232/232**、实际 Core 完整 preview **85/85**,均 exit0、无 skip/cancel。隔离子代理独立重跑原始所有权复现两项与新增专项七项,**9/9** 通过:全局与同 agent 外部 sentinel 均保持原响应,另一 companion 正常注册。未发现新的确认缺陷。这些专项与完整结果重叠,不累加。
177
+
178
+ 最初疑似 Bash policy 时序问题已撤回:实际宿主契约为逐操作快照,模式变更作用于下一次调用;直接修改 fixture header.policy 不能证明违约。本轮保持该契约。上述四项正式验收仍待,继续保持 Draft/RFC。
179
+
180
+ ## 同步 v0.39.4 后的验证(2026-10-15,第一轮设计评审收敛)
181
+
182
+ 按[维护者第一轮设计评审](https://github.com/KannaKuron/dsh-gitbash-shell/pull/26#issuecomment-6182031633)rebase 到 v0.39.4(52da331)并完成三项收敛:① 新增实际 Core 断言——`fastctxTools` OFF(v0.39.4 默认值)且工具面无 `gb_*`/`ops_*` 的单装场景下 companion 注册检测照常挂载(`tests/runtime-preview.mjs`「companions mount on the single-install face with fastctxTools OFF」);② 设计文档 §3 补同装 dsh-ops 组合盲点;③ session-workdir 保留模块化 `src/session-workdir.js`(52da331 未触及 bash-tool.js,收敛在 rebase 中自动完成,smoke 新增源码断言防双实现静默回归)。
183
+
184
+ 普通完整回归 **236/236**、实际 Core(desktop-next host,0.2.0-rc.1)完整 preview **86/86**(较上轮 85 增本轮①断言一例),均 exit0、无 skip/cancel。有界只读交叉核验(隔离子代理)独立复跑数字一致,变异测试确认新断言对 fastctx 门控移除有唯一检测力;fastctxTools OFF 下禁用的桥走 unpublish 短路、不进入 publish(disabled 日志断言)。已知非本轮引入的遗留:客户端 FastCtx 卡 21 语言 hint 仍写「默认开启」而实际默认 OFF(继承自 main 52da331),拟作为上游问题在 PR 说明。上述四项正式验收仍待。
package/dsh.plugin.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-gitbash-shell",
3
- "version": "0.39.4",
3
+ "version": "0.40.1",
4
4
  "main": "./src/index.js",
5
5
  "description": "Windows 全模式 Git Bash:自带 bash 工具(私有 Git Bash 执行器实例,官方 PowerShell 执行器原样保留),官方四个模式经行内工具补位直接获得 Git Bash(v0.35.0 起;v0.39.0 起不再替换宿主执行器),四个 *·Git Bash 变体 preset 可开关;官方 pwsh 工具默认隐藏、开关可见(真 PowerShell);附带 gb_* FastCtx 文件工具,检测到 dsh-ops 自动让位。联动:dsh-ptc-cordis-preset(PTC 创造模式带 Git Bash 行且可去重)+ dsh-better-sidebar(终端标签自动切 Git Bash)。",
6
6
  "engines": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-gitbash-shell",
3
- "version": "0.39.4",
3
+ "version": "0.40.1",
4
4
  "description": "Windows 上给 agent 一个真正的 Git Bash 命令工具:私有 Git Bash 执行器实例(官方 PowerShell 执行器原样保留),统一 MSYS 盘根路径方言,官方 pwsh 工具默认隐藏可开关,物化四个内置预设的 Git Bash 变体,侧栏终端接管可开关。· Give agents a real Git Bash tool on Windows: a private Git Bash executor instance (the official PowerShell executor stays untouched), a unified MSYS drive-root path dialect, an opt-in official pwsh tool, and four Git Bash preset variants.",
5
5
  "type": "module",
6
6
  "repository": {
@@ -42,6 +42,7 @@
42
42
  },
43
43
  "files": [
44
44
  "src",
45
+ "docs",
45
46
  "cordis.patch.yml",
46
47
  "dsh.plugin.json",
47
48
  "CHANGELOG.md",
@@ -52,8 +53,8 @@
52
53
  "icon.svg"
53
54
  ],
54
55
  "scripts": {
55
- "test": "node --test tests/smoke.mjs tests/private-executor.mjs tests/client-regressions.mjs tests/bridge-regressions.mjs tests/runtime-regressions.mjs tests/host-regressions.mjs tests/compatibility-regressions.mjs",
56
- "test:preview": "node --import ./tests/host-loader.mjs --test tests/preview.mjs tests/bridge-preview.mjs tests/runtime-preview.mjs tests/host-preview.mjs",
56
+ "test": "node --test tests/smoke.mjs tests/private-executor.mjs tests/client-regressions.mjs tests/bridge-regressions.mjs tests/runtime-regressions.mjs tests/host-regressions.mjs tests/compatibility-regressions.mjs tests/path-tool.mjs tests/native-output.mjs tests/native-executor.mjs",
57
+ "test:preview": "node --import ./tests/host-loader.mjs --import ./tests/preview-env.mjs --test tests/preview.mjs tests/bridge-preview.mjs tests/runtime-preview.mjs tests/host-preview.mjs tests/path-preview.mjs tests/native-output-preview.mjs tests/native-preview.mjs tests/native-integration-preview.mjs",
57
58
  "version": "node -e \"const fs=require('fs');const m=JSON.parse(fs.readFileSync('dsh.plugin.json','utf8'));const v=JSON.parse(fs.readFileSync('package.json','utf8')).version;m.version=v;fs.writeFileSync('dsh.plugin.json',JSON.stringify(m,null,2)+'\\n')\" && git add dsh.plugin.json"
58
59
  },
59
60
  "keywords": [
package/src/bash-tool.js CHANGED
@@ -40,6 +40,7 @@
40
40
  * @module dsh-gitbash-shell/bash-tool
41
41
  */
42
42
 
43
+ import { resolveSessionCwd } from './session-workdir.js'
43
44
  import {
44
45
  ESCALATION_TARGETS,
45
46
  parseExitStatus,
@@ -102,7 +103,7 @@ export function bashDescription() {
102
103
  + 'Git for Windows (MSYS) converts path-looking arguments when invoking native Windows programs: ipconfig /all and cmd /c may break; use ipconfig //all or cmd //c, or a per-command MSYS2_ARG_CONV_EXCL prefix. '
103
104
  + 'Exclusions are prefixes: excluding /c also excludes /c/... paths, so use native drive-letter paths for those arguments; do not disable conversion globally. Verify the expected effect as well as the exit code. '
104
105
  + 'Output is decoded as UTF-8; native Windows programs may emit OEM/ANSI bytes (for example CP936/GBK) and display garbled text. Use a program UTF-8 option or explicit known-encoding conversion before capture; locale variables alone do not fix native output, and mixed streams cannot safely be guessed or decoded globally as GBK. '
105
- + 'On Windows, this Git Bash route does NOT enforce shell file-write restrictions in read-only/workspace-write mode (unconfined). Official file-tool write/edit policy is separate; never use shell to bypass its denial.'
106
+ + 'On Windows, while Experimental native execution / fail-closed (experimentalNative, default ON) is enabled, read-only/workspace-write or unresolved policy is refused before this Git Bash command starts because verified confinement is unavailable. Only an explicitly resolved danger-full-access policy can execute this route. The user can turn off that switch in Settings > Plugins > dsh-gitbash-shell to restore explicitly UNCONFINED legacy Bash, or select danger-full-access in the session access-mode control. Do not switch tools or permissions automatically to bypass a refusal; official file-tool write/edit policy remains separate.'
106
107
  }
107
108
 
108
109
  /** The official foreground/present call view (index.ts:108). */
@@ -511,16 +512,13 @@ export function createBashToolModule({ executor, toolsModule, sandboxModule, llm
511
512
  const policy = approvedMode === undefined
512
513
  ? standingPolicy
513
514
  : { ...standingPolicy, mode: approvedMode }
515
+ // Refuse unsupported policy before directory preparation or job admission.
516
+ if (typeof executor.gitBashRoute === 'function') executor.gitBashRoute({ sandboxPolicy: policy })
514
517
  // Optional read (issue #27): on hosts that provide workingDirectory
515
518
  // this is the same ensure() seam the modern official tool-bash uses;
516
519
  // on hosts without it, fall back to the session cwd exactly the way
517
520
  // the rc.2-era official resolveWorkdir did (policy root ?? header).
518
- const workingDirectory = ctx.get !== undefined ? ctx.get('workingDirectory') : undefined
519
- const cwd = exec.agent === undefined
520
- ? undefined
521
- : workingDirectory !== undefined
522
- ? await workingDirectory.ensure(exec.agent, exec.signal)
523
- : (standingPolicy?.workspaceRoot ?? exec.agent?.session?.header?.cwd)
521
+ const cwd = await resolveSessionCwd(ctx, exec, policy)
524
522
  const workdir = resolveWorkdir(args.workdir, cwd)
525
523
  const dshEnv = ctx.shellEnv.collect(exec)
526
524
  const request = {